【ARM裸板】Nor Flash基础知识与编程示例

示例代码下载

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);
}
发布了42 篇原创文章 · 获赞 176 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_39492932/article/details/104150664