Nand flash复用数据总线和地址总线,并由相应的控制信号区分。以页为单位读取,块为单位擦除。由于没有完整的数据总线和地址总线,并且不能以字节为单位擦除,所以存储在Nand flash的程序要加载到ARM中执行。
mini2440 Nand flash连接:
nFCE:使能信号。
CLE:控制信号。
ALE:地址控制信号。
nFWE:写使能信号。
nFRE:读使能信号。
LDATA0 - LDATA7:数据传输。
mini2440中256M的NAND FLASH 存储单元结构组织结构图:
NAND FLASH每页有2KB+64B的ECC校验。每块有64页。有8个IO进行数据传输,由相应的控制信号来区分传输的为命令,地址,数据。
地址序列表
Nand Flash的使用:
Makefile文件:
objs := start.o init.o nand.o led.o
nand.bin : $(objs)
arm-linux-ld -Tnand.lds -o nand_elf $^
arm-linux-objcopy -O binary -S nand_elf $@
arm-linux-objdump -D -m arm nand_elf > nand.dis
%.o:%.c
arm-linux-gcc -Wall -c -O2 -o $@ $<
%.o:%.S
arm-linux-gcc -Wall -c -O2 -o $@ $<
clean:
rm -f nand.dis nand.bin nand_elf *.o
链接脚本:
SECTIONS
{
. = 0x0;
.one : { start.o init.o nand.o }
. = 0x30000000;
.two : AT(4096) { led.o }
}
把初始化操作放到前4KB,点亮LED的放到4KB后面,可以验证是否从Nand Flash读取成功。前4K中在CPU内部ARM执行的指令的跳转是与位置无关,而ldr pc,=main是与位置有关的指令,所以程序将调到SDARM中执行。由于没有开MMU,所以CPU发出的地址就是实际的物理地址。
初始化CPU:
#define mem_addr 0x48000000
#define WTDOG (*(volatile unsigned long *)0x53000000)
void close_wtdog()
{
WTDOG = 0x00;
}
void mem_init()
{
unsigned int i;
const unsigned long table[]={ 0x22011110, //BWSCON
0x00000700, //BANKCON0
0x00000700, //BANKCON1
0x00000700, //BANKCON2
0x00000700, //BANKCON3
0x00000700, //BANKCON4
0x00000700, //BANKCON5
0x00018005, //BANKCON6
0x00018005, //BANKCON7
0x008C07A3, //REFRESH
0x000000B1, //BANKSIZE
0x00000030, //MRSRB6
0x00000030, //MRSRB7
};
unsigned long* temp = (unsigned long*)mem_addr;
for(i = 0; i < 13; i++)
temp[i] = table[i];
}
Nand flash的操作:
#define NFCNF (*(volatile unsigned char*)0x4E000000)
#define NFCONT (*(volatile unsigned char*)0x4E000004)
#define NFCMMD (*(volatile unsigned char*)0x4E000008)
#define NFADDR (*(volatile unsigned char*)0x4E00000C)
#define NFDATA (*(volatile unsigned char*)0x4E000010)
#define NFSTAT (*(volatile unsigned char*)0x4E000020)
#define pag_size 2048
void write_cmd(unsigned int cmd)
{
NFCMMD = cmd;
}
void write_addr(unsigned int adds)
{
unsigned int col = adds & (pag_size - 1);
unsigned int pag = adds / pag_size;
NFADDR = col & 0xff;
unsigned char i;
for(i = 0; i < 10; i++);
NFADDR = (col >> 8) & 0xf;
for(i = 0; i < 10; i++);
NFADDR = pag & 0xff;
for(i = 0; i < 10; i++)
NFADDR = (pag >> 8) & 0xff;
for(i = 0; i < 10; i++);
NFADDR = (pag >> 16) & 0x3;
for(i = 0; i < 10; i++);
}
unsigned char read_data()
{
return NFDATA;
}
void busy()
{
int i;
while(!(NFSTAT & (0x1 << 0)))
for(i = 0; i < 10; i++);
}
void set_disce()
{
NFCONT |= (0x1 << 1);
}
void set_edce()
{
unsigned char i;
NFCONT &= (~(0x1 << 1));
for(i = 0; i < 10; i ++);
}
void rest_nand()
{
set_edce();
write_cmd(0xff);
busy();
set_disce();
}
void init_nand()
{
NFCNF = (0x0 << 12 | 0x3 << 8 | 0x0 << 4);
NFCONT = (0x1 << 4 | 0x1 << 1 | 0x1 << 0);
rest_nand();
}
void Read(unsigned char* buf,unsigned long addr,unsigned long size)
{
if((addr & (pag_size -1)) || (size & (pag_size - 1))) return;
set_edce();
unsigned int i,j;
for(i = addr; i < (addr + size); )
{
write_cmd(0x0);
write_addr(i);
write_cmd(0x30);
busy();
for(j = 0; j < pag_size; j++,i++)
{
*buf = read_data();
buf++;
}
}
set_disce();
return;
}
由于Nand flash的数据是通过8个IO传输的,所以要用char类型,否则不能达到预期的效果。
LED闪烁:
#define GPIO 0x56000000 /*GPIO的基地址*/
#define GPBCON (*(volatile unsigned long *)(GPIO + 0x10))
#define GPBDAT (*(volatile unsigned long *)(GPIO + 0x14))
void time(volatile unsigned long t)
{
while(t--);
}
int main()
{
GPBCON &= (~(0x3 << 12 | 0x3 << 14 | 0x3 << 16 | 0x3 << 10));
GPBCON |= (0x1 << 12 | 0x1 << 14 | 0x1 << 16 | 0x1 << 10);
while(1)
{
GPBDAT &= (~(0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8));
time(50000);
GPBDAT |= (0x1 << 5 | 0x1 << 6 | 0x1 << 7 | 0x1 << 8);
time(50000);
}
return 0;
}