天天看點

NandFlash和iNandNandFlashiNand

NandFlash

Nand型号和命名

例如K9F2G08這個NandFlash,K9F表示三星公司的NandFlash,2G表示2Gbit,08表示該Nand有8位資料位。

Nand資料位

Nand的資料位有8位和16位之分,軟體應該根據實際采用的Nand來進行設計和編寫。Nand采用并行接口存儲,資料位上傳遞的不一定是純資料,也可能是指令,位址等。

Nand功能框圖

框圖如下:

NandFlash和iNandNandFlashiNand

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的框圖如下:

NandFlash和iNandNandFlashiNand

程式設計中就是通過讀寫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中,每種回複都有 特定的解析規則