本帖最后由 聚沃科技 于 2024-6-20 09:40 编辑
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 Flash和NOR Flash都是两种非易失性存储器,其读写速度、读写方式,存储区结构、成本、容量、擦写寿命都有很大区别。NAND在寿命、速度、读写方式上都不如NOR,但在成本和容量上有很大区别,故而决定了大容量数据存储是NAND的主要应用领域,而快速启动、快速数据读取等场景是NOR的主要应用领域。而SPI是目前NAND和NOR的主要通信接口形式,降低了器件体积,标准化了器件接口。 • NAND Flash结构示例 如上图所示,以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.2 SPI NAND介绍 SPI NAND简化了NAND的接口设计和尺寸,SPI接口更是降低了主控对接口的要求,同时内置ECC。下图是GD5F1GQ5UEYIGY的命令表,常用的命令为擦除、编程、读取命令。 20.4.3 main函数实现 main 函数中实现了擦除一个block,并对该block中的page进行写入操作,然后读取后进行数据对比校验的功能。
• block擦除命令 • 编程 ○ 编程流程 i. 先用数据缓存写入指令将数据写入缓冲区 ii. 然后发送写使能命令,并确认写使能成功 iii. 然后发送数据载入命令执行缓冲区数据到FLASH的写 iv. 最后查询读寄存器确认P_FAIL是否有错,OIP是否完成 注意(84h/C4h/34h) 和(FFh) 指令是不会清除缓存中的内容的,所以下次编程时要注意是否缓存区都是需要更新的数据,所以必须是一次更新整个缓冲区,不要部分更新。 编程page地址按照块的顺序 ○ 数据缓存写入命令 ○ 数据载入命令 • 读取 ○ 读取流程 i. 读需要先通过读cache命令从FLASH中读出数据到缓存中 ii. 然后通过读cache指令从缓冲区中开始读出数据 读到2048+128后绕回从0开始继续。 20.3 硬件设计 红枫派开发板SPI——NAND FLASH的硬件设计如下: 从图中可以看出,本实验使用的是普通单线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.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 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<byte_cnt;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<byte_cnt;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.3 main函数实现 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转串口可以看到打印结果。 本教程由GD32 MCU方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网,GD32MCU技术交流群:859440462
|