zoukankan      html  css  js  c++  java
  • 转载:百为STM32开发板教程之十二——NAND FLASH

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

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

    参考资料:
    百为stm32开发板光盘V3百为stm32开发板光盘芯片数据手册K9F1208.pdf
    百为stm32开发板光盘st官方参考资料Application notesAN2784 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);  
      }

  • 相关阅读:
    Mysql Error Code: 1175. You are using safe update mode and you tried to update a table without a WHERE that uses a KEY column To disable safe mode
    vs2012+ winform+.net4.0发布如何在xp上运行
    ubuntu下手动配置apache2.4.12
    mysql连接错误解决(ERROR 2049 (HY000): Connection using old (pre-4.1.1) authentication protocol ref used (client option 'secure_auth' enabled))
    位运算取绝对值
    位运算两数交换
    java mysql prepareStatement模糊查询like使用注意
    idea14远程调试linux下的tomcat
    web视频播放插件:Video For Everybody
    cmd杀死进程
  • 原文地址:https://www.cnblogs.com/wwjdwy/p/4664373.html
Copyright © 2011-2022 走看看