第10课-NOR FLASH

    注:以下内容学习于韦东山老师arm裸机第一期视频教程


    一.nor Flash原理及硬件操作


        使用UBOOT体验NOR FLASH的操作(开发板设为NOR启动,进入UBOOT)

        先使用OpenJTAG烧写UBOOT到NOR FLASH


        1.1  读数据

             md.b 0


        1.2 读ID

            NOR手册上:

            往地址555H写AAH

            往地址2AAH写55H

            往地址555H写90H

            读0地址得到厂家ID: C2H

            读1地址得到设备ID: 22DAH或225BH

            退出读ID状态: 给任意地址写F0H

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

            UBOOT怎么操作?

            往地址AAAH写AAH                         mw.w aaa aa

            往地址554写55H                              mw.w 554 55

            往地址AAAH写90H                          mw.w aaa 90

            读0地址得到厂家ID: C2H                  md.w 0 1

            读2地址得到设备ID: 22DAH或225BH      md.w 2 1

            退出读ID状态:                                   mw.w 0 f0


        1.3 NOR有两种规范, jedec, cfi(common flash interface)

            读取CFI信息

            NOR手册:   

            进入CFI模式    往55H写入98H

            读数据:    读10H得到0051

                           读11H得到0052

                           读12H得到0059

                           读27H得到容量

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

            UBOOT怎么操作?

            进入CFI模式    往AAH写入98H            mw.w aa 98

            读数据:    读20H得到0051                   md.w 20 1

                           读22H得到0052                   md.w 22 1

                           读24H得到0059                   md.w 24 1

                           读4EH得到容量                    md.w 4e 1

                           退出CFI模式                         mw.w 0 f0


        1.4 写数据: 在地址0x100000写入0x1234

            md.w 100000 1     // 得到ffff

            mw.w 100000 1234

            md.w 100000 1     // 还是ffff

            NOR手册:

            往地址555H写AAH

            往地址2AAH写55H

            往地址555H写A0H

            往地址PA写PD

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

            UBOOT怎么操作?

            往地址AAAH写AAH               mw.w aaa aa

            往地址554H写55H               mw.w 554 55

            往地址AAAH写A0H               mw.w aaa a0

            往地址0x100000写1234h         mw.w 100000 1234


        1.5 再次往0x100000写入0x5678

            因为原来0x100000上的数据不是0xffff,再次烧写失败(NOR只能将1写为0,因此在再次烧些之前需要NOR FLASH上面的数据全是1,即进行擦除操作)

            往地址AAAH写AAH               

            往地址554H写55H               

            往地址AAAH写A0H               

            往地址0x100000写5678h         mw.w 100000 5678


            1.5.1 先擦除

            mw.w aaa aa

            mw.w 554 55

            mw.w aaa 80

            mw.w aaa aa

            mw.w 554 55

            mw.w 100000 30



            1.5.2 再烧写

            mw.w aaa aa

            mw.w 554 55

            mw.w aaa a0

            mw.w 100000 5678

            

    二.NorFlash编程_识别

       
       #include "my_printf.h"
        #include "string_utils.h"

        #define NOR_FLASH_BASE  0  /* jz2440, nor-->cs0, base addr = 0 */

        /* 比如:   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 short *)(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);
        }


        /* 进入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 */
            
            nor_cmd(0x55, 0x98);  /* 进入cfi模式 */

            str[0] = nor_dat(0x10);
            str[1] = nor_dat(0x11);
            str[2] = nor_dat(0x12);
            str[3] = '\0';
            printf("str = %s\n\r", str);

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

            /* 打印各个扇区的起始地址 */
            /* 名词解释:
             *    erase block region : 里面含有1个或多个block, 它们的大小一样
             * 一个nor flash含有1个或多个region
             * 一个region含有1个或多个block(扇区)

             * Erase block region information:
             *    前2字节+1    : 表示该region有多少个block
             *    后2字节*256  : 表示block的大小
             */

            regions = nor_dat(0x2c);
            region_info_base = 0x2d;
            block_addr = 0;
            printf("Block/Sector start Address:\n\r");
            cnt = 0;
            for (i = 0; i < regions; i++)
            {
                blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8);
                block_size = 256 * (nor_dat(region_info_base+2) + (nor_dat(region_info_base+3)<<8));
                region_info_base += 4;

        //        printf("\n\rregion %d, blocks = %d, block_size = 0x%x, block_addr = 0x%x\n\r", i, blocks, block_size, block_addr);

                for (j = 0; j < blocks; j++)
                {
                    /* 打印每个block的起始地址 */
                    //printf("0x%08x ", block_addr);
                    printHex(block_addr);
                    putchar(' ');
                    cnt++;
                    block_addr += block_size;
                    if (cnt % 5 == 0)
                        printf("\n\r");
                }
            }
            printf("\n\r");
            /* 退出CFI模式 */
            nor_cmd(0, 0xf0);
        }

        void do_erase_nor_flash(void)
        {
        }

        void do_write_nor_flash(void)
        {
        }
        void do_read_nor_flash(void)
        {
        }

        void nor_flash_test(void)
        {
            char c;

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

                c = getchar();
                printf("%c\n\r", 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;
                }
            }
        }

        注意,

        1. 编译程序时加上: -march=armv4

            否则

           volatile unsigned short *p = xxx;

           *p = val; // 会被拆分成2个strb操作

        2. 把timer中断去掉

           否则: 测试nor时进入CFI等模式时, 如果发生了中断,cpu必定读NOR,

           那么读不到正确的指令,导致程序崩溃


    三.NOR_FLASH的编程_擦写读

        #include "my_printf.h"
        #include "string_utils.h"


        #define NOR_FLASH_BASE  0  /* jz2440, nor-->cs0, base addr = 0 */


        /* 比如:   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 short *)(base + (offset << 1));
            *p = val;
        }

        /* offset是基于NOR的角度看到 */
        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 */
            
            nor_cmd(0x55, 0x98);  /* 进入cfi模式 */

            str[0] = nor_dat(0x10);
            str[1] = nor_dat(0x11);
            str[2] = nor_dat(0x12);
            str[3] = '\0';
            printf("str = %s\n\r", str);

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

            /* 打印各个扇区的起始地址 */
            /* 名词解释:
             *    erase block region : 里面含有1个或多个block, 它们的大小一样
             * 一个nor flash含有1个或多个region
             * 一个region含有1个或多个block(扇区)

             * Erase block region information:
             *    前2字节+1    : 表示该region有多少个block
             *    后2字节*256  : 表示block的大小
             */

            regions = nor_dat(0x2c);
            region_info_base = 0x2d;
            block_addr = 0;
            printf("Block/Sector start Address:\n\r");
            cnt = 0;
            for (i = 0; i < regions; i++)
            {
                blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8);
                block_size = 256 * (nor_dat(region_info_base+2) + (nor_dat(region_info_base+3)<<8));
                region_info_base += 4;

        //        printf("\n\rregion %d, blocks = %d, block_size = 0x%x, block_addr = 0x%x\n\r", i, blocks, block_size, block_addr);

                for (j = 0; j < blocks; j++)
                {
                    /* 打印每个block的起始地址 */
                    //printf("0x%08x ", block_addr);
                    printHex(block_addr);
                    putchar(' ');
                    cnt++;
                    block_addr += block_size;
                    if (cnt % 5 == 0)
                        printf("\n\r");
                }
            }
            printf("\n\r");
            /* 退出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 ...\n\r");
            nor_cmd(0x555, 0xaa);    /* 解锁 */
            nor_cmd(0x2aa, 0x55);
            nor_cmd(0x555, 0x80);     /* erase sector */
            
            nor_cmd(0x555, 0xaa);    /* 解锁 */
            nor_cmd(0x2aa, 0x55);
            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 ...\n\r");

            /* str[0],str[1]==>16bit
             * str[2],str[3]==>16bit
             */
            i = 0;
            j = 1;
            while (str[i] && str[j])
            {
                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 : \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");
            }
        }

        void nor_flash_test(void)
        {
            char c;

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

                c = getchar();
                printf("%c\n\r", 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;
                }
            }
        }

猜你喜欢

转载自blog.csdn.net/qq_36521904/article/details/80777438
今日推荐