Nand Flash的概述
nandflash主要以page(页)为单位进行读写,以block(块)为单位进行擦除。每一页中又分为main区和spare区,main区用于正常数据的存储,spare区用于存储一些附加信息,如块好坏的标记、块的逻辑地址、页内数据的ECC校验和等。
先看下电路原理图
从原理图可以看出:
在DATA0~DATA7上既传输数据,又传输地址,也传输命令:
当ALE为高电平时传输的是地址。
当CLE为高电平时传输的是命令。
当ALE和CLE都为低电平时传输的是数据。
假设烧写NAND FLASH,把命令、地址、数据发给它之后,NAND FLASH肯定不可能瞬间完成烧写的,怎么判断烧写完成?
通过状态引脚RnB来判断:它为高电平表示就绪,它为低电平表示正忙。
看一下NAND的引脚:
根据NAND FLASH的芯片手册,一般的过程是:
发出命令
发出地址
发出数据/读数据
相应的命令字如图:
s3c2440来说,内部集成了一个NAND FLASH控制器,CPU只需操作NAND FLASH控制器,就实现对NAND FLASH的操作。
NAND Flash控制器提供了NFCONF、NFCONT、NFCMMD、NFADDR、NFDATA、NFSTAT和其他一些寄存器。
NAND FLASH控制器的时序,是为了让NAND FLASH外设工作起来,假如外接不同的 NAND FLASH外设,那么它的操作时序可能就会不同,所以对Nand Flash控制器的设置也不同。
需要根据不同的nand flash,设置nand flash控制器。
看一下nand flash控制器时序:
由图可知,需要设置nand flash控制器的TACLS、TWRPH0、TWRPH1。
TACLS、TWRPH0、TWRPH1分别是NFCONF寄存器的某几位。
看一下nand flash芯片的时序:
分析:
依次看这3张图
图1 ,TACLS是CLE / ALE使能之后,过多久nWE使能。就对应图2中nand flash芯片的时序中的tCLS和tALS减去tWP。再看图3,tCLS、tALS、tWP最小都为12ns;也就是TACLS可以为12-12=0。
图1,TWRPH0是nWE的使能时间。对应图2中的tWP。再看图3,为12ns。
图1,TWRPH1是nWE失能之后,过多久CLE / ALE失能。就对应图2中nand flash芯片的时序中的tCLH、tALH。再看图3,都为5ns。
然后根据时序,设置寄存器。
HCLK为100MHz,也就是10ns。
所以,可设置TACLS即[13:12] = 0;TWRPH0即[10:8] = 1;TWRPH1即[6:4] = 0;
再根据NFCONT寄存器,初始化NAND Flash控制器。
void nand_init(void)
{
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/*设置NAND FLASH的时序*/
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
/*使能NAND FLASH控制器,初始化ECC,禁止片选*/
NFCONT = (1<<4) | (1<<1) | (1<<0);
}
再实现一个函数,读取nand的ID
void nand_select(void)
{
/*使能片选*/
NFCONT &=~(1<<1);
}
void nand_deselect(void)
{
/*禁止片选*/
NFCONT |= (1<<1);
}
void nand_chip_id(void)
{
unsigned char buf[5]={0};
nand_select();
nand_cmd(0x90);
nand_addr_byte(0x00);
buf[0] = nand_data();
buf[1] = nand_data();
buf[2] = nand_data();
buf[3] = nand_data();
buf[4] = nand_data();
nand_deselect();
printf("maker id = 0x%xnr",buf[0]);
printf("device id = 0x%xnr",buf[1]);
printf("3rd byte = 0x%xnr",buf[2]);
printf("4th byte = 0x%xnr",buf[3]);
printf("page size = %d kbnr",1 << (buf[3] & 0x03));
printf("block size = %d kbnr",64 << ((buf[3] >> 4) & 0x03));
printf("5th byte = 0x%xnr",buf[4]);
}
在之前的博文中,重定位代码到SDRAM,只能实现从NOR Flash复制代码到SDRAM。现在实现 读NAND 函数,然后实现从nand到SDRA的重定位。
具体代码如下:
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
int i = 0;
int page = addr / 2048;
int col = addr & (2048 - 1);
nand_select();
while (i < len)
{
/* 发出00h命令 */
nand_cmd(00);
/* 发出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
/* 发出30h命令 */
nand_cmd(0x30);
/* 等待就绪 */
wait_ready();
/* 读数据 */
for (; (col < 2048) && (i < len); col++)
{
buf[i++] = nand_data();
}
if (i == len)
break;
col = 0;
page++;
}
nand_deselect();
}
//然后在重定位函数中,添加代码
void nand_init(void);
void nand_read_relocate(unsigned int addr, unsigned char *buf, unsigned int len);
void copy2sdram(void)
{
/* 要从lds文件中获得 __code_start, __bss_start
* 然后从0地址把数据复制到__code_start
*/
extern int __code_start, __bss_start;
volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
volatile unsigned int *end = (volatile unsigned int *)&__bss_start;
volatile unsigned int *src = (volatile unsigned int *)0;
int len;
len = ((int)&__bss_start) - ((int)&__code_start);
if (isBootFromNorFlash()) //判断是nor还是nand启动
{//nor启动
while (dest < end)
{
*dest++ = *src++;
}
}
else
{//nand启动
nand_init();
nand_read(src, dest, len);//从nand复制代码到SDRAM
}
}
最后实现,读、写、擦除函数。
#include "s3c2440_soc.h"
#include "printf.h"
void nand_init(void)
{
#define TACLS 0
#define TWRPH0 1
#define TWRPH1 0
/*设置NAND FLASH的时序*/
NFCONF = (TACLS<<12) | (TWRPH0<<8) | (TWRPH1<<4);
/*使能NAND FLASH控制器,初始化ECC,禁止片选*/
NFCONT = (1<<4) | (1<<1) | (1<<0);
}
void nand_select(void)
{
/*使能片选*/
NFCONT &=~(1<<1);
}
void nand_deselect(void)
{
/*禁止片选*/
NFCONT |= (1<<1);
}
void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCCMD = cmd;
for(i=0; i<10; i++);
}
void nand_addr_byte(unsigned char addr)
{
volatile int i;
NFADDR = addr;
for(i=0; i<10; i++);
}
unsigned char nand_data(void)
{
return NFDATA;
}
void nand_w_data(unsigned char val)
{
NFDATA = val;
}
void wait_ready(void)
{
while (!(NFSTAT & 1));
}
void nand_chip_id(void)
{
unsigned char buf[5]={0};
nand_select();
nand_cmd(0x90);
nand_addr_byte(0x00);
buf[0] = nand_data();
buf[1] = nand_data();
buf[2] = nand_data();
buf[3] = nand_data();
buf[4] = nand_data();
nand_deselect();
printf("maker id = 0x%xnr",buf[0]);
printf("device id = 0x%xnr",buf[1]);
printf("3rd byte = 0x%xnr",buf[2]);
printf("4th byte = 0x%xnr",buf[3]);
printf("page size = %d kbnr",1 << (buf[3] & 0x03));
printf("block size = %d kbnr",64 << ((buf[3] >> 4) & 0x03));
printf("5th byte = 0x%xnr",buf[4]);
}
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
int i = 0;
int page = addr / 2048;
int col = addr & (2048 - 1);
nand_select();
while (i < len)
{
/* 发出00h命令 */
nand_cmd(00);
/* 发出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
/* 发出30h命令 */
nand_cmd(0x30);
/* 等待就绪 */
wait_ready();
/* 读数据 */
for (; (col < 2048) && (i < len); col++)
{
buf[i++] = nand_data();
}
if (i == len)
break;
col = 0;
page++;
}
nand_deselect();
}
int nand_erase(unsigned int addr, unsigned int len)
{
int page = addr / 2048;
if (addr & (0x1FFFF))
{
printf("nand_erase err, addr is not block alignnr");
return -1;
}
if (len & (0x1FFFF))
{
printf("nand_erase err, len is not block alignnr");
return -1;
}
nand_select();
while (1)
{
page = addr / 2048;
nand_cmd(0x60);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
nand_cmd(0xD0);
wait_ready();
len -= (128*1024);
if (len == 0)
break;
addr += (128*1024);
}
nand_deselect();
return 0;
}
void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)
{
int page = addr / 2048;
int col = addr & (2048 - 1);
int i = 0;
nand_select();
while (1)
{
nand_cmd(0x80);
/* 发出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
/* 发出数据 */
for (; (col < 2048) && (i < len); )
{
nand_w_data(buf[i++]);
}
nand_cmd(0x10);
wait_ready();
if (i == len)
break;
else
{
/* 开始下一个循环page */
col = 0;
page++;
}
}
nand_deselect();
}
void do_read_nand_flash(void)
{
unsigned int addr;
volatile unsigned char *p;
int i, j;
unsigned char c;
unsigned char str[16];
unsigned char buf[64];
/* 获得地址 */
printf("Enter the address to read: ");
addr = get_uint();
nand_read(addr, buf, 64);
p = (volatile unsigned char *)buf;
printf("Data : nr");
/* 长度固定为64 */
for (i = 0; i < 4; i++)
{
/* 每行打印16个数据 */
for (j = 0; j < 16; j++)
{
/* 先打印数值 */
c = *p++;
str[j] = c;
printf("%02x ", c);
}
printf(" ; ");
for (j = 0; j < 16; j++)
{
/* 后打印字符 */
if (str[j] < 0x20 || str[j] > 0x7e) /* 不可视字符 */
putchar('.');
else
putchar(str[j]);
}
printf("nr");
}
}
void do_erase_nand_flash(void)
{
unsigned int addr;
/* 获得地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();
printf("erasing ...nr");
nand_erase(addr, 128*1024);
}
void do_write_nand_flash(void)
{
unsigned int addr;
unsigned char str[100];
int i, j;
unsigned int val;
/* 获得地址 */
printf("Enter the address of sector to write: ");
addr = get_uint();
printf("Enter the string to write: ");
gets(str);
printf("writing ...nr");
nand_write(addr, str, strlen(str)+1);
}
void nand_flash_test(void)
{
char c;
while (1)
{
/* 打印菜单, 供我们选择测试内容 */
printf("[s] Scan nand flashnr");
printf("[e] Erase nand flashnr");
printf("[w] Write nand flashnr");
printf("[r] Read nand flashnr");
printf("[q] quitnr");
printf("Enter selection: ");
c = getchar();
printf("%cnr", c);
/* 测试内容:
* 1. 识别nand flash
* 2. 擦除nand flash某个扇区
* 3. 编写某个地址
* 4. 读某个地址
*/
switch (c)
{
case 'q':
case 'Q':
return;
break;
case 's':
case 'S':
nand_chip_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;
}
}
}
上一篇:对LCD的操作(S3C2440)
下一篇:对Nor Flash的操作(S3C2440)
推荐阅读最新更新时间:2024-11-17 16:24
设计资源 培训 开发板 精华推荐
- 基于0JCXG0的Windows Hello 人脸摄像头转接板
- AKD4613-A,AK4613 评估板,24 位,4/12 通道音频编解码器
- STEVAL-IHT005V2,基于 STM32F100 MCU 的全 3.3V ACS/Triac 控制演示板
- AD8657ARMZ比较器B典型应用电路
- 使用 ROHM Semiconductor 的 BD25IA5WEFJ 的参考设计
- orangepi-zerov2.0
- 使用 Analog Devices 的 LTC3725EMSE 的参考设计
- 降压转换器简化了辅助低压负电源轨的设计任务
- 使用 LTC1644 的紧凑型 PCI 热插拔控制应用
- AKD4586,带 DIR 的 AK4586 多通道音频编解码器评估板