您当前的位置: 首页 > 

韦东山

暂无认证

  • 0浏览

    0关注

    506博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

第016课 Nand Flash操作原理及裸机程序分析

韦东山 发布时间:2018-02-28 11:02:22 ,浏览量:0

第001节_NAND_FLASH操作原理

这里写图片描述

NAND FLASH原理图 NAND FLASH是一个存储芯片 那么: 这样的操作很合理”读地址A的数据,把数据B写到地址A”

问1. 原理图上NAND FLASH和S3C2440之间只有数据线,怎么传输地址? 答1.在DATA0~DATA7上既传输数据,又传输地址当ALE为高电平时传输的是地址,

那么在数据线上是不是只传输数据和只传输地址呢?

我们参考NAND FLASH的芯片手册可以知道,对NAND FLASH的操作还需要发出命令,下面有个NAND FLASH的命令表格 这里写图片描述 问2. 从NAND FLASH芯片手册可知,要操作NAND FLASH需要先发出命令怎么传入命令?

答2.在DATA0~DATA7上既传输数据,又传输地址,也传输命令:

  1. 当ALE为高电平时传输的是地址。

  2. 当CLE为高电平时传输的是命令。

  3. 当ALE和CLE都为低电平时传输的是数据。

问3. 数据线既接到NAND FLASH,也接到NOR FLASH,还接到SDRAM、DM9000等等 那么怎么避免干扰?

答3. 这些设备,要访问之必须”选中”,没有选中的芯片不会工作,相当于没接一样。

问4. 假设烧写NAND FLASH,把命令、地址、数据发给它之后,NAND FLASH肯定不可能瞬间完成烧写的,怎么判断烧写完成?

答4. 通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙

问5. 怎么操作NAND FLASH呢?

答5. 根据NAND FLASH的芯片手册,一般的过程是:

发出命令

发出地址

发出数据/读数据

看上面的命令表格,不容易看,我们看一下读ID的时序图, 这里写图片描述

每个NAND FLASH都内嵌一些ID(譬如:厂家ID,设备ID),时序图从左往右看,纵向放是一列一列的看。

对于我们s3c2440来说,内部集成了一个NAND FLASH控制器,2440和外设连接的简易图,如下图所示 这里写图片描述

NAND FLASH控制器,帮我们简化了对NAND FLASH的操作,下面来分析一下不使用NAND FLASH控制器和使用NAND FLASH控制器对外设NAND FLASH的操作。

发命令:

NAND FLASHS3C2440选中芯片NFCMMD=命令值CLE设为高电平在DATA0~DATA7上输出命令值发出一个写脉冲

发地址:

NAND FLASHS3C2440选中芯片NFADDR=地址值ALE设为高电平在DATA0~DATA7上输出地址值发出一个写脉冲

发数据:

NAND FLASHS3C2440选中芯片NFDATA=数据值ALE,CLE设为低电平在DATA0~DATA7上输出数据值发出一个写脉冲

读数据 :

NAND FLASHS3C2440选中芯片val=NFDATA发出读脉冲读DATA0~DATA7的数据

用UBOOT来体验NAND FLASH的操作:

  1. 读ID
S3C2440u-boot选中NFCONT的bit1设为0发出命令0x90NFCMMD=0x90发出地址0x00NFADDR=0x00读数据得到0xECval=NFDATA读数据得到device codeval=NFDATA退出读ID的状态NFCMMD=0xff

下图是读操作时序图 这里写图片描述

对于存储为256M的NAND FLASH,需要28条地址线,来表示这个地址值,根据原理图可以,只用8根地址线,所以需要4个周期的地址,为了兼容更大容量的NAND FLASH,要发出5个周期的地址:(如下图所示) 这里写图片描述

2,读数据

S3C2440u-boot选中NFCONT的bit1设为0发出命令0x00NFCMMD=0x00发出地址0x00NFADDR=0x00发出地址0x00NFADDR=0x00发出地址0x00NFADDR=0x00发出地址0x00NFADDR=0x00发出地址0x00NFADDR=0x00发出命令0x30NFCMMD=0x30读数据得到0x17val=NFDATA读数据得到0x00val=NFDATA读数据得到0x00val=NFDATA读数据得到0xeaval=NFDATA退出读状态NFCMMD=0xff 第002节_NandFlash时序及初始化 存储芯片的编程NAND FLASH存储芯片编程初始化主控芯片的NAND FLASH控制器的初始化识别读取ID读操作一次读一个页(page)写操作一次写一个页(page)擦除一次擦除一个块(block)

NAND FLASH控制器的时序,是为了让NAND FLASH外设工作起来,假如外接不同的 NAND FLASH外设,那么它的操作时序可能就会不同,所以NAND FLASH控制器发出 的时序图,就是不一样的,所以我们根据NAND FLASH外设来设置NAND FLASH控制器, 这里写图片描述

NAND FLASH时序图,如下所示: 这里写图片描述 这里写图片描述 这里写图片描述 我们在汇编语言中已经设置HCLK为100MHZ,一个周期T = 1000/100 = 10s,通过上面三个图可以知道:TACLS的值可以为0;TWRPH0的值可以为1;TWRPH1的值可以为0。 这里写图片描述 所以NFCONF寄存器设置如下:

#define  TACLS   0
#define  TWRPH0  1
#define  TWRPH1  0
/*设置NAND FLASH的时序*/
NFCONF = (TACLS8) & 0xff);
        nand_addr_byte((page>>16) & 0xff);

        /* 发出30h命令 */
        nand_cmd(0x30);

        /* 等待就绪 */
        wait_ready();

        /* 读数据 */
        for (; (col < 2048) && (i < len); col++)
        {
            buf[i++] = nand_data();         
        }
        if (i == len)
            break;

        col = 0;
        page++;
    }

    nand_deselect();    
}

在init.c文件中,加上如下代码,用来判断所使用的FLASH是NOR FLASH还是NAND FLASH。代码如下:

int isBootFromNorFlash(void)
{
    volatile unsigned int *p = (volatile unsigned int *)0;
    unsigned int val = *p;

    *p = 0x12345678;
    if (*p == 0x12345678)
    {
        /* 写成功, 对应nand启动 */
        *p = val;
        return 0;
    }
    else
    {
        return 1;
    }
}

在init.c文件中的copy2sdram函数里面加上如下代码,用来支持NAND FLASH启动,当isBootFromNorFlash函数的返回值为1时,是从NOR FLASH启动,当isBootFromNorFlash函数的返回值为0是,是从NAND FLASH启动。

if (isBootFromNorFlash())
    {
        while (dest < end)
        {
            *dest++ = *src++;
        }
    }
    else
    {
        nand_init();
        nand_read(src, dest, len);
    }
}
第005节_NandFlash的擦除与烧写

我们本节需要做的事情:

1,实现nand_erase

2, 实现nand_write

3, 实现测试菜单

下面我们逐个来实现他们:

本节讲的NAND FLASH的烧写和擦除还是比较简单的,它只涉及到页数据区,不涉及到oob区,擦出的时候是以块为单位。下图为擦除的时序图: 这里写图片描述

我们就根据擦除的时序图发出对应的命令和地址,NAND FLASH是以块为单位进行擦除的,假如我们传入len的值为1,但是它仍然会擦出一个块(128k字节),我们根据芯片手册,来操作NAND FLASH的擦出操作,函数功能:从addr地址开始,擦除len长度的数据。代码如下:

nt nand_erase(unsigned int addr, unsigned int len)
{
    int page = addr / 2048;

    if (addr & (0x1FFFF))
    {
        printf("nand_erase err, addr is not block align\n\r");
        return -1;
    }

    if (len & (0x1FFFF))
    {
        printf("nand_erase err, len is not block align\n\r");
        return -1;
    }

    nand_select(); 

    while (1)
    {
        page = addr / 2048;

        nand_cmd(0x60);

        /* row/page addr */
        nand_addr_byte(page & 0xff);
        nand_addr_byte((page>>8) & 0xff);
        nand_addr_byte((page>>16) & 0xff);

        nand_cmd(0xD0);

        wait_ready();

        len -= (128*1024);
        if (len == 0)
            break;
        addr += (128*1024);
    }

    nand_deselect();    
    return 0;
}

操作NAND FLASH之前要,选中芯片,然后就可以根据芯片手册来操作NAND FLASH的擦除操作了,操作完之后,要取消片选。

往NAND FLASH写数据时,只需要把要写的数据复制给NFDATA寄存器即可。代码如下:

void nand_w_data(unsigned char val)
{
    NFDATA = val;
}

下图为烧写的时序图: 这里写图片描述 从上图中的NAND FLASH烧写时序图可以知道对于NAND FLASH的烧写,先发出0x80命令,再发出地址周期,然后发出要烧写的数据,最后发出0x10,就开始内部烧写,然后等待烧写成功。(我们写数据的时候是逐页写的,开始要烧写的数据地址可能不是该页的起始地址)。操作之前需要选中片选,操作完之后取消片选,代码如下

void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)
{
    int page = addr / 2048;
    int col  = addr & (2048 - 1);
    int i = 0;

    nand_select(); 

    while (1)
    {
        nand_cmd(0x80);

        /* 发出地址 */
        /* col addr */
        nand_addr_byte(col & 0xff);
        nand_addr_byte((col>>8) & 0xff);

        /* row/page addr */
        nand_addr_byte(page & 0xff);
        nand_addr_byte((page>>8) & 0xff);
        nand_addr_byte((page>>16) & 0xff);

        /* 发出数据 */
        for (; (col < 2048) && (i < len); )
        {
            nand_w_data(buf[i++]);
        }
        nand_cmd(0x10);
        wait_ready();

        if (i == len)
            break;
        else
        {
            /* 开始下一个循环page */
            col = 0;
            page++;
        }

    }

    nand_deselect();    
}

我们封装擦除操作NAND FLASH函数的时候,每一次擦除的大小是一个块(128*1024)代码如下:

void do_erase_nand_flash(void)
{
    unsigned int addr;

    /* 获得地址 */
    printf("Enter the address of sector to erase: ");
    addr = get_uint();

    printf("erasing ...\n\r");
    nand_erase(addr, 128*1024);
}

我们封装读取操作NAND FLASH函数,我们实现NAND FLASH每次的读取,每次读取64字节数据。把从地址addr读取得到的64字节数据存放到buf缓冲区中,然后通过串口显示出来,代码如下图所示:

void do_read_nand_flash(void)
{
    unsigned int addr;
    volatile unsigned char *p;
    int i, j;
    unsigned char c;
    unsigned char str[16];
    unsigned char buf[64];

    /* 获得地址 */
    printf("Enter the address to read: ");
    addr = get_uint();

    nand_read(addr, buf, 64);
    p = (volatile unsigned char *)buf;

    printf("Data : \n\r");
    /* 长度固定为64 */
    for (i = 0; i < 4; i++)
    {
        /* 每行打印16个数据 */
        for (j = 0; j < 16; j++)
        {
            /* 先打印数值 */
            c = *p++;
            str[j] = c;
            printf("%02x ", c);
        }

        printf("   ; ");

        for (j = 0; j < 16; j++)
        {
            /* 后打印字符 */
            if (str[j] < 0x20 || str[j] > 0x7e)  /* 不可视字符 */
                putchar('.');
            else
                putchar(str[j]);
        }
        printf("\n\r");
    }
}

NAND FLASH的烧写封装函数代码如下:

void do_write_nand_flash(void)
{
    unsigned int addr;
    unsigned char str[100];
    int i, j;
    unsigned int val;

    /* 获得地址 */
    printf("Enter the address of sector to write: ");
    addr = get_uint();

    printf("Enter the string to write: ");
    gets(str);

    printf("writing ...\n\r");
    nand_write(addr, str, strlen(str)+1);

}

NAND FLASH的测试菜单函数代码如下:

void nand_flash_test(void)
{
    char c;

    while (1)
    {
        /* 打印菜单, 供我们选择测试内容 */
        printf("[s] Scan nand flash\n\r");
        printf("[e] Erase nand flash\n\r");
        printf("[w] Write nand flash\n\r");
        printf("[r] Read nand flash\n\r");
        printf("[q] quit\n\r");
        printf("Enter selection: ");

        c = getchar();
        printf("%c\n\r", c);

        /* 测试内容:
         * 1. 识别nand flash
         * 2. 擦除nand flash某个扇区
         * 3. 编写某个地址
         * 4. 读某个地址
         */
        switch (c)       
        {
            case 'q':
            case 'Q':
                return;
                break;

            case 's':
            case 'S':
                nand_chip_id();
                break;

            case 'e':
            case 'E':
                do_erase_nand_flash();
                break;

            case 'w':
            case 'W':
                do_write_nand_flash();
                break;

            case 'r':
            case 'R':
                do_read_nand_flash();
                break;
            default:
                break;
        }
    }
}
关注
打赏
1658827356
查看更多评论
立即登录/注册

微信扫码登录

0.0445s