本文是基于韦东山视频的学习笔记
总汇
YONG
R/B - 状态引脚
CLE - (COMMAND LATCH ENABLE)高电平时传输命令
nCE - (CHIP ENABLE)片选
ALE - (ADDRESS LATCH ENABLE)高电平时传输地址
nWE - (WRITE ENABLE)
nRE - (READ ENABLE)
nWP - (WRITE PROTECT)
CLE & ALE - 都为高电平时,传输数据
CLE/ALE 都拉高后,需要等待TACLS后,才能拉低WE。WE动作持续TWRPH0。WE动作完后,需要等待TWRPH1后,才能把WE拉高。
ALE/CLE 动作后(tCLS - tWP = 12-12=0=TACLS) 后,WE才能动作。WE动作持续tWP=12=TWRPH0。WE动作完,(tCLH/tALH=5=TWRPH1)后,ALE/CLE 才能动作。
寄存器
[NFCONF] NAND flash configuration register - 需要配置
[NFCONT] NAND flash control register - 需要配置
[NFCMMD] NAND flash command set register - 命令寄存器,写命令
[NFADDR] NAND flash address set register - 地址寄存器,写地址
[NFDATA] NAND flash data register - 数据寄存器,读写数据
[NFSTAT] NAND flash operation status register - 操作寄存器
NFCONF NAND flash configuration register 0x4E000000
- [13:12] = 0 : Duration = 0 = HCLK x TACLS =10ns x 0
- [10:8] = 1 :Duration = 12 ≤ 20 = HCLK x ( TWRPH0 + 1 ) = 10ns x (1+1)
- [6:4] = 0 :Duration = 5 ≤ 10 = HCLK x ( TWRPH1 + 1 ) = 10ns x (0+1)
NFCONT NAND flash control register 0x4E000004
- [0] = 1 :NAND flash controller enable
- [1] = 1 :Reg_nCE 片选信号(初始化先禁止片选,要用才片选)
- [4] = 1 :InitECC (Initialize ECC decoder/encoder(Write-only))
NFCMMD 命令寄存器 0x4E000008
NFADDR 地址寄存器 0x4E00000C
NFDATA 数据寄存器 0x4E000010
NFSTAT 状态寄存器 0x4E000020
- [0] 0: NAND Flash memory busy
- [0] 1: NAND Flash memory ready to operate
读数据
col 是 0~2047
row 是 0~0x1F400 (128k)
片选CE拉低动作后,CLE命令引脚拉高……其实这一切都有nand控制器帮我们做这些时序。实际上我们输送0x00命令,然后传入地址(2col x 3row),然后输送0x30命令,等R/B引脚就绪后,就可以读数据了。
擦除
写操作
void nand_write(unsigned char addr, unsigned char *buf, unsigned int len)
{
unsigned int i = 0;
unsigned int col = addr & (2048 - 1); //相等于addr % 2047
unsigned int page = addr / 2048;
nand_select();
while(i < len)
{
nand_cmd(0x80); //set up write cmd
nand_addr_byte((col) & 0xFF); //col的低8位
nand_addr_byte((col>>8) & 0xFF); //col的高8位
nand_addr_byte((page) & 0xFF); //page的低8位
nand_addr_byte((page>>8) & 0xFF); //page的高8位
nand_addr_byte((page>>16) & 0xFF); //page的8-16位
for (; (col < 2048) && (i < len); col++)
{
nand_w_data(buf[i++]);
}
col = 0; //写完了一页重置col
page++; //读下一页
nand_cmd(0x10); //end write cmd
wait_ready();
}
nand_deselect();
}
!!!
需要注意的是,Makefile的编译顺序,要把sdram的初始化放在nand前面,nand也要尽量放前,不然就不是在4k内存以内了。
!
执行读操作,发现是这样的… (数据应该是www.baidu.com)
77 00 00 00 77 00 00 00 77 00 00 00 2e 00 00 00 ; w…w…w…
62 00 00 00 61 00 00 00 69 00 00 00 64 00 00 00 ; b…a…i…d…
75 00 00 00 2e 00 00 00 63 00 00 00 6f 00 00 00 ; u…c…o…
6d 00 00 00 00 00 00 00 ff 00 00 00 ff 00 00 00 ; m…
找了半个小时bug,原来是nand_read函数的unsigned char buf,写成了unsigned int*buf*,导致每次读数据都读4字节,醉了。。
undefined reference to `memset’
在编译的时候说什么
nand_flash.o(.text+0x694): In function
do_read_nand_flash': : undefined reference to
memset’
Makefile:2: recipe for target ‘all’ failed
make: *** [all] Error 1
这样的一大堆东东,但是我并没有使用memset函数啊。
查找后发现,原来是数组赋初值这里用到了。
unsigned char buf[64] = {0};
但是是库文件没有这个函数喔,那写一个呗。
void *memset(void *str, int c, unsigned int n)
{
char *p = (char *)str;
if (str == NULL || n < 0) //如果是空则退出
return NULL;
while (n--)
{*p++ = c;} //逐一赋值
return p;
}
代码
/* NAND FLASH控制器 */
#define NFCONF (*((volatile unsigned long *)0x4E000000))
#define NFCONT (*((volatile unsigned long *)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))void nand_init()
{
/*
**NFCONF** NAND flash configuration register 0x4E000000
- [13:12] = 0 : Duration = 0 = HCLK x TACLS =10ns x 0
- [10:8] = 1 :Duration = 12 ≤ 20 = HCLK x ( TWRPH0 + 1 ) = 10ns x (1+1)
- [6:4] = 0 :Duration = 5 ≤ 10 = HCLK x ( TWRPH1 + 1 ) = 10ns x (0+1)
**NFCONT** NAND flash control register 0x4E000004
- [0] = 1 :NAND flash controller enable
- [1] = 1 :Reg_nCE 片选信号(初始化先禁止片选,要用才片选)
- [4] = 1 :InitECC (Initialize ECC decoder/encoder(Write-only)
*/
NFCONF = ((0<<12) | (1<<8) | (0<<4)); //设置时序
NFCONT = (( 1<<0) | (1<<1) | (1<<4)); //初始化
}
void wait_ready()
{
/*
**NFSTAT** 状态寄存器 0x4E000020
- [0] 0: NAND Flash memory busy
- [0] 1: NAND Flash memory ready to operate
*/
while(!(NFSTAT & 1)); //当[0]不等于1一直等待
}
void nand_select()
{
NFCONT &= ~(1<<1);
}
void nand_deselect()
{
NFCONT |= (1<<1);
}
unsigned char nand_data()
{
return NFDATA;
}
void nand_w_data(unsigned char val)
{
NFDATA = val;
}
void nand_cmd(unsigned char cmd) //8位的寄存器
{
int i = 0;
NFCCMD = cmd;
for(i=0; i<10; i++); //延时一会保证命令完成
}
void nand_addr_byte(unsigned char addr) //8位的寄存器
{
int i = 0;
NFADDR = addr;
for(i=0; i<10; i++); //延时一会保证命令完成
}
void read_id()
{
unsigned char buf[5] = {0};
nand_select();
nand_cmd(0x90);
nand_addr_byte(0X00); //读id的命令
buf[0] = nand_data();
buf[1] = nand_data();
buf[2] = nand_data();
buf[3] = nand_data();
buf[4] = nand_data();
puts("\n\r");
puts("maker id =");print_hex(buf[0]);puts("\n\r");
puts("device id =");print_hex(buf[1]);puts("\n\r");
puts("3th id =");print_hex(buf[2]);puts("\n\r");
puts("4th id =");print_hex(buf[3]);puts("\n\r");
puts("Page Size =");print_hex(1<<(buf[3] & 0x3));puts("\n\r");
puts("Block Size =");print_hex(64<<((buf[3]>>4) & 0x3));puts("\n\r");
puts("5th id =");print_hex(buf[4]);
nand_deselect();
}
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
/* col 是0~0x2FF (2047)
* page是0~0x1F400 (128k)
*/
unsigned int i = 0;
unsigned int col = addr & (2048 - 1); //相等于addr % 2047
unsigned int page = addr / 2048;
nand_select();
while(i < len)
{
nand_cmd(0x00);
nand_addr_byte((col) & 0xFF); //col的低8位
nand_addr_byte((col>>8) & 0xFF); //col的高8位
nand_addr_byte((page) & 0xFF); //page的低8位
nand_addr_byte((page>>8) & 0xFF); //page的高8位
nand_addr_byte((page>>16) & 0xFF); //page的8-16位
nand_cmd(0x30); //读命令
wait_ready();
for (; (col < 2048) && (i < len); col++)
{
buf[i++] = nand_data();
}
col = 0; //读完了一页重置col
page++; //读下一页
}
nand_deselect();
}
int nand_erase(unsigned int addr)
{
unsigned int page = addr / 2048;
nand_select();
nand_cmd(0x60); //set up cmd
/* 块擦除,只输入行地址 */
nand_addr_byte((page) & 0xFF); //page的低8位
nand_addr_byte((page>>8) & 0xFF); //page的高8位
nand_addr_byte((page>>16) & 0xFF); //page的8-16位
nand_cmd(0xD0); //erase cmd
wait_ready();
nand_cmd(0x70); //获取擦除状态
if ((nand_data() & 0x01)) //0: success, 1: error
{
nand_deselect();
return -1;
}
nand_deselect();
return 0;
}
void nand_write(unsigned char addr, unsigned char *buf, unsigned int len)
{
unsigned int i = 0;
unsigned int col = addr & (2048 - 1); //相等于addr % 2047
unsigned int page = addr / 2048;
nand_select();
while(i < len)
{
nand_cmd(0x80); //set up write cmd
nand_addr_byte((col) & 0xFF); //col的低8位
nand_addr_byte((col>>8) & 0xFF); //col的高8位
nand_addr_byte((page) & 0xFF); //page的低8位
nand_addr_byte((page>>8) & 0xFF); //page的高8位
nand_addr_byte((page>>16) & 0xFF); //page的8-16位
for (; (col < 2048) && (i < len); col++)
{
nand_w_data(buf[i++]);
}
col = 0; //写完了一页重置col
page++; //读下一页
nand_cmd(0x10); //end write cmd
wait_ready();
}
nand_deselect();
}
void do_read_nand_flash()
{
unsigned int i = 0, j = 0;
unsigned char addr, c;
unsigned char str[16];
unsigned char buf[64];
volatile unsigned char *p;
puts("Input addr: ");
addr = get_uint();
nand_read(addr, buf, 64);
p = (volatile unsigned char *)buf;
for (i=0; i<4; i++) //打印四行
{
for (j=0; j<16; j++) //每行16个数据
{
c = *p++;
str[j] = c;
printf("%02x ", c);
}
puts(" ; ");
for (j=0; j<16; j++)
{
if (str[j] < 0x20 || str[j] > 0x7e) //不可视字符
{
puts(".");
}
else //打印字符
{
putchar(str[j]);
}
}
puts("\n\r"); //每行换行
}
}
void do_erase_nand_flash()
{
unsigned char addr=0;
puts("Input addr: ");
addr = get_uint();
if (nand_erase(addr) == 0)
{
puts("Erase successful.\n\r");
}
else
puts("Erase error.\n\r");
}
void do_write_nand_flash()
{
unsigned char addr=0;
unsigned char str[100];
puts("Input addr: ");
addr = get_uint();
puts("Input string: ");
gets(str);
puts("Write...\n\r");
nand_write(addr, str, strlen(str)+1);
}
void nand_flash_test(void)
{
char c;
while (1)
{
/* 打印菜单, 供我们选择测试内容 */
puts("\n\r");
puts("[s] Scan nand flash\n\r");
puts("[e] Erase nand flash\n\r");
puts("[w] Write nand flash\n\r");
puts("[r] Read nand flash\n\r");
puts("[q] quit\n\r");
puts("Enter selection: ");
c = getchar();
putchar(c);
puts("\n\r");
/* 测试内容:
* 1. 识别nor flash
* 2. 擦除nor flash某个扇区
* 3. 编写某个地址
* 4. 读某个地址
*/
switch (c)
{
case 'q':
case 'Q':
return;
break;
case 's':
case 'S':
read_id();
break;
case 'e':
case 'E':
do_erase_nand_flash();
break;
case 'w':
case 'W':
do_write_nand_flash();
break;
case 'r':
case 'R':
do_read_nand_flash();
break;
default:
break;
}
}
}