NandFlash
Nand型号和命名
例如K9F2G08这个NandFlash,K9F表示三星公司的NandFlash,2G表示2Gbit,08表示该Nand有8位数据位。
Nand数据位
Nand的数据位有8位和16位之分,软件应该根据实际采用的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的框图如下:
编程中就是通过读写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中,每种回复都有 特定的解析规则