Nand Flash 原理及硬件操作(1)

NAND Flash是一个存储芯片。

那么这样的操作很合理:“读地址A的数据,把数据B写到地址A”。

jz2440 NAND Flash 原理图:

问1:原理图上Nand Flash 和 s3c2440之间只有数据线,怎么传输地址?

答1:在DATA7~DATA0上既传输数据,又传输地址。

    当ALE为高电平时传输的是地址

    当ALE为低电平时传输的是数据

问2:从Nand Flash芯片数据手册可知,要操作Nand Flash需要先发出命令,如何分辨是命令还是数据呢?

答2:在DATA7~DATA0上既传输数据,又传输地址,又传输命令。

     当ALE为高电平时传输的是地址

     当CLE为高电平时传输的是命令

     当ALE和CLE都为低电平时传输的是数据

问3:数据线既接到Nand Flash,也接到Nor Flash,还接到SDRAM,DM9000等等,那么怎么避免干扰呢?

答3:这些设备要访问之前,必须先“选中”,没有“选中”的芯片不会工作,相当于没接一样。

问4:假设烧写Nand Flash,把命令、地址、数据发送给它之后,Nand Flash如何判断烧写完成?

答4:通过判断引脚RnB:  高电平表示就绪,低电平表示正忙状态。

问5:怎么操作Nand Flash呢?

答5:根据Nand Flash的芯片手册,一般的过程是:

    发出命令,发出地址,发出数据/读数据。

 

操作 Nand Flash jz2440
发出命令

1.CE 低电平

2.CLE 设为高电平

3.DATA7~DATA0上写入命令值

4.发出一个写脉冲WE

把命令值写到寄存器:NFCMMD

例如:NFCMMD = 命令值

发出地址

1.CE 低电平

2.ALE 设为高电平

3.DATA7~DATA0上写入地址

4.发出一个写脉冲WE

把地址的值写到寄存器:NFADDR

例子: NFADDR = 地址
发出数据

1.CE 低电平

2.ALE和CLE 设为高电平

3.DATA7~DATA0上写入数据

4.发出一个写脉冲WE

把数据的值写到寄存器:NFDATA

例子:NFDATA = 数据值
读数据

1.CE 低电平

2.发出一个读脉冲RE

3.读取DATA7~DATA0上的数据

 

读取寄存器:NFDATA

例子: 数据 = NFDATA

用u-boot来体验Nand Flash的操作:

1.实验一:读ID

操作 jz2400 u-boot
选中 NFCONT=0 md.l 0x4E000004 1;mw.w 0x4E000004 1
发出命令0x90 NFCMMD=0x90 mw.b 0x4E000008 0x90
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000c 0x00
读数据得到0xec value=NFDATA    md.b 0x4E000010 1
读数据得到Device Code value=NFDATA    md.b 0x4E000010 1
退出读ID状态 NFCMMD = 0xff mw.b 0x4E000008 0xff

mw.w 0x4E000004 1是将bit[1]=0吗???明显不是吧!

那是为什么呢?仔细查看一下手册才知道:

 

ec即为Maker Code。

 

2.实验二:读内容:读0地址的数据

使用u-boot命令:

nand dump 0

 

操作 jz2440 u-boot
选中 NFCONT=0 md.l 0x4E000004 1;mw.w 0x4E000004 1
发出命令0x00 NFCMMD=0x00 mw.b 0x4E000008 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000c 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000c 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000c 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000c 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000c 0x00
发出命令0x30 NFCMMD=0x30 mw.b 0x4E000008 0x30
读数据得到0x17 value=NFDATA md.b 0x4E000010 1
读数据得到0x00 value=NFDATA md.b 0x4E000010 1
读数据得到0x00 value=NFDATA md.b 0x4E000010 1
读数据得到0xea value=NFDATA md.b 0x4E000010 1
...    
退出读数据状态 NFCMMD = 0xff mw.b 0x4E000008 0xff

 

 Nand_Flash的编程:

程序框架:

 1 void nand_flash_test(void)
 2 {
 3     char c;
 4     
 5     /*打印菜单,供我们选择测试内容*/
 6     myprintf("\n\r");
 7     myprintf("[s]  Scan Nand Flash\n\r");
 8     myprintf("[e]  Erase Nand Flash\n\r");
 9     myprintf("[w]  Write Nand Flash\n\r");
10     myprintf("[r]  Read Nand 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             nand_flash_read_id();
28             break;
29 
30             case 'e':
31             case 'E':
32             do_erase_nand_flash();
33             break;
34 
35             case 'w':
36             case 'W':
37             do_write_nand_flash();
38             break;
39 
40             case 'r':
41             case 'R':
42             do_read_nand_flash();
43             break;
44 
45             case 'q':
46             case 'Q':
47             return;
48             break;
49 
50             default:
51             break;
52             
53         }
54 }

1.Nand_Flash的初始化函数:

 1 void nand_flash_init(void)
 2 {
 3     /*设置NNAD Flash 的时序,HCKL=100MHz*/
 4     NFCONF = ((0<<12)|(1<<8)|(0<<4));
 5 
 6     /*使能NAND Flash 控制器、禁止片选使能、初始化ECC*/
 7     NFCONT = ((1<<0)|(1<<1)|(1<<4));
 8 }
 9 
10 void nand_flash_selected(void)
11 {
12     /*使能片选*/
13     NFCONT &= ~(1<<1);
14 }
15 
16 void nand_flash_deselected(void)
17 {
18     /*禁止片选使能*/
19     NFCONT |= (1<<1);
20 }

2.由于Nand_Flash需要发出命令和发出地址才能进行读/写/擦除操作,所以我们需要单独地实现发出命令函数、发出地址函数。

 1 void nand_flash_cmd(unsigned char cmd )
 2 {
 3     volatile int i;
 4     NFCMD = cmd;
 5     for(i=0; i<10; i++);
 6 }
 7 
 8 void nand_flash_addr_byte(unsigned char addr)
 9 {
10     volatile int i;
11     NFADDR = addr;
12     for(i=0; i<10; i++);
13 }

3.由于对Nand_Flash读/写需要通过寄存器实现,所以我们还需要单独地实现读/写函数。

1 unsigned char nand_flash_r_data(void)
2 {
3     return NFDATA;
4 }
5 
6 void nand_flash_w_data(unsigned char val)
7 {
8     NFDATA = val;
9 }

4.nand_flash_read_id函数:

Page Size = 1<<(buf[3] & 0x03)  //等价于1x2^n

Block Size = 64<<(((buf[3]>>4) & 0x03))  //等价于64x2^n

 1 void nand_flash_read_id(void)
 2 {
 3     int i;
 4     unsigned char buf[5]={0};
 5     
 6     nand_flash_selected();
 7     nand_flash_cmd(0x90);
 8     nand_flash_addr_byte(0x00);
 9 
10     for(i=0;i<5;i++)
11         {
12             buf[i]=nand_flash_r_data();  //读取数据
13         }
14 
15     myprintf("Maker id= 0x%x\n\r",buf[0]);
16     myprintf("Device id= 0x%x\n\r",buf[1]);
17     myprintf("3rd   byte= 0x%x\n\r",buf[2]);
18     myprintf("4th   byte= 0x%x\n\r",buf[3]);
19     myprintf("page  size  = %d KB\n\r",1<<(buf[3]&0x03));    
20     myprintf("block size  = %d KB\n\r",64<<((buf[3]>>4)&0x03));
21     myprintf("5th   byte= 0x%x\n\r",buf[4]);
22 
23     nand_flash_deselected();
24 }

5.do_erase_nand_flash函数:

(1)实现do_erase_nand_flash函数之前,我们需要先实现擦除函数:

注意:Nand_Flash的是以Block为单位进行擦除的!!!

由于Nand_Flash是8位寻址方式,所以64M的Nand_Flash寻址范围为:64*2048=(131072-1)Byte = 0x1ffff

 1 int nand_flash_erase(unsigned int addr,unsigned int len)
 2 {
 3     int row = addr/2048;  //以Block为单位进行擦除
 4 
 5     if(addr & (0x1ffff))  //擦除的地址不能超过Nand_Flash的寻址范围
 6         {
 7             myprintf("Nand Flash Erase Err, Addr Is Not Block Align:\n\r");
 8             return -1;
 9         }
10     if(len & (0x1ffff))   //擦出的长度也不能超过Nand_Flash的最大长度
11         {
12             myprintf("Nand Flash Erase Err, Len Is Not Block Align:\n\r");
13             return -1;
14         }
15 
16     nand_flash_selected( );
17 
18     while(1)
19         {
20             row = addr/2048;  //擦除的Block
21             
22             nand_flash_cmd(0x60);
23 
24             /*发出row地址*/
25             nand_flash_addr_byte(row & 0xff);    
26             nand_flash_addr_byte((row >>8) & 0xff);
27             nand_flash_addr_byte((row >>16) & 0xff);
28 
29             nand_flash_cmd(0xd0);
30 
31             wait_ready();
32 
33             len -= (128*1024);  //1Block = 128*1024 Byte,不考虑OOB
34             if(len == 0)
35                 break;
36             addr += 128*1024;   //擦除下一个Block
37         }
38     
39     nand_flash_deselected();
40 
41     return 0;
42 }

(2)do_erase_nand_flash函数:

 1 void do_erase_nand_flash(void )
 2 {    
 3     unsigned int addr;
 4 
 5     /*获得地址*/
 6     myprintf("Enter the address of sector to erase: ");
 7     addr = get_uint();
 8 
 9     myprintf("Erase ...\n\r");
10     nand_flash_erase(addr,128*1024);
11 }

6.do_read_nand_flash函数:

(1)实现do_read_nand_flash函数之前,我们还需要实现nand_flash_read函数:

  首先要明白Nand_Flash是以页(Page)为最小单位进行读/写以块(Block)为最小单位进行擦除,也就是说当我们给定了读取的起始位置后,读操作将从该位置开始,连续读取到本Page的最后一个 Byte为止(可以包括OOB)。

1Block = 64 Pages

1Page = (2K+64)Bytes

Row Address - 行地址可以简单的认为是页号(Page)。

Column Address - 列地址相当于在一个页内的偏移地址,有了页号偏移地址便能正确的访问到每个存储单元。

Address=2048*Row_Address + Column_Address

 1 void nand_flash_read(unsigned int addr,unsigned char *buf,unsigned int len)
 2 {
 3     int i = 0;
 4     int row = addr/2048;
 5     int column = addr & (2048-1);    //等价于addr%2048,很重要的结论哦!   
 6         
 7     nand_flash_selected( );    //片选选中
 8 
 9     while(i<len)
10         {
11             /*发出00h命令*/
12             nand_flash_cmd(0x00);
13 
14             /*发出column地址*/
15             nand_flash_addr_byte(column & 0xff);    
16             nand_flash_addr_byte((column >>8) & 0xff);    
17 
18             /*发出row地址*/
19             nand_flash_addr_byte(row & 0xff);    
20             nand_flash_addr_byte((row >>8) & 0xff);
21             nand_flash_addr_byte((row >>16) & 0xff);
22 
23             /*发出30h命令*/
24             nand_flash_cmd(0x30);
25 
26             /*等待就绪*/
27             wait_ready();
28 
29             /*读数据*/
30             for(;(column<2048) && (i < len);column++)
31                 {
32                     buf[i++] = nand_flash_r_data();
33                 }
34             if(i==len)
35                 {
36                     break;
37                 }
38             column = 0;  
39             row++;  //读取下一页
40         
41         }
42 
43     nand_flash_deselected();    //取消选中
44 }

(2)do_read_nand_flash函数:

 1 void do_read_nand_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     unsigned char buf[64];
 9 
10 
11     /*获得地址*/
12     myprintf("Enter the address to read: ");
13     addr = get_uint();
14 
15     nand_flash_read(addr,buf,64);  //读取长度为64 Byte
16      
17     p = (volatile unsigned char *)buf;
18 
19     myprintf("Data :\n\r");
20     
21     /*长度固定为64Bytes*/
22     for(i=0; i<4; i++)
23         {
24             /*每行打印16个字符数据*/
25         for(j=0; j<16;j++)
26             {
27                 /*先打印数值*/
28                 c = *p++;
29                 str[j] = c;
30                 myprintf("%02x",c);
31                 myprintf(" ");
32             }
33     
34         myprintf("   ;");
35         for(j=0; j<16;j++)
36             {
37                 /*后打印不可显示字符*/
38                 if(str[j] < 0x20 || str[j] > 0x7e)
39                     {
40                         putchar('.');
41                     }
42                 else
43                     {
44                         putchar(str[j]);
45                     }
46             }
47         myprintf("\n\r");
48         }
49 }

7.do_write_nand_flash函数:

(1)实现do_write_nand_flash函数之前,我们还需要实现nand_flash_write函数:

 1 void nand_flash_write(unsigned int addr,unsigned char *buf,unsigned int len)
 2 {
 3     int i = 0;
 4     int row = addr/2048;
 5     int column = addr & (2048-1);
 6     
 7     nand_flash_selected( );
 8     while(1)
 9         {
10             nand_flash_cmd(0x80);
11 
12             /*发出column地址*/
13             nand_flash_addr_byte(column & 0xff);    
14             nand_flash_addr_byte((column >>8) & 0xff);    
15             
16             /*发出row地址*/
17             nand_flash_addr_byte(row & 0xff);    
18             nand_flash_addr_byte((row >>8) & 0xff);
19             nand_flash_addr_byte((row >>16) & 0xff);
20 
21             /*发出数据*/
22             for(;column<2048 && (i<len);)
23                 {
24                     nand_flash_w_data(buf[i++]);
25                     
26                 }    
27             
28             nand_flash_cmd(0x10);
29 
30             wait_ready();
31 
32             if(i == len)
33                 {
34                     break;
35                 }
36             column = 0;
37             row++;
38         }
39 
40     nand_flash_deselected();
41 }

(2)do_write_nand_flash函数:

void do_write_nand_flash(void )
{
    unsigned int addr;
    unsigned char str[100];
    int i,j;
    unsigned int val;

    /*获得地址*/
    myprintf("Enter the address of sector to write: ");
    addr = get_uint();

    myprintf("Enter the string to write: ");
    gets(str);

    myprintf("Writing ...\n\r");
    nand_flash_write(addr,str,strlen(str)+1);

    myprintf("Writing finished!");    
}

8.读/写状态函数:

1 static void wait_ready(void)
2 {
3     while(!(NFSTAT & 1));
4 }

在jz2440开发板中,如果程序的大小超过4K的话,是无法从Nand_Flash启动的!原因就不赘述了            

解决方案:

1.代码重定位

利用连接脚本将程序重定位到SDRAM中运行,前提是执行重定位功能的代码大小不能超过4K!              

Ps:小生不才,自己编写的执行重定位功能的代码超过了4K,所以一直。。。                      

Nand_Flash启动的条件:

1.代码大小小于4K;

2.具有重定位功能的代码大小小于4K;

3.编译时将具有重定位功能的源文件放在前面先编译;                    

之前的代码重定位代码:

 1 /*
 2 *reload all codes to SDRAM by C codes
 3 *we must know from where copy to where
 4 */
 5 void copy_to_sdram(void)
 6 {
 7 
 8     extern int code_start,bss_start;
 9 
10     volatile unsigned int *des = (volatile unsigned int *)&code_start;
11     volatile unsigned int *src = (volatile unsigned int *)0;
12     volatile unsigned int *end = (volatile unsigned int *)&bss_start;
13 
14     while(des < end)
15         {
16             *des++ = *src++;    
17         }
18 }
19 
20 
21 /*
22 *clean .bss segment
23 *we must know from where clean  where
24 */
25 void clean_bss_seg(void)
26 {
27     extern int bss_start,bss_end;
28 
29     volatile unsigned int *start = (volatile unsigned int *)&bss_start;
30     volatile unsigned int *end = (volatile unsigned int *)&bss_end;
31 
32     while(start <= end)
33         {
34 
35             *start++=0;
36         }
37 }

改进的重定位代码:

 1 int isBootFromNorFlash(void)
 2 {
 3     volatile unsigned int *p = (volatile unsigned int *)0;
 4     unsigned int value = *p;
 5 
 6     *p = 0x12345678;    
 7     if(*p == 0x12345678)
 8         {
 9             /*写成功,对应nand启动*/
10             *p = value;
11             return 0;
12         }
13     else
14         {
15             return 1;
16         }
17 }
18 
19 /*
20 *reload all codes to SDRAM by C codes
21 *we must know from where copy to where
22 */
23 void copy_to_sdram(void)
24 {
25     extern int code_start,bss_start;  
26     
27     volatile unsigned int *des =(volatile unsigned int *)&code_start;
28     volatile unsigned int *end =(volatile unsigned int *)&bss_start;
29     volatile unsigned int *src =(volatile unsigned int *)0;
30     int len;
31     len = ((int)&bss_start) -  ((int)&code_start); 
32 
33     if(isBootFromNorFlash()) 
34     {
35         while(des<end)
36         {
37             *des++ = *src++;
38         }
39  
40     }
41     else 
42     {
43         /*需要使用到Nand Flash,先初始化Nand Flash*/
44         nand_flash_init();
45         /*调用nand flash读取数据函数
46          *从 src 复制到 des ,总共复制len字节,也就是重定位的代码
47          */
48         nand_flash_read(src,des,len);  
49     }    
50 }
51 
52 /*
53 *clean .bss segment
54 *we must know from where clean  where
55 */
56 void clean_bss_seg(void)
57 {
58     extern int bss_start,bss_end;
59 
60     volatile unsigned int *start = (volatile unsigned int *)&bss_start;
61     volatile unsigned int *end = (volatile unsigned int *)&bss_end;
62 
63     while(start <= end)
64         {
65 
66             *start++=0;
67         }
68 }

 

 

 

 

                       

                 

 

posted @ 2020-04-02 21:30  坦率  阅读(1328)  评论(0编辑  收藏  举报