NAND Flash在嵌入式系统中的地位与PC机上的硬盘类似,用于保存系统运行所必需的操作系统、应用程序、用户数据、运行过程中产生的各类数据。与内存掉电后数据丢失不同,NAND Flash中的数据在掉电后仍可永久保存。
操作NAND Flash时,先传输命令,然后传输地址,最后读/写数据,期间要检查Flash的状态。对于K9F5608U0D,它的容量为32MB,需要25位地址。发出命令后,后面要紧跟3个地址序列。比如读Flash时,发出读命令和3个地址序列后,后续的读操作就可以得到这个地址及其后续地址的数据。相应的命令字和地址序列如表1和2所示:
表1:K9F5608U0D命令设置表
表2:K9F5608U0D寻址周期表
K9F5608U0D一页的大小为512字节,分为两部分:前半页,后半页。由于列地址只有8根数据线,所以寻址宽度只有256个字节。而在这512个字节的一页中,当发出读命令为00h时,表示列地址将在前半部分寻址,命令为01h时,表示列地址将在后半部分寻址。A8被读命令00h设置为低电平,而在01时设置成高电平。
S3C2410对NAND Flash操作提供了几个寄存器来简化对NAND Flash的操作。比如要发出读命令时,只需要往NFCMD寄存器中写入0即可,而控制器会自动发出各种控制信号进行操作。以下几个寄存器,为2410专为NAND而设计的:
NFCONF:NAND Flash配置寄存器。
被用来使能/禁止NAND Flash控制器、使能/禁止控制引脚信号nFCE、初始化ECC、设置NAND Flash的时序参数等。
TACLS、TWRPH0、TWRPH1这3个参数控制的是NAND Flash信号线CLE/ALE与写控制信号n/WE的时序关系,如图1所示:
图1:S3C2410 NAND控制时序图
NFCMD:NAND Flash命令寄存器。
对于不同型号的Flash,操作命令一般不一样。
NFADDR:NAND Flash地址寄存器。
当写这个寄存器时,它将对Flash发出地址信号。
NFDATA:NAND Flash数据寄存器。
只用到低8位,读,写此寄存器将启动对NAND Flash的读数据、写数据操作。
NFSTAT:NAND Flash状态寄存器。
只用到位0,0:busy,1:ready。
在使用NAND Flash之前,需要先对NAND进行初始化:
/************************************************************************
* 名称: init_nandconf
* 功能: 初始化2410内部nandflash控制寄存器
* 返回: 无
************************************************************************/
void init_nandcof(void)
{
rNFCONF=0xf820;//设置NANDFLASH,各位定义如表3所示:
表3:NFCONF寄存器
[15]:设置为1,NANDFLASH 控制器开,由datasheet得到,在自动启动后,nandflash控制器会关闭,如果要使用控制器,就要手工开。
[12]:初始化ECC寄存器,设置为1
[11]:外部NAND使能,这里设置为1,先关一下。
[10:8]:TACLS设置,由表4得,ALE和CLE要求保持10ns,而现在的HCLK为100m,所以TACLS为0就可以了。
表4:AC TIMING CHARACTERISTICS FOR COMMAND / ADDRESS / DATA INPUT
[6:4]:由表4得,twp在3.3V时需要至少25ns的时间,现在在HCLK为100M的情况下,需要3个周期,所以TWRPH0为2,
[2:0]:由表4得到,tch要求时间为10ns,所以在HCLK为100M的情况下,只要1个周期就行了,所以TWRPH1为0
rNFCONF &= ~0x800;//NAND使能。CE低电平有效
rNFCMD=0xff; //重启一下NAND,由表5所示,得到复位的命令
表5:NANDFLASH命令表
while(!(rNFSTAT&0x1));//等待复位完成,由表6得,NFSTAT寄存器定义
表6:NFSTAT寄存器
}
对于NANDFLASH操作来说,一般有4种:1、读NAND ID,2、读NAND内容,3、写NANDFLASH,4、擦除NANDFLASH。以下分4个函数说明NANDFLASH的4种操作:
1、读NANDFLASH的ID
/************************************************************************
* 名称: nand_read_id
* 功能: 读取nandflash的ID
* 输入: 无
* 返回: id
************************************************************************/
static int nand_read_id(void)
{
int i,id;//i用于记录当前的,id用于记录读取的ID
/* NAND使能 */
rNFCONF &= ~0x800;使能//NANDFLASH
for(i=0; i<10; i++);//等待10个周期,等待Nand准备就绪
图2:NAND读时序图
以下部分按照图2时序图而写
rNFCMD=0x90;
for(i=0;i<5;i++);//由于FCLK为200MHz,这里5个周期为25ns
查器件手册得tAR最小需要10ns,最大没有上限
id=rNFDATA;//读出NAND的制做商编号:这里三星为0xEC
id=(id<<8)+rNFDATA
//关芯片使能,防止误操作对NAND中的数据修改
rNFCONF |= 0x800;
return id;//返回读取的ID
}
2、读NAND内容
/************************************************************************
* 名称: nand_read
* 功能: 读取nandflash上一块内容到指定的地址中
* 输入: unsigned char *buf: 要写入数据的首地址
unsigned long start_addr: 要读取的数据在Nand上的首地址
int size: 读取长度
* 返回: 0
************************************************************************/
static int nand_read(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;//i用于记录当前的,j记录每一页中的byte地址
/* NAND使能 */
rNFCONF &= ~0x800;//使能NANDFLASH
for(i=0; i<10; i++);//等待10个周期
图3:NAND读时序图
以下部分按照图3时序图而写
for(i=start_addr; i < (start_addr + size);)
{
/* READ0 */
rNFCMD = 0;//由表3-4-5所示,读数据区的命令为0x0或者0x1,而对于512bytes来说,0x0是从第0个字节开始读起,而0x1是从第256个字节读起。当使用NFCMD寄存器时,控制NAND的ALE会置0,CLE会置1,数据写入时,WE也会由低变高,而当WE由低变高的过程后,命令将锁存在了NAND中的命令寄存器中,而这些都是自动的
/* Write Address */
//nand的写入方法见时序图,由于向2410的NAND的NFADDR寄存器写数据,此时ALE至1,CLE至0,数据写入时,WE也会由低变高,而当WE由低变高的过程后,地址数据将锁存在了NAND中的地址寄存器中,这些全是自动的,而又因为32M的NAND只需要3个周期寻址,所以这里只向地址寄存器发3个周期的命令就可以了
rNFADDR = i & 0xff;
rNFADDR = (i >> 9) & 0xff;//(左移9位,不是8位)
rNFADDR = (i >> 17) & 0xff; //(左移17位,不是16位)
表3-4-2列出了在地址操作的3个步骤对应的NAND内部地址线,没有A8(它由读命令设置,当读命令为0时,A8=0;当读命令为1时,A8=1),所以在第二,第三次向rNFADDR寄存器发送地址时,需要再多移一位,而不是原来的8和16。
wait_idle();//由时序图得到,当输入地址完成后,NANDFLASH会进入忙状态,我个人认为是NAND内部对输入的地址进行解释。
for(j=0; j < 512; j++, i++)
{
*buf = (rNFDATA & 0xff);//读取NAND中的数据,将数据写入到指定的数据写入位置,当使用NFDATA寄存器时,控制NAND的CLE和ALE都会自动1。而buf指针是外面传进来的地址,在下面的函数调用会用到,每读取一次NFDATA寄存器,控制NAND的OE都会由高电平到低电平转变。而NANDFLASH的数据每读取一次,都会指向下一个内部的地址,而一个数据区由512bytes组成,当继续读下去的时候,将读到16bytes的ECC区,所以每对完512个字节后,要对nand的地址重新定位。
buf++;//写入地址位置+1
}
}
//关芯片使能,防止误操作对NAND中的数据修改
rNFCONF |= 0x800;
return 0;//返回0,表示读取成功
}
3、写NAND内容
/************************************************************************
* 名称: nand_write
* 功能: 将内存中的一块内容写到nandflash上
* 输入: unsigned char *buf: 要读取数据的首地址
unsigned long start_addr: 要写入的数据在Nand上的首地址
int size: 写入长度
* 返回: 写入状态
************************************************************************/
static int nand_write(unsigned char *buf, unsigned long start_addr, int size)
{
int i, j;//i用于记录当前的,j记录每一页中的byte地址
/* NAND使能 */
rNFCONF &= ~0x800;使能//NANDFLASH
for(i=0; i<10; i++);//等待10个周期
图4:NAND编程时序图
以下部分按照图4时序图而写
for(i=start_addr; i < (start_addr + size);)
{
/* READ0 */
rNFCMD = 0;//由图3-4-5所示,写数据区前先确认写入哪个区,命令为0x0或者0x1。而对于512bytes来说,0x0是从第0个字节开始读起,而0x1是从第256个字节读起。当使用NFCMD寄存器时,控制NAND的ALE会置0,CLE会置1,数据写入时,WE也会由低变高,而当WE由低变高的过程后,命令将锁存在了NAND中的命令寄存器中,而这些都是自动的。
图5:NAND写入位置图
/* Write Address */
//nand的写入方法见时序图,由于向2410的NAND的NFADDR寄存器写数据,此时ALE至1,CLE至0,数据写入时,WE也会由低变高,而当WE由低变高的过程后,地址数据将锁存在了NAND中的地址寄存器中,这些全是自动的,而又因为32M的NAND只需要3个周期寻址,所以这里只向地址寄存器发3个周期的命令就可以了。
rNFADDR = i & 0xff;
rNFADDR = (i >> 9) & 0xff;//(左移9位,不是8位)
rNFADDR = (i >> 17) & 0xff; //(左移17位,不是16位)
表3-4-2列出了在地址操作的3个步骤对应的NAND内部地址线,没有A8(它由读命令设置,当读命令为0时,A8=0;当读命令为1时,A8=1),所以在第二,第三次向rNFADDR寄存器发送地址时,需要再多移一位,而不是原来的8和16。
for(j=0; j < 512; j++, i++)
{
rNFDATA =(*buf )& 0xff;//向NAND写入数据,将数据写入到指定的数据写入位置,当使用NFDATA寄存器时,控制NAND的CLE和ALE都会自动1。而buf指针是外面传进来的地址,在下面的函数调用会用到,每写一次NFDATA寄存器,控制NAND的WE都会由低电平向高电平转变。而NANDFLASH的数据每写一次,都会指向下一个内部的地址,而一个数据区由512bytes组成,当继续写下去的时候,将写到16bytes的ECC区,所以每对完512个字节后,要对nand的地址重新定位。
buf++;//写入地址位置+1
}
rNFCMD(0x10);//写入确认命令,由表3-4-5所得,在512个字节写完后,需要确认。这种设计可以防止误操作而影响了内部数据区
wait_idle();//由时序图得到,等待512字节输入完成。
rNFCMD(0x70);//读取NAND状态寄存器的内容
if(rNFDATA&0x1==0x1)
return 1;//读取状态寄存器,判读写入是否成功,由图3-4-4所得,读出的数据0位为1时,写入失败。
}
//关芯片使能,防止误操作对NAND中的数据修改
rNFCONF |= 0x800;
return 0;//返回0,表示写入成功
}
4、擦NAND内容
/************************************************************************
* 名称: nand_erase
* 功能: 擦除nandflash上一块内容到指定
* 输入: unsigned long start_addr: 要擦除的数据在Nand上的首地址
int size: 擦除长度
* 返回: 0
************************************************************************/
static int nand_ erase (unsigned long start_addr, int size)
{
int i, j;//i用于记录当前的,j记录每一页中的byte地址
/* NAND使能 */
rNFCONF &= ~0x800;使能//NANDFLASH
for(i=0; i<10; i++);//等待10个周期
图6:NAND擦除时序图
以下部分按照图6时序图而写
for(i=start_addr; i < (start_addr + size);)
{
rNFCMD=0x60;//擦除命令
rNFADDR = (i >> 9) & 0xff;//(左移9位,不是8位)
rNFADDR = (i >> 17) & 0xff; //(左移17位,不是16位)
NFCMD=0xD0;//确认擦除命令
i+=512*32;//擦除一块的长度为512字节的页,此种页32页组成一块
wait_idle();//由时序图得到,等待1块内容被擦除完成。
rNFCMD(0x70);//读取NAND状态寄存器的内容
if(rNFDATA&0x1==0x1)
returen 1;//如果失败,则返回1
}
//关芯片使能,防止误操作对NAND中的数据修改
rNFCONF |= 0x800;
return 0;//返回0,表示擦除成功
}
以上4次情况可以基本完成对NAND的操作,但是这里要注意的是,在linux写入和读出时,必须是出NAND中的每一页的第0个字节读取,而不能随便从其中间的位置读取,而这一部分是MTD中有明确的方法,这里不作进一步分析了。
Nandflash 驱动深度分析(基于S3C2410)相关推荐
- 慢慢欣赏linux之串口驱动代码分析 - 基于powerpc 2.6.x版本
串口驱动分两阶段初始化 第一阶段 串口驱动没有初始化前的准备工作,包括设备树的解析,platform设备注册 asmlinkage void __init start_kernel(void) ...
- 20150311 NandFlash驱动分析
20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备 ...
- linux内核usb驱动框架,基于S3C2440平台的linux2.6.22内核版本的USB驱动框架分析
基于S3C2440平台的linux2.6.22内核版本的USB驱动框架分析 发布时间:2014-07-18 16:47:31来源:红联作者:linux08071151 driver/usb/host/ ...
- 基于S3C2410的触摸屏驱动程序设计
基于S3C2410的触摸屏驱动程序设计 作者:沈阳农业大学 关键词: ADS7843 S3C2410 触摸屏 嵌入式Linux 消费电子 触摸屏 消费电子 摘要: 本文介绍了基于三星 ...
- ECG分析:基于深度学习的ECG心律失常分类入门(3)
ECG分析:基于深度学习的ECG心律失常分类入门(3) 数据库的Python读取 本次读取数据,用的是一款专门读取MITAB数据的工具--WFDB-python,WFDB包下载 ,全称是 Python ...
- ECG分析:基于深度学习的ECG心律失常分类入门(1)
ECG分析:基于深度学习的ECG心律失常分类入门(1) 写作动机 由于受突发疫情的影响,开学时间推迟了(在此特向奋斗在前线的各行各业的工作者们致以崇高的敬意!).前天晚上刚好看到一篇新出的论文,跟自己 ...
- ECG分析:基于深度学习的ECG心律失常分类入门(4)
ECG分析:基于深度学习的ECG心律失常分类入门(4) 在搭建模型之前,讲一下本次任务需要区分的类别,MITAB根据心拍类型划分了14个小类: 也可以用wfdb查看: wfdb.show_ann_la ...
- ECG分析:基于深度学习的ECG心律失常分类入门(5)
ECG分析:基于深度学习的ECG心律失常分类入门(5) 数据和模型完成了之后,就是训练和测试了,这里顺带提一下,MITAB的数据是48条记录的,而我们在做ECG分析的时候,都是去掉了四条记录(102, ...
- ECG分析:基于深度学习的ECG心律失常分类入门(2)
ECG分析:基于深度学习的ECG心律失常分类入门(2) 数据来源:MIT-BIH Arrhythmia Database 数据库介绍和获取 前面已经对ECG信号有了简单的认识,那么现在来简单看看我们的 ...
最新文章
- SQL数据库无法附加 系统表损坏修复 数据库中病毒解密恢复
- 混合app用百分比还是rem_一次搞懂前端所有CSS长度单位,px、em、rem、rpx、%....
- 杀毒软件对Platform Builder编译的影响
- iview图表_iview踩坑
- RTOS原理与实现09:事件标志组实现
- java---同步与并发概念
- 简单脚本之显示系统当前的一些信息
- numpy—np.tensordot
- 用户id oracle设置,Oracle中的架构,用户和功能ID
- DB9串口定义及含义
- 2021年安徽无为中学高考成绩查询,安徽省无为中学2021届高三年级这些学生,被表彰了...
- adobe dreamweaver cs6 css,Adobe Dreamweaver CS6
- pytest 接口自动化 从百草园到三味书屋...
- 揭密 HAP 激光雷达的实际性能表现
- Linux 安装 .7z 解压和压缩文件
- 面向对象三大特性(多态)
- 为什么mysql的delete操作不释放磁盘空间
- 大飞品树莓——自定制树莓派4B系统安装
- Kruskal算法和并查集
- MySql系列06:数据库对象
热门文章
- tigervnc远程控制linux,CentOS 6.8 安装TigerVNC 实现 Linux 远程桌面(示例代码)
- tushare调用API获取金融相关数据
- Mac selenium调试已经存在的Chrome浏览器
- html页面手机端console,vue项目以及独立HTML项目在手机端查看控制台日志 vconsole
- python开发个人博客_[Web开发] Flask+Python 开发个人博客(一)
- SharePoint中添加或者修改Item时调用EventReceiver(Event Handler)处理额外的逻辑
- springboot错误: 找不到或无法加载主类
- 中小企业 DevOps 从 0 到 1
- (一)问候 Log4j 你好
- 富文本编辑器 CKeditor 配置使用