文章目录
示例代码下载
1.NOR与NAND的区别
Flash | NOR | NAND |
---|---|---|
接口 | RAM-Like,引脚多 | 引脚少,复用(地址数据共用) |
容量 | 小(1-32M) | 大(128M+) |
读 | 简单 | 复杂 |
写 | 发出特定命令(慢) | 发出特定命令(快) |
价格 | 贵 | 较便宜 |
缺点 | 无位反转、坏块 | 位反转、坏块 |
一般存放 | bootloader(关键程序) | 大文件、视频 |
xip | 可以 | 不可以 |
- xip(eXecute In Place),即芯片内执行,指应用程序可以直接在flash闪存内运行,不必再把代码读到系统RAM中。flash内执行是指nor flash 不需要初始化,可以直接在flash内执行代码。但往往只执行部分代码,比如初始化RAM.
2.命令表
- UBOOT下读写数据
2.1 读数据
md.b
为读命令mw.w
为写命令
md.b 0
2.2 读ID
- Nor手册
- 往(555H)写入AAH
- 往(2AAH)写入55H
- 往(555H)写入90H
- 读(0)得到厂家(Manifacture)ID:C2H
- 读(1)得到设备(Device)ID:22C4/2249
但是由于地址是错开1位的(具体原因可查看第15章),则往CPU写的地址需要addr<<1,即(地址*2)因此:
- UBOOT下
- 往(AAAH)写入AAH
- 往(554H)写入55H (这两步为解锁命令)
- 往(AAAH)写入90H(90H为命令)
- 读(0H)得到厂家(Manifacture)ID:C2H
- 读(2H)得到设备(Device)ID:22C4/2249
- 退出读ID状态(即复位)
mw.w aaa aa
mw.w 554 55
mw.w aaa 90
md.w 0 1 #读0地址1次
md.w 2 1
mw.w 0 F0
2.3 CFI模式
CFI(Common Flash Interface)
- Nor手册
- 往(55H)写入98H(进入CFI模式)
- 读(27H)得到容量2^n的n
- 退出CFI模式(复位)
- UBOOT下
- 往(AAH)写入98H(进入CFI模式)
- 读(4EH)得到容量2^n的n
- 往(0H)写入F0H
3.基本函数
3.1 写函数
- Nor Flash 地址线21:即可访问2M内存,
0x1FFFFF
,其范围地址为 0~0x1FFFFF
#define NOR_FLASH_BASE 0 /* Nor Flash基地址 nor-->cs0,base_addr = 0 */
/* Nor Flash写入一个字
* 基地址:base,偏移地址:offset,写入的值:value
* eg: 55 98
* 往(0 + (0x55)<<1 )写入0x98
*/
void nor_write_word(unsigned int base,unsigned int offset,unsigned short value)
{
volatile unsigned short *p = (volatile unsigned short *) (base + offset<<1);
*p = value;
}
- 进行封装
void nor_cmd(unsigned int offset,unsigned short cmd)
{
nor_write_word(NOR_FLASH_BASE, offset, cmd);
}
3.2 读函数
/* Nor Flash读取一个字
* 基地址:base,偏移地址:offset
* eg: 55 98
* 往(0 + (0x55)<<1 )写入0x98
*/
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);
}
4.识别NOR
4.1 读取ID号
- 往(555H)写入AAH
- 往(2AAH)写入55H
- 往(555H)写入90H
- 读(0)得到厂家(Manifacture)ID:C2H
- 读(1)得到设备(Device)ID:22C4/2249
/* 1.打印 Manifacture ID、Device ID */
nor_unlock(); //解锁
nor_cmd(0x555,0x90);//读命令
manifa_id = nor_dat(0x00); //读厂家ID
device_id = nor_dat(0x01); //读设备ID
nor_reset_mode(); //复位
4.2 进入CFI Mode
- 往(55H)写98H
- CFI(Common Flash Interface)
/* 1.进入CFI Mode */
nor_cmd(0x55,0x98);
4.3 读取容量
- 读(27H)得到容量bytes
/* 2.打印容量 */
size = 1<<(nor_dat(0x27));
printf("nor size = 0x%x, %dM\r\n",size,size>>20);
4.4 读取各个扇区
4.4.1 获得region数量
regions = nor_dat(0x2C);
4.4.2 region详细信息
erase block region :里面含有1个或多个block,它们大小都一样,一个nor含有1个或多个region,一个region含有1个或多个block(扇区)
- Erase block region information:
前2字节+1
:表示该region有多少个block后2字节*256
:表示该block的大小(bytes)
参考CFI标准:
- regions的基址从
0x2D
开始
region_info_base = 0x2D;
block_addr = 0;
for(i = 0;i < regions; i++){
/* 获取block的数量和大小 */
blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8);//低2字节,获取block数量
block_size = 256 * (nor_dat(region_info_base+2) + (nor_dat(region_info_base+3)<<8));//高2字节,获取block大小
region_info_base += 4;//region读取的基址+4
/* 打印每个block的起始地址 */
for(j = 0;j < blocks; j++){
printf("0x%08x ",block_addr);
block_addr += block_size;
if( ((++cnt) % 5) == 0){ //每打印5个换行
printf("\r\n");
}
}
}
4.5 退出CFI Mode
nor_cmd(0x00,0xF0);//复位
5.写数据
5.1 写入
需要注意一个点,写入的数据是16位的,也就是两个字节,需要一次性写入一个字节,所以在写入之前需要对数据进行整合。
while(str[i] && str[i+1]){//两个字符都不为0时
data = str[i] + (str[i+1]<<8);
nor_unlock();//解锁
nor_cmd(0x555,0xA0); //写命令
nor_cmd(addr>>1,data);
/* 等待烧写完成:读数据Q6,无变化时表示完成*/
wait_ready(addr);
i += 2;
addr += 2;
}
5.2 判断数据写入完成
- 等待烧写完成:读数据Q6,无变化时表示完成
- 两次读取的结果不一致,说明数据还在变化,继续等待
//等待读取或擦除完毕
void wait_ready(unsigned int addr)
{
unsigned int pre_val;//上一次的值
unsigned int cur_val;
pre_val = nor_dat(addr>>1);
cur_val = nor_dat(addr>>1);
/* 两次读取的结果不一致,说明数据还在变化,继续等待 */
while((cur_val & (1<<6) != (pre_val &(1<<6)))){//当前的Q6不等于上一次的Q6则等待
pre_val = cur_val;//更新上一次的值
val = nor_dat(addr>>1);//重新获取
}
}
6.测试
6.1 读取
6.2 写入
6.3 擦除
7.问题
7.1 系统异常卡死
- 执行多次的菜单选择,导致系统卡死
- 该为定时器造成的异常错误,关闭定时器,则不会卡死
- 因为当测试nor进入CFI模式时,如果发生了中断,CPU必定读NOR,那么读不到正确的指令,导致程序异常崩溃
7.2 nor数据错误
-
读取设备ID时,读到的是
0x002f
,0xea00
,改为反汇编中text的[0]上的数据 -
编译程序加上选项:指定ARM版本指令集
-march=armv4
或者指定芯片类型-mcpu=arm9tdmi
否则像如下的写入操作会被分成两个strb步骤(我们需要的是strh,一次性写入两个字节),最后导致读取设备ID和厂家ID的时候出错。
volatile unsigned short *p =value;
*p = value;
- 没加编译指定选项(分两次存2个字节)
- 加了编译指定芯片类型
-mcpu=arm9tdmi
(一次性存2个字节)
8.擦除扇区
void erase_nor_flash(void)
{
unsigned int addr;
/* 获得地址*/
printf("Enter the address of sector to erase:");
addr = get_uint();
printf("Erase...\r\n");
nor_unlock();
nor_cmd(0x555,0x80); //擦除扇区命令
nor_unlock();
nor_cmd(addr>>1,0x30); //发出扇区地址
wait_ready(addr);
}