OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 5238|回复: 10

NAND_ReadPage关于ECC的疑问

[复制链接]

4

主题

59

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
367
金钱
367
注册时间
2016-8-29
在线时间
128 小时
发表于 2018-12-19 10:27:23 | 显示全部楼层 |阅读模式
1金钱
本帖最后由 破风者 于 2018-12-19 10:30 编辑

[mw_shl_code=c,true]//读取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;          读取200-711的硬件 ECC
                        FMC_Bank2_3->PCR3&=~(1<<6);                                                //禁止ECC校验
                }
                i=nand_dev.page_mainsize+0X10+eccstart*4;                        //从spare区的0X10位置开始读取之前存储的ecc值
                NAND_Delay(NAND_TRHW_DELAY);//等待tRHW
                *(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(NAND_TWHR_DELAY);//等待tWHR
                pBuffer=(u8*)&nand_dev.ecc_rdbuf[eccstart];
                for(i=0;i<4*eccnum;i++)                                                                //读取保存的ECC值
                {
                        *(vu8*)pBuffer++= *(vu8*)NAND_ADDRESS;                                                         读取保存的0-511 ECC
                }                        
                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;        //成功   
} [/mw_shl_code]
比如ColNum为200,NumByteToRead为512,nand_dev.ecc_hdbuf是200-711的硬件 ECC,nand_dev.ecc_rdbuf是保存的0-511 ECC
是我理解错了还是程序有bug?

最佳答案

查看完整内容[请看2#楼]

参考另外一个帖子(http://www.openedv.com/forum.php?mod=viewthread&tid=290963&extra=)及NAND_FLASH官方关于NAND的说明,这一行代码的正确写法应该是i = nand_dev.page_mainsize + 0X10* eccstart +4; 0X10* eccstart表示第几个512字节对应的ECC区域,4表示避开前面四个字节,为什么请看NAND_FLASH官方手册 及 另外一个帖子
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

14

主题

45

帖子

0

精华

初级会员

Rank: 2

积分
177
金钱
177
注册时间
2018-5-5
在线时间
40 小时
发表于 2018-12-19 10:27:24 | 显示全部楼层
本帖最后由 huyu268897 于 2019-4-6 14:33 编辑
huyu268897 发表于 2019-4-4 15:53
i = nand_dev.page_mainsize + 0X10 + eccstart*4;          版主能解释一下 这一行代码是怎么来的吗?    我怎 ...

参考另外一个帖子(http://www.openedv.com/forum.php?mod=viewthread&tid=290963&extra=)及NAND_FLASH官方关于NAND的说明,这一行代码的正确写法应该是i = nand_dev.page_mainsize + 0X10* eccstart +4;   
0X10* eccstart表示第几个512字节对应的ECC区域,4表示避开前面四个字节,为什么请看NAND_FLASH官方手册 及 另外一个帖子      

回复

使用道具 举报

4

主题

59

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
367
金钱
367
注册时间
2016-8-29
在线时间
128 小时
 楼主| 发表于 2018-12-19 21:39:08 | 显示全部楼层
??????
回复

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165426
金钱
165426
注册时间
2010-12-1
在线时间
2113 小时
发表于 2018-12-20 02:01:43 | 显示全部楼层
代码没有bug
回复

使用道具 举报

14

主题

45

帖子

0

精华

初级会员

Rank: 2

积分
177
金钱
177
注册时间
2018-5-5
在线时间
40 小时
发表于 2019-4-4 15:53:28 | 显示全部楼层

i = nand_dev.page_mainsize + 0X10 + eccstart*4;          版主能解释一下 这一行代码是怎么来的吗?    我怎么都没看懂,    0X10代表什么    为什么eccstart要*4
回复

使用道具 举报

14

主题

45

帖子

0

精华

初级会员

Rank: 2

积分
177
金钱
177
注册时间
2018-5-5
在线时间
40 小时
发表于 2019-4-5 10:13:14 | 显示全部楼层
huyu268897 发表于 2019-4-4 15:53
i = nand_dev.page_mainsize + 0X10 + eccstart*4;          版主能解释一下 这一行代码是怎么来的吗?    我怎 ...

为什么*4可以从  MT29F4G08ABADAWP.pdf  Figure 80: Spare Area Mapping (x8)  找到答案,但是为什么避开0X10还是没有头绪
回复

使用道具 举报

2

主题

32

帖子

0

精华

新手上路

积分
26
金钱
26
注册时间
2019-4-5
在线时间
6 小时
发表于 2019-4-5 22:35:24 | 显示全部楼层
this is a test
回复

使用道具 举报

2

主题

32

帖子

0

精华

新手上路

积分
26
金钱
26
注册时间
2019-4-5
在线时间
6 小时
发表于 2019-4-6 07:50:31 | 显示全部楼层
这个金钱也太多了,新手没钱
回复

使用道具 举报

4

主题

12

帖子

0

精华

初级会员

Rank: 2

积分
154
金钱
154
注册时间
2015-3-31
在线时间
37 小时
发表于 2019-7-9 23:51:55 | 显示全部楼层

版主,就像楼主说的,这个在写入的时候ecc是按照512字节存4组,那么读取的时候,如果是从页中开始读取,例如页地址700读512个字节,那么读取时计算的ecc是地址700到700+511的,这个怎么和之前写入时的ecc比较呢?
回复

使用道具 举报

8

主题

22

帖子

0

精华

初级会员

Rank: 2

积分
119
金钱
119
注册时间
2017-7-27
在线时间
30 小时
发表于 2019-7-17 11:51:48 | 显示全部楼层
chensky 发表于 2019-7-9 23:51
版主,就像楼主说的,这个在写入的时候ecc是按照512字节存4组,那么读取的时候,如果是从页中开始读取, ...

nand flash一般会在文件系统中使用,文件系统是以一个扇区为单位进行读写的,一般512Byte,实际使用中不可能出现你说的那种情况
回复

使用道具 举报

10

主题

97

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
488
金钱
488
注册时间
2019-12-6
在线时间
152 小时
发表于 2023-10-20 18:04:19 | 显示全部楼层
huyu268897 发表于 2019-4-5 10:13
为什么*4可以从  MT29F4G08ABADAWP.pdf  Figure 80: Spare Area Mapping (x8)  找到答案,但是为什么避开 ...

这个可以参考最新的HAL库视频的第123讲,1:26:30这个时段有说明,视频链接【【正点原子】手把手教你学STM32 HAL库开发全集【真人出镜】STM32入门教学视频教程 单片机 嵌入式】https://www.bilibili.com/video/B ... e4acf9422aa415a487c
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-4-7 12:40

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表