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結構示例
如上圖所示,以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的命令表,常用的命令為擦除、編程、讀取命令。
- block擦除命令
- 編程
- 編程流程
- 先用數據緩存寫入指令將數據寫入緩沖區
- 然后發送寫使能命令,并確認寫使能成功
- 然后發送數據載入命令執行緩沖區數據到FLASH的寫
- 最后查詢讀寄存器確認P_FAIL是否有錯,OIP是否完成
注意(84h/C4h/34h) 和(FFh)指令是不會清除緩存中的內容的,所以下次編程時要注意是否緩存區都是需要更新的數據,所以必須是一次更新整個緩沖區,不要部分更新。
編程page地址按照塊的順序
- 數據緩存寫入命令
- 數據載入命令
- 讀取
- 讀取流程
- 讀需要先通過讀cache命令從FLASH中讀出數據到緩存中
- 然后通過讀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.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轉串口可以看到打印結果。
-
單片機
+關注
關注
6035文章
44554瀏覽量
634634 -
NAND
+關注
關注
16文章
1681瀏覽量
136118 -
FlaSh
+關注
關注
10文章
1633瀏覽量
147939 -
SPI
+關注
關注
17文章
1706瀏覽量
91502 -
開發板
+關注
關注
25文章
5032瀏覽量
97371
發布評論請先 登錄
相關推薦
評論