打印

stm32f429 + MT29F16G08CBACA(NAND)

[复制链接]
3387|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
枫行幻幻|  楼主 | 2018-4-17 09:05 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 枫行幻幻 于 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;//成功   
}



评论
枫行幻幻 2018-4-17 14:43 回复TA
求大神解救,有偿悬赏 

相关帖子

沙发
托尼库尔茨| | 2018-6-5 16:46 | 只看该作者
哥们 弄出来没?  我现在也用f407配置MT29F8G  但是读id不对, 咋回事。  你有结果呢吗

使用特权

评论回复
板凳
枫行幻幻|  楼主 | 2018-8-22 14:01 | 只看该作者
托尼库尔茨 发表于 2018-6-5 16:46
哥们 弄出来没?  我现在也用f407配置MT29F8G  但是读id不对, 咋回事。  你有结果呢吗 ...

没弄出来,你有弄出来吗?

使用特权

评论回复
地板
franfli| | 2019-2-27 14:04 | 只看该作者
弄出来了么?楼主

使用特权

评论回复
5
wandersky| | 2019-7-29 08:23 | 只看该作者
安富莱的V5开发板上有NAND的驱动,不过是hynix 的NAND片子,把延迟改一下就好了,
把for (i = 0; i < 20;i++);  修改为 for (i = 0; i < 1000; i++);
        可以驱动MT29F16G  MICRON 存储芯片

使用特权

评论回复
6
wandersky| | 2019-7-29 08:24 | 只看该作者
V5-107b_FatFS文件系统例程(NAND Flash).zip (2.38 MB) 这是代码,可以读ID,可以读写测试 ,但是加FATFS有问题。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1

主题

4

帖子

0

粉丝