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);
}編程