1 inand的介绍
1.1 inand由接口电路和系统储存组成。
1.2 inand有8根IO线,一根命令线,一根时钟线。SD卡有四根IO线,一根命令线,一根时钟线。
1.3 inand接口电路的功能
1.3.1提供eMMC接口协议和SOC对接。
1.3.2提供eMMC校验的相关逻辑,简化了SOC的编程。
1.3.3 内部使用了MLC的储存颗粒,价格便宜,性价比高。
1.3.4 提供cache机制,所以inand的读取速度快。
2 inand的操作
2.1 支持1,4,8线并行传输
2.2 通过CMD线来传输命令,通过CLK线来传输时钟。
2.3 soc可以通过寻址或者广播向一个或者多个SD卡发送命令,SD卡接收到命令之后会返回一个响应。
2.4 soc也可以进行多块读写,soc先发送一个多块读写的命令,之后SD卡回应一个响应,当停止读写的时候,SOC会发送一个停止命令,之后SD卡发送一个响应,多块读写结束。如图
2.5 命令牌图
2.6 SD卡结构图
3 代码分析
3.1初始化流程图
3.2 传输图
3.3 Hsmmc.h文件
#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__*/
3.4 Hsmmc.c文件
#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) |&