title: NorFlash 学习

tags: ARM

date: 2018-10-19 18:31:59

NorFlash 学习

NOR/NAND Flash的差别

NOR NAND
容量 1~32MB 16~512MB
XIP 可执行程序 Yes(因为是内存接口), 可以随机访问任意地址的数据 No
擦除 非常慢(5s) 快(3ms)
可靠性 比较高,位反转的比例小于NAND Flash 的10% 比较低:,位反转比较常见,必需有校验措施,比如“1-4 bit EDC/ECC”;必须有坏块管理措施
可擦除次数 10000~100000 100000~1000000
生命周期 低于NAND Flash的10% 是NOR Flash的10倍以上
接口 RAM接口相同 I/O接口
访问方法 随机访问 顺序访问
易用性 容易 复杂
主要用途 常用于保存代码和关键数据 用于保存数据
价格
  • Nor Flash的块大小范围为64kB、128kB:NAND Flash的块大小范围为8kB,64kB,擦/写一个Nor Flash块需4s,而擦/写一个NAND Flash块仅需2ms。
  • NAND Flash一般以512字节为单位进行读写。这使得Nor Flash适合于运行程序,而NAND Flash更适合于存储数据
  • 容量相同的情况下,NAND Flash的体积更小.Nor Flash的容量通常为IMB~4MB(也有32MB的Nor Flash),NAND Flash的容量为8MB~512MB。
  • 在Nor Flash上常用jffs2文 件系统,而在NAND Flash常用yaffs文件系统。在更底层,有MTD驱动程序实现对它们的读、写、擦除操仵,它也实现了EDC/ECC校验。
  • 这里用的nor里面有4个bank,每个bank里面有若干block(擦除大小),不同bank里面的block可能不一样的

引脚接口

更多信息参考 2440内存管理

这里NorFlash是16位的,所以地址线忽略A0

命令

所谓命令是什么意思呢?

  • Read MODE,就是先发送地址,然后就有数据得到,和正常的读内存是一样的

  • Reset Mode,往任意地址写0xF0都退出特殊模式

  • 读ID,NOR Flash 分为1字节和2字节模式,所以有两种情况的操作指令.板载是2字节的.操作如下:

    1. 发送地址555,发送数据AA, 0x555*2=0xaaa
    2. 发送地址2AA,发送数据55 0x2AA*2=0x554
    3. 发送地址555,发送数据90 0x555*2=0xaaa
    4. 发送地址00,收到数据C2

但是板载的MCU的A1接到Nor的A0,所以也就是Nor获得地址X,实际MCU需要发送地址X<<1

Nor Flash的操作 2440的操作 U-BOOT上的操作
往地址555H写入AAH(解锁) 往地址AAAH写入AAH(解锁) mw.w aaa aa
往地址2AAH写入55H(解锁) 往地址554H写入55H(解锁) mw.w 554 55
往地址555H写入90H(命令) 往地址AAAH写入90H(命令) mw.w aaa 90
读0地址得到厂家ID(C2H) 读0地址得到厂家ID(C2H) md.w 0 1 (1:表示读一次)
读1地址得到设备ID(22DAH或225BH) 读2地址得到设备ID 2249 md.w 2 1 (1:表示读一次)
退出读ID状态:给任意地址写F0H就可以了 退出读ID状态:给任意地址写F0H就可以了 mw.w 0 f0

注意,这个uboot应该在NOR上启动,因为NAND启动的时候,看不到NORFLASH

Device ID : MX29LV160DT: 22C4; MX29LV160DB: 2249.

OpenJTAG> mw.w 0 f0
OpenJTAG> mw.w aaa aa
OpenJTAG> mw.w 554 55
OpenJTAG> mw.w aaa 90
OpenJTAG> md.w 0 1
00000000: 00c2 ..
OpenJTAG> md.w 2 1
00000002: 2249 I"

参数识别(CFI)

CFI(common flash interface)探测,就是直接发各种命令来读取芯片的信息,比如 ID、容量等,芯片本身就包含了电压有多大,容量有有多少等信息.其实就是先用55地址写98,然后读参数.

一般情况下先去读取是否支持CFI,也就是

  1. 写98H到55H
  2. 查询0x10,0x11,0x12是否为QRY字符串
  3. 如果是,那么支持CFI接口
Nor Flash上操作cfi 2440上操作cfi U-BOOT上操作cfi
往55H地址写入98H 往AAH地址写入98H mw.w aa 98
读地址10H得到0051 读地址20H得到0051 md.w 20 1
读地址11H得到0052 读地址22H得到0052 md.w 22 1
读地址12H得到0059 读地址24H得到0059 md.w 24 1
读地址27H得到容量 读地址4EH得到容量 md.w 4e 1

下图中读取地址10,读取到0x0051='Q',比如读设备大小地址0x27,会读到0x15=2**15/1024/1024=2M,具体的一些参数参考标准文档CFI100

识别存储信息,其中具体的信息要搜索文档CFI100

可以看到有4个bank reggion,region里又若干sector.这个是擦除最小单位了.根据计算如下.其中2D为低位,2E为高位.block=[2E:2D]+1,

blocks sector size
bank1 1 256*0x0040/1024=16k
bank2 2 256*0x0020/1024=8k
bank3 1 256*0x0080/1024=32k
bank3 0x1E+1=31 256*0x0100/1024=64k
总计 35个block 空间大小=16+2*8+32+64*31=2048k=2M

CFI描述如下

备注手册里另一张图Table 1-1. MX29LV160DT SECTOR ARCHITECTURE好像是倒着来的扇区排序

写数据

NorFlash也是一种FLASH,需要先擦除才能写数据.对于已经擦除的单元,同样需要命令才能写入

Nor Flash上操作写操作 2440上操作写操作 U-BOOT上操作写操作
往地址555H写AAH(解锁) 往地址AAAH写AAH(解锁) mw.w aaa aa
往地址2AAH写55H(解锁) 往地址554H写55H(解锁) mw.w 554 55
往地址555H写A0H 往地址AAAH写A0H mw.w aaa a0
往地址PA写PD 往地址0x100000写1234h mw.w 100000 1234

命令参考,往1M的地方写,地址需要*2,也就是0x20,0000


OpenJTAG> mw.w aaa aa
OpenJTAG> mw.w 554 55
OpenJTAG> mw.w aaa 80
OpenJTAG> mw.w aaa aa
OpenJTAG> mw.w 554 55
OpenJTAG> mw.w 200000 30
OpenJTAG> mw.w 0 f0
OpenJTAG> md.w 200000 1
00200000: ffff .. //读取
OpenJTAG> mw.w aaa aa
OpenJTAG> mw.w 554 55
OpenJTAG> mw.w aaa a0
OpenJTAG> mw.w 200000 1234
OpenJTAG> md.w 200000 1
00200000: 1234 4. //接下来不能直接写了 又需要重新来执行命令
OpenJTAG> mw.w 200004 5555
OpenJTAG> md.w 200004
00200004: ffff ..

程序设计

注意 板载的是16位位宽,地址线是CPU的A1接到NOR的A0,所以MCU地址=NOR指令地址*2.

调试代码 的时候发现工具MobaXterm_Personal_10.4有时候无法接受串口输入,重启下软件串口

  • 时序设置: 参考内存管理.md

  • 基本的读写操作

     #define NOR_BASE_BANK   0
    
    /**
    * @brief 在基于Nor cmd_addr的位置写入 val
    * 比如在55H的地方写入x,也就是地址信号发出55,
    * 对应实际的地址线为55<<1 地址线 加上bank的基地址,
    * 所以cpu实际的地址是 base+(55<<1)
    * @param base
    * @param addr
    * @param val
    */
    void NorWriteU16(uint16_t base,uint16_t addr, uint16_t val)
    {
    volatile uint16_t* p= (volatile uint16_t* )((addr<<1)+base);
    *p=val;
    }
    /**
    * @brief 读取2字节
    *
    * @param base
    * @param addr
    * @param val
    */
    uint16_t NorReadU16(uint16_t base,uint16_t addr)
    {
    volatile uint16_t* p= (volatile uint16_t* )((addr<<1)+base);
    return *p;
    } /**
    * @brief CMD写入16位数据
    * @param addr cmd地址
    * @param val 写入的值
    */
    void NorFlashCMDPut(uint16_t addr,uint16_t val )
    {
    NorWriteU16(NOR_BASE_BANK,addr,val);
    }
    /**
    * @brief CMD读取Nor的16位数据
    *
    * @param addr
    * @param val
    * @return uint16_t
    */
    uint16_t NorFlashCMDGet(uint16_t addr)
    {
    return NorReadU16(NOR_BASE_BANK,addr);
    }
  • 识别CFI

    void NorFlashInfo(void)
    {
    // 进入CFI模式,INTERFACE (CFI) MODE
    NorFlashCMDPut(0x55,0x98);
    //开始读取数据
    uint8_t buf[10]={0,0,0,0,0,0,0,0,0};
    //尝试读取“QRY”
    buf[0]=(uint8_t)NorFlashCMDGet(0x10);
    buf[1]=(uint8_t)NorFlashCMDGet(0x11);
    buf[2]=(uint8_t)NorFlashCMDGet(0x12);
    buf[3]=(uint8_t)'\0';
    printf("get 0x20,0x22,0x24=%s\r\n",buf);
    printf("end\r\n");
    NorFlashCMDPut(0, 0xf0);
    }
  • NOR的擦除与写

    注意: NOR的物理地址是需要CPU地址>>1的,所以操作地址的时候之前的底层函数已经使用了cpu地址>>1

    但是,Nor是16位位宽的,也就是如下排列的

    16位宽 8位宽
    00 00,01
    01 02,03
    02 04,05
    03 06,17

    我们需要将8位位宽的地址转换为16位位宽的地址,也就是说addr>>1.

    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);
    }

    FAQ:

    在老师的视频中,写NOR的具体地址时,nor_cmd(addr>>1, val); 这里地址右移1位,老师的解释是地址线A1->A0的缘故.但是我觉得不对.

    因为 nor_cmd函数已经将地址转换为nor视角的地址了,也就是说我想写nor中的x地址,我们应该直接操作地址x即可.

    但是测试之后发现代码确实跑的没问题?那么我哪里理解错了呢?

    后来想到 Nor是16位位宽的,也就是说Nor中的0,其实对应了存储单元[0,1],

    结论: 所以这里的地址>>1,并不是因为地址线偏移(其实也是因为地址线,因为是16位,所以偏移1位),但是从函数的角度来说,实际是因为nor的地址是16位宽的,我们需要转换为8位位宽的 addr>>1 变成16位位宽的地址

    后面的测试了下往 addr=1里面写12,实际是在0,1里面写,往addr=2里面写12,实际是往2里面写,往addr=3里面写,也是在往2里面写

    参考链接

  • 判断是否忙(DQ6)

    • DQ7:Data# Polling bit,DQ7在编程时的状态变化.

      在编程过程中从正在编程的地址中读出的数据的DQ7为要写进数据的补码.比如写进的数据为0x0000,及输进的DQ7为'0',则在编程中读出的数据为'1';当编程完成时读出的数据又变回输进的数据即'0'. 在擦除过程中DQ7输出为'0';擦除完成后输出为'1';留意读取的地址必须是擦除范围内的地址.

      RY/BY#:高电平表示'停当',低电平表示'忙'.

    • DQ6:轮转位1(Toggle Bit 1).

      在编程和擦除期间,读任意地址都会导致DQ6的轮转(0,1间相互变换)当操纵完成后,DQ6停止转换.

      DQ2:轮转位2(Toggle Bit 2).当某个扇区被选中擦除时,读有效地址(地址都在擦除的扇区范围内)会导致DQ2的轮转.

      留意:DQ2只能判定一个特定的扇区是否被选中擦除.但不能区分这个快是否正在擦除中或者正处于擦除暂停状态.相比之下,DQ6可以区分NorFLASH是否处于擦除中或者擦除状态,但不能区分哪个快被选中擦除.因此需要这2个位来确定扇区和模式状态信息.

    • DQ5: 超时位(Exceeded Timing Limits),当编程或擦除操纵超过了一个特定内部脉冲计数是DQ5=1;这表明操纵失败.当编程时把'0'改为'1'就会导致DQ5=1,由于只有擦除擦做才能把'0'改为'1'.当错误发生后需要执行复位命令(见图1-1)才能返回到读数据状态.

    • DQ3: (扇区擦除计时位)Sector Erase Timer,只在扇区擦除指令时起作用.当擦除指令真正开始工作是DQ3=1,此时输进的命令(除擦除暂停命令外)都被忽略.DQ3=0,是可以添加附加的扇区用于多扇区擦除.

      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);
      }
      }

程序优化

  1. 老师的写操作并没有去判断地址是否是高低地址,也就是写0地址和写1地址都是写在0地址2个字节,实际上如果写1地址应该先读取0地址,再写1地址,这里会涉及到擦除? 如果都先擦除就没有这个问题

其他设计点

  1. 读写NORFLASH的时候需要关闭中断

    因为我们使用NOR启动的时候,cpu执行中断的时候会跳回到0地址,也就是NOR中去读取指令,这个时候如果在操作NOR,很明显取址有问题了,会死机等.之前串口有时候没输出死机可能就是这个原因了.

  2. nor是16位宽的,所以在擦除和写nor的时候,实际地址需要>>1才是nor要接受的实际地址,具体分析见程序设计章节中的写操作

  3. Makefile中加入选项-march=armv4否则在操作下面函数时,16位变量被处理为2个8位变量.网上搜索是可能gcc 考虑地址对齐的问题

    	volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
    *p = val; 30000f3c: e5c23000 strb r3, [r2]
    %.o : %.c
    arm-linux-gcc -march=armv4 -c -o $@ $< %.o : %.S
    arm-linux-gcc -march=armv4 -c -o $@ $<

    https://blog.csdn.net/klutz120215/article/details/7277130

  4. 程序应该烧录在NOR上,因为nand启动的时候,片内ram是0地址,mcu无法访问nor

NorFlash 学习的更多相关文章

  1. 驱动开发学习笔记. 0.02 基于EASYARM-IMX283 烧写uboot和linux系统

    驱动开发读书笔记. 0.02 基于EASYARM-IMX283 怎么烧写自己裁剪的linux内核?(非所有arm9通用) 手上有一块tq2440,但是不知道什么原因,没有办法烧boot进norflas ...

  2. 嵌入式Llinux学习路线图

    版本 日期 作者 说明 V1 2016.07.29 韦东山 第1版本,Android部分未写 我是1999年上的大学,物理专业.在大一时,我们班里普遍弥漫着对未来的不安,不知道学习了物理后出去能做什么 ...

  3. C语言学习(记录)【内存相关_1:内存基础】

    本学习是基于嵌入式的C语言学习记录(课程内容来源于某位老师的网络课程,为了证明不是在打广告,就不写出老师的名字了,感谢.) -------------------------------------- ...

  4. u-boot2010.06移植阶段三--norflash驱动

    2011-03-20 23:06:24 学习笔记: 效果图: 参考步骤: 一,把smdk2410 # 改成apple2440 #  1,在board/samsung/apple2440/apple24 ...

  5. NOR Flash的学习

    NOR Flash简介    NOR FLASH是INTEL在1988年推出的一款商业性闪存芯片,它需要很长的时间进行抹写,大半生它能够提供完整的寻址与数据总线,并允许随机存取存储器上的任何区域,而且 ...

  6. jz2440-uboot-201204版本移植【学习笔记】【原创】

    平台:jz2440 作者:庄泽彬(欢迎转载,请注明作者) 说明:韦东山二期视频学习笔记 交叉编译工具:arm-linux-gcc (GCC)4.3.2 PC环境:ubuntu18.04 一.uboot ...

  7. NANDflash和NORflash的区别(设计师在使用闪存时需要慎重选择)

    NANDflash和NORflash的区别(设计师在使用闪存时需要慎重选择)     NOR和NAND是现在市场上两种主要的非易失闪存技术.Intel于1988年首先开发出NOR flash技术,彻底 ...

  8. 学习嵌入式为什么要有uboot(深度解析)

    ref:http://www.elecfans.com/d/617674.html     为什么要有uboot 1.1.计算机系统的主要部件 (1)计算机系统就是以CPU为核心来运行的系统. 典型的 ...

  9. stm32学习笔记之串口通信

    在基础实验成功的基础上,对串口的调试方法进行实践.硬件代码顺利完成之后,对日后调试需要用到的printf重定义进行调试,固定在自己的库函数中. b) 初始化函数定义: void USART_Confi ...

随机推荐

  1. Nginx 简单的cpu配置

    配置指定CPU Nginx建议进程数和CPU数量一致,这样每个CPU都有自己独立的缓存 worker_processes 4; worker_cpu_affinity 1000 0100 0010 0 ...

  2. myclipse里有感叹号的问题,希望可以帮到各位

    今天,我在myeclipse中导入一个项目的时候就发现一个问题:项目有红色感叹号,并且项目有红色错误提示.我首先看了这个红色错误提示的地方,发现这个根本不应该报错,想必是这个红色感叹号的原因.   于 ...

  3. 51Nod1778 小Q的集合 【组合数】【Lucas定理】

    题目分析: 题解好高深...... 我给一个辣鸡做法算了,题解真的看不懂. 注意到方差恒为$0$,那么其实就是要我们求$\sum_{i=0}^{n}\binom{n}{i}(i^k-(n-i)^k)^ ...

  4. Flask 构建微电影视频网站(一)

    Flask构建电影视频网站 Python MTV模型 Flask微内核 Flask扩展插件配置及使用方法 根据业务开发网站前后台功能 Flask结合MySQL数据库 你将可以独立开发网站 独立部署运维 ...

  5. POJ 3074 Sudoku(算竞进阶习题)

    二进制优化+dfs 话说这题数据中真的丧心病狂..不加inline还过不去.. 因为不会DLX只好用二进制来优化了...万万没想到还是低空飘过 我们在行.列.格分别用一个9位二进制常数来记录什么数能放 ...

  6. Matplotlib学习---用matplotlib画面积图(area chart)

    这里利用Nathan Yau所著的<鲜活的数据:数据可视化指南>一书中的数据,学习画图. 数据地址:http://book.flowingdata.com/ch05/data/us-pop ...

  7. Nginx 添加 PHP 支持

    背景介绍默认安装的Nginx是无法打开php文件的,需要修改相关配置才能支持php 安装yum -y install epel-release yum -y install nginx yum ins ...

  8. 洛谷P4907【CYH-01】小奔的国庆练习赛 :$A$换$B$ $problem$(DFS,剪枝)

    洛谷题目传送门 顺便提一下题意有一个地方不太清楚,就是如果输出No还要输出最少需要添加多少张牌才能满足要求.蒟蒻考完以后发现四个点Too short on line 2... 比较需要技巧的搜索 既然 ...

  9. 【dfs】LETTERS

    1212:LETTERS [题目描述] 给出一个roe×colroe×col的大写字母矩阵,一开始的位置为左上角,你可以向上下左右四个方向移动,并且不能移向曾经经过的字母.问最多可以经过几个字母. [ ...

  10. 「SCOI2015」小凸玩矩阵 解题报告

    「SCOI2015」小凸玩矩阵 我好沙茶啊 把点当边连接行和列,在外面二分答案跑图的匹配就行了 我最开始二分方向搞反了,样例没过. 脑袋一抽,这绝壁要费用流,连忙打了个KM 然后wa了,一想这个不是完 ...