首页 > 技术文章 > Nor flash 原理及硬件操作(2)

huanian 2020-03-27 18:55 原文

回顾:

其实Nor flash的硬件操作---->Nor flash的内存操作---->Nor flash地址数据操作。

所以,对Nor flash手册的解读至关重要!!!《MX29LV800BT/BB(推荐)/MX29LV160D T/B》

对了,u-boot是裸机开发的集大成者,虚心学习总是没错的。

 

 

 程序框架:

 1 void nor_flash_test(void)
 2 {
 3     char c;
 4     
 5     /*打印菜单,供我们选择测试内容*/
 6     myprintf("\n\r");
 7     myprintf("[s]  Scan nor flash\n\r");
 8     myprintf("[e]  Erase nor flash\n\r");
 9     myprintf("[w]  Write nor flash\n\r");
10     myprintf("[r]  Read nor flash\n\r");
11     myprintf("[q]  Quit\n\r");
12     myprintf("Enter selection:");
13 
14     c = getchar();
15     myprintf("%c\n\r",c);
16 
17     /*测试内容:
18      *1.识别nor_falsh
19      *2.擦出nor_flash某扇区
20      *3.编写某个地址
21      *4.读某个地址
22      */
23     switch(c)
24         {
25             case 's':
26             case 'S':
27             do_scan_nor_flash();
28             break;
29 
30             case 'e':
31             case 'E':
32             do_erase_nor_flash();
33             break;
34 
35             case 'w':
36             case 'W':
37             do_write_nor_flash();
38             break;
39 
40             case 'r':
41             case 'R':
42             do_read_nor_flash();
43             break;
44 
45             case 'q':
46             case 'Q':
47             return;  //return直接返回到函数起始位置重新开始执行
48             break;
49 
50             default:
51             break;
52             
53         }
54 }

基本框架有了,接着我们就来完善每一个选择对应的被调函数:

再回忆一下,在Nor flash 原理及硬件操作(1)中,我们是如何对Nor flash的进行读/写的:

1 md.w addr        //读操作命令,参数
2
mw.w addr data //写操作命令,参数

所以,我们最好首先分别封装读/写函数,然后提供给其他的函数使用:

 #define  NOR_FLASH_BASE  0  //Nor flash的基地址为0
...

 1 unsigned int nor_read_word(unsigned int base,unsigned int offset)
 2 {
 3     volatile unsigned short *p = (volatile unsigned short *)(base + (offset<<1));
 4     return *p;
 5 }
 6 
 7 unsigned int nor_dat(unsigned int offset)
 8 {
 9     return nor_read_word(NOR_FLASH_BASE, offset);
10 }

(1)do_scan_nor_flash函数

 1 void do_scan_nor_flash(void)
 2 {
 3     char str[4];
 4     unsigned int size;
 5     int regions,i;
 6     int region_info_base;
 7     int block_addr, blocks,block_size,j;
 8     int cnt;
 9     int vendor,device;
10 
11     /*打印厂家ID*/
12     nor_cmd(0x555,0xaa);    //解锁
13     nor_cmd(0x2aa,0x55);
14     nor_cmd(0x555,0x90);
15 
16     vendor = nor_dat(0);    //读ID
17     device = nor_dat(1);
18 
19     nor_cmd(0,0xf0);        //复位,退出读ID操作
20     
21     
22     nor_cmd(0x55,0x98);    //进入CFI模式,读取其他的信息
23 
24     str[0] = nor_dat(0x10);  //读QRY确认进入CFI模式
25     str[1] = nor_dat(0x11);
26     str[2] = nor_dat(0x12);
27     str[3] = '\0';
28     myprintf("str = %s\n\r",str);
29 
30     /*打印容量*/
31     size = (1<<nor_dat(0x27));  //1个数左移n位,就等于将这个数乘以2的n次方
32     myprintf("vendor id = 0x%x,device id = 0x%x,Nor flash size = %dM\n\r",vendor,device,size/(1024*1024));
33 
34     /*打印各个扇区的起始地址*/
35     regions = nor_dat(0x2c);
36     region_info_base = 0x2d;
37     block_addr = 0;
38     myprintf("Block/sector start Address:\n\r");
39     cnt = 0;
40     for(i=0; i<regions; i++)
41         {
42             blocks = 1 + (nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8));
43             block_size = 256 * (nor_dat(region_info_base+2) + (nor_dat(region_info_base+3)<<8));
44             region_info_base += 4;
45             
46             for(j=0; j<blocks; j++)
47                 {
48                     /*打印每个block的起始地址*/
49                     printHex(block_addr);
50                     putchar(' ');
51                     block_addr += block_size;
52                     cnt++;
53                     if(cnt % 5 == 0 )
54                         {
55                             myprintf("\n\r");
56                         }
57                 }
58         }
59     myprintf("\n\r");
60     /*退出CFI模式*/
61     nor_cmd(0,0xf0);
62 }

(bits:15-0): 2F 0x00 30 0x01   所以要将2F、30合成低16位数0x0100

((bits:15-0) + 1)=blocks

(bits:31-16): 2D 0x0f 2E 0x00 所以要将2D、2E合成高16位数0x000f

(bits:31-16)*256=block_size

 

(2)do_erase_nor_flash函数

 1 void do_erase_nor_flash(void )
 2 {    
 3     unsigned int addr;
 4 
 5     /*获得地址*/
 6     myprintf("Enter the address of sector to erase: ");
 7     addr = get_uint();  //string_utils.c里面的函数,可以直接获得标准输入
 8 
 9     myprintf("Erase ...\n\r");
10 
11     nor_cmd(0x555,0xaa);    //解锁
12     nor_cmd(0x2aa,0x55);
13     nor_cmd(0x555,0x80);    //擦除
14     
15     nor_cmd(0x555,0xaa);    //再次解锁
16     nor_cmd(0x2aa,0x55);
17     
18   /*扇区地址写入0x30*/

19   nor_cmd(addr>>1,0x30); /*Nor flash 16bits*/ //一个数右移n位,就等于这个数除以2的n次方 20 wait_ready(addr); 21    myprintf("Erasing finished!"); 22 }

(3)由于读/写/擦除都需要时间,所以我们需要设置延时,但是延时又无法保证精准,可能会浪费系统时间。

我们查看手册,看是否有更好的解决办法:

 

 

 于是wait_ready函数:

 1 void wait_ready(unsigned int addr)
 2 {
 3     unsigned int val;
 4     unsigned int pre;
 5     
 6     pre = nor_dat(addr>>1);  //先读一次数据
 7     val = nor_dat(addr>>1);  //再读一次数据
 8 
 9     while((val & (1<<6)) != (pre & (1<<6)))  //若两次读取结果不一致,说明说明数据还在变化,继续等待
10         {
11             pre = val;  //继续读取
12             val = nor_dat(addr>>1);  
13         }
14 }

(4)do_read_nor_flash函数:

 1 void do_read_nor_flash(void )
 2 {
 3     unsigned int addr;
 4     volatile unsigned char *p;
 5     int i,j;
 6     unsigned char c;
 7     unsigned char str[16];
 8 
 9     /*获得地址*/
10     myprintf("Enter the address to read: ");
11     addr = get_uint();
12 
13     p = (volatile unsigned char *)addr;
14 
15     myprintf("Data :\n\r");
16     
17     /*长度固定为64Bytes*/
18     for(i=0; i<4; i++)
19         {
20             /*每行打印16个字符数据*/
21         for(j=0; j<16;j++)
22             {
23                 /*先打印数值*/
24                 c = *p++;
25                 str[j] = c;
26                 myprintf("%02x",c);  //格式控制显示结果为2个字节,高位补零
27                 myprintf(" ");
28             }
29     
30         myprintf("   ;");
31         for(j=0; j<16;j++)
32             {
33                 /*后打印不可显示字符*/ 
34                 if(str[j] < 0x20 || str[j] > 0x7e)  //ASCII码 可显示字符0x20~0x7e
35                     {
36                         putchar('.');
37                     }
38                 else
39                     {
40                         putchar(str[j]);
41                     }
42             }
43         myprintf("\n\r");
44         }
45 }

(5)do_write_nor_flash函数:

 1 void do_write_nor_flash(void )
 2 {
 3     unsigned int addr;
 4     unsigned char str[100];
 5     int i,j;
 6     unsigned int val;
 7 
 8     /*获得地址*/
 9     myprintf("Enter the address of sector to write: ");
10     addr = get_uint();
11 
12     myprintf("Enter the string to write: ");
13     gets(str);
14 
15     myprintf("Writing ...\n\r");
16 
17     /*str[0],str[1]==>>16bit*/
18     /*str[2],str[3]==>>16bit*/
19     
20     i=0;
21     j=1;
22     while(str[i] && str[j])
23         {
24             val = str[i] + (str[j]<<8);  //str[0],str[1]合并成16bit的数据,依次类推...
25 
26             /*烧写*/
27             nor_cmd(0x555,0xaa);    //解锁
28             nor_cmd(0x2aa,0x55);
29             nor_cmd(0x555,0xa0);    //发出烧写信号
30             nor_cmd(addr>>1,val);
31 
32             /*等待烧写完成,读数据Q6无变化时表示结束*/
33             wait_ready(addr);
34 
35             i += 2;  //合并str[2],str[3]
36             j += 2;
37             addr += 2;
38         }
39 
40         val = str[i];  //10 50 63 00 1f ff ff
41     
42     nor_cmd(0x555,0xaa);    //解锁
43     nor_cmd(0x2aa,0x55);
44     nor_cmd(0x555,0xa0);    //发出烧写信号
45     nor_cmd(addr>>1,val);  //写完最后一个高位数据
46     /*等待烧写完成,读数据Q6无变化时表示结束*/
47     wait_ready(addr);
48  
49   myprintf("Writting finished!");
50 50 }

代码展示:

  1 #include "myprintf.h"
  2 #include "string_utils.h"
  3 
  4 #define    NOR_FLASH_BASE        0
  5 
  6 /*进入nor flash的CFI模式
  7  *读取各类信息
  8  */
  9 
 10 /*55H 98*/
 11 /*地址(0+(0x55<<1))写0x98*/
 12 void nor_write_word(unsigned int base,unsigned int offset,unsigned short val)
 13 {
 14     volatile unsigned short *p = (volatile unsigned short *)(base + (offset<<1));
 15     *p = val;
 16 }
 17 
 18 /*offset是基于Nor flash的角度*/
 19 void nor_cmd(unsigned int offset,unsigned int cmd)
 20 {
 21     nor_write_word(NOR_FLASH_BASE, offset, cmd);
 22 }
 23 
 24 unsigned int nor_read_word(unsigned int base,unsigned int offset)
 25 {
 26     volatile unsigned short *p = (volatile unsigned short *)(base + (offset<<1));
 27     return *p;
 28 }
 29 
 30 unsigned int nor_dat(unsigned int offset)
 31 {
 32     return nor_read_word(NOR_FLASH_BASE, offset);
 33 }
 34 
 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 
 43     while((val & (1<<6)) != (pre & (1<<6)))
 44         {
 45             pre = val;
 46             val = nor_dat(addr>>1);
 47         }
 48 }
 49 
 50 void do_scan_nor_flash(void)
 51 {
 52     char str[4];
 53     unsigned int size;
 54     int regions,i;
 55     int region_info_base;
 56     int block_addr, blocks,block_size,j;
 57     int cnt;
 58     int vendor,device;
 59 
 60     /*打印厂家ID*/
 61     nor_cmd(0x555,0xaa);    //解锁
 62     nor_cmd(0x2aa,0x55);
 63     nor_cmd(0x555,0x90);
 64 
 65     vendor = nor_dat(0);    //读ID
 66     device = nor_dat(1);
 67 
 68     nor_cmd(0,0xf0);        //复位
 69     
 70     
 71     nor_cmd(0x55,0x98);    //进入CFI模式
 72 
 73     str[0] = nor_dat(0x10);
 74     str[1] = nor_dat(0x11);
 75     str[2] = nor_dat(0x12);
 76     str[3] = '\0';
 77     myprintf("str = %s\n\r",str);
 78 
 79     /*打印容量*/
 80     size = (1<<nor_dat(0x27));
 81     myprintf("vendor id = 0x%x,device id = 0x%x,Nor flash size = %dM\n\r",vendor,device,size/(1024*1024));
 82 
 83     /*打印各个扇区的起始地址*/
 84     regions = nor_dat(0x2c);
 85     region_info_base = 0x2d;
 86     block_addr = 0;
 87     myprintf("Block/sector start Address:\n\r");
 88     cnt = 0;
 89     for(i=0; i<regions; i++)
 90         {
 91             blocks = 1 + (nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8));
 92             block_size = 256 * (nor_dat(region_info_base+2) + (nor_dat(region_info_base+3)<<8));
 93             region_info_base += 4;
 94             
 95             for(j=0; j<blocks; j++)
 96                 {
 97                     /*打印每个block的起始地址*/
 98                     printHex(block_addr);
 99                     putchar(' ');
100                     block_addr += block_size;
101                     cnt++;
102                     if(cnt % 5 == 0 )
103                         {
104                             myprintf("\n\r");
105                         }
106                 }
107         }
108     myprintf("\n\r");
109     /*退出CFI模式*/
110     nor_cmd(0,0xf0);
111 }
112 
113 void do_erase_nor_flash(void )
114 {    
115     unsigned int addr;
116 
117     /*获得地址*/
118     myprintf("Enter the address of sector to erase: ");
119     addr = get_uint();
120 
121     myprintf("Erase ...\n\r");
122 
123     nor_cmd(0x555,0xaa);    //解锁
124     nor_cmd(0x2aa,0x55);
125     nor_cmd(0x555,0x80);    //擦除
126     
127     nor_cmd(0x555,0xaa);    //再次解锁
128     nor_cmd(0x2aa,0x55);
129     nor_cmd(addr>>1,0x30);    //扇区地址写入0x30
130     wait_ready(addr);
131 
132     myprintf("Erase finished!");
133 }
134 
135 void do_write_nor_flash(void )
136 {
137     unsigned int addr;
138     unsigned char str[100];
139     int i,j;
140     unsigned int val;
141 
142     /*获得地址*/
143     myprintf("Enter the address of sector to write: ");
144     addr = get_uint();
145 
146     myprintf("Enter the string to write: ");
147     gets(str);
148 
149     myprintf("Writing ...\n\r");
150 
151     /*str[0],str[1]==>>16bit*/
152     /*str[2],str[3]==>>16bit*/
153     
154     i=0;
155     j=1;
156     while(str[i] && str[j])
157         {
158             val = str[i] + (str[j]<<8);
159 
160             /*烧写*/
161             nor_cmd(0x555,0xaa);    //解锁
162             nor_cmd(0x2aa,0x55);
163             nor_cmd(0x555,0xa0);    //发出烧写信号
164             nor_cmd(addr>>1,val);
165 
166             /*等待烧写完成,读数据Q6无变化时表示结束*/
167             wait_ready(addr);
168 
169             i += 2;
170             j += 2;
171             addr += 2;
172         }
173 
174         val = str[i];    
175     
176     nor_cmd(0x555,0xaa);    //解锁
177     nor_cmd(0x2aa,0x55);
178     nor_cmd(0x555,0xa0);    //发出烧写信号
179     nor_cmd(addr>>1,val);
180     /*等待烧写完成,读数据Q6无变化时表示结束*/
181     wait_ready(addr);
182 
183     myprintf("Writing finished!");
184     
185 }
186 
187 void do_read_nor_flash(void )
188 {
189     unsigned int addr;
190     volatile unsigned char *p;
191     int i,j;
192     unsigned char c;
193     unsigned char str[16];
194 
195     /*获得地址*/
196     myprintf("Enter the address to read: ");
197     addr = get_uint();
198 
199     p = (volatile unsigned char *)addr;
200 
201     myprintf("Data :\n\r");
202     
203     /*长度固定为64Bytes*/
204     for(i=0; i<4; i++)
205         {
206             /*每行打印16个字符数据*/
207         for(j=0; j<16;j++)
208             {
209                 /*先打印数值*/
210                 c = *p++;
211                 str[j] = c;
212                 myprintf("%02x",c);
213                 myprintf(" ");
214             }
215     
216         myprintf("   ;");
217         for(j=0; j<16;j++)
218             {
219                 /*后打印不可显示字符*/
220                 if(str[j] < 0x20 || str[j] > 0x7e)
221                     {
222                         putchar('.');
223                     }
224                 else
225                     {
226                         putchar(str[j]);
227                     }
228             }
229         myprintf("\n\r");
230         }
231 }
232 
233 void nor_flash_test(void)
234 {
235     char c;
236     
237     /*打印菜单,供我们选择测试内容*/
238     myprintf("\n\r");
239     myprintf("[s]  Scan nor flash\n\r");
240     myprintf("[e]  Erase nor flash\n\r");
241     myprintf("[w]  Write nor flash\n\r");
242     myprintf("[r]  Read nor flash\n\r");
243     myprintf("[q]  Quit\n\r");
244     myprintf("Enter selection:");
245 
246     c = getchar();
247     myprintf("%c\n\r",c);
248 
249     /*测试内容:
250     *1.识别nor_falsh
251     *2.擦出nor_flash某扇区
252     *3.编写某个地址
253     *4.读某个地址
254     */
255     switch(c)
256         {
257             case 's':
258             case 'S':
259             do_scan_nor_flash();
260             break;
261 
262             case 'e':
263             case 'E':
264             do_erase_nor_flash();
265             break;
266 
267             case 'w':
268             case 'W':
269             do_write_nor_flash();
270             break;
271 
272             case 'r':
273             case 'R':
274             do_read_nor_flash();
275             break;
276 
277             case 'q':
278             case 'Q':
279             return;
280             break;
281 
282             default:
283             break;
284             
285         }
286 }

 

推荐阅读