📄 sddriver.c
字号:
// SD/MMC卡读写模块: 物理层 用户API函数
#include "sdhal.h"
#include "sdcmd.h"
#include "sddriver.h"
#if 0
#include "FreeRTOSConfig.h"
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "semphr.h"
#endif
///////////////////////////////////////////////////////////////
// 向卡发送命令,并取得响应 (命令字,命令参数--长度为4字节,响应类型,响应-长度为1-5字节) 0:正确 >0:错误码
U8 SD_TxCMD(U8 cmd, U8 *param, U8 resptype, U8 *resp)
{
S32 i,rlen;
U8 tmp;
SPI_CS_Enable();
SPI_TxBYTE((cmd & 0x3F) | 0x40); /* 发送命令头和命令字 */
for (i = 3; i >= 0; i--)
SPI_TxBYTE(param[i]); /* 发送参数 */
SPI_TxBYTE(0x95); /* CRC校验码,只用于第1个命令 */
rlen = 0;
switch (resptype)/* 根据不同的命令,得到不同的响应长度 */
{
case R1:
case R1B: rlen = 1; break;
case R2: rlen = 2; break;
case R3: rlen = 5; break;
default: SPI_TxBYTE(0xFF);
SPI_CS_Disable();
return SD_ERR_CMD_RESPTYPE; /* 返回命令响应类型错误*/
break;
}
i = 0;
do /* 等待响应,响应的开始位为0 */
{
tmp = SPI_RxBYTE();
i++;
}while (((tmp & 0x80) != 0) && (i < SD_CMD_TIMEOUT));
if (i >= SD_CMD_TIMEOUT)
{
SPI_CS_Disable();
return SD_ERR_CMD_TIMEOUT; /* 超时 */
}
for (i = rlen - 1; i >= 0; i--)
{
resp[i] = tmp;
tmp = SPI_RxBYTE();/* 循环的最后发送8clock */
}
SPI_CS_Disable();
return SD_NO_ERR; /* 执行成功 */
}
// 将32位的参数转为字节形式 (字节参数缓冲区,32位参数)
void SD_PackPara(U8* para, U32 value)
{
para[3] = (U8)(value >> 24);
para[2] = (U8)(value >> 16);
para[1] = (U8)(value >> 8);
para[0] = (U8)(value);
}
// 块命令 (命令字, 响应类型, 块操作参数) 0:正确 >0:错误码
U8 SD_BlockCMD(U8 cmd, U8 resptype, U32 para)
{
U8 param[4],resp,ret;
para <<= SD_BLOCKSIZE_NBITS; /* 调整地址:左移9位 */
SD_PackPara(param, para); /* 将参数转化为字节形式 */
ret = SD_TxCMD(cmd, param, resptype, &resp);
if (ret != SD_NO_ERR)
return ret; /* 结束数据传输失败*/
if (resp != 0)
return SD_ERR_CMD_RESP; /* 响应错误 */
return SD_NO_ERR;
}
// 复位SD/MMC卡 0:正确 >0:错误码
U8 SD_ResetSD(void)
{
U8 param[4] = {0,0,0,0},resp;
return (SD_TxCMD(CMD0, param, CMD0_R, &resp)); /* 复位命令*/
}
// 读SD/MMC卡的CSD寄存器 (寄存器长度(固定为16), 接收缓冲区) 0:正确 >0:错误码
U8 SD_ReadCSD(U8 csdlen, U8 *recbuf)
{
U8 param[4] = {0,0,0,0},resp,ret;
ret = SD_TxCMD(CMD9, param, CMD9_R, &resp); /* 读CSD寄存器命令*/
if (ret != SD_NO_ERR)
return ret;
if (resp != 0)
return SD_ERR_CMD_RESP; /* 响应错误*/
return (SD_ReadRegister(csdlen, recbuf));
}
// 读SD/MMC卡的 Card Status 寄存器 (寄存器长度(固定为2), 接收缓冲区) 0:正确 >0:错误码
U8 SD_ReadCard_Status(U8 len, U8 *buffer)
{
U8 param[4] = {0,0,0,0};
return (SD_TxCMD(CMD13, param, CMD13_R, buffer)); /* 读 Card Status 寄存器 */
}
// 设置一个块的长度 (块的长度值) 0:正确 >0:错误码
U8 SD_SetBlockLen(U32 length)
{
U8 param[4],resp,ret;
SD_PackPara(param, length);/* 将参数转化为字节形式 */
ret = SD_TxCMD(CMD16, param, CMD16_R, &resp);
if (ret != SD_NO_ERR)
return ret; /* 设置块的长度为length失败 */
if (resp != 0)
return SD_ERR_CMD_RESP; /* 响应错误 */
return SD_NO_ERR; /* 返回执行成功 */
}
// 读操作条件寄存器OCR (寄存器长度(固定为4), 接收缓冲区) 0:正确 >0:错误码
U8 SD_ReadOCR(U8 ocrlen, U8 *recbuf)
{
U8 param[4] = {0,0,0,0},resp[5],tmp;
tmp = SD_TxCMD(CMD58, param, CMD58_R, resp); /* 读 OCR 寄存器命令 */
if (tmp != SD_NO_ERR)
return tmp;
if (resp[0] != 0)
return SD_ERR_CMD_RESP; /* 响应错误*/
for (tmp = 0; tmp < 4; tmp++)
recbuf[tmp] = resp[tmp + 1]; /* 复制OCR寄存器内容到接收缓冲区 */
return SD_NO_ERR;
}
// 从SD卡读取数据 (长度, 接收缓冲区) 0:正确 >0:错误码
U8 SD_ReadRegister(U32 len, U8 *recbuf)
{
U32 i = 0;
U8 resp;
SPI_CS_Enable();
do{ /* 等待数据起始令牌*/
resp = SPI_RxBYTE();
i++;
}while((resp == 0xFF) && (i < SD_READREG_TIMEOUT));
if (i >= SD_READREG_TIMEOUT)
{
SPI_CS_Disable();
return SD_ERR_TIMEOUT_READ; /* 超时, 返回错误*/
}
if (resp != SD_TOK_READ_STARTBLOCK)
{ /* 不是收到数据起始令牌*/
recbuf[0] = resp;
i = 1; /* 还有len - 1个字节要接收 */
}
else
i = 0; /* 收到数据起始令牌,还有len个字节要接收 */
for (; i < len; i++)
recbuf[i] = SPI_RxBYTE(); /* 接收数据 receive data */
i = SPI_RxBYTE();
i = (i << 8) + SPI_RxBYTE(); /* 读取16位CRC*/
SPI_TxBYTE(0xFF); /* 返回之前发送8个clock*/
SPI_CS_Disable();
return SD_NO_ERR;
}
// 从卡中读取数据块 (长度,接收缓冲区) 0:正确 >0:错误码
U8 SD_ReadBlockData(U32 len, U8 *recbuf)
{
U8 tmp;
U32 i = 0,timeout;
#if SD_FreeRTOS_EN
timeout = SD_FreeRTOS_SMALLWAIT; /* 很少的等待时间*/
#else
timeout = sds.timeout_read; /* 等待接收数据开始令牌最长时间 */
#endif
SPI_CS_Enable();
do
{ /* 等待接收数据开始令牌0xFE*/
tmp = SPI_RxBYTE();
i++;
}while((tmp == 0xFF) && (i < timeout));
#if SD_FreeRTOS_EN
if (i >= timeout)
{ /* 继续等待(挂起任务)*/
timeout = sds.timeout_read;
i = 0;
do
{
vTaskDelay(1); /* 挂起该任务1 tick*/
tmp = SPI_RxBYTE();
i++;
}while((tmp == 0xFF) && (i < timeout));
}
#endif
if (i >= timeout)
{
SPI_CS_Disable();
return SD_ERR_TIMEOUT_READ; /* 返回读超时错误码*/
}
if (tmp != SD_TOK_READ_STARTBLOCK) /* 块读开始令牌错误*/
{
SPI_TxBYTE(0xFF);
SPI_CS_Disable();
return SD_ERR_DATA_START_TOK;
}
for (i = 0; i < len; i++)
recbuf[i] = SPI_RxBYTE(); /* 接收数据*/
i = SPI_RxBYTE();
i = (i << 8) + SPI_RxBYTE(); /* 读取16位CRC */
SPI_TxBYTE(0xFF);
SPI_CS_Disable();
return SD_NO_ERR; /* 执行成功*/
}
// 向卡写数据块 (是否为多块操作1:是0:否, 长度, 发送缓冲区) 0:正确 >0:错误码
U8 SD_WriteBlockData(U8 bmulti, U32 len, const U8 *sendbuf)
{
U16 i;
U8 tmp;
SPI_CS_Enable();
SPI_TxBYTE(0xFF); /* 开始发送数据之前发送8个*/
if (bmulti == 1)
SPI_TxBYTE(SD_TOK_WRITE_STARTBLOCK_M); /* 写多块开始令牌 */
else
SPI_TxBYTE(SD_TOK_WRITE_STARTBLOCK); /* 写单块开始令牌 */
for (i = 0; i < len; i++)
SPI_TxBYTE(sendbuf[i]); /* 发送数据 */
SPI_TxBYTE((i >> 8) & 0xFF);
SPI_TxBYTE(i & 0xFF); /* 发送CRC16校验码 */
tmp = SPI_RxBYTE();
if ((tmp & SD_RESP_DATA_MSK) != SD_RESP_DATA_ACCETPTED)
{
SPI_TxBYTE(0xFF); /* 返回之前发送8个clock */
SPI_CS_Disable();
return SD_ERR_DATA_RESP;/* 数据响应错误 */
}
SPI_CS_Disable();
if (SD_WaitBusy(SD_WAIT_WRITE) != SD_NO_ERR)
return SD_ERR_TIMEOUT_WRITE; /* 写入超时 */
else
return SD_NO_ERR; /* 写入正确 */
}
// 查询SD卡是否处于忙状态 0: 未超时 >0: 错误码
U8 SD_WaitBusy(U8 waittype)
{
U32 timeout, i = 0;
U8 tmp;
if (waittype == SD_WAIT_WRITE)
timeout = sds.timeout_write; /* 等待类型为写操作 */
else
timeout = sds.timeout_erase; /* 等待类型为擦除操作 */
#if SD_FreeRTOS_EN
timeout = SD_FreeRTOS_SMALLWAIT; /* 很少的等待时间 */
#endif
SPI_CS_Enable();
do
{ /* 等待忙结束 */
tmp = SPI_RxBYTE();
i++;
}while ((tmp != 0xFF) && (i < timeout)); /* 忙时收到的值为0 */
#if SD_FreeRTOS_EN
if (i >= timeout)
{ /* 很少等待后卡仍忙, */
if (waittype == SD_WAIT_WRITE)
timeout = sds.timeout_write;
else
timeout = sds.timeout_erase;
i = 0;
do
{
vTaskDelay(1); /* 操作系统挂起1 tick */
tmp = SPI_RxBYTE();
i++;
}while ((tmp != 0xFF) && (i < timeout)); /* 忙时收到的值为0*/
}
#endif
if(i < timeout)
tmp = SD_NO_ERR; /* 返回0,表示没超时 */
else
tmp = SD_ERR_TIMEOUT_WAIT; /* 返回错误码,表示超时*/
SPI_TxBYTE(0xFF);
SPI_CS_Disable();
return tmp; /* 返回执行结果 */
}
// SPI总线延时
void SD_SPIDelay(U8 value)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -