0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【GD32F303红枫派开发板使用手册】第二十讲 SPI-SPI NAND FLASH读写实验

聚沃科技 2024-06-20 09:50 次阅读
wKgaomZVdiiAfR9BAB3mDFhHnZc972.png

20.1实验内容

通过本实验主要学习以下内容:

  • SPI通信协议,参考19.2.1东方红开发板使用手册
  • GD32F303 SPI操作方式,参考19.2.2东方红开发板使用手册
  • NAND FLASH基本原理
  • SPI NAND介绍
  • 使用GD32F303 SPI接口实现对GD5F1GQ5UEYIGY的读写操作

20.2实验原理

20.2.1NAND FLASH基本原理

NAND Flash和NOR Flash都是两种非易失性存储器,其读写速度、读写方式,存储区结构、成本、容量、擦写寿命都有很大区别。NAND在寿命、速度、读写方式上都不如NOR,但在成本和容量上有很大区别,故而决定了大容量数据存储是NAND的主要应用领域,而快速启动、快速数据读取等场景是NOR的主要应用领域。而SPI是目前NAND和NOR的主要通信接口形式,降低了器件体积,标准化了器件接口。

  • NAND Flash结构示例
wKgaomZzie-ASHMCAACpYmz1joQ526.png

如上图所示,以GD5F1GQ5UEYIGY为例,一个1Gb的存储结构下是由1024个block组成,每个block又64page组成,每个page是2K Main Area+Spare Area(ECC ON:64B;ECC OFF:128B)组成。

NAND的擦除单位是blocks,写入单位是page,所以寻址的方式上和nor是有本质区别的,需要按blocks、page、page字节偏移地址进行一个数据的寻址。

20.2.2SPI NAND介绍

SPI NAND简化了NAND的接口设计和尺寸,SPI接口更是降低了主控对接口的要求,同时内置ECC。下图是GD5F1GQ5UEYIGY的命令表,常用的命令为擦除、编程、读取命令。

wKgZomZzifqAdlMUAAKpHZv_HdQ326.png
  • block擦除命令
wKgZomZzigeAKIIUAAES6EvOPGw289.png
  • 编程
  • 编程流程
  1. 先用数据缓存写入指令将数据写入缓冲区
  2. 然后发送写使能命令,并确认写使能成功
  3. 然后发送数据载入命令执行缓冲区数据到FLASH的写
  4. 最后查询读寄存器确认P_FAIL是否有错,OIP是否完成

注意(84h/C4h/34h) 和(FFh)指令是不会清除缓存中的内容的,所以下次编程时要注意是否缓存区都是需要更新的数据,所以必须是一次更新整个缓冲区,不要部分更新。

编程page地址按照块的顺序

  • 数据缓存写入命令
wKgZomZzihqAQqKoAADV6NvTRjk583.png
  • 数据载入命令
wKgZomZzij2AZf6eAAEgT0-fPeU146.png
  • 读取
  • 读取流程
  1. 读需要先通过读cache命令从FLASH中读出数据到缓存中
  2. 然后通过读cache指令从缓冲区中开始读出数据

读到2048+128后绕回从0开始继续。

20.3硬件设计

红枫派开发板SPI——NAND FLASH的硬件设计如下:

wKgZomZzikyAAxbIAADuwlzsORg877.pngwKgZomZyPjeALX0BAADP0lLHKYs115.png

从图中可以看出,本实验使用的是普通单线SPI,GD5F1GQ5UEYIGY的片选由GD32F303ZET6的PG13控制(因PG14不是SPI的NSS管脚,所以本实验用主机NSS软件模式,,通过普通IO控制片选),GD25Q32ESIGR的SO、SI和SCLK分别和GD32F303ZET6的PB4(SPI2_MISO)、PB5(SPI2_MOSI)以及PB3(SPI2_CLK)相连。

20.4代码解析

20.4.1SPI初始化和读写BYTE函数实现

SPI初始化配置流程可参考19.4.1东方红开发板使用手册

SPI读写BYTE函数实现可参考19.4.2东方红开发板使用手册

20.4.2SPI NAND FLASH BSP驱动层实现

操作NAND FLASH的函数都定义在bsp层文件bsp_spi_nand.c中,这个文件中定义的函数都是针对NAND FLASH命令来实现的,我们选取几个函数进行介绍。

  • NOR FLASH按block擦除函数bsp_nandflash_block_erase,输入block号即可擦除;该函数流程是:使能NAND FLASH的写功能->向NOR FLASH发送block擦除指令0xD8->发送左移6位的Block NO->查询OIP标志等待完成
C
/*!
\brief erase the nandflash blcok
\param[in] block_No:the serial number of erase block
\param[out] none
\retval SPI_NAND_FAIL: erase the nandflash block fail
\retval SPI_NAND_SUCCESS: erase the nandflash block success
*/
uint8_t bsp_spi_nandflash_block_erase(uint32_t block_No)
{
uint8_t result = SPI_NAND_SUCCESS;

block_No<<=6;        //block_No=block_No*64
bsp_spi_nandflash_write_enable();
/* select the flash: chip select low */
bsp_spi_nand_cs_low();
/* send "ERASE BLOCK" command */
driver_spi_master_transmit_receive_byte(&BOARD_SPI,SPI_NAND_BLOCK_ERASE);
/* send the address of memory */
driver_spi_master_transmit_receive_byte(&BOARD_SPI,(block_No>>16)&0xFF);
driver_spi_master_transmit_receive_byte(&BOARD_SPI,(block_No>>8)&0xFF);
driver_spi_master_transmit_receive_byte(&BOARD_SPI,block_No&0xFF);
/* deselect the flash: chip select high */
bsp_spi_nand_cs_high();
while(bsp_spi_nandflash_get_status_flag(OIP)==SPI_NAND_BUSY);
/* check program result */

return result;
}
  • NOR FLASH按page写入函数bsp_nandflash_page_program,输入待写入数据指针、block号、page号;该函数流程是:
  • 写缓冲区,实现流程:向NOR FLASH发送写缓冲区指令0x02->发送写入的page偏移地址->发送待写入数据
  • 载入数据到page,实现流程:使能NAND FLASH的写功能->发送载入命令0x10->发送写入的page号
  • 查询OIP标志等待完成
C
/*!
\brief send the program load command,write data to cache
\param[in] buffer: the data of array
\param[in] address_in_page: the address in nandflash page
\param[in] byte_cnt: the number of data
\param[out] none
\retval none
*/
void bsp_spi_nandflash_program_load(uint8_t *buffer,uint16_t address_in_page,uint32_t byte_cnt)
{
uint32_t i=0;

/* select the flash: chip select low */
bsp_spi_nand_cs_low();
#ifdef SPI_NANDFLASH
/* send "PAGE READ" command */
driver_spi_master_transmit_receive_byte(&BOARD_SPI,SPI_NAND_PAGE_LOAD);
/* send the serial number of page */
driver_spi_master_transmit_receive_byte(&BOARD_SPI,(address_in_page>>8)&0xFF);
driver_spi_master_transmit_receive_byte(&BOARD_SPI,address_in_page&0xFF);
#endif


/* deselect the flash: chip select high */


for(i=0;i driver_spi_master_transmit_receive_byte(&BOARD_SPI,*buffer++);
}
//printf("cache program %x %x\n\r",m32record[0],m32record[1]);

bsp_spi_nand_cs_high();
qspi_disable(BOARD_SPI.spi_x);
}

/*!
\brief send the program excute command
\param[in] page_No: the serial number of nandflash page
\param[out] none
\retval none
*/
void bsp_spi_nandflash_program_execute(uint32_t page_No)
{
/* enable the write access to the flash */
bsp_spi_nandflash_write_enable();
/* select the flash: chip select low */
bsp_spi_nand_cs_low();
/* send "PAGE READ" command */
driver_spi_master_transmit_receive_byte(&BOARD_SPI,SPI_NAND_PROGRAM_EXEC);
/* send the serial number of page */
driver_spi_master_transmit_receive_byte(&BOARD_SPI,(page_No>>16)&0xFF);
driver_spi_master_transmit_receive_byte(&BOARD_SPI,(page_No>>8)&0xFF);
driver_spi_master_transmit_receive_byte(&BOARD_SPI,page_No&0xFF);
/* deselect the flash: chip select high */
bsp_spi_nand_cs_high();
}

/*!
\brief write the data to nandflash
\param[in] *buffer:the data of array
\param[in] page_No: the serial number of nandflash page
\param[in] address_in_page: the address of nandflash page
\param[in] byte_cnt:the number of data
\param[out] none
\retval SPI_NAND_FAIL,SPI_NAND_SUCCESS
*/
uint8_t spi_nandflash_write_data(uint8_t *buffer,uint32_t page_No,uint16_t address_page,uint32_t byte_cnt)
{


/*sned the program load command,write data to cache*/
bsp_spi_nandflash_program_load(buffer, address_page, byte_cnt);
/*sned the program excute command*/
bsp_spi_nandflash_program_execute(page_No);
/* Check program result */
while(bsp_spi_nandflash_get_status_flag(OIP)==SPI_NAND_BUSY);


#ifdef WRITE_PAGE_VERIFY_EN
spi_nandflash_read_data (tem_buffer,page_No, address_page, byte_cnt);
if (memcmp(tem_buffer, buffer, byte_cnt) != 0){
return SUCCESS;
}
#endif
return 1;

}
  • NOR FLASH按page读取函数spi_nandflash_read_data,输入读取数据指针、page号、page内地址偏移、读取长度;该函数流程是:
  • 读page到缓冲区,实现流程:向NOR FLASH发送写缓冲区指令0x13->送要读取的page号
  • 等待OIP标志(NAND读取page到缓冲区完成)
  • 从缓冲区读取数据,实现流程:发送读cache命令0x03->发送要读取的page地址偏移->读取所需长度的数据
  • 查询是否有ecc错误
C
/*!
\brief send the read page command
\param[in] page_No: the serial number of nandflash page
\param[out] none
\retval none
*/
void bsp_spi_nandflash_page_read(uint32_t page_No)
{
/* select the flash: chip select low */
bsp_spi_nand_cs_low();
/* send "PAGE READ" command */
driver_spi_master_transmit_receive_byte(&BOARD_SPI,SPI_NAND_PAGE_READ);
/* send the serial number of page */
driver_spi_master_transmit_receive_byte(&BOARD_SPI,(page_No>>16)&0xFF);
driver_spi_master_transmit_receive_byte(&BOARD_SPI,(page_No>>8)&0xFF);
driver_spi_master_transmit_receive_byte(&BOARD_SPI,page_No&0xFF);
/* deselect the flash: chip select high */
bsp_spi_nand_cs_high();
}

/*!
\brief send the read cache command
\param[in] buffer: a pointer to the array
\param[in] address_in_page: the address in nandflash page
\param[in] byte_cnt: the number of data
\param[out] none
\retval none
*/
void bsp_spi_nandflash_read_cache(uint8_t *buffer,uint16_t address_in_page,uint32_t byte_cnt)
{
uint32_t i=0;

/* select the flash: chip select low */
bsp_spi_nand_cs_low();
#ifdef SPI_NANDFLASH
/* send "PAGE READ" command */
driver_spi_master_transmit_receive_byte(&BOARD_SPI,SPI_NAND_READ_CACHE);
//driver_spi_master_transmit_receive_byte(&BOARD_SPI,DUMMY_BYTE);//Q4UC ++ Q5 --
/* send the address of page */
driver_spi_master_transmit_receive_byte(&BOARD_SPI,(address_in_page>>8)&0xFF);
driver_spi_master_transmit_receive_byte(&BOARD_SPI,address_in_page&0xFF);
driver_spi_master_transmit_receive_byte(&BOARD_SPI,DUMMY_BYTE);//Q4UC -- Q5 ++

#endif



for(i=0;i *buffer++=driver_spi_master_transmit_receive_byte(&BOARD_SPI,DUMMY_BYTE);
}

/* deselect the flash: chip select high */
bsp_spi_nand_cs_high();
qspi_disable(BOARD_SPI.spi_x);
}

/*!
\brief read the data from nandflash
\param[in] *buffer:the data of array
\param[in] page_No: the serial number of nandflash page
\param[in] address_in_page: the address in nandflash page
\param[in] byte_cnt:the number of data
\param[out] none
\retval SPI_NAND_FAIL,SPI_NAND_SUCCESS
*/
uint8_t spi_nandflash_read_data(uint8_t *buffer,uint32_t page_No,uint32_t address_in_page,uint32_t byte_cnt)
{
uint8_t result = SPI_NAND_SUCCESS;
uint8_t status = 0;
uint8_t retrycnt = 0;

/* the capacity of page must be equal or greater than the taotal of address_in_page and byte_cnt */
if((address_in_page+byte_cnt)>SPI_NAND_PAGE_TOTAL_SIZE){
return SPI_NAND_FAIL;
}
ReadRetry:
/* send the read page command */
bsp_spi_nandflash_page_read(page_No);
/* wait for NANDFLASH is ready */
while(bsp_spi_nandflash_get_status_flag(OIP)==SPI_NAND_BUSY);
/* read data from cache */
bsp_spi_nandflash_read_cache(buffer, address_in_page, byte_cnt);

bsp_spi_nandflash_get_feature( STATUS, &status );
if(( (status & ECCS0) == 0 )&&( (status & ECCS1) == ECCS1 )){ //UECC
if(retrycnt < 3)
{
retrycnt++;

printf("\rReadretry:%x %x\n",retrycnt,page_No);

goto ReadRetry;
}
else
{
printf("\rRead Fail %x\n",page_No);
}
}
return result;
}

20.4.3main函数实现

main函数中实现了擦除一个block,并对该block中的page进行写入操作,然后读取后进行数据对比校验的功能。

C
/*!
* 说明 main函数
* 输入 无
* 输出 无
* 返回值 无
*/
int main(void)
{

//延时、共用驱动部分初始化
driver_init();

//初始化LED组和默认状态
bsp_led_group_init();
bsp_led_on(&LED0);
bsp_led_off(&LED1);

//初始化UART打印
bsp_uart_init(&BOARD_UART);

//初始化SPI
bsp_spi_init(&BOARD_SPI);

//初始化SPI NAND
bsp_spi_nand_init();

printf("\n\rSPI NAND:GD5F1G configured...\n\r");

//读取flash id
flash_id=bsp_spi_nandflash_read_id();
printf("\n\rThe NAND_ID:0x%X\n\r",flash_id);

//比对flash id是否一致
if(NAND_ID != flash_id)
{
printf("\n\r\n\rWrite to tx_buffer:\n\r\n\r");

//准备数据
for(uint16_t i = 0; i < BUFFER_SIZE; i ++){
tx_buffer[i] = i;
printf("0x%02X ",tx_buffer[i]);

if(15 == i%16)
printf("\n\r");
}

printf("\n\r\n\rRead from rx_buffer:\n\r");

//擦除要写入的block
bsp_nandflash_block_erase(0);
//写入数据
bsp_nandflash_page_program((uint8_t*)tx_buffer,0,0,0);

//回读写入数据
bsp_nandflash_page_read(rx_buffer,0,0);

/* printf rx_buffer value */
for(uint16_t i = 0; i <= 255; i ++){
printf("0x%02X ", rx_buffer[i]);
if(15 == i%16)
printf("\n\r");
}

//比较回读和写入数据
if(ERROR == memory_compare(tx_buffer,rx_buffer,BUFFER_SIZE)){
printf("Err:Data Read and Write aren't Matching.\n\r");
/* spi flash read id fail */
printf("\n\rSPI nand: Read ID Fail!\n\r");

//写入错误
/* turn off all leds */
bsp_led_on(&LED0);
/* turn off all leds */
bsp_led_on(&LED1);
while(1);
}else{
printf("\n\rSPI-GD5F1G Test Passed!\n\r");
}
}else{ //ID读取错误
/* spi flash read id fail */
printf("\n\rSPI Nand:Read ID Fail!\n\r");
/* turn off all leds */
bsp_led_on(&LED0);
/* turn off all leds */
bsp_led_on(&LED1);
while(1);
}

while(1){
/* turn off all leds */
bsp_led_toggle(&LED0);
/* turn off all leds */
bsp_led_toggle(&LED1);
delay_ms(200);
}
}

20.5实验结果

nand读取到正确ID后开始擦写读流程,如果ID读取错误或者数据比对不通过点亮LED0,熄灭LED1,如果比对通过则交替闪烁LED0和LED1,通过USB转串口可以看到打印结果。

wKgaomZzim6ARlMMAAG4cbJ1vl0550.png

教程GD32 MCU方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 单片机
    +关注

    关注

    6032

    文章

    44513

    浏览量

    632689
  • NAND
    +关注

    关注

    16

    文章

    1677

    浏览量

    135999
  • FlaSh
    +关注

    关注

    10

    文章

    1621

    浏览量

    147735
  • SPI
    SPI
    +关注

    关注

    17

    文章

    1700

    浏览量

    91291
  • 开发板
    +关注

    关注

    25

    文章

    4933

    浏览量

    97158
收藏 人收藏

    评论

    相关推荐

    GD32F470紫藤开发板使用手册】第十一讲 SPI-SPI NOR FLASH读写实验

    通过本实验主要学习以下内容: •SPI简介 •GD32F470 SPI简介 •SPI NOR FLASH
    的头像 发表于 05-17 09:57 1642次阅读
    【<b class='flag-5'>GD32F</b>470紫藤<b class='flag-5'>派</b><b class='flag-5'>开发板</b><b class='flag-5'>使用手册</b>】第十一讲 <b class='flag-5'>SPI-SPI</b> NOR <b class='flag-5'>FLASH</b><b class='flag-5'>读写实验</b>

    GD32F303】星空介绍

    一、开发板介绍星空GD开发板是由旗点科技推出的一款GD32开发板,板载
    发表于 09-11 17:55

    【星空GD32F303开发板试用体验】开箱+环境搭建

    https://bbs.elecfans.com/jishu_2179209_1_1.html感谢 发烧友学院以及广州旗点智能科技有限公司为我和孩子提供此产品星空gd32F303开发板。收到了星空
    发表于 11-02 15:36

    【星空GD32F303开发板试用体验】+板卡概览

    本帖最后由 cooldog123pp 于 2021-11-6 21:07 编辑 星空GD开发板是由旗点科技推出的一款GD32开发板
    发表于 11-06 21:05

    【星空GD32F303开发板试用体验】文件读写与数码相框的实现 (兼结题报告)

    本帖最后由 jinglixixi 于 2021-11-27 01:23 编辑 星空开发板是旗点科技推出的一款以GD32F303ZET6为核心的开发板,该
    发表于 11-26 12:05

    星空GD32F303开发板的相关资料下载

    一、开发板介绍星空GD开发板是由旗点科技推出的一款GD32开发板,板载
    发表于 12-10 08:27

    GD32-Colibri-F207实验SPI_FLASH

    GD32-Colibri-F207实验SPI_FLASH,很好的GD32资料,快来学习吧。
    发表于 04-21 16:35 10次下载

    GD32F303固件库开发

    /qq_24312945/article/details/124325797] GD32F303固件库开发(2)----读保护与写保护 芯片读保护以后,flash将不可以从外部读取,这样可以防止别人读取或者盗取芯片代码,如果想再
    的头像 发表于 07-27 09:27 1104次阅读
    <b class='flag-5'>GD32F303</b>固件库<b class='flag-5'>开发</b>

    STM32CUBEMX开发GD32F303(17)----内部Flash读写

    本章STM32CUBEMX配置STM32F103,并且在GD32F303中进行开发,同时通过开发板内进行验证。 本例程主要讲解如何对芯片自带Fla
    的头像 发表于 07-27 09:35 1730次阅读
    STM32CUBEMX<b class='flag-5'>开发</b><b class='flag-5'>GD32F303</b>(17)----内部<b class='flag-5'>Flash</b><b class='flag-5'>读写</b>

    GD32F303红枫开发板使用手册第二 GPIO-流水灯实验

    GD32F303系列MCU最多可支持 112 个通用I/O 引脚(GPIO),分别为 PA0 ~ PA15, PB0 ~ PB15, PC0 ~ PC15,PD0 ~ PD15, PE0
    的头像 发表于 05-29 10:02 1509次阅读
    【<b class='flag-5'>GD32F303</b><b class='flag-5'>红枫</b><b class='flag-5'>派</b><b class='flag-5'>开发板</b><b class='flag-5'>使用手册</b>】<b class='flag-5'>第二</b><b class='flag-5'>讲</b> GPIO-流水灯<b class='flag-5'>实验</b>

    GD32F303红枫开发板使用手册】第五 FMC-片内Flash擦写读实验

    MC即Flash控制器,其提供了片上Flash操作所需要的所有功能,在GD32F303系列MCU中,Flash前256K字节空间内, CPU执行指令零等待,具有相同主频下最快的代码执行
    的头像 发表于 06-02 10:05 706次阅读
    【<b class='flag-5'>GD32F303</b><b class='flag-5'>红枫</b><b class='flag-5'>派</b><b class='flag-5'>开发板</b><b class='flag-5'>使用手册</b>】第五<b class='flag-5'>讲</b> FMC-片内<b class='flag-5'>Flash</b>擦写读<b class='flag-5'>实验</b>

    GD32H757Z海棠开发板使用手册】第十一讲 SPI-SPI NOR FLASH读写实验

    通过本实验主要学习以下内容: •SPI简介 •GD32H7 SPI简介 •SPI NOR FLASH
    的头像 发表于 06-04 11:42 858次阅读
    【<b class='flag-5'>GD</b>32H757Z海棠<b class='flag-5'>派</b><b class='flag-5'>开发板</b><b class='flag-5'>使用手册</b>】第十一讲 <b class='flag-5'>SPI-SPI</b> NOR <b class='flag-5'>FLASH</b><b class='flag-5'>读写实验</b>

    GD32F303红枫开发板使用手册】第十九 SPI-SPI NOR FLASH读写实验

    SPI(Serial Peripheral interface),顾名思义是串行外设接口,和UART不同的是,SPI是同步通讯接口,所以带有时钟线,而UART是异步通讯接口,不需要时钟线。 SPI通常使用4根线,分别为SCK、M
    的头像 发表于 06-19 10:12 1048次阅读
    【<b class='flag-5'>GD32F303</b><b class='flag-5'>红枫</b><b class='flag-5'>派</b><b class='flag-5'>开发板</b><b class='flag-5'>使用手册</b>】第十九<b class='flag-5'>讲</b> <b class='flag-5'>SPI-SPI</b> NOR <b class='flag-5'>FLASH</b><b class='flag-5'>读写实验</b>

    GD32F303红枫开发板使用手册第二十三讲 SDIO-SD卡读写实验

    通过本实验主要学习以下内容: •SDIO操作原理 •SD卡读写实
    的头像 发表于 06-23 10:49 541次阅读
    【<b class='flag-5'>GD32F303</b><b class='flag-5'>红枫</b><b class='flag-5'>派</b><b class='flag-5'>开发板</b><b class='flag-5'>使用手册</b>】<b class='flag-5'>第二十</b>三讲 SDIO-SD卡<b class='flag-5'>读写实验</b>

    GD32F303红枫开发板使用手册第二十 USB-虚拟键盘实验

    ,传输速度也很快,这些特性使支持USB接口的电子设备更易用、更大众化。GD32F303系列MCU集成了USB2.0全速设备USBD模块,可以满足作为USB设备与主机
    的头像 发表于 06-27 09:42 706次阅读
    【<b class='flag-5'>GD32F303</b><b class='flag-5'>红枫</b><b class='flag-5'>派</b><b class='flag-5'>开发板</b><b class='flag-5'>使用手册</b>】<b class='flag-5'>第二十</b>七<b class='flag-5'>讲</b>  USB-虚拟键盘<b class='flag-5'>实验</b>