NandFlash目录
- 1. 硬件描述
-
- 1.1 MT29F4G08
- 1.2 H27(U_S)4G8_6F2D
- 1.3 芯片引脚描述
- 1.4 STM32 nandflash原理图
- 1.5 FMC存储区域
- 1.6 时序图
-
- 1.6.1 Table 28: AC Timing Characteristics
- 1.6.2 Figure 5: Command Latch Cycle
- 1.6.3 Figure 6: Address Latch Cycle
- 1.6.4 Figure 7: Input Data Latch Cycle
- 1.6.5 Figure 8: Sequential Out Cycle after Read
- 1.6.6 Figure 10: Status / EDC Read Cycle
- 1.6.7 Figure 12: Read Operation (Read One Page)
- 1.6.8 Figure 14: Random Data Output
- 1.6.9 Figure 15: Page Program Operation
- 1.6.10 Figure 16: Random Data In
- 1.6.11 Figure 19: Block Erase Operation (Erase One Block)
- 1.6.12 Figure 20: Multiple plane page program
- 1.6.13 Figure 27: ID Read
- 1.6.14 Figure 29: Reset operation timing
- 1.6.15 ...
- 1.7 时序图解析
- 1.8 命令列表
- 2. HAL库解析
- 3 STM32F429 cubemx的配置
- 4 驱动代码
写这篇文章的时候,我已经花了三天搞这个东西了。期间遇到两个问题,搞了好久才解决:
1、生成的代码全速跑,弹框提示 Could not stop Coretx-M device!Please check the JTAG cable。
2、ID可以读出,但是数据写进去与读出来不一致,出现错位。
上面问题我网上搜索了很久,很多人遇到的问题,但是只是提,没人答。
先介绍两颗芯片
1. 硬件描述
1.1 MT29F4G08
由图可知:MT29F4G08 由 2 个 plane 组成,每个 plane 有 2048 个 block,每个 block 由 64 个 page 组成,每个 page 有 2K+64 字节(2112 字节)的存储容量。所以,MT29F4G08 的总容 量为:2 * 2048 * 64*(2K+64)= 553648128 字节(512MB)。其中,plane、block、page 等的个数根据 NAND FLASH 型号的不同,会有所区别,大家注意查看对应 NAND FLASH 芯片的数据
手册。
2plane =2 * 2048block=2 * 2048 * 64page
1page=2K+64Byte 2K: 数据区data area, 64Byte:备用区spare area。
NAND FLASH 的 page 由 2 部分组成:数据存储区(data area)和备用区域(spare area),
对 MT29F4G08 来说,数据存储区大小为 2K 字节,备用区域大小为 64 字节。我们存储的有效 数据,一般都是存储在数据存储区(data area)。备用区域(spare area),一般用来存放 ECC(Error Checking and Correcting)校验值,可利用这个区域,来实现 NAND FLASH 坏块管理和磨损均衡。
1.2 H27(U_S)4G8_6F2D
H27(U_S)4G8_6F2Dseries is a 512Mx8bit with spare 16Mx8 bit capacity.
数据区:512M8bit
备用去:16M8bit
- X8: (2K + 64) bytes x 64 pages x 4096 blocks
- X16: (1k+32) words x 64 pages x 2048 blocks
PAGE SIZE
- X8: (2048 + 64 spare) bytes
- X16:(1024 + 32spare) Words
-
Multiplane Architecture
-
Array is split into two independent planes.
Parallel operations on both planes are available, having
program and erase time.
从上面两颗芯片对比,你可以发现,结构、引脚、存储方法、内存大小等等和MT29F4G08是一模一样的,不一样可能就是里面某些性能参数。
1.3 芯片引脚描述
开发板NANDFALSH芯片:H27U4G8F2ETR-BI
4Gbit Nandflash
上图是常见的Nand Flash所拥有的引脚(Pin)所对应的功能,简单翻译如下:
1、I/O0 ~ I/O7:I/O0 ~ I/O7:用于输入地址/数据/命令,输出数据。
2、CLE:Command Latch Enable,命令锁存使能,在输入命令之前,要先在模式寄存器中,设置CLE使能。
3、ALE:Address Latch Enable,地址锁存使能,在输入地址之前,要先在模式寄存器中,设置ALE使能
4、CE#:Chip Enable,芯片使能,在操作Nand Flash之前,要先选中此芯片,才能操作。
5、RE#:Read Enable,读使能,在读取数据之前,要先使CE#有效。
6、WE#:Write Enable,写使能,在写取数据之前,要先使WE#有效。
7、WP#:Write Protect,写保护
8、R/B#:Ready/Busy Output,就绪/忙,主要用于在发送完编程/擦除命令后,检测这些操作是否完成,忙,表示编程/擦除操作仍在进行中,就绪表示操作完成.
9、Vcc:Power,电源
10、Vss:Ground,接地
11、N.C:Non-Connection,未定义,未连接
(字母上面带横杠说明引脚是低电平有效,为书写方便,在字母后面加“#”表示)
1.4 STM32 nandflash原理图
根据STM32F7中文参考手册如下:
存储芯片与单片机的关系
如下编程操作和读操作的时序。首先先拉低NCE片选,只有选中芯片才能让他工作。
Nand Flash是通过 ALE/CLE 来区分数据线上的数据是命令(CLE有效),地址(ALE有效)还是数据(CLE/ALE都无效)。通过NWE/NRE来区分数据线上的数据是写操作(NWE有效)还是读操作(NRE有效)。
1.5 FMC存储区域
FMC的存储区域外部储存分配图如图。其中Nand Flash为储存区域3,4x64MB。起始地址为0x8000 0000。
1.6 时序图
一下时H27U4G8F2E芯片资料上的截图:
1.6.1 Table 28: AC Timing Characteristics
1.6.2 Figure 5: Command Latch Cycle
1.6.3 Figure 6: Address Latch Cycle
1.6.4 Figure 7: Input Data Latch Cycle
1.6.5 Figure 8: Sequential Out Cycle after Read
1.6.6 Figure 10: Status / EDC Read Cycle
1.6.7 Figure 12: Read Operation (Read One Page)
1.6.8 Figure 14: Random Data Output
1.6.9 Figure 15: Page Program Operation
1.6.10 Figure 16: Random Data In
1.6.11 Figure 19: Block Erase Operation (Erase One Block)
1.6.12 Figure 20: Multiple plane page program
1.6.13 Figure 27: ID Read
1.6.14 Figure 29: Reset operation timing
1.6.15 …
还有其他很大时序图,请参考芯片手册
1.7 时序图解析
从上面图中命令序列:
命令1# + Col.Addr1 + Col.Addr2 + Row.Addr1 + Row.Addr2 + Row.Addr3 +命令2#
Col:列地址(等价于页里具体地址)
Row:行地址(等价于总页地址)
NAND FLASH 的地址分为三类:
块地址(Block Address)、页地址(Page Address)和列地址(Column Address)。以 MT29F4G08 为例,这三个地址,通过 5 个周期发送,如表 46.1.1.2所示:
表中,CA0~CA11 为列地址(Column Address),用于在一个 Page 内部寻址,MT29F4G08的一个 Page 大小为 2112 字节,需要 12 个地址线寻址;PA0~PA5 为页地址(Page Address),用于在一个 Block 内部寻址,MT29F4G08 一个 Block 大小为 64 个 Page,需要 6 个地址线寻址;BA6~BA17 为块地址(Block Address),用于块寻址,MT29F4G08 总共有 4096 个Block,需要12 根地址线寻址。
整个寻址过程,分 5 次发送(5 个周期),首先发送列地址,在发送页地址和块地址。这里提醒一下:块地址和页地址,其实是可以写在一起的,由一个参数传递即可,所以表中的 BA并不是由 BA0 开始的,大家可以理解为这个地址(PA+BA)为整个 NAND FLASH 的 Page 地址。在完成寻址以后,数据线 I/O0~ I/O7 来传输数据了。
举例:
该指令用于读取 NAND 的一个 Page(包括 spare 区数据,但不能跨页读),该指令时序如上图由图可知,READ PAGE 的命令分两次发送,首先发送 00H 命令,然后发送 5 次地址
(Block&Page&Column 地址),指定读取的地址,随后发送 30H 命令,在等待 RDY 后,即可读取 PAGE 里面的数据。注意:不能跨页读,所以最多一次读取一个 PAGE 的数据(包括 spare
区)。
1.8 命令列表
2. HAL库解析
2.1 NAND_DeviceConfigTypeDef
typedef struct
{
uint32_t PageSize; /*!< NAND memory page (without spare area) size measured in bytes
for 8 bits adressing or words for 16 bits addressing */
uint32_t SpareAreaSize; /*!< NAND memory spare area size measured in bytes
for 8 bits adressing or words for 16 bits addressing */
uint32_t BlockSize; /*!< NAND memory block size measured in number of pages */
uint32_t BlockNbr; /*!< NAND memory number of total blocks */
uint32_t PlaneNbr; /*!< NAND memory number of planes */
uint32_t PlaneSize; /*!< NAND memory zone size measured in number of blocks */
FunctionalState ExtraCommandEnable; /*!< NAND extra command needed for Page reading mode. This
parameter is mandatory for some NAND parts after the read
command (NAND_CMD_AREA_TRUE1) and before DATA reading sequence.
Example: Toshiba THTH58BYG3S0HBAI6.
This parameter could be ENABLE or DISABLE
Please check the Read Mode sequnece in the NAND device datasheet */
} NAND_DeviceConfigTypeDef;
芯片:H27U4G8F2ETR-BI 容量:(2K+64Byte)* 64 * 2048 * 2 =512M Byte
1、PageSize: 1页里出去备用区数据大小 :2048Byte。
2、SpareAreaSize: 1页备用区数据大小:64Byte。
3、BlockSize: NAND内存块大小(以页数为单位):64page
4、BlockNbr: 设备内存总块数:4096 block
5、PlaneNbr: 设备 plane数:2 plane
6、PlaneSize: 1plane多少Block(以块数为单位): 2048 block
2.2 库函数
HAL_StatusTypeDef HAL_NAND_Read_Page_8b(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress, uint8_t *pBuffer, uint32_t NumPageToRead);
HAL_StatusTypeDef HAL_NAND_Write_Page_8b(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress, uint8_t *pBuffer, uint32_t NumPageToWrite);
HAL_StatusTypeDef HAL_NAND_Read_SpareArea_8b(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress, uint8_t *pBuffer, uint32_t NumSpareAreaToRead);
HAL_StatusTypeDef HAL_NAND_Write_SpareArea_8b(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress, uint8_t *pBuffer, uint32_t NumSpareAreaTowrite);
HAL_StatusTypeDef HAL_NAND_Read_Page_16b(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress, uint16_t *pBuffer, uint32_t NumPageToRead);
HAL_StatusTypeDef HAL_NAND_Write_Page_16b(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress, uint16_t *pBuffer, uint32_t NumPageToWrite);
HAL_StatusTypeDef HAL_NAND_Read_SpareArea_16b(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress, uint16_t *pBuffer, uint32_t NumSpareAreaToRead);
HAL_StatusTypeDef HAL_NAND_Write_SpareArea_16b(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress, uint16_t *pBuffer, uint32_t NumSpareAreaTowrite);
HAL_StatusTypeDef HAL_NAND_Erase_Block(NAND_HandleTypeDef *hnand, NAND_AddressTypeDef *pAddress);
函数中第二个地址参数详解:
NAND_AddressTypeDef temp;//地址句柄
temp.Plane = 1; //(第几个plane 0-1)
temp.Block = 2047;//(第几个block 0-2047)
temp.Page = 63; //(第几个papge 0-63)
了解了这之后,基本上可以开始搞代码了
3 STM32F429 cubemx的配置
特别注意了:
当space timing in HCLK cycles那四个时间参数配置错了(上下是一样的),就会出现写读出来的数据移位,不一致的情况。至于怎么计算来的,我也不太懂。具体可以参考:仅供参考,不一定对。
仅供参考
我测出来可用的数据如下:至于什么关系,我也没搞多么清楚
这里面的值是生成代码里面static void MX_FMC_Init(void)函数
/* ComSpaceTiming */
ComSpaceTiming.SetupTime = 4;
ComSpaceTiming.WaitSetupTime = 4;
ComSpaceTiming.HoldSetupTime = 4;
ComSpaceTiming.HiZSetupTime = 3;
/* AttSpaceTiming */
AttSpaceTiming.SetupTime = 4;
AttSpaceTiming.WaitSetupTime = 4;
AttSpaceTiming.HoldSetupTime = 4;
AttSpaceTiming.HiZSetupTime = 3;
NAND 属性信息根据自己NANDFlash来填写,我的是
NAND=2plane =22048block=22048*64page
1page=2K+64Byte
2K: 数据区data area,
64Byte:备用区spare area。
Page size:2048 //一页多少字节数,不包含备用区
Spare area size:64 //一页备用区多少字节
Block size:64 //一个bock有多少page
Block number:4096 //Device 多少个block
Plane number:2 //Device 多少plane
Plane size:2048 //1plane多少block
这里也需要特别注意:FMC_NWAT是就绪/忙碌信号,低电平为忙碌,高电平为准备就绪。如果配置的时候不拉高,会弹框提示:Could not stop Coretx-M device!Please check the JTAG cable。程序卡死再读ID那个地方。
至于为什么会出现,那是因为R/B脚是低电平,NANDFLASH 没有准备就绪。所以读ID就死掉。经过测试发现它整个过程并没有对R/B脚操作,它一直默认为就就绪态,就是一直是高电平。所以配置的时候必须要高电平。
经过测试还发现,当执行HAL_NAND_Reset(&hnand1);复位函数时,R/B有0.2uS拉低的一个动作。
配置好后直接生成代码就可以了:
下面附上测试代码:
/* USER CODE BEGIN 2 */
bsp_init();
NAND_FLASH_TEST();
/* USER CODE END 2 */
结果就会出现,读出与写入一致.
4 驱动代码
测试代码:
bsp_nandflash.c
#include "bsp_nandflash.h"
#include "pub_delay.h"
#include <stdio.h>
#include <string.h>
//#include "malloc.h"
//NAND_HandleTypeDef NAND_Handler; //NAND FLASH句柄
nand_attriute nand_dev; //nand重要参数结构体
extern NAND_HandleTypeDef hnand1;
#define NAND_Handler hnand1
#define FMC_Bank3 FMC_Bank3_R
//初始化NAND FLASH
u8 NAND_Init(void)
{
NAND_IDTypeDef id;
NAND_MPU_Config();
HAL_NAND_Reset(&NAND_Handler);
nand_dev.id=NAND_ReadID();
//HAL_NAND_Read_ID(&NAND_Handler, &id);
printf("NAND ID:%#x\r\n",nand_dev.id);
NAND_ModeSet(4); //设置为MODE4,高速模式
if(nand_dev.id==MT29F16G08ABABA) //NAND为MT29F16G08ABABA
{
nand_dev.page_totalsize=4320;
nand_dev.page_mainsize=4096;
nand_dev.page_sparesize=224;
nand_dev.block_pagenum=128;
nand_dev.plane_blocknum=2048;
nand_dev.block_totalnum=4096;
}
else if(nand_dev.id==MT29F4G08ABADA)//NAND为MT29F4G08ABADA H27U4GBF
{
nand_dev.page_totalsize=2112;
nand_dev.page_mainsize=2048;
nand_dev.page_sparesize=64;
nand_dev.block_pagenum=64;
nand_dev.plane_blocknum=2048;
nand_dev.block_totalnum=4096;
}
else
return 1; //错误,返回
return 0;
}
//配置MPU的region
void NAND_MPU_Config(void)
{
MPU_Region_InitTypeDef MPU_Initure;
HAL_MPU_Disable(); //配置MPU之前先关闭MPU,配置完成以后在使能MPU
//配置RAM为region1,大小为256MB,此区域可读写
MPU_Initure.Enable=MPU_REGION_ENABLE; //使能region
MPU_Initure.Number=NAND_REGION_NUMBER; //设置region,NAND使用的region0
MPU_Initure.BaseAddress=NAND_ADDRESS_START; //region基地址
MPU_Initure.Size=NAND_REGION_SIZE; //region大小
MPU_Initure.SubRegionDisable=0X00;
MPU_Initure.TypeExtField=MPU_TEX_LEVEL0;
MPU_Initure.AccessPermission=MPU_REGION_FULL_ACCESS; //此region可读写
MPU_Initure.DisableExec=MPU_INSTRUCTION_ACCESS_ENABLE; //允许读取此区域中的指令
MPU_Initure.IsShareable=MPU_ACCESS_NOT_SHAREABLE;
MPU_Initure.IsCacheable=MPU_ACCESS_NOT_CACHEABLE;
MPU_Initure.IsBufferable=MPU_ACCESS_BUFFERABLE;
HAL_MPU_ConfigRegion(&MPU_Initure);
HAL_MPU_Enable(MPU_PRIVILEGED_DEFAULT); //开启MPU
}
//读取NAND FLASH的ID
//返回值:0,成功;
// 其他,失败
u8 NAND_ModeSet(u8 mode)
{
*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_FEATURE;//发送设置特性命令
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=0X01