📄 sd.c
字号:
}
/************************************************************************
函数名称: void SD_SPIDelay()
** 功能描述: SPI总线延时
** 输 入: INT8U value: 延时值,不超过255
** 输 出: 无
*************************************************************************/
void SD_SPIDelay(int value)
{
int i;
for (i = 0; i < value; i++)
SPI_SendByte(0xFF); /* 发送0xFF clock out 0xFF */
}
/**************************************************************************
** 函数名称: void SD_Power()
** 功能描述: 对卡先下电,再上电
** 输 入: 无
** 输 出: 无
****************************************************************************/
void SD_Power(void)
{
int i;
PINSEL0 &= ~(0x03 << 8); /* 设置 SCK 口为GPIO口 */
IODIR |= SPI_SCK; /* 设置 SCK 口为输出口 */
IOCLR = SPI_SCK; /* 置 SCK 为低电平 */
PINSEL0 &= ~(0x03 << 10); /* 设置 MISO 口为GPIO口 */
IODIR |= SPI_MISO; /* 设置 MISO 口为输出口 */
IOCLR = SPI_MISO; /* 置 MISO 为低电平 */
PINSEL0 &= ~(0x03 << 12); /* 设置 MOSI 口为GPIO口 */
IODIR |= SPI_MOSI; /* 设置 MOSI 口为输出口 */
IOCLR = SPI_MOSI; /* 置 MISO 为低电平 */
PINSEL0 &= ~(0x03 << 20); /* 设置 CS 口为GPIO口 */
IODIR |= SPI_CS; /* 设置 CS 口为输出口 */
IOCLR |= SPI_CS; /* 置 CS 为低电平 */
for(i = 0; i < 0x9000; i++);
}
/****************************************************************************
** 函数名称: void SD_HardWareInit()
** 功能描述: 初始化访问SD卡的硬件条件
** 输 入: 无
** 输 出: 无
*****************************************************************************/
void SD_HardWareInit(void)
{
SD_Power();
PINSEL0 &= ~((0x03 << 8) + (0x03 << 10) + (0x03 << 12) + (0x03 << 14));
PINSEL0 |= (0x01 << 8) + (0x01 << 10) + (0x01 << 12) + (0x01 << 14);
SD_INSERT_GPIO();
SD_INSERT_IN();
SD_WP_GPIO();
SD_WP_IN();
S0SPCCR = 128; /* 设置SPI时钟分频值为128 */
S0SPCR = (0 << 3) | // CPHA = 0, 数据在SCK 的第一个时钟沿采样
(1 << 4) | // CPOL = 1, SCK 为低有效
(1 << 5) | // MSTR = 1, SPI 处于主模式
(0 << 6) | // LSBF = 0, SPI 数据传输MSB (位7)在先
(0 << 7); // SPIE = 0, SPI 中断被禁止
}
/***********************************************************
** 函数名称: void SPI_ClkToMax()
** 功能描述: 设置SPI的clock到最大值
** 输 入: 无
** 输 出: 无
************************************************************/
void SPI_ClkToMax(void)
{
S0SPCCR = 8; /* 设置SPI时钟分频值为8 Set the value of dividing frequency to 8 */
}
/*****************************************************************************
** 函数名称: void SPI_CS_Assert()
** 功能描述: 片选SPI从机
** 输 入: 无
** 输 出: 无
******************************************************************************/
void SPI_CS_Assert(void)
{
SPI_CS_CLR(); /* 片选SPI从机 select the SPI slave */
}
/**********************************************************************
** 函数名称: void SPI_CS_Deassert()
** 功能描述: 不片选SPI从机
** 输 入: 无
** 输 出: 无
**************************************************************************/
void SPI_CS_Deassert(void)
{
SPI_CS_SET(); /* 不片选SPI从机 not select the SPI slave */
}
/*************************************************************************
** 函数名称: INT8U SD_SendCmd()
** 功能描述: 向卡发送命令,并取得响应
** 输 入: INT8U cmd : 命令字
INT8U *param : 命令参数,长度为4字节
INT8U resptype : 响应类型
INT8U *resp : 响应,长度为1-5字节
** 输 出: 0: 正确 >0: 错误码 Output: 0: right >0: error code
*************************************************************************/
unsigned char SD_SendCmd(unsigned char cmd, unsigned char *param, unsigned char resptype, unsigned char *resp)
{
int i,rlen;
unsigned char tmp;
SPI_CS_Assert();
SPI_SendByte((cmd & 0x3F) | 0x40); /* 发送命令头和命令字 send command header and word */
for (i = 0; i < 4; i++)
SPI_SendByte(param[i]); /* 发送参数 send parameters */
#if SD_CRC_EN
tmp = SD_GetCmdByte6((cmd & 0x3F) | 0x40, param);
SPI_SendByte(tmp);
#else
SPI_SendByte(0x95); /* CRC校验码,只用于第1个命令 CRC,only used for the first command */
#endif
rlen = 0;
switch (resptype) /* 根据不同的命令,得到不同的响应长度 */
{ /* according various command,get the various response length */
case R1:
case R1B: rlen = 1; break;
case R2: rlen = 2; break;
case R3: rlen = 5; break;
default: SPI_SendByte(0xFF);
SPI_CS_Deassert();
return SD_ERR_CMD_RESPTYPE; /* 返回命令响应类型错误 return error of command response type */
}
i = 0;
do /* 等待响应,响应的开始位为0 */
{ /* Wait for a response,a response is a start bit(zero) */
tmp = SPI_RecByte();
i++;
}while (((tmp & 0x80) != 0) && (i < SD_CMD_TIMEOUT));
if (i >= SD_CMD_TIMEOUT)
{
SPI_CS_Deassert();
return SD_ERR_CMD_TIMEOUT; /* 返回命令超时 return response timeout of command */
}
for (i = rlen - 1; i >= 0; i--)
{
resp[i] = tmp;
tmp = SPI_RecByte(); /* 循环的最后发送8clock at the last recycle,clock out 8 clock */
}
SPI_CS_Deassert();
// Uart0_SendByte(0xc1);
return SD_NO_ERR;
}
/**********************************************************************
** 函数名称: INT8U SD_ResetSD()
** 功能描述: 复位SD/MMC卡
** 输 入: 无
** 输 出: 0: 正确 >0: 错误码
**********************************************************************/
unsigned char SD_ResetSD(void)
{
unsigned char param[4] = {0,0,0,0},resp;
return (SD_SendCmd(CMD0, param, CMD0_R, &resp)); /* 复位命令 command that reset card */
}
/**********************************************************************
** 函数名称: INT8U SD_ActiveInit()
** 功能描述: 激活卡,并获得卡型
** 输 入: 无
** 输 出: 0: 成功 >0: 错误码
** 函数说明: 该命令不断重复发送到SD卡,直到响应R1的Bit0(Idle)位为0,表示SD卡内部初始化处理完成。
当响应的Idle位为0时,SD卡就完全进入SPI模式了。当然重复发送命令CMD1是有次数限制的,
最大次数为宏定义SD_IDLE_WAIT_MAX.
***********************************************************************/
unsigned char SD_ActiveInit(void)
{
unsigned char param[4] = {0,0,0,0},resp[5],ret;
unsigned int i = 0;
do
{ /* 发出CMD1, 查询卡的状态, send CMD1 to poll card status */
ret = SD_SendCmd(CMD1, param, CMD1_R, resp);
if (ret != SD_NO_ERR)
{
return ret;
}
i ++;
}while (((resp[0] & MSK_IDLE) == MSK_IDLE) && (i <= SD_IDLE_WAIT_MAX));
if (i >= SD_IDLE_WAIT_MAX)
{
return SD_ERR_TIMEOUT_WAITIDLE; /* 超时,返回错误 time out,return error */
}
ret = SD_SendCmd(CMD55, param, CMD55_R, resp); /* 禁止下一个命令为应用命令 Flag that the next command is application-specific */
if (ret != SD_NO_ERR)
{
return ret;
}
ret = SD_SendCmd(ACMD41, param, ACMD41_R, resp); /* 激活内部初始化命令 active card to initialize process internal */
if (ret != SD_NO_ERR)
{
return SD_ERR_UNKNOWN_CARD;
}
if ((resp[0] & 0xFE) == 0)
{
sds.card_type = CARDTYPE_SD; /* 是SD卡 the card is SD card */
}
else
{
sds.card_type = CARDTYPE_MMC; /* 是MMC卡 the card is MMC card */
}
return SD_NO_ERR;
}
/***************************************************************
** 函数名称: INT8U SD_ReadOCR()
** 功能描述: 读操作条件寄存器OCR
** 输 入: INT8U ocrlen : 寄存器长度(固定为4)
INT8U *recbuf : 接收缓冲区
** 输 出: 0: 正确 >0: 错误码
**************************************************************/
unsigned char SD_ReadOCR(unsigned char ocrlen, unsigned char *recbuf)
{
unsigned char param[4] = {0,0,0,0},resp[5],tmp;
tmp = SD_SendCmd(CMD58, param, CMD58_R, resp); /* 读 OCR 寄存器命令 */
if (tmp != SD_NO_ERR) /* read OCR register command */
{
return tmp;
}
if (resp[0] != 0)
{
return SD_ERR_CMD_RESP; /* 响应错误 response is error */
}
for (tmp = 0; tmp < 4; tmp++)
{
recbuf[tmp] = resp[tmp + 1]; /* 复制OCR寄存器内容到接收缓冲区 */
}
return SD_NO_ERR;
}
/*******************************************************************
** 函数名称: INT8U SD_ReadRegister()
** 功能描述: 从SD卡读取寄存器数据
** 输 入: INT32U len : 长度
INT8U *recbuf: 接收缓冲区
** 输 出: 0: 正确 >0: 错误码
*********************************************************************/
unsigned char SD_ReadRegister(unsigned int len, unsigned char *recbuf)
{
unsigned int i = 0;
unsigned char resp;
SPI_CS_Assert();
do{ /* 等待数据起始令牌 wait for data start token */
resp = SPI_RecByte();
i++;
}while((resp == 0xFF) && (i < SD_READREG_TIMEOUT));
if (i >= SD_READREG_TIMEOUT)
{
SPI_CS_Deassert();
return SD_ERR_TIMEOUT_READ; /* 超时, 返回错误 timeout, return error */
}
if (resp != SD_TOK_READ_STARTBLOCK)
{ /* 不是收到数据起始令牌 not receive data start token */
recbuf[0] = resp;
i = 1; /* 还有len - 1个字节要接收 still len - 1 bytes will be received */
}
else
{
i = 0;
}
for (; i < len; i++)
{
recbuf[i] = SPI_RecByte(); /* 接收数据 receive data */
}
i = SPI_RecByte();
i = (i << 8) + SPI_RecByte(); /* 读取16位CRC get 16-bit CRC */
#if SD_CRC_EN
if (i != SD_GetCRC16(recbuf, len))
{ /* CRC校验错误 CRC check is error */
SPI_SendByte(0xFF);
SPI_CS_Deassert();
return SD_ERR_DATA_CRC16; /* 返回RCR16错误 return error of CRC16 */
}
#endif
SPI_SendByte(0xFF); /* 返回之前发送8个clock clock out 8 clk before return */
SPI_CS_Deassert();
return SD_NO_ERR;
}
/******************************************************************************
** 函数名称: INT8U SD_ReadCSD()
** 功能描述: 读SD/MMC卡的CSD寄存器
** 输 入: INT8U csdlen : 寄存器长度(固定为16)
INT8U *recbuf : 接收缓冲区
** 输 出: 0: 正确 >0: 错误码
************************************************************************/
unsigned char SD_ReadCSD(unsigned char csdlen, unsigned char *recbuf)
{
unsigned char param[4] = {0,0,0,0},resp,ret;
ret = SD_SendCmd(CMD9, param, CMD9_R, &resp); /* 读CSD寄存器命令 command that read CSD register */
if (ret != SD_NO_ERR)
{
return ret;
}
if (resp != 0)
{
return SD_ERR_CMD_RESP; /* 响应错误 response is error */
}
return (SD_ReadRegister(csdlen, recbuf));
}
/**********************************************************************
** 函数名称: void SD_PackParam()
** 功能描述: 将32位的参数转为字节形式
** 输 入: INT8U *parameter: 字节参数缓冲区
INT32U value : 32位参数
** 输 出: 无
************************************************************************/
void SD_PackParam(unsigned char *parameter, unsigned int value)
{
parameter[0] = (unsigned char)(value >> 24);
parameter[1] = (unsigned char)(value >> 16);
parameter[2] = (unsigned char)(value >> 8);
parameter[3] = (unsigned char)(value);
}
/************************************************************************
** 函数名称: INT8U SD_SetBlockLen()
** 功能描述: 设置一个块的长度
** 输 入: INT32U length : 块的长度值
** 输 出: 0: 正确 >0: 错误码
**************************************************************************/
unsigned char SD_SetBlockLen(unsigned int length)
{
unsigned char param[4],resp,ret;
SD_PackParam(param, length); /* 将参数转化为字节形式 change the parameter to bytes form */
ret = SD_SendCmd(CMD16, param, CMD16_R, &resp);
if (ret != SD_NO_ERR)
{
return ret; /* 设置块的长度为length失败 set the length of block to length fail */
}
if (resp != 0)
{
return SD_ERR_CMD_RESP; /* 响应错误 response is error */
}
return SD_NO_ERR; /* 返回执行成功 return perform sucessfully */
}
/*******************************************************************************************************************
** 函数名称: INT8U SD_CalTimeout() Name: INT8U SD_CalTimeout()
** 功能描述: 计算读/写/擦超时时间 Function: calculate timeout of reading,writing,erasing
** 输 入: INT8U *csdbuf : CSD寄存器内容 Input: INT8U *csdbuf : CSD register content
** 输 出: 0: 成功 >0: 错误码 Output: 0: right >0: error code
*******************************************************************************************************************/
unsigned char SD_CalTimeout(unsigned char *csdbuf)
{
unsigned int tmp;
unsigned char time_u,time_v,fator;
sds.timeout_read = READ_TIMEOUT_100MS; /* 默认读超时为100ms */
sds.timeout_write = WRITE_TIMEOUT_250MS; /* 默认写超时为250ms */
sds.timeout_erase = WRITE_TIMEOUT_250MS;
time_u = (csdbuf[TAAC_POS] & TAAC_MSK); /* 读超时时间单位 read timeout unit */
time_v = (csdbuf[TAAC_POS] & NSAC_MSK) >> 3; /* 读超时时间值 read timeout value */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -