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)相关推荐

  1. 慢慢欣赏linux之串口驱动代码分析 - 基于powerpc 2.6.x版本

    串口驱动分两阶段初始化 第一阶段  串口驱动没有初始化前的准备工作,包括设备树的解析,platform设备注册 asmlinkage void __init start_kernel(void)    ...

  2. 20150311 NandFlash驱动分析

    20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备 ...

  3. linux内核usb驱动框架,基于S3C2440平台的linux2.6.22内核版本的USB驱动框架分析

    基于S3C2440平台的linux2.6.22内核版本的USB驱动框架分析 发布时间:2014-07-18 16:47:31来源:红联作者:linux08071151 driver/usb/host/ ...

  4. 基于S3C2410的触摸屏驱动程序设计

    基于S3C2410的触摸屏驱动程序设计   作者:沈阳农业大学 关键词:  ADS7843  S3C2410  触摸屏  嵌入式Linux  消费电子  触摸屏  消费电子 摘要: 本文介绍了基于三星 ...

  5. ECG分析:基于深度学习的ECG心律失常分类入门(3)

    ECG分析:基于深度学习的ECG心律失常分类入门(3) 数据库的Python读取 本次读取数据,用的是一款专门读取MITAB数据的工具--WFDB-python,WFDB包下载 ,全称是 Python ...

  6. ECG分析:基于深度学习的ECG心律失常分类入门(1)

    ECG分析:基于深度学习的ECG心律失常分类入门(1) 写作动机 由于受突发疫情的影响,开学时间推迟了(在此特向奋斗在前线的各行各业的工作者们致以崇高的敬意!).前天晚上刚好看到一篇新出的论文,跟自己 ...

  7. ECG分析:基于深度学习的ECG心律失常分类入门(4)

    ECG分析:基于深度学习的ECG心律失常分类入门(4) 在搭建模型之前,讲一下本次任务需要区分的类别,MITAB根据心拍类型划分了14个小类: 也可以用wfdb查看: wfdb.show_ann_la ...

  8. ECG分析:基于深度学习的ECG心律失常分类入门(5)

    ECG分析:基于深度学习的ECG心律失常分类入门(5) 数据和模型完成了之后,就是训练和测试了,这里顺带提一下,MITAB的数据是48条记录的,而我们在做ECG分析的时候,都是去掉了四条记录(102, ...

  9. ECG分析:基于深度学习的ECG心律失常分类入门(2)

    ECG分析:基于深度学习的ECG心律失常分类入门(2) 数据来源:MIT-BIH Arrhythmia Database 数据库介绍和获取 前面已经对ECG信号有了简单的认识,那么现在来简单看看我们的 ...

最新文章

  1. SQL数据库无法附加 系统表损坏修复 数据库中病毒解密恢复
  2. 混合app用百分比还是rem_一次搞懂前端所有CSS长度单位,px、em、rem、rpx、%....
  3. 杀毒软件对Platform Builder编译的影响
  4. iview图表_iview踩坑
  5. RTOS原理与实现09:事件标志组实现
  6. java---同步与并发概念
  7. 简单脚本之显示系统当前的一些信息
  8. numpy—np.tensordot
  9. 用户id oracle设置,Oracle中的架构,用户和功能ID
  10. DB9串口定义及含义
  11. 2021年安徽无为中学高考成绩查询,安徽省无为中学2021届高三年级这些学生,被表彰了...
  12. adobe dreamweaver cs6 css,Adobe Dreamweaver CS6
  13. pytest 接口自动化 从百草园到三味书屋...
  14. 揭密 HAP 激光雷达的实际性能表现
  15. Linux 安装 .7z 解压和压缩文件
  16. 面向对象三大特性(多态)
  17. 为什么mysql的delete操作不释放磁盘空间
  18. 大飞品树莓——自定制树莓派4B系统安装
  19. Kruskal算法和并查集
  20. MySql系列06:数据库对象

热门文章

  1. tigervnc远程控制linux,CentOS 6.8 安装TigerVNC 实现 Linux 远程桌面(示例代码)
  2. tushare调用API获取金融相关数据
  3. Mac selenium调试已经存在的Chrome浏览器
  4. html页面手机端console,vue项目以及独立HTML项目在手机端查看控制台日志 vconsole
  5. python开发个人博客_[Web开发] Flask+Python 开发个人博客(一)
  6. SharePoint中添加或者修改Item时调用EventReceiver(Event Handler)处理额外的逻辑
  7. springboot错误: 找不到或无法加载主类
  8. 中小企业 DevOps 从 0 到 1
  9. (一)问候 Log4j 你好
  10. 富文本编辑器 CKeditor 配置使用