http://bbs.21ic.com/icview-586200-1-1.html

百为STM32开发板教程之十二——NAND FLASH

参考资料:
百为stm32开发板光盘V3\百为stm32开发板光盘\芯片数据手册\K9F1208.pdf
百为stm32开发板光盘\st官方参考资料\Application notes\AN2784 Using the high-density STM32F10xxx FSMC peripheral to drive external memories.pdf

实验目的:实现擦除NAND FLASH的第一个块,并读写NAND FLASH的开头两页

主要内容:
一、了解STM32 FSMC NAND控制器
二、了解K9F1208 NAND FLASH的原理与操作
三、编程实现擦除K9F1208 NAND FLASH的第一个块,并读写K9F1208 NAND FLASH的开头两页

一、STM32 FSMC NAND控制器
1、1、STM32 FSMC功能框图:
FSMC主要包括有:AHB接口(包含FSMC配置寄存器) , NOR闪存和PSRAM控制器,NAND闪存和PC卡控制器,外部设备接口
 
2、STM32 FSMC外部设备地址映射:
从FSMC的角度看,可以把外部存储器划分为固定大小为256M字节的四个存储块,见下图
● 存储块1用于访问最多4个NOR闪存或PSRAM存储设备。这个存储区又被划分为4个NOR/PSRAM区,并有4个专用的片选。 
● 存储块2和3用于访问NAND闪存设备,每个存储块连接一个NAND闪存。 
● 存储块4用于访问PC卡设备 
每一个存储块上的存储器类型是由用户在配置寄存器中定义的。

其中块2和块3属于NAND存储块,每个存储块又可以划分为通用和属性空间

通用和属性空间又可以在低256K字节部分划分为3个区
● 数据区(通用/属性空间的前64K字节区域) 
● 命令区(通用/属性空间的第2个64K字节区域) 
● 地址区(通用/属性空间的第2个128K字节区域)
 
应用软件使用这3个区访问NAND闪存存储器: 
● 发送命令到NAND闪存存储器:软件只需对命令区的任意一个地址写入命令即可。 
● 指定操作NAND闪存存储器的地址:软件只需对地址区的任意一个地址写入命令即可。因为一个NAND地址可以有4或5个字节(依实际的存储器容量而定),需要连续地执行对地址区的写才能输出完整的操作地址。 
● 读写数据:软件只需对数据区的任意一个地址写入或读出数据即可。 因为NAND闪存存储器自动地累加其内部的操作地址,读写数据时没有必要变换数据区的地址,即不必对连续的地址区操作。

3、STM32的NAND控制信号
(1)STM32 FSMC NAND控制信号描述:

(2)STM32 FSMC信号连接K9F1208 NAND FLASH:

(3)百为STM3210E-EVAL开发板上STM32和K9F1208的连接电路图(这里电路图上画的是NAND512,实际焊接的硬件是K9F1208):

二、K9F1208 NAND FLASH的原理与操作
1、K9F1208 的硬件结构组织
K9F1208是容量为512M bit,即64M byte的存储器,它是由4096个块(block)组成,其中每个块又是由32个页(page)组成,每个页由512byte+16byte组成。
K9F1208的读写都是以页为单位,而擦除则是以块为单位。

程序中相关定义:
/* FSMC NAND memory parameters */
#define NAND_PAGE_SIZE             ((u16)0x0200) /* 512 bytes per page w/o Spare Area */
#define NAND_BLOCK_SIZE            ((u16)0x0020) /* 32x512 bytes pages per block */
#define NAND_ZONE_SIZE             ((u16)0x0400) /* 1024 Block per zone */
#define NAND_SPARE_AREA_SIZE       ((u16)0x0010) /* last 16 bytes as spare area */
#define NAND_MAX_ZONE              ((u16)0x0004) /* 4 zones of 1024 block */

2、K9F1208引脚定义

3、K9F1208的操作命令集

(1)读ID命令:
 
先输出命令90H,再输出地址00H,然后读回4个字节的数据即是K9F1208的ID,ECH,76H,5AH,3FH
代码如下:
void FSMC_NAND_ReadID(NAND_IDTypeDef* NAND_ID)
{
  u32 data = 0;
  /* 发送命令到命令区0x70010000 */  
  *(vu8 *)(Bank_NAND_ADDR | CMD_AREA) = 0x90;
  *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = 0x00;
   /* 从K9F1208 NAND FLASH读回ID序列 */ 
   data = *(vu32 *)(Bank_NAND_ADDR | DATA_AREA);  //从数据区0x70000000读回数据
   NAND_ID->Maker_ID   = ADDR_1st_CYCLE (data);
   NAND_ID->Device_ID  = ADDR_2nd_CYCLE (data);
   NAND_ID->Third_ID   = ADDR_3rd_CYCLE (data);
   NAND_ID->Fourth_ID  = ADDR_4th_CYCLE (data);  
}

(2)块擦除命令
 
块擦除是先输出60H,再输出块地址,然后输出D0H,用70H读回状态,等待操作完成即可
u32 FSMC_NAND_EraseBlock(NAND_ADDRESS Address)
{
  *(vu8 *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_ERASE0;  //发送命令60H到命令区0x70010000
  *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_1st_CYCLE(ROW_ADDRESS);  //发送地址A9~A16到地址区0x70020000
  *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_2nd_CYCLE(ROW_ADDRESS); //发送地址A17~A24到地址区0x70020000
  *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_3rd_CYCLE(ROW_ADDRESS);  //发送地址A25到地址区0x70020000
   
  *(vu8 *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_ERASE1;  //发送命令D0H到命令区0x70010000 
  return (FSMC_NAND_GetStatus());  //读回操作结果
}

(4)页写入命令
因为每个页(page)可分为A,B,C三个区

所以页写入也分为三种方式:

具体时序:

我们这里采用的第一种方式,可以写入0~528byte的数据。K9F1208属于小页的NAND(512byte+16byte),区别于大页的NAND(2048byte+64byte)
u32 FSMC_NAND_WriteSmallPage(u8 *pBuffer, NAND_ADDRESS Address, u32 NumPageToWrite)
{
  u32 index = 0x00, numpagewritten = 0x00, addressstatus = NAND_VALID_ADDRESS;
  u32 status = NAND_READY, size = 0x00;
  while((NumPageToWrite != 0x00) && (addressstatus == NAND_VALID_ADDRESS) && (status == NAND_READY))
  {
    /* 页写命令和地址 */
    *(vu8 *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_AREA_A;  //发送命令00H到命令区0x70010000,从A区开始写入
    *(vu8 *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_WRITE0;  //发送命令80H到命令区0x70010000
    *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = 0x00;  //发送地址A0~A7到地址区0x70020000,从地址0开始写入  
    *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_1st_CYCLE(ROW_ADDRESS); //发送地址A9~A16到地址区0x70020000  
    *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_2nd_CYCLE(ROW_ADDRESS);  //发送地址A17~A24到地址区0x70020000  
    *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_3rd_CYCLE(ROW_ADDRESS);  //发送地址A25到地址区0x70020000
    /* 计算写入数据的大小 */
    size = NAND_PAGE_SIZE + (NAND_PAGE_SIZE * numpagewritten);
    /* 写入数据 */
    for(; index < size; index++)
    {
      *(vu8 *)(Bank_NAND_ADDR | DATA_AREA) = pBuffer[index];  //发送数据到数据区0x70000000
    }
    
    /* 检查状态看是否操作成功 */
    status = FSMC_NAND_GetStatus();
    
    if(status == NAND_READY)  //如果操作完成
    {
      numpagewritten++;  //已写入页数加1
      NumPageToWrite--;  //待写入页数减1
      /* 计算要写入的下一个小页的地址 */
      addressstatus = FSMC_NAND_AddressIncrement(&Address);    
    }    
  }
  
  return (status | addressstatus);
}

(5)read 1页读命令
 
页读命令是先输出00H,再输出要读入的页地址,然后就可以读回最多528byte的数据了。
u32 FSMC_NAND_ReadSmallPage(u8 *pBuffer, NAND_ADDRESS Address, u32 NumPageToRead)
{
  u32 index = 0x00, numpageread = 0x00, addressstatus = NAND_VALID_ADDRESS;
  u32 status = NAND_READY, size = 0x00;
  while((NumPageToRead != 0x0) && (addressstatus == NAND_VALID_ADDRESS))
  {    
    /* 页读命令和页地址*/
    *(vu8 *)(Bank_NAND_ADDR | CMD_AREA) = NAND_CMD_AREA_A;   //发送命令00H到命令区0x70010000
   
    *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = 0x00;  ////发送地址A0~A7(00H)到地址区0x70020000
    *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_1st_CYCLE(ROW_ADDRESS); //发送地址A9~A16到地址区0x70020000 
    *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_2nd_CYCLE(ROW_ADDRESS);  //发送地址A17~A24到地址区0x70020000 
    *(vu8 *)(Bank_NAND_ADDR | ADDR_AREA) = ADDR_3rd_CYCLE(ROW_ADDRESS);   //发送地址A25到地址区0x70020000
     
    /* 计算要读的数据大小 */
    size = NAND_PAGE_SIZE + (NAND_PAGE_SIZE * numpageread);
    
    /* 读数据到pBuffer */    
    for(; index < size; index++)
    {
      pBuffer[index]= *(vu8 *)(Bank_NAND_ADDR | DATA_AREA);  //从数据区0x70000000读回数据
    }
    numpageread++;  //已读的页数加1
    
    NumPageToRead--;  //待读的页数减1
    /* 计算下一个要读的页地址 */               
    addressstatus = FSMC_NAND_AddressIncrement(&Address);
  }
  /* 检查状态看是否操作成功 */
  status = FSMC_NAND_GetStatus();
  
  return (status | addressstatus);
}

三、编程实现擦除K9F1208 NAND FLASH的第一个块,并读写K9F1208 NAND FLASH的开头两页

/* main.c */

/* FSMC NAND初始化 */
  FSMC_NAND_Init();
  /* 读NAND ID操作 */
  FSMC_NAND_ReadID(&NAND_ID);
  /* 检查ID是否正确 */
  if((NAND_ID.Maker_ID == NAND_K9F1208_MakerID) && (NAND_ID.Device_ID == NAND_K9F1208_DeviceID))
  {
    /* 初始化要写入NAND的页地址 */ 
    WriteReadAddr.Zone = 0x00;
    WriteReadAddr.Block = 0x00;
    WriteReadAddr.Page = 0x00; 
    /* 擦除NAND FLASH的第一个块(第1和第2页所在的块) */
    status = FSMC_NAND_EraseBlock(WriteReadAddr);
    /* 写数据到NAND FLASH的第1和第2页 */
    /* 填充要发送的数据到buffer */
    Fill_Buffer(TxBuffer, BUFFER_SIZE , 0x66);
    status = FSMC_NAND_WriteSmallPage(TxBuffer, WriteReadAddr, PageNumber);  //PageNumber=2,表示要写入第1和第2页
    /* 从NAND FLASH读回数据 */
    status = FSMC_NAND_ReadSmallPage (RxBuffer, WriteReadAddr, PageNumber);
   
    /* 比较写入的数据和读回的数据是否相等 */
    for(j = 0; j < BUFFER_SIZE; j++)
    {
      if(TxBuffer[j] != RxBuffer[j])
      {     
        WriteReadStatus++;
      } 
    }
    if (WriteReadStatus == 0)
    { 
      /* 如果相等,则点亮LED1 */
      GPIO_SetBits(GPIOF, GPIO_Pin_6);
    }
    else
    { 
      /* 否则,点亮LED2 */
      GPIO_SetBits(GPIOF, GPIO_Pin_7);     
    }
  }
  else //ID不正确
  {
    /* 点亮LED3 */
    GPIO_SetBits(GPIOF, GPIO_Pin_8);  
  }

转载:百为STM32开发板教程之十二——NAND FLASH的更多相关文章

  1. 转载:百为STM32开发板教程之十一——NOR FLASH

    转载:http://bbs.21ic.com/icview-586199-1-1.html 百为STM32开发板教程之十一——NOR FLASH 参考文档:百为stm32开发板光盘\st官方参考资料\ ...

  2. 复习完毕STM32开发板

        经过半个晚上的折腾,终于复习了STM32开发板,并使用ST官方库调试完毕一个printf重定向到串口的程序,3.5的库同以前的库不大一样,不过最终搞好了可以睡觉了,还可以睡7个小时.     ...

  3. Senparc.Weixin.MP SDK 微信公众平台开发教程(十二):OAuth2.0说明

    紧接上一篇<Senparc.Weixin.MP SDK 微信公众平台开发教程(十一):高级接口说明>,这里专讲OAuth2.0. 理解OAuth2.0 首先我们通过一张图片来了解一下OAu ...

  4. iOS 11开发教程(十二)iOS11应用视图始祖——UIView

    iOS 11开发教程(十二)iOS11应用视图始祖——UIView 在Swift中,NSObject是所有类的根类.同样在UIKit框架(UIKit框架为iOS应用程序提供界面对象和控制器)中,也存在 ...

  5. SNF开发平台WinForm之十二-发送手机短信功能调用-金笛-SNF快速开发平台3.3-Spring.Net.Framework

    1.调用前组装参数 2.调用发送信息服务脚本   .调用前组装参数: BaseSendTaskEntity entity = new BaseSendTaskEntity(); entity.Mess ...

  6. 【OpenCV新手教程之十二】OpenCV边缘检測:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

    本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog.csdn.net/poem_qianmo/article/details/25560901 作者:毛星云(浅墨) ...

  7. [OpenCV入门教程之十二】OpenCV边缘检测:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

    http://blog.csdn.net/poem_qianmo/article/details/25560901 本系列文章由@浅墨_毛星云 出品,转载请注明出处. 文章链接:http://blog ...

  8. 【转】抓包工具Fiddler的使用教程(十二)下:Fiddler抓取HTTPS

    在教程十二(上),我们也了解了HTTPS协议,该教程就和大家分享Fiddler如何抓取HTTPS 抓包工具Fiddler的使用教程(十二):[转载]HTTPS协议 再次回忆一下关键内容: iddler ...

  9. struts2官方 中文教程 系列十二:控制标签

    介绍 struts2有一些控制语句的标签,本教程中我们将讨论如何使用 if 和iterator 标签.更多的控制标签可以参见 tags reference. 到此我们新建一个struts2 web 项 ...

随机推荐

  1. java web 项目常用框架

    java框架实在是太多了,网上一搜索一大箩筐,根本就了解不到什么. 我还是以我的经验来说一下j2ee的框架. 1.首先力推struts2框架,这是最经典的框架(可以说没有“之一”).可以帮你快速搭建出 ...

  2. Codevs 二叉树遍历问题 合集

    2010 求后序遍历 时间限制: 1 s 空间限制: 64000 KB 题目等级 : 白银 Silver   题目描述 Description 输入一棵二叉树的先序和中序遍历序列,输出其后序遍历序列. ...

  3. 积累js中的一些问题及解决方案

    一.取字符串的第i位不兼容的问题 1.问题:对于字符串str来说,要获取第i位,常见的是str[i],但是在低版本的浏览器中不兼容,例如ie7. 2.解决:使用str.charAt(i); 二.使用定 ...

  4. java基础 4 继承(1)抽象类与接口的区别

    抽象类: 是用来捕捉子类的通用特性的,至少包含一个抽象方法,该抽象方法必须在子类中实现,由于抽象类没有抽象方法的具体实现,因此不能对抽象类进行实例化. 接口: 定义了一组方法,是抽象方法的集合,但是接 ...

  5. Java数组操作方法收集(快速判断某个值在这个数组中)

    Java数组操作最高效的方式是循环取值,如果转换成集合那么就会分配内存,效率不如前者,但是方法多,需要在性能调优上去权衡.切记:数组是数组,集合是集合. 下面是收集最常用的数组转成集合的操作方法: i ...

  6. JAVA原始的导出excel文件,快捷通用 方便 还能够导出word文档哦

    如今导出excel基本上都是用poi了,当报表格式非常负责的时候 开发难度会加大 假设报表有格式有变化 那就更复杂了,先发现一个非常老的技术.能够解决格式复杂的报表. 实例代码例如以下: <%@ ...

  7. 【APUE】进程间通信之管道

    管道是UNIX系统IPC最古老形式,并且所有UNIX系统都提供此种通信机制.管道由下面两种局限性: 1)历史上,它们是半双工的(即数据只能在一个方向上流动) 2)它们只能在具有公共祖先的进程之间使用. ...

  8. hdu 1068 Girls and Boys(匈牙利算法求最大独立集)

    Girls and Boys Time Limit: 20000/10000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) ...

  9. Download Software Top 10

    We are quite rich in terms of web browsers! Nevertheless, it's a bit surprising to know that even so ...

  10. HTTP协议漫谈 C#实现图(Graph) C#实现二叉查找树 浅谈进程同步和互斥的概念 C#实现平衡多路查找树(B树)

    HTTP协议漫谈   简介 园子里已经有不少介绍HTTP的的好文章.对HTTP的一些细节介绍的比较好,所以本篇文章不会对HTTP的细节进行深究,而是从够高和更结构化的角度将HTTP协议的元素进行分类讲 ...