程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

Nor Flash的两种规范

balukai 2025-01-05 15:46:24 文章精选 8 ℃

来源:韦东山嵌入式专栏_ARM裸机加强版维基教程

作者:韦东山

(本文字数:7838,阅读时长:10分钟)

通常内核里面要识别一个 Nor Flash 有两种方法:

一种是 jedec 探测,就是在内核里面事先定义一个数组,该数组里面放有不同厂家各个芯片的一些参数,探测的时候将 flash 的 ID 和数组里面的 ID 一一比较,如果发现相同的,就使用该数组的参数。jedec 探测的优点就是简单,缺点是如果内核要支持的 flash 种类很多,这个数组就会很庞大。内核里面用 jedec 探测一个芯片时,是先通过发命令来获取 flash 的 ID,然后和数组比较,但是 flash.c 中,连 ID 都是自己通过宏配置的。

一种是 CFI(common flash interface)探测,就是直接发各种命令来读取芯片的信息,比如 ID、容量等,芯片本身就包含了电压有多大,容量有有多少等信息。

下面对在Nor Flash上操作,2440上操作,U-BOOT上操作cfi 探测(读取芯片信息)进行比较参考芯片手册。


Nor Flash写数据

我们在Nor Flash的10000的地址读数据,
md.w 100000 1

结果:
00100000:ffff…

在Nor flash的10000的地址写数据下0x1234,
mw.w 100000 1234

然后在这个地址读数据,
md.w 100000 1

结果:
00100000:ffff(这个地址上的数据没有被修改,写操作无效)。

怎样把数据写进Nor Flash进去呢?

写数据之前必须保证,要写的地址是擦除的。

下面是Nor Flash的写操作,如下表:


1).U-BOOT执行完上述指令后,0x1234,就被写到0x100000地址处,

执行:
md.w1000001
结果(1234被写进去):
00100000:1234 4

从这里可以看出来U-BOOT的操作不是很复杂。

2).我们再次往0x100000地址处,写入0x5678,执行如下命令:

mw.w aaa aa
mw.w 554 55
mw.w aaa a0
mw.w 100000 5678
查看0x100000地址处的数据
md.w 100000 1

结果:
00100000:12300.
0x100000地址处的数据不是0x5678,写操作失败,失败的原因是,原来的数据已经是0x1234不是全0xffff,再次写操作失败,(Nor Flash只有先擦出,才能烧写)。

先擦除(参考Nor Flash芯片手册)
Nor Flash操作 u-boot操作
555H AAH mw.w aaa aa
2AAH 55H mw.w 554 55
555H 80H mw.w aaa 80
555H AAH mw.w aaa aa
2AAH 55H mw.w 554 55
SA 30H //往扇区地址写入30 mw.w 100000 30

执行完上述指令后测试

执行:
md.w 100000 1
结果:
00100000:ffff…
已被擦除,这个时候再次烧写就不会有问题了。

再烧写
mw.w aaa aa
mw.w 554 55
mw.w aaa a0
mw.w 100000 5678

测试烧写结果
执行:
md.w 100000 1
结果:
00100000:5678 xV
数据被烧写进去,烧写成功。

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



Nor Flash编程_识别

本节实例的目的目的:识别nor flash
发送命令函数
nor_cmd函数代码如下,往NOR Flash某个地址发送指令,

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

读取函数

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

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

读取地址中的数据

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

29	unsigned int nor_dat(unsigned int offset)
30	{
31		return nor_read_word(NOR_FLASH_BASE, offset);
32	}

进入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模式。



Nor Flash的测试

nor_flash_test函数通过switch语句,分别处理识别NOR Flash,擦除NOR Flash某个扇区,编写某个地址,读某个地址。代码如下:

232	void nor_flash_test(void)
233	{
234		char c;
235
236		while (1)
237		{
238			/* 打印菜单, 供我们选择测试内容 */
239			printf("[s] Scan nor flash\n\r");
240			printf("[e] Erase nor flash\n\r");
241			printf("[w] Write nor flash\n\r");
242			printf("[r] Read nor flash\n\r");
243			printf("[q] quit\n\r");
244			printf("Enter selection: ");
245
246			c = getchar();
247			printf("%c", c);
248
249			/* 测试内容:
250			 * 1. 识别nor flash
251			 * 2. 擦除nor flash某个扇区
252			 * 3. 编写某个地址
253			 * 4. 读某个地址
254			 */
255			switch (c)		 
256			{
257				case 'q':
258				case 'Q':
259					return;
260					break;
261				
262				case 's':
263				case 'S':
264					do_scan_nor_flash();
265					break;
266
267				case 'e':
268				case 'E':
269					do_erase_nor_flash();
270					break;
271
272				case 'w':
273				case 'W':
274					do_write_nor_flash();
275					break;
276
277				case 'r':
278				case 'R':
279					do_read_nor_flash();
280					break;
281				default:
282					break;
283			}
284		}
285	}

主函数

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

12	int main(void)
13	{
14		led_init();
15		//interrupt_init();  /* 初始化中断控制器 */
16		key_eint_init();   /* 初始化按键, 设为中断源 */
17		//timer_init();
18	
19		puts("\n\rg_A = ");
20		printHex(g_A);
21		puts("\n\r");
22
23		nor_flash_test();
24	
25		return 0;
26	}



Nor Flash编程_擦写读

本实例的目的目的:擦除nor flash某个扇区,编写某个地址,读某个地址。

等待烧写

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

35	void wait_ready(unsigned int addr)
36	{
37		unsigned int val;
38		unsigned int pre;
39
40		pre = nor_dat(addr>>1);
41		val = nor_dat(addr>>1);
42		while ((val & (1<<6)) != (pre & (1<<6)))
43		{
44			pre = val;
45			val = nor_dat(addr>>1);		
46		}
47}

擦除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行用来判断,输出的字符是否为不可视字符,要是为不可视字符输出点’.’,要是可视字符输出字符。

「新品首发」STM32MP157开发板火爆预售!首批仅300套

最近发表
标签列表