• Mini2440裸机开发之Nand Flash 编程


    一、Nand Flash命令

    1.1 命令表

    对NAND FLASH的操作需要发出命令,下面有个NAND FLASH的命令表格,那么我们可以此表格上的命令来访问我们的Nand Flash。

     针对每一个命令的时序可以参考NAND FLASH芯片使用手册。下面我们将会分析一些常用命令的时序。时序中部分信号的信息如下:

    • 当ALE为高电平时传输的是地址;
    • 当CLE为高电平时传输的是命令;
    • 当ALE,CLE都为低电平表示传输的是数据 ;
    • $overline{CE}$片选信号,低电平有效;
    • $overline{RE}$读使能,低电平有效;
    • $overline{WE}$写使能,低电平有效;

    1.2 Read ID时序分析

    • 第一条竖线位置,写命令,发送了$overline{CE}$、$CLE$、$overline{WE}$信号,90h命令被锁存;
    • 第二条竖线,写地址,发送了$overline{WE}$、$ALE$、$overline{CE}$信号,地址00被锁存;
    • 继续往后,命令、地址都发完了,要read数据了,所以释放$overline{WE}$,$ALE$,这里$tAR$表示$ALE$释放多久后才可以发送$overline{RE}$信号,$tREA$表示$overline{RE}$信号的建立时间;
    • 第三条竖线位置,发送了$overline{CE}$,$overline{RE}$信号,所以数据被锁存,第一个访问周期锁存的数据为marker code,值为0xEC,第二个访问周期的数据为device code,值为0xDA。读id时读5个周期含义对应如下表:

    该Nand Flash的5个周期读取出来的值对应如下:

    第三个访问周期含义如下表:

     第四个访问周期含义如下表:

     第五个访问周期含义如下表:

    根据第4、5个访问周期的结果0x15、0x44我们得知该NAND FLASH的block_size=128K,page_size=2k, 有2个plane,plane_size=1Gb = 128M, 共256M。

    1.3 页读取(page read)

    • 第一条竖线,写命令,发送了$overline{CE}$、$CLE$、$overline{WE}$信号,00h命令被锁存;
    • 然后,写地址,发送了$overline{WE}$、$ALE$、$overline{CE}$信号,NDFLASH的地址周期中可以看出来,先发出2个周期的col列地址,再发出3个周期的Row行地址,关于5个周期地址的计算可以参考上一篇博客;
    • 写命令,发送了$overline{CE}$、$CLE$、$overline{WE}$信号,30h命令被锁存;
    • 然后会有一个busy时间段,$R/overline{B}$为低电平。$tRR$表示busy状态的持续时间(手册上最小为20ns);
    • .开始锁存数据,$overline{RE}$使能,nand上的数据被同步到数据nand控制器上。我们的nand是8bit数据位宽,所以每隔一个read时钟周期($tRC$),传输1byte数据。每传输1byte数据,地址会自动往后偏移1byte,一般我们会连续读取1page数据;

    1.4 块擦除(block erase)

    • 首先发送0x60命令;
    • 发送row地址(由于擦除是以block为单位的,所以无需知道页内地址,只需要知道要擦除哪个page、哪个block即可);
    • 发送0xDO命令,执行擦除动作
    • 然后会有一个busy时间段,$R/overline{B}$为低电平;
    • 发送0x70命令,用来读取状态;
    • 判断NFDATA寄存器的第0位是否擦除成功;

    1.5 页写入(page write)

    • 首先发送0x80命令;
    • 发送地址(5个周期);
    • 发送数据;
    •  发送0x10命令,执行烧写动作;
    • 然后会有一个busy时间段,$R/overline{B}$为低电平;
    • 发送0x70命令,用来读取状态;
    • 判断NFDATA寄存器的第0位是否烧写成功

     二、寄存器介绍以及初始化

    2.1  寄存器

    Nand控制器要按照我们Nand Flash的实际型号和性能来设置初始值。

    S3C2440 NAND FLASH相关寄存器如下:

    2.2 配置寄存器(NFCONF)

    寄存器信息:

    寄存器 地址 R/W 描述 复位值
    NFCONF 0X4E000000 R/W NAND FLASH配置寄存器 0x0000100X

    寄存器位信息:

    NFCONF 描述 初始状态
    保留 [15:14] 保留 ——
    TACLS [13:12]

    CLE 和ALE 持续值设置(0 至3)

    Duration = HCLK × TACLS

    01
    保留 [11] 保留 0
    TWRPH0 [10:8]

    TWRPH0 持续值设置(0~7)

    Duration = HCLK × ( TWRPH0 + 1 )

    000
    保留 [7] 保留 0
    TWRPH1 [6:4]

    TWRPH1 持续值设置(0~7)

    Duration = HCLK × ( TWRPH1 + 1 )

    000

    AdvFlash
    (只读)

    [3]

    自动引导启动用的先进NAND Flash 存储器。
    0:支持256 字或512 字节/页的NAND Flash 存储器;
    1:支持1K 字或2K 字节/页的NAND Flash 存储器。
    此位由在复位和从睡眠模式中唤醒时的NCON0 引脚状态所决定。

    硬件设置
    (NCON0)

    PageSize
    (只读)

    [2]

    自动引导启动用的NAND Flash 存储器的页面大小。
    先进闪存页面大小
    当AdvFlash 为0 时
    0=256 字/页;1=512 字节/页
    当AdvFlash 为1 时
    0=1024 字/页;1=2048 字节/页
    此位由在复位和从睡眠模式中唤醒时的GPG13 引脚状态所决定。
    复位后,GPG13 可以用于通用I/O 口或外部中断。

    硬件设置
    (GPG13)

    AddrCycle
    (只读)

    [1]

    自动引导启动用的NAND Flash 存储器的地址周期。
    先进闪存地址周期
    当AdvFlash 为0 时
    0=3 个地址周期;1=4 个地址周期
    当AdvFlash 为1 时
    0=4 个地址周期;1=5 个地址周期
    此位由在复位和从睡眠模式中唤醒时的GPG14 引脚状态所决定。
    复位后,GPG14 可以用于通用I/O 口或外部中断。

    硬件设置
    (GPG14)

    BusWidth
    (R/W)

    [0]

    自动引导启动和普通访问用的NAND Flash 存储器的输入输出总线宽
    度。
    0=8 位总线;1=16 位总线
    此位由在复位和从睡眠模式中唤醒时的GPG15 引脚状态所决定。
    复位后,GPG15 可以用于通用I/O 口或外部中断。
    此位可以被软件改变。

    硬件设置
    (GPG15)

    假设$HCLK=100MHZ$,则$T=10ns$。

    前面一节分析了$TACLS = max(tCLS,tALS) - tWP$,我们从NAND手册得知$tCLS$、$tALS$、$tWP$最小都可以取到12ns, 所以我们可以取$TACLS=0$;

    $TWRPH0 = tWP$,我们的NAND手册上要求$tWP$最少12ns, 那么取$TWRPH0 =1$, $Duration = HCLK*(TWRPH0+1)=20ns>12ns$,满足要求;

    $TWRPH1 = max(tCLH,tALH)$, 我们的NAND手册上要求$tCLH$、$tALH$最少5ns, 那么取$TWRPH1 =0$, $Duration = HCLK*(TWRPH1+1)=10ns>5ns$,满足要求。

    再配置BusWidth总线位宽为8bit;
    所以NFCONF寄存器设置如下:

    #define  TACLS   0
    #define  TWRPH0  1
    #define  TWRPH1  0
    /*设置NAND FLASH的时序*/
    NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);

    2.3 控制寄存器(NFCONT)

    寄存器信息:

    寄存器 地址 R/W 描述 复位值
    NFCONF 0X4E000004 R/W NAND FLASH控制寄存器 0x0384

    寄存器位信息:

    NFCONT 描述 初始状态
    保留 [15:14] 保留 0
    Lock-tight [13]

    紧锁配置(Lock-tight)
    0: 禁止紧锁 1:使能紧锁
    只要此位被设置为1 一次,你就不能清除了。只有复位或从睡眠模式中被唤醒才能使此
    位为禁止(即不能由软件清零)。当此位被设置为1 的情况下,在NFSBLK(0x4E000038)
    到NFEBLK(0x4E00003C)-1 的区域设置未被上锁,除了这些区域以外的区域,写入或
    擦除命令将会无效,只有只读命令有效。

    0
    SotLock [12]

    软件上锁设置
    0: 禁止上锁 1:使能上锁
    软件锁定区域可以随时用软件修改。当此位被设置为1 的情况下, 在
    NFSBLK(0x4E000038)到NFEBLK(0x4E00003C)-1 的区域设置未被上锁,除了这些区
    域以外的区域,写入或擦除命令将会无效,只有只读命令有效。当你试图写入或擦除这
    些锁定区域时,将发生非法访问(NFSTAT[3]位将会置位)。如果NFSBLK 和NFEBLK
    相同时,整个区域都被锁定。

    1
    保留 [11] 保留 0
    EnbIllegalAccINT [10]

    非法访问中断控制
    0: 禁止中断 1:使能中断
    当CPU 试图编程或擦除锁定区域( 由NFSBLK(0x4E000038) 到
    NFEBLK(0x4E00003C)-1 的区域设置)而产生非法访问中断。

    0
    EnbRnBINT [9]

    RnB 状态输入信号传输中断控制
    0: 禁止RnB 中断 1:使能RnB 中断

    0

    RnB_TransMode

    [8]

    RnB 传输检测配置
    0: 检测上升沿 1:检测下降沿

    0

    保留

    [7]

    保留

    0

    SpareECCLock

    [6]

    锁定备份区域ECC 产生
    0: 开锁备份 ECC 1:锁定备份 ECC
    备份区域ECC 寄存器为NFSECC(0x4E000034)。

    1

    MainECCLock

    [5]

    锁定主数据区域ECC 生成
    0: 开锁主数据区域 ECC生成 1:锁定主数据区域 ECC生成
    主数据区域ECC 状态寄存器为NFMECC0/1(0x4E00002C/30)。

    1

    InitECC

    [4]  

    初始化ECC 编码器/译码器(只写)
    1:初始化ECC 编码器/译码器

     0
    保留 [3:2]  保留  00
    Reg_nCE [1]  

    NAND Flash 存储器nFCE 信号控制
    0:强制 nFCE 为低(使能片选) 1:强制 nFCE 为高(禁止片选)
    注意:在引导启动期间其自动被控制。只有MODE 位为1 该值才有效。

     1
    MODE [0]  

    NAND Flash 控制器运行模式
    0:NAND Flash 控制器禁止(不工作) 1:NAND Flash 控制器使能

     0

    MODE [0]: 设置为1,使能NAND控制器。

    Reg_nCE [1]: 设置为1,禁止片选(等要使用的时候再使能片选信号)。

    所以NFCONF寄存器设置如下:

    /*使能NAND FLASH控制器,禁止片选*/
    NFCONT = (1<<1) | (1<<0);

    2.4 命令寄存器(NFCMMD)

    寄存器信息:

    寄存器 地址 R/W 描述 复位值
    NFCMMD 0X4E000008 R/W NAND FLASH命令寄存器 0x00

    寄存器位信息:

    NFCMMD 描述 初始状态
    保留 [15:8] 保留 0x00
    NFCMMD [7:0] NAND FLASH存储器命令值 0x00

    我们可以使用2440上的NAND FLASH控制器简化操作,只需要往NFCMMD寄存器写入要传输的命令就可以了,NAND FLASH控制器默认把上面复杂的时序发出来。

    2.5 地址寄存器(NFADDR)

    寄存器信息:

    寄存器 地址 R/W 描述 复位值
    NFADDR 0X4E00000C R/W NAND FLASH地址寄存器 0x00000XX00

    寄存器位信息:

    NFADDR 描述 初始状态
    保留 [15:8] 保留 0x00
    NFADDR [7:0] NAND FLASH存储器地址值 0x00

    发命令后,后面就需要发送地址了,当$nWE$和$ALE$有效的时候,表示锁存的是地址,往NFADDR寄存器中写值就可以了,比如:NFADDR=0x00。
    上一节我们得知地址需要用5个周期来发送,前2个周期为col地址,后三个周期为row(page)地址。

    ① column:列地址A0~A10,就是页内地址,地址范围是从0到2047。(A11用来确定oob的地址,即2048-2111这64个字节的范围)
    ② page:A12~A28,称作页号,page(row)编号。

    2.6 数据寄存器(NFDATA) 

    寄存器信息:

    寄存器 地址 R/W 描述 复位值
    NFDATA 0X4E000010 R/W NAND FLASH数据寄存器 0xXXXX

    寄存器位信息:

    NFDATA 描述 初始状态
    NFDATA [31:0] NAND Flash 读取/编程数据给I/O 0xXXXX

    当命令、地址都发送完后就可以从数据总线上DATA[7:0]获取数据或者写入数据。同样往NFDATA寄存器中写值或者读值就可以了,如unsigned char buf=NFDATA,由于是数据位宽是8位的,所以访问时数据组织形式如下:

    从上图可以看出,当word access时,只需一个时钟周期;当byteaccess的时候,需要4个时钟周期,小端模式下第一个时钟周期对应低字节,第四个时钟周期对应高字节。nand_wait_rdle函数等待NAND FLASH空闲,从上图可以看出当NFSTAT寄存器[0]的值为1时NAND FLASH是空闲的,我们可以通过该位来判断NAND FLASH是否繁忙。代码如下:

    /* 等待NAND Flash就绪 */
    void _nand_wait_idle(void)
    {
        int i;
        while(!(NFSTAT & BUSY));
        for(i=0; i<10; i++);
    }

    2.7 状态寄存器(NFSTAT)

    寄存器信息:

    寄存器 地址 R/W 描述 复位值
    NFSTAT 0X4E000020 R/W NAND FLASH运行状态寄存器 0xXX00

    寄存器位信息:

    NFSTAT 描述 初始状态
    保留 [7] 保留 0xXXXX

    保留 [6:4]

    保留

     X
    illegalAccess [3]

    软件锁定或紧锁一次使能。非法访问(编程,擦除)存储器屏蔽此位设置

    0:不检测非法访问 1:检测非法访问

     0
    $R/overline{B}$_TransDetect [2]

    当$R/overline{B}$由低变高时发生传输,如果使能了此位则设置和发出中断。要清

    除此位时对其写入‘1’

    0:不检测$R/overline{B}$ 传输 1:检测$R/overline{B}$ 传输

    传输配置设置在$R/overline{B}$_TransMode(NFCONT[8])中

     0
    $overline{CE}$(只读) [1]  $overline{CE}$ 输出引脚的状态  1
    $R/overline{B}$(只读) [0]

     $R/overline{B}$ 输入引脚的状态

    0:NAND Flash 存储器忙 1:NAND Flash 存储器运行就绪

     1

    之前在介绍页读取命令时,我们说过,当发完命令、地址后再进行读数据前我们知道有一段时间$tRR$处于busy状态,我们可以通过查询NFSTAT寄存器来确定busy状态有没有结束,是不是已经ready了。

    三 代码

    在前面我们已经介绍了NAND的读写等命令以及时序图,这里就不再单独介绍具体的步骤,下面直接上代码。

    3.1 区分nand或者nor启动

    我们知道nand启动0x00地址对应片内SRAM,可以像内存一样的写0x00地址;nor启动,0x00地址对应片内nor flash,nor falsh不能像内存一样的写地址, 所以往0x00地址写入数据成功表示nand启动,写不成功表示nor启动。

    int is_boot_from_nor_flash(void)
    {
        volatile u32 *p = (volatile u32 )0x00;
    
        u32 val = *p;                      /* get value from address 0x00 */
        *p = 0x12345678;                /* write value to address 0x00 */
    
        /* 写成功, 对应nand启动 */
        if(*p == 0x12345678){
            *p = val;
            return 0;
        }
    
        return 1;
    }

    3.2 nand.h

    /*****************************************************************************************************************
     *
     *  FileName  : nand.h
     *  Function  : NAND FLASH设置
     *  Author    : zy
     *              K9F2G08U0C   258M   28位地址
     *
     ****************************************************************************************************************/
    #ifndef __NAND_H__
    #define __NDND_H__
    
    #include  "type.h"
    
    #define GSTATUS1        (*(volatile u32 *)0x560000B0)
    #define BUSY            1
    
    /* page size 2048byte */
    #define NAND_PAGE_SIZE           2048
    #define NAND_PAGE_MASK_SIZE      (NAND_PAGE_SIZE - 1)
    #define NAND_BLOCK_SIZE          (64*NAND_PAGE_SIZE)
    #define NAND_BLOCK_MASK_SIZE     (NAND_BLOCK_SIZE-1)
    
    /******************************************************************************************************************/
    /* NAND FLASH (see S3C2440 manual chapter 6, www.100ask.net) */
    #define     NFCONF        (*(volatile u32 *)0x4e000000)        // 配置寄存器
    #define     NFCONT       (*(volatile u32 *)0x4e000004)        // 控制寄存器
    #define     NFCMMD       (*(volatile u8  *)0x4e000008)        // 命令寄存器
    #define     NFADDR       (*(volatile u8  *)0x4e00000C)        // 地址寄存器
    #define     NFDATA       (*(volatile u8  *)0x4e000010)        // 数据寄存器
    #define     NFMECCD0     (*(volatile u32 *)0x4e000014)
    #define     NFMECCD      (*(volatile u32 *)0x4e000018)
    #define     NFSECCD      (*(volatile u32 *)0x4e00001C)
    #define     NFSTAT       (*(volatile u32 *)0x4e000020)
    #define     NFESTAT0     (*(volatile u32 *)0x4e000024)
    #define     NFESTAT1     (*(volatile u32 *)0x4e000028)
    #define     NFMECC0      (*(volatile u32 *)0x4e00002C)
    #define     NFMECC1      (*(volatile u32 *)0x4e000030)
    #define     NFSECC       (*(volatile u32 *)0x4e000034)
    #define     NFSBLK       (*(volatile u32 *)0x4e000038)
    #define     NFEBLK       (*(volatile u32 *)0x4e00003C)
    
    /******************************************************************************************************************/
    typedef struct {                                 /* 函数指针作为结构体成员 */
        /* 组成完整的命令操作的步骤 */
        void (*_nand_reset)(void);                         /* 初始化nand控制器 */
        void (*_nand_wait_idle)(void);                     /* 等待NAND Flash就绪 */
        void (*_nand_select_chip)(void);                   /* 使能片选 */
        void (*_nand_deselect_chip)(void);                 /* 禁止片选 */
        void (*_nand_write_cmd)(u32 cmd);                  /* 发命令 */
        void (*_nand_write_addr)(u32 addr);                /* 发地址 */
        u8 (*_nand_read_data)(void);                       /* 读数据 */
    
        /* 完整的命令操作 */
        int (*nand_read_data)(u8 *buf, u32 start_addr, u32 size);    /* 发送读取数据命令, start_addr参数必须按页对齐 success:0 fail:-1 */
        void (*nand_read_id)(u8 *buf);                               /* 读ID,buf length 5*/
        int  (*nand_block_erase)(u32 start_addr,u32 size);           /* 块擦除(block erase),参数必须按块对齐  success:0 fail:-1  */
        int (*nand_write_data)(u32 start_addr,u8 *buf, u32 size);    /* 发送写数据命令, start_addr参数必须按页对齐  success:0 fail:-1*/
    }t_nand_chip;
    
    /* 供外部调成员和方法 */
    extern t_nand_chip nand_chip;                   /* nand_init执行后可以使用,extern全局变量避免重复定义 */
    extern void nand_init(void);                    /* 初始化NAND Flash,给nand_chip函数指针成员赋值 */
    
    #endif

    3.3 nand.c

    /****************************************************************************************************/
    #include "nand.h"
    
    /* 全局变量 */
    t_nand_chip nand_chip;
    /****************************************************************************************************/
    /* 等待NAND Flash就绪 */
    void _nand_wait_idle(void)
    {
        int i;
        while(!(NFSTAT & BUSY));
        for(i=0; i<10; i++);
    }
    
    /* 发出片选信号 */
    void _nand_select_chip(void)
    {
        int i;
        NFCONT &= ~(1<<1);                   /* set CE=0 */
        for(i=0; i<10; i++);    
    }
    
    /* 取消片选信号 */
    void _nand_deselect_chip(void)
    {
        NFCONT |= (1<<1);                   /* set CE=1 */
    }
    
    /* 发出命令 */
    void _nand_write_cmd(u8 cmd)
    {
        int i;
        NFCMMD = cmd;
        for(i=0; i<10; i++);
    }
    
    /* 发出地址 */
    void _nand_write_addr_byte(u8 addr)
    {
        volatile int i;
        NFADDR = addr;
        for(i=0; i<10; i++);
    }
    
    /* 发出地址,5个周期来发送,前2个周期为col地址,后三个周期为row(page)地址 */
    void _nand_write_addr(u32 addr)
    {
        int col, page;
    
        col = addr & NAND_PAGE_MASK_SIZE;
        page = addr / NAND_PAGE_SIZE;     /* 块号  */
    
        _nand_write_addr_byte(col & 0xff);            /* Column Address A0~A7 */
        _nand_write_addr_byte((col >> 8) & 0x0f);     /* Column Address A8~A11 */
        _nand_write_addr_byte(page & 0xff);            /* Row Address A12~A19 */
        _nand_write_addr_byte((page >> 8) & 0xff);    /* Row Address A20~A27 */
        _nand_write_addr_byte((page >> 16) & 0x01);    /* Row Address A28 */
    }
    
    /* 复位 */
    void _nand_reset(void)
    {
        _nand_select_chip();
        _nand_write_cmd(0xff);          // 复位命令
        _nand_wait_idle();
        _nand_deselect_chip();
    }
    
    /* 读取数据 */
    u8 _nand_read_data(void)
    {
        return NFDATA;
    }
    
    /* 写入数据 */
    void _nand_write_data(u8 data)
    {
        int i;
        NFDATA = data;
        for(i=0; i<10; i++);
    }
    
    /* Read ID */
    void nand_read_id(u8 *buf)
    {
        /* 选中芯片 */
        _nand_select_chip();
    
        /* write cmd 0x90 */
        _nand_write_cmd(0x90);
    
        /* write addr 0x00 */
        _nand_write_addr(0x00);
    
        /* read */
        buf[0] = _nand_read_data();    /* 第一个访问周期锁存的数据为marker code,值为0xEC */
        buf[1] = _nand_read_data();    /* 第二个访问周期的数据为device code,值为0xDA */
        buf[2] = _nand_read_data();    /* 0X10 */
        buf[3] = _nand_read_data();    /* 0X95*/
        buf[4] = _nand_read_data();    /* 0X44 */
    
        /* 取消片选信号 */
        _nand_deselect_chip();
    }
    
    /* 发送读取数据命令,start_addr参数必须按页对齐   success:0 fail:-1  */
    int nand_read_data(u8 *buf, u32 start_addr, u32 size)
    {
        int i = 0;
        int j = 0;
    
        if (start_addr & NAND_PAGE_MASK_SIZE ) {
            return -1;    /* 地址或长度不对齐 */
        }
    
    
        /* 选中芯片 */
        _nand_select_chip();
    
        /* 按页读取 */
        while (i < size) {
          /* 发出READ命令 */
          _nand_write_cmd(0);
    
          /* write address */
          _nand_write_addr(start_addr+i);
    
          /* write cmd */
          _nand_write_cmd(0x30);
    
          /* wait */
          _nand_wait_idle();
    
          /* 读取一页数据 */
          for(; (j < NAND_PAGE_SIZE) &&  (i < size); j++)
          {
              buf[i++] = _nand_read_data();
          }
          j = 0;
        }
    
        /* 取消片选信号 */
        _nand_deselect_chip();
        
        return 0;
    }
    
    /* 块擦除(block erase),参数必须按块对齐  success:0 fail:-1  */
    int nand_block_erase(u32 start_addr,u32 size)
    {
        int i;
        int page = start_addr / NAND_PAGE_SIZE;     /* 块号  */
    
        if ((start_addr & NAND_BLOCK_MASK_SIZE) || (size & NAND_BLOCK_MASK_SIZE)) {
            return -1;    /* 地址或长度不对齐 */
        }
    
        /* 选中芯片 */
        _nand_select_chip();
    
        for(i=start_addr; i < (start_addr + size);)
        {
            page = i / NAND_PAGE_SIZE;
    
            /* write cmd 0x60 */
            _nand_write_cmd(0x60);
    
            /* write row addr  */
            _nand_write_addr_byte(page & 0xff);            /* Row Address A12~A19 */
            _nand_write_addr_byte((page >> 8) & 0xff);    /* Row Address A20~A27 */
            _nand_write_addr_byte((page >> 16) & 0x01);    /* Row Address A28 */
    
            /* write cmd 0xD0 */
            _nand_write_cmd(0xD0);
    
            /* wait */
            _nand_wait_idle();
    
            /* write cmd 0x70 */
            _nand_write_cmd(0x70);
    
            if (_nand_read_data()&0x1)
            {
                return -1;
            }
            i += NAND_BLOCK_SIZE;
        }
    
        /* 取消片选信号 */
        _nand_deselect_chip();
        return 0;
    }
    
    
    /* 发送写数据命令, start_addr参数必须按页对齐  success:0 fail:-1*/
    int nand_write_data(u32 start_addr,u8 *buf, u32 size)
    {
        int i = 0;
        int j = 0;
    
        if (start_addr & NAND_PAGE_MASK_SIZE )
        {
            return -1;    /* 地址或长度不对齐 */
        }
    
        /* 选中芯片 */
        _nand_select_chip();
    
        /* 按页写 */
        while (i < size)
        {
          /* 发出WRITE命令 */
          _nand_write_cmd(0x80);
    
          /* write address */
          _nand_write_addr(start_addr + i);
    
          /* WRITE数据 */
          for(; (j < NAND_PAGE_SIZE) && (i<size); j++)
          {
              _nand_write_data(buf[i++]);
          }
    
          /* write cmd */
          _nand_write_cmd(0x10);
    
          /* wait */
          _nand_wait_idle();
    
          /* write cmd 0x70 */
          _nand_write_cmd(0x70);
    
          if (_nand_read_data()&0x1)
          {
              return -1;
          }
    
          j = 0;
        }
    
        /* 取消片选信号 */
        _nand_deselect_chip();
    
        return 0;
    }
    
    /* 初始化NAND Flash, 给nand_chip函数指针成员赋值*/
    void nand_init(void)
    {
        #define TACLS   0
        #define TWRPH0  1
        #define TWRPH1  0
    
        nand_chip._nand_reset          = _nand_reset;
        nand_chip._nand_wait_idle      = _nand_wait_idle;
        nand_chip._nand_select_chip    = _nand_select_chip;
        nand_chip._nand_deselect_chip  = _nand_deselect_chip;
        nand_chip._nand_write_cmd      = _nand_write_cmd;
        nand_chip._nand_write_addr     = _nand_write_addr;
        nand_chip._nand_read_data      = _nand_read_data;
        nand_chip.nand_read_data       = nand_read_data;
        nand_chip.nand_read_id         = nand_read_id;
        nand_chip.nand_block_erase     = nand_block_erase;
        nand_chip.nand_write_data      = nand_write_data;
    
        /* 设置时序 */
        NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
        /* 使能NAND Flash控制器, 初始化ECC, 禁止片选, 使能NAND控制器 */
        NFCONT = (1<<4)|(1<<1)|(1<<0);
    
        /* 复位NAND Flash */
        _nand_reset();
    }

    nand_read_data函数,每read一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。

    nand_write_data函数,每write一个page,都要重新发送命令地址,因为这里是顺序访问,flash的读写都是以page为单位的。

    nand_block_erase是以block为单位擦除的。

    实际上,NAND FLASH的型号K9F2G08U0C也支持随机读写,也就是支持单字节读写。这里就不介绍了,具体可以参考NAND FLASH实验读ID和随机读写

    需要注意的是:在这段代码中使用了全局变量t_nand_chip nand_chip,nand_chip未初始化,存放在未始化数据段,也就是bss段。

    3.4 main.c

    运行结果通过串口输出如下:

    四、代码下载

    Young / s3c2440_project【6.nand_flash】

    这个代码已经远超过4kb,并且没有将代码从NAND拷贝到SDRAM运行,只可以下载到SDRAM 0x30000000处运行。

    参考文章:

    [1] s3c2440裸机-nandflash编程(三. 初始化及识别)

    [2]s3c2440裸机-nandflash编程(四. nand读写擦实现)

  • 相关阅读:
    代码混淆那些事
    Windows10环境下 Nginx+ffmpeg自搭服务器制作RTMP直播流
    在Windows下搭建基于nginx的视频直播和点播系统
    windows下搭建基于nginx的rtmp服务器
    ijkplayer相关
    直播技术总结(三)ijkplayer的一些问题优化记录
    【.NET深呼吸】应用上下文(AppContext)
    【Win 10应用开发】自定义浮动层——Flyout
    【Win 10应用开发】AdaptiveTrigger在自定义控件中是可以触发的
    【.NET深呼吸】元组数据(Tuple)
  • 原文地址:https://www.cnblogs.com/zyly/p/15269309.html
Copyright © 2020-2023  润新知