NandFlash
Nand型号和命名
例如K9F2G08這個NandFlash,K9F表示三星公司的NandFlash,2G表示2Gbit,08表示該Nand有8位資料位。
Nand資料位
Nand的資料位有8位和16位之分,軟體應該根據實際采用的Nand來進行設計和編寫。Nand采用并行接口存儲,資料位上傳遞的不一定是純資料,也可能是指令,位址等。
Nand功能框圖
框圖如下:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICOwUTO0MDNxITMwATM2EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
Nand可以了解為一個3D矩陣存儲器,每一塊可以存儲一個資料位,進而組合成整個Nand
Nand結構
Nand中可以被通路的最小單元是Page,相當于磁儲存設備中的扇區的概念,對Nand進行一次讀寫,最少要讀寫一個Page或者Page的整數倍,由圖可知,在K9F2G08中一個Page為2K + 64B,是以Nand是典型的塊裝置
一個Nand晶片的Block是不确定的,一個Block的Page也是不确定的,甚至每個Page的大小也不确定,是以Nand的組織架構比較不一緻。
多個Page組成一個Block,在K9F2G08中一個Block為64個Page,Page是讀寫Nand的最小的機關,Block是擦除Nand的最小機關,Nand中主要包含存儲顆粒和接口電路,接口電路用于管理存儲顆粒,并向外提供通路Nand的統一接口,是一種公用的Nand标準接口。Nand有多個存儲單元,每個單元精确到位元組,但是實際讀寫隻能按照Page,是以很多操作都要求給的位址是頁對齊的,是2K的整數倍,位址傳遞通過IO資料線來發送的,按照要求的時序和順序依次寫入,由于位址有30位,但是資料位卻沒有那麼多,是以需要好幾個周期才能把位址發送完畢,一般都需要4-8個周期,是以Nand也有4Cycle,5Cycle之分。
帶内資料和帶外資料
Nand的每個頁由兩部分組成,各自都有一定的存儲空間。例如K9F2G08就是2K+64B,2K屬于帶内資料,是真正的存儲空間,Nand中的有效資料都是存儲在這裡的,Nand的容量也隻考慮這個2K,64B作為附加用途,不能用來有效資料,可以用來存儲ECC,壞塊标志等資料
Nand指令碼
外部通過Nand接口發送位址,指令,資料到Nand,Nand有多個指令,指令也分有多個周期,對Nand進行的所有操作都要有指令,位址,資料的參與,按照規定的流程進行動作,才能完成,
Nand常見操作
壞塊檢查
Flash使用之前要統一擦除,擦除機關是Block,擦除之後全是1(0xFF),是以壞塊的檢查思路如下:
- 先擦除該塊
- 讀取該塊的值,判斷各個位元組是否是0xFF,如果是就不是壞塊,如果不是,則該塊就是一個壞塊,
頁寫操作
寫之前確定該Page是擦除幹淨的,否則,會導緻寫入的資料是錯誤的:
- 通過資料引腳将指令,位址,資料寫入
- 讀取狀态寄存器,判斷狀态是否寫入成功
- ECC校驗
擦除操作
擦除時必須給已經對齊的塊位址,否則,擦除的結果是不可知的
S5PV210的Nand控制器
Soc内部內建了Nand控制器,我們隻需要操控Nand控制器的寄存器,Nand控制器根據寄存器生成Nand接口時序和Nand進行通信
,Nand的框圖如下:
程式設計中就是通過讀寫SFR産生Nand接口時序來讀寫Nand晶片,Nand
interface和Nand晶片的相應引腳相連接配接
Nand代碼
頭檔案中的聲明
void nand_init(void);
void nand_read_id(void);
int nand_block_erase(unsigned long block_num);
int copy_nand_to_sdram(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length);
int copy_sdram_to_nand(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length);
int nand_page_read(unsigned int pgaddr, unsigned char *buf, unsigned int length);
int nand_page_write(unsigned int pgaddr, const unsigned char *buf, unsigned int length);
int nand_random_read(unsigned long pgaddr,unsigned short offset, unsigned char *data);
int nand_random_write(unsigned long pgaddr,unsigned short offset,unsigned char wrdata);
宏定義和類型聲明
#include "nand.h"
#include "stdio.h"
#define rNFCONF ( *((volatile unsigned long *)0xB0E00000) )
#define rNFCONT ( *((volatile unsigned long *)0xB0E00004) )
#define rNFCMMD ( *((volatile unsigned long *)0xB0E00008) )
#define rNFADDR ( *((volatile unsigned long *)0xB0E0000C) )
#define rNFDATA ( *((volatile unsigned long *)0xB0E00010) )
#define rNFDATA8 ( *((volatile unsigned char *)0xB0E00010) )
#define rNFSTAT ( *((volatile unsigned long *)0xB0E00028) )
#define rMP0_1CON ( *((volatile unsigned long *)0xE02002E0) )
#define rMP0_2CON ( *((volatile unsigned long *)0xE0200300) )
#define rMP0_3CON ( *((volatile unsigned long *)0xE0200320) )
#define MAX_NAND_BLOCK 8192 /*定義nand最大塊數:8192塊 */
#define NAND_PAGE_SIZE 2048 /*定義一頁的容量:2048 byte */
#define NAND_BLOCK_SIZE 64 /*定義block大小:64頁 */
#define TACLS 1 // 12ns /* 時序相關的設定 */
#define TWRPH0 4
#define TWRPH1 1
#define NAND_CMD_READ_1st 0x00 /* 指令 */
#define NAND_CMD_READ_2st 0x30
#define NAND_CMD_READ_CB_1st 0x00
#define NAND_CMD_READ_CB_2st 0x35
#define NAND_CMD_RANDOM_WRITE 0x85
#define NAND_CMD_RANDOM_READ_1st 0x05
#define NAND_CMD_RANDOM_READ_2st 0xe0
#define NAND_CMD_READ_ID 0x90
#define NAND_CMD_RESET 0xff
#define NAND_CMD_READ_STATUS 0x70
#define NAND_CMD_WRITE_PAGE_1st 0x80
#define NAND_CMD_WRITE_PAGE_2st 0x10
#define NAND_CMD_BLOCK_ERASE_1st 0x60
#define NAND_CMD_BLOCK_ERASE_2st 0xd0
#define ECC_EN (1<<4)
#define CONTROL_EN (1<<0)
static void nand_reset(void);
static void nand_wait_idle(void);
static void nand_select_chip(void);
static void nand_deselect_chip(void);
static void nand_send_cmd(unsigned long cmd);
static void nand_send_addr(unsigned long addr);
static unsigned char nand_read8(void);
static void nand_write8(unsigned char data);
static unsigned int nand_read32(void);
static void nand_write32(unsigned int data);
typedef struct nand_id_info
{
//marker code
unsigned char IDm;
//device code
unsigned char IDd;
unsigned char ID3rd;
unsigned char ID4th;
unsigned char ID5th;
}nand_id_info;
操作函數
// 複位
void nand_reset(void)
{
nand_select_chip();
nand_send_cmd(NAND_CMD_RESET);
nand_wait_idle();
nand_deselect_chip();
}
// 等待就緒
void nand_wait_idle(void)
{
unsigned long i;
while( !(rNFSTAT & (<<)) )
for(i=; i<; i++);
}
// 發片選
void nand_select_chip(void)
{
unsigned long i;
rNFCONT &= ~(<<);
for(i=; i<; i++);
}
// 取消片選
void nand_deselect_chip(void)
{
unsigned long i = ;
rNFCONT |= (<<);
for(i=; i<; i++);
}
// 發指令
void nand_send_cmd(unsigned long cmd)
{
unsigned long i = ;
rNFCMMD = cmd;
for(i=; i<; i++);
}
// 發位址
void nand_send_addr(unsigned long addr)
{
unsigned long i;
unsigned long col, row;
// 列位址,即頁内位址
col = addr % NAND_PAGE_SIZE;
// 行位址,即頁位址
row = addr / NAND_PAGE_SIZE;
// Column Address A0~A7
rNFADDR = col & ;
for(i=; i<; i++);
// Column Address A8~A11
rNFADDR = (col >> ) & ;
for(i=; i<; i++);
// Row Address A12~A19
rNFADDR = row & ;
for(i=; i<; i++);
// Row Address A20~A27
rNFADDR = (row >> ) & ;
for(i=; i<; i++);
// Row Address A28~A30
rNFADDR = (row >> ) & ;
for(i=; i<; i++);
}
unsigned int nand_read32(void)
{
return rNFDATA;
}
void nand_write32(unsigned int data)
{
rNFDATA = data;
}
// 讀一個位元組的資料
unsigned char nand_read8(void)
{
return rNFDATA8;
}
// 寫一個位元組的資料
void nand_write8(unsigned char data)
{
rNFDATA8 = data;
}
unsigned char nand_read_status(void)
{
unsigned char ch;
int i;
// 1. 發出片選信号
nand_select_chip();
// 2. 讀狀态
nand_send_cmd(NAND_CMD_READ_STATUS);
for(i=; i<; i++);
ch = nand_read8();
// 3. 取消片選
nand_deselect_chip();
return ch;
}
// nandflash 初始化
void nand_init(void)
{
// 1. 配置nandflash
rNFCONF = (TACLS<<)|(TWRPH0<<)|(TWRPH1<<)|(<<)|(<<)|(<<)|(<<);
rNFCONT = (<<)|(<<)|(<<)|(<<)|(<<)|(<<)|(<<)|(<<)|(<<)|(<<);
// 2. 配置引腳
rMP0_1CON = ;
rMP0_2CON = ;
rMP0_3CON = ;
// 3. 複位
nand_reset();
}
// 讀晶片ID
void nand_read_id(void)
{
nand_id_info nand_id;
// 1. 發片選
nand_select_chip();
// 2. 讀ID
nand_send_cmd(NAND_CMD_READ_ID);
nand_send_addr();
nand_wait_idle();
nand_id.IDm = nand_read8();
nand_id.IDd = nand_read8();
nand_id.ID3rd = nand_read8();
nand_id.ID4th = nand_read8();
nand_id.ID5th = nand_read8();
printf("nandflash: makercode = %x\r\n devicecode = %x\r\n ID3rd = %x\r\n ID4rd = %x\r\n ID5rd = %x\r\n", nand_id.IDm, nand_id.IDd, nand_id.ID3rd, nand_id.ID4th, nand_id.ID5th);
nand_deselect_chip();
}
// 擦除塊,參數為塊号(0 ~ MAX_NAND_BLOCK-1)
int nand_block_erase(unsigned long block_num)
{
unsigned long i = ;
// 獲得row位址,即頁位址
unsigned long row = block_num * NAND_BLOCK_SIZE;
// 1. 發出片選信号
nand_select_chip();
// 2. 擦除:第一個周期發指令0x60,第二個周期發塊位址,第三個周期發指令0xd0
nand_send_cmd(NAND_CMD_BLOCK_ERASE_1st);
for(i=; i<; i++);
// Row Address A12~A19
rNFADDR = row & ;
for(i=; i<; i++);
// Row Address A20~A27
rNFADDR = (row >> ) & ;
for(i=; i<; i++);
// Row Address A28~A30
rNFADDR = (row >> ) & ;
rNFSTAT |= (<<); // clear RnB bit
nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st);
for(i=; i<; i++);
// 3. 等待就緒
nand_wait_idle();
// 4. 讀狀态
unsigned char status = nand_read_status();
if (status & )
{
// statas[0] = 1,表示擦除失敗,詳見NAND Flash資料手冊中 READ STATUS一節的描述
// 取消片選信号
nand_deselect_chip();
printf("masking bad block %d\r\n", block_num);
return -;
}
else
{
// status[0] = 0,表示擦除成功,傳回0
nand_deselect_chip();
return ;
}
}
int nand_erase(unsigned long block_addr)
{
int i = ;
if((nand_read_status() & ) == )
{
printf("Write protected.\n");
return -;
}
unsigned long row = block_addr >> ;
// 1. 發出片選信号
nand_select_chip();
// 2. 擦除:第一個周期發指令0x60,第二個周期發塊位址,第三個周期發指令0xd0
nand_send_cmd(NAND_CMD_BLOCK_ERASE_1st);
for(i=; i<; i++);
// Row Address A12~A19
rNFADDR = row & ;
for(i=; i<; i++);
// Row Address A20~A27
rNFADDR = (row >> ) & ;
for(i=; i<; i++);
// Row Address A28~A30
rNFADDR = (row >> ) & ; // 隻要最低1bit為 A28
for(i=; i<; i++);
rNFSTAT |= (<<); // clear RnB bit
nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st);
for(i=; i<; i++);
// 3. 等待就緒
nand_wait_idle();
// 4. 讀狀态
unsigned char status = nand_read_status();
if (status & )
{
// statas[0] = 1,表示擦除失敗,詳見NAND Flash資料手冊中 READ STATUS一節的描述
// 取消片選信号
nand_deselect_chip();
printf("masking bad block %d\r\n", block_addr);
return -;
}
else
{
// status[0] = 0,表示擦除成功,傳回0
nand_deselect_chip();
return ;
}
}
// 從nand中讀資料到sdram
int copy_nand_to_sdram(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length)
{
unsigned long i = ;
// 1. 發出片選信号
nand_select_chip();
// 2. 從nand讀資料到sdram,第一周期發指令0x00,第二周期發位址nand_addr,第三個周期發指令0x30,可讀一頁(2k)的資料
while(length)
{
nand_send_cmd(NAND_CMD_READ_1st);
nand_send_addr(nand_addr);
rNFSTAT = (rNFSTAT)|(<<);
nand_send_cmd(NAND_CMD_READ_2st);
nand_wait_idle();
// 列位址,即頁内位址
unsigned long col = nand_addr % NAND_PAGE_SIZE;
i = col;
// 讀一頁資料,每次拷1byte,共拷2048次(2k),直到長度為length的資料拷貝完畢
for(; i<NAND_PAGE_SIZE && length!=; i++,length--)
{
*sdram_addr = nand_read8();
sdram_addr++;
nand_addr++;
}
}
// 3. 讀狀态
unsigned char status = nand_read_status();
if (status & )
{
// 取消片選信号
nand_deselect_chip();
printf("copy nand to sdram fail\r\n");
return -;
}
else
{
nand_deselect_chip();
return ;
}
}
// 從sdram中寫資料到nand
int copy_sdram_to_nand(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length)
{
unsigned long i = ;
// 1. 發出片選信号
nand_select_chip();
// 2. 從sdram讀資料到nand,第一周期發指令0x80,第二周期發位址nand_addr,第三個周期寫一頁(2k)資料,第四周期發0x10
while(length)
{
nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);
nand_send_addr(nand_addr);
// 列位址,即頁内位址
unsigned long col = nand_addr % NAND_PAGE_SIZE;
i = col;
// 寫一頁資料,每次拷1byte,共拷2048次(2k),直到長度為length的資料拷貝完畢
for(; i<NAND_PAGE_SIZE && length!=; i++,length--)
{
nand_write8(*sdram_addr);
sdram_addr++;
nand_addr++;
}
rNFSTAT = (rNFSTAT)|(<<);
nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
nand_wait_idle();
}
// 3. 讀狀态
unsigned char status = nand_read_status();
if (status & )
{
// 取消片選信号
nand_deselect_chip();
printf("copy sdram to nand fail\r\n");
return -;
}
else
{
nand_deselect_chip();
return ;
}
}
int nand_page_read(unsigned int pgaddr, unsigned char *buf, unsigned int length)
{
int i = ;
// 1 發出片選信号
nand_select_chip();
// 2 寫頁讀指令1st
nand_send_cmd(NAND_CMD_READ_1st);
// 3 寫入頁位址
rNFADDR = ;
rNFADDR = ;
rNFADDR = pgaddr&;
rNFADDR = (pgaddr>>)&;
rNFADDR = (pgaddr>>)&;
// 4 clear RnB
rNFSTAT |= (<<);
// 5 寫頁讀指令2st
nand_send_cmd(NAND_CMD_READ_2st);
// 6 等待空閑
nand_wait_idle();
// 7 連續讀取2KB的Page main區資料 (繼續讀取可讀出64B的spare area資料)
for (i=; (i<NAND_PAGE_SIZE) && (length!=); i++,length--)
*buf++ = nand_read8();
// 8 讀狀态
unsigned char status = nand_read_status();
if (status & )
{
// 讀出錯,取消片選信号,傳回錯誤碼-1
nand_deselect_chip();
printf("nand random read fail\r\n");
return -;
}
else
{
// 讀正确,取消片選,傳回0
nand_deselect_chip();
return ;
}
}
int nand_page_read32(unsigned int pgaddr, unsigned int *buf, unsigned int lengthB)
{
int i = ;
// 1 發出片選信号
nand_select_chip();
// 2 寫頁讀指令1st
nand_send_cmd(NAND_CMD_READ_1st);
// 3 寫入頁位址
rNFADDR = ;
rNFADDR = ;
rNFADDR = pgaddr&;
rNFADDR = (pgaddr>>)&;
rNFADDR = (pgaddr>>)&;
// 4 clear RnB
rNFSTAT |= (<<);
// 5 寫頁讀指令2st
nand_send_cmd(NAND_CMD_READ_2st);
// 6 等待空閑
nand_wait_idle();
// 7 連續讀取2KB的Page main區資料 (繼續讀取可讀出64B的spare area資料)
for (i=; (i<NAND_PAGE_SIZE/) && (lengthB!=); i++,lengthB--)
*buf++ = nand_read32();
// 8 讀狀态
unsigned char status = nand_read_status();
if (status & )
{
// 讀出錯,取消片選信号,傳回錯誤碼-1
nand_deselect_chip();
printf("nand random read fail\r\n");
return -;
}
else
{
// 讀正确,取消片選,傳回0
nand_deselect_chip();
return ;
}
}
int nand_page_write(unsigned int pgaddr, const unsigned char *buf, unsigned int length)
{
int i = ;
// 1 發出片選信号
nand_select_chip();
// 2 write cmd 1st
nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);
// 3 write page addr
rNFADDR = ;
rNFADDR = ;
rNFADDR = pgaddr&;
rNFADDR = (pgaddr>>)&;
rNFADDR = (pgaddr>>)&;
// 4 寫入一頁内容
for(; i<NAND_PAGE_SIZE && length!=; i++,length--)
nand_write8(*buf++);
// 5 clear RnB
rNFSTAT = (rNFSTAT)|(<<);
// 6 write cmd 2
nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
// 7 wait idle
nand_wait_idle();
// 8 讀狀态
unsigned char status = nand_read_status();
if (status & )
{
// 取消片選信号
nand_deselect_chip();
printf("nand random write fail\r\n");
return -;
}
else
{
nand_deselect_chip();
return ;
}
}
/*
* 函數功能: 随機讀資料
* 參數: pgaddr 為頁位址, offset為頁内偏移位址,data為傳回值
* 傳回值: 0表示讀取成功,1表示讀取失敗
*/
int nand_random_read(unsigned long pgaddr,unsigned short offset, unsigned char *data)
{
unsigned char readdata;
// 1. 發出片選信号
nand_select_chip();
// 2. 随機讀頁内某個位址的值
nand_send_cmd(NAND_CMD_READ_1st);
//寫入頁位址
rNFADDR = ;
rNFADDR = ;
rNFADDR = pgaddr&;
rNFADDR = (pgaddr>>)&;
rNFADDR = (pgaddr>>)&;
rNFSTAT |= (<<);
nand_send_cmd(NAND_CMD_READ_2st);
nand_wait_idle();
nand_send_cmd(NAND_CMD_RANDOM_READ_1st);
//寫入頁内偏移位址
rNFADDR = offset&;
rNFADDR = (offset>>)&;
rNFSTAT = (rNFSTAT)|(<<);
nand_send_cmd(NAND_CMD_RANDOM_READ_2st);
readdata = nand_read8();
// 3. 讀狀态
unsigned char status = nand_read_status();
if (status & )
{
// 取消片選信号
nand_deselect_chip();
printf("nand random read fail\r\n");
return -;
}
else
{
nand_deselect_chip();
*data = readdata;
return ;
}
}
/*
* 函數功能: 随機寫資料
* 參數: pgaddr 為頁位址, offset為頁内偏移位址,wrdata為要寫入的資料
* 傳回值: 0表示寫入成功,1表示寫入失敗
* 測試結論: 1、random write一次隻能寫入一個位元組,是以内部隻能使用 nand_write8,使用nand_write32就會出錯
*/
int nand_random_write(unsigned long pgaddr,unsigned short offset,unsigned char wrdata)
{
// 1. 發出片選信号
nand_select_chip();
// 2. 随機寫頁内某個位址的值
nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);
rNFADDR = ;
rNFADDR = ;
rNFADDR = pgaddr&;
rNFADDR = (pgaddr>>)&;
rNFADDR = (pgaddr>>)&;
nand_send_cmd(NAND_CMD_RANDOM_WRITE);
//寫入頁内偏移位址
rNFADDR = offset&;
rNFADDR = (offset>>)&;
nand_write8(wrdata);
rNFSTAT = (rNFSTAT)|(<<);
nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
nand_wait_idle();
// 3. 讀狀态
unsigned char status = nand_read_status();
if (status & )
{
// 取消片選信号
nand_deselect_chip();
printf("nand random write fail\r\n");
return -;
}
else
{
nand_deselect_chip();
return ;
}
}
// nand_test 使用的是BLOCK_NO塊中的首頁
#define BLOCK_NO 10
#define PAGE_NO (BLOCK_NO * NAND_BLOCK_SIZE)
#define PAGE_ADDR (PAGE_NO * NAND_PAGE_SIZE)
#define OFFSET0 4
#define OFFSET1 5
#define OFFSET2 6
#define OFFSET3 7
void nand_test(void)
{
int ret = ;
unsigned char data1, data2, data3, data4;
unsigned char buf[];
unsigned int bufInt[];
nand_init();
nand_read_id();
#if 0
// 得先擦除才能寫啊
if ((ret = nand_erase(PAGE_ADDR)) == )
printf("success to erase block %d\r\n", BLOCK_NO);
else
printf("fail to erase block %d\r\n", BLOCK_NO);
#endif
// 先給丫random寫4個byte
#if 1
nand_random_write(PAGE_ADDR, OFFSET0, 'a');
nand_random_write(PAGE_ADDR, OFFSET1, 'b');
nand_random_write(PAGE_ADDR, OFFSET2, 'c');
nand_random_write(PAGE_ADDR, OFFSET3, 'd');
#endif
#if 0
nand_random_write(PAGE_ADDR, OFFSET0, );
nand_random_write(PAGE_ADDR, OFFSET1, );
nand_random_write(PAGE_ADDR, OFFSET2, );
nand_random_write(PAGE_ADDR, OFFSET3, );
#endif
// 然後再用三種方法,讀出來看看對不對得上
nand_random_read(PAGE_ADDR, OFFSET0, &data1);
nand_random_read(PAGE_ADDR, OFFSET1, &data2);
nand_random_read(PAGE_ADDR, OFFSET2, &data3);
nand_random_read(PAGE_ADDR, OFFSET3, &data4);
printf("PAGE_ADDR: \r\n", PAGE_ADDR);
printf("4 byte data from nand_random_read: %x, %x, %x, %x\r\n", data1, data2, data3, data4);
ret = nand_page_read(PAGE_ADDR, buf, sizeof(buf));
if (ret != )
printf("nand_page_read error!\r\n");
else
printf("4 byte data form nand_page_read: %x, %x, %x, %x\r\n", buf[OFFSET0], buf[OFFSET1], buf[OFFSET2], buf[OFFSET3]);
ret = nand_page_read32(PAGE_ADDR, bufInt, sizeof(bufInt)/sizeof(unsigned int));
if (ret != )
printf("nand_page_read32 error!\r\n");
else
printf("1 word data form nand_page_read32: %x\r\n", bufInt[OFFSET0]);
}
iNand
SD和MMC都是以卡片形式存在,iNand和eMMC在SD卡基礎上發展起來的,将SD卡晶片化以解決接觸不良和體積限制等問題,eMMC作為一種協定,iNand是符合eMMC協定的存儲晶片
iNand内部和Nand類似,隻在接口電路功能不同,相較于Nand更複雜,功能更健全,iNand提供ECC的校驗邏輯,本身已經完成了ECC的功能,Soc不用寫代碼來控制ECC的相關操作,iNand程式設計容易,成本效益更高,并且内部具有Cache機制,速度更快
iNand總共有8根資料線,一根CMD線和一根CLK線,S5PV210晶片支援4通道的SD/MMC,開發闆上把通道0接了iNand晶片,在通道2和3接了SD卡,通過引腳對比可知iNand和SD卡接線基本是一樣的,隻不過SD卡的資料線有4根而iNand有8根資料線,說明iNand在操作的時候和SD卡基本一樣
iNand的基本操作
iNand支援1,4,8根總線的并行傳輸,SD支援1,4根總線的并行傳輸,CLK和CMD線是一模一樣的,CLK說明主機Soc通過CLK傳輸時鐘信号,SD卡和iNand工作在同步模式下,工作速率是由主機給的時鐘頻率來控制的
SD卡是指令-響應工作模式的存儲晶片,指令在CMD線上串行傳輸,SD協定中有很多标準指令,每個指令都有作用,使用條件以及對應的響應,SD卡的工作是每個指令周期組合起來的,主機先發送一個指令到SD卡,SD解析該指令并回發響應到主機,個别指令不需要響應,主機也不用等待響應
SD卡有一些内部寄存器以供通路,通路這些寄存器可以得到一些相關資訊,其中RCA相對位址寄存器存放了SD卡中每個存儲單元的位置
iNand代碼
宏定義以及函數聲明
#ifndef __HSMMC_H__
#define __HSMMC_H__
#ifdef __cplusplus
extern "C" {
#endif
#include "stdint.h"
// SD協定規定的指令碼
#define CMD0 0
#define CMD1 1
#define CMD2 2
#define CMD3 3
#define CMD6 6
#define CMD7 7
#define CMD8 8
#define CMD9 9
#define CMD13 13
#define CMD16 16
#define CMD17 17
#define CMD18 18
#define CMD23 23
#define CMD24 24
#define CMD25 25
#define CMD32 32
#define CMD33 33
#define CMD38 38
#define CMD41 41
#define CMD51 51
#define CMD55 55
// 卡類型
#define UNUSABLE 0
#define SD_V1 1
#define SD_V2 2
#define SD_HC 3
#define MMC 4
// 卡狀态
#define CARD_IDLE 0 // 空閑态
#define CARD_READY 1 // 準備好
#define CARD_IDENT 2
#define CARD_STBY 3
#define CARD_TRAN 4
#define CARD_DATA 5
#define CARD_RCV 6
#define CARD_PRG 7 // 卡程式設計狀态
#define CARD_DIS 8 // 斷開連接配接
// 卡回複類型
#define CMD_RESP_NONE 0 // 無回複
#define CMD_RESP_R1 1
#define CMD_RESP_R2 2
#define CMD_RESP_R3 3
#define CMD_RESP_R4 4
#define CMD_RESP_R5 5
#define CMD_RESP_R6 6
#define CMD_RESP_R7 7
#define CMD_RESP_R1B 8
typedef struct {
uint32_t RESERVED1;
uint32_t RESERVED2 : ;
uint32_t SD_BUS_WIDTHS : ;
uint32_t SD_SECURITY : ;
uint32_t DATA_STAT_AFTER_ERASE : ;
uint32_t SD_SPEC : ;
uint32_t SCR_STRUCTURE : ;
} SD_SCR;
int32_t Hsmmc_Init(void);
int32_t Hsmmc_GetCardState(void);
int32_t Hsmmc_GetSdState(uint8_t *pStatus);
int32_t Hsmmc_Get_SCR(SD_SCR *pSCR);
int32_t Hsmmc_Get_CSD(uint8_t *pCSD);
int32_t Hsmmc_EraseBlock(uint32_t StartBlock, uint32_t EndBlock);
int32_t Hsmmc_WriteBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber);
int32_t Hsmmc_ReadBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber);
#ifdef __cplusplus
}
#endif
#endif /*__HSMMC_H__*/
實際操作代碼
#include "ProjectConfig.h"
#include "Hsmmc.h"
#define HSMMC_NUM 2
#if (HSMMC_NUM == 0)
#define HSMMC_BASE (0xEB000000)
#elif (HSMMC_NUM == 1)
#define HSMMC_BASE (0xEB100000)
#elif (HSMMC_NUM == 2)
#define HSMMC_BASE (0xEB200000)
#elif (HSMMC_NUM == 3)
#define HSMMC_BASE (0xEB300000)
#else
#error "Configure HSMMC: HSMMC0 ~ HSMM3(0 ~ 3)"
#endif
#define MAX_BLOCK 65535
#define SWRST_OFFSET 0x2F
static uint8_t CardType; // 卡類型
static uint32_t RCA; // 卡相對位址
static void Hsmmc_ClockOn(uint8_t On)
{
uint32_t Timeout;
if (On) {
__REGw(HSMMC_BASE+CLKCON_OFFSET) |= (<<); // sd時鐘使能
Timeout = ; // Wait max 10 ms
while (!(__REGw(HSMMC_BASE+CLKCON_OFFSET) & (<<))) {
// 等待SD輸出時鐘穩定
if (Timeout == ) {
return;
}
Timeout--;
Delay_us();
}
} else {
__REGw(HSMMC_BASE+CLKCON_OFFSET) &= ~(<<); // sd時鐘禁止
}
}
static void Hsmmc_SetClock(uint32_t Clock)
{
uint32_t Temp;
uint32_t Timeout;
uint32_t i;
Hsmmc_ClockOn(); // 關閉時鐘
Temp = __REG(HSMMC_BASE+CONTROL2_OFFSET);
// Set SCLK_MMC(48M) from SYSCON as a clock source
Temp = (Temp & (~(<<))) | (<<);
Temp |= (u<<) | (u<<) | (<<);
if (Clock <= ) {
Temp &= ~((<<) | (<<));
__REG(HSMMC_BASE+CONTROL3_OFFSET) = ;
} else {
Temp |= ((<<) | (<<));
__REG(HSMMC_BASE+CONTROL3_OFFSET) = (u<<) | (<<);
}
__REG(HSMMC_BASE+CONTROL2_OFFSET) = Temp;
for (i=; i<=; i++) {
if (Clock >= (/(<<i))) {
break;
}
}
Temp = ((<<i) / ) << ; // clock div
Temp |= (<<); // Internal Clock Enable
__REGw(HSMMC_BASE+CLKCON_OFFSET) = Temp;
Timeout = ; // Wait max 10 ms
while (!(__REGw(HSMMC_BASE+CLKCON_OFFSET) & (<<))) {
// 等待内部時鐘振蕩穩定
if (Timeout == ) {
return;
}
Timeout--;
Delay_us();
}
Hsmmc_ClockOn(); // 使能時鐘
}
static int32_t Hsmmc_WaitForBufferReadReady(void)
{
int32_t ErrorState;
while () {
if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<)) { // 出現錯誤
break;
}
if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<)) { // 讀緩存準備好
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (<<); // 清除準備好标志
return ;
}
}
ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & ; // 可能通信錯誤,CRC檢驗錯誤,逾時等
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中斷标志
__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除錯誤中斷标志
Debug("Read buffer error, NORINTSTS: %04x\r\n", ErrorState);
return ErrorState;
}
static int32_t Hsmmc_WaitForBufferWriteReady(void)
{
int32_t ErrorState;
while () {
if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<)) { // 出現錯誤
break;
}
if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<)) { // 寫緩存準備好
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (<<); // 清除準備好标志
return ;
}
}
ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & ; // 可能通信錯誤,CRC檢驗錯誤,逾時等
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中斷标志
__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除錯誤中斷标志
Debug("Write buffer error, NORINTSTS: %04x\r\n", ErrorState);
return ErrorState;
}
static int32_t Hsmmc_WaitForCommandDone(void)
{
uint32_t i;
int32_t ErrorState;
// 等待指令發送完成
for (i=; i<; i++) {
if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<)) { // 出現錯誤
break;
}
if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<)) {
do {
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = (<<); // 清除指令完成位
} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<));
return ; // 指令發送成功
}
}
ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & ; // 可能通信錯誤,CRC檢驗錯誤,逾時等
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中斷标志
__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除錯誤中斷标志
do {
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = (<<); // 清除指令完成位
} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<));
Debug("Command error, ERRINTSTS = 0x%x ", ErrorState);
return ErrorState; // 指令發送出錯
}
static int32_t Hsmmc_WaitForTransferDone(void)
{
int32_t ErrorState;
uint32_t i;
// 等待資料傳輸完成
for (i=; i<; i++) {
if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<)) { // 出現錯誤
break;
}
if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<)) { // 資料傳輸完
do {
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (<<); // 清除傳輸完成位
} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<));
return ;
}
}
ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & ; // 可能通信錯誤,CRC檢驗錯誤,逾時等
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中斷标志
__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除錯誤中斷标志
Debug("Transfer error, rHM1_ERRINTSTS = 0x%04x\r\n", ErrorState);
do {
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = (<<); // 出錯後清除資料完成位
} while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (<<));
return ErrorState; // 資料傳輸出錯
}
static int32_t Hsmmc_IssueCommand(uint8_t Cmd, uint32_t Arg, uint8_t Data, uint8_t Response)
{
uint32_t i;
uint32_t Value;
uint32_t ErrorState;
// 檢查CMD線是否準備好發送指令
for (i=; i<; i++) {
if (!(__REG(HSMMC_BASE+PRNSTS_OFFSET) & (<<))) {
break;
}
}
if (i == ) {
Debug("CMD line time out, PRNSTS: %04x\r\n", __REG(HSMMC_BASE+PRNSTS_OFFSET));
return -; // 指令逾時
}
// 檢查DAT線是否準備好
if (Response == CMD_RESP_R1B) { // R1b回複通過DAT0回報忙信号
for (i=; i<; i++) {
if (!(__REG(HSMMC_BASE+PRNSTS_OFFSET) & (<<))) {
break;
}
}
if (i == ) {
Debug("Data line time out, PRNSTS: %04x\r\n", __REG(HSMMC_BASE+PRNSTS_OFFSET));
return -;
}
}
__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Arg; // 寫入指令參數
Value = (Cmd << ); // command index
// CMD12可終止傳輸
if (Cmd == ) {
Value |= ( << ); // command type
}
if (Data) {
Value |= ( << ); // 需使用DAT線作為傳輸等
}
switch (Response) {
case CMD_RESP_NONE:
Value |= (<<) | (<<) | ; // 沒有回複,不檢查指令及CRC
break;
case CMD_RESP_R1:
case CMD_RESP_R5:
case CMD_RESP_R6:
case CMD_RESP_R7:
Value |= (<<) | (<<) | ; // 檢查回複中的指令,CRC
break;
case CMD_RESP_R2:
Value |= (<<) | (<<) | ; // 回複長度為136位,包含CRC
break;
case CMD_RESP_R3:
case CMD_RESP_R4:
Value |= (<<) | (<<) | ; // 回複長度48位,不包含指令及CRC
break;
case CMD_RESP_R1B:
Value |= (<<) | (<<) | ; // 回複帶忙信号,會占用Data[0]線
break;
default:
break;
}
__REGw(HSMMC_BASE+CMDREG_OFFSET) = Value;
ErrorState = Hsmmc_WaitForCommandDone();
if (ErrorState) {
Debug("Command = %d\r\n", Cmd);
}
return ErrorState; // 指令發送出錯
}
int32_t Hsmmc_Switch(uint8_t Mode, int32_t Group, int32_t Function, uint8_t *pStatus)
{
int32_t ErrorState;
int32_t Temp;
uint32_t i;
__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (<<) | (<<); // 最大DMA緩存大小,block為512位64位元組
__REGw(HSMMC_BASE+BLKCNT_OFFSET) = ; // 寫入這次讀1 block的sd狀态資料
Temp = (Mode << U) | ;
Temp &= ~(<<(Group * ));
Temp |= Function << (Group * );
__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Temp; // 寫入指令參數
// DMA禁能,讀單塊
__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (<<) | (<<) | (<<) | (<<) | (<<);
// 設定指令寄存器,SWITCH_FUNC CMD6,R1回複
__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD6<<)|(<<)|(<<)|(<<)|;
ErrorState = Hsmmc_WaitForCommandDone();
if (ErrorState) {
Debug("CMD6 error\r\n");
return ErrorState;
}
ErrorState = Hsmmc_WaitForBufferReadReady();
if (ErrorState) {
return ErrorState;
}
pStatus += - ;
for (i=; i</; i++) {
Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
*pStatus-- = (uint8_t)Temp;
*pStatus-- = (uint8_t)(Temp>>);
*pStatus-- = (uint8_t)(Temp>>);
*pStatus-- = (uint8_t)(Temp>>);
}
ErrorState = Hsmmc_WaitForTransferDone();
if (ErrorState) {
Debug("Get sd status error\r\n");
return ErrorState;
}
return ;
}
// 512位的sd卡擴充狀态位
int32_t Hsmmc_GetSdState(uint8_t *pStatus)
{
int32_t ErrorState;
uint32_t Temp;
uint32_t i;
if (CardType == SD_HC || CardType == SD_V2 || CardType == SD_V1) {
if (Hsmmc_GetCardState() != CARD_TRAN) { // 必需在transfer status
return -; // 卡狀态錯誤
}
Hsmmc_IssueCommand(CMD55, RCA<<, , CMD_RESP_R1);
__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (<<) | (<<); // 最大DMA緩存大小,block為512位64位元組
__REGw(HSMMC_BASE+BLKCNT_OFFSET) = ; // 寫入這次讀1 block的sd狀态資料
__REG(HSMMC_BASE+ARGUMENT_OFFSET) = ; // 寫入指令參數
// DMA禁能,讀單塊
__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (<<) | (<<) | (<<) | (<<) | (<<);
// 設定指令寄存器,讀狀态指令CMD13,R1回複
__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD13<<)|(<<)|(<<)|(<<)|;
ErrorState = Hsmmc_WaitForCommandDone();
if (ErrorState) {
Debug("CMD13 error\r\n");
return ErrorState;
}
ErrorState = Hsmmc_WaitForBufferReadReady();
if (ErrorState) {
return ErrorState;
}
pStatus += - ;
for (i=; i</; i++) {
Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
*pStatus-- = (uint8_t)Temp;
*pStatus-- = (uint8_t)(Temp>>);
*pStatus-- = (uint8_t)(Temp>>);
*pStatus-- = (uint8_t)(Temp>>);
}
ErrorState = Hsmmc_WaitForTransferDone();
if (ErrorState) {
Debug("Get sd status error\r\n");
return ErrorState;
}
return ;
}
return -; // 非sd卡
}
// Reads the SD Configuration Register (SCR).
int32_t Hsmmc_Get_SCR(SD_SCR *pSCR)
{
uint8_t *pBuffer;
int32_t ErrorState;
uint32_t Temp;
uint32_t i;
Hsmmc_IssueCommand(CMD55, RCA<<, , CMD_RESP_R1);
__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (<<) | (<<); // 最大DMA緩存大小,block為64位8位元組
__REGw(HSMMC_BASE+BLKCNT_OFFSET) = ; // 寫入這次讀1 block的sd狀态資料
__REG(HSMMC_BASE+ARGUMENT_OFFSET) = ; // 寫入指令參數
// DMA禁能,讀單塊
__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (<<) | (<<) | (<<) | (<<) | (<<);
// 設定指令寄存器,read SD Configuration CMD51,R1回複
__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD51<<)|(<<)|(<<)|(<<)|;
ErrorState = Hsmmc_WaitForCommandDone();
if (ErrorState) {
Debug("CMD51 error\r\n");
return ErrorState;
}
ErrorState = Hsmmc_WaitForBufferReadReady();
if (ErrorState) {
return ErrorState;
}
// Wide width data (SD Memory Register)
pBuffer = (uint8_t *)pSCR + sizeof(SD_SCR) - ;
for (i=; i</; i++) {
Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
*pBuffer-- = (uint8_t)Temp;
*pBuffer-- = (uint8_t)(Temp>>);
*pBuffer-- = (uint8_t)(Temp>>);
*pBuffer-- = (uint8_t)(Temp>>);
}
ErrorState = Hsmmc_WaitForTransferDone();
if (ErrorState) {
Debug("Get SCR register error\r\n");
return ErrorState;
}
return ;
}
// Asks the selected card to send its cardspecific data
int32_t Hsmmc_Get_CSD(uint8_t *pCSD)
{
uint32_t i;
uint32_t Response[];
int32_t State = -;
if (CardType != SD_HC && CardType != SD_V1 && CardType != SD_V2) {
return State; // 未識别的卡
}
// 取消卡選擇,任何卡均不回複,已選擇的卡通過RCA=0取消選擇,
// 卡回到stand-by狀态
Hsmmc_IssueCommand(CMD7, , , CMD_RESP_NONE);
for (i=; i<; i++) {
if (Hsmmc_GetCardState() == CARD_STBY) { // CMD9指令需在standy-by status
break; // 狀态正确
}
Delay_us();
}
if (i == ) {
return State; // 狀态錯誤
}
// 請求已标記卡發送卡特定資料(CSD),獲得卡資訊
if (!Hsmmc_IssueCommand(CMD9, RCA<<, , CMD_RESP_R2)) {
pCSD++; // 跳過第一位元組,CSD中[127:8]位對位寄存器中的[119:0]
Response[] = __REG(HSMMC_BASE+RSPREG0_OFFSET);
Response[] = __REG(HSMMC_BASE+RSPREG1_OFFSET);
Response[] = __REG(HSMMC_BASE+RSPREG2_OFFSET);
Response[] = __REG(HSMMC_BASE+RSPREG3_OFFSET);
for (i=; i<; i++) { // 拷貝回複寄存器中的[119:0]到pCSD中
*pCSD++ = ((uint8_t *)Response)[i];
}
State = ; // CSD擷取成功
}
Hsmmc_IssueCommand(CMD7, RCA<<, , CMD_RESP_R1); // 選擇卡,卡回到transfer狀态
return State;
}
// R1回複中包含了32位的card state,卡識别後,可在任一狀态通過CMD13獲得卡狀态
int32_t Hsmmc_GetCardState(void)
{
if (Hsmmc_IssueCommand(CMD13, RCA<<, , CMD_RESP_R1)) {
return -; // 卡出錯
} else {
return ((__REG(HSMMC_BASE+RSPREG0_OFFSET)>>) & ); // 傳回R1回複中的[12:9]卡狀态
}
}
static int32_t Hsmmc_SetBusWidth(uint8_t Width)
{
int32_t State;
if ((Width != ) || (Width != )) {
return -;
}
State = -; // 設定初始為未成功
__REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) &= ~(<<); // 關閉卡中斷
Hsmmc_IssueCommand(CMD55, RCA<<, , CMD_RESP_R1);
if (Width == ) {
if (!Hsmmc_IssueCommand(CMD6, , , CMD_RESP_R1)) { // 1位寬
__REGb(HSMMC_BASE+HOSTCTL_OFFSET) &= ~(<<);
State = ; // 指令成功
}
} else {
if (!Hsmmc_IssueCommand(CMD6, , , CMD_RESP_R1)) { // 4位寬
__REGb(HSMMC_BASE+HOSTCTL_OFFSET) |= (<<);
State = ; // 指令成功
}
}
__REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) |= (<<); // 打開卡中斷
return State; // 傳回0為成功
}
int32_t Hsmmc_EraseBlock(uint32_t StartBlock, uint32_t EndBlock)
{
uint32_t i;
if (CardType == SD_V1 || CardType == SD_V2) {
StartBlock <<= ; // 标準卡為位元組位址
EndBlock <<= ;
} else if (CardType != SD_HC) {
return -; // 未識别的卡
}
Hsmmc_IssueCommand(CMD32, StartBlock, , CMD_RESP_R1);
Hsmmc_IssueCommand(CMD33, EndBlock, , CMD_RESP_R1);
if (!Hsmmc_IssueCommand(CMD38, , , CMD_RESP_R1B)) {
for (i=; i<; i++) {
if (Hsmmc_GetCardState() == CARD_TRAN) { // 擦除完成後傳回到transfer狀态
Debug("Erasing complete!\r\n");
return ; // 擦除成功
}
Delay_us();
}
}
Debug("Erase block failed\r\n");
return -; // 擦除失敗
}
int32_t Hsmmc_ReadBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber)
{
uint32_t Address = ;
uint32_t ReadBlock;
uint32_t i;
uint32_t j;
int32_t ErrorState;
uint32_t Temp;
if (pBuffer == || BlockNumber == ) {
return -;
}
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中斷标志
__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除錯誤中斷标志
while (BlockNumber > ) {
if (BlockNumber <= MAX_BLOCK) {
ReadBlock = BlockNumber; // 讀取的塊數小于65536 Block
BlockNumber = ; // 剩餘讀取塊數為0
} else {
ReadBlock = MAX_BLOCK; // 讀取的塊數大于65536 Block,分多次讀
BlockNumber -= ReadBlock;
}
// 根據sd主機控制器标準,按順序寫入主機控制器相應的寄存器
__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (<<) | (<<); // 最大DMA緩存大小,block為512位元組
__REGw(HSMMC_BASE+BLKCNT_OFFSET) = ReadBlock; // 寫入這次讀block數目
if (CardType == SD_HC) {
Address = BlockAddr; // SDHC卡寫入位址為block位址
} else if (CardType == SD_V1 || CardType == SD_V2) {
Address = BlockAddr << ; // 标準卡寫入位址為位元組位址
}
BlockAddr += ReadBlock; // 下一次讀塊的位址
__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Address; // 寫入指令參數
if (ReadBlock == ) {
// 設定傳輸模式,DMA禁能,讀單塊
__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (<<) | (<<) | (<<) | (<<) | (<<);
// 設定指令寄存器,單塊讀CMD17,R1回複
__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD17<<)|(<<)|(<<)|(<<)|;
} else {
// 設定傳輸模式,DMA禁能,讀多塊
__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (<<) | (<<) | (<<) | (<<) | (<<);
// 設定指令寄存器,多塊讀CMD18,R1回複
__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD18<<)|(<<)|(<<)|(<<)|;
}
ErrorState = Hsmmc_WaitForCommandDone();
if (ErrorState) {
Debug("Read Command error\r\n");
return ErrorState;
}
for (i=; i<ReadBlock; i++) {
ErrorState = Hsmmc_WaitForBufferReadReady();
if (ErrorState) {
return ErrorState;
}
if (((uint32_t)pBuffer & ) == ) {
for (j=; j</; j++) {
*(uint32_t *)pBuffer = __REG(HSMMC_BASE+BDATA_OFFSET);
pBuffer += ;
}
} else {
for (j=; j</; j++) {
Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
*pBuffer++ = (uint8_t)Temp;
*pBuffer++ = (uint8_t)(Temp>>);
*pBuffer++ = (uint8_t)(Temp>>);
*pBuffer++ = (uint8_t)(Temp>>);
}
}
}
ErrorState = Hsmmc_WaitForTransferDone();
if (ErrorState) {
Debug("Read block error\r\n");
return ErrorState;
}
}
return ; // 所有塊讀完
}
int32_t Hsmmc_WriteBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber)
{
uint32_t Address = ;
uint32_t WriteBlock;
uint32_t i;
uint32_t j;
int32_t ErrorState;
if (pBuffer == || BlockNumber == ) {
return -; // 參數錯誤
}
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中斷标志
__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除錯誤中斷标志
while (BlockNumber > ) {
if (BlockNumber <= MAX_BLOCK) {
WriteBlock = BlockNumber;// 寫入的塊數小于65536 Block
BlockNumber = ; // 剩餘寫入塊數為0
} else {
WriteBlock = MAX_BLOCK; // 寫入的塊數大于65536 Block,分多次寫
BlockNumber -= WriteBlock;
}
if (WriteBlock > ) { // 多塊寫,發送ACMD23先設定預擦除塊數
Hsmmc_IssueCommand(CMD55, RCA<<, , CMD_RESP_R1);
Hsmmc_IssueCommand(CMD23, WriteBlock, , CMD_RESP_R1);
}
// 根據sd主機控制器标準,按順序寫入主機控制器相應的寄存器
__REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (<<) | (<<); // 最大DMA緩存大小,block為512位元組
__REGw(HSMMC_BASE+BLKCNT_OFFSET) = WriteBlock; // 寫入block數目
if (CardType == SD_HC) {
Address = BlockAddr; // SDHC卡寫入位址為block位址
} else if (CardType == SD_V1 || CardType == SD_V2) {
Address = BlockAddr << ; // 标準卡寫入位址為位元組位址
}
BlockAddr += WriteBlock; // 下一次寫位址
__REG(HSMMC_BASE+ARGUMENT_OFFSET) = Address; // 寫入指令參數
if (WriteBlock == ) {
// 設定傳輸模式,DMA禁能寫單塊
__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (<<) | (<<) | (<<) | (<<) | (<<);
// 設定指令寄存器,單塊寫CMD24,R1回複
__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD24<<)|(<<)|(<<)|(<<)|;
} else {
// 設定傳輸模式,DMA禁能寫多塊
__REGw(HSMMC_BASE+TRNMOD_OFFSET) = (<<) | (<<) | (<<) | (<<) | (<<);
// 設定指令寄存器,多塊寫CMD25,R1回複
__REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD25<<)|(<<)|(<<)|(<<)|;
}
ErrorState = Hsmmc_WaitForCommandDone();
if (ErrorState) {
Debug("Write Command error\r\n");
return ErrorState;
}
for (i=; i<WriteBlock; i++) {
ErrorState = Hsmmc_WaitForBufferWriteReady();
if (ErrorState) {
return ErrorState;
}
if (((uint32_t)pBuffer & ) == ) {
for (j=; j</; j++) {
__REG(HSMMC_BASE+BDATA_OFFSET) = *(uint32_t *)pBuffer;
pBuffer += ;
}
} else {
for (j=; j</; j++) {
__REG(HSMMC_BASE+BDATA_OFFSET) = pBuffer[] + ((uint32_t)pBuffer[]<<) +
((uint32_t)pBuffer[]<<) + ((uint32_t)pBuffer[]<<);
pBuffer += ;
}
}
}
ErrorState = Hsmmc_WaitForTransferDone();
if (ErrorState) {
Debug("Write block error\r\n");
return ErrorState;
}
for (i=; i<; i++) {
if (Hsmmc_GetCardState() == CARD_TRAN) { // 需在transfer status
break; // 狀态正确
}
}
if (i == ) {
return -; // 狀态錯誤或Programming逾時
}
}
return ; // 寫完所有資料
}
int Hsmmc_Init(void)
{
int32_t Timeout;
uint32_t Capacity;
uint32_t i;
uint32_t OCR;
uint32_t Temp;
uint8_t SwitchStatus[];
SD_SCR SCR;
uint8_t CSD[];
uint32_t c_size, c_size_multi, read_bl_len;
// 設定HSMMC的接口引腳配置
#if (HSMMC_NUM == 0)
// channel 0,GPG0[0:6] = CLK, CMD, CDn, DAT[0:3]
GPG0CON_REG = ;
// pull up enable
GPG0PUD_REG = ;
GPG0DRV_REG = ;
// channel 0 clock src = SCLKEPLL = 96M
CLK_SRC4_REG = (CLK_SRC4_REG & (~(<<))) | (<<);
// channel 0 clock = SCLKEPLL/2 = 48M
CLK_DIV4_REG = (CLK_DIV4_REG & (~(<<))) | (<<);
#elif (HSMMC_NUM == 1)
// channel 1,GPG1[0:6] = CLK, CMD, CDn, DAT[0:3]
GPG1CON_REG = ;
// pull up enable
GPG1PUD_REG = ;
GPG1DRV_REG = ;
// channel 1 clock src = SCLKEPLL = 96M
CLK_SRC4_REG = (CLK_SRC4_REG & (~(<<))) | (<<);
// channel 1 clock = SCLKEPLL/2 = 48M
CLK_DIV4_REG = (CLK_DIV4_REG & (~(<<))) | (<<);
#elif (HSMMC_NUM == 2)
// channel 2,GPG2[0:6] = CLK, CMD, CDn, DAT[0:3]
GPG2CON_REG = ;
// pull up enable
GPG2PUD_REG = ;
GPG2DRV_REG = ;
// channel 2 clock src = SCLKEPLL = 96M
CLK_SRC4_REG = (CLK_SRC4_REG & (~(<<))) | (<<);
// channel 2 clock = SCLKEPLL/2 = 48M
CLK_DIV4_REG = (CLK_DIV4_REG & (~(<<))) | (<<);
#elif (HSMMC_NUM == 3)
// channel 3,GPG3[0:6] = CLK, CMD, CDn, DAT[0:3]
GPG3CON_REG = ;
// pull up enable
GPG3PUD_REG = ;
GPG3DRV_REG = ;
// channel 3 clock src = SCLKEPLL = 96M
CLK_SRC4_REG = (CLK_SRC4_REG & (~(<<))) | (<<);
// channel 3 clock = SCLKEPLL/2 = 48M
CLK_DIV4_REG = (CLK_DIV4_REG & (~(<<))) | (<<);
#endif
// software reset for all 複位主機SoC控制器,而不是複位SD卡
__REGb(HSMMC_BASE+SWRST_OFFSET) = ;
Timeout = ; // Wait max 10 ms
while (__REGb(HSMMC_BASE+SWRST_OFFSET) & (<<)) {
if (Timeout == ) {
return -; // reset timeout
}
Timeout--;
Delay_us();
}
// 上面設定的是SoC的SD控制器的時鐘,現在設定的是SD卡的時鐘
Hsmmc_SetClock(); // 400k
__REGb(HSMMC_BASE+TIMEOUTCON_OFFSET) = ; // 最大逾時時間
__REGb(HSMMC_BASE+HOSTCTL_OFFSET) &= ~(<<); // 正常速度模式
// 清除正常中斷狀态标志
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET);
// 清除錯誤中斷狀态标志
__REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET);
__REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) = ; // [14:0]中斷狀态使能
__REGw(HSMMC_BASE+ERRINTSTSEN_OFFSET) = ; // [9:0]錯誤中斷狀态使能
__REGw(HSMMC_BASE+NORINTSIGEN_OFFSET) = ; // [14:0]中斷信号使能
__REGw(HSMMC_BASE+ERRINTSIGEN_OFFSET) = ; // [9:0]錯誤中斷信号使能
// 從這裡開始和SD卡通信,通信其實就是發指令然後收響應
Hsmmc_IssueCommand(CMD0, , , CMD_RESP_NONE); // 複位所有卡到空閑狀态
CardType = UNUSABLE; // 卡類型初始化不可用
if (Hsmmc_IssueCommand(CMD8, , , CMD_RESP_R7)) { // 沒有回複,MMC/sd v1.x/not card
for (i=; i<; i++) {
// CMD55 + CMD41 = ACMD41
Hsmmc_IssueCommand(CMD55, , , CMD_RESP_R1);
if (!Hsmmc_IssueCommand(CMD41, , , CMD_RESP_R3)) { // CMD41有回複說明為sd卡
OCR = __REG(HSMMC_BASE+RSPREG0_OFFSET); // 獲得回複的OCR(操作條件寄存器)值
if (OCR & ) { // 卡上電是否完成上電流程,是否busy
CardType = SD_V1; // 正确識别出sd v1.x卡
Debug("SD card version 1.x is detected\r\n");
break;
}
} else {
// MMC卡識别
Debug("MMC card is not supported\r\n");
return -;
}
Delay_us();
}
} else { // sd v2.0
Temp = __REG(HSMMC_BASE+RSPREG0_OFFSET);
if (((Temp&) == ) && (((Temp>>)&) == )) { // 判斷卡是否支援2.7~3.3v電壓
OCR = ;
for (i=; i<; i++) {
OCR |= (<<);
Hsmmc_IssueCommand(CMD55, , , CMD_RESP_R1);
Hsmmc_IssueCommand(CMD41, OCR, , CMD_RESP_R3); // reday态
OCR = __REG(HSMMC_BASE+RSPREG0_OFFSET);
if (OCR & ) { // 卡上電是否完成上電流程,是否busy
if (OCR & (<<)) { // 判斷卡為标準卡還是高容量卡
CardType = SD_HC; // 高容量卡
Debug("SDHC card is detected\r\n");
} else {
CardType = SD_V2; // 标準卡
Debug("SD version 2.0 standard card is detected\r\n");
}
break;
}
Delay_us();
}
}
}
if (CardType == SD_HC || CardType == SD_V1 || CardType == SD_V2) {
Hsmmc_IssueCommand(CMD2, , , CMD_RESP_R2); // 請求卡發送CID(卡ID寄存器)号,進入ident
Hsmmc_IssueCommand(CMD3, , , CMD_RESP_R6); // 請求卡釋出新的RCA(卡相對位址),Stand-by狀态
RCA = (__REG(HSMMC_BASE+RSPREG0_OFFSET) >> ) & ; // 從卡回複中得到卡相對位址
Hsmmc_IssueCommand(CMD7, RCA<<, , CMD_RESP_R1); // 選擇已标記的卡,transfer狀态
Hsmmc_Get_SCR(&SCR);
if (SCR.SD_SPEC == ) { // sd 1.0 - sd 1.01
// Version 1.0 doesn't support switching
Hsmmc_SetClock(); // 設定SDCLK = 48M/2 = 24M
} else { // sd 1.10 / sd 2.0
Temp = ;
for (i=; i<; i++) {
if (Hsmmc_Switch(, , , SwitchStatus) == ) { // switch check
if (!(SwitchStatus[] & (<<))) { // Group 1, function 1 high-speed bit 273
// The high-speed function is ready
if (SwitchStatus[] & (<<)) { // Group, function 1 high-speed support bit 401
// high-speed is supported
if (Hsmmc_Switch(, , , SwitchStatus) == ) { // switch
if ((SwitchStatus[] & ) == ) { // function switch in group 1 is ok?
// result of the switch high-speed in function group 1
Debug("Switch to high speed mode: CLK @ 50M\r\n");
Hsmmc_SetClock(); // 設定SDCLK = 48M
Temp = ;
}
}
}
break;
}
}
}
if (Temp == ) {
Hsmmc_SetClock(); // 設定SDCLK = 48M/2 = 24M
}
}
if (!Hsmmc_SetBusWidth()) {
Debug("Set bus width error\r\n");
return -; // 位寬設定出錯
}
if (Hsmmc_GetCardState() == CARD_TRAN) { // 此時卡應在transfer态
if (!Hsmmc_IssueCommand(CMD16, , , CMD_RESP_R1)) { // 設定塊長度為512位元組
__REGw(HSMMC_BASE+NORINTSTS_OFFSET) = ; // 清除中斷标志
Hsmmc_Get_CSD(CSD);
if ((CSD[]>>) == ) { // CSD v1.0->sd V1.x, sd v2.00 standar
read_bl_len = CSD[] & ; // [83:80]
c_size_multi = ((CSD[] & ) << ) + ((CSD[] & ) >> ); // [49:47]
c_size = ((int32_t)(CSD[]&) << ) + ((int32_t)CSD[]<<) + (CSD[]>>); // [73:62]
Capacity = (c_size + ) << ((c_size_multi + ) + (read_bl_len-)); // block(512 byte)
} else {
c_size = ((CSD[]&) << ) + (CSD[]<<) + CSD[]; // [69:48]
// 卡容量為位元組(c_size+1)*512K byte,以1扇區512 byte字,卡的扇區數為
Capacity = (c_size+) << ;// block (512 byte)
}
Debug("Card Initialization succeed\r\n");
Debug("Capacity: %ldMB\r\n", Capacity / (* / ));
return ; // 初始化成功
}
}
}
Debug("Card Initialization failed\r\n");
return -; // 卡工作異常
}
SD協定分為CMDx和ACMDx,ACMDx是一種擴充,需要發兩個CMDx加起來才能表示一個意思,Soc通過發送指令聽取響應來判斷識别不同的卡類型,SD卡有不同的狀态,不同狀态下能夠接收的指令是不一樣的,如果發送的指令不能被目前狀态所接收,SD卡是不響應的,回複類型有8中,每種回複都有 特定的解析規則