Nand Flash 就是一个flash闪存 ,存储数据
而S3C2440A 又为Nand Flash 配有相应的控制寄存器,可以直接将Flash 接上处理器
整个代码:
//////////////////LAB13 NandrFlash 应用实验
#define GLOBAL_CLK 1
#include"def.h"
#include"2440addr.h"
#include"2440lib.h"
#include"Nand.h"
/*
//////////////////////////////////////////////////////////////
#define SUM_BLOCKS 2048
#define PAGES_PER_BLOCK 64
#define DATA_BYTES_PER_PAGE 2048
#define SPARE_BYTES_PER_PAGE 64
////////
#define NUM_BLOCKS 0x1000 // 64 MB Smartmedia card.
#define SECTOR_SIZE 512
#define SPARE_SIZE 16
#define PAGES_PER_BLOCK 32
// For flash chip that is bigger than 32 MB, we need to have 4 step address
//
#define NFCONF_INIT 0xF830 // 512-byte 4 Step Address
#define NEED_EXT_ADDR 1
//#define NFCONF_INIT 0xA830 // 256-byte 4 Step Address
//#define NEED_EXT_ADDR 0
//#define NFCONF_INIT 0xF840
// NAND Flash Command. This appears to be generic across all NAND flash chips
#define CMD_READ 0x00 // Read
#define CMD_READ1 0x01 // Read1
#define CMD_READ2 0x50 // Read2
#define CMD_READ3 0x30 // Read3
#define CMD_READID 0x90 // ReadID
#define CMD_WRITE1 0x80 // Write phase 1
#define CMD_WRITE2 0x10 // Write phase 2
#define CMD_ERASE1 0x60 // Erase phase 1
#define CMD_ERASE2 0xd0 // Erase phase 2
#define CMD_STATUS 0x70 // Status read
#define CMD_RESET 0xff // Reset
// Status bit pattern
#define STATUS_READY 0x40 // Ready
#define STATUS_ERROR 0x01 // Error
// Status bit pattern
#define STATUS_READY 0x40
#define STATUS_ERROR 0x01
#define NF_CMD(cmd) {rNFCMD = (cmd); }
#define NF_ADDR(addr) {rNFADDR = (addr); }
#define NF_nFCE_L() {rNFCONT &= ~(1<<1); } //FCE 片选低电平信号 Enable
#define NF_nFCE_H() {rNFCONT |= (1<<1); } // FCE Disable
#define NF_RSTECC() {rNFCONT |= (1<<4); } //Init Ecc decoder/encoder
#define NF_RDMECC() (rNFMECC0 )
#define NF_RDSECC() (rNFSECC )
#define NF_RDDATA() (rNFDATA)
#define NF_RDDATA8() (rNFDATA8)
#define NF_WRDATA(data) {rNFDATA = (data); }
#define NF_WAITRB() {while(!(rNFSTAT&(1<<0)));} //等待nandflash不忙 NFSTAT[0] 0 for busy , 1 for ready to operate
#define NF_CLEAR_RB() {rNFSTAT |= (1<<2); } //清除RnB信号 to clear this value write 1
#define NF_DETECT_RB() {while(!(rNFSTAT&(1<<2)));} //等待RnB信号变高,waiting for ready from busy
#define NF_MECC_UnLock() {rNFCONT &= ~(1<<5); } //解锁main区ECC
#define NF_MECC_Lock() {rNFCONT |= (1<<5); } //锁定main区ECC
#define NF_SECC_UnLock() {rNFCONT &= ~(1<<6); } //解锁spare区ECC
#define NF_SECC_Lock() {rNFCONT |= (1<<6); } //锁定spare区ECC
#define RdNFDat8() (rNFDATA8) //byte access
#define RdNFDat() RdNFDat8() //for 8 bit nand flash, use byte access
#define WrNFDat8(dat) (rNFDATA8 = (dat)) //byte access
#define WrNFDat(dat) WrNFDat8(dat) //for 8 bit nand flash, use byte access
#define pNFCONF rNFCONF
#define pNFCMD rNFCMD
#define pNFADDR rNFADDR
#define pNFDATA rNFDATA
#define pNFSTAT rNFSTAT
#define pNFECC rNFECC0
#define NF_CE_L() NF_nFCE_L() //FCE 片选低电平信号 Enable
#define NF_CE_H() NF_nFCE_H()
#define NF_DATA_R() rNFDATA
#define NF_ECC() rNFECC0
// HCLK=100Mhz
#define TACLS 1 // 1-clk(0ns)
#define TWRPH0 4 // 3-clk(25ns)
#define TWRPH1 0 // 1-clk(10ns) //TACLS+TWRPH0+TWRPH1>=50ns
//////////////////////////
*/
#define CMD_RANDOMREAD1 (0x05)
#define CMD_RANDOMREAD2 (0xE0)
#define CMD_RANDOMWRITE (0x80)
U8 myNF_RamdomRead(U32 page_number, U32 add)
{
NF_nFCE_L(); //打开nandflash片选
NF_CLEAR_RB(); //清RnB信号
NF_CMD(CMD_READ); //页读命令周期 0x00h
//写入5个地址周期
NF_ADDR(0x00); //列地址A0~A7
NF_ADDR(0x00); //列地址A8~A11
NF_ADDR((page_number) & 0xff); //行地址A12~A19
NF_ADDR((page_number >> 8) & 0xff); //行地址A20~A27
NF_ADDR((page_number >> 16) & 0xff); //行地址A28
NF_CMD(CMD_READ3); //页读命令周期0x 30h
NF_DETECT_RB(); //等待RnB信号变高,即不忙
NF_CMD(CMD_RANDOMREAD1); //随意读命令周期1
//页内地址
NF_ADDR((char)(add&0xff)); //列地址A0~A7
NF_ADDR((char)((add>>8)&0x0f)); //列地址A8~A11
NF_CMD(CMD_RANDOMREAD2); //随意读命令周期2
return NF_RDDATA8(); //读取数据
}
U8 myNF_RamdomWrite(U32 page_number, U32 add, U8 dat)
{
U8 temp,stat;
int i ;
NF_nFCE_L(); //打开nandflash片选
NF_CLEAR_RB(); //清RnB信号
NF_CMD(CMD_WRITE1); //页写命令周期1 0x80h
//写入5个地址周期
NF_ADDR(0x00); //列地址A0~A7
NF_ADDR(0x00); //列地址A8~A11
NF_ADDR((page_number) & 0xff); //行地址A12~A19
NF_ADDR((page_number >> 8) & 0xff); //行地址A20~A27
NF_ADDR((page_number >> 16) & 0xff); //行地址A28
NF_CMD(CMD_RANDOMWRITE); //随意写命令 0x85h
//页内地址
NF_ADDR((char)(add&0xff)); //列地址A0~A7
NF_ADDR((char)((add>>8)&0x0f)); //列地址A8~A11
WrNFDat8(dat); //写入数据
NF_CMD(CMD_WRITE2); //页写命令周期2 0x10h
for(i = 0; i != 1000; ++i); //延时一段时间
NF_CMD(CMD_STATUS); //读状态命令
//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
do{
stat = NF_RDDATA8();
}while(!(stat&0x40));
NF_nFCE_H(); //关闭nandflash片选
//判断状态值的第0位是否为0,为0则写操作正确,否则错误
if (stat & 0x1)
return 0x44; //失败
else
return 0x66; //成功
}
/*
自定义坏块表示形式 每一页主数据区为2K == 2048bytes 即是0--2047
2048--2051 为4字节的主数据区的ECC
2052---2053 为2字节的spare区的ECC
定义: 每块的第一页的 2054 处的1个字节,数据如果为0x33 则表示该为坏块
*/
static U8 myNF_IsBadBlock(U32 block)
{
return myNF_RamdomRead(block*64, 2054);
}
static U8 myNF_MarkBadBlock(U32 block)
{
U8 result;
result = myNF_RamdomWrite(block*64, 2054, 0x33);
if(result == 0x44)
return 0x21; //写坏块标注失败
else
return 0x60; //写坏块标注成功
}
static U8 myNF_EraseBlock(U32 addr)
{
char stat, temp;
int i ;
temp = myNF_IsBadBlock(addr); //判断该块是否为坏块
if(temp == 0x33)
return 0x42; //是坏块,返回
NF_nFCE_L(); //打开片选
NF_CLEAR_RB(); //清RnB信号
NF_CMD(CMD_ERASE1); //擦除命令周期1
//写入3个地址周期,从A18开始写起
NF_ADDR((addr << 6) & 0xff); //行地址A18~A19 ----IO6 IO7
NF_ADDR((addr >> 2) & 0xff); //行地址A20~A27 ----IO0--IO7
NF_ADDR((addr >> 10) & 0xff); //行地址A28 ----IO0
NF_CMD(CMD_ERASE2); //擦除命令周期2
for(i = 0 ; i != 1000 ; ++i) ; //延时一段时间
NF_CMD(CMD_STATUS); //读状态命令
//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
do{
stat = NF_RDDATA8();
}while(!(stat&0x40));
NF_nFCE_H(); //关闭nandflash片选
//判断状态值的第0位是否为0,为0则擦除操作正确,否则错误
if (stat & 0x1)
{
temp = myNF_MarkBadBlock(addr>>6); //标注该块为坏块
if (temp == 0x21)
return 0x43; //标注坏块失败
else
return 0x44; //擦除操作失败
}
else
return 0x66; //擦除操作成功
}
static void myNF_Reset(void)
{
NF_CE_L(); //打开nandflash片选
NF_CLEAR_RB(); //清除RnB信号
NF_CMD(CMD_RESET);
NF_DETECT_RB();
NF_CE_H(); //关闭nandflash片选
}
static void myInitNand(void)
{
//配置芯片引脚
rGPACON = (rGPACON &~(0x3f<<17) ) |(0x3f<<17);
//TACLS=1、TWRPH0=2、TWRPH1=0,8位IO
rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<0);
//非锁定,屏蔽nandflash中断,初始化ECC及锁定main区和spare区ECC,使能nandflash片选及控制器
rNFCONT = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(1<<6)|(1<<5)|(1<<4)|(1<<1)|(1<<0);
rNFSTAT = 0;
}
static char myNF_ReadID()
{
char pMID;
char pDID;
char cyc3, cyc4, cyc5;
int i;
NF_nFCE_L(); //打开nandflash片选
NF_CLEAR_RB(); //清除RnB信号
NF_CMD(CMD_READID); // read id command
NF_ADDR(0x0);
for ( i = 0; i < 100; i++ );
pMID = NF_RDDATA8(); ///Maker code
pDID = NF_RDDATA8(); ///Device code
cyc3 = NF_RDDATA8();
cyc4 = NF_RDDATA8();
cyc5 = NF_RDDATA8();
NF_nFCE_H();
Uart_Printf("Maker Code: %X\n\r",pMID); //EC
Uart_Printf("Device Code: %X\n\r",pDID); //DA
Uart_Printf("3rd Cyc Code: %X\n\r",cyc3); //10
Uart_Printf("4th Cyc Code: %X\n\r",cyc4); //95
Uart_Printf("5th Cyc Code: %X\n\r",cyc5); //44
return (pDID);
}
static void mySB_ReadPage(U32 addr, unsigned char * to)
{
U32 i;
myNF_Reset();
// Enable the chip
NF_nFCE_L();
NF_CLEAR_RB();
// Issue Read command
NF_CMD(CMD_READ);
// Set up address
NF_ADDR(0x00);
NF_ADDR((addr) & 0xff);
NF_ADDR((addr >> 8) & 0xff);
NF_ADDR((addr >> 16) & 0xff);
NF_DETECT_RB(); // wait tR(max 12us)
for (i = 0; i < 512; i++)
{
to[i] = NF_RDDATA8();
}
NF_nFCE_H();
}
static void myLB_ReadPage(U32 addr, unsigned char * to)
{
U32 i;
myNF_Reset();
// Enable the chip
NF_nFCE_L();
NF_CLEAR_RB();
// Issue Read command
NF_CMD(CMD_READ); ////00
// Set up address
NF_ADDR(0x00); //column Address A0 --A7
NF_ADDR(0x00); //column Address A8 --A11
NF_ADDR((addr) & 0xff); //Row Address A12--A19
NF_ADDR((addr >> 8) & 0xff); //Row Address A20--A27
NF_ADDR((addr >> 16) & 0xff); //Row Address A28
NF_CMD(CMD_READ3); ///30
NF_DETECT_RB(); // wait tR(max 12us)
for (i = 0; i < 2048; i++)
{
to[i] = NF_RDDATA8();
}
NF_nFCE_H();
}
static unsigned char myNF_ReadPage(U32 addr,unsigned char * to )
{
U32 i;
U32 mecc0, secc;
NF_RSTECC();
NF_MECC_UnLock(); //解锁main区ECC
// Enable the chip
NF_nFCE_L();
NF_CLEAR_RB();
// Issue Read command
NF_CMD(CMD_READ); ////00
// Set up address
NF_ADDR(0x00); //column Address A0 --A7
NF_ADDR(0x00); //column Address A8 --A11
NF_ADDR((addr) & 0xff); //Row Address A12--A19
NF_ADDR((addr >> 8) & 0xff); //Row Address A20--A27
NF_ADDR((addr >> 16) & 0xff); //Row Address A28
NF_CMD(CMD_READ3); ///30
NF_DETECT_RB(); // wait tR(max 12us)
for (i = 0; i < 2048; i++)
{
to[i] = NF_RDDATA8();
}
NF_MECC_Lock(); //锁定main区ECC值
NF_SECC_UnLock(); //解锁spare区ECC
mecc0=NF_RDDATA(); //读spare区的前4个地址内容,即第2048~2051地址,这4个字节为main区的ECC
//把读取到的main区的ECC校验码放入NFMECCD0/1的相应位置内
rNFMECCD0=((mecc0&0xff00)<<8)|(mecc0&0xff);
rNFMECCD1=((mecc0&0xff000000)>>8)|((mecc0&0xff0000)>>16);
NF_SECC_Lock(); //锁定spare区的ECC值
secc=NF_RDDATA(); //继续读spare区的4个地址内容,即第2052~2055地址,其中前2个字节为spare区的ECC值
//把读取到的spare区的ECC校验码放入NFSECCD的相应位置内
rNFSECCD=((secc&0xff00)<<8)|(secc&0xff);
NF_nFCE_H();
//判断所读取到的数据是否正确
if ((rNFESTAT0&0xf) == 0x0)
return 0x66; //正确
else
return 0x44; //错误
}
static U8 myNF_WritePage(U32 addr,unsigned char * from )
{
U32 i, mecc0, secc;
U8 stat, temp;
U8 ECCBuf[6];
int j;
temp = myNF_IsBadBlock(addr>>6); //判断该块是否为坏块
if(temp == 0x33)
return 0x42; //是坏块,返回
NF_RSTECC(); //复位ECC
NF_MECC_UnLock(); //解锁main区的ECC
NF_nFCE_L(); //打开nandflash片选
NF_CLEAR_RB(); //清RnB信号
NF_CMD(CMD_WRITE1); //页写命令周期1
//写入5个地址周期
NF_ADDR(0x00); //列地址A0~A7
NF_ADDR(0x00); //列地址A8~A11
NF_ADDR((addr) & 0xff); //行地址A12~A19
NF_ADDR((addr >> 8) & 0xff); //行地址A20~A27
NF_ADDR((addr >> 16) & 0xff); //行地址A28
//写入一页数据
for (i = 0; i < 2048; i++)
{
//Uart_Printf("Write %X \n\r",from[i]);
WrNFDat8((unsigned char)(from[i]));
}
NF_MECC_Lock(); //锁定main区的ECC值
mecc0=rNFMECC0; //读取main区的ECC校验码
//把ECC校验码由字型转换为字节型,并保存到变量数组ECCBuf中
ECCBuf[0]=(U8)(mecc0&0xff);
ECCBuf[1]=(U8)((mecc0>>8) & 0xff);
ECCBuf[2]=(U8)((mecc0>>16) & 0xff);
ECCBuf[3]=(U8)((mecc0>>24) & 0xff);
NF_SECC_UnLock(); //解锁spare区的ECC
//把main区的ECC值写入到spare区的前4个字节地址内,即第2048~2051地址
for(i=0;i<4;i++)
{
WrNFDat8(ECCBuf[i]);
}
NF_SECC_Lock(); //锁定spare区的ECC值
secc=rNFSECC; //读取spare区的ECC校验码
//把ECC校验码保存到全局变量数组ECCBuf中
ECCBuf[4]=(U8)(secc&0xff);
ECCBuf[5]=(U8)((secc>>8) & 0xff);
//把spare区的ECC值继续写入到spare区的第2052~2053地址内
for(i=4;i<6;i++)
{
WrNFDat8(ECCBuf[i]);
}
NF_CMD(CMD_WRITE2); //页写命令周期2
for(i = 0 ; i != 1000; ++i)
;
NF_CMD(CMD_STATUS); //读状态命令 0X70H
//判断状态值的第6位是否为1,即是否在忙,该语句的作用与NF_DETECT_RB();相同
do{
stat = NF_RDDATA8();
}while(!(stat&0x40)); /// stat == x04 is ready
NF_nFCE_H(); //关闭nandflash片选
//判断状态值的第0位是否为0,为0则写操作正确,否则错误
if (stat & 0x1)
{
temp = myNF_MarkBadBlock(addr>>6); //标注该页所在的块为坏块
if (temp == 0x21)
return 0x43; //标注坏块失败
else
return 0x44; //写操作失败
}
else
return 0x66; //写操作成功
}
//////////////
static U32 cpu_freq;
static U32 UPLL;
static void cal_cpu_bus_clk(void)
{
U32 val;
U8 m, p, s;
val = rMPLLCON;
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
//(m+8)*FIN*2 不要超出32位数!
FCLK = ((m+8)*(FIN/100)*2)/((p+2)*(1<<s))*100;
val = rCLKDIVN;
m = (val>>1)&3;
p = val&1;
val = rCAMDIVN;
s = val>>8;
switch (m) {
case 0:
HCLK = FCLK;
break;
case 1:
HCLK = FCLK>>1;
break;
case 2:
if(s&2)
HCLK = FCLK>>3;
else
HCLK = FCLK>>2;
break;
case 3:
if(s&1)
HCLK = FCLK/6;
else
HCLK = FCLK/3;
break;
}
if(p)
PCLK = HCLK>>1;
else
PCLK = HCLK;
if(s&0x10)
cpu_freq = HCLK;
else
cpu_freq = FCLK;
val = rUPLLCON;
m = (val>>12)&0xff;
p = (val>>4)&0x3f;
s = val&3;
UPLL = ((m+8)*FIN)/((p+2)*(1<<s));
UCLK = (rCLKDIVN&8)?(UPLL>>1):UPLL;
}
void Main(void)
{
U8 buff[2112];
int i ;
U8 ret;
U16 temp = 0 ;
U32 flashid;
char ID ;
ChangeClockDivider(3,1);///1:3:6
//ChangeMPllValue(127,2,1);///405MHz
ChangeMPllValue(60,4,1);///95.96MHz
cal_cpu_bus_clk();
// Port_Init();
//Isr_Init();
Uart_Init(0,115200);
Uart_Select(0);
Uart_Printf("the main is running \n\r");
myInitNand();
ID= myNF_ReadID();
Uart_Printf("the ID is %x \n\r",ID);
myNF_ReadPage(0,buff);
for( i = 0; i != 2048 ; ++i)
Uart_Printf("%X ",buff[i]);
Uart_Printf("\n\r\n\r\n\r");
myNF_EraseBlock(0);
//memset(buff,0,2048*sizeof(U8));
memset(buff,0xAA,2048*sizeof(U8));
for(i=0; i!= 1000; ++i);
ret = myNF_WritePage(0,buff);
if(0x66 == ret )
{
Uart_Printf("data write success %X \n\r",ret);
for( i = 0; i != 2048 ; ++i)
Uart_Printf("%X ",buff[i]);
Uart_Printf("\n\r\n\r\n\r");
}
else
{
Uart_Printf("data write error %X \n\r",ret);
}
for(i=0; i!= 1000; ++i);
myNF_ReadPage(0,buff);
for( i = 0; i != 2048 ; ++i)
Uart_Printf("%X ",buff[i]);
Uart_Printf("\n\r\n\r\n\r");
}
//////////////////////////////////////////////////////
整个数据中,关键是要看S3C2440A的芯片手册,和Nand Flash 的手册,我的Nand Flash是K9F2G08U0B ,整个Flash的
Commad Sets ,其实整个数据的读取,还是对其他设备编程一样,主要是开始对什么是Nand Flash根本不知道,还是以为NanaFlash 和RAM是不是差不多,但是到现在才感觉到,NandFlash就是一个i数据存储设备,而且块设备,是用来做为数据存储用的,只是S3C2440A里面集成了对应NandFlash的接口芯片,这样就可以直接就将这外部存储设备直接硬连接到总线上。
既然和一般的通过接口芯片控制的设备一样,那么控制方式也是一样,软件控制硬件,软件改变控制器中寄存器的数据,修改电平,控制硬件工作。
在看NandFlash的过程中,主要是Command Sets需要知道,然后就是NandFlash IC 的数据手册,确实自己看得太少这种介绍硬件IC的手册,根本不懂怎么去看,凭着学数字电路的记忆,靠着感觉来把整个数据手册给看了,而且还是英语,看起来的速度还是慢。
要点:
1,地址空间,我这是共有2048个block,每个block有64个page ,每页 是 2K+64BYTES ,2K是主数据存储区域,存储用户数据,而64所在的spare区域,前4Bytes和2bytes是用来存储MECC和SECC的,64-4-2=58 bytes的空间,其实是可以用来自定义使用的 ,在定义模块为坏块的时候,就可以在这个区域里,加上一个标志,标识其为坏块。
2,地址,在读取和写入数据的时候,因为存储空间的原因,所以地址需要29位,而这29是分5次来传输的,column值页内地址,而row值是页地址;还有就是,因为每页的地址空间是2K+64,所以地址长度是12位,而不是11位。
3,锁ECC和解锁ECC,其是要锁的目标是NandFlash控制中的ECC纠错码产生器,而不是NandFlash中的ECC存储区;锁,就是硬件计算ECC的功能关闭
4,往NandFlash写入数据前,需要将该block Erase ,然后在写入数据,否则,写零可以直接写入,但是想在对应的bit上面写1的话,就就是不可能的。