轉載:百爲STM32開發板教程之十二——NAND FLASH

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

百爲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的開頭兩頁

主要內容:
1、瞭解STM32 FSMC NAND控制器
2、瞭解K9F1208 NAND FLASH的原理與操做
3、編程實現擦除K9F1208 NAND FLASH的第一個塊,並讀寫K9F1208 NAND FLASH的開頭兩頁

1、STM32 FSMC NAND控制器
一、一、STM32 FSMC功能框圖:
FSMC主要包括有:AHB接口(包含FSMC配置寄存器) , NOR閃存和PSRAM控制器,NAND閃存和PC卡控制器,外部設備接口
 
二、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閃存存儲器自動地累加其內部的操做地址,讀寫數據時沒有必要變換數據區的地址,即沒必要對連續的地址區操做。

三、STM32的NAND控制信號
(1)STM32 FSMC NAND控制信號描述:
 

(2)STM32 FSMC信號鏈接K9F1208 NAND FLASH:
 

(3)百爲STM3210E-EVAL開發板上STM32和K9F1208的鏈接電路圖(這裏電路圖上畫的是NAND512,實際焊接的硬件是K9F1208):
 


2、K9F1208 NAND FLASH的原理與操做
一、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 */

二、K9F1208引腳定義
 

三、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);
}

3、編程實現擦除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);  
  }編程

相關文章
相關標籤/搜索