实验四--nand flash的使用

一。环境和编译器

  开发板:jz2440

       系统:ubuntu12.04

       编译器:gcc

二。验证代码

1.head.S

 1 @******************************************************************************
 2 @ File:head.s
 3 @ 功能:设置SDRAM,将程序复制到SDRAM,然后跳到SDRAM继续执行
 4 @auther:
 5 @******************************************************************************       
 6   
 7 .text
 8 .global _start
 9 _start:
10             @函数disable_watch_dog, memsetup, init_nand, nand_read_ll在init.c中定义
11             ldr     sp, =4096               @设置堆栈 
12             bl      disable_watch_dog       @关WATCH DOG
13             bl      memsetup                @初始化SDRAM
14             bl      nand_init               @初始化NAND Flash
15 
16             @将NAND Flash中地址4096开始的1024字节代码(main.c编译得到)复制到SDRAM中
17                                             @nand_read_ll函数需要3个参数:
18             ldr     r0,     =0x30000000     @1. 目标地址=0x30000000,这是SDRAM的起始地址
19             mov     r1,     #4096           @2.  源地址   = 409620             mov     r2,     #2048           @3.  复制长度= 2048(bytes)
21 bl nand_read @调用C函数nand_read 22 23 ldr sp, =0x34000000 @设置栈 24 ldr lr, =halt_loop @设置返回地址 25 ldr pc, =main @b指令和bl指令只跳转32M的范围,所以这里使用向pc赋值的方法进行跳转 26 halt_loop: 27 b halt_loop

2.init.c

 1 #define     WTCON    (*(volatile unsigned long *)0x53000000)
 2 #define     MEM_CTL_BASE        0x48000000
 3  
 4 void disable_watch_dog();
 5 void memsetup();
 6 
 7 /*关掉看门狗 */
 8 void disable_watch_dog()
 9 {
10     WTCON    = 0;
11 }
12 
13 /* 设置SDRAM */
14 void memsetup()
15 {
16     int     i = 0;
17     unsigned long *p = (unsigned long *)MEM_CTL_BASE;
18 
19     /* SDRAM 13个寄存器的值 */
20     unsigned long  const    mem_cfg_val[]={ 0x22011110,     //BWSCON
21                                             0x00000700,     //BANKCON0
22                                             0x00000700,     //BANKCON1
23                                             0x00000700,     //BANKCON2
24                                             0x00000700,     //BANKCON3  
25                                             0x00000700,     //BANKCON4
26                                             0x00000700,     //BANKCON5
27                                             0x00018005,     //BANKCON6
28                                             0x00018005,     //BANKCON7
29                                             0x008C07A3,     //REFRESH
30                                             0x000000B1,     //BANKSIZE
31                                             0x00000030,     //MRSRB6
32                                             0x00000030,     //MRSRB7
33                                     };
34 
35     for(; i < 13; i++)
36         p[i] = mem_cfg_val[i];
37 }

3.nand.c  以下代码不支持2410 也不支持小页读写,是裁剪源码的结果。


  1 #define GSTATUS1        (*(volatile unsigned int *)0x560000B0)
  2 #define BUSY            1
  3 
  4 #define NAND_SECTOR_SIZE_LP    2048
  5 #define NAND_BLOCK_MASK_LP     (NAND_SECTOR_SIZE_LP - 1)
  6 
  7 typedef unsigned int S3C24X0_REG32;
  8 
  9 
 10 typedef struct {
 11     S3C24X0_REG32   NFCONF;
 12     S3C24X0_REG32   NFCONT;
 13     S3C24X0_REG32   NFCMD;
 14     S3C24X0_REG32   NFADDR;
 15     S3C24X0_REG32   NFDATA;
 16     S3C24X0_REG32   NFMECCD0;
 17     S3C24X0_REG32   NFMECCD1;
 18     S3C24X0_REG32   NFSECCD;
 19     S3C24X0_REG32   NFSTAT;
 20     S3C24X0_REG32   NFESTAT0;
 21     S3C24X0_REG32   NFESTAT1;
 22     S3C24X0_REG32   NFMECC0;
 23     S3C24X0_REG32   NFMECC1;
 24     S3C24X0_REG32   NFSECC;
 25     S3C24X0_REG32   NFSBLK;
 26     S3C24X0_REG32   NFEBLK;
 27 } S3C2440_NAND;
 28 
 29 static S3C2440_NAND * s3c2440nand = (S3C2440_NAND *)0x4e000000;
 30 
 31 
 32 
 33 /* 供外部调用的函数 */
 34 void nand_init(void);
 35 void nand_read(unsigned char *buf, unsigned long start_addr, int size);
 36 
 37 
 38 /* S3C2440的NAND Flash处理函数 */
 39 static void s3c2440_nand_reset(void);
 40 static void s3c2440_wait_idle(void);
 41 static void s3c2440_nand_select_chip(void);
 42 static void s3c2440_nand_deselect_chip(void);
 43 static void s3c2440_write_cmd(int cmd);
 44 static void s3c2440_write_addr_lp(unsigned int addr);
 45 static unsigned char s3c2440_read_data(void);
 46 
 47 
 48 
 49 /* 复位 */
 50 static void s3c2440_nand_reset(void)
 51 {
 52     s3c2440_nand_select_chip();
 53     s3c2440_write_cmd(0xff);  // 复位命令
 54     s3c2440_wait_idle();
 55     s3c2440_nand_deselect_chip();
 56 }
 57 
 58 /* 等待NAND Flash就绪 */
 59 static void s3c2440_wait_idle(void)
 60 {
 61     int i;
 62     volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;
 63     while(!(*p & BUSY))
 64         for(i=0; i<10; i++);
 65 }
 66 
 67 /* 发出片选信号 */
 68 static void s3c2440_nand_select_chip(void)
 69 {
 70     int i;
 71     s3c2440nand->NFCONT &= ~(1<<1);
 72     for(i=0; i<10; i++);    
 73 }
 74 
 75 /* 取消片选信号 */
 76 static void s3c2440_nand_deselect_chip(void)
 77 {
 78     s3c2440nand->NFCONT |= (1<<1);
 79 }
 80 
 81 /* 发出命令 */
 82 static void s3c2440_write_cmd(int cmd)
 83 {
 84     volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;
 85     *p = cmd;
 86 }
 87 
 88 
 89 
 90 
 91 static void s3c2440_write_addr_lp(unsigned int addr)
 92 {
 93     int i;
 94     volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
 95     int col, page;
 96 
 97     col = addr & NAND_BLOCK_MASK_LP;
 98     page = addr / NAND_SECTOR_SIZE_LP;
 99     
100     *p = col & 0xff;            /* Column Address A0~A7 */
101     for(i=0; i<10; i++);        
102     *p = (col >> 8) & 0x0f;     /* Column Address A8~A11 */
103     for(i=0; i<10; i++);
104     *p = page & 0xff;            /* Row Address A12~A19 */
105     for(i=0; i<10; i++);
106     *p = (page >> 8) & 0xff;    /* Row Address A20~A27 */
107     for(i=0; i<10; i++);
108     *p = (page >> 16) & 0x03;    /* Row Address A28~A29 */
109     for(i=0; i<10; i++);
110 }
111 
112 
113 /* 读取数据 */
114 static unsigned char s3c2440_read_data(void)
115 {
116     volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;
117     return *p;
118 }
119 
120 
121 /* 初始化NAND Flash */
122 void nand_init(void)
123 {
124 #define TACLS   0
125 #define TWRPH0  3
126 #define TWRPH1  0
127 
128 
129         /* 设置时序 */
130         s3c2440nand->NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
131         /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */
132         s3c2440nand->NFCONT = (1<<4)|(1<<1)|(1<<0);
133    
134     
135     /* 复位NAND Flash */
136     s3c2440_nand_reset();
137 }
138 
139 
140 /* 读函数 */
141 void nand_read(unsigned char *buf, unsigned long start_addr, int size)
142 {
143     int i, j;
144 
145 
146     if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) {
147         return ;    /* 地址或长度不对齐 */
148     }     //NAND_BLOCK_MASK_LP=2047   111_1111_1111  SIZE与之相与,若等于0,说明SIZE低11都是0,那么地址对齐;否则不对齐
149     
150 
151     /* 选中芯片 */
152     s3c2440_nand_select_chip();
153 
154     for(i=start_addr; i < (start_addr + size);) {
155       /* 发出READ0命令 */
156       s3c2440_write_cmd(0);
157 
158       /* Write Address */
159       s3c2440_write_addr_lp(i);
160 
161       s3c2440_write_cmd(0x30);        
162 
163       s3c2440_wait_idle();
164 
165 
166       for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) {
167 
168           *buf = s3c2440_read_data();
169           buf++;
170       }   //i的值等于0,512,1024,1536这四个值,也就可以读写0到2047这2k之内连续的地址的值
171     }
172 
173     /* 取消片选信号 */
174     s3c2440_nand_deselect_chip();
175     
176     return ;
177 }



4.led.c

    就是led闪烁之类的,免了。

5.Makefile

  

 1 objs := head.o init.o nand.o led.o
 2 
 3 nand.bin : $(objs)
 4     arm-linux-ld -Tnand.lds    -o nand_elf $^
 5     arm-linux-objcopy -O binary -S nand_elf $@
 6     arm-linux-objdump -D -m arm  nand_elf > nand.dis
 7 
 8 %.o:%.c
 9     arm-linux-gcc -Wall -c -O2 -o $@ $<
10 
11 %.o:%.S
12     arm-linux-gcc -Wall -c -O2 -o $@ $<
13 
14 clean:
15     rm -f  nand.dis nand.bin nand_elf  *.o

以上makefile与之前不同,下面是编译细节,请用心比较

6.链接脚本

1 SECTIONS { 
2   firtst      0x00000000 : { head.o init.o nand.o}
3   second     0x30000000 : AT(4096) { led.o }
4 } 
5  

三。实验结果

  经验证,可实现。

  本代码参考韦东山先生的源码,作来相应的裁剪。

  源码上支持2410和2440,以及大页,小页的读写,这里都分别作了裁剪到只支持2440和大页读。

  nand flash理论性的东西改天附上。

四。根据

        

  我们这里选用芯片的存储结构,这里选用大页读写,单位是2k,小页是512字节。其中64BYTES是OBB,这里没有用到。

  而大页的写地址定义如下:

  

  需要连续5次,正如代码中:

  

 1 static void s3c2440_write_addr_lp(unsigned int addr)
 2 {
 3     int i;
 4     volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFADDR;
 5     int col, page;
 6 
 7     col = addr & NAND_BLOCK_MASK_LP;
 8     page = addr / NAND_SECTOR_SIZE_LP;
 9     
10     *p = col & 0xff;            /* Column Address A0~A7 */
11     for(i=0; i<10; i++);        
12     *p = (col >> 8) & 0x0f;     /* Column Address A8~A11 */
13     for(i=0; i<10; i++);
14     *p = page & 0xff;            /* Row Address A12~A19 */
15     for(i=0; i<10; i++);
16     *p = (page >> 8) & 0xff;    /* Row Address A20~A27 */
17     for(i=0; i<10; i++);
18     *p = (page >> 16) & 0x03;    /* Row Address A28~A29 */
19     for(i=0; i<10; i++);
20 }

明显区别与小页的,可以参看原理图中小页的写地址定义。

此外,读写命令如下:

  复位命令是ff,代码是

1 /* 复位 */
2 static void s3c2440_nand_reset(void)
3 {
4     s3c2440_nand_select_chip();
5     s3c2440_write_cmd(0xff);  // 复位命令
6     s3c2440_wait_idle();
7     s3c2440_nand_deselect_chip();
8 }

而2440中相关控制寄存器如下截图:

使能信号是NFCONT的最低位  置1  ;

片选是NFCONT【1】位    写0;

NFCONT[4]是ECC功能用的。

片选代码:

 1 /* 发出片选信号 */
 2 static void s3c2440_nand_select_chip(void)
 3 {
 4     int i;
 5     s3c2440nand->NFCONT &= ~(1<<1);
 6     for(i=0; i<10; i++);    
 7 }
 8 
 9 /* 取消片选信号 */
10 static void s3c2440_nand_deselect_chip(void)
11 {
12     s3c2440nand->NFCONT |= (1<<1);
13 }

此外等待信号由NFSTAT决定:

代码是:

1 /* 等待NAND Flash就绪 */
2 static void s3c2440_wait_idle(void)
3 {
4     int i;
5     volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFSTAT;
6     while(!(*p & BUSY))
7         for(i=0; i<10; i++);
8 }

发出命令即直接向NFCMD写地址就好:

1 /* 发出命令 */
2 static void s3c2440_write_cmd(int cmd)
3 {
4     volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFCMD;
5     *p = cmd;
6 }

读数据:

1 /* 读取数据 */
2 static unsigned char s3c2440_read_data(void)
3 {
4     volatile unsigned char *p = (volatile unsigned char *)&s3c2440nand->NFDATA;
5     return *p;
6 }

连续读写一页的数据:

 1 /* 读函数 */
 2 void nand_read(unsigned char *buf, unsigned long start_addr, int size)
 3 {
 4     int i, j;
 5 
 6 
 7     if ((start_addr & NAND_BLOCK_MASK_LP) || (size & NAND_BLOCK_MASK_LP)) {
 8         return ;    /* 地址或长度不对齐 */
 9     }
10     
11 
12     /* 选中芯片 */
13     s3c2440_nand_select_chip();
14 
15     for(i=start_addr; i < (start_addr + size);) {
16       /* 发出READ0命令 */
17       s3c2440_write_cmd(0);
18 
19       /* Write Address */
20       s3c2440_write_addr_lp(i);
21 
22       s3c2440_write_cmd(0x30);        
23 
24       s3c2440_wait_idle();
25 
26 
27       for(j=0; j < NAND_SECTOR_SIZE_LP; j++, i++) {
28 
29           *buf = s3c2440_read_data();
30           buf++;
31       }
32     }
33 
34     /* 取消片选信号 */
35     s3c2440_nand_deselect_chip();
36     
37     return ;
38 }

首先判断地址对齐,如不对齐,读数据取消,因为结果是未知的。   上面解释就是SIZE与上2047(111_1111_1111),若SIZE的低11位都是0,那么相与后,结果是0,则说明地址对齐;否则不对齐。

i的值是0、2048这个值,可以在0到2047这2k区间连续读。

      /* 发出READ0命令 */

      s3c2440_write_cmd(0);

      /* Write Address */
      s3c2440_write_addr_lp(i);
      s3c2440_write_cmd(0x30);
  s3c2440_wait_idle();

先发出命令00,表示从开始读,也就是顺序读写。

然后写如地址。

原文地址:https://www.cnblogs.com/hulig7/p/4088187.html