Nor Flash

Posted 今天天气眞好

tags:

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


1.NorFlash原理及硬件操作

首先我们看到nor flash的电路图:
在这里插入图片描述
可以看到,nor flash有数据线,有地址线,同时也要明白nor flash可以像内存一样读,但是不能像内存一样写

我们下面通过uboot来演示nor flash的操作,涉及到很多命令

首先看nor 和 nand的区别:
在这里插入图片描述
解释一下坏块:即在使用过程中,用着用着书籍就被破坏了

一般用nor来存储关键性的程序,用nand flash来存储海量文件,允许出错的文件,比如存储视频监控的文件,还有就是手机一般都是使用的nand flash

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

1. 读数据
md.b 0

2. 读ID
在这里插入图片描述
在这里插入图片描述

看到nor的命令手册
NOR手册上:
往地址555HAAH
往地址2AAH55H
往地址555H90H
读0地址得到厂家ID: C2H
读1地址得到设备ID: 22DAH或225BH
退出读ID状态: 给任意地址写F0H

又要注意到:
2440的A1接到NOR的A0,并不是一一对应,相差一位,所以2440发出(555h<<1), 左移1位即乘以2,NOR才能收到555h这个地址

注:555<<1即AAAH,后续同理

那么UBOOT怎么操作?
往地址AAAHAAH mw.w aaa aa
往地址55455H mw.w 554 55
往地址AAAH90H mw.w aaa 90
读0地址得到厂家ID: C2H md.w 0 1
读2地址得到设备ID: 22DAH或225BH md.w 2 1(1表示读一次)
退出读ID状态: mw.w 0 f0

mw.w这里的w就是十六位操作,可以将前两条命令当做解锁,后面的一条为命令

如果没有退出读状态的话,再执行md.b 0 就读不出来了,因此要退出相应的状态。

3.读取CFI信息
NOR有两种规范, jedec, cfi(common flash interface)
在这里插入图片描述
在这里插入图片描述
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

分析:执行md.w 4e 1得到的结果为15(十进制),转换为16进制即为21,即容量为2^21 ,再/1024/1024 = 2M,即是nor的大小

4. 写数据: 在地址0x100000写入0x1234
md.w 100000 1 // 得到ffff
mw.w 100000 1234
md.w 100000 1 // 还是ffff
会发现写不进去

下面使用uboot来操作nor flash
在这里插入图片描述

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

5. 再次往0x100000写入0x5678
因为原来0x100000上的数据不是0xffff,再次烧写失败(需要先擦除,再烧写)

往地址AAAH写AAH               
往地址554H写55H               
往地址AAAH写A0H               
往地址0x100000写5678h         mw.w 100000 5678

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

5.2 再烧写

mw.w aaa aa
mw.w 554 55
mw.w aaa a0
mw.w 100000 5678

总结:我们烧写时,如果上面的数据,不是0ffff,没有被擦除过,我们就要先擦出,擦除完后,才可以烧写,擦除烧写的命令可以从芯片手册里面获得。

2.NorFlash编程_识别

下面进行nor flash的编程

目的:识别nor flash

大致步骤:
1.打印菜单, 供我们选择测试内容
如下代码(放在一个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);

2.选择测试内容

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

下面对每个函数进行编写

发送命令函数
nor_cmd函数代码如下,往NOR Flash某个地址发送指令,

/* offset是基于NOR的角度看到 */
void nor_cmd(unsigned int offset, unsigned int cmd)
{
	nor_write_word(NOR_FLASH_BASE, offset, cmd);
}

写命令函数

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

读取函数
nor_read_word函数是从NOR Flash 读取两个字节(本开发板位宽16bit),读取数据的地址,是基于2440,所以读取NOR Flash某个地址上的数据时,需要把NOR Flash对应的地址左移一位(地址乘以2)。

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

读取地址中的数据
nor_dat函数中写入NOR Flash某个地址,返回该NOR Flash地址上的数据。

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

进入NOR FLASH的CFI模式,读取各类信息
do_scan_nor_flash函数代码如下,该函数的功能:进入CFI模式读取NOR Flash中的厂家ID,设备ID,容量等信息。

50/* 进入NOR FLASH的CFI模式
51 * 读取各类信息
52 */
53	void do_scan_nor_flash(void)
54	{
55		char str[4];
56		unsigned int size;
57		int regions, i;
58		int region_info_base;
59		int block_addr, blocks, block_size, j;
60		int cnt;
61
62		int vendor, device;
63	
64		/* 打印厂家ID、设备ID */
65		nor_cmd(0x555, 0xaa);    /* 解锁 */
66		nor_cmd(0x2aa, 0x55); 
67		nor_cmd(0x555, 0x90);    /* read id */
68		vendor = nor_dat(0);
69		device = nor_dat(1);
70		nor_cmd(0, 0xf0);        /* reset */
71	
72		nor_cmd(0x55, 0x98);  /* 进入cfi模式 */
073
74		str[0] = nor_dat(0x10);
75		str[1] = nor_dat(0x11);
76		str[2] = nor_dat(0x12);
77		str[3] = '\\0';
78		printf("str = %s", str);
79
80		/* 打印容量 */
81		size = 1<<(nor_dat(0x27));
82		printf("v=0x%x,d=0x%x,s=0x%x,%dM",vendor,device,size,size/(1024*1024));
83
84		/* 打印各个扇区的起始地址 */
85		/* 名词解释:
86		 *    erase block region : 里面含有1个或多个block, 它们的大小一样
87		 * 一个nor flash含有1个或多个region
88		 * 一个region含有1个或多个block(扇区)
89
90		 * Erase block region information:
91		 *    前2字节+1    : 表示该region有多少个block 
92	 	*    后2字节*256  : 表示block的大小
93	 	*/
94
95		regions = nor_dat(0x2c);
96		region_info_base = 0x2d;
97		block_addr = 0;
98		printf("Block/Sector start Address:");
99		cnt = 0;
100		for (i = 0; i < regions; i++)
101		{
102		   blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8);
103		   block_size=256*(nor_dat(region_info_base+2)+(nor_dat(region_info_base+3)<<8));
104		   region_info_base += 4;
105
106 		   //printf("…………");
107
108			for (j = 0; j < blocks; j++)
109			{
110				/* 打印每个block的起始地址 */
111				//printf("0x%08x ", block_addr);
112				printHex(block_addr);
113				putchar(' ');
114				cnt++;
115				block_addr += block_size;
116				if (cnt % 5 == 0)
117				printf("\\n\\r");
118		}
119		}
120	printf("\\n\\r");
121	/* 退出CFI模式 */
122	nor_cmd(0, 0xf0);
123	}

分析:
第65,66行 这两步是解锁,解锁之后就进入读ID状态,就可以读取厂家和设备ID了。

第68行 是把读取到的厂家ID的值,复制给vendor变量。

第69行 是把读取到的设备ID的值,复制给device变量。

第70行 退出读ID状态: 给任意地址写F0H。

第72行,往地址0x55地址写入数据0x98,是进入cfi模式

第74,75,76行是读取NOR Flash地址0x10,0x11,x012中的字符,赋值给字符串str。
在这里插入图片描述
第81行,根据芯片手册可知道,读取NOR Flash地址0x27处的数据,得到的是NOR Flash容量大小2的幂数,所以把1左移读取到的数据,就可得到NOR Flash的容量。
在这里插入图片描述

第95行读取NOR Flash地址0x2c地址中的数据,可以得到NOR Flash中有多少region

第102行根据Erase block region information:的信息可以知道读取[2E,2D]这两个字节的地址+1,可以得到一个region有多少block(参考芯片手册)。代码中的region_info_base变量的值是0x2d,0x2d是前两个字节中的低字节,0x2e是前两个字节中的高字节,所以需要左移8位,然后加上1就得到了一个region有多少block.。
在这里插入图片描述
第103行参考芯片手册,读取[30,2F]这两个字节地址,然后乘上256就可以得到一个块的大小。

第104行,地址加4,读取下一个region有多少block和每个block的大小。

第112,115行,由于NOR Flash的基地址是0,所以第一个block的首地址是0,下一个block的首地址,就是上一个block的首地址加上block的大小。

第112行往0地址写入0xf0,退出CFI模式

主函数
main函数代码如下所示。把timer中断去掉,否则: 测试NOR Flash时进入CFI等模式时, 如果发生了中断,cpu必定读NOR Flash,那么读不到正确的指令,导致程序崩溃。


#include "s3c2440_soc.h"
#include "uart.h"
#include "init.h"

char g_Char = 'A';
char g_Char3 = 'a';
const char g_Char2 = 'B';
int g_A = 0;
int g_B;

int main(void)
{
	led_init();
	//interrupt_init();  /* 初始化中断控制器 */
	key_eint_init();   /* 初始化按键, 设为中断源 */
	//timer_init();
	
	puts("\\n\\rg_A = ");
	printHex(g_A);
	puts("\\n\\r");

	nor_flash_test();
	
	return 0;
}

同时注意 编译程序时加上: -march=armv4, 否则

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

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 $@ $<	

3.NorFlash编程_擦写读

可以看到上面的测试函数中有三个函数还没有实现,分别是:
do_erase_nor_flashdo_write_nor_flashdo_read_nor_flash

目的:擦除nor flash某个扇区编写某个地址读某个地址

等待烧写wait_ready
等待烧写完成 : 读数据, Q6无变化时表示结束 (参考芯片手册),

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 某个扇区
do_erase_nor_flash函数的代码如下。参考芯片手册,就可以知道擦除某个扇区,还是相对比较简单的。

125	void do_erase_nor_flash(void)
126	{
127		unsigned int addr;
128	
129			/* 获得地址 */
130		printf("Enter the address of sector to erase: ");
131		addr = get_uint();
132
133		printf("erasing ...");
134		nor_cmd(0x555, 0xaa);    /* 解锁 */
135		nor_cmd(0x2aa, 0x55); 
136		nor_cmd(0x555, 0x80);	 /* erase sector */
137	
138		nor_cmd(0x555, 0xaa);    /* 解锁 */
139		nor_cmd(0x2aa, 0x55); 
140		nor_cmd(addr>>1, 0x30);	 /* 发出扇区地址 */
141		wait_ready(addr);
142	}

第131行,get_uint函数用于获取输入的地址。

第134,135这两行是解锁

第136行是erase sector。

第138,139行是再次解锁

第140行是对发出的扇区地址。

第 141行等待擦除完成。

写NOR Flash
do_write_nor_flash的代码如下所示,开发板上的NOR Flash的位宽是16bit,所以可以把要写的数据构造出16bit然后在写进NOR Flash中。

144	void do_write_nor_flash(void)
145	{
146		unsigned int addr;
147		unsigned char str[100];
148		int i, j;
149			unsigned int val;
150	
151		/* 获得地址 */
152		printf("Enter the address of sector to write: ");
153		addr = get_uint();
154
155		printf("Enter the string to write: ");
156		gets(str);
157
158		printf("writing ...\\n\\r");
159
160		/* str[0],str[1]==>16bit 
161		 * str[2],str[3]==>16bit 
162		*/
163		i = 0;
164		j = 1;
165		while (str[i] && str[j])
166		{
167			val = str[i] + (str[j]<<8);
168		
169			/* 烧写 */
170			nor_cmd(0x555, 0xaa);	 /* 解锁 */
171			nor_cmd(0x2aa, 0x55); 
172			nor_cmd(0x555, 0xa0);	 /* program */
173			nor_cmd(addr>>1, val);
174			/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
175			wait_ready(addr);
176
177			i += 2;
178			j += 2;
179			addr += 2;
180		}
181
182		val = str[i];
183		/* 烧写 */
184		nor_cmd(0x555, 0xaa);	 /* 解锁 */
185		nor_cmd(0x2aa, 0x55); 
186		nor_cmd(0x555, 0xa0);	 /* program */
187		nor_cmd(addr>>1, val);
188		/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
189		wait_ready(addr);
190	}

第153行把通过get_uint获得的地址赋值给addr变量,

第156行通过gets函数获得输入的字符串

第168行两个8位的数据,组合成一个16位的数据赋值给变量val。

读NOR Flash
do_read_nor_flash函数代码如下,由于NOR Flash是内存类接口,可以像内存一样读取。

191	void do_read_nor_flash(void)
192	{
193		unsigned int addr;
194		volatile unsigned char *p;
195		int i, j;
196		unsigned char c;
197		unsigned char str[16];
198	
199		/* 获得地址 */
200		printf("Enter the address to read: ");
201		addr = get_uint();
202
203		p = (volatile unsigned char *)addr;
204
205		printf("Data :  \\n\\r");
206		/* 长度固定为64 */
207		for (i = 0; i < 4; i++)
208		{
209			/* 每行打印16个数据 */
210			for (j = 0; j < 16; j++)
211			{
212				/* 先打印数值 */
213				c = *p++;
214				str[j] = c;
215				printf("%02x ", c);
216			}
217
218			printf("   ; ");
219
220			for (j = 0; j < 16; j++)
221			{
222				/* 后打印字符 */
223				if (str[j] < 0x20 || str[j] > 0x7e)  /* 不可视字符 */
224					putchar('.');
225				else
226					putchar(str[j]);
227			}
228			printf("\\n\\r");
229	}

第201行中的get_uint函数,从串口中获得输入的地址。

第203行,强制类型转化。

第207行~216行是对NOR Flash内容的读取,输出的内容为16进制的数据,由于NOR Flash是内存类接口,可以像内存一样读取。

第220行~227输出NOR Flash的内容为字符型数据,其中的第223行用来判断,输出的字符是否为不可视字符,要是为不可视字符输出点’.’,要是可视字符输出字符。

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

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

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

NAND Flash和NOR Flash的比较

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

转SPI FLASH与NOR FLASH的区别 详解SPI FLASH与NOR FLASH的不一样

uboot解除nor flash写保护