NandFlash和iNand

NandFlash

Nand型号和命名

例如K9F2G08这个NandFlash,K9F表示三星公司的NandFlash,2G表示2Gbit,08表示该Nand有8位数据位。

Nand数据位

Nand的数据位有8位和16位之分,软件应该根据实际采用的Nand来进行设计和编写。Nand采用并行接口存储,数据位上传递的不一定是纯数据,也可能是命令,地址等。

Nand功能框图

框图如下:
Nand框图
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的框图如下: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 & (1<<4)) )
        for(i=0; i<10; i++);
}

// 发片选  
void nand_select_chip(void)
{
    unsigned long i;
    rNFCONT &= ~(1<<1);
    for(i=0; i<10; i++);
}

// 取消片选  
void nand_deselect_chip(void)
{
    unsigned long i = 0;
    rNFCONT |= (1<<1);
    for(i=0; i<10; i++);
}

// 发命令  
void nand_send_cmd(unsigned long cmd)
{
    unsigned long i = 0;

    rNFCMMD = cmd;
    for(i=0; i<10; 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 & 0xff;           
    for(i=0; i<10; i++);        

    // Column Address A8~A11  
    rNFADDR = (col >> 8) & 0x0f;        
    for(i=0; i<10; i++);

    // Row Address A12~A19 
    rNFADDR = row & 0xff;           
    for(i=0; i<10; i++);

    // Row Address A20~A27 
    rNFADDR = (row >> 8) & 0xff;
    for(i=0; i<10; i++);

    // Row Address A28~A30 
    rNFADDR = (row >> 16) & 0xff;
    for(i=0; i<10; 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=0; i<10; i++);
    ch = nand_read8();

    // 3. 取消片选  
    nand_deselect_chip();
    return ch;
}

// nandflash 初始化  
void nand_init(void)
{

    // 1. 配置nandflash  
    rNFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4)|(0<<3)|(0<<2)|(1<<1)|(0<<0);
    rNFCONT = (0<<18)|(0<<17)|(0<<16)|(0<<10)|(0<<9)|(0<<8)|(0<<7)|(0<<6)|(0x3<<1)|(1<<0);

    // 2. 配置引脚  
    rMP0_1CON = 0x22333322;
    rMP0_2CON = 0x00002222;
    rMP0_3CON = 0x22222222;

    // 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(0x00);
    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 = 0;

    // 获得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=0; i<10; i++);

    // Row Address A12~A19 
    rNFADDR = row & 0xff;                           
    for(i=0; i<10; i++);
    // Row Address A20~A27  
    rNFADDR = (row >> 8) & 0xff;
    for(i=0; i<10; i++);
    // Row Address A28~A30  
    rNFADDR = (row >> 16) & 0xff;   

    rNFSTAT |= (1<<4);          // clear RnB bit 

    nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st);
    for(i=0; i<10; i++);
    // 3. 等待就绪  
    nand_wait_idle();

    // 4. 读状态  
    unsigned char status = nand_read_status();
    if (status & 1 )
    {
        // statas[0] = 1,表示擦除失败,详见NAND Flash数据手册中 READ STATUS一节的描述
        // 取消片选信号  
        nand_deselect_chip();                       
        printf("masking bad block %d\r\n", block_num);
        return -1;
    }
    else
    {
        // status[0] = 0,表示擦除成功,返回0
        nand_deselect_chip();
        return 0;
    }
}

int nand_erase(unsigned long block_addr)
{
    int i = 0;
    if((nand_read_status() & 0x80) == 0) 
    {
        printf("Write protected.\n");
        return -1;
    }

    unsigned long row = block_addr >> 18;
    // 1. 发出片选信号  
    nand_select_chip();
    // 2. 擦除:第一个周期发命令0x60,第二个周期发块地址,第三个周期发命令0xd0  
    nand_send_cmd(NAND_CMD_BLOCK_ERASE_1st);
    for(i=0; i<10; i++);

    // Row Address A12~A19 
    rNFADDR = row & 0xff;                           
    for(i=0; i<10; i++);
    // Row Address A20~A27  
    rNFADDR = (row >> 8) & 0xff;
    for(i=0; i<10; i++);
    // Row Address A28~A30  
    rNFADDR = (row >> 16) & 0x01;   // 只要最低1bit为 A28
    for(i=0; i<10; i++);

    rNFSTAT |= (1<<4);          // clear RnB bit 

    nand_send_cmd(NAND_CMD_BLOCK_ERASE_2st);
    for(i=0; i<10; i++);
    // 3. 等待就绪  
    nand_wait_idle();

    // 4. 读状态  
    unsigned char status = nand_read_status();
    if (status & 1)
    {
        // statas[0] = 1,表示擦除失败,详见NAND Flash数据手册中 READ STATUS一节的描述
        // 取消片选信号  
        nand_deselect_chip();                       
        printf("masking bad block %d\r\n", block_addr);
        return -1;
    }
    else
    {
        // status[0] = 0,表示擦除成功,返回0
        nand_deselect_chip();
        return 0;
    }
}

// 从nand中读数据到sdram  
int copy_nand_to_sdram(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length)
{
    unsigned long i = 0;

    // 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)|(1<<4);
        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!=0; i++,length--)
        {
            *sdram_addr = nand_read8();
            sdram_addr++;
            nand_addr++;
        }
    }

    // 3. 读状态  
    unsigned char status = nand_read_status();
    if (status & 1 )
    {
        // 取消片选信号  
        nand_deselect_chip();
        printf("copy nand to sdram fail\r\n");
        return -1;
    }
    else
    {
        nand_deselect_chip();
        return 0;
    }
}

// 从sdram中写数据到nand  
int copy_sdram_to_nand(unsigned char *sdram_addr, unsigned long nand_addr, unsigned long length)
{
    unsigned long i = 0;

    // 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!=0; i++,length--)
        {
            nand_write8(*sdram_addr);
            sdram_addr++;
            nand_addr++;
        }
        rNFSTAT = (rNFSTAT)|(1<<4);
        nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
        nand_wait_idle();
    }
    // 3. 读状态  
    unsigned char status = nand_read_status();
    if (status & 1 )
    {
        // 取消片选信号  
        nand_deselect_chip();
        printf("copy sdram to nand fail\r\n");
        return -1;
    }
    else
    {
        nand_deselect_chip();
        return 0;
    }
}

int nand_page_read(unsigned int pgaddr, unsigned char *buf, unsigned int length)
{
    int i = 0;

    // 1 发出片选信号  
    nand_select_chip();

    // 2 写页读命令1st  
    nand_send_cmd(NAND_CMD_READ_1st);

    // 3 写入页地址
    rNFADDR = 0;                                        
    rNFADDR = 0;
    rNFADDR = pgaddr&0xff;
    rNFADDR = (pgaddr>>8)&0xff;
    rNFADDR = (pgaddr>>16)&0xff;

    // 4 clear RnB
    rNFSTAT |= (1<<4);

    // 5  写页读命令2st 
    nand_send_cmd(NAND_CMD_READ_2st);

    // 6 等待空闲
    nand_wait_idle();

    // 7 连续读取2KB的Page main区数据 (继续读取可读出64B的spare area数据)
    for (i=0; (i<NAND_PAGE_SIZE) && (length!=0); i++,length--)
        *buf++ = nand_read8();

    // 8 读状态  
    unsigned char status = nand_read_status();
    if (status & 1 )
    {
        // 读出错,取消片选信号,返回错误码-1  
        nand_deselect_chip();
        printf("nand random read fail\r\n");
        return -1;
    }
    else
    {
        // 读正确,取消片选,返回0
        nand_deselect_chip();
        return 0;
    }
}


int nand_page_read32(unsigned int pgaddr, unsigned int *buf, unsigned int lengthB)
{
    int i = 0;

    // 1 发出片选信号  
    nand_select_chip();

    // 2 写页读命令1st  
    nand_send_cmd(NAND_CMD_READ_1st);

    // 3 写入页地址
    rNFADDR = 0;                                        
    rNFADDR = 0;
    rNFADDR = pgaddr&0xff;
    rNFADDR = (pgaddr>>8)&0xff;
    rNFADDR = (pgaddr>>16)&0xff;

    // 4 clear RnB
    rNFSTAT |= (1<<4);

    // 5  写页读命令2st 
    nand_send_cmd(NAND_CMD_READ_2st);

    // 6 等待空闲
    nand_wait_idle();

    // 7 连续读取2KB的Page main区数据 (继续读取可读出64B的spare area数据)
    for (i=0; (i<NAND_PAGE_SIZE/4) && (lengthB!=0); i++,lengthB--)
        *buf++ = nand_read32();

    // 8 读状态  
    unsigned char status = nand_read_status();
    if (status & 1 )
    {
        // 读出错,取消片选信号,返回错误码-1  
        nand_deselect_chip();
        printf("nand random read fail\r\n");
        return -1;
    }
    else
    {
        // 读正确,取消片选,返回0
        nand_deselect_chip();
        return 0;
    }
}


int nand_page_write(unsigned int pgaddr, const unsigned char *buf, unsigned int length)
{
    int i = 0;

    // 1 发出片选信号  
    nand_select_chip();

    // 2 write cmd 1st  
    nand_send_cmd(NAND_CMD_WRITE_PAGE_1st);

    // 3 write page addr
    rNFADDR = 0;
    rNFADDR = 0;
    rNFADDR = pgaddr&0xff;
    rNFADDR = (pgaddr>>8)&0xff;
    rNFADDR = (pgaddr>>16)&0xff;

    // 4 写入一页内容
    for(; i<NAND_PAGE_SIZE && length!=0; i++,length--)
        nand_write8(*buf++);

    // 5 clear RnB
    rNFSTAT = (rNFSTAT)|(1<<4);

    // 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 & 1 )
    {
        // 取消片选信号  
        nand_deselect_chip();
        printf("nand random write fail\r\n");
        return -1;
    }
    else
    {
        nand_deselect_chip();
        return 0;
    }

}

/*
 * 函数功能:    随机读数据 
 * 参数:      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 = 0;                                        
    rNFADDR = 0;
    rNFADDR = pgaddr&0xff;
    rNFADDR = (pgaddr>>8)&0xff;
    rNFADDR = (pgaddr>>16)&0xff;
    rNFSTAT |= (1<<4);
    nand_send_cmd(NAND_CMD_READ_2st);
    nand_wait_idle();

    nand_send_cmd(NAND_CMD_RANDOM_READ_1st);
    //写入页内偏移地址
    rNFADDR = offset&0xff;                          
    rNFADDR = (offset>>8)&0xff;
    rNFSTAT = (rNFSTAT)|(1<<4);
    nand_send_cmd(NAND_CMD_RANDOM_READ_2st);

    readdata = nand_read8();

    // 3. 读状态  
    unsigned char status = nand_read_status();
    if (status & 1 )
    {
        // 取消片选信号  
        nand_deselect_chip();
        printf("nand random read fail\r\n");
        return -1;
    }
    else
    {
        nand_deselect_chip();
        *data = readdata;
        return 0;
    }
}

/*
 * 函数功能:    随机写数据
 * 参数:      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 = 0;
    rNFADDR = 0;
    rNFADDR = pgaddr&0xff;
    rNFADDR = (pgaddr>>8)&0xff;
    rNFADDR = (pgaddr>>16)&0xff;
    nand_send_cmd(NAND_CMD_RANDOM_WRITE);
    //写入页内偏移地址
    rNFADDR = offset&0xff;                  
    rNFADDR = (offset>>8)&0xff;
    nand_write8(wrdata);
    rNFSTAT = (rNFSTAT)|(1<<4);
    nand_send_cmd(NAND_CMD_WRITE_PAGE_2st);
    nand_wait_idle();

    // 3. 读状态  
    unsigned char status = nand_read_status();
    if (status & 1 )
    {
        // 取消片选信号  
        nand_deselect_chip();
        printf("nand random write fail\r\n");
        return -1;
    }
    else
    {
        nand_deselect_chip();
        return 0;
    }
}


// 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 = 0;
    unsigned char data1, data2, data3, data4;
    unsigned char buf[8];
    unsigned int bufInt[2];

    nand_init();
    nand_read_id();


    #if 0
    // 得先擦除才能写啊
    if ((ret = nand_erase(PAGE_ADDR)) == 0)
        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, 0xba);
    nand_random_write(PAGE_ADDR, OFFSET1, 0xde);
    nand_random_write(PAGE_ADDR, OFFSET2, 0xc0);
    nand_random_write(PAGE_ADDR, OFFSET3, 0xde);

    #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 != 0)
        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 != 0)
        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 : 16;    
    uint32_t SD_BUS_WIDTHS : 4;
    uint32_t SD_SECURITY : 3;   
    uint32_t DATA_STAT_AFTER_ERASE : 1;
    uint32_t SD_SPEC : 4;   
    uint32_t SCR_STRUCTURE : 4;
} 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) |= (1<<2); // sd时钟使能
        Timeout = 1000; // Wait max 10 ms
        while (!(__REGw(HSMMC_BASE+CLKCON_OFFSET) & (1<<3))) {
            // 等待SD输出时钟稳定
            if (Timeout == 0) {
                return;
            }
            Timeout--;
            Delay_us(10);
        }
    } else {
        __REGw(HSMMC_BASE+CLKCON_OFFSET) &= ~(1<<2); // sd时钟禁止
    }
}

static void Hsmmc_SetClock(uint32_t Clock)
{
    uint32_t Temp;
    uint32_t Timeout;
    uint32_t i;
    Hsmmc_ClockOn(0); // 关闭时钟   
    Temp = __REG(HSMMC_BASE+CONTROL2_OFFSET);
    // Set SCLK_MMC(48M) from SYSCON as a clock source  
    Temp = (Temp & (~(3<<4))) | (2<<4);
    Temp |= (1u<<31) | (1u<<30) | (1<<8);
    if (Clock <= 500000) {
        Temp &= ~((1<<14) | (1<<15));
        __REG(HSMMC_BASE+CONTROL3_OFFSET) = 0;
    } else {
        Temp |= ((1<<14) | (1<<15));
        __REG(HSMMC_BASE+CONTROL3_OFFSET) = (1u<<31) | (1<<23);
    }
    __REG(HSMMC_BASE+CONTROL2_OFFSET) = Temp;

    for (i=0; i<=8; i++) {
        if (Clock >= (48000000/(1<<i))) {
            break;
        }
    }
    Temp = ((1<<i) / 2) << 8; // clock div
    Temp |= (1<<0); // Internal Clock Enable
    __REGw(HSMMC_BASE+CLKCON_OFFSET) = Temp;
    Timeout = 1000; // Wait max 10 ms
    while (!(__REGw(HSMMC_BASE+CLKCON_OFFSET) & (1<<1))) {
        // 等待内部时钟振荡稳定
        if (Timeout == 0) {
            return;
        }
        Timeout--;
        Delay_us(10);
    }

    Hsmmc_ClockOn(1); // 使能时钟
}

static int32_t Hsmmc_WaitForBufferReadReady(void)
{   
    int32_t ErrorState;
    while (1) {
        if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误
            break;
        }
        if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<5)) { // 读缓存准备好
            __REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (1<<5); // 清除准备好标志
            return 0;
        }               
    }

    ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,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 (1) {
        if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误
            break;
        }
        if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<4)) { // 写缓存准备好
            __REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (1<<4); // 清除准备好标志
            return 0;
        }               
    }

    ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,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=0; i<0x20000000; i++) {
        if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误
            break;
        }
        if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<0)) {
            do {
                __REGw(HSMMC_BASE+NORINTSTS_OFFSET) = (1<<0); // 清除命令完成位
            } while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<0));             
            return 0; // 命令发送成功
        }
    }

    ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,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) = (1<<0); // 清除命令完成位
    } while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<0));

    Debug("Command error, ERRINTSTS = 0x%x ", ErrorState);  
    return ErrorState; // 命令发送出错    
}

static int32_t Hsmmc_WaitForTransferDone(void)
{
    int32_t ErrorState;
    uint32_t i;
    // 等待数据传输完成
    for (i=0; i<0x20000000; i++) {
        if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<15)) { // 出现错误
            break;
        }                                           
        if (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<1)) { // 数据传输完                                
            do {
                __REGw(HSMMC_BASE+NORINTSTS_OFFSET) |= (1<<1); // 清除传输完成位
            } while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<1));                                 
            return 0;
        }
    }

    ErrorState = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) & 0x1ff; // 可能通信错误,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) = (1<<1); // 出错后清除数据完成位
    } while (__REGw(HSMMC_BASE+NORINTSTS_OFFSET) & (1<<1));

    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=0; i<0x1000000; i++) {
        if (!(__REG(HSMMC_BASE+PRNSTS_OFFSET) & (1<<0))) {
            break;
        }
    }
    if (i == 0x1000000) {
        Debug("CMD line time out, PRNSTS: %04x\r\n", __REG(HSMMC_BASE+PRNSTS_OFFSET));
        return -1; // 命令超时
    }

    // 检查DAT线是否准备好
    if (Response == CMD_RESP_R1B) { // R1b回复通过DAT0反馈忙信号
        for (i=0; i<0x10000000; i++) {
            if (!(__REG(HSMMC_BASE+PRNSTS_OFFSET) & (1<<1))) {
                break;
            }       
        }
        if (i == 0x10000000) {
            Debug("Data line time out, PRNSTS: %04x\r\n", __REG(HSMMC_BASE+PRNSTS_OFFSET));         
            return -2;
        }       
    }

    __REG(HSMMC_BASE+ARGUMENT_OFFSET) = Arg; // 写入命令参数

    Value = (Cmd << 8); // command index
    // CMD12可终止传输
    if (Cmd == 0x12) {
        Value |= (0x3 << 6); // command type
    }
    if (Data) {
        Value |= (1 << 5); // 需使用DAT线作为传输等
    }   

    switch (Response) {
    case CMD_RESP_NONE:
        Value |= (0<<4) | (0<<3) | 0x0; // 没有回复,不检查命令及CRC
        break;
    case CMD_RESP_R1:
    case CMD_RESP_R5:
    case CMD_RESP_R6:
    case CMD_RESP_R7:       
        Value |= (1<<4) | (1<<3) | 0x2; // 检查回复中的命令,CRC
        break;
    case CMD_RESP_R2:
        Value |= (0<<4) | (1<<3) | 0x1; // 回复长度为136位,包含CRC
        break;
    case CMD_RESP_R3:
    case CMD_RESP_R4:
        Value |= (0<<4) | (0<<3) | 0x2; // 回复长度48位,不包含命令及CRC
        break;
    case CMD_RESP_R1B:
        Value |= (1<<4) | (1<<3) | 0x3; // 回复带忙信号,会占用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) = (7<<12) | (64<<0); // 最大DMA缓存大小,block为512位64字节          
    __REGw(HSMMC_BASE+BLKCNT_OFFSET) = 1; // 写入这次读1 block的sd状态数据
    Temp = (Mode << 31U) | 0xffffff;
    Temp &= ~(0xf<<(Group * 4));
    Temp |= Function << (Group * 4);
    __REG(HSMMC_BASE+ARGUMENT_OFFSET) = Temp; // 写入命令参数 

    // DMA禁能,读单块        
    __REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);  
    // 设置命令寄存器,SWITCH_FUNC CMD6,R1回复
    __REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD6<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
    ErrorState = Hsmmc_WaitForCommandDone();
    if (ErrorState) {
        Debug("CMD6 error\r\n");
        return ErrorState;
    }

    ErrorState = Hsmmc_WaitForBufferReadReady();
    if (ErrorState) {
        return ErrorState;
    }

    pStatus += 64 - 1;
    for (i=0; i<64/4; i++) {
        Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
        *pStatus-- = (uint8_t)Temp;
        *pStatus-- = (uint8_t)(Temp>>8);
        *pStatus-- = (uint8_t)(Temp>>16);
        *pStatus-- = (uint8_t)(Temp>>24);           
    }

    ErrorState = Hsmmc_WaitForTransferDone();
    if (ErrorState) {
        Debug("Get sd status error\r\n");
        return ErrorState;
    }   
    return 0;   
}

// 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 -1; // 卡状态错误
        }       
        Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1);

        __REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (64<<0); // 最大DMA缓存大小,block为512位64字节          
        __REGw(HSMMC_BASE+BLKCNT_OFFSET) = 1; // 写入这次读1 block的sd状态数据
        __REG(HSMMC_BASE+ARGUMENT_OFFSET) = 0; // 写入命令参数    

        // DMA禁能,读单块        
        __REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);  
        // 设置命令寄存器,读状态命令CMD13,R1回复
        __REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD13<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
        ErrorState = Hsmmc_WaitForCommandDone();
        if (ErrorState) {
            Debug("CMD13 error\r\n");
            return ErrorState;
        }

        ErrorState = Hsmmc_WaitForBufferReadReady();
        if (ErrorState) {
            return ErrorState;
        }

        pStatus += 64 - 1;
        for (i=0; i<64/4; i++) {
            Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
            *pStatus-- = (uint8_t)Temp;
            *pStatus-- = (uint8_t)(Temp>>8);
            *pStatus-- = (uint8_t)(Temp>>16);
            *pStatus-- = (uint8_t)(Temp>>24);           
        }

        ErrorState = Hsmmc_WaitForTransferDone();
        if (ErrorState) {
            Debug("Get sd status error\r\n");
            return ErrorState;
        }

        return 0;
    }
    return -1; // 非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<<16, 0, CMD_RESP_R1); 
    __REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (8<<0); // 最大DMA缓存大小,block为64位8字节         
    __REGw(HSMMC_BASE+BLKCNT_OFFSET) = 1; // 写入这次读1 block的sd状态数据
    __REG(HSMMC_BASE+ARGUMENT_OFFSET) = 0; // 写入命令参数    

    // DMA禁能,读单块        
    __REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);  
    // 设置命令寄存器,read SD Configuration CMD51,R1回复
    __REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD51<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
    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) - 1;
    for (i=0; i<8/4; i++) {
        Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
        *pBuffer-- = (uint8_t)Temp;
        *pBuffer-- = (uint8_t)(Temp>>8);
        *pBuffer-- = (uint8_t)(Temp>>16);
        *pBuffer-- = (uint8_t)(Temp>>24);       
    }

    ErrorState = Hsmmc_WaitForTransferDone();
    if (ErrorState) {
        Debug("Get SCR register error\r\n");
        return ErrorState;
    }

    return 0;
}

// Asks the selected card to send its cardspecific data
int32_t Hsmmc_Get_CSD(uint8_t *pCSD)
{
    uint32_t i;
    uint32_t Response[4];
    int32_t State = -1;

    if (CardType != SD_HC && CardType != SD_V1 && CardType != SD_V2) {
        return State; // 未识别的卡
    }
    // 取消卡选择,任何卡均不回复,已选择的卡通过RCA=0取消选择,
    // 卡回到stand-by状态
    Hsmmc_IssueCommand(CMD7, 0, 0, CMD_RESP_NONE);
    for (i=0; i<1000; i++) {
        if (Hsmmc_GetCardState() == CARD_STBY) { // CMD9命令需在standy-by status                
            break; // 状态正确
        }
        Delay_us(100);
    }
    if (i == 1000) {
        return State; // 状态错误
    }   

    // 请求已标记卡发送卡特定数据(CSD),获得卡信息
    if (!Hsmmc_IssueCommand(CMD9, RCA<<16, 0, CMD_RESP_R2)) {
        pCSD++; // 跳过第一字节,CSD中[127:8]位对位寄存器中的[119:0]
        Response[0] = __REG(HSMMC_BASE+RSPREG0_OFFSET);
        Response[1] = __REG(HSMMC_BASE+RSPREG1_OFFSET);
        Response[2] = __REG(HSMMC_BASE+RSPREG2_OFFSET);
        Response[3] = __REG(HSMMC_BASE+RSPREG3_OFFSET); 
        for (i=0; i<15; i++) { // 拷贝回复寄存器中的[119:0]到pCSD中
            *pCSD++ = ((uint8_t *)Response)[i];
        }   
        State = 0; // CSD获取成功
    }

    Hsmmc_IssueCommand(CMD7, RCA<<16, 0, CMD_RESP_R1); // 选择卡,卡回到transfer状态
    return State;
}

// R1回复中包含了32位的card state,卡识别后,可在任一状态通过CMD13获得卡状态
int32_t Hsmmc_GetCardState(void)
{
    if (Hsmmc_IssueCommand(CMD13, RCA<<16, 0, CMD_RESP_R1)) {
        return -1; // 卡出错
    } else {
        return ((__REG(HSMMC_BASE+RSPREG0_OFFSET)>>9) & 0xf); // 返回R1回复中的[12:9]卡状态
    }
}

static int32_t Hsmmc_SetBusWidth(uint8_t Width)
{
    int32_t State;
    if ((Width != 1) || (Width != 4)) {
        return -1;
    }
    State = -1; // 设置初始为未成功
    __REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) &= ~(1<<8); // 关闭卡中断
    Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1);
    if (Width == 1) {
        if (!Hsmmc_IssueCommand(CMD6, 0, 0, CMD_RESP_R1)) { // 1位宽
            __REGb(HSMMC_BASE+HOSTCTL_OFFSET) &= ~(1<<1);
            State = 0; // 命令成功
        }
    } else {
        if (!Hsmmc_IssueCommand(CMD6, 2, 0, CMD_RESP_R1)) { // 4位宽
            __REGb(HSMMC_BASE+HOSTCTL_OFFSET) |= (1<<1);
            State = 0; // 命令成功
        }
    }
    __REGw(HSMMC_BASE+NORINTSTSEN_OFFSET) |= (1<<8); // 打开卡中断   
    return State; // 返回0为成功
}

int32_t Hsmmc_EraseBlock(uint32_t StartBlock, uint32_t EndBlock)
{
    uint32_t i;

    if (CardType == SD_V1 || CardType == SD_V2) {
        StartBlock <<= 9; // 标准卡为字节地址
        EndBlock <<= 9;
    } else if (CardType != SD_HC) {
        return -1; // 未识别的卡
    }   
    Hsmmc_IssueCommand(CMD32, StartBlock, 0, CMD_RESP_R1);
    Hsmmc_IssueCommand(CMD33, EndBlock, 0, CMD_RESP_R1);
    if (!Hsmmc_IssueCommand(CMD38, 0, 0, CMD_RESP_R1B)) {
        for (i=0; i<0x10000; i++) {
            if (Hsmmc_GetCardState() == CARD_TRAN) { // 擦除完成后返回到transfer状态
                Debug("Erasing complete!\r\n");
                return 0; // 擦除成功               
            }
            Delay_us(1000);         
        }       
    }       

    Debug("Erase block failed\r\n");
    return -1; // 擦除失败
}

int32_t Hsmmc_ReadBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber)
{
    uint32_t Address = 0;
    uint32_t ReadBlock;
    uint32_t i;
    uint32_t j;
    int32_t ErrorState;
    uint32_t Temp;

    if (pBuffer == 0 || BlockNumber == 0) {
        return -1;
    }

    __REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志
    __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志  

    while (BlockNumber > 0) {
        if (BlockNumber <= MAX_BLOCK) {
            ReadBlock = BlockNumber; // 读取的块数小于65536 Block
            BlockNumber = 0; // 剩余读取块数为0
        } else {
            ReadBlock = MAX_BLOCK; // 读取的块数大于65536 Block,分多次读
            BlockNumber -= ReadBlock;
        }
        // 根据sd主机控制器标准,按顺序写入主机控制器相应的寄存器     
        __REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (512<<0); // 最大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 << 9; // 标准卡写入地址为字节地址
        }   
        BlockAddr += ReadBlock; // 下一次读块的地址
        __REG(HSMMC_BASE+ARGUMENT_OFFSET) = Address; // 写入命令参数      

        if (ReadBlock == 1) {
            // 设置传输模式,DMA禁能,读单块
            __REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (1<<4) | (0<<2) | (1<<1) | (0<<0);
            // 设置命令寄存器,单块读CMD17,R1回复
            __REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD17<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;
        } else {
            // 设置传输模式,DMA禁能,读多块         
            __REGw(HSMMC_BASE+TRNMOD_OFFSET) = (1<<5) | (1<<4) | (1<<2) | (1<<1) | (0<<0);
            // 设置命令寄存器,多块读CMD18,R1回复    
            __REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD18<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;         
        }   
        ErrorState = Hsmmc_WaitForCommandDone();
        if (ErrorState) {
            Debug("Read Command error\r\n");
            return ErrorState;
        }   
        for (i=0; i<ReadBlock; i++) {
            ErrorState = Hsmmc_WaitForBufferReadReady();
            if (ErrorState) {
                return ErrorState;
            }
            if (((uint32_t)pBuffer & 0x3) == 0) {   
                for (j=0; j<512/4; j++) {
                    *(uint32_t *)pBuffer = __REG(HSMMC_BASE+BDATA_OFFSET);
                    pBuffer += 4;
                }
            } else {
                for (j=0; j<512/4; j++) {
                    Temp = __REG(HSMMC_BASE+BDATA_OFFSET);
                    *pBuffer++ = (uint8_t)Temp;
                    *pBuffer++ = (uint8_t)(Temp>>8);
                    *pBuffer++ = (uint8_t)(Temp>>16);
                    *pBuffer++ = (uint8_t)(Temp>>24);
                }
            }                       
        }

        ErrorState = Hsmmc_WaitForTransferDone();
        if (ErrorState) {
            Debug("Read block error\r\n");
            return ErrorState;
        }   
    }
    return 0; // 所有块读完
}

int32_t Hsmmc_WriteBlock(uint8_t *pBuffer, uint32_t BlockAddr, uint32_t BlockNumber)
{
    uint32_t Address = 0;
    uint32_t WriteBlock;
    uint32_t i;
    uint32_t j;
    int32_t ErrorState;

    if (pBuffer == 0 || BlockNumber == 0) {
        return -1; // 参数错误
    }   

    __REGw(HSMMC_BASE+NORINTSTS_OFFSET) = __REGw(HSMMC_BASE+NORINTSTS_OFFSET); // 清除中断标志
    __REGw(HSMMC_BASE+ERRINTSTS_OFFSET) = __REGw(HSMMC_BASE+ERRINTSTS_OFFSET); // 清除错误中断标志

    while (BlockNumber > 0) {   
        if (BlockNumber <= MAX_BLOCK) {
            WriteBlock = BlockNumber;// 写入的块数小于65536 Block
            BlockNumber = 0; // 剩余写入块数为0
        } else {
            WriteBlock = MAX_BLOCK; // 写入的块数大于65536 Block,分多次写
            BlockNumber -= WriteBlock;
        }
        if (WriteBlock > 1) { // 多块写,发送ACMD23先设置预擦除块数
            Hsmmc_IssueCommand(CMD55, RCA<<16, 0, CMD_RESP_R1);
            Hsmmc_IssueCommand(CMD23, WriteBlock, 0, CMD_RESP_R1);
        }

        // 根据sd主机控制器标准,按顺序写入主机控制器相应的寄存器         
        __REGw(HSMMC_BASE+BLKSIZE_OFFSET) = (7<<12) | (512<<0); // 最大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 << 9; // 标准卡写入地址为字节地址
        }
        BlockAddr += WriteBlock; // 下一次写地址
        __REG(HSMMC_BASE+ARGUMENT_OFFSET) = Address; // 写入命令参数          

        if (WriteBlock == 1) {
            // 设置传输模式,DMA禁能写单块
            __REGw(HSMMC_BASE+TRNMOD_OFFSET) = (0<<5) | (0<<4) | (0<<2) | (1<<1) | (0<<0);
            // 设置命令寄存器,单块写CMD24,R1回复
            __REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD24<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;         
        } else {
            // 设置传输模式,DMA禁能写多块
            __REGw(HSMMC_BASE+TRNMOD_OFFSET) = (1<<5) | (0<<4) | (1<<2) | (1<<1) | (0<<0);
            // 设置命令寄存器,多块写CMD25,R1回复
            __REGw(HSMMC_BASE+CMDREG_OFFSET) = (CMD25<<8)|(1<<5)|(1<<4)|(1<<3)|0x2;                 
        }
        ErrorState = Hsmmc_WaitForCommandDone();
        if (ErrorState) {
            Debug("Write Command error\r\n");
            return ErrorState;
        }   

        for (i=0; i<WriteBlock; i++) {
            ErrorState = Hsmmc_WaitForBufferWriteReady();
            if (ErrorState) {
                return ErrorState;
            }
            if (((uint32_t)pBuffer & 0x3) == 0) {
                for (j=0; j<512/4; j++) {
                    __REG(HSMMC_BASE+BDATA_OFFSET) = *(uint32_t *)pBuffer;
                    pBuffer += 4;
                }
            } else {
                for (j=0; j<512/4; j++) {
                    __REG(HSMMC_BASE+BDATA_OFFSET) = pBuffer[0] + ((uint32_t)pBuffer[1]<<8) +
                    ((uint32_t)pBuffer[2]<<16) + ((uint32_t)pBuffer[3]<<24);
                    pBuffer += 4;
                }
            }   
        }

        ErrorState = Hsmmc_WaitForTransferDone();
        if (ErrorState) {
            Debug("Write block error\r\n");
            return ErrorState;
        }
        for (i=0; i<0x10000000; i++) {
            if (Hsmmc_GetCardState() == CARD_TRAN) { // 需在transfer status
                break; // 状态正确
            }
        }
        if (i == 0x10000000) {
            return -3; // 状态错误或Programming超时
        }       
    }
    return 0; // 写完所有数据
}

int Hsmmc_Init(void)
{
    int32_t Timeout;
    uint32_t Capacity;
    uint32_t i;
    uint32_t OCR;
    uint32_t Temp;
    uint8_t SwitchStatus[64];
    SD_SCR SCR;
    uint8_t CSD[16];
    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 = 0x2222222;
    // pull up enable
    GPG0PUD_REG = 0x2aaa;
    GPG0DRV_REG = 0x3fff;
    // channel 0 clock src = SCLKEPLL = 96M
    CLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<0))) | (0x7<<0);
    // channel 0 clock = SCLKEPLL/2 = 48M
    CLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<0))) | (0x1<<0);    

#elif (HSMMC_NUM == 1)
    // channel 1,GPG1[0:6] = CLK, CMD, CDn, DAT[0:3]
    GPG1CON_REG = 0x2222222;
    // pull up enable
    GPG1PUD_REG = 0x2aaa;
    GPG1DRV_REG = 0x3fff;
    // channel 1 clock src = SCLKEPLL = 96M
    CLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<4))) | (0x7<<4);
    // channel 1 clock = SCLKEPLL/2 = 48M
    CLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<4))) | (0x1<<4);

#elif (HSMMC_NUM == 2)
    // channel 2,GPG2[0:6] = CLK, CMD, CDn, DAT[0:3]
    GPG2CON_REG = 0x2222222;
    // pull up enable
    GPG2PUD_REG = 0x2aaa;
    GPG2DRV_REG = 0x3fff;
    // channel 2 clock src = SCLKEPLL = 96M
    CLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<8))) | (0x7<<8);
    // channel 2 clock = SCLKEPLL/2 = 48M
    CLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<8))) | (0x1<<8);    

#elif (HSMMC_NUM == 3)
    // channel 3,GPG3[0:6] = CLK, CMD, CDn, DAT[0:3]
    GPG3CON_REG = 0x2222222;
    // pull up enable
    GPG3PUD_REG = 0x2aaa;
    GPG3DRV_REG = 0x3fff;
    // channel 3 clock src = SCLKEPLL = 96M
    CLK_SRC4_REG = (CLK_SRC4_REG & (~(0xf<<12))) | (0x7<<12);
    // channel 3 clock = SCLKEPLL/2 = 48M
    CLK_DIV4_REG = (CLK_DIV4_REG & (~(0xf<<12))) | (0x1<<12);  

#endif
    // software reset for all 复位主机SoC控制器,而不是复位SD卡
    __REGb(HSMMC_BASE+SWRST_OFFSET) = 0x1;
    Timeout = 1000; // Wait max 10 ms
    while (__REGb(HSMMC_BASE+SWRST_OFFSET) & (1<<0)) {
        if (Timeout == 0) {
            return -1; // reset timeout
        }
        Timeout--;
        Delay_us(10);
    }   
    // 上面设置的是SoC的SD控制器的时钟,现在设置的是SD卡的时钟
    Hsmmc_SetClock(400000); // 400k
    __REGb(HSMMC_BASE+TIMEOUTCON_OFFSET) = 0xe; // 最大超时时间
    __REGb(HSMMC_BASE+HOSTCTL_OFFSET) &= ~(1<<2); // 正常速度模式
    // 清除正常中断状态标志
    __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) = 0x7fff; // [14:0]中断状态使能
    __REGw(HSMMC_BASE+ERRINTSTSEN_OFFSET) = 0x3ff; // [9:0]错误中断状态使能
    __REGw(HSMMC_BASE+NORINTSIGEN_OFFSET) = 0x7fff; // [14:0]中断信号使能 
    __REGw(HSMMC_BASE+ERRINTSIGEN_OFFSET) = 0x3ff; // [9:0]错误中断信号使能

    // 从这里开始和SD卡通信,通信其实就是发命令然后收响应
    Hsmmc_IssueCommand(CMD0, 0, 0, CMD_RESP_NONE); // 复位所有卡到空闲状态    

    CardType = UNUSABLE; // 卡类型初始化不可用
    if (Hsmmc_IssueCommand(CMD8, 0x1aa, 0, CMD_RESP_R7)) { // 没有回复,MMC/sd v1.x/not card
            for (i=0; i<100; i++) {
                // CMD55 + CMD41 = ACMD41
                Hsmmc_IssueCommand(CMD55, 0, 0, CMD_RESP_R1);
                if (!Hsmmc_IssueCommand(CMD41, 0, 0, CMD_RESP_R3)) { // CMD41有回复说明为sd卡
                    OCR = __REG(HSMMC_BASE+RSPREG0_OFFSET); // 获得回复的OCR(操作条件寄存器)值
                    if (OCR & 0x80000000) { // 卡上电是否完成上电流程,是否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 -1;
                }
                Delay_us(1000);             
            }
    } else { // sd v2.0
        Temp = __REG(HSMMC_BASE+RSPREG0_OFFSET);
        if (((Temp&0xff) == 0xaa) && (((Temp>>8)&0xf) == 0x1)) { // 判断卡是否支持2.7~3.3v电压
            OCR = 0;
            for (i=0; i<100; i++) {
                OCR |= (1<<30);
                Hsmmc_IssueCommand(CMD55, 0, 0, CMD_RESP_R1);
                Hsmmc_IssueCommand(CMD41, OCR, 0, CMD_RESP_R3); // reday态
                OCR = __REG(HSMMC_BASE+RSPREG0_OFFSET);
                if (OCR & 0x80000000) { // 卡上电是否完成上电流程,是否busy
                    if (OCR & (1<<30)) { // 判断卡为标准卡还是高容量卡
                        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(1000);
            }
        }
    }
    if (CardType == SD_HC || CardType == SD_V1 || CardType == SD_V2) {
        Hsmmc_IssueCommand(CMD2, 0, 0, CMD_RESP_R2); // 请求卡发送CID(卡ID寄存器)号,进入ident
        Hsmmc_IssueCommand(CMD3, 0, 0, CMD_RESP_R6); // 请求卡发布新的RCA(卡相对地址),Stand-by状态
        RCA = (__REG(HSMMC_BASE+RSPREG0_OFFSET) >> 16) & 0xffff; // 从卡回复中得到卡相对地址
        Hsmmc_IssueCommand(CMD7, RCA<<16, 0, CMD_RESP_R1); // 选择已标记的卡,transfer状态

        Hsmmc_Get_SCR(&SCR);
        if (SCR.SD_SPEC == 0) { // sd 1.0 - sd 1.01
            // Version 1.0 doesn't support switching
            Hsmmc_SetClock(24000000); // 设置SDCLK = 48M/2 = 24M          
        } else { // sd 1.10 / sd 2.0
            Temp = 0;
            for (i=0; i<4; i++) {
                if (Hsmmc_Switch(0, 0, 1, SwitchStatus) == 0) { // switch check
                    if (!(SwitchStatus[34] & (1<<1))) { // Group 1, function 1 high-speed bit 273                   
                        // The high-speed function is ready
                        if (SwitchStatus[50] & (1<<1)) { // Group, function 1 high-speed support bit 401
                            // high-speed is supported
                            if (Hsmmc_Switch(1, 0, 1, SwitchStatus) == 0) { // switch
                                if ((SwitchStatus[47] & 0xf) == 1) { // 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(48000000); // 设置SDCLK = 48M  
                                    Temp = 1;                                   
                                }
                            }
                        }
                        break;
                    }
                } 
            }
            if (Temp == 0) {
                Hsmmc_SetClock(24000000); // 设置SDCLK = 48M/2 = 24M
            }
        }

        if (!Hsmmc_SetBusWidth(4)) {
            Debug("Set bus width error\r\n");
            return -1; // 位宽设置出错
        }
        if (Hsmmc_GetCardState() == CARD_TRAN) { // 此时卡应在transfer态
            if (!Hsmmc_IssueCommand(CMD16, 512, 0, CMD_RESP_R1)) { // 设置块长度为512字节
                __REGw(HSMMC_BASE+NORINTSTS_OFFSET) = 0xffff; // 清除中断标志
                Hsmmc_Get_CSD(CSD);
                if ((CSD[15]>>6) == 0) { // CSD v1.0->sd V1.x, sd v2.00 standar
                    read_bl_len = CSD[10] & 0xf; // [83:80]
                    c_size_multi = ((CSD[6] & 0x3) << 1) + ((CSD[5] & 0x80) >> 7); // [49:47]
                    c_size = ((int32_t)(CSD[9]&0x3) << 10) + ((int32_t)CSD[8]<<2) + (CSD[7]>>6); // [73:62]             
                    Capacity = (c_size + 1) << ((c_size_multi + 2) + (read_bl_len-9)); // block(512 byte)
                } else {
                    c_size = ((CSD[8]&0x3f) << 16) + (CSD[7]<<8) + CSD[6]; // [69:48]
                    // 卡容量为字节(c_size+1)*512K byte,以1扇区512 byte字,卡的扇区数为      
                    Capacity = (c_size+1) << 10;// block (512 byte)
                }
                Debug("Card Initialization succeed\r\n");               
                Debug("Capacity: %ldMB\r\n", Capacity / (1024*1024 / 512));
                return 0; // 初始化成功                          
            }
        }
    }
    Debug("Card Initialization failed\r\n");
    return -1; // 卡工作异常
}

SD协议分为CMDx和ACMDx,ACMDx是一种扩展,需要发两个CMDx加起来才能表示一个意思,Soc通过发送命令听取响应来判断识别不同的卡类型,SD卡有不同的状态,不同状态下能够接收的命令是不一样的,如果发送的命令不能被当前状态所接收,SD卡是不响应的,回复类型有8中,每种回复都有 特定的解析规则

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值