“第015课 Nor Flash”的版本间的差异
Baiwen root(讨论 | 贡献) |
|||
(未显示3个用户的17个中间版本) | |||
第1行: | 第1行: | ||
+ | <div body style="width:800px;"> | ||
= 第001节_Nor Flash原理及硬件操作 = | = 第001节_Nor Flash原理及硬件操作 = | ||
− | Nor Flash的连接线有地址线,数据线,片选信号读写信号等,Nor Flash的接口属于内存类接口,Nor | + | Nor Flash的连接线有地址线,数据线,片选信号读写信号等,Nor Flash的接口属于内存类接口,Nor Flash可以向内存一样读,但是不能像内存一样写,需要做一些特殊的操作才能进行写操作,读只需像内存一样读很简单。 |
+ | |||
+ | Nor Flash原理图如图: | ||
[[File:chapter15_lesson1_001.png]] | [[File:chapter15_lesson1_001.png]] | ||
== Flash介绍 == | == Flash介绍 == | ||
− | 常用的Flash类型有Nor Flash和NAND | + | 常用的Flash类型有Nor Flash和NAND Flash两种。 |
+ | |||
+ | Nor Flash由Intel公司在1988年发明,以替代当时在市场上占据主要地位的EPROM和E2PROM。NAND Flash由Toshiba公司在1989年发明。两者的主要差别如下表: | ||
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
第50行: | 第55行: | ||
== Nor Flash的操作 == | == Nor Flash的操作 == | ||
下面我们使用u-boot来体验Nor Flash的操作(开发板设置Nor启动,进入u-boot)。 | 下面我们使用u-boot来体验Nor Flash的操作(开发板设置Nor启动,进入u-boot)。 | ||
− | 1) | + | |
+ | 1).使用OpenJTAG烧写UBOOT到Nor Flash | ||
+ | |||
那么我们怎么用u-boot来操作呢? | 那么我们怎么用u-boot来操作呢? | ||
+ | |||
Nor Flash手册里会有一个命令的表格,如图: | Nor Flash手册里会有一个命令的表格,如图: | ||
− | [[File:chapter15_lesson1_002.png| | + | [[File:chapter15_lesson1_002.png|700px]] |
+ | |||
下面简单的举一些例子: | 下面简单的举一些例子: | ||
+ | |||
复位(reset):往任何一个地址写入F0。 | 复位(reset):往任何一个地址写入F0。 | ||
+ | |||
读ID(ReadSiliconID):很多的Nor Flash可以配置成位宽16bit(Word),位宽8bit(Byte)。对于我们使用的jz2440开发板使用是位宽16bit,怎样读ID呢? | 读ID(ReadSiliconID):很多的Nor Flash可以配置成位宽16bit(Word),位宽8bit(Byte)。对于我们使用的jz2440开发板使用是位宽16bit,怎样读ID呢? | ||
+ | |||
根据前面得图可知,往Nor Flash的555地址写AA,再往2AA的地址写入55,再往555的地址写入90,然后就可以读ADI地址,就可以读到DDI数据了。 | 根据前面得图可知,往Nor Flash的555地址写AA,再往2AA的地址写入55,再往555的地址写入90,然后就可以读ADI地址,就可以读到DDI数据了。 | ||
第62行: | 第74行: | ||
读数据: | 读数据: | ||
+ | |||
在u-boot上执行:md.b0 | 在u-boot上执行:md.b0 | ||
+ | |||
结果(和我们烧进去的数据完全一样): | 结果(和我们烧进去的数据完全一样): | ||
<syntaxhighlight lang="c" > | <syntaxhighlight lang="c" > | ||
00000000:170000ea14f09fe514f09fe514f09fe5................ | 00000000:170000ea14f09fe514f09fe514f09fe5................ | ||
00000010:14f09fe514f09fe514f09fe514f09fe5................ | 00000010:14f09fe514f09fe514f09fe514f09fe5................ | ||
− | 00000020: | + | 00000020:6001f833c001f8332002f8337002f833`..3...3..3...3 |
00000030:e002f8330004f8332004f833efbeadde...3...3..3.... | 00000030:e002f8330004f8332004f833efbeadde...3...3..3.... | ||
</syntaxhighlight> | </syntaxhighlight> | ||
第75行: | 第89行: | ||
读ID(参考Nor手册) | 读ID(参考Nor手册) | ||
− | + | ||
− | + | * 往地址555H写入AAH(解锁) | |
− | + | * 往地址2AAH写入55H(解锁) | |
− | + | * 往地址555H写入90H(命令) | |
− | + | * 读0地址得到厂家ID(C2H) | |
− | + | * 读1地址得到设备ID(22DAH或225BH) | |
+ | * 退出读ID状态:给任意地址写F0H就可以了。 | ||
下图为2440和Nor Flash的简易连接图: | 下图为2440和Nor Flash的简易连接图: | ||
− | [[File:chapter15_lesson1_003.jpg| | + | [[File:chapter15_lesson1_003.jpg|700px]] |
+ | |||
2440的A1接到Nor的A0所以2440发出的地址是,Nor Flash收到的地址左移一位。比如:2440发出(555H<<1)地址,Nor Flash才能收到555H这个地址。 | 2440的A1接到Nor的A0所以2440发出的地址是,Nor Flash收到的地址左移一位。比如:2440发出(555H<<1)地址,Nor Flash才能收到555H这个地址。 | ||
+ | |||
下面对在Nor Flash的操作,2440的操作,U-BOOT上的操作进行比较,如下表: | 下面对在Nor Flash的操作,2440的操作,U-BOOT上的操作进行比较,如下表: | ||
{| class="wikitable" | {| class="wikitable" | ||
第103行: | 第120行: | ||
|} | |} | ||
− | + | 1).当执行过 | |
+ | md.w 0 1 | ||
结果(输出厂家ID): | 结果(输出厂家ID): | ||
− | 00000000:00c2..(00c2就是厂家ID) | + | 00000000:00c2..(00c2就是厂家ID) |
− | + | ||
+ | |||
+ | 2).当执行过 | ||
+ | md.w 2 1 | ||
+ | |||
结果(输出设备ID): | 结果(输出设备ID): | ||
− | 00000002:2249I"(2249就是设备ID) | + | 00000002:2249I"(2249就是设备ID) |
+ | |||
+ | |||
+ | 3).当执行 | ||
+ | mw.w 0 f0 | ||
+ | 就退出读ID的状态, | ||
− | + | 执行: | |
− | + | md.b0 | |
结果: | 结果: | ||
− | 00000000:17.(读到的就是Nor Flash地址·0的数据) | + | 00000000:17.(读到的就是Nor Flash地址·0的数据) |
+ | |||
== Nor Flash的两种规范 == | == Nor Flash的两种规范 == | ||
通常内核里面要识别一个 Nor Flash 有两种方法: | 通常内核里面要识别一个 Nor Flash 有两种方法: | ||
− | 一种是 jedec 探测,就是在内核里面事先定义一个数组,该数组里面放有不同厂家各个芯片的一些参数,探测的时候将 flash 的 ID 和数组里面的 ID | + | |
− | 一种是 CFI( | + | 一种是 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上操作,2440上操作,U-BOOT上操作cfi 探测(读取芯片信息)进行比较参考芯片手册。 | ||
{| class="wikitable" | {| class="wikitable" | ||
第137行: | 第169行: | ||
== Nor Flash写数据 == | == Nor Flash写数据 == | ||
我们在Nor Flash的10000的地址读数据, | 我们在Nor Flash的10000的地址读数据, | ||
− | md.w 100000 1 | + | md.w 100000 1 |
结果: | 结果: | ||
− | 00100000:ffff.. | + | 00100000:ffff.. |
− | + | 在Nor flash的10000的地址写数据下0x1234, | |
− | mw.w 100000 1234 | + | mw.w 100000 1234 |
然后在这个地址读数据, | 然后在这个地址读数据, | ||
− | md.w 100000 1 | + | md.w 100000 1 |
结果: | 结果: | ||
− | 00100000:ffff(这个地址上的数据没有被修改,写操作无效)。 | + | 00100000:ffff(这个地址上的数据没有被修改,写操作无效)。 |
+ | |||
+ | 怎样把数据写进Nor Flash进去呢? | ||
+ | |||
+ | 写数据之前必须保证,要写的地址是擦除的。 | ||
− | + | 下面是Nor Flash的写操作,如下表: | |
{| class="wikitable" | {| class="wikitable" | ||
|- | |- | ||
第161行: | 第197行: | ||
|} | |} | ||
− | 1) | + | 1).U-BOOT执行完上述指令后,0x1234,就被写到0x100000地址处, |
− | + | ||
+ | 执行: | ||
+ | md.w1000001 | ||
结果(1234被写进去): | 结果(1234被写进去): | ||
− | 00100000:1234 4 | + | 00100000:1234 4 |
从这里可以看出来U-BOOT的操作不是很复杂。 | 从这里可以看出来U-BOOT的操作不是很复杂。 | ||
− | + | 2).我们再次往0x100000地址处,写入0x5678,执行如下命令: | |
− | mw.w aaa aa | + | mw.w aaa aa |
− | mw.w 554 55 | + | mw.w 554 55 |
− | mw.w aaa a0 | + | mw.w aaa a0 |
− | mw.w 100000 5678 | + | mw.w 100000 5678 |
查看0x100000地址处的数据 | 查看0x100000地址处的数据 | ||
− | md.w 100000 1 | + | md.w 100000 1 |
结果: | 结果: | ||
− | 00100000:12300. | + | 00100000:12300. |
0x100000地址处的数据不是0x5678,写操作失败,失败的原因是,原来的数据已经是0x1234不是全0xffff,再次写操作失败,(Nor Flash只有先擦出,才能烧写)。 | 0x100000地址处的数据不是0x5678,写操作失败,失败的原因是,原来的数据已经是0x1234不是全0xffff,再次写操作失败,(Nor Flash只有先擦出,才能烧写)。 | ||
先擦除(参考Nor Flash芯片手册) | 先擦除(参考Nor Flash芯片手册) | ||
− | Nor Flash操作 u-boot操作 | + | Nor Flash操作 u-boot操作 |
− | 555H AAH mw.w aaa aa | + | 555H AAH mw.w aaa aa |
− | 2AAH 55H mw.w 554 55 | + | 2AAH 55H mw.w 554 55 |
− | 555H 80H mw.w aaa 80 | + | 555H 80H mw.w aaa 80 |
− | 555H AAH mw.w aaa aa | + | 555H AAH mw.w aaa aa |
− | 2AAH 55H mw.w 554 55 | + | 2AAH 55H mw.w 554 55 |
− | SA 30H //往扇区地址写入30 mw.w 100000 30 | + | SA 30H //往扇区地址写入30 mw.w 100000 30 |
+ | |||
+ | |||
执行完上述指令后测试 | 执行完上述指令后测试 | ||
− | + | ||
− | + | 执行: | |
+ | md.w 100000 1 | ||
+ | 结果: | ||
+ | 00100000:ffff.. | ||
已被擦除,这个时候再次烧写就不会有问题了。 | 已被擦除,这个时候再次烧写就不会有问题了。 | ||
再烧写 | 再烧写 | ||
− | mw.w aaa aa | + | mw.w aaa aa |
− | mw.w 554 55 | + | mw.w 554 55 |
− | mw.w aaa a0 | + | mw.w aaa a0 |
− | mw.w 100000 5678 | + | mw.w 100000 5678 |
测试烧写结果 | 测试烧写结果 | ||
− | + | 执行: | |
− | + | md.w 100000 1 | |
+ | 结果: | ||
+ | 00100000:5678 xV | ||
数据被烧写进去,烧写成功。 | 数据被烧写进去,烧写成功。 | ||
第206行: | 第251行: | ||
= 第002节_Nor Flash编程_识别 = | = 第002节_Nor Flash编程_识别 = | ||
+ | 本节实例的目的目的:识别nor flash | ||
+ | '''发送命令函数''' | ||
+ | nor_cmd函数代码如下,往NOR Flash某个地址发送指令, | ||
+ | <syntaxhighlight lang="c" > | ||
+ | 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 } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | '''读取函数''' | ||
+ | |||
+ | nor_read_word函数是从NOR Flash 读取两个字节(本开发板位宽16bit),读取数据的地址,是基于2440,所以读取NOR Flash某个地址上的数据时,需要把NOR Flash对应的地址左移一位(地址乘以2)。 | ||
+ | <syntaxhighlight lang="c" > | ||
+ | 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 } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | '''读取地址中的数据''' | ||
+ | |||
+ | 向nor_dat函数中写入NOR Flash某个地址,返回该NOR Flash地址上的数据。 | ||
+ | <syntaxhighlight lang="c" > | ||
+ | 29 unsigned int nor_dat(unsigned int offset) | ||
+ | 30 { | ||
+ | 31 return nor_read_word(NOR_FLASH_BASE, offset); | ||
+ | 32 } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | '''进入NOR FLASH的CFI模式,读取各类信息''' | ||
+ | |||
+ | do_scan_nor_flash函数代码如下,该函数的功能:进入CFI模式读取NOR Flash中的厂家ID,设备ID,容量等信息。 | ||
+ | <syntaxhighlight lang="c" > | ||
+ | 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 } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | 第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某个扇区,编写某个地址,读某个地址。代码如下: | ||
+ | <syntaxhighlight lang="c" > | ||
+ | 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 } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | '''主函数''' | ||
+ | |||
+ | main函数代码如下所示。把timer中断去掉,否则: 测试NOR Flash时进入CFI等模式时, 如果发生了中断,cpu必定读NOR Flash,那么读不到正确的指令,导致程序崩溃。 | ||
+ | <syntaxhighlight lang="c" > | ||
+ | 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 } | ||
+ | </syntaxhighlight> | ||
= 第003节_Nor Flash编程_擦写读 = | = 第003节_Nor Flash编程_擦写读 = | ||
+ | 本实例的目的目的:擦除nor flash某个扇区,编写某个地址,读某个地址。 | ||
+ | |||
+ | '''等待烧写''' | ||
+ | |||
+ | 等待烧写完成 : 读数据, Q6无变化时表示结束 (参考芯片手册), | ||
+ | <syntaxhighlight lang="c" > | ||
+ | 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} | ||
+ | </syntaxhighlight> | ||
+ | '''擦除NOR Flash 某个扇区''' | ||
+ | do_erase_nor_flash函数的代码如下。参考芯片手册,就可以知道擦除某个扇区,还是相对比较简单的。 | ||
+ | <syntaxhighlight lang="c" > | ||
+ | 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 } | ||
+ | </syntaxhighlight> | ||
+ | 第131行,get_uint函数用于获取输入的地址。 | ||
+ | |||
+ | 第134,135这两行是解锁。 | ||
+ | |||
+ | 第136行是erase sector。 | ||
+ | |||
+ | 第138,139行是再次解锁。 | ||
+ | |||
+ | 第140行是对发出的扇区地址。 | ||
+ | |||
+ | 第 141行等待擦除完成。 | ||
+ | |||
+ | '''写NOR Flash''' | ||
+ | |||
+ | do_write_nor_flash的代码如下所示,开发板上的NOR Flash的位宽是16bit,所以可以把要写的数据构造出16bit然后在写进NOR Flash中。 | ||
+ | <syntaxhighlight lang="c" > | ||
+ | 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 } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | 第153行把通过get_uint获得的地址赋值给addr变量, | ||
+ | |||
+ | 第156行通过gets函数获得输入的字符串。 | ||
+ | |||
+ | 第168行两个8位的数据,组合成一个16位的数据赋值给变量val。 | ||
+ | |||
+ | |||
+ | '''读NOR Flash''' | ||
+ | |||
+ | do_read_nor_flash函数代码如下,由于NOR Flash是内存类接口,可以像内存一样读取。 | ||
+ | <syntaxhighlight lang="c" > | ||
+ | 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 } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | 第201行中的get_uint函数,从串口中获得输入的地址。 | ||
+ | |||
+ | 第203行,强制类型转化。 | ||
+ | |||
+ | 第207行~216行是对NOR Flash内容的读取,输出的内容为16进制的数据,由于NOR Flash是内存类接口,可以像内存一样读取。 | ||
+ | |||
+ | 第220行~227输出NOR Flash的内容为字符型数据,其中的第223行用来判断,输出的字符是否为不可视字符,要是为不可视字符输出点'.',要是可视字符输出字符。 | ||
+ | </div> | ||
='''查看《《所有章节目录》》'''= | ='''查看《《所有章节目录》》'''= | ||
− | |||
<categorytree mode=all background-color:white;">ARM裸机加强版</categorytree> | <categorytree mode=all background-color:white;">ARM裸机加强版</categorytree> | ||
[[Category:ARM裸机加强版 ]] | [[Category:ARM裸机加强版 ]] | ||
− | + | [[Category:Jz2440 ]] | |
− | [[Category: | + | [[Category:NorFlash]] |
2018年4月23日 (一) 11:08的最新版本
目录
第001节_Nor Flash原理及硬件操作
Nor Flash的连接线有地址线,数据线,片选信号读写信号等,Nor Flash的接口属于内存类接口,Nor Flash可以向内存一样读,但是不能像内存一样写,需要做一些特殊的操作才能进行写操作,读只需像内存一样读很简单。
Nor Flash原理图如图:
Flash介绍
常用的Flash类型有Nor Flash和NAND Flash两种。
Nor Flash由Intel公司在1988年发明,以替代当时在市场上占据主要地位的EPROM和E2PROM。NAND Flash由Toshiba公司在1989年发明。两者的主要差别如下表:
- | Nor | NAND |
---|---|---|
XIP(代码可以直接运行) | Yes | no |
性能(擦除) | 非常慢(5s) | 快(3ms) |
性能(写) | 慢 | 快 |
性能(读) | 快 | 快 |
可靠性 | 较高,位反转的比例小于NAND Flash的10% | 比较低,位反转比较常见,必须有校验措施,比如TNR必须有坏块管理措施 |
可擦除次数 | 10000 ~ 100000 | 100000 ~ 1000000 |
生命周期 | 低于NAND Flash的10% | 是Nor Flash的10倍以上 |
接口 | 与RAM接口相同 | I/O接口 |
易用性 | 容易 | 复杂 |
主要用途 | 常用于保存代码和关键数 | 用于保存数据 |
价格 | 高 | 低 |
Nor Flash支持XIP,即代码可以直接在Nor Flash上执行,无需复制到内存中。这是由于NorF lash的接口与RAM完全相同,可以随机访问任意地址的数据。Nor Flash进行读操作的效率非常高,但是擦除和写操作的效率很低,另外,Nor Flash的容量一般比较小。NAND Flash进行擦除和写操作的效率更高,并且容量更大。一般而言,Nor Flash用于存储程序,NAND Flash用于存储数据。基于NAND Flash的设备通常也要搭配Nor Flash以存储程字。
Flash存储器件由擦除单元(也称为块)组成,当要写某个块时,需要确保这个块己经 被擦除。Nor Flash的块大小范围为64kB、128kB:NAND Flash的块大小范围为8kB,64kB,擦/写一个Nor Flash块需4s,而擦/写一个NAND Flash块仅需2ms。Nor Flash的块太大,不仅增加了擦写时间,对于给定的写操作,Nor Flash也需要更多的擦除操作——特别是小文件,比如一个文件只有IkB,但是为了保存它却需要擦除人小为64kB—128kB的Nor Flash块。
Nor Flash的接口与RAM完全相同,可以随意访问任意地址的数据。而NAND Flash的 接口仅仅包含几个I/O引脚,需要串行地访问。NAND Flash一般以512字节为单位进行读写。这使得Nor Flash适合于运行程序,而NAND Flash更适合于存储数据。
容量相同的情况下,NAND Flash的体积更小,对于空间有严格要求的系统,NAND Flash可以节省更多空间。市场上Nor Flash的容量通常为IMB~4MB(也有32MB的Nor Flash),NAND Flash的容量为8MB~512MB。容量的差别也使得Nor Flash多用于存储程序,NAND Flash多用于存储数据。
对于Flash存储器件的可靠性需要考虑3点:位反转、坏块和可擦除次数。所有Flash器件都遭遇位反转的问题:由于Flash固有的电器特性,在读写数据过程中,偶然会产生一位或几位数据错误(这种概率很低),而NAND Flash出现的概率远大于Nor Flash,当位反转发生在关键的代码、数据上时,有可能导致系统崩溃。当仅仅是报告位反转,重新读取即可:如果确实发生了位反转,则必须有相应的错误检测/恢复措施。在NAND Flash上发生位反转的概率史高,推荐使用EDC/ECC进行错误检测和恢复。NAND Flash上面会有坏块随机分布在使用前需要将坏块扫描出来,确保不再使用它们,否则会使产品含有严重的故障。NAND Flash每块的可擦除次数通常在100000次左右,是Nor Flash的10倍。另外,因为NAND Flash的块大小通常是NorF lash的1/8,所以NAND Flash的寿命远远超过Nor Flash。
嵌入式Linux对Nor、NAND Flash的软件支持都很成熟。在Nor Flash上常用jffs2文 件系统,而在NAND Flash常用yaffs文件系统。在更底层,有MTD驱动程序实现对它们的读、写、擦除操仵,它也实现了EDC/ECC校验。
Nor Flash的操作
下面我们使用u-boot来体验Nor Flash的操作(开发板设置Nor启动,进入u-boot)。
1).使用OpenJTAG烧写UBOOT到Nor Flash
那么我们怎么用u-boot来操作呢?
下面简单的举一些例子:
复位(reset):往任何一个地址写入F0。
读ID(ReadSiliconID):很多的Nor Flash可以配置成位宽16bit(Word),位宽8bit(Byte)。对于我们使用的jz2440开发板使用是位宽16bit,怎样读ID呢?
根据前面得图可知,往Nor Flash的555地址写AA,再往2AA的地址写入55,再往555的地址写入90,然后就可以读ADI地址,就可以读到DDI数据了。
实例1
读数据:
在u-boot上执行:md.b0
结果(和我们烧进去的数据完全一样):
00000000:170000ea14f09fe514f09fe514f09fe5................
00000010:14f09fe514f09fe514f09fe514f09fe5................
00000020:6001f833c001f8332002f8337002f833`..3...3..3...3
00000030:e002f8330004f8332004f833efbeadde...3...3..3....
可以得出结论:u-boot可以像读内存一样来读nor flash
实例2
读ID(参考Nor手册)
- 往地址555H写入AAH(解锁)
- 往地址2AAH写入55H(解锁)
- 往地址555H写入90H(命令)
- 读0地址得到厂家ID(C2H)
- 读1地址得到设备ID(22DAH或225BH)
- 退出读ID状态:给任意地址写F0H就可以了。
2440的A1接到Nor的A0所以2440发出的地址是,Nor Flash收到的地址左移一位。比如:2440发出(555H<<1)地址,Nor Flash才能收到555H这个地址。
下面对在Nor Flash的操作,2440的操作,U-BOOT上的操作进行比较,如下表:
Nor Flash的操作 | 2440的操作 | U-BOOT上的操作 |
---|---|---|
往地址555H写入AAH(解锁) | 往地址AAAH写入AAH(解锁) | mw.waaaaa |
往地址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(22DAH或225BH) | md.w 2 1 (1:表示都一次) |
退出读ID状态:给任意地址写F0H就可以了 | 退出读ID状态:给任意地址写F0H就可以了 | mw.w 0 f0 |
1).当执行过
md.w 0 1
结果(输出厂家ID):
00000000:00c2..(00c2就是厂家ID)
2).当执行过
md.w 2 1
结果(输出设备ID):
00000002:2249I"(2249就是设备ID)
3).当执行
mw.w 0 f0
就退出读ID的状态,
执行:
md.b0
结果:
00000000:17.(读到的就是Nor Flash地址·0的数据)
Nor Flash的两种规范
通常内核里面要识别一个 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上操作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 |
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的写操作,如下表:
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 |
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,没有被擦除过,我们就要先擦出,擦除完后,才可以烧写,擦除烧写的命令可以从芯片手册里面获得。
第002节_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 }
第003节_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行用来判断,输出的字符是否为不可视字符,要是为不可视字符输出点'.',要是可视字符输出字符。