📄 sdhal.c
字号:
lpc_mci.RecCnt = 0; /* 初始化mci_struct结构体全局变量lpc_mci */
lpc_mci.RxTxFlag = MCI_RESET_FLAG;
lpc_mci.psds = sds;
lpc_mci.CurTran = MCI_CUR_REC;
return SD_NO_ERR;
}
/*******************************************************************************************************************
** 函数名称: SdHal_ReadBlock()
**
** 功能描述: 在发送读单块命令之后, 由硬件控制器实现读取数据
**
** 输 入: sd_struct *sds : 卡信息结构体
**
** 输 出: INT8U *recbuf: 接收缓冲区, 大小为512字节
**
** 返 回 值: 0: 正确 >0: 错误码, 见 sddriver.h 文件
********************************************************************************************************************/
INT8U SdHal_ReadBlock(sd_struct *sds, INT8U *recbuf)
{
INT32U DataCtrl = 0;
INT8U ret;
lpc_mci.pRecBuf = recbuf;
#if LPC24xx_MCI_DMA_EN
// DMA 控制器初始化
DMA_Init(1, P2M);
DMACC1Configuration |= 0x10001 | (0x04 << 1) | (0x00 << 6) | (0x06 << 11);
// 设置数据控制寄存器: 读,块传输,DMA传输,块长度
DataCtrl |= ((1 << 0) | (1 << 1) | (1 << 3) | (SD_BLOCKSIZE_NBITS << 4));
#else
// 设置数据控制寄存器: 读,块传输,块长度
DataCtrl |= ((1 << 0) | (1 << 1) | (SD_BLOCKSIZE_NBITS << 4));
#endif
#if SD_UCOSII_EN
OS_ENTER_CRITICAL();
MCIDataCtrl = DataCtrl; /* 启动数据传输 */
OSSemPend(sds->pSemSdmmcRW, sds->timeout_read_os, &ret); /* 进入等待状态 */
if (ret == OS_NO_ERR)
ret = lpc_mci.RxTxFlag; /* 返回数据接收结果 */
else if (ret == OS_TIMEOUT)
ret = SD_ERR_TIMEOUT_READ; /* 接收数据超时 */
else
ret = SD_ERR_WAIT_WRSEM; /* 等待信号量失败 */
OS_EXIT_CRITICAL();
#else
MCIDataCtrl = DataCtrl; /* 启动数据传输 */
do
{ /* 等待数据接收 */
if (lpc_mci.RxTxFlag == SD_NO_ERR)
break; /* 数据接收成功 */
}while (lpc_mci.RxTxFlag == MCI_RESET_FLAG);
ret = lpc_mci.RxTxFlag; /* 返回接收结果 */
#endif
MCIMask0 &= ~((DATA_RX_INT_MASK) | (FIFO_INT_MASK)); /* 仅禁止接收中断 */
#if LPC24xx_MCI_DMA_EN
src_addr = (INT32U *)DMA_SRC; /* 取得 DMA 源地址 */
dest_addr = (INT32U *)DMA_DST; /* 取得 DMA 目标地址 */
if (ret == SD_NO_ERR)
memcpy(recbuf, dest_addr, 512); /* 返回接收到的数据 */
#endif
return ret;
}
/*******************************************************************************************************************
** 函数名称: SdHal_BeforeWriteBlock()
**
** 功能描述: 在发送写单块命令之前, 给硬件控制器做一些事情的机会
**
** 输 入: 无
**
** 输 出: INT8U *recbuf: 接收缓冲区, 大小为512字节
**
** 返 回 值: 0: 正确 >0: 错误码, 见 sddriver.h 文件
********************************************************************************************************************/
INT8U SdHal_BeforeWriteBlock(sd_struct *sds)
{
MCIMask0 = DATA_TX_INT_MASK | (FIFO_INT_MASK); /* 仅使能TX中断 */
MCIClear = 0x7FF; /* 清空清零寄存器的所有标志 */
MCIDataCtrl = 0; /* 数据控制寄存器清0 */
MCIDataTimer = sds->timeout_write; /* 写入写数据超时值 */
MCIDataLength = SD_BLOCKSIZE; /* 数据块的长度 */
MCIFifoCnt = 0;
lpc_mci.psds = sds; /* 初始化lpc_mci结构体变量成员 */
lpc_mci.RxTxFlag = MCI_RESET_FLAG;
lpc_mci.SendCnt = 0;
lpc_mci.CurTran = MCI_CUR_SEND;
return SD_NO_ERR;
}
/*******************************************************************************************************************
** 函数名称: SdHal_WriteBlock()
**
** 功能描述: 在发送写单块命令之后, 由硬件控制器向卡发送数据
**
** 输 入: 无
**
** 输 出: INT8U *recbuf: 接收缓冲区, 大小为512字节
**
** 返 回 值: 0: 正确 >0: 错误码, 见 sddriver.h 文件
********************************************************************************************************************/
INT8U SdHal_WriteBlock(sd_struct *sds, INT8U *sendbuf)
{
INT32U DataCtrl = 0;
INT8U ret;
#if LPC24xx_MCI_DMA_EN
src_addr = (INT32U *)DMA_SRC;
dest_addr = (INT32U *)DMA_DST;
//memcpy(sendbuf, src_addr, 512);
memcpy(src_addr, sendbuf, 512); /* 复制要写入的数据到DMA源地址 */
DMA_Init(0, M2P); /* DMA控制器初始化 */
DMACC0Configuration |= 0x00001 | (0x00 << 1) | (0x04 << 6) | (0x05 << 11);
/* 设置数据控制寄存器: 写, 块传输, DMA, 数据长度 */
DataCtrl |= ((1 << 0) | (1 << 3) | (SD_BLOCKSIZE_NBITS << 4));
#else
lpc_mci.pSendBuf = sendbuf;
/* 设置数据控制寄存器: 读, 块传输, 数据长度 */
DataCtrl |= ((1 << 0) | (SD_BLOCKSIZE_NBITS << 4));
#endif
#if SD_UCOSII_EN
OS_ENTER_CRITICAL();
MCIDataCtrl = DataCtrl; /* 启动数据传输 */
OSSemPend(sds->pSemSdmmcRW, sds->timeout_write_os, &ret); /* 进入等待状态 */
if (ret == OS_NO_ERR)
ret = lpc_mci.RxTxFlag; /* 返回写块数据结果 */
else if (ret == OS_TIMEOUT)
ret = SD_ERR_TIMEOUT_WRITE; /* 返回等待超时 */
else
ret = SD_ERR_WAIT_WRSEM; /* OSSemPend()执行失败 */
OS_EXIT_CRITICAL();
#else
MCIDataCtrl = DataCtrl; /* 启动数据传输 */
do
{
if (lpc_mci.RxTxFlag == SD_NO_ERR)
break; /* 发送数据成功 */
}while (lpc_mci.RxTxFlag == MCI_RESET_FLAG);
ret = lpc_mci.RxTxFlag; /* 返回发送数据执行结果 */
#endif
MCIMask0 &= ~(DATA_TX_INT_MASK | FIFO_INT_MASK); /* 仅禁止TX中断 */
return ret;
}
/*******************************************************************************************************************
** 函数名称: SDMMC_WaitReadyforData()
**
** 功能描述: 等待卡可接收数据(等待卡中有空的接收缓冲区), 即等待卡状态寄存器的 bit8 为1,
**
** 输 入: sd_struct *sds : SD/MMC卡信息结构体
**
** 输 出: 无
**
** 返 回 值: 0: 正确 >0: 错误码, 见 sddriver.h 文件
********************************************************************************************************************/
INT8U SdHal_WaitReadyforData(sd_struct *sds)
{
return (SdHal_WaitBusy(sds, BUSY_TYPE_RDY_DATA));
}
#define SD_UCOSII_SMALLWAIT 256 /* 运行于UCOS-II时的小等待时间(避免OSTimeDly(1)降低系统性能) */
#define CMD13_CLK_NUM 160 /* 一个CMD13大约需要160个clk */
/*******************************************************************************************************************
** 函数名称: SdHal_WaitBusy()
**
** 功能描述: 等待卡编程或擦除结束, 即等待卡状态由编程状态变为传输状态: prg --> tran
**
** 输 入: sd_struct *sds : SD/MMC卡信息结构体
** INT32U BusyType : 等待忙类型, 取值为: SD_WAIT_WRITE 和 SD_WAIT_ERASE
** 输 出: 无
**
** 返 回 值: 0: 正确 >0: 错误码, 见 sddriver.h 文件
********************************************************************************************************************/
INT8U SdHal_WaitBusy(sd_struct *sds, INT32U busytype)
{
INT8U ret;
INT32U status, timeout = 0, cnt = 0;
INT32U stmask = CARD_STATUS_CURRENT_STATE;
INT32U stwait = CARD_STATUS_PRG; /* 等待时, 卡处于编程状态 */
if (busytype == BUSY_TYPE_RDY_DATA)
{
timeout = sds->timeout_write / CMD13_CLK_NUM; /* 写超时等待时间 */
stmask = CARD_STATUS_RDY_DATA;
stwait = 0; /* 等待时, 卡的接收缓冲区未就绪 */
}
else if (busytype == BUSY_TYPE_PROG)
timeout = sds->timeout_write / CMD13_CLK_NUM; /* 擦除超时等待时间 */
else if (busytype == BUSY_TYPE_ERASE)
timeout = sds->timeout_erase / CMD13_CLK_NUM;
#if SD_UCOSII_EN
timeout = SD_UCOSII_SMALLWAIT; /* 先进行快速查询, 再每隔1 os tick查询 */
#endif
do
{
ret = SdCmd_Send_Status(sds, &status); /* 读取卡的状态 */
if (ret != SD_NO_ERR)
return ret;
if (!SdHal_CheckCard(sds)) /* 检测卡是否拔出 */
return SD_ERR_NO_CARD;
cnt++;
}while(((status & stmask) == stwait) && (cnt < timeout));
#if SD_UCOSII_EN
if (cnt >= timeout)
{ /* 很少等待后卡仍忙 */
if ((busytype == BUSY_TYPE_RDY_DATA) || (busytype == BUSY_TYPE_PROG))
timeout = sds->timeout_write_os;
else
timeout = sds->timeout_erase_os;
cnt = 0;
do
{
OSTimeDly(1); /* 操作系统挂起1 tick */
ret = SdCmd_Send_Status(sds, &status); /* 读取卡的状态 */
if (ret != SD_NO_ERR)
return ret;
if (!SdHal_CheckCard(sds)) /* 检测卡是否拔出 */
return SD_ERR_NO_CARD;
cnt++;
}while(((status & stmask) == stwait) && (cnt < timeout));
}
#endif
return SD_NO_ERR;
}
/*******************************************************************************************************************
** 函数名称: MCI_Delay()
**
** 功能描述: 延时函数
**
** 输 入: INT16U val: 延时值, 1大约相当于5ms
**
** 输 出: 无
**
** 返 回 值: 无
********************************************************************************************************************/
void MCI_Delay(INT16U val)
{
#if !SD_UCOSII_EN
INT16U i,j;
for (i = 0; i < val; i++)
for (j = 0; j < 0xC000; j++);
#else
OSTimeDly(val);
#endif
}
/*******************************************************************************************************************
** 函数名称: MCI_SendCmd()
**
** 功能描述: 发送命令给卡, 但不获取响应
**
** 输 入: INT32U CmdIndex : 命令索引
** INT32U Argument : 命令参数
** INT32U ExpectResp : 期望得到的回复
** INT32U AllowTimeout: 是否允许超时
**
** 输 出: 无
**
** 返 回 值: 无
********************************************************************************************************************/
void MCI_SendCmd(INT32U CmdIndex, INT32U Argument, INT32U ExpectResp, INT32U AllowTimeout)
{
INT32U CmdData = 0, i;
INT32U CmdStatus;
while ((CmdStatus = MCIStatus) & MCI_CMD_ACTIVE) /* 命令正在处理中 */
{
MCICommand = 0;
MCIClear = CmdStatus | MCI_CMD_ACTIVE; /* 清除相关状态 */
for(i = 0; i < 0x20; i++);
}
CmdData |= (CmdIndex & 0x3F); /* 命令索引只有bit0 ~ bit5 有效 */
if (ExpectResp == EXPECT_NO_RESP) /* 无响应类型 */
{
CmdData &= ~((1 << 6) | (1 << 7)); /* 清除短响应和长响应位 */
}
else if (ExpectResp == EXPECT_SHORT_RESP) /* 希望得到短响应 */
{
CmdData |= (1 << 6);
}
else if (ExpectResp == EXPECT_LONG_RESP) /* 希望得到长响应 */
{
CmdData |= (1 << 6) | (1 << 7);
}
if (AllowTimeout) /* 是否允许超时中断 */
CmdData |= (1 << 8);
else
CmdData &= ~(1 << 8);
CmdData |= (1 << 10); /* 使能发送命令 */
MCIArgument = Argument; /* 命令参数 */
MCICommand = CmdData; /* 启动命令发送 */
}
/*******************************************************************************************************************
** 函数名称: MCI_GetCmdResp()
**
** 功能描述: 从卡获取响应, 该函数与MCI_SendCmd()成对使用
**
** 输 入: INT32U ExpectCmdData: SD/MMC 命令码
** INT32U ExpectResp : 希望的响应, 取值为: EXPECT_NO_RESP, EXPECT_SHORT_RESP, EXPECT_LONG_RESP
** INT8U resplen : 响应长度
**
**
** 输 出: INT32U *CmdResp : 响应内容, 长度为resplen
**
** 返 回 值: 0: 正确 >0: 错误码, 见 sddriver.h 文件
********************************************************************************************************************/
INT8U MCI_GetCmdResp(INT32U ExpectCmdData, INT32U ExpectResp, INT8U resplen, INT32U *CmdResp)
{
INT32U LastCmdIndex;
INT32U CmdRespStatus = 0;
while (1)
{ /* 不断循环等待卡的响应 */
CmdRespStatus = MCIStatus;
if (CmdRespStatus & (MCI_CMD_TIMEOUT))
{ /* 超时错误, 超时周期固定为64个MCICLK时钟周期 */
MCIClear = CmdRespStatus | MCI_CMD_TIMEOUT;
MCICommand = 0; /* 清空命令寄存器及参数寄存器 */
MCIArgument = 0xFFFFFFFF;
return SD_ERR_CMD_TIMEOUT; /* 返回响应命令超时错误 */
}
if (CmdRespStatus & MCI_CMD_CRC_FAIL)
{ /* CRC校验失败错误 */
MCIClear = CmdRespStatus | MCI_CMD_CRC_FAIL;
LastCmdIndex = MCICommand & 0x003F;
if ((LastCmdIndex == SEND_OP_COND) || (LastCmdIndex == SEND_APP_OP_COND)
|| (LastCmdIndex == STOP_TRANSMISSION))
{ /* 忽略命令 SEND_OP_COND和STOP_TRANSMISSION的CRC校验 */
MCICommand = 0;
MCIArgument = 0xFFFFFFFF;
break;
}
else
return SD_ERR_CMD_RESPCRC; /* 返回响应命令CRC校验失败错误 */
}
else if (CmdRespStatus & MCI_CMD_RESP_END)
{
MCIClear = CmdRespStatus | MCI_CMD_RESP_END;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -