第15课.Nor Flash

Posted huangdengtao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第15课.Nor Flash相关的知识,希望对你有一定的参考价值。

1.Nor Flash与Nand Flash的对比

技术图片

a.Nor Flash的块大小范围为64kb,128kb:Nand Flash的块大小范围为8kb,64kb,擦/写一个Nor Flash块需4s,而擦/写一个Nand Flash块仅需2ms
b.Nand Flash一般以512字节为单位进行读写。这使得Nor Flash适合运行程序,而Nand Flash更适合于存储数据
c.容量相同的情况下,Nand Flash的体积更小。Nor Flash的容量通常为1MB~4MB(也有32M的Nor Flash),Nand Flash的容量为8MB~512MB
d.在Nor Flash上常用jffs2文件系统,而在Nand Flash常用yaffs文件系统。在更底层,有MTD驱动程序实现对他们的读,写,擦除操作。它也实现了EDC/ECC校验
e.这里用的Nor里面有4个bank,每个bank里面有若干block(擦除大小),不同bank里面的block可能不一样

2.引脚接口

技术图片

2440的A1接到Nor的A0,所以2440发出(555h<<1)(左移一位),Nor才能收到555h这个地址

3.NOR FLASH的命令

技术图片

解析:
    Read Mood:先发送地址,然后就有数据得到和正常的读内存是一样的
    Reset Mood:往任意地址写0xF0都退出特殊模式
    读ID:Nor Flash分为1字节和2字节模式,所以有两种情况的操作指令,板载2字节的操作如下:
        ·发送地址555,发送数据AA,0x555*2 = 0xaaa
        ·发送地址2AA,发送数据55,0x2AA*2 = 0x554 
        ·发送地址555,发送数据90,0x555*2 = 0xaaa
        ·发送地址00, 发送数据C2

技术图片

注意:
    1.板载的MCU的A1接到Nor的A0,所以也就是Nor获得的地址为,实际MCU出的地址<<1(2的多少次方就是<<多少位)
    2.写数据时需确保addr上的数据为0xffff,否则写失败,即需要先擦除,再烧写
    3.CFI是制定的一个接口,用来帮助读取Flash制造商ID和设备ID,确定Flash大小物理特性,以及block的信息
    

技术图片

1. 读数据
2. 读ID
    往地址555H写AAH
    往地址2AAH写55H
    往地址555H写90H
    读0地址得到厂家ID: C2H
    读1地址得到设备ID: 22DAH或225BH
    退出读ID状态: 给任意地址写F0H
3.读取CFI信息   
进入CFI模式    往55H写入98H
读数据:        读10H得到0051
          读11H得到0052
              读12H得到0059
          读27H得到容量
4. 写数据
    往地址555H写AAH 
    往地址2AAH写55H 
    往地址555H写A0H 
    往地址PA写PD
5.擦除
    往地址AAAH写AAH   
    往地址554H写55H
    往地址AAAH写80H 

    往地址AAAH写AAH   
    往地址554H写55H
    往地址写30H
6.region and block
    进入CFI模式    往55H写入98H
    读地址0x2c得到regions
    读0x2c后的4位地址0x2d, 0x2e, 0x2f, 0x30得到blocks,block size
    继续读下一个region,知道region读完

    a.erase block region
        一个Nor Flash含有一个或多个region,一个region含有1个或多个block(block大小相等)
    b.erase block region information:
        前2字节 + 1:表示该region有多少个block
        后2字节 * 256:表示block的大小
        eg:
            写2c读erase block region的个数
            前两字节:
                2D         低位
                2E << 8    高位
            后两字节:
                2F         低位
                3D << 8    高位

4.代码

nor_flash.c

/*  比如: 55H 98
 *  本意是往(0 + (0x55) << 1 )写入0x98
 */
void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)
{
    volatile unsigned short *p = (volatile unsigned int *)(base + (offset << 1))
    *p = val;
}

void nor_cmd(unsigned int offset, unsigned int cmd)
{
    nor_write_word(NOR_FLASH_BASE, offset, cmd);
}

unsigned int nor_read_word(unsigned int base, unsigned int offset)
{
    volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
    return *p;
}

unsigned int nor_dat(unsigned int offset)
{
    return nor_read_word(NOR_FLASH_BASE, offset);
}

/*      */
void wait_ready(unsigned int addr)
{
    unsigned int val;
    unsigned int pre;

    pre = nor_dat(addr>>1);
    val = nor_dat(addr>>1);

    while((val & (1<<6)) != (pre & (1<<6)))
    {
        pre = val;
        val = nor_dat(addr>>1);
    }
}

/*  进入NOR FLASH的CFI模式   
 *  读取各类信息
 */
void do_scan_nor_flash(void)
{
    char str[4];
    unsigned int size;
    int regions, i;
    int region_info_base;
    int block_addr, blocks,block_size, j;
    int cnt;

    int vendor, device;

    /*  打印厂家ID,设备ID */
    nor_cmd(0x555, 0xaa);   /*  解锁  */
    nor_cmd(0x2aa, 0x55);
    nor_cmd(0x555, 0x90);   /*  read id */
    vendor = nor_dat(0);
    device = nor_dat(1);
    nor_cmd(0, 0xf0);       /*  reset   */

    /*  进入CFI模式 */
    nor_cmd(0x55, 0x98);

    str[0] = nor_dat(0x10);
    str[1] = nor_dat(0x11);
    str[2] = nor_dat(0x12);
    str[3] = '';

    printf("str = %s

", str);

    /*  打印容量    */
    size = 1 << (nor_dat(0x27));
    printf("vendor id = 0x%x, device id = 0x%x, nor size = 0x%x, %dM

", vendor, device, size, size/(1024*1024));

    /*  打印各个扇区的起始地址 */
    regions = nor_dat(0x2c);
    region_info_base = 0x2d;
    block_addr = 0;
    printf("Block/Sector start Address:

");
    cnt = 0;

    for(i = 0; i < regions; i++)
    {
        blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base + 1) << 8);
        block_size = 255 * (nor_dat(region_info_base + 2) + (nor_dat(region_info_base + 3) << 8));
        region_info_base += 4;

        /*  打印每个block的起始地址  */
        for(j = 0; j < blocks; j++)
        {
            printHex(block_addr);
            putchar(' ')
            cnt++;
            block_addr += block_size;
            if(cnt % 5 == 0)
            {
                printf("

");
            }
        }
    }
    printf("

");

    /*  退出CFI模式 */
    nor_cmd(0, 0xf0);
}

void do_erase_nor_flash(void)
{
    unsigned int addr;

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

    printf("erasing ...

");
    
    nor_cmd(0x555, 0xaa);   /*  解锁  */
    nor_cmd(0x2aa, 0x55);   
    nor_cmd(0x555, 0x80);   /*  erase sector    */

    nor_cmd(0x2aa, 0x55);   /*  解锁  */
    nor_cmd(0x2aa, 0x55);
    /*  擦除是的地址是对nor flash而言此地址不是cpu发出和控制的
     *  所以这里的地址不需要<<1位。
     *  如读写命令是从CPU发出的所以命令需要<<1位
     */
    nor_cmd(addr>>1, 0x30); /*  发出扇区地址  */
    
    wait_ready(addr);   
}


void do_write_nor_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 ...

");
    /*  写的数据是16位的
     *  str[0], str[1] ==> 16bit
     *  str[2], str[3] ==> 16bit
     */
    i = 0;
    j = 1;
    while(str[i] + (str[j] << 8))
    {
        val = str[i] + (str[j] << 8);

        /*  烧写  */
        nor_cmd(0x555, 0xaa);   /*  解锁  */
        nor_cmd(0x2aa, 0x55);   
        nor_cmd(0x555, 0xa0);   /*  program */
        nor_cmd(addr>>1; val);

        /*  等待烧写完成: 读数据,Q6无变化是表示结束  */
        wait_ready(addr);

        i += 2;
        j += 2;
        addr += 2;
    }

    val = str[i];
    /*  烧写  */
    nor_cmd(0x555, 0xaa);    /* 解锁 */
    nor_cmd(0x2aa, 0x55); 
    nor_cmd(0x555, 0xa0);    /* program */
    nor_cmd(addr>>1, val);
    /* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
    wait_ready(addr);
}

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

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

    p = (volatile unsigned char*)addr;
    printf("Data : 

");

    /*  固定长度为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("

");
    }
}

void nor_flash_test(void)
{
    char c;

    while(1)
    {
        /*  打印菜单,供我们选择测试内容  */
        printf("[s] Scan nor flash

");
        printf("[e] Erase nor flash

");
        printf("[w] Write nor flash

");
        printf("[r] Read nor flash

");
        printf("[q] quit

");
        printf("Enter selection: ");

        c = getchar();
        printf("%c

", c);

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

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

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

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

            case 'r':
            case 'R':
                do_read_nor_flash();
                break;

            default:
                break;
        }
    }
}

Makefile

all: start.o led.o uart.o init.o main.o exception.o interrupt.o timer.o nor_flash.o my_printf.o string_utils.o lib1funcs.o
#arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf
arm-linux-ld -T sdram.lds $^ -o sdram.elf
arm-linux-objcopy -O binary -S sdram.elf sdram.bin
arm-linux-objdump -D sdram.elf > sdram.dis
clean:
rm .bin .o .elf .dis

%.o : %.c
arm-linux-gcc -march=armv4 -c -o $@ $<

%.o : %.S
arm-linux-gcc -march=armv4 -c -o $@ $<

注意事项

1. 编译程序时加上: -march=armv4
   否则 
   volatile unsigned short *p = xxx;
   *p = val; // 会被拆分成2个strb操作

2. 把timer中断去掉
   否则: 测试nor时进入CFI等模式时, 如果发生了中断,cpu必定读NOR,
   那么读不到正确的指令,导致程序崩溃

以上是关于第15课.Nor Flash的主要内容,如果未能解决你的问题,请参考以下文章

SPI FLASH与NOR FLASH的区别?

SPI FLASH与NOR FLASH的区别?

u-boot移植---代码修改---支持nor flash

十u-boot 调试-- NOR FLASH 支持

NAND Flash和NOR Flash的比较

请问nand flash和nor flash有啥不同?