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

发布者:HeavenlyJoy444最新更新时间:2024-11-18 来源: elecfans关键字:GD32F303  NAND  FLASH  读写实验 手机看文章 扫描二维码
随时随地手机看文章

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 %xnr',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 %xn',retrycnt,page_No);


goto ReadRetry;

}

else

{

printf('rRead Fail %xn',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('nrSPI NAND:GD5F1G configured...nr');


//读取flash id

flash_id=bsp_spi_nandflash_read_id();

printf('nrThe NAND_ID:0x%Xnr',flash_id);


//比对flash id是否一致

if(NAND_ID != flash_id)

{

printf('nrnrWrite to tx_buffer:nrnr');


//准备数据

for(uint16_t i = 0; i < BUFFER_SIZE; i ++){

tx_buffer[i] = i;

printf('0x%02X ',tx_buffer[i]);


if(15 == i%16)

printf('nr');

}


printf('nrnrRead from rx_buffer:nr');


//擦除要写入的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('nr');

}


//比较回读和写入数据

if(ERROR == memory_compare(tx_buffer,rx_buffer,BUFFER_SIZE)){

printf('Err:Data Read and Write aren't Matching.nr');

/* spi flash read id fail */

printf('nrSPI nand: Read ID Fail!nr');


//写入错误

/* turn off all leds */

bsp_led_on(&LED0);

/* turn off all leds */

bsp_led_on(&LED1);

while(1);

}else{

printf('nrSPI-GD5F1G Test Passed!nr');

}

}else{ //ID读取错误

/* spi flash read id fail */

printf('nrSPI Nand:Read ID Fail!nr');

/* turn off all leds */

bsp_led_on(&LED0);

/* turn off all leds */

bsp_led_on(&LED1);

[1] [2]
关键字:GD32F303  NAND  FLASH  读写实验 引用地址:【GD32F303红枫派开发板使用手册】第二十讲 SPI-SPI NAND FLASH读写实验

上一篇:GD32F303固件库开发(13)----定时器TIM捕获PWM测量频率与占空比
下一篇:GD32330C-START开发板试用体验:GD32F330开发环境构建

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved