本帖最后由 枫行幻幻 于 2018-4-17 09:07 编辑
现在需要做存储文件信息,没有做文件管理,ecc。
目前的错误现象:连续读后会出现错误位,断电重启后,错误位出现的块数位置会变化。系统采用STM32 的FSMC,192M系统时钟。
MCU型号:stm32f429IGT6
NAND FLASH型号:MT29F16G08CBACA
部分代码如下:
u8 NAND_Init(void){
u32 tempreg = 0;
// 初始化NAND FLASH相关IO口
RCC->AHB1ENR |= 3<<3;// 使能PD,PE
RCC->AHB1ENR |= 1<<6;// 使能PG
RCC->AHB3ENR |= 1<<0;// 使能FMC时钟
GPIO_Set(GPIOD,PIN6,GPIO_MODE_IN,0,0,GPIO_PUPD_PU);// PD6 上拉输入,NAND FLASH的R/B信号
GPIO_Set(GPIOD,(3<<0)|(3<<4)|(3<<11)|(3<<14),GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);// PD0,1,4,5,11,12,14,15 AF OUT
GPIO_Set(GPIOE,(0xF<<7),GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);// PE7~10,AF OUT
GPIO_Set(GPIOG,(1<<9),GPIO_MODE_AF,GPIO_OTYPE_PP,GPIO_SPEED_100M,GPIO_PUPD_PU);// PG9 AF OUT
GPIO_AF_Set(GPIOD,0,12);// PD0,AF12
GPIO_AF_Set(GPIOD,1,12);// PD1,AF12
GPIO_AF_Set(GPIOD,4,12);// PD4,AF12
GPIO_AF_Set(GPIOD,5,12);// PD5,AF12
GPIO_AF_Set(GPIOD,11,12);// PD11,AF12
GPIO_AF_Set(GPIOD,12,12);// PD12,AF12
GPIO_AF_Set(GPIOD,14,12);// PD14,AF12
GPIO_AF_Set(GPIOD,15,12);// PD15,AF12
GPIO_AF_Set(GPIOE,7,12);// PE7,AF12
GPIO_AF_Set(GPIOE,8,12);// PE8,AF12
GPIO_AF_Set(GPIOE,9,12);// PE9,AF12
GPIO_AF_Set(GPIOE,10,12);// PE10,AF12
GPIO_AF_Set(GPIOG,9,12);// PG9,AF12
tempreg = 0<<1;// 关闭等待特性
tempreg |= 1<<3;// 存储器类型为NAND FLASH
tempreg |= 0<<4;// 数据线宽度为8位
tempreg |= 1<<6;// 禁止ECC
tempreg |= 0x07<<17;// ECC页大小为512字节
tempreg |= 1<<9;// 设置TCLR(tCLR=CLE到RE的延时)=(TCLR+TSET+2)*THCLK,THCLK=1/192=5.2ns
tempreg |= 1<<13;// 设置TAR(tAR=ALE到RE的延时)=(TAR+TSET+2)*THCLK,THCLK=1/192=5.2ns
FMC_Bank2_3->PCR3 = tempreg;// 设置NAND/PC卡控制寄存器
tempreg = 1<<0;// MEMSET,建立时间2个HCLK(设置值+1)
tempreg |= 3<<8;// MEMWAIT,等待时间4个HCLK(设置值+1)
tempreg |= 1<<16;// MEMHOLD,保持时间1个HCLK
tempreg |= 1<<24;// MEMHIZ,高阻态时2个HCLK(设置值+1)
FMC_Bank2_3->PMEM3 = tempreg;// 设置通用时序寄存器
FMC_Bank2_3->PCR3 |= 1<<2;// 使能存储区3
NAND_Reset();//复位NAND
delay_ms(100);
nand_dev.id = NAND_ReadID();// 读取ID
NAND_ModeSet(4);// 设置为MODE4,高速模式
if(nand_dev.id==MT29F16G08ABABA){// NAND为MT29F16G08ABABA
nand_dev.page_totalsize = 4320;// nand一个page的总大小(包括spare区)
nand_dev.page_mainsize = 4096;// nand一个page的有效数据区大小
nand_dev.page_sparesize = 224;// nand一个page的spare区大小
nand_dev.block_pagenum = 128;// nand一个block所包含的page数目
nand_dev.plane_blocknum = 2048;// nand一个plane所包含的block数目
nand_dev.block_totalnum = 4096;// nand的总block数目
}
else if(nand_dev.id==MT29F4G08ABADA){//NAND为MT29F4G08ABADA
nand_dev.page_totalsize = 2112;// nand一个page的总大小(包括spare区)
nand_dev.page_mainsize = 2048;// nand一个page的有效数据区大小
nand_dev.page_sparesize = 64;// nand一个page的spare区大小
nand_dev.block_pagenum = 64;// nand一个block所包含的page数目
nand_dev.plane_blocknum = 2048;// nand一个plane所包含的block数目
nand_dev.block_totalnum = 4096;// nand的总block数目
}
else if(nand_dev.id==MT29F16G08CBACA){//NAND为MT29F16G08CBACA
nand_dev.page_totalsize = 4320;// nand一个page的总大小(包括spare区)
nand_dev.page_mainsize = 4096;// nand一个page的有效数据区大小
nand_dev.page_sparesize = 224;// nand一个page的spare区大小
nand_dev.block_pagenum = 256;// nand一个block所包含的page数目
nand_dev.plane_blocknum = 1024;// nand一个plane所包含的block数目
nand_dev.block_totalnum = 2048;// nand的总block数目
}
else{
return 1;// 错误,返回
}
return 0;
}
// 读取NAND FLASH的ID
// 返回值: 0: 成功
// 其他: 失败
u8 NAND_ModeSet(u8 mode){
*(vu8*)(NAND_ADDRESS|NAND_CMD) = NAND_FEATURE;//发送设置特性命令
*(vu8*)(NAND_ADDRESS|NAND_ADDR) = 0x01;// 地址为0x01,设置mode
*(vu8*)NAND_ADDRESS = mode;// P1参数,设置mode
*(vu8*)NAND_ADDRESS = 0;
*(vu8*)NAND_ADDRESS = 0;
*(vu8*)NAND_ADDRESS = 0;
if(NAND_WaitForReady()==NSTA_READY){
return 0;// 成功
}
else{
return 1;// 失败
}
}
//读取NAND Flash的指定页指定列的数据(main区和spare区都可以使用此函数)
//PageNum:要读取的页地址,范围:0~(block_pagenum*block_totalnum-1)
//ColNum:要读取的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)
//*pBuffer:指向数据存储区
//NumByteToRead:读取字节数(不能跨页读)
//返回值:0,成功
// 其他,错误代码
u8 NAND_ReadPage(u32 PageNum,u16 ColNum,u8 *pBuffer,u16 NumByteToRead)
{
vu16 i=0;
u8 res=0;
u8 eccnum=0;// 需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc
u8 eccstart=0;// 第一个ECC值所属的地址范围
u8 errsta=0;
u8 *p;
*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_A;
// 发送地址
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)ColNum;
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(ColNum>>8);
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)PageNum;
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>8);
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>16);
*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_AREA_TRUE1;
// 下面两行代码是等待R/B引脚变为低电平,其实主要起延时作用的,等待NAND操作R/B引脚.因为我们是通过
// 将STM32的NWAIT引脚(NAND的R/B引脚)配置为普通IO,代码中通过读取NWAIT引脚的电平来判断NAND是否准备
// 就绪的.这个也就是模拟的方法,所以在速度很快的时候有可能NAND还没来得及操作R/B引脚来表示NAND的忙
// 闲状态,结果我们就读取了R/B引脚,这个时候肯定会出错的,事实上确实是会出错!大家也可以将下面两行
// 代码换成延时函数,只不过这里我们为了效率所以没有用延时函数.
res = NAND_WaitRB(0);// 等待RB=0
if(res){
return NSTA_TIMEOUT;// 超时退出
}
// 下面2行代码是真正判断NAND是否准备好的
res = NAND_WaitRB(1);// 等待RB=1
if(res){
return NSTA_TIMEOUT;// 超时退出
}
if(NumByteToRead%NAND_ECC_SECTOR_SIZE){// 不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验
// 读取NAND FLASH中的值
for(i=0;i<NumByteToRead;i++){
*(vu8*)pBuffer++ = *(vu8*)NAND_ADDRESS;
}
}
else{
eccnum = NumByteToRead/NAND_ECC_SECTOR_SIZE;// 得到ecc计算次数
eccstart = ColNum/NAND_ECC_SECTOR_SIZE;
p = pBuffer;
for(res=0;res<eccnum;res++){
FMC_Bank2_3->PCR3|=1<<6;// 使能ECC校验
for(i=0;i<NAND_ECC_SECTOR_SIZE;i++){// 读取NAND_ECC_SECTOR_SIZE个数据
*(vu8*)pBuffer++ = *(vu8*)NAND_ADDRESS;
}
while(!(FMC_Bank2_3->SR3&(1<<6)));// 等待FIFO空
nand_dev.ecc_hdbuf[res+eccstart] = FMC_Bank2_3->ECCR3;// 读取硬件计算后的ECC值
FMC_Bank2_3->PCR3 &= ~(1<<6);// 禁止ECC校验
}
i = nand_dev.page_mainsize+0x10+eccstart*4;// 从spare区的0x10位置开始读取之前存储的ecc值
NAND_Delay(30);// 等待tADL
*(vu8*)(NAND_ADDRESS|NAND_CMD) = 0x05;// 随机读指令
// 发送地址
*(vu8*)(NAND_ADDRESS|NAND_ADDR) = (u8)i;
*(vu8*)(NAND_ADDRESS|NAND_ADDR) = (u8)(i>>8);
*(vu8*)(NAND_ADDRESS|NAND_CMD) = 0xE0;// 开始读数据
NAND_Delay(30);// 等待tADL
pBuffer = (u8*)&nand_dev.ecc_rdbuf[eccstart];
for(i=0;i<4*eccnum;i++){// 读取保存的ECC值
*(vu8*)pBuffer++= *(vu8*)NAND_ADDRESS;
}
for(i=0;i<eccnum;i++){// 检验ECC
if(nand_dev.ecc_rdbuf[i+eccstart]!=nand_dev.ecc_hdbuf[i+eccstart]){// 不相等,需要校正
printf("err hd,rd:0x%x,0x%x\r\n",nand_dev.ecc_hdbuf[i+eccstart],nand_dev.ecc_rdbuf[i+eccstart]);
printf("eccnum,eccstart:%d,%d\r\n",eccnum,eccstart);
printf("PageNum,ColNum:%d,%d\r\n",PageNum,ColNum);
res = NAND_ECC_Correction(p+NAND_ECC_SECTOR_SIZE*i,nand_dev.ecc_rdbuf[i+eccstart],nand_dev.ecc_hdbuf[i+eccstart]);// ECC校验
if(res){
errsta = NSTA_ECC2BITERR;// 标记2BIT及以上ECC错误
}
else{
errsta = NSTA_ECC1BITERR;// 标记1BIT ECC错误
}
}
}
}
if(NAND_WaitForReady()!=NSTA_READY){
errsta = NSTA_ERROR;// 失败
}
return errsta;// 成功
}
//在NAND一页中写入指定个字节的数据(main区和spare区都可以使用此函数)
//PageNum:要写入的页地址,范围:0~(block_pagenum*block_totalnum-1)
//ColNum:要写入的列开始地址(也就是页内地址),范围:0~(page_totalsize-1)
//pBbuffer:指向数据存储区
//NumByteToWrite:要写入的字节数,该值不能超过该页剩余字节数!!!
//返回值:0,成功
// 其他,错误代码
u8 NAND_WritePage(u32 PageNum,u16 ColNum,u8 *pBuffer,u16 NumByteToWrite)
{
vu16 i=0;
u8 res=0;
u8 eccnum=0;// 需要计算的ECC个数,每NAND_ECC_SECTOR_SIZE字节计算一个ecc
u8 eccstart=0;// 第一个ECC值所属的地址范围
*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE0;
//发送地址
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)ColNum;
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(ColNum>>8);
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)PageNum;
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>8);
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(PageNum>>16);
NAND_Delay(30);//等待tADL
if(NumByteToWrite%NAND_ECC_SECTOR_SIZE)//不是NAND_ECC_SECTOR_SIZE的整数倍,不进行ECC校验
{
for(i=0;i<NumByteToWrite;i++)// 写入数据
{
*(vu8*)NAND_ADDRESS=*(vu8*)pBuffer++;
}
}else
{
eccnum=NumByteToWrite/NAND_ECC_SECTOR_SIZE;// 得到ecc计算次数
eccstart=ColNum/NAND_ECC_SECTOR_SIZE;
for(res=0;res<eccnum;res++)
{
FMC_Bank2_3->PCR3|=1<<6;// 使能ECC校验
for(i=0;i<NAND_ECC_SECTOR_SIZE;i++)// 写入NAND_ECC_SECTOR_SIZE个数据
{
*(vu8*)NAND_ADDRESS=*(vu8*)pBuffer++;
}
while(!(FMC_Bank2_3->SR3&(1<<6)));// 等待FIFO空
nand_dev.ecc_hdbuf[res+eccstart]=FMC_Bank2_3->ECCR3;// 读取硬件计算后的ECC值
FMC_Bank2_3->PCR3&=~(1<<6);// 禁止ECC校验
}
i=nand_dev.page_mainsize+0x10+eccstart*4;// 计算写入ECC的spare区地址
NAND_Delay(30);//等待
*(vu8*)(NAND_ADDRESS|NAND_CMD)=0x85;// 随机写指令
//发送地址
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)i;
*(vu8*)(NAND_ADDRESS|NAND_ADDR)=(u8)(i>>8);
NAND_Delay(30);//等待tADL
pBuffer=(u8*)&nand_dev.ecc_hdbuf[eccstart];
for(i=0;i<eccnum;i++)// 写入ECC
{
for(res=0;res<4;res++)
{
*(vu8*)NAND_ADDRESS=*(vu8*)pBuffer++;
}
}
}
*(vu8*)(NAND_ADDRESS|NAND_CMD)=NAND_WRITE_TURE1;
if(NAND_WaitForReady()!=NSTA_READY)return NSTA_ERROR;//失败
return 0;//成功
}
|
求大神解救,有偿悬赏