[研电赛技术支持] 【GD32F303红枫派使用手册】第二十讲 SPI-SPI NAND FLASH读写实验

[复制链接]
4331|0
 楼主| 聚沃科技 发表于 2024-6-20 09:39 | 显示全部楼层 |阅读模式
本帖最后由 聚沃科技 于 2024-6-20 09:40 编辑

红枫派首图.png
20.1 实验内容
通过本实验主要学习以下内容:
• SPI通信协议,参考19.2.1东方红开发板使用手册
• GD32F303 SPI操作方式,参考19.2.2东方红开发板使用手册
• NAND FLASH基本原理
• SPI NAND介绍
使用GD32F303 SPI接口实现对GD5F1GQ5UEYIGY的读写操作
20.2 实验原理
20.2.1 NAND FLASH基本原理
NAND FlashNOR Flash都是两种非易失性存储器,其读写速度、读写方式,存储区结构、成本、容量、擦写寿命都有很大区别。NAND在寿命、速度、读写方式上都不如NOR,但在成本和容量上有很大区别,故而决定了大容量数据存储是NAND的主要应用领域,而快速启动、快速数据读取等场景是NOR的主要应用领域。而SPI是目前NANDNOR的主要通信接口形式,降低了器件体积,标准化了器件接口。
• NAND Flash结构示例
图片1.png
如上图所示,以GD5F1GQ5UEYIGY为例,一个1Gb的存储结构下是由1024block组成,每个block64page组成,每个page2K Main Area+Spare Area(ECC ON:64B;ECC OFF:128B)组成。
NAND的擦除单位是blocks,写入单位是page,所以寻址的方式上和nor是有本质区别的,需要按blockspagepage字节偏移地址进行一个数据的寻址。
20.2.2 SPI NAND介绍
SPI NAND简化了NAND的接口设计和尺寸,SPI接口更是降低了主控对接口的要求,同时内置ECC。下图是GD5F1GQ5UEYIGY的命令表,常用的命令为擦除、编程、读取命令。
20.4.3 main函数实现
main函数中实现了擦除一个block,并对该block中的page进行写入操作,然后读取后进行数据对比校验的功能。 图片2.png
block擦除命令
图片3.png
编程
编程流程
i. 先用数据缓存写入指令将数据写入缓冲区
ii. 然后发送写使能命令,并确认写使能成功
iii. 然后发送数据载入命令执行缓冲区数据到FLASH的写
iv. 最后查询读寄存器确认P_FAIL是否有错,OIP是否完成
注意(84h/C4h/34h) (FFh) 指令是不会清除缓存中的内容的,所以下次编程时要注意是否缓存区都是需要更新的数据,所以必须是一次更新整个缓冲区,不要部分更新。
编程page地址按照块的顺序
数据缓存写入命令
图片4.png
数据载入命令
图片5.png
读取
读取流程
i. 读需要先通过读cache命令从FLASH中读出数据到缓存中
ii. 然后通过读cache指令从缓冲区中开始读出数据
读到2048+128后绕回从0开始继续。
20.3 硬件设计
红枫派开发板SPI——NAND FLASH的硬件设计如下:
图片6.png
图片7.png
从图中可以看出,本实验使用的是普通单线SPIGD5F1GQ5UEYIGY的片选由GD32F303ZET6PG13控制(因PG14不是SPINSS管脚,所以本实验用主机NSS软件模式,,通过普通IO控制片选),GD25Q32ESIGRSOSISCLK分别和GD32F303ZET6PB4SPI2_MISO)、PB5SPI2_MOSI)以及PB3SPI2_CLK)相连。
20.4 代码解析
20.4.1 SPI初始化和读写BYTE函数实现
SPI初始化配置流程可参考19.4.1东方红开发板使用手册
SPI读写BYTE函数实现可参考19.4.2东方红开发板使用手册
20.4.2 SPI NAND FLASH BSP驱动层实现
操作NAND FLASH的函数都定义在bsp层文件bsp_spi_nand.c中,这个文件中定义的函数都是针对NAND FLASH命令来实现的,我们选取几个函数进行介绍。
• NOR FLASHblock擦除函数bsp_nandflash_block_erase,输入block号即可擦除;该函数流程是:使能NAND FLASH的写功能->NOR FLASH发送block擦除指令0xD8->发送左移6位的Block NO->查询OIP标志等待完成
  1. C
  2. /*!
  3.     \brief      erase the nandflash blcok
  4.     \param[in]  block_No:the serial number of erase block
  5.     \param[out] none
  6.     \retval     SPI_NAND_FAIL: erase the nandflash block fail
  7.     \retval     SPI_NAND_SUCCESS: erase the nandflash block success
  8. */
  9. uint8_t bsp_spi_nandflash_block_erase(uint32_t block_No)
  10. {
  11.     uint8_t result = SPI_NAND_SUCCESS;

  12.     block_No<<=6;        //block_No=block_No*64
  13.     bsp_spi_nandflash_write_enable();
  14.     /* select the flash: chip select low */
  15.     bsp_spi_nand_cs_low();
  16.     /* send "ERASE BLOCK" command */
  17.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,SPI_NAND_BLOCK_ERASE);
  18.     /* send the address of memory */
  19.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,(block_No>>16)&0xFF);
  20.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,(block_No>>8)&0xFF);
  21.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,block_No&0xFF);
  22.     /* deselect the flash: chip select high */
  23.     bsp_spi_nand_cs_high();
  24.     while(bsp_spi_nandflash_get_status_flag(OIP)==SPI_NAND_BUSY);
  25.     /* check program result */        

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

  13.     /* select the flash: chip select low */
  14.     bsp_spi_nand_cs_low();
  15. #ifdef SPI_NANDFLASH
  16.     /* send "PAGE READ" command */
  17.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,SPI_NAND_PAGE_LOAD);
  18.     /* send the serial number of page */
  19.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,(address_in_page>>8)&0xFF);
  20.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,address_in_page&0xFF);
  21. #endif
  22.    

  23.     /* deselect the flash: chip select high */

  24.         
  25.     for(i=0;i<byte_cnt;i++){
  26.         driver_spi_master_transmit_receive_byte(&BOARD_SPI,*buffer++);
  27.     }
  28.     //printf("cache program %x %x\n\r",m32record[0],m32record[1]);
  29.         
  30.     bsp_spi_nand_cs_high();
  31.     qspi_disable(BOARD_SPI.spi_x);
  32. }

  33. /*!
  34.     \brief      send the program excute command
  35.     \param[in]  page_No: the serial number of nandflash page
  36.     \param[out] none
  37.     \retval     none
  38. */
  39. void bsp_spi_nandflash_program_execute(uint32_t page_No)
  40. {
  41.     /* enable the write access to the flash */
  42.     bsp_spi_nandflash_write_enable();
  43.     /* select the flash: chip select low */
  44.     bsp_spi_nand_cs_low();
  45.     /* send "PAGE READ" command */
  46.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,SPI_NAND_PROGRAM_EXEC);
  47.     /* send the serial number of page */
  48.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,(page_No>>16)&0xFF);
  49.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,(page_No>>8)&0xFF);
  50.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,page_No&0xFF);
  51.     /* deselect the flash: chip select high */
  52.     bsp_spi_nand_cs_high();
  53. }

  54. /*!
  55.     \brief      write the data to nandflash
  56.     \param[in]  *buffer:the data of array
  57.     \param[in]  page_No: the serial number of nandflash page
  58.     \param[in]  address_in_page: the address of nandflash page
  59.     \param[in]  byte_cnt:the number of data
  60.     \param[out] none
  61.     \retval     SPI_NAND_FAIL,SPI_NAND_SUCCESS
  62. */
  63. uint8_t spi_nandflash_write_data(uint8_t *buffer,uint32_t page_No,uint16_t address_page,uint32_t byte_cnt)
  64. {


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

  71.    
  72. #ifdef WRITE_PAGE_VERIFY_EN
  73.      spi_nandflash_read_data (tem_buffer,page_No, address_page, byte_cnt);
  74.     if (memcmp(tem_buffer, buffer,  byte_cnt) != 0){
  75.         return SUCCESS;
  76.     }
  77. #endif
  78.     return 1;

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

  21. /*!
  22.     \brief      send the read cache command
  23.     \param[in]  buffer: a pointer to the array
  24.     \param[in]  address_in_page: the address in nandflash page
  25.     \param[in]  byte_cnt: the number of data
  26.     \param[out] none
  27.     \retval     none
  28. */
  29. void bsp_spi_nandflash_read_cache(uint8_t *buffer,uint16_t address_in_page,uint32_t byte_cnt)
  30. {
  31.     uint32_t i=0;

  32.     /* select the flash: chip select low */
  33.     bsp_spi_nand_cs_low();
  34. #ifdef SPI_NANDFLASH
  35.     /* send "PAGE READ" command */
  36.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,SPI_NAND_READ_CACHE);
  37.     //driver_spi_master_transmit_receive_byte(&BOARD_SPI,DUMMY_BYTE);//Q4UC ++ Q5 --
  38.     /* send the address of page */
  39.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,(address_in_page>>8)&0xFF);
  40.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,address_in_page&0xFF);
  41.     driver_spi_master_transmit_receive_byte(&BOARD_SPI,DUMMY_BYTE);//Q4UC -- Q5 ++
  42.    
  43. #endif


  44.    
  45.     for(i=0;i<byte_cnt;i++){
  46.         *buffer++=driver_spi_master_transmit_receive_byte(&BOARD_SPI,DUMMY_BYTE);
  47.     }
  48.    
  49.     /* deselect the flash: chip select high */
  50.     bsp_spi_nand_cs_high();
  51.     qspi_disable(BOARD_SPI.spi_x);
  52. }

  53. /*!
  54.     \brief      read the data from nandflash
  55.     \param[in]  *buffer:the data of array
  56.     \param[in]  page_No: the serial number of nandflash page
  57.     \param[in]  address_in_page: the address in nandflash page
  58.     \param[in]  byte_cnt:the number of data
  59.     \param[out] none
  60.     \retval     SPI_NAND_FAIL,SPI_NAND_SUCCESS
  61. */
  62. uint8_t spi_nandflash_read_data(uint8_t *buffer,uint32_t page_No,uint32_t address_in_page,uint32_t byte_cnt)
  63. {
  64.     uint8_t result = SPI_NAND_SUCCESS;
  65.     uint8_t status = 0;
  66.     uint8_t retrycnt = 0;

  67.     /* the capacity of page must be equal or greater than the taotal of address_in_page and byte_cnt */
  68.     if((address_in_page+byte_cnt)>SPI_NAND_PAGE_TOTAL_SIZE){
  69.         return SPI_NAND_FAIL;
  70.     }
  71. ReadRetry:
  72.     /* send the read page command */
  73.     bsp_spi_nandflash_page_read(page_No);
  74.     /* wait for NANDFLASH is ready */
  75.     while(bsp_spi_nandflash_get_status_flag(OIP)==SPI_NAND_BUSY);
  76.     /* read data from cache */
  77.     bsp_spi_nandflash_read_cache(buffer, address_in_page, byte_cnt);
  78.    
  79.     bsp_spi_nandflash_get_feature( STATUS, &status );
  80.     if(( (status & ECCS0) == 0 )&&( (status & ECCS1) == ECCS1 )){    //UECC
  81.         if(retrycnt < 3)
  82.         {
  83.             retrycnt++;

  84.             printf("\rReadretry:%x %x\n",retrycnt,page_No);
  85.       
  86.             goto ReadRetry;
  87.         }
  88.         else
  89.         {
  90.             printf("\rRead Fail %x\n",page_No);
  91.         }      
  92.     }            
  93.     return result;
  94. }
20.4.3 main函数实现
main函数中实现了擦除一个block,并对该block中的page进行写入操作,然后读取后进行数据对比校验的功能。
  1. C
  2. /*!
  3. * 说明     main函数
  4. * 输入     无
  5. * 输出     无
  6. * 返回值   无
  7. */
  8. int main(void)
  9. {
  10.    
  11.     //延时、共用驱动部分初始化
  12.     driver_init();
  13.          
  14.     //初始化LED组和默认状态
  15.     bsp_led_group_init();
  16.     bsp_led_on(&LED0);
  17.     bsp_led_off(&LED1);     

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

  20.     //初始化SPI   
  21.     bsp_spi_init(&BOARD_SPI);
  22.    
  23.     //初始化SPI NAND         
  24.     bsp_spi_nand_init();
  25.   
  26.     printf("\n\rSPI NAND:GD5F1G configured...\n\r");

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

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

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

  38.             if(15 == i%16)
  39.                 printf("\n\r");
  40.         }

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

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

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

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

  54.         //比较回读和写入数据        
  55.         if(ERROR == memory_compare(tx_buffer,rx_buffer,BUFFER_SIZE)){
  56.             printf("Err:Data Read and Write aren't Matching.\n\r");
  57.             /* spi flash read id fail */
  58.             printf("\n\rSPI nand: Read ID Fail!\n\r");
  59.             
  60.             //写入错误            
  61.             /* turn off all leds */
  62.             bsp_led_on(&LED0);
  63.             /* turn off all leds */
  64.             bsp_led_on(&LED1);           
  65.             while(1);
  66.         }else{
  67.             printf("\n\rSPI-GD5F1G Test Passed!\n\r");
  68.         }
  69.     }else{ //ID读取错误
  70.         /* spi flash read id fail */
  71.         printf("\n\rSPI Nand:Read ID Fail!\n\r");
  72.         /* turn off all leds */
  73.         bsp_led_on(&LED0);
  74.         /* turn off all leds */
  75.         bsp_led_on(&LED1);           
  76.         while(1);
  77.     }

  78.     while(1){
  79.         /* turn off all leds */
  80.         bsp_led_toggle(&LED0);
  81.         /* turn off all leds */
  82.         bsp_led_toggle(&LED1);        
  83.         delay_ms(200);
  84.     }
  85. }
20.5 实验结果
nand读取到正确ID后开始擦写读流程,如果ID读取错误或者数据比对不通过点亮LED0,熄灭LED1,如果比对通过则交替闪烁LED0LED1,通过USB转串口可以看到打印结果。
图片8.png
本教程由GD32 MCU方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网,GD32MCU技术交流群:859440462

您需要登录后才可以回帖 登录 | 注册

本版积分规则

170

主题

190

帖子

13

粉丝
快速回复 在线客服 返回列表 返回顶部