NUC970 SD卡驱动(SDIO)
在读取SD卡的R2长响应折腾了不少时间,原因是必须开启DMA,不开启DMA,R2响应将不会传输到SDIO的FIFO中,将SDIO接口与STM32保持一致,并兼容应用层SDIO_SDCARD驱动。
寄存器地址
//SDIO========================================================================================================
#define SDIO_BUFF_BASE (0xB000C000) //寄存器基址
#define SDIO_DMA_BASE (0xB000C000+0x400) //寄存器基址
#define SDIO_BASE (0xB000C000+0x800) //寄存器基址typedef struct
{vu8 Buff[128]; //SD主机嵌入式缓冲区
}SDIO_BUFF_TypeDef;typedef struct
{vu32 CTL; //SD主机DMA控制和状态寄存器u32 Reserved1;vu32 SA; //SD主机DMA传输起始地址寄存器vu32 BCNT; //SD主机DMA传输字节计数寄存器vu32 INTEN; //SD主机DMA中断使能寄存器vu32 INTSTS; //SD主机DMA中断状态寄存器
}SDIO_DMA_TypeDef;typedef struct
{vu32 GCTL; //SD主机全局控制和状态寄存器vu32 GINTEN; //SD主机全局中断控制寄存器vu32 GINTSTS; //SD主机全局中断状态寄存器u32 Reserved1[5];vu32 CTL; //SD主机控制和状态寄存器vu32 ARG; //SD主机命令参数寄存器vu32 INTEN; //SD主机中断允许寄存器vu32 INTSTS; //SD主机中断状态寄存器vu32 RESP0; //SD主机接收响应令牌寄存器0vu32 RESP1; //SD主机接收响应令牌寄存器1vu32 BLEN; //SD主机块长度寄存器vu32 TMOUT; //SD主机响应/数据输入超时寄存器vu32 ECTL; //SD主机扩展控制寄存器
}SDIO_TypeDef;#define SDIO_BUFF ((SDIO_BUFF_TypeDef *) SDIO_BUFF_BASE)
#define SDIO_DMA ((SDIO_DMA_TypeDef *) SDIO_DMA_BASE)
#define SDIO ((SDIO_TypeDef *) SDIO_BASE)
//SDIO.c
/************************************************************************************************************** 文件名: sdio.c* 功能: NUC970 sdio驱动* 作者: cp1300@139.com* 创建时间: 2020-09-04* 最后修改时间: 2020-09-04* 详细:
*************************************************************************************************************/
#include "nuc970_system.h"
#include "sdio.h"
#include "sdio_const.h"__inline u32 SDIO_GetResp1CardStatu(void);//获取R1响应的卡状态数据#define SDIO_DelayUS(x) Delay_US(x)
#define SDIO_TIMEOUT_US (100*1000) //SDIO软件延时/*************************************************************************************************************************
* 函数 : bool SDIO_Init(void)
* 功能 : SDIO接口初始化
* 参数 : 无
* 返回 : TRUE:初始化成功;FALSE:初始化失败;
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : 默认会选择 SD host port 0
*************************************************************************************************************************/
bool SDIO_Init(void)
{//SDIO时钟选择,使用CPU时钟,CPU使用的是UPLL,300MHzu32 tempreg = SYS_REG_TYPE(REG_CLK_DIVCTL9);SYS_DeviceClockEnable(DEV_SDIO, TRUE); //使能时钟SYS_DeviceReset(DEV_RESET_SDIO); //复位外设tempreg &= ~(0xFFFF); //清除掉之前的设置tempreg |= 3 << 3; //时钟源选择UPLL 0x0:XIN;0x2:APLL;0x3:UPLLtempreg |= 2 << 0; //PLL时钟分频(2+1),输出100MHztempreg |= SDIO_CLK_400K << 8; //SDH_N分频系数+1, 390KHzSYS_REG_TYPE(REG_CLK_DIVCTL9) = tempreg;Delay_US(100);SDIO->GCTL = 1; //关闭并复位SDIOnop;nop;SDIO->GCTL |= BIT1; //使能SDIO-不使能无法设置寄存器nop;nop;//SDIO寄存器设置 SDIO->GINTEN = 0; //关闭DMA中断SDIO->GINTSTS = 1; //清除DMA错误中断SDIO->CTL = SDIO_CTL_SW_RST; //数据1bit模式;复位内部状态机;while(SDIO->CTL & SDIO_CTL_SW_RST);SDIO->CTL = (0x09u<<24) | (1<<16); //设置默认配置SDIO->INTEN = 0; //中断全部关闭SDIO_SetDataTransBlockSize(SDIO_DataBlockSize_512B); //默认块大小:512字节SDIO_SetDataTransTimeOut(0xFFFFFF); //SDIO传输数据超时设置-设置为非常大,直接由应用程序来决定超时SDIO_SetBusWide(SDIO_BusWide_1b); //SDIO设置总线宽度SDIO_SetPower(TRUE); //SDIO电源控制-使能SDIO_DMA->CTL = BIT1; //DMA复位,不启动分散加载功能while(SDIO_DMA->CTL & BIT1);SDIO_DMA->INTEN = 0; //关闭DMA中断SDIO_DMA->CTL |= BIT0; //使能DMA-2020-09-12折腾几天发现,此处必须开启DMA,否则R2响应的数据无法被接收return TRUE;
}/*************************************************************************************************************************
* 函数 : bool SDIO_SetOut74Clock(void)
* 功能 : SDIO输出74个时钟
* 参数 : 无
* 返回 : TRUE:成功;FALSE:失败
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-12
* 最后修改时间 : 2020-09-12
* 说明 :
*************************************************************************************************************************/
bool SDIO_SetOut74Clock(void)
{u32 timeout = SDIO_TIMEOUT_US;//读等待超时计数器SDIO->CTL |= BIT5; //SDIO输出74个时钟,用于初始化SD卡,等待SD卡上电稳定while(SDIO->CTL & BIT5){timeout --;Delay_US(1);}if(timeout == 0){SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器return FALSE;}return TRUE;
}/*************************************************************************************************************************
* 函数 : void SDIO_SetClockSpeed(SDIO_CLOCK_SPEED ClkSpeed)
* 功能 : SDIO设置时钟速度
* 参数 : ClkSpeed:时钟速度,见SDIO_CLOCK_SPEED
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 :
*************************************************************************************************************************/
void SDIO_SetClockSpeed(SDIO_CLOCK_SPEED ClkSpeed)
{u32 tempreg = SYS_REG_TYPE(REG_CLK_DIVCTL9);tempreg &= ~(0xFF << 8); //清除掉之前的设置tempreg |= (ClkSpeed & 0xff) << 8; //SDH_N分频系数+1SYS_REG_TYPE(REG_CLK_DIVCTL9) = tempreg;Delay_US(10);
} /*************************************************************************************************************************
* 函数 : u8 SDIO_GetPortSelect(void)
* 功能 : 获取当前选择的port(0-1)
* 参数 : 无
* 返回 : 通道0-1
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 :
*************************************************************************************************************************/
u8 SDIO_GetPortSelect(void)
{if(SDIO->CTL & BIT29) return 1;else return 0;
} /*************************************************************************************************************************
* 函数 : void SDIO_SetPortSelect(u8 port)
* 功能 : 设置SDIO当前port(0-1)
* 参数 : 通道0-1
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 :
*************************************************************************************************************************/
void SDIO_SetPortSelect(u8 port)
{if(port) SDIO->CTL |= BIT29;else SDIO->CTL &= ~BIT29;
} /*************************************************************************************************************************
* 函数 : void SDIO_SetBusWide(SDIO_BUS_WIDE BusWide)
* 功能 : SDIO设置总线宽度
* 参数 : BusWide:总线宽度,见SDIO_BUS_WIDE
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 :
*************************************************************************************************************************/
void SDIO_SetBusWide(SDIO_BUS_WIDE BusWide)
{if(BusWide == SDIO_BusWide_1b){SDIO->CTL &= ~BIT15; //设置为1bit模式}else{SDIO->CTL |= BIT15; //设置为4bit模式}
} /*************************************************************************************************************************
* 函数 : SDIO_BUS_WIDE SDIO_GetBusWide(void)
* 功能 : 获取SDIO设置的总线宽度
* 参数 : 无
* 返回 : 总线宽度,见SDIO_BUS_WIDE
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : 可以获取当前端口的总线宽度
*************************************************************************************************************************/
SDIO_BUS_WIDE SDIO_GetBusWide(void)
{if(SDIO->CTL & BIT15) return SDIO_BusWide_4b;else return SDIO_BusWide_1b;
} /*************************************************************************************************************************
*函数 : void SDIO_SendCommand(u8 CmdIndex,u32 CmdArg,SDIO_WAIT_RESP WaitResp)
*功能 : SDIO发送一个命令
*参数 : CmdIdx:指令索引;CmdArg:命令参数;WaitResp:响应类型
*返回 : 无
*依赖 : 底层宏定义
*作者 : cp1300@139.com
*时间 : 2020-09-04
*最后修改时间: 2020-09-04
*说明 : 响应类型SDIO_Response_No 无应答SDIO_Response_Short 短应答SDIO_Response_Long 长应答此单片机只区分有应答还是无应答
*************************************************************************************************************************/
void SDIO_SendCommand(u8 CmdIndex, u32 CmdArg, SDIO_WAIT_RESP WaitResp)
{ u32 tmpreg;//先确保之前的命令都执行完成了,如果存在超时的,请清除掉while(SDIO->CTL & (BIT5 | BIT6 | 0X1F)){SDIO->CTL |= BIT14; //SW_RESETSYS_DelayMS(1);}SDIO->ARG = CmdArg; //参数tmpreg = SDIO->CTL; tmpreg &= ~(0X3F << 8); //清除命令tmpreg |= (CmdIndex&0X3F) << 8; //设置新的命令index if(SDIO_Response_Long == WaitResp) //长响应R2{tmpreg |= SDIO_CTL_R2IEN_Msk; //使能R2应答}else if(SDIO_Response_Short == WaitResp) //短响应R1{tmpreg |= SDIO_CTL_RIEN_Msk; //使能R1应答}tmpreg |= SDIO_CTL_COEN_Msk; //使能命令发送//清除所有标志SDIO->INTSTS = SDIO->INTSTS; //清除中断状态SDIO->CTL = tmpreg; //写入,执行
}/*************************************************************************************************************************
* 函数 : void SDIO_SetPower(bool isPowerUp)
* 功能 : SDIO电源控制
* 参数 : isPowerUp:TRUE:通电,为卡提供时钟;FALSE:掉电,停止时钟
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 :
*************************************************************************************************************************/
void SDIO_SetPower(bool isPowerUp)
{if(isPowerUp){SDIO->ECTL |= 1<<(SDIO_GetPortSelect()&0x1); //通电}else{SDIO->ECTL &= ~(1<<(SDIO_GetPortSelect()&0x1));//掉电}
}/*************************************************************************************************************************
* 函数 : void SDIO_SetPowerSaving(bool isSaving)
* 功能 : SDIO节能模式开关
* 参数 : isSaving:TRUE:仅在总线激活时使能 SDIO_CK;FALSE:始终使能 SDIO_CK 时钟
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : 要实现节能模式,可在总线空闲时通过将 CLK_KEEP1 置 0来禁止 SDIO_CK 时钟输出
*************************************************************************************************************************/
void SDIO_SetPowerSaving(bool isSaving)
{if(isSaving){SDIO->CTL &= ~BIT31; //使能节能模式}else{SDIO->CTL |= BIT31; //关闭节能模式}
}/*************************************************************************************************************************
* 函数 : void SDIO_StartDataTrans(bool isRead)
* 功能 : SDIO开始数据传输
* 参数 : isRead:是否是读取
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : 数据传输必须首先写入到数据计时器寄存器和数据长度寄存器,然后才写入到数据控制寄存器。对于 SDIO 多字节传输,数据长度寄存器中的值必须在 1 到 512 之间。
*************************************************************************************************************************/
void SDIO_StartDataTrans(bool isRead)
{SDIO->INTSTS = SDIO->INTSTS; //清除中断状态if(isRead) //读取方向{SDIO->CTL |= BIT2;}else //发送方向{SDIO->CTL |= BIT3;}
} /*************************************************************************************************************************
* 函数 : void SDIO_SetAndStartDataTrans(u8 *pDataBuff, bool isEnableReadData, SDIO_DataBlockSize SDIOBlockSize, u32 TransDataLen)
* 功能 : 设置SDIO并开始准备数据传输
* 参数 : pDataBuff:传输的数据缓冲区;isEnableReadData:是否为读取数据;SDIOBlockSize:SDIO传输块大小;TransDataLen:传输数据长度
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : 数据传输必须首先写入到数据计时器寄存器和数据长度寄存器,然后才写入到数据控制寄存器。对于 SDIO 多字节传输,数据长度寄存器中的值必须在 1 到 512 之间。会使能DMA传输
*************************************************************************************************************************/
void SDIO_SetAndStartDataTrans(u8 *pDataBuff, bool isEnableReadData, SDIO_DataBlockSize SDIOBlockSize, u32 TransDataLen)
{//初始化DMASDIO_DMA->SA = (u32)pDataBuff; //设置DMA数据传输地址//SDIO传输数据块大小设置SDIO_SetDataTransBlockSize(SDIOBlockSize); //设置传输块大小//SDIO传输数据长度SDIO_SetDataTransLength(TransDataLen); SDIO->INTSTS = SDIO->INTSTS; //清除中断状态 //数据读取方向if(isEnableReadData) //读数据,卡到控制器{SDIO->CTL |= BIT2;}else //写数据,控制器到卡{SDIO->CTL |= BIT3;}
} /*************************************************************************************************************************
* 函数 : void SDIO_ClearDataTrans(void)
* 功能 : SDIO清除数据传输
* 参数 :
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : 手册上说无需清除DTEN,但是实际使用发现DTEN并不会被清除。
*************************************************************************************************************************/
void SDIO_ClearDataTrans(void)
{u32 timeout = SDIO_TIMEOUT_US;//读等待超时计数器if(SDIO->CTL & (SDIO_CTL_DOEN_Msk | SDIO_CTL_DIEN_Msk)) //之前的数据传输未完成{SDIO->CTL |= SDIO_CTL_SW_RST; //SW_RESETwhile(SDIO->CTL & SDIO_CTL_SW_RST){timeout --;Delay_US(1);}if(timeout == 0){DEBUG("复位SDIO命令失败\r\n");}}if((SDIO_DMA->INTSTS & 0x03) || (SDIO_DMA->CTL & BIT9)) //传输出错,或者DMA还在忙{SDIO_DMA->INTSTS = 0x03; //清除错误timeout = SDIO_TIMEOUT_US; //读等待超时计数器SDIO_DMA->CTL |= BIT1; //复位DMA,会清除DMA相关配置寄存器while(SDIO_DMA->CTL & BIT1){timeout --;Delay_US(1);}if(timeout == 0){DEBUG("复位SDIO DMA失败\r\n");}}
} /*************************************************************************************************************************
* 函数 : void SDIO_SetDataTransTimeOut(u32 DataIimeOut)
* 功能 : SDIO传输数据超时设置
* 参数 : DataIimeOut:数据超时计数,以总线周期为准
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 :
*************************************************************************************************************************/
void SDIO_SetDataTransTimeOut(u32 DataIimeOut)
{SDIO->TMOUT = DataIimeOut; //以卡总线时钟周期表示的数据超时周期。
} /*************************************************************************************************************************
* 函数 : void SDIO_SetDataTransBlockSize(SDIO_DataBlockSize DataBlockSize)
* 功能 : SDIO传输数据块大小设置
* 参数 : DataBlockSize:块大小
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 :
*************************************************************************************************************************/
void SDIO_SetDataTransBlockSize(SDIO_DataBlockSize DataBlockSize)
{SDIO->BLEN = DataBlockSize & 0X7FF;
} /*************************************************************************************************************************
* 函数 : void SDIO_SetDataTransAddr(u32 Addr)
* 功能 : SDIO要传输的数据地址
* 参数 : Addr:要传输的数据地址
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-12
* 最后修改时间 : 2020-09-12
* 说明 :
*************************************************************************************************************************/
void SDIO_SetDataTransAddr(u32 Addr)
{SDIO_DMA->SA = Addr;
}/*************************************************************************************************************************
* 函数 : void SDIO_SetDataTransLength(u32 DataLen)
* 功能 : SDIO传输数据长度
* 参数 : DataLen:传输数据长度(必须为块大小的整数倍)
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : 对于块数据传输,数据长度寄存器中的值必须是块大小的倍数(请参见 SDIO_DCTRL)。 数据传输必须首先写入到数据计时器寄存器和数据长度寄存器,然后才写入到数据控制寄存器。对于 SDIO 多字节传输,数据长度寄存器中的值必须在 1 到 512 之间。块大小为512字节时,最大传输512*255=130560B,最小512字节。
*************************************************************************************************************************/
void SDIO_SetDataTransLength(u32 DataLen)
{u32 BlockByteSize = (SDIO->BLEN&0x7FF)+1; //当前设置的块字节大小u32 tempreg;DataLen /= BlockByteSize; //转换为要传输的快if(DataLen == 0) {DEBUG("传输大小不足一块,无法开始传输!\r\n");return; //如果传输数量不足一块,则直接退出}else if(DataLen > 255){DEBUG("最多一次传输255个块,超出范围了%d!\r\n", DataLen);DataLen = 255;}tempreg = SDIO->CTL;tempreg &= ~(0xFF<<16); //清除块设置tempreg |= DataLen << 16;SDIO->CTL = tempreg;
} /*************************************************************************************************************************
* 函数 : void SDIO_GetResponse(SDIO_RESP_TYPE RespIndex, u32 *pRespData)
* 功能 : 获取返回响应状态
* 参数 : RespIndex:见SDIO_RESP_TYPE;pRespData:响应数据缓冲区
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-12
* 说明 : 短响应需要1个字;长响应需要4个字的缓冲区短响应:u32[0] 卡状态 [31:0]长响应:u32[0] 卡状态 [127:96];[1]:卡状态 [95:64];[2]:卡状态 [63:32];[3]:卡状态 [31:1]0bNUC970 的SDH返回的长响应去掉了最低8位,并且最高字节在前面,最前面固定为0x3F(去掉)从第二字节开始,但是最低16bit都是0
*************************************************************************************************************************/
void SDIO_GetResponse(SDIO_RESP_TYPE RespIndex, u32 *pRespData)
{switch(RespIndex){case SDIO_RESP_Short: {pRespData[0] = SDIO_GetResp1CardStatu(); //获取R1响应的卡状态数据;}break;case SDIO_RESP_Long:{u8 i;//只会响应16字节,但是最前面字节是无效的固定为0x3F,也就是缺少一字节有效响应for(i = 0;i < 4;i ++){pRespData[i] = SDIO_BUFF->Buff[1+i*4+0];pRespData[i] <<= 8;pRespData[i] |= SDIO_BUFF->Buff[1+i*4+1];pRespData[i] <<= 8;pRespData[i] |= SDIO_BUFF->Buff[1+i*4+2];pRespData[i] <<= 8;pRespData[i] |= SDIO_BUFF->Buff[1+i*4+3];}pRespData[3] &= 0xFFFFFF00;}break;default:break;}
} /*************************************************************************************************************************
* 函数 : SDIO_Error SDIO_CmdError(void)
* 功能 : 获取指令执行状态(无响应)
* 参数 : 无
* 返回 : SDIO_Error
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : 无响应,只要命令发送完成了就退出
*************************************************************************************************************************/
SDIO_Error SDIO_CmdError(void)
{SDIO_Error Status = SDIO_OK;u32 timeout=SDIO_TIMEOUT_US;while (timeout){timeout--;if((SDIO->CTL & SDIO_CTL_COEN_Msk) == 0) break; //命令发送完成了SDIO_DelayUS(1);} if(timeout==0) {SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器return SDIO_CMD_RSP_TIMEOUT; }//清除所有标志SDIO->INTSTS = SDIO->INTSTS;return Status;
} /*************************************************************************************************************************
* 函数 : SDIO_Error SDIO_CmdResp7Error(void)
* 功能 : 获取指令执行状态(R7)
* 参数 : 无
* 返回 : SDIO_Error
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : R7命令优先处理响应成功,后处理CRC错误
*************************************************************************************************************************/
SDIO_Error SDIO_CmdResp7Error(void)
{SDIO_Error Status = SDIO_OK;u32 timeout=SDIO_TIMEOUT_US;while (timeout){timeout--;if(SDIO->INTSTS &(SDIO_INT_STATUS_RITO | SDIO_INT_STATUS_CRC)) break;//CRC错误/命令响应超时if((SDIO->CTL & SDIO_CTL_RIEN_Msk) == 0) break; //收到响应了Delay_US(1);} if(timeout==0) //响应超时{ Status = SDIO_CMD_RSP_TIMEOUT; //正常程序不应该执行到此处,如果到了此处请检查原因DEBUG("程序不应该执行到此处(0x%X)\r\n", SDIO->INTSTS);SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}if(SDIO->INTSTS & SDIO_INT_STATUS_RITO) //响应超时{ Status = SDIO_CMD_RSP_TIMEOUT; SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}//命令响应成功if((SDIO->CTL & SDIO_CTL_RIEN_Msk) == 0){/* Card is SD V2.0 compliant */Status = SDIO_OK;SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}//SDIO_STA_CCRCFAIL 错误 Status = SDIO_CMD_CRC_FAIL;//清除所有标志SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;
} /*************************************************************************************************************************
* 函数 : SDIO_Error SDIO_CmdResp1Error(void)
* 功能 : 指令执行状态(R1)
* 参数 : 无
* 返回 : SDIO_Error
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : 此命令超时设置为1分钟,必须增加超时,防止程序假死在此处如果响应不包含 CRC(CMD1 响应),则设备驱动程序必须忽略 CRC 失败状态。
*************************************************************************************************************************/
SDIO_Error SDIO_CmdResp1Error(void)
{ SDIO_Error Status = SDIO_OK;u32 timeout=SDIO_TIMEOUT_US/1; u32 response_r1;while (timeout){timeout--;if(SDIO->INTSTS &(SDIO_INT_STATUS_RITO | SDIO_INT_STATUS_CRC)) break;//CRC错误/命令响应超时if((SDIO->CTL & SDIO_CTL_RIEN_Msk) == 0) break; //收到响应了Delay_US(1);} if(timeout==0) //响应超时{ Status = SDIO_CMD_RSP_TIMEOUT; //正常程序不应该执行到此处,如果到了此处请检查原因DEBUG("程序不应该执行到此处(0x%X)\r\n", SDIO->INTSTS);SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}if(SDIO->INTSTS & SDIO_INT_STATUS_RITO) //响应超时{ Status = SDIO_CMD_RSP_TIMEOUT; SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}//SDIO_STA_CCRCFAIL 错误if(SDIO->INTSTS & SDIO_INT_STATUS_CRC) //命令校验错误{ Status = SDIO_CMD_CRC_FAIL;SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}//清除所有标志SDIO->INTSTS = SDIO->INTSTS; //清除所有标志//命令执行成功,获取R1响应结果response_r1 = SDIO_GetResp1CardStatu(); //返回的响应if ((response_r1 & SDIO_OCR_ERRORBITS) == 0) //没有错误响应{return Status;}if (response_r1 & SDIO_OCR_ADDR_OUT_OF_RANGE){return (SDIO_ADDR_OUT_OF_RANGE);}if (response_r1 & SDIO_OCR_ADDR_MISALIGNED){return (SDIO_ADDR_MISALIGNED);}if (response_r1 & SDIO_OCR_BLOCK_LEN_ERR){return (SDIO_BLOCK_LEN_ERR);}if (response_r1 & SDIO_OCR_ERASE_SEQ_ERR){return (SDIO_ERASE_SEQ_ERR);}if (response_r1 & SDIO_OCR_BAD_ERASE_PARAM){return (SDIO_BAD_ERASE_PARAM);}if (response_r1 & SDIO_OCR_WRITE_PROT_VIOLATION){return (SDIO_WRITE_PROT_VIOLATION);}if (response_r1 & SDIO_OCR_LOCK_UNLOCK_FAILED){return (SDIO_LOCK_UNLOCK_FAILED);}if (response_r1 & SDIO_OCR_COM_CRC_FAILED){return (SDIO_COM_CRC_FAILED);}if (response_r1 & SDIO_OCR_ILLEGAL_CMD){return (SDIO_ILLEGAL_CMD);}if (response_r1 & SDIO_OCR_CARD_ECC_FAILED){return (SDIO_CARD_ECC_FAILED);}if (response_r1 & SDIO_OCR_CC_ERROR){return (SDIO_CC_ERROR);}if (response_r1 & SDIO_OCR_GENERAL_UNKNOWN_ERROR){return (SDIO_GENERAL_UNKNOWN_ERROR);}if (response_r1 & SDIO_OCR_STREAM_READ_UNDERRUN){return (SDIO_STREAM_READ_UNDERRUN);}if (response_r1 & SDIO_OCR_STREAM_WRITE_OVERRUN){return (SDIO_STREAM_WRITE_OVERRUN);}if (response_r1 & SDIO_OCR_CID_CSD_OVERWRIETE){return (SDIO_CID_CSD_OVERWRITE);}if (response_r1 & SDIO_OCR_WP_ERASE_SKIP){return (SDIO_WP_ERASE_SKIP);}if (response_r1 & SDIO_OCR_CARD_ECC_DISABLED){return (SDIO_CARD_ECC_DISABLED);}if (response_r1 & SDIO_OCR_ERASE_RESET){return (SDIO_ERASE_RESET);}if (response_r1 & SDIO_OCR_AKE_SEQ_ERROR){return (SDIO_AKE_SEQ_ERROR);}return Status;
}/*************************************************************************************************************************
* 函数 : SDIO_Error SDIO_CmdResp3Error(void)
* 功能 : 指令执行状态(R3,OCR)
* 参数 : 无
* 返回 : SDIO_Error
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : 优先处理CRC错误
*************************************************************************************************************************/
SDIO_Error SDIO_CmdResp3Error(void)
{SDIO_Error Status = SDIO_OK;u32 timeout=SDIO_TIMEOUT_US;while (timeout){timeout--;if(SDIO->INTSTS &(SDIO_INT_STATUS_RITO | SDIO_INT_STATUS_CRC)) break;//CRC错误/命令响应超时if((SDIO->CTL & SDIO_CTL_RIEN_Msk) == 0) break; //收到响应了Delay_US(1);} if(timeout==0) //响应超时{ Status = SDIO_CMD_RSP_TIMEOUT; //正常程序不应该执行到此处,如果到了此处请检查原因DEBUG("程序不应该执行到此处(0x%X)\r\n", SDIO->INTSTS);SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}if(SDIO->INTSTS & SDIO_INT_STATUS_RITO) //响应超时{ Status = SDIO_CMD_RSP_TIMEOUT; SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}//SDIO_STA_CCRCFAIL 错误 if(SDIO->INTSTS & SDIO_INT_STATUS_CRC) //命令校验错误{ Status = SDIO_CMD_CRC_FAIL;SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}//清除所有标志SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;
}/*************************************************************************************************************************
* 函数 : SDIO_Error SDIO_CmdResp2Error(void)
* 功能 : 指令执行状态(R2,CID or CSD)
* 参数 : 无
* 返回 : SDIO_Error
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 : R2响应跟R3响应处理完全一致
*************************************************************************************************************************/
SDIO_Error SDIO_CmdResp2Error(void)
{SDIO_Error Status = SDIO_OK;u32 timeout=SDIO_TIMEOUT_US;while (timeout){timeout--;if(SDIO->INTSTS &(SDIO_INT_STATUS_RITO | SDIO_INT_STATUS_CRC)) break;//CRC错误/命令响应超时if((SDIO->CTL & SDIO_CTL_R2IEN_Msk) == 0) break; //收到r2响应了Delay_US(1);} if(timeout==0) //响应超时{ Status = SDIO_CMD_RSP_TIMEOUT; //正常程序不应该执行到此处,如果到了此处请检查原因DEBUG("程序不应该执行到此处(0x%X)\r\n", SDIO->INTSTS);SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}if(SDIO->INTSTS & SDIO_INT_STATUS_RITO) //响应超时{ Status = SDIO_CMD_RSP_TIMEOUT; SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}//SDIO_STA_CCRCFAIL 错误 if(SDIO->INTSTS & SDIO_INT_STATUS_CRC) //命令校验错误{ Status = SDIO_CMD_CRC_FAIL;SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}//清除所有标志SDIO->INTSTS = SDIO->INTSTS; //清除所有标志 return Status;
} /*************************************************************************************************************************
* 函数 : SDIO_Error SDIO_CmdResp6Error(u16*prca)
* 功能 : 指令执行状态(R6,RCA)
* 参数 : prca:RCA缓冲区指针
* 返回 : SDIO_Error
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 :
*************************************************************************************************************************/
SDIO_Error SDIO_CmdResp6Error(u16*prca)
{SDIO_Error Status = SDIO_OK; u32 response_r1;u32 timeout=SDIO_TIMEOUT_US/1;while (timeout){timeout--;if(SDIO->INTSTS &(SDIO_INT_STATUS_RITO | SDIO_INT_STATUS_CRC)) break;//CRC错误/命令响应超时if((SDIO->CTL & SDIO_CTL_RIEN_Msk) == 0) break; //收到响应了Delay_US(1);} if(timeout==0) //响应超时{ Status = SDIO_CMD_RSP_TIMEOUT; //正常程序不应该执行到此处,如果到了此处请检查原因DEBUG("程序不应该执行到此处(0x%X)\r\n", SDIO->INTSTS);SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}if(SDIO->INTSTS & SDIO_INT_STATUS_RITO) //响应超时{ Status = SDIO_CMD_RSP_TIMEOUT; SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}//SDIO_STA_CCRCFAIL 错误 if(SDIO->INTSTS & SDIO_INT_STATUS_CRC) //命令校验错误{ Status = SDIO_CMD_CRC_FAIL;SDIO->INTSTS = SDIO->INTSTS; //清除所有标志return Status;}//清除所有标志SDIO->INTSTS = SDIO->INTSTS; //清除所有标志//We have received response, retrieve it. response_r1 = SDIO_GetResp1CardStatu();if (0 == (response_r1 & (SDIO_R6_GENERAL_UNKNOWN_ERROR | SDIO_R6_ILLEGAL_CMD | SDIO_R6_COM_CRC_FAILED))){*prca = (u16) (response_r1 >> 16);return Status;}if (response_r1 & SDIO_R6_GENERAL_UNKNOWN_ERROR){return (SDIO_GENERAL_UNKNOWN_ERROR);}if (response_r1 & SDIO_R6_ILLEGAL_CMD){return (SDIO_ILLEGAL_CMD);}if (response_r1 & SDIO_R6_COM_CRC_FAILED){return(SDIO_COM_CRC_FAILED);}return Status;
}/*************************************************************************************************************************
* 函数 : SDIO_Error SDIO_WaitTransComplete(bool isEnableReadData, u32 *pDataLen, u32 TimeOutMs)
* 功能 : SDIO等待传输完成(等待读取或写入数据完成)
* 参数 : isEnableReadData:是否为读取数据;pDataLen:返回实际传输的数据长度;TimeOutMs:超时时间,毫秒
* 返回 : SDIO_Error
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-04
* 最后修改时间 : 2020-09-04
* 说明 :
*************************************************************************************************************************/
SDIO_Error SDIO_WaitTransComplete(bool isEnableReadData, u32 *pDataLen, u32 TimeOutMs)
{SDIO_Error Status = SDIO_OK; u32 timeout=TimeOutMs*1000/10+1;u32 cnt= ((SDIO->BLEN&0x7FF)+1) * ((SDIO->CTL>>16)&0xFF); //获取DMA要传输的数据长度,块大小*块数量 *pDataLen = 0; //初始化接收数量为0//等待读取完成while (timeout){timeout--;if(SDIO_DMA->INTSTS & 0x03) break; //传输出错if(SDIO->INTSTS & (SDIO_INT_STATUS_CRC | SDIO_INT_STATUS_BLKD)) break; //传输完成或者CRC校验SDIO_DelayUS(1); }if(timeout == 0) //读取超时{Status = SDIO_DATA_TIMEOUT; //正常程序不应该执行到此处,如果到了此处请检查原因DEBUG("传输超时(0x%X)\r\n", SDIO->INTSTS);SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO_DMA->CTL |= BIT1; //复位SDIO DMASDIO->INTSTS = SDIO->INTSTS; //清除所有标志SDIO_DMA->INTSTS = SDIO_DMA->INTSTS; //清除DMA中断标志goto _error;}if((SDIO_DMA->INTSTS & 0x03) || (SDIO_DMA->CTL & BIT9)) //传输出错,或者DMA还在忙{Status = SDIO_INTERNAL_ERROR; //正常程序不应该执行到此处,如果到了此处请检查原因DEBUG("传输出错(0x%X)\r\n", SDIO->INTSTS);SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO_DMA->CTL |= BIT1; //复位SDIO DMASDIO->INTSTS = SDIO->INTSTS; //清除所有标志SDIO_DMA->INTSTS = SDIO_DMA->INTSTS; //清除DMA中断标志goto _error;}if(SDIO->INTSTS & SDIO_INT_STATUS_CRC) //数据校验错误{Status = SDIO_DATA_CRC_FAIL; SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志goto _error;}if(SDIO->INTSTS & SDIO_INT_STATUS_BLKD) //传输完成{Status = SDIO_OK;SDIO->INTSTS = SDIO->INTSTS; //清除所有标志goto _error;}Status = SDIO_UNSUPPORTED_FEATURE; SDIO->CTL |= SDIO_CTL_SW_RST; //复位SDIO控制器SDIO->INTSTS = SDIO->INTSTS; //清除所有标志_error: *pDataLen = cnt-SDIO_DMA->BCNT; //计算实际传输的数据量return Status;
}/*************************************************************************************************************************
* 函数 : u32 SDIO_GetResp1CardStatu(void)
* 功能 : 获取R1响应的卡状态数据
* 参数 : 无
* 返回 : 响应数据的[39:8],也就是卡状态数据
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-09-09
* 最后修改时间 : 2020-09-09
* 说明 : 响应数据的[39:8]
*************************************************************************************************************************/
__inline u32 SDIO_GetResp1CardStatu(void)
{u32 temp;temp = SDIO->RESP0;//响应数据的[47:16],只取出[39:16]temp <<= 8;temp |= SDIO->RESP1 & 0xFF;//响应数据的[15:8]return temp;
}
//SDIO.h
/************************************************************************************************************** 文件名: sdio.h* 功能: NUC970 sdio驱动* 作者: cp1300@139.com* 创建时间: 2020-09-04* 最后修改时间: 2020-09-04* 详细:
*************************************************************************************************************/
#ifndef _SDIO_H_
#define _SDIO_H_
#include "nuc970_system.h"
#include "sdio_const.h"//SDIO时钟速度定义
typedef enum
{SDIO_CLK_100M = 0,SDIO_CLK_50M = 1,SDIO_CLK_33M = 2,SDIO_CLK_25M = 3,SDIO_CLK_20M = 4,SDIO_CLK_16M = 5,SDIO_CLK_14M = 6,SDIO_CLK_12M = 7, SDIO_CLK_11M = 8,SDIO_CLK_10M = 9, SDIO_CLK_9M = 10,SDIO_CLK_8M = 11,SDIO_CLK_7M = 13,SDIO_CLK_6M = 15,SDIO_CLK_5M = 19,SDIO_CLK_4M = 24,SDIO_CLK_3M = 32,SDIO_CLK_2M = 49,SDIO_CLK_1M = 99,SDIO_CLK_500K = 199,SDIO_CLK_400K = 255,
}SDIO_CLOCK_SPEED;#define SDIO_STA_ALL ((u32)0x000fffff) //所有标志//中断状态掩码
#define SDIO_INT_STATUS_SD1DAT1 (1<<19) //该位反映了SD端口1的DAT1引脚状态
#define SDIO_INT_STATUS_SD0DAT1 (1<<18) //该位反映了SD端口0的DAT1引脚状态
#define SDIO_INT_STATUS_CDPS1 (1<<17) //SD端口1卡插入状态
#define SDIO_INT_STATUS_CDPS0 (1<<16) //SD端口0卡插入状态
#define SDIO_INT_STATUS_DITO (1<<13) //读取数据超时
#define SDIO_INT_STATUS_RITO (1<<12) //响应数据超时
#define SDIO_INT_STATUS_SDIO1 (1<<11) //该位指示SDIO卡1向主机发出中断
#define SDIO_INT_STATUS_SDIO0 (1<<10) //该位指示SDIO卡0向主机发出中断
#define SDIO_INT_STATUS_CD1 (1<<9) //该位指示已插入或卸下SD卡1
#define SDIO_INT_STATUS_CD0 (1<<8) //该位指示已插入或卸下SD卡0
#define SDIO_INT_STATUS_SDDAT0 (1<<7) //当前所选SD端口的DAT0引脚状态
#define SDIO_INT_STATUS_CRC (1<<1) //发生了CRC错误,注意R3命令没有CRC依旧会有此错误
#define SDIO_INT_STATUS_BLKD (1<<0) //该位表示SD主机已完成所有数据输入或数据输出块传输//命令,或数据响应状态
#define SDIO_CTL_SW_RST (1<<14) //复位命令
#define SDIO_CTL_R2IEN_Msk (1<<4) //使能R2响应输入,收到响应后清零
#define SDIO_CTL_DOEN_Msk (1<<3) //使能数据输出,发送完后清零
#define SDIO_CTL_DIEN_Msk (1<<2) //使能数据输入,收到数据后清零
#define SDIO_CTL_RIEN_Msk (1<<1) //使能响应输入,收到响应后清零
#define SDIO_CTL_COEN_Msk (1<<0) //使能命令输出,发送完后清零bool SDIO_Init(void); //SDIO接口初始化
void SDIO_SetClockSpeed(SDIO_CLOCK_SPEED ClkSpeed); //SDIO设置时钟速度
void SDIO_SetBusWide(SDIO_BUS_WIDE BusWide); //SDIO设置总线宽度
void SDIO_SendCommand(u8 CmdIndex,u32 CmdArg,SDIO_WAIT_RESP WaitResp); //SDIO发送一个命令
void SDIO_SetPower(bool isPowerUp); //SDIO电源控制
void SDIO_SetPowerSaving(bool isSaving); //SDIO节能模式开关
void SDIO_StartDataTrans(bool isRead); //SDIO开始数据传输
void SDIO_ClearDataTrans(void); //SDIO清除数据传输
void SDIO_SetDataTransTimeOut(u32 DataIimeOut); //SDIO传输数据超时设置
void SDIO_SetDataTransBlockSize(SDIO_DataBlockSize DataBlockSize); //SDIO传输数据块大小设置
void SDIO_SetDataTransLength(u32 DataLen); //SDIO传输数据长度
SDIO_Error SDIO_WaitTransComplete(bool isEnableReadData, u32 *pDataLen, u32 TimeOutMs);//SDIO等待传输完成(等待读取或写入数据完成)
void SDIO_SetAndStartDataTrans(u8 *pDataBuff, bool isEnableReadData, SDIO_DataBlockSize SDIOBlockSize, u32 TransDataLen);//设置SDIO并开始准备数据传输
u8 SDIO_GetPortSelect(void); //获取当前选择的port(0-1)
void SDIO_SetPortSelect(u8 port); //设置SDIO当前port(0-1)
bool SDIO_SetOut74Clock(void); //SDIO输出74个时钟SDIO_Error SDIO_CmdError(void); //获取指令执行状态(无响应)
SDIO_Error SDIO_CmdResp7Error(void); //获取指令执行状态(R7)
SDIO_Error SDIO_CmdResp1Error(void); //指令执行状态(R1)
SDIO_Error SDIO_CmdResp3Error(void); //指令执行状态(R3,OCR)
SDIO_Error SDIO_CmdResp2Error(void); //指令执行状态(R2,CID or CSD)
SDIO_Error SDIO_CmdResp6Error(u16*prca); //指令执行状态(R6,RCA)
void SDIO_GetResponse(SDIO_RESP_TYPE RespIndex, u32 *pRespData); //获取返回响应状态
__inline u32 SDIO_GetMaxTransBlockCount(void) {return 0xFF;} //获取最大传输块数量,2020-09-13新增接口#endif //_SDIO_H_
//SDIO_CONST.H
/************************************************************************************************************** 文件名 : sdio_const.h* 功能 : SDIO相关常量与状态定义* 作者 : cp1300@139.com* 创建时间 : 2018-03-26* 最后修改时间 : 2018-03-26* 详细:
*************************************************************************************************************/
#ifndef _SDIO_CONST_H_
#define _SDIO_CONST_H_
#include "system.h" //SDIO块大小
typedef enum
{SDIO_DataBlockSize_1B = 0,SDIO_DataBlockSize_2B = 1, SDIO_DataBlockSize_4B = 3, SDIO_DataBlockSize_8B = 7, SDIO_DataBlockSize_16B = 15, SDIO_DataBlockSize_32B = 31, SDIO_DataBlockSize_64B = 63, SDIO_DataBlockSize_128B = 127, SDIO_DataBlockSize_256B = 255, SDIO_DataBlockSize_512B = 511, SDIO_DataBlockSize_1024B = 1023, SDIO_DataBlockSize_2048B = 2047,
}SDIO_DataBlockSize;//SDIO命令响应定义
typedef enum
{SDIO_Response_No = 0, //无应答SDIO_Response_Short = 1, //短应答48bitSDIO_Response_Long = 3, //长应答136bit
}SDIO_WAIT_RESP;//SDIO总线宽度定义
typedef enum
{ SDIO_BusWide_1b = 0, //1BITSDIO_BusWide_4b = 1, //4BIT
}SDIO_BUS_WIDE;//SDIO 各种错误枚举定义
typedef enum
{ //特殊错误定义 SDIO_CMD_CRC_FAIL = (1), /*!< Command response received (but CRC check failed) */SDIO_DATA_CRC_FAIL = (2), /*!< Data bock sent/received (CRC check Failed) */SDIO_CMD_RSP_TIMEOUT = (3), /*!< Command response timeout */SDIO_DATA_TIMEOUT = (4), /*!< Data time out */SDIO_TX_UNDERRUN = (5), /*!< Transmit FIFO under-run */SDIO_RX_OVERRUN = (6), /*!< Receive FIFO over-run */SDIO_START_BIT_ERR = (7), /*!< Start bit not detected on all data signals in widE bus mode */SDIO_CMD_OUT_OF_RANGE = (8), /*!< CMD's argument was out of range.*/SDIO_ADDR_MISALIGNED = (9), /*!< Misaligned address */SDIO_BLOCK_LEN_ERR = (10), /*!< Transferred block length is not allowed for the card or the number of transferred bytes does not match the block length */SDIO_ERASE_SEQ_ERR = (11), /*!< An error in the sequence of erase command occurs.*/SDIO_BAD_ERASE_PARAM = (12), /*!< An Invalid selection for erase groups */SDIO_WRITE_PROT_VIOLATION = (13), /*!< Attempt to program a write protect block */SDIO_LOCK_UNLOCK_FAILED = (14), /*!< Sequence or password error has been detected in unlock command or if there was an attempt to access a locked card */SDIO_COM_CRC_FAILED = (15), /*!< CRC check of the previous command failed */SDIO_ILLEGAL_CMD = (16), /*!< Command is not legal for the card state */SDIO_CARD_ECC_FAILED = (17), /*!< Card internal ECC was applied but failed to correct the data */SDIO_CC_ERROR = (18), /*!< Internal card controller error */SDIO_GENERAL_UNKNOWN_ERROR = (19), /*!< General or Unknown error */SDIO_STREAM_READ_UNDERRUN = (20), /*!< The card could not sustain data transfer in stream read operation. */SDIO_STREAM_WRITE_OVERRUN = (21), /*!< The card could not sustain data programming in stream mode */SDIO_CID_CSD_OVERWRITE = (22), /*!< CID/CSD overwrite error */SDIO_WP_ERASE_SKIP = (23), /*!< only partial address space was erased */SDIO_CARD_ECC_DISABLED = (24), /*!< Command has been executed without using internal ECC */SDIO_ERASE_RESET = (25), /*!< Erase sequence was cleared before executing because an out of erase sequence command was received */SDIO_AKE_SEQ_ERROR = (26), /*!< Error in sequence of authentication. */SDIO_INVALID_VOLTRANGE = (27),SDIO_ADDR_OUT_OF_RANGE = (28),SDIO_SWITCH_ERROR = (29),SDIO_SDIO_DISABLED = (30),SDIO_SDIO_FUNCTION_BUSY = (31),SDIO_SDIO_FUNCTION_FAILED = (32),SDIO_SDIO_UNKNOWN_FUNCTION = (33),//标准错误定义SDIO_INTERNAL_ERROR, SDIO_NOT_CONFIGURED,SDIO_REQUEST_PENDING, SDIO_REQUEST_NOT_APPLICABLE, SDIO_INVALID_PARAMETER, SDIO_UNSUPPORTED_FEATURE, SDIO_UNSUPPORTED_HW, SDIO_ERROR, SDIO_OK = 0
} SDIO_Error; //SDIO响应获取定义
typedef enum
{SDIO_RESP_Short= 0, //获取段响应中的32bit,命令索引数据 u32[0] 卡状态 [31:0]SDIO_RESP_Long = 1, //获取长响应CID或CSD(包括内部CRC7) u32[0] 卡状态 [127:96];[1]:卡状态 [95:64];[2]:卡状态 [63:32];[3]:卡状态 [31:1]0b
}SDIO_RESP_TYPE;//Masks for R6 Response
#define SDIO_R6_GENERAL_UNKNOWN_ERROR ((u32)0x00002000)
#define SDIO_R6_ILLEGAL_CMD ((u32)0x00004000)
#define SDIO_R6_COM_CRC_FAILED ((u32)0x00008000)//Mask for errors Card Status R1 (OCR Register)
#define SDIO_OCR_ADDR_OUT_OF_RANGE ((u32)0x80000000)
#define SDIO_OCR_ADDR_MISALIGNED ((u32)0x40000000)
#define SDIO_OCR_BLOCK_LEN_ERR ((u32)0x20000000)
#define SDIO_OCR_ERASE_SEQ_ERR ((u32)0x10000000)
#define SDIO_OCR_BAD_ERASE_PARAM ((u32)0x08000000)
#define SDIO_OCR_WRITE_PROT_VIOLATION ((u32)0x04000000)
#define SDIO_OCR_LOCK_UNLOCK_FAILED ((u32)0x01000000)
#define SDIO_OCR_COM_CRC_FAILED ((u32)0x00800000)
#define SDIO_OCR_ILLEGAL_CMD ((u32)0x00400000)
#define SDIO_OCR_CARD_ECC_FAILED ((u32)0x00200000)
#define SDIO_OCR_CC_ERROR ((u32)0x00100000)
#define SDIO_OCR_GENERAL_UNKNOWN_ERROR ((u32)0x00080000)
#define SDIO_OCR_STREAM_READ_UNDERRUN ((u32)0x00040000)
#define SDIO_OCR_STREAM_WRITE_OVERRUN ((u32)0x00020000)
#define SDIO_OCR_CID_CSD_OVERWRIETE ((u32)0x00010000)
#define SDIO_OCR_WP_ERASE_SKIP ((u32)0x00008000)
#define SDIO_OCR_CARD_ECC_DISABLED ((u32)0x00004000)
#define SDIO_OCR_ERASE_RESET ((u32)0x00002000)
#define SDIO_OCR_AKE_SEQ_ERROR ((u32)0x00000008)
#define SDIO_OCR_ERRORBITS ((u32)0xFDFFE008)//SDIO 指令集
#define SDIO_CMD_GO_IDLE_STATE ((u8)0)
#define SDIO_CMD_SEND_OP_COND ((u8)1)
#define SDIO_CMD_ALL_SEND_CID ((u8)2)
#define SDIO_CMD_SET_REL_ADDR ((u8)3) /*!< SDIO_SEND_REL_ADDR for SD Card */
#define SDIO_CMD_SET_DSR ((u8)4)
#define SDIO_CMD_SDIO_SEN_OP_COND ((u8)5)
#define SDIO_CMD_HS_SWITCH ((u8)6)
#define SDIO_CMD_SEL_DESEL_CARD ((u8)7)
#define SDIO_CMD_HS_SEND_EXT_CSD ((u8)8)
#define SDIO_CMD_SEND_CSD ((u8)9)
#define SDIO_CMD_SEND_CID ((u8)10)
#define SDIO_CMD_READ_DAT_UNTIL_STOP ((u8)11) /*!< SD Card doesn't support it */
#define SDIO_CMD_STOP_TRANSMISSION ((u8)12)
#define SDIO_CMD_SEND_STATUS ((u8)13)
#define SDIO_CMD_HS_BUSTEST_READ ((u8)14)
#define SDIO_CMD_GO_INACTIVE_STATE ((u8)15)
#define SDIO_CMD_SET_BLOCKLEN ((u8)16)
#define SDIO_CMD_READ_SINGLE_BLOCK ((u8)17)
#define SDIO_CMD_READ_MULT_BLOCK ((u8)18)
#define SDIO_CMD_HS_BUSTEST_WRITE ((u8)19)
#define SDIO_CMD_WRITE_DAT_UNTIL_STOP ((u8)20)
#define SDIO_CMD_SET_BLOCK_COUNT ((u8)23)
#define SDIO_CMD_WRITE_SINGLE_BLOCK ((u8)24)
#define SDIO_CMD_WRITE_MULT_BLOCK ((u8)25)
#define SDIO_CMD_PROG_CID ((u8)26)
#define SDIO_CMD_PROG_CSD ((u8)27)
#define SDIO_CMD_SET_WRITE_PROT ((u8)28)
#define SDIO_CMD_CLR_WRITE_PROT ((u8)29)
#define SDIO_CMD_SEND_WRITE_PROT ((u8)30)
#define SDIO_CMD_SDIO_ERASE_GRP_START ((u8)32) /*!< To set the address of the first writeblock to be erased. (For SD card only) */
#define SDIO_CMD_SDIO_ERASE_GRP_END ((u8)33) /*!< To set the address of the last write block of thecontinuous range to be erased. (For SD card only) */
#define SDIO_CMD_ERASE_GRP_START ((u8)35) /*!< To set the address of the first write block to be erased.(For MMC card only spec 3.31) */#define SDIO_CMD_ERASE_GRP_END ((u8)36) /*!< To set the address of the last write block of thecontinuous range to be erased. (For MMC card only spec 3.31) */#define SDIO_CMD_ERASE ((u8)38)
#define SDIO_CMD_FAST_IO ((u8)39) /*!< SD Card doesn't support it */
#define SDIO_CMD_GO_IRQ_STATE ((u8)40) /*!< SD Card doesn't support it */
#define SDIO_CMD_LOCK_UNLOCK ((u8)42)
#define SDIO_CMD_APP_CMD ((u8)55)
#define SDIO_CMD_GEN_CMD ((u8)56)
#define SDIO_CMD_NO_CMD ((u8)64)/** * @brief Following commands are SD Card Specific commands.* SDIO_APP_CMD :CMD55 should be sent before sending these commands. */
#define SDIO_CMD_APP_SDIO_SET_BUSWIDTH ((u8)6) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_STAUS ((u8)13) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_SEND_NUM_WRITE_BLOCKS ((u8)22) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_OP_COND ((u8)41) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_SET_CLR_CARD_DETECT ((u8)42) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_SEND_SCR ((u8)51) /*!< For SD Card only */
#define SDIO_CMD_SDIO_RW_DIRECT ((u8)52) /*!< For SD I/O Card only */
#define SDIO_CMD_SDIO_RW_EXTENDED ((u8)53) /*!< For SD I/O Card only *//** * @brief Following commands are SD Card Specific security commands.* SDIO_APP_CMD should be sent before sending these commands. */
#define SDIO_CMD_SDIO_APP_GET_MKB ((u8)43) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_GET_MID ((u8)44) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_SET_CER_RN1 ((u8)45) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_GET_CER_RN2 ((u8)46) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_SET_CER_RES2 ((u8)47) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_GET_CER_RES1 ((u8)48) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_SECURE_READ_MULTIPLE_BLOCK ((u8)18) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_SECURE_WRITE_MULTIPLE_BLOCK ((u8)25) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_SECURE_ERASE ((u8)38) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_CHANGE_SECURE_AREA ((u8)49) /*!< For SD Card only */
#define SDIO_CMD_SDIO_APP_SECURE_WRITE_MKB ((u8)48) /*!< For SD Card only */#define SD_VOLTAGE_WINDOW_SD ((u32)0x80100000)
#define SD_HIGH_CAPACITY ((u32)0x40000000)
#define SD_STD_CAPACITY ((u32)0x00000000)
#define SD_CHECK_PATTERN ((u32)0x000001AA)
#define SD_VOLTAGE_WINDOW_MMC ((u32)0x80FF8000)#define SD_MAX_VOLT_TRIAL ((u32)0x0000FFFF)
#define SD_ALLZERO ((u32)0x00000000)#define SD_WIDE_BUS_SUPPORT ((u32)0x00040000)
#define SD_SINGLE_BUS_SUPPORT ((u32)0x00010000)
#define SD_CARD_LOCKED ((u32)0x02000000)
#define SD_CARD_PROGRAMMING ((u32)0x00000007)
#define SD_CARD_RECEIVING ((u32)0x00000006)
#define SD_DATATIMEOUT ((u32)0xFFFFFFFF)
#define SD_0TO7BITS ((u32)0x000000FF)
#define SD_8TO15BITS ((u32)0x0000FF00)
#define SD_16TO23BITS ((u32)0x00FF0000)
#define SD_24TO31BITS ((u32)0xFF000000)
#define SD_MAX_DATA_LENGTH ((u32)0x01FFFFFF)#define SD_HALFFIFO ((u32)0x00000008)
#define SD_HALFFIFOBYTES ((u32)0x00000020)//Command Class Supported
#define SD_CCCC_LOCK_UNLOCK ((u32)0x00000080)
#define SD_CCCC_WRITE_PROT ((u32)0x00000040)
#define SD_CCCC_ERASE ((u32)0x00000020)//CMD8指令
#define SDIO_SEND_IF_COND ((u32)0x00000008)#endif //_SDIO_CONST_H_
//SDIO_SDCARD.C
/************************************************************************************************************** 文件名 : sdio_sdcard.c* 功能 : SDIO接口SD卡驱动* 作者 : cp1300@139.com* 创建时间 : 2018-03-27* 最后修改时间 : 2018-03-27* 详细 : 源程序借鉴了意法STM32F103X库函数分离了底层SDIO接口,但是依赖一些数据定义,在sdio_const中,去掉了MMC卡支持,没遇到过MMC卡异常情况:如果先将卡设置为高速模式,然后再设置为4线模式,之后读取多块超时后会出现CMD12响应3,并且卡不断电情况下无法恢复通讯,所有命令均响应超时。出现了这种异常情况后,必须对卡进行重新断电才能恢复,就算单片机复位都不行。2018-03-29:删除了SCR状态读取,这个状态唯一的作用就是获取卡是否支持4线模式(很少有不支持的,就算不支持,设置失败了也会还原为单线模式),SD卡上电默认必须为单线模式2018-03-30:如果SD卡在读取或者写入数据过程中突然中断(程序复位),但是SD卡没有断电,下次可能会初始化失败,并且必须断电才能恢复。2018-03-30:读取入1-3个扇区时间均为3m,写入1个扇区为4ms,由于通信内部使用了系统延时1ms导致,此时提高系统时钟,SDIO时钟均无效,但是增加这个1ms延时可以有效防止系统卡死,提高OS运行效率2018-03-30:测试写入3个扇区时间为5ms,写入10个扇区为7ms2019-10-31:修复初始化时判断大容量卡时&写成了&=,导致判断错误2020-02-03:修改一次最大的读写限制为65535字节,FAT文件系统一次最大只会读取32KB数据2020-02-21:修改可以设置SD卡块大小,同步修改SDIO块大小,SDHC卡不支持修改块大小,去掉了读写时卡锁定检查(没啥用)2020-09-13:增加获取最大传输块数量功能,修改读写扇区是,检查并清理传输错误,修改响应数据读取接口
*************************************************************************************************************/
#include "sdio_sdcard.h"
#include "system.h"
#include "sdio_const.h"
#include "string.h"// SD卡指令表
#define CMD0 0 //复位所有的卡到idle状态
#define CMD1 1
#define CMD2 2 //通知所有卡通过CMD线返回CID值。
#define CMD3 3 //通知所有卡发布新RCA
#define ACMD6 6 //设置宽总线模式
#define CMD7 7 //选择/取消选择RCA地址卡。
#define CMD8 8 //发送SD卡接口条件,包含主机支持的电压信息,并询问卡是否支持。
#define CMD9 9 //命令9 ,读CSD数据
#define CMD10 10 //命令10,读CID数据
#define CMD12 12 //命令12,停止数据传输
#define CMD13 13 //命令13,选定卡通过CMD线发送它状态寄存器
#define CMD16 16 //命令16,设置SectorSize 应返回0x00
#define CMD17 17 //命令17,读sector
#define CMD18 18 //命令18,读Multi sector
#define ACMD23 23 //命令23,设置多sector写入前预先擦除N个block
#define CMD24 24 //命令24,写sector
#define CMD25 25 //命令25,写Multi sector
#define CMD32 32 //设置擦除的起始块地址
#define CMD33 33 //设置擦除的结束块地址
#define CMD38 38 //擦除预先选定的块#define ACMD41 41 //命令41,主机要求卡发送它的支持信息(HCS)和OCR寄存器内容。
#define CMD55 55 //命令55,指定下个命令为特定应用命令,不是标准命令
#define CMD58 58 //命令58,读OCR信息
#define CMD59 59 //命令59,使能/禁止CRC,应返回0x0#define SD_VOLTAGE_WINDOW_SD ((u32)0x80100000)
#define SD_HIGH_CAPACITY ((u32)0x40000000)
#define SD_STD_CAPACITY ((u32)0x00000000)
#define SD_CHECK_PATTERN ((u32)0x000001AA)#define SD_MAX_VOLT_TRIAL ((u32)0x0000FFFF)
#define SD_ALLZERO ((u32)0x00000000)#define SD_WIDE_BUS_SUPPORT ((u32)0x00040000)
#define SD_SINGLE_BUS_SUPPORT ((u32)0x00010000)
#define SD_CARD_LOCKED ((u32)0x02000000)
#define SD_CARD_PROGRAMMING ((u32)0x00000007)
#define SD_CARD_RECEIVING ((u32)0x00000006)#define SD_0TO7BITS ((u32)0x000000FF)
#define SD_8TO15BITS ((u32)0x0000FF00)
#define SD_16TO23BITS ((u32)0x00FF0000)
#define SD_24TO31BITS ((u32)0xFF000000)
#define SD_MAX_DATA_LENGTH ((u32)0x01FFFFFF)#define SD_HALFFIFO ((u32)0x00000008)
#define SD_HALFFIFOBYTES ((u32)0x00000020)/* Command Class Supported */
#define SD_CCCC_LOCK_UNLOCK ((u32)0x00000080)
#define SD_CCCC_WRITE_PROT ((u32)0x00000040)
#define SD_CCCC_ERASE ((u32)0x00000020)/* Following commands are SD Card Specific commands.SDIO_APP_CMD should be sent before sending these commands. */
#define SDIO_SEND_IF_COND ((u32)0x00000008)//调试开关
#define SDCARD_DBUG 1
#if SDCARD_DBUG#include "system.h"#define sdcard_debug(format,...) uart_printf(format,##__VA_ARGS__)
#else#define sdcard_debug(format,...) /\
/#endif //SDCARD_DBUGSDIO_Error SD_SetIdleSta(SDIO_SD_HANDLE *pHandle); //SD卡上电进入空闲模式,并识别卡
SDIO_Error SD_InitializeCards(SDIO_SD_HANDLE *pHandle); //将所有的卡进行初始化配置
static SDIO_Error SDEnableWideBus(SDIO_SD_HANDLE *pHandle, bool isEnableWideBus); //设置SD卡宽总线模式
SDIO_Error SD_SetBlockSize(SDIO_SD_HANDLE *pHandle, u16 BlockSize); //设置SD卡块大小
SDIO_Error SD_EnableWideBusMode(SDIO_SD_HANDLE *pHandle, bool isEnable4BusWide); //使能4bit DAT线模式,如果失败将保持原来模式
SDIO_Error SD_SelectDeselect(SDIO_SD_HANDLE *pHandle, u32 addr); //选中一个卡,并处于传输状态
SDIO_Error SD_GetCardInfo(SDIO_SD_HANDLE *pHandle, SD_CardInfo *cardinfo); //获取卡的细节信息
SDIO_Error SD_WaitProgrammingFinish(SDIO_SD_HANDLE *pHandle, u32 TimeOutMs); //等待编程完成
SDIO_Error SD_GetSDSCR(SDIO_SD_HANDLE *pHandle, u32 pSCR[2]); //获取SCR信息/*************************************************************************************************************************
* 函数 : void SD_InterfaceInit(SDIO_SD_HANDLE *pHandle,const SDIO_SD_INTERFACE *pInterface)
* 功能 : SD卡底层接口初始化
* 参数 : pHandle:句柄;pInterface:通信接口
* 返回 : 无
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 2018-03-27
* 最后修改时间 : 2018-03-27
* 说明 : 初始化SD卡
*************************************************************************************************************************/
void SD_InterfaceInit(SDIO_SD_HANDLE *pHandle, const SDIO_SD_INTERFACE *pInterface)
{pHandle->pInterface = pInterface; //初始化通信接口
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_ReadStatus(SDIO_SD_HANDLE *pHandle, u32 *pSCR)
* 功能 : 读取SD卡状态寄存器
* 参数 : pHandle:句柄;pSCR:状态寄存器的值
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 2018-03-29
* 最后修改时间 : 2018-03-29
* 说明 : 必须在初始化SD卡成功后才能调用,因为需要SD卡的RCA地址
**************************************************************************************************************************/
SDIO_Error SD_ReadStatus(SDIO_SD_HANDLE *pHandle, u32 *pSCR)
{SDIO_Error errorstatus = SDIO_OK;//发送CMD13,SDIO_SEND_STATUS,读 Card_Status 寄存器,参数,RCA地址,短返回,R1;pHandle->pInterface->SDIO_SendCommand(CMD13, (u32) pHandle->RCA << 16, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输DEBUG("CMD13 error (%d)!\r\n",errorstatus);return(errorstatus);}pHandle->pInterface->SDIO_GetResponse(SDIO_RESP_Short, pSCR); //获取响应的状态return(errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_Init(SDIO_SD_HANDLE *pHandle)
* 功能 : SD卡初始化
* 参数 : pHandle:句柄
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 2018-03-27
* 最后修改时间 : 2018-03-27
* 说明 : 初始化SD卡,需要提前初始化通信接口,调用:SD_InterfaceInit
*************************************************************************************************************************/
SDIO_Error SD_Init(SDIO_SD_HANDLE *pHandle)
{SDIO_Error errorstatus = SDIO_OK;if(pHandle == NULL || pHandle->pInterface == NULL){DEBUG("SDIO 接口无效!\n"); //调试,打印错误return SDIO_ERROR;}//初始化相关变量memset(&pHandle->SDCardInfo, 0, sizeof(SD_CardInfo));pHandle->SDIO_BlockSize = SDIO_DataBlockSize_512B; //设置默认的SDIO传输块大小:512字节pHandle->pInterface->SDIO_Init(); //SDIO硬件初始化pHandle->MaxTransBlockCount = pHandle->pInterface->SDIO_GetMaxTransBlockCount();//从SDIO驱动层获取一次最大传输块数量errorstatus = SD_SetIdleSta(pHandle); //SD卡上电if (errorstatus != SDIO_OK) //卡上电发送错误{DEBUG("SD power up error:(%d)!\n",errorstatus); //调试,打印错误return (errorstatus); //返回错误}errorstatus = SD_InitializeCards(pHandle);if (errorstatus != SDIO_OK){DEBUG("SD initialize error(%d)!\r\n",errorstatus); //调试,打印错误return (errorstatus);}//更新卡信息SD_GetCardInfo(pHandle, &pHandle->SDCardInfo); //获取卡信息uart_printf("SD卡容量:%llu\r\n", pHandle->SDCardInfo.CardCapacity);return (errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_Reset(SDIO_SD_HANDLE *pHandle)
* 功能 : 复位并检查卡,不支持MMC卡
* 参数 : pHandle:接口句柄
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 2018-03-29
* 最后修改时间 : 2018-03-29
* 说明 : SD卡上电进入空闲模式(不带响应),并使用CMD55检查卡是否通讯正常
*************************************************************************************************************************/
SDIO_Error SD_Reset(SDIO_SD_HANDLE *pHandle)
{SDIO_Error errorstatus = SDIO_OK;u32 i;//循环发生发送CMD0,无响应,无返回,让SD卡进入空闲模式-无响应,无返回,多发送几次for(i = 0; i < 10; i ++){pHandle->pInterface->SDIO_SendCommand(CMD0, 0, SDIO_Response_No); //发送命令 pHandle->pInterface->SDIO_CmdError(); //判断命令是否执行成功,此命令只要初始化了SDIO就会执行成功SYS_DelayMS(1);//发送CMD55pHandle->pInterface->SDIO_SendCommand(CMD55, 0, SDIO_Response_Short); //发送命令errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); if(errorstatus == SDIO_OK) //卡检测成功后再发送一次CMD0,否则后面的CMD8将不会响应{pHandle->pInterface->SDIO_SendCommand(CMD0, 0, SDIO_Response_No); //发送命令 pHandle->pInterface->SDIO_CmdError(); //判断命令是否执行成功,此命令只要初始化了SDIO就会执行成功return SDIO_OK;//SD卡}else if(errorstatus == SDIO_CMD_CRC_FAIL) //我的一个v1.1的卡在此处会返回校验错误{pHandle->pInterface->SDIO_SendCommand(CMD0, 0, SDIO_Response_No); //发送命令 pHandle->pInterface->SDIO_CmdError(); //判断命令是否执行成功,此命令只要初始化了SDIO就会执行成功return SDIO_OK;//SD卡}}DEBUG("没有检测到SD卡(错误:%d)\r\n", errorstatus);return errorstatus;
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_SetIdleSta(SDIO_SD_HANDLE *pHandle)
* 功能 : SD卡上电进入空闲模式,并识别卡
* 参数 : pHandle:接口句柄
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 20120516
* 最后修改时间 : 2018-03-28
* 说明 : SD卡上电进入空闲模式
*************************************************************************************************************************/
SDIO_Error SD_SetIdleSta(SDIO_SD_HANDLE *pHandle)
{SDIO_Error errorstatus = SDIO_OK;u32 response = 0, count = 0;bool validvoltage = FALSE;u32 SDType = SD_STD_CAPACITY;pHandle->pInterface->SDIO_SetLowSpeedClock(); //设置为低速时钟//发送至少74个时钟,等待SD卡上电成功并同步,要求SDIO一直输出时钟SYS_DelayMS(5);errorstatus = SD_Reset(pHandle); //复位并检查卡if(errorstatus != SDIO_OK) return errorstatus;//发送CMD8之前一定要发送一个CMD0-此处为重复发送(保险起见),因为在SD_Reset中已经发送pHandle->pInterface->SDIO_SendCommand(CMD0, 0, SDIO_Response_No); //发送命令 pHandle->pInterface->SDIO_CmdError(); //判断命令是否执行成功,此命令只要初始化了SDIO就会执行成功//发送CMD8:SEND_IF_COND;短响应,命令参数:SD_CHECK_PATTERN;返回响应R7//识别卡版本pHandle->pInterface->SDIO_SendCommand(CMD8, SD_CHECK_PATTERN, SDIO_Response_Short); //发送命令errorstatus = pHandle->pInterface->SDIO_CmdResp7Error(); //获取响应R7if (errorstatus == SDIO_OK) //返回成功;说明卡为SD Card 2.0 V2.0{pHandle->CardType = SD_STD_CAPACITY_SD_CARD_V2_0; //SD Card 2.0SDType = SD_HIGH_CAPACITY; sdcard_debug("SD_STD_CAPACITY_SD_CARD_V2_0!\r\n");}else //V1.0 V1.1{pHandle->CardType = SD_STD_CAPACITY_SD_CARD_V1_1; //V1.0 V1.1sdcard_debug("SD Card V1.1!\r\n"); //调试,打印信息pHandle->pInterface->SDIO_SendCommand(CMD55, 0, SDIO_Response_Short); //发送命令errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); }//发送CMD55pHandle->pInterface->SDIO_SendCommand(CMD55, 0, SDIO_Response_Short); //发送命令errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); if(errorstatus == SDIO_OK) //SD卡{do{//发送CMD55pHandle->pInterface->SDIO_SendCommand(CMD55, 0, SDIO_Response_Short); //发送命令errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if(errorstatus != SDIO_OK) //SD卡{DEBUG("CMD55 error(%d)!\r\n",errorstatus); //调试,打印错误信息return(errorstatus);}//发送ACMD41命令;命令参数:SD_APP_OP_COND(0x80100000);短响应.响应为R3,返回操作条件寄存器RCA-注意,这个命令的响应是不带CRC的,会报CRC错误,忽略即可pHandle->pInterface->SDIO_SendCommand(ACMD41, SD_VOLTAGE_WINDOW_SD | SDType, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp3Error();if (errorstatus != SDIO_OK && errorstatus != SDIO_CMD_CRC_FAIL) //CMD CRC错误也是正常的,ACM41不响应CRC {DEBUG("ACM41 error(%d)!\r\n",errorstatus); //调试,打印错误信息return(errorstatus);}errorstatus = SDIO_OK;pHandle->pInterface->SDIO_GetResponse(SDIO_RESP_Short, &response); //获取响应,RESE1validvoltage = (bool) (((response >> 31) == 1) ? 1 : 0);count++;}while((!validvoltage) && (count < SD_MAX_VOLT_TRIAL)); //循环初始化,直到返回成功或者超时if (count >= SD_MAX_VOLT_TRIAL) //重试次数超出{errorstatus = SDIO_INVALID_VOLTRANGE;return (errorstatus);}if (response & SD_HIGH_CAPACITY) //大容量卡,超过4G以上{uart_printf("SD卡是大容量卡!\r\n");pHandle->CardType = SD_HIGH_CAPACITY_SD_CARD;}}return(errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_InitializeCards(SDIO_SD_HANDLE *pHandle)
* 功能 : 将所有的卡进行初始化配置
* 参数 : pHandle:句柄
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 20120516
* 最后修改时间 : 2018-03-28
* 说明 : 将所有的卡进行初始化配置
*************************************************************************************************************************/
SDIO_Error SD_InitializeCards(SDIO_SD_HANDLE *pHandle)
{SDIO_Error errorstatus = SDIO_OK;u16 rca = 0x01;if(SD_SECURE_DIGITAL_IO_CARD != pHandle->CardType) //非SECURE_DIGITAL_IO_CARD{//发送CMD2 SDIO_ALL_SEND_CID命令,命令参数:0;长回复,R2pHandle->pInterface->SDIO_SendCommand(CMD2, 0, SDIO_Response_Long);errorstatus = pHandle->pInterface->SDIO_CmdResp2Error(); //获取响应R2if (errorstatus != SDIO_OK){DEBUG("CMD2 error!(%d)\r\n",errorstatus); //调试,打印错误信息return (errorstatus);}//到每个卡以获取每个卡的唯一标识CIDpHandle->pInterface->SDIO_GetResponse(SDIO_RESP_Long, pHandle->CID_Tab); }if((SD_STD_CAPACITY_SD_CARD_V1_1 == pHandle->CardType)||(SD_STD_CAPACITY_SD_CARD_V2_0 == pHandle->CardType)||(SD_SECURE_DIGITAL_IO_COMBO_CARD == pHandle->CardType)||(SD_HIGH_CAPACITY_SD_CARD == pHandle->CardType))//判断卡类型{//发送CMD3,短响应R6pHandle->pInterface->SDIO_SendCommand(CMD3, 0, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp6Error(&rca); //获取响应R6if (errorstatus != SDIO_OK){DEBUG("CMD3 error!(%d)\r\n",errorstatus); //调试,打印错误信息return (errorstatus);} } if (SD_MULTIMEDIA_CARD == pHandle->CardType){//发送CMD3,短响应R2pHandle->pInterface->SDIO_SendCommand(CMD3, (u32)(rca<<16), SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp2Error(); //获取响应R2if (errorstatus != SDIO_OK){DEBUG("CMD3 error!(%d)\r\n",errorstatus); //调试,打印错误信息return (errorstatus);} }if (SD_SECURE_DIGITAL_IO_CARD != pHandle->CardType) //非SECURE_DIGITAL_IO_CARD{//发送CMD9+卡RCA,取得CSD,长响应 R2;给卡发送一个新的RCA,主要是用来设置卡地址的pHandle->pInterface->SDIO_SendCommand(CMD9, (u32)(rca<<16), SDIO_Response_Long);errorstatus = pHandle->pInterface->SDIO_CmdResp2Error(); //获取响应R2if (errorstatus != SDIO_OK){DEBUG("CMD3 error!(%d)\r\n",errorstatus); //调试,打印错误信息return (errorstatus);} pHandle->pInterface->SDIO_GetResponse(SDIO_RESP_Long, pHandle->CSD_Tab); }pHandle->RCA = rca; //存储卡地址//选中卡,并激活errorstatus = SD_SelectDeselect(pHandle, (u32)pHandle->RCA << 16);if(errorstatus != SDIO_OK){DEBUG("SelectDeselect error!(%d)!\r\n",errorstatus); //调试,打印错误信息return(errorstatus);}//先设置为4线模式,然后再设置为高速时钟//设置宽总线模式-一定要使用低速时钟,否则会出现卡不响应,并且不可恢复,必须对卡进行重新上电errorstatus = SDIO_OK; /* All cards get intialized */errorstatus = SD_EnableWideBusMode(pHandle, TRUE); //配置SD卡为4线模式if(errorstatus == SDIO_OK) {sdcard_debug("SD SDIO 4BIT OK\r\n");}else //设置失败了,还原为1线模式{errorstatus = SD_EnableWideBusMode(pHandle, FALSE); //还原为1线模式DEBUG("SD SDIO 4BIT ERROR (%d)\r\n",errorstatus);}//提高时钟速度-必须放到最后,否则会出现卡各种不响应,必须对卡重新上电pHandle->pInterface->SDIO_SetHighpeedClock(); //设置高速时钟//读取SD卡SCR信息,第一次使用数据线通信errorstatus = SD_GetSDSCR(pHandle, pHandle->SCR_Tab); //获取SCR信息//配置SD卡的块大小,之后不要再修改了,固定块大小进行传输errorstatus = SD_SetBlockSize(pHandle, SD_BLOCK_SIZE); //配置SD卡块大小 if (errorstatus != SDIO_OK){DEBUG("SD SetBlockSize error(%d)!\r\n",errorstatus);return(errorstatus);}return(errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_SelectDeselect(SDIO_SD_HANDLE *pHandle, u32 addr)
* 功能 : 选中一个卡,并处于传输状态
* 参数 : pHandle:句柄;addr:卡地址
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 20120516
* 最后修改时间 : 2018-03-28
* 说明 : 选择一个卡并将它置于传输状态(Transfer state)CMD7用来选定和取消指定的卡,卡在待机状态下还不能进行数据通信,因为总线上可能有多个卡都是出于待机状态,必须选择一个RCA地址目标卡使其进入传输状态才可以进行数据通信。同时通过CMD7命令也可以让已经被选择的目标卡返回到待机状态。
*************************************************************************************************************************/
SDIO_Error SD_SelectDeselect(SDIO_SD_HANDLE *pHandle, u32 addr)
{SDIO_Error errorstatus = SDIO_OK;//CMD7用来选择一个卡并将它置于传输状态(Transfer state),在任何时间只能有一个卡处于传输状态pHandle->pInterface->SDIO_SendCommand(CMD7, addr, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();return(errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_GetCardInfo(SDIO_SD_HANDLE *pHandle, SD_CardInfo *cardinfo)
* 功能 : 获取卡的细节信息
* 参数 : pHandle:句柄;cardinfo:卡信息结构指针,指向信息存放缓冲区地址
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 20120516
* 最后修改时间 : 2018-03-28
* 说明 : 获取卡的信息,通过CSD信息得到
*************************************************************************************************************************/
SDIO_Error SD_GetCardInfo(SDIO_SD_HANDLE *pHandle, SD_CardInfo *cardinfo)
{SDIO_Error errorstatus = SDIO_OK;u8 tmp=0; cardinfo->CardType=(u8)pHandle->CardType; //卡类型cardinfo->RCA=(u16)pHandle->RCA; //卡RCA值tmp=(u8)((pHandle->CSD_Tab[0]&0xFF000000)>>24);cardinfo->SD_csd.CSDStruct=(tmp&0xC0)>>6; //CSD结构cardinfo->SD_csd.SysSpecVersion=(tmp&0x3C)>>2; //2.0协议还没定义这部分(为保留),应该是后续协议定义的cardinfo->SD_csd.Reserved1=tmp&0x03; //2个保留位 tmp=(u8)((pHandle->CSD_Tab[0]&0x00FF0000)>>16); //第1个字节cardinfo->SD_csd.TAAC=tmp; //数据读时间1tmp=(u8)((pHandle->CSD_Tab[0]&0x0000FF00)>>8); //第2个字节cardinfo->SD_csd.NSAC=tmp; //数据读时间2tmp=(u8)(pHandle->CSD_Tab[0]&0x000000FF); //第3个字节cardinfo->SD_csd.MaxBusClkFrec=tmp; //传输速度 tmp=(u8)((pHandle->CSD_Tab[1]&0xFF000000)>>24); //第4个字节cardinfo->SD_csd.CardComdClasses=tmp<<4; //卡指令类高四位tmp=(u8)((pHandle->CSD_Tab[1]&0x00FF0000)>>16); //第5个字节cardinfo->SD_csd.CardComdClasses|=(tmp&0xF0)>>4; //卡指令类低四位cardinfo->SD_csd.RdBlockLen=tmp&0x0F; //最大读取数据长度tmp=(u8)((pHandle->CSD_Tab[1]&0x0000FF00)>>8); //第6个字节cardinfo->SD_csd.PartBlockRead=(tmp&0x80)>>7; //允许分块读cardinfo->SD_csd.WrBlockMisalign=(tmp&0x40)>>6; //写块错位cardinfo->SD_csd.RdBlockMisalign=(tmp&0x20)>>5; //读块错位cardinfo->SD_csd.DSRImpl=(tmp&0x10)>>4;cardinfo->SD_csd.Reserved2=0; //保留if((pHandle->CardType==SD_STD_CAPACITY_SD_CARD_V1_1)||(pHandle->CardType==SD_STD_CAPACITY_SD_CARD_V2_0)||(pHandle->CardType == SD_MULTIMEDIA_CARD))//标准1.1/2.0卡/MMC卡{cardinfo->SD_csd.DeviceSize=(tmp&0x03)<<10; //C_SIZE(12位)tmp=(u8)(pHandle->CSD_Tab[1]&0x000000FF); //第7个字节 cardinfo->SD_csd.DeviceSize|=(tmp)<<2;tmp=(u8)((pHandle->CSD_Tab[2]&0xFF000000)>>24); //第8个字节 cardinfo->SD_csd.DeviceSize|=(tmp&0xC0)>>6;cardinfo->SD_csd.MaxRdCurrentVDDMin=(tmp&0x38)>>3;cardinfo->SD_csd.MaxRdCurrentVDDMax=(tmp&0x07);tmp=(u8)((pHandle->CSD_Tab[2]&0x00FF0000)>>16); //第9个字节 cardinfo->SD_csd.MaxWrCurrentVDDMin=(tmp&0xE0)>>5;cardinfo->SD_csd.MaxWrCurrentVDDMax=(tmp&0x1C)>>2;cardinfo->SD_csd.DeviceSizeMul=(tmp&0x03)<<1; //C_SIZE_MULTtmp=(u8)((pHandle->CSD_Tab[2]&0x0000FF00)>>8); //第10个字节 cardinfo->SD_csd.DeviceSizeMul|=(tmp&0x80)>>7;cardinfo->CardCapacity=(cardinfo->SD_csd.DeviceSize+1); //计算卡容量cardinfo->CardCapacity*=(1<<(cardinfo->SD_csd.DeviceSizeMul+2));cardinfo->CardBlockSize=1<<(cardinfo->SD_csd.RdBlockLen); //块大小cardinfo->CardCapacity*=cardinfo->CardBlockSize;}else if(pHandle->CardType==SD_HIGH_CAPACITY_SD_CARD) //高容量卡{tmp=(u8)(pHandle->CSD_Tab[1]&0x000000FF); //第7个字节 cardinfo->SD_csd.DeviceSize=(tmp&0x3F)<<16;//C_SIZEtmp=(u8)((pHandle->CSD_Tab[2]&0xFF000000)>>24); //第8个字节 cardinfo->SD_csd.DeviceSize|=(tmp<<8);tmp=(u8)((pHandle->CSD_Tab[2]&0x00FF0000)>>16); //第9个字节 cardinfo->SD_csd.DeviceSize|=(tmp);tmp=(u8)((pHandle->CSD_Tab[2]&0x0000FF00)>>8); //第10个字节 cardinfo->CardCapacity=(u64)(cardinfo->SD_csd.DeviceSize+1)*512*1024;//计算卡容量cardinfo->CardBlockSize=512; //块大小固定为512字节} cardinfo->SD_csd.EraseGrSize=(tmp&0x40)>>6;cardinfo->SD_csd.EraseGrMul=(tmp&0x3F)<<1; tmp=(u8)(pHandle->CSD_Tab[2]&0x000000FF); //第11个字节 cardinfo->SD_csd.EraseGrMul|=(tmp&0x80)>>7;cardinfo->SD_csd.WrProtectGrSize=(tmp&0x7F);tmp=(u8)((pHandle->CSD_Tab[3]&0xFF000000)>>24); //第12个字节 cardinfo->SD_csd.WrProtectGrEnable=(tmp&0x80)>>7;cardinfo->SD_csd.ManDeflECC=(tmp&0x60)>>5;cardinfo->SD_csd.WrSpeedFact=(tmp&0x1C)>>2;cardinfo->SD_csd.MaxWrBlockLen=(tmp&0x03)<<2; tmp=(u8)((pHandle->CSD_Tab[3]&0x00FF0000)>>16); //第13个字节cardinfo->SD_csd.MaxWrBlockLen|=(tmp&0xC0)>>6;cardinfo->SD_csd.WriteBlockPaPartial=(tmp&0x20)>>5;cardinfo->SD_csd.Reserved3=0;cardinfo->SD_csd.ContentProtectAppli=(tmp&0x01); tmp=(u8)((pHandle->CSD_Tab[3]&0x0000FF00)>>8); //第14个字节cardinfo->SD_csd.FileFormatGrouop=(tmp&0x80)>>7;cardinfo->SD_csd.CopyFlag=(tmp&0x40)>>6;cardinfo->SD_csd.PermWrProtect=(tmp&0x20)>>5;cardinfo->SD_csd.TempWrProtect=(tmp&0x10)>>4;cardinfo->SD_csd.FileFormat=(tmp&0x0C)>>2;cardinfo->SD_csd.ECC=(tmp&0x03); tmp=(u8)(pHandle->CSD_Tab[3]&0x000000FF); //第15个字节cardinfo->SD_csd.CSD_CRC=(tmp&0xFE)>>1;cardinfo->SD_csd.Reserved4=1; tmp=(u8)((pHandle->CID_Tab[0]&0xFF000000)>>24); //第0个字节cardinfo->SD_cid.ManufacturerID=tmp; tmp=(u8)((pHandle->CID_Tab[0]&0x00FF0000)>>16); //第1个字节cardinfo->SD_cid.OEM_AppliID=tmp<<8; tmp=(u8)((pHandle->CID_Tab[0]&0x000000FF00)>>8); //第2个字节cardinfo->SD_cid.OEM_AppliID|=tmp; tmp=(u8)(pHandle->CID_Tab[0]&0x000000FF); //第3个字节 cardinfo->SD_cid.ProdName1=tmp<<24; tmp=(u8)((pHandle->CID_Tab[1]&0xFF000000)>>24); //第4个字节cardinfo->SD_cid.ProdName1|=tmp<<16; tmp=(u8)((pHandle->CID_Tab[1]&0x00FF0000)>>16); //第5个字节cardinfo->SD_cid.ProdName1|=tmp<<8; tmp=(u8)((pHandle->CID_Tab[1]&0x0000FF00)>>8); //第6个字节cardinfo->SD_cid.ProdName1|=tmp; tmp=(u8)(pHandle->CID_Tab[1]&0x000000FF); //第7个字节cardinfo->SD_cid.ProdName2=tmp; tmp=(u8)((pHandle->CID_Tab[2]&0xFF000000)>>24); //第8个字节cardinfo->SD_cid.ProdRev=tmp; tmp=(u8)((pHandle->CID_Tab[2]&0x00FF0000)>>16); //第9个字节cardinfo->SD_cid.ProdSN=tmp<<24; tmp=(u8)((pHandle->CID_Tab[2]&0x0000FF00)>>8); //第10个字节cardinfo->SD_cid.ProdSN|=tmp<<16; tmp=(u8)(pHandle->CID_Tab[2]&0x000000FF); //第11个字节cardinfo->SD_cid.ProdSN|=tmp<<8; tmp=(u8)((pHandle->CID_Tab[3]&0xFF000000)>>24); //第12个字节cardinfo->SD_cid.ProdSN|=tmp; tmp=(u8)((pHandle->CID_Tab[3]&0x00FF0000)>>16); //第13个字节cardinfo->SD_cid.Reserved1|=(tmp&0xF0)>>4;cardinfo->SD_cid.ManufactDate=(tmp&0x0F)<<8; tmp=(u8)((pHandle->CID_Tab[3]&0x0000FF00)>>8); //第14个字节cardinfo->SD_cid.ManufactDate|=tmp; tmp=(u8)(pHandle->CID_Tab[3]&0x000000FF); //第15个字节cardinfo->SD_cid.CID_CRC=(tmp&0xFE)>>1;cardinfo->SD_cid.Reserved2=1; return errorstatus;
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_EnableWideBusMode(SDIO_SD_HANDLE *pHandle, bool isEnable4BusWide)
* 功能 : 使能4bit DAT线模式,如果失败将保持原来模式
* 参数 : pHandle:服务器句柄;Enable:4bit模式;Disable:1bit模式
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 20120516
* 最后修改时间 : 2018-03-28
* 说明 : 设置卡和控制器为4bit总线模式,线设置开启4线模式再开启高速时钟,必须先激活选中卡
*************************************************************************************************************************/
SDIO_Error SD_EnableWideBusMode(SDIO_SD_HANDLE *pHandle, bool isEnable4BusWide)
{SDIO_Error errorstatus = SDIO_OK;//SD卡不支持设置if (pHandle->CardType == SD_MULTIMEDIA_CARD){errorstatus = SDIO_UNSUPPORTED_FEATURE;return(errorstatus);}else //SD卡{if (isEnable4BusWide) //4BIT模式{errorstatus = SDEnableWideBus(pHandle, TRUE); //设置SD卡为4bit总线模式if (errorstatus == SDIO_OK) //设置SD卡成功{pHandle->pInterface->SDIO_Set4BitBusWide(); //设置SDIO控制器为4线模式} }else{errorstatus = SDEnableWideBus(pHandle, FALSE); //设置SD卡为1bit总线模式if (errorstatus == SDIO_OK) //设置SD卡成功{pHandle->pInterface->SDIO_Set1BitBusWide(); //设置SDIO控制器为1线模式}}} return (errorstatus);
}/*************************************************************************************************************************
* 函数 : static SDIO_Error SDEnableWideBus(SDIO_SD_HANDLE *pHandle, bool isEnableWideBus)
* 功能 : 设置SD卡宽总线模式
* 参数 : pHandle:服务器句柄;isEnableWideBus:4bit模式;Disable:1bit模式
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 20120516
* 最后修改时间 : 2018-03-28
* 说明 : 设置SD卡宽总线模式-需要先读取RCA
*************************************************************************************************************************/
static SDIO_Error SDEnableWideBus(SDIO_SD_HANDLE *pHandle, bool isEnableWideBus)
{SDIO_Error errorstatus = SDIO_OK;u32 temp;pHandle->pInterface->SDIO_GetResponse(SDIO_RESP_Short, &temp); //获取响应if (temp & SD_CARD_LOCKED) //锁了{errorstatus = SDIO_LOCK_UNLOCK_FAILED;return(errorstatus);}if (isEnableWideBus) //使能4bit dat{//发送CMD55,SDIO_APP_CMD,激活卡//发送CMD55 SDIO_APP_CMD;命令参数:RCA;返回响应R1,设置RCA为0,短响应pHandle->pInterface->SDIO_SendCommand(CMD55, (u32)pHandle->RCA << 16, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if (errorstatus != SDIO_OK){DEBUG("CMD55 error(%d)!\r\n",errorstatus); //调试,打印错误信息return (errorstatus);}//发送ACMD6,SDIO_APP_SD_SET_BUSWIDTH,设置宽总线模式,参数0x2,短响应,R1pHandle->pInterface->SDIO_SendCommand(ACMD6, 0x2, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if (errorstatus != SDIO_OK){DEBUG("ACMD6 error(%d)!\r\n",errorstatus); //调试,打印错误信息return (errorstatus);}return (errorstatus);} else //设置为单线模式{//发送CMD55 SDIO_APP_CMD;命令参数:RCA;返回响应R1,设置RCA为0,短响应pHandle->pInterface->SDIO_SendCommand(CMD55, (u32)pHandle->RCA << 16, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if (errorstatus != SDIO_OK){DEBUG("CMD55 error(%d)!\r\n",errorstatus); //调试,打印错误信息return (errorstatus);}//发送ACMD6,SDIO_APP_SD_SET_BUSWIDTH,设置宽总线模式,参数0x0,短响应,R1pHandle->pInterface->SDIO_SendCommand(ACMD6, 0x0, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if (errorstatus != SDIO_OK){DEBUG("ACMD6 error(%d)!\r\n",errorstatus); //调试,打印错误信息return (errorstatus);}return(errorstatus);}
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_SetBlockSize(SDIO_SD_HANDLE *pHandle, u16 BlockSize)
* 功能 : 设置SD卡块大小
* 参数 : pHandle:句柄;BlockSize:设置SD卡块大小
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 20120516
* 最后修改时间 : 2018-03-28
* 说明 : 通常取512B2020-02-21:尝试设置不同大小的块,以提高速度,但是SDHC卡是不支持设置块大小的
*************************************************************************************************************************/
SDIO_Error SD_SetBlockSize(SDIO_SD_HANDLE *pHandle, u16 BlockSize)
{SDIO_Error errorstatus = SDIO_OK;u32 temp;//如果卡锁定了则返回pHandle->pInterface->SDIO_GetResponse(SDIO_RESP_Short, &temp); //获取响应if (temp & SD_CARD_LOCKED) //锁了{errorstatus = SDIO_LOCK_UNLOCK_FAILED;return(errorstatus);}//CMD16 Set Block Size for Card pHandle->pInterface->SDIO_SendCommand(CMD16, BlockSize, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if (errorstatus != SDIO_OK){DEBUG("CMD16 ERROR %d\r\n",errorstatus);//如果失败了,尝试切换到512字节//CMD16 Set Block Size for Card pHandle->pInterface->SDIO_SendCommand(CMD16, 512, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if (errorstatus != SDIO_OK){DEBUG("恢复默认配置失败 ,CMD16 ERROR %d\r\n",errorstatus);}else{pHandle->SDIO_BlockSize = GetSDIO_BlockSizeForSDBlock(512); //SDIO传输块大小DEBUG("使用默认512B块大小,设置成功\r\n");}}else //设置成功了{pHandle->SDIO_BlockSize = GetSDIO_BlockSizeForSDBlock(BlockSize); //SDIO传输块大小}return(errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_ReadBlock(SDIO_SD_HANDLE *pHandle, u32 BlockAddr, u8 *pBlockBuff)
* 功能 : 读SD卡一个块
* 参数 : pHandle:句柄;BlockAddr:块地址;BlockBuff:块缓冲区地址
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 20120516
* 最后修改时间 : 2018-03-28
* 说明 : 读SD卡一个块
*************************************************************************************************************************/
SDIO_Error SD_ReadBlock(SDIO_SD_HANDLE *pHandle, u32 BlockAddr, u8 *pBlockBuff)
{SDIO_Error errorstatus = SDIO_OK;u32 ReadLen; //读取的数据长度if (pBlockBuff == NULL) //没有分配接收缓冲区,返回{errorstatus = SDIO_INVALID_PARAMETER;return(errorstatus);}if(pHandle->CardType != SD_HIGH_CAPACITY_SD_CARD){BlockAddr <<= 9; //小容量卡,转换为字节单位}pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输//发送CMD17 READ_SINGLE_BLOCK,块读取指令,参数:块地址,短回复,R1pHandle->pInterface->SDIO_SendCommand(CMD17, BlockAddr, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{DEBUG("CMD17 error (%d)!\r\n",errorstatus);return(errorstatus);}pHandle->pInterface->SDIO_SetAndStartDataTrans(pBlockBuff, TRUE, pHandle->SDIO_BlockSize, pHandle->SDCardInfo.CardBlockSize); //初始化DMA,并准备读取数据errorstatus = pHandle->pInterface->SDIO_WaitTransComplete(TRUE, &ReadLen, 150); //SDIO等待传输完成(等待读取或写入数据完成)(超时:150ms)if(errorstatus == SDIO_OK && ReadLen != pHandle->SDCardInfo.CardBlockSize){DEBUG("读取数据错误,需要读取:%dB,实际返回:%dB\r\n", pHandle->SDCardInfo.CardBlockSize, ReadLen);}return(errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_ReadMultiBlocks(SDIO_SD_HANDLE *pHandle, u32 BlockAddr, u8 *pBlockBuff, u16 NumberOfBlocks)
* 功能 : 读SD卡多个块
* 参数 : pHandle:句柄;BlockAddr:块地址;pBlockBuff:块缓冲区地址;NumberOfBlocks:块数量
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 20120516
* 最后修改时间 : 2018-03-28
* 说明 : 读SD卡多个(大于1个)块
*************************************************************************************************************************/
SDIO_Error SD_ReadMultiBlocks(SDIO_SD_HANDLE *pHandle, u32 BlockAddr, u8 *pBlockBuff, u16 NumberOfBlocks)
{SDIO_Error errorstatus = SDIO_OK;SDIO_Error error1;u32 ReadLen; //读取的数据长度if (pBlockBuff == NULL) //没有分配接收缓冲区,返回{errorstatus = SDIO_INVALID_PARAMETER;return(errorstatus);}//限制读取的块数量if(NumberOfBlocks > pHandle->MaxTransBlockCount) {DEBUG("限制一次读取%d块,当前一次读取%d\r\n", pHandle->MaxTransBlockCount, NumberOfBlocks);NumberOfBlocks = pHandle->MaxTransBlockCount;}if(pHandle->CardType != SD_HIGH_CAPACITY_SD_CARD){BlockAddr <<= 9; //小容量卡,转换为字节单位}pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输//发送CMD18 SDIO_READ_MULT_BLOCK;多区段读命令,参数:开始地址,短返回,R1(发送后会不停的发送数据出来,需要CMD12命令取消发送)pHandle->pInterface->SDIO_SendCommand(CMD18, BlockAddr, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输DEBUG("CMD18 error (%d)!\r\n",errorstatus);return(errorstatus);}pHandle->pInterface->SDIO_SetAndStartDataTrans(pBlockBuff, TRUE, pHandle->SDIO_BlockSize, pHandle->SDCardInfo.CardBlockSize*NumberOfBlocks); //初始化DMA开始读取数据errorstatus = pHandle->pInterface->SDIO_WaitTransComplete(TRUE, &ReadLen, 100*NumberOfBlocks); //SDIO等待传输完成(等待读取或写入数据完成)(超时:100ms*NumberOfBlocks)if(errorstatus == SDIO_OK && ReadLen != (pHandle->SDCardInfo.CardBlockSize*NumberOfBlocks)){DEBUG("读取数据错误,需要读取:%dB,实际返回:%dB\r\n", pHandle->SDCardInfo.CardBlockSize*NumberOfBlocks, ReadLen);}//发送CMD12 SDIO_STOP_TRANSMISSION命令,终止读取;参数:0,短响应,R1pHandle->pInterface->SDIO_SendCommand(CMD12, 0, SDIO_Response_Short);error1 = pHandle->pInterface->SDIO_CmdResp1Error();if(error1 != SDIO_OK) //获取回复{DEBUG("CMD12 error (%d)!\r\n",error1);}return(errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_WriteBlock(SDIO_SD_HANDLE *pHandle, u32 BlockAddr, const u8 *pBlockBuff)
* 功能 : 写SD卡一个块
* 参数 : BlockAddr:块地址;writebuff:写缓冲区地址
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 2018-03-29
* 最后修改时间 : 2018-03-29
* 说明 : 写SD卡一个块
**************************************************************************************************************************/
SDIO_Error SD_WriteBlock(SDIO_SD_HANDLE *pHandle, u32 BlockAddr, const u8 *pBlockBuff)
{SDIO_Error errorstatus = SDIO_OK;u32 cardstatus = 0;u32 ReadLen = 0;u32 timeout;if (pBlockBuff == NULL){errorstatus = SDIO_INVALID_PARAMETER;return(errorstatus);}if(pHandle->CardType != SD_HIGH_CAPACITY_SD_CARD){BlockAddr <<= 9; //小容量卡,转换为字节单位}//检查timeout = 100; //超时100ms`do{//发送CMD13,SDIO_SEND_STATUS,读 Card_Status 寄存器,参数,RCA地址,短返回,R1;pHandle->pInterface->SDIO_SendCommand(CMD13, (u32) pHandle->RCA << 16, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输DEBUG("CMD13 error (%d)!\r\n",errorstatus);return(errorstatus);}pHandle->pInterface->SDIO_GetResponse(SDIO_RESP_Short, &cardstatus); //获取响应的状态if((cardstatus & 0x00000100) == 0) //没有准备好{SYS_DelayMS(1); //延时}else //READY_FOR_DATA位有效,卡已经准备好写入了{break;}timeout --;}while (timeout);if (timeout == 0){DEBUG("等待卡写入超时,错误:%d\r\n",errorstatus);return(SDIO_ERROR);}pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输//发送CMD24,SDIO_WRITE_SINGLE_BLOCK,写命令,参数:地址(大容量卡为块地址),短响应,R1pHandle->pInterface->SDIO_SendCommand(CMD24, BlockAddr, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输DEBUG("CMD24 error (%d)!\r\n",errorstatus);return(errorstatus);}//开始传输-写入数据pHandle->pInterface->SDIO_SetAndStartDataTrans((u8 *)pBlockBuff, FALSE, pHandle->SDIO_BlockSize, pHandle->SDCardInfo.CardBlockSize);//等待传输完成errorstatus = pHandle->pInterface->SDIO_WaitTransComplete(FALSE, &ReadLen, 150);//SDIO等待传输完成(等待读取或写入数据完成)(超时:150ms)if(errorstatus == SDIO_OK && ReadLen != pHandle->SDCardInfo.CardBlockSize){DEBUG("写入数据错误,需要写入:%dB,实际写入:%dB\r\n", pHandle->SDCardInfo.CardBlockSize, ReadLen);}//读取卡编程状态,等待写入完成errorstatus = SD_WaitProgrammingFinish(pHandle, 100); //等待编程完成return(errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_WriteMultiBlocks(SDIO_SD_HANDLE *pHandle, u32 BlockAddr, const u8 *pBlockBuff, u16 NumberOfBlocks)
* 功能 : 写SD卡多个块
* 参数 : pHandle:句柄;BlockAddr:块地址;BlockBuff:块缓冲区地址NumberOfBlocks:块数量
* 返回 : SD_OK:成功,其它见SD Card Error code.
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 2018-03-30
* 最后修改时间 : 2018-03-30
* 说明 : 写SD卡多个(大于1个)块
**************************************************************************************************************************/
SDIO_Error SD_WriteMultiBlocks(SDIO_SD_HANDLE *pHandle, u32 BlockAddr, const u8 *pBlockBuff, u16 NumberOfBlocks)
{SDIO_Error errorstatus = SDIO_OK;u32 cardstatus = 0;u32 ReadLen = 0;u32 timeout;if (pBlockBuff == NULL){errorstatus = SDIO_INVALID_PARAMETER;return(errorstatus);}//限制读取的块数量if(NumberOfBlocks > pHandle->MaxTransBlockCount) {DEBUG("限制一次读取%d块,当前一次读取%d\r\n", pHandle->MaxTransBlockCount, NumberOfBlocks);NumberOfBlocks = pHandle->MaxTransBlockCount;}//地址检查if(pHandle->CardType != SD_HIGH_CAPACITY_SD_CARD){BlockAddr <<= 9; //小容量卡,转换为字节单位}//检查timeout = 100; //超时100ms`do{//发送CMD13,SDIO_SEND_STATUS,读 Card_Status 寄存器,参数,RCA地址,短返回,R1;pHandle->pInterface->SDIO_SendCommand(CMD13, (u32) pHandle->RCA << 16, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输DEBUG("CMD13 error (%d)!\r\n",errorstatus);return(errorstatus);}pHandle->pInterface->SDIO_GetResponse(SDIO_RESP_Short, &cardstatus); //获取响应的状态if((cardstatus & 0x00000100) == 0) //没有准备好{SYS_DelayMS(1); //延时}else //READY_FOR_DATA位有效,卡已经准备好写入了{break;}timeout --;}while (timeout);if (timeout == 0){DEBUG("等待卡写入超时,错误:%d(状态0x%X)\r\n",errorstatus,cardstatus);return(SDIO_ERROR);}pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输//发送CMD25,SDIO_WRITE_MULT_BLOCK,参数:字节地址(大容量卡为块地址),短返回,R1pHandle->pInterface->SDIO_SendCommand(CMD25, BlockAddr, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输DEBUG("CMD25 error (%d)!\r\n",errorstatus);return(errorstatus);}//开始传输-写入数据pHandle->pInterface->SDIO_SetAndStartDataTrans((u8 *)pBlockBuff, FALSE, pHandle->SDIO_BlockSize, pHandle->SDCardInfo.CardBlockSize*NumberOfBlocks);//等待传输完成errorstatus = pHandle->pInterface->SDIO_WaitTransComplete(FALSE, &ReadLen, 100*NumberOfBlocks);//SDIO等待传输完成(等待读取或写入数据完成)(超时:100ms*NumberOfBlocks)if(errorstatus == SDIO_OK && ReadLen != (pHandle->SDCardInfo.CardBlockSize*NumberOfBlocks)){DEBUG("写入数据错误,需要写入:%dB,实际写入:%dB\r\n", pHandle->SDCardInfo.CardBlockSize, ReadLen);}//发送CMD12 SDIO_STOP_TRANSMISSION命令,终止读取;参数:0,短响应,R1pHandle->pInterface->SDIO_SendCommand(CMD12, 0, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输DEBUG("CMD12 error (%d)!\r\n",errorstatus);return(errorstatus);}//读取卡编程状态,等待写入完成errorstatus = SD_WaitProgrammingFinish(pHandle, 100); //等待编程完成return(errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_EraseBlock(SDIO_SD_HANDLE *pHandle,u32 BlockAddr, u32 NumberOfBlocks)
* 功能 : 擦除指定的块
* 参数 : pHandle:句柄;BlockAddr:擦除的块地址;NumberOfBlocks:擦除的块数量
* 返回 : SDIO_Error
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2018-03-30
* 最后修改时间 : 2018-03-30
* 说明 : 地址必须是块地址-擦除特别慢
*************************************************************************************************************************/
SDIO_Error SD_EraseBlock(SDIO_SD_HANDLE *pHandle,u32 BlockAddr, u32 NumberOfBlocks)
{SDIO_Error errorstatus = SDIO_OK;/*!< Check if the card coomnd class supports erase command */if (((pHandle->CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0) //卡不支持擦除命令{errorstatus = SDIO_REQUEST_NOT_APPLICABLE;return (errorstatus);}//发送CMD32 ERASE_GROUP_START命令,写入擦除开始地址;参数:擦除开始地址,短响应,R1pHandle->pInterface->SDIO_SendCommand(CMD32, BlockAddr, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输DEBUG("CMD32 error (%d)!\r\n",errorstatus);return(errorstatus);}//发送CMD33 SD_ERASE_GRP_END命令,写入擦除结束地址;参数:擦除结束地址,短响应,R1pHandle->pInterface->SDIO_SendCommand(CMD33, BlockAddr+NumberOfBlocks, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输DEBUG("CMD33 error (%d)!\r\n",errorstatus);return(errorstatus);}//发送CMD38 ERASE命令,参数0,开始擦除,响应R1bpHandle->pInterface->SDIO_SendCommand(CMD38, 0, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (errorstatus != SDIO_OK) //命令发送错误,返回{pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输DEBUG("CMD38 error (%d)!\r\n",errorstatus);return(errorstatus);} //读取卡编程状态,等待擦除完成errorstatus = SD_WaitProgrammingFinish(pHandle, NumberOfBlocks*500); //等待编程完成return(errorstatus);
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_WaitProgrammingFinish(SDIO_SD_HANDLE *pHandle, u32 TimeOutMs)
* 功能 : 等待编程完成
* 参数 : pHandle:句柄;TimeOutMs:通信超时时间
* 返回 : SDIO_Error
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2018-03-30
* 最后修改时间 : 2018-03-30
* 说明 : 用于写入完成后等待编程完成
*************************************************************************************************************************/
SDIO_Error SD_WaitProgrammingFinish(SDIO_SD_HANDLE *pHandle, u32 TimeOutMs)
{SDIO_Error Status = SDIO_OK;u32 respR1 = 0;u8 cardstate;while(TimeOutMs){//发送CMD13,SDIO_SEND_STATUS,读 Card_Status 寄存器,参数,RCA地址,短返回,R1;pHandle->pInterface->SDIO_SendCommand(CMD13, (u32) pHandle->RCA << 16, SDIO_Response_Short);Status = pHandle->pInterface->SDIO_CmdResp1Error(); //获取回复if (Status != SDIO_OK) //命令发送错误,返回{DEBUG("CMD13 error (%d)!\r\n",Status);return(Status);}pHandle->pInterface->SDIO_GetResponse(SDIO_RESP_Short, &respR1); //获取响应的状态//返回状态错误查询-加入以下状态可以在编程错误时方便找出问题if (respR1 & SDIO_OCR_ADDR_OUT_OF_RANGE){Status = (SDIO_ADDR_OUT_OF_RANGE);}else if (respR1 & SDIO_OCR_ADDR_MISALIGNED){Status = (SDIO_ADDR_MISALIGNED);}else if (respR1 & SDIO_OCR_BLOCK_LEN_ERR){Status =(SDIO_BLOCK_LEN_ERR);}else if (respR1 & SDIO_OCR_ERASE_SEQ_ERR){Status = (SDIO_ERASE_SEQ_ERR);}else if (respR1 & SDIO_OCR_BAD_ERASE_PARAM){Status = (SDIO_BAD_ERASE_PARAM);}else if (respR1 & SDIO_OCR_WRITE_PROT_VIOLATION){Status = (SDIO_WRITE_PROT_VIOLATION);}else if (respR1 & SDIO_OCR_LOCK_UNLOCK_FAILED){Status = (SDIO_LOCK_UNLOCK_FAILED);}else if (respR1 & SDIO_OCR_COM_CRC_FAILED){Status = (SDIO_COM_CRC_FAILED);}else if (respR1 & SDIO_OCR_ILLEGAL_CMD){Status = (SDIO_ILLEGAL_CMD);}else if (respR1 & SDIO_OCR_CARD_ECC_FAILED){Status = (SDIO_CARD_ECC_FAILED);}else if (respR1 & SDIO_OCR_CC_ERROR){Status = (SDIO_CC_ERROR);}else if (respR1 & SDIO_OCR_GENERAL_UNKNOWN_ERROR){Status =(SDIO_GENERAL_UNKNOWN_ERROR);}else if (respR1 & SDIO_OCR_STREAM_READ_UNDERRUN){Status = (SDIO_STREAM_READ_UNDERRUN);}else if (respR1 & SDIO_OCR_STREAM_WRITE_OVERRUN){Status = (SDIO_STREAM_WRITE_OVERRUN);}else if (respR1 & SDIO_OCR_CID_CSD_OVERWRIETE){Status = (SDIO_CID_CSD_OVERWRITE);}else if (respR1 & SDIO_OCR_WP_ERASE_SKIP){Status = (SDIO_WP_ERASE_SKIP);}else if (respR1 & SDIO_OCR_CARD_ECC_DISABLED){Status = (SDIO_CARD_ECC_DISABLED);}else if (respR1 & SDIO_OCR_ERASE_RESET){Status = (SDIO_ERASE_RESET);}else if (respR1 & SDIO_OCR_AKE_SEQ_ERROR){Status = (SDIO_AKE_SEQ_ERROR);}if (Status != SDIO_OK) //命令发送错误,返回{DEBUG("CMD13 resp error (%d)!\r\n",Status);return(Status);}/* Find out card status */cardstate = (u8) ((respR1 >> 9) & 0x0000000F);if((cardstate == SD_CARD_PROGRAMMING) || (cardstate == SD_CARD_RECEIVING)) //卡在编程中,等待{Status = SDIO_ERASE_RESET; //等待编程完成超时SYS_DelayMS(1);}else {return SDIO_OK; //操作成功了}TimeOutMs--;}return Status;
}/*************************************************************************************************************************
* 函数 : SDIO_Error SD_GetSDSCR(SDIO_SD_HANDLE *pHandle, u32 pSCR[2])
* 功能 : 获取SCR信息
* 参数 : pHandle:句柄;prca:RCA缓冲区指针
* 返回 : SDIO_Error
* 依赖 : 底层寄存器操作函数
* 作者 : cp1300@139.com
* 时间 : 2020-09-13
* 最后修改时间 : 2020-09-13
* 说明 :
*************************************************************************************************************************/
SDIO_Error SD_GetSDSCR(SDIO_SD_HANDLE *pHandle, u32 pSCR[2])
{SDIO_Error errorstatus = SDIO_OK;u32 cnt = 0;u8 Buff[8];//设置块大小为8字节//发送SDIO_SET_BLOCKLEN,参数8,短响应pHandle->pInterface->SDIO_SendCommand(CMD16, 8, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if (errorstatus != SDIO_OK){DEBUG("SDIO_SET_BLOCKLEN error(%d)!\n",errorstatus); //调试,打印错误信息goto _error;}//发送CMD55 SDIO_APP_CMD;命令参数:RCA;返回响应R1,设置RCA为0,短响应pHandle->pInterface->SDIO_SendCommand(SDIO_CMD_APP_CMD, (u32)pHandle->RCA << 16, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if (errorstatus != SDIO_OK){goto _error;}pHandle->pInterface->SDIO_ClearDataTrans(); //清除数据传输//发送ACMD51 SD_APP_SEND_SCR,参数0,短响应,R1pHandle->pInterface->SDIO_SendCommand(SDIO_CMD_SDIO_APP_SEND_SCR, 0, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if (errorstatus != SDIO_OK){goto _error;}pHandle->pInterface->SDIO_SetAndStartDataTrans(Buff, TRUE, SDIO_DataBlockSize_8B, 8);//设置SDIO并开始准备数据传输errorstatus = pHandle->pInterface->SDIO_WaitTransComplete(TRUE, &cnt, 500);if(errorstatus == SDIO_OK){pSCR[0] = Buff[0];pSCR[0] <<= 8;pSCR[0] |= Buff[1];pSCR[0] <<= 8;pSCR[0] |= Buff[2];pSCR[0] <<= 8;pSCR[0] |= Buff[3];pSCR[1] = Buff[4];pSCR[1] <<= 8;pSCR[1] |= Buff[5];pSCR[1] <<= 8;pSCR[1] |= Buff[6];pSCR[1] <<= 8;pSCR[1] |= Buff[7];}_error://恢复块大小为 SD_BLOCK_SIZEpHandle->pInterface->SDIO_SendCommand(CMD16, SD_BLOCK_SIZE, SDIO_Response_Short);errorstatus = pHandle->pInterface->SDIO_CmdResp1Error();if (errorstatus != SDIO_OK){DEBUG("SDIO_SET_BLOCKLEN error(%d)!\n",errorstatus); //调试,打印错误信息}return (errorstatus);
}
//sdio_sdcard.h
/************************************************************************************************************** 文件名 : sdio_sdcard.c* 功能 : SDIO接口SD卡驱动* 作者 : cp1300@139.com* 创建时间 : 2018-03-27* 最后修改时间 : 2018-03-27* 详细 : 源程序借鉴了意法STM32F103X库函数分离了底层SDIO接口,但是依赖一些数据定义,在sdio_const中
*************************************************************************************************************/
#ifndef _SDIO_SDCARD_H_
#define _SDIO_SDCARD_H_
#include "sdio_const.h"
#include "system.h"//SD卡 CSD
typedef struct /* Card Specific Data */
{u8 CSDStruct; /* CSD structure */u8 SysSpecVersion; /* System specification version */u8 Reserved1; /* Reserved */u8 TAAC; /* Data read access-time 1 */u8 NSAC; /* Data read access-time 2 in CLK cycles */u8 MaxBusClkFrec; /* Max. bus clock frequency */u16 CardComdClasses; /* Card command classes */u8 RdBlockLen; /* Max. read data block length */u8 PartBlockRead; /* Partial blocks for read allowed */u8 WrBlockMisalign; /* Write block misalignment */u8 RdBlockMisalign; /* Read block misalignment */u8 DSRImpl; /* DSR implemented */u8 Reserved2; /* Reserved */u16 DeviceSize; /* Device Size */u8 MaxRdCurrentVDDMin; /* Max. read current @ VDD min */u8 MaxRdCurrentVDDMax; /* Max. read current @ VDD max */u8 MaxWrCurrentVDDMin; /* Max. write current @ VDD min */u8 MaxWrCurrentVDDMax; /* Max. write current @ VDD max */u8 DeviceSizeMul; /* Device size multiplier */u8 EraseGrSize; /* Erase group size */u8 EraseGrMul; /* Erase group size multiplier */u8 WrProtectGrSize; /* Write protect group size */u8 WrProtectGrEnable; /* Write protect group enable */u8 ManDeflECC; /* Manufacturer default ECC */u8 WrSpeedFact; /* Write speed factor */u8 MaxWrBlockLen; /* Max. write data block length */u8 WriteBlockPaPartial; /* Partial blocks for write allowed */u8 Reserved3; /* Reserded */u8 ContentProtectAppli; /* Content protection application */u8 FileFormatGrouop; /* File format group */u8 CopyFlag; /* Copy flag (OTP) */u8 PermWrProtect; /* Permanent write protection */u8 TempWrProtect; /* Temporary write protection */u8 FileFormat; /* File Format */u8 ECC; /* ECC code */u8 CSD_CRC; /* CSD CRC */u8 Reserved4; /* always 1*/
} SD_CSD;//SD卡CID
typedef struct /*Card Identification Data*/
{u8 ManufacturerID; /* ManufacturerID */u16 OEM_AppliID; /* OEM/Application ID */u32 ProdName1; /* Product Name part1 */u8 ProdName2; /* Product Name part2*/u8 ProdRev; /* Product Revision */u32 ProdSN; /* Product Serial Number */u8 Reserved1; /* Reserved1 */u16 ManufactDate; /* Manufacturing Date */u8 CID_CRC; /* CID CRC */u8 Reserved2; /* always 1 */
} SD_CID;//SD卡信息
typedef struct
{SD_CSD SD_csd;SD_CID SD_cid;u64 CardCapacity; //SD卡容量,单位:字节,最大支持2^64字节大小的卡u32 CardBlockSize; //SD卡块大小 u16 RCA; //卡相对地址u8 CardType; //卡类型
} SD_CardInfo;/* Supported Memory Cards */
typedef enum
{SD_STD_CAPACITY_SD_CARD_V1_1 = 0x0, //SD V1.1版SD_STD_CAPACITY_SD_CARD_V2_0 = 0x1, //SD V2.0版SD_HIGH_CAPACITY_SD_CARD = 0x2, //大容量SD卡-必须以块为单位读取SD_MULTIMEDIA_CARD = 0x3, //MMC卡SD_SECURE_DIGITAL_IO_CARD = 0x4, //普通SD卡SD_HIGH_SPEED_MULTIMEDIA_CARD = 0x5, //高速SD卡SD_SECURE_DIGITAL_IO_COMBO_CARD = 0x6,SD_HIGH_CAPACITY_MMC_CARD = 0x7, //大容量MMC卡
}SD_CARD_TYPE;//SD卡扇区大小定义-注意大容量卡不支持此设置,只能使用512字节
#define SD_BLOCK_SIZE 512 //SD卡块大小
#if(SD_BLOCK_SIZE != 512 && SD_BLOCK_SIZE != 1024 && SD_BLOCK_SIZE != 2048 && SD_BLOCK_SIZE != 4096)
#error("无效的SD卡块大小");
#endif //SD_BLOCK_SIZE//获取与SD卡块大小对应的SDIO传输块大小
__inline SDIO_DataBlockSize GetSDIO_BlockSizeForSDBlock(u32 SD_BlockSize)
{switch(SD_BlockSize){case 512: return SDIO_DataBlockSize_512B;case 1024: return SDIO_DataBlockSize_1024B;case 2048: return SDIO_DataBlockSize_2048B;default:return SDIO_DataBlockSize_512B;}
}//SD接口
typedef struct
{void (*SDIO_Init)(void); //SDIO初始化void (*SDIO_SetLowSpeedClock)(void); //SDIO设置低速时钟void (*SDIO_SetHighpeedClock)(void); //SDIO设置高速时钟void (*SDIO_Set1BitBusWide)(void); //SDIO设置单线模式void (*SDIO_Set4BitBusWide)(void); //SDIO设置4线模式void (*SDIO_SendCommand)(u8 CmdIndex,u32 CmdArg,SDIO_WAIT_RESP WaitResp); //SDIO发送一个命令void (*SDIO_SetAndStartDataTrans)(u8 *pDataBuff, bool isEnableReadData, SDIO_DataBlockSize SDIOBlockSize, u32 TransDataLen);//设置SDIO并开始准备数据传输void (*SDIO_ClearDataTrans)(void); //SDIO清除数据传输SDIO_Error (*SDIO_WaitTransComplete)(bool isEnableReadData, u32 *pDataLen, u32 TimeOutMs);//SDIO等待传输完成(等待读取或写入数据完成)void (*SDIO_GetResponse)(SDIO_RESP_TYPE RespIndex, u32 *pRespData); //获取返回响应状态 2020-09-12 更换接口形态,更通用一些SDIO_Error (*SDIO_CmdError)(void); //获取指令执行状态(无响应)SDIO_Error (*SDIO_CmdResp1Error)(void); //指令执行状态(R1)SDIO_Error (*SDIO_CmdResp2Error)(void); //指令执行状态(R2,CID or CSD)SDIO_Error (*SDIO_CmdResp3Error)(void); //指令执行状态(R3,OCR)SDIO_Error (*SDIO_CmdResp6Error)(u16*prca); //指令执行状态(R6,RCA)SDIO_Error (*SDIO_CmdResp7Error)(void); //获取指令执行状态(R7)u32 (*SDIO_GetMaxTransBlockCount)(void); //获取最大传输块数量,2020-09-13新增接口
}SDIO_SD_INTERFACE;//SDIO句柄
typedef struct
{const SDIO_SD_INTERFACE *pInterface; //所需的SDIO接口SD_CARD_TYPE CardType; //卡类型u32 CSD_Tab[4];u32 CID_Tab[4];u32 SCR_Tab[2];u32 RCA;u32 MaxTransBlockCount; //SD卡控制器一次能传输的块最大数量SD_CardInfo SDCardInfo; //SD卡信息SDIO_DataBlockSize SDIO_BlockSize; //SD卡block大小
}SDIO_SD_HANDLE;//相关接口
void SD_InterfaceInit(SDIO_SD_HANDLE *pHandle, const SDIO_SD_INTERFACE *pInterface); //SD卡底层接口初始化
SDIO_Error SD_Init(SDIO_SD_HANDLE *pHandle); //SD卡初始化
SDIO_Error SD_ReadStatus(SDIO_SD_HANDLE *pHandle, u32 *pSCR); //读取SD卡状态寄存器
SDIO_Error SD_ReadBlock(SDIO_SD_HANDLE *pHandle, u32 BlockAddr, u8 *pBlockBuff); //读SD卡一个块
SDIO_Error SD_ReadMultiBlocks(SDIO_SD_HANDLE *pHandle, u32 BlockAddr, u8 *pBlockBuff, u16 NumberOfBlocks); //读取SD卡多个块
SDIO_Error SD_WriteBlock(SDIO_SD_HANDLE *pHandle, u32 BlockAddr,const u8 *pBlockBuff); //写SD卡一个块
SDIO_Error SD_WriteMultiBlocks(SDIO_SD_HANDLE *pHandle, u32 BlockAddr,const u8 *pBlockBuff, u16 NumberOfBlocks); //写SD卡多个块
SDIO_Error SD_EraseBlock(SDIO_SD_HANDLE *pHandle,u32 BlockAddr, u32 NumberOfBlocks); //擦除指定的块#endif //_SDIO_SDCARD_H_
//中间层接口
/************************************************************************************************************** 文件名 : SDIO_SD_Interface.c* 功能 : SD卡所需的SDIO接口* 作者 : cp1300@139.com* 创建时间 : 2018-03-27* 最后修改时间 : 2018-03-27* 详细 : 定义了SDIO SD卡所需的底层SDIO接口
*************************************************************************************************************/
#include "system.h"
#include "SDIO_SD_Interface.h"
#include "sdio.h"
#include "sdio_const.h"
#include "board.h"void BI_SDIO_Init(void); //SDIO SD卡初始化
void BI_SDIO_SetLowSpeedClock(void); //SDIO设置低速时钟
void BI_SDIO_SetHighpeedClock(void); //SDIO设置高速时钟
void BI_SDIO_Set1BitBusWide(void); //SDIO设置单线模式
void BI_SDIO_Set4BitBusWide(void); //SDIO设置4线模式//默认的SD卡接口
const SDIO_SD_INTERFACE g_DefaultSD_Interface =
{BI_SDIO_Init, //SDIO初始化BI_SDIO_SetLowSpeedClock, //SDIO设置低速时钟BI_SDIO_SetHighpeedClock, //SDIO设置高速时钟BI_SDIO_Set1BitBusWide, //SDIO设置单线模式BI_SDIO_Set4BitBusWide, //SDIO设置4线模式SDIO_SendCommand, //SDIO发送一个命令SDIO_SetAndStartDataTrans, //SDIO设置并开始数据传输SDIO_ClearDataTrans, //SDIO清除数据传输SDIO_WaitTransComplete, //SDIO等待传输完成(等待读取或写入数据完成)SDIO_GetResponse, //获取返回响应状态SDIO_CmdError, //获取指令执行状态(无响应)SDIO_CmdResp1Error, //指令执行状态(R1)SDIO_CmdResp2Error, //指令执行状态(R2,CID or CSD)SDIO_CmdResp3Error, //指令执行状态(R3,OCR)SDIO_CmdResp6Error, //指令执行状态(R6,RCA)SDIO_CmdResp7Error, //获取指令执行状态(R7)SDIO_GetMaxTransBlockCount, //获取最大传输块数量,2020-09-13新增接口
};//SDIO SD卡初始化
void BI_SDIO_Init(void)
{//SDIO 相关IO初始化SYS_GPIOx_SetAF(GPIO_PD6_SD0_nCD);SYS_GPIOx_SetAF(GPIO_PD5_SD0_DATA3);SYS_GPIOx_SetAF(GPIO_PD4_SD0_DATA2);SYS_GPIOx_SetAF(GPIO_PD3_SD0_DATA1);SYS_GPIOx_SetAF(GPIO_PD2_SD0_DATA0);SYS_GPIOx_SetAF(GPIO_PD1_SD0_SCK);SYS_GPIOx_SetAF(GPIO_PD0_SD0_CMD);SDIO_Init(); //SDIO接口初始化SDIO_SetPortSelect(0); //设置SDIO当前port(0-1)SDIO_SetOut74Clock(); //SDIO输出74个时钟
}//SDIO设置低速时钟
void BI_SDIO_SetLowSpeedClock(void)
{SDIO_SetClockSpeed(SDIO_CLK_400K); //SDIO设置时钟速度
}//SDIO设置高速时钟
void BI_SDIO_SetHighpeedClock(void)
{SDIO_SetClockSpeed(SDIO_CLK_25M); //SDIO设置时钟速度
}//SDIO设置单线模式
void BI_SDIO_Set1BitBusWide(void)
{SDIO_SetBusWide(SDIO_BusWide_1b); //SDIO设置总线宽度
}//SDIO设置4线模式
void BI_SDIO_Set4BitBusWide(void)
{SDIO_SetBusWide(SDIO_BusWide_4b); //SDIO设置总线宽度
}
//测试
#include "typedef.h"
#include "nuc970_system.h"
#include "uart.h"
#include "stdlib.h"
#include "timer.h"
#include "main.h"
#include "test.h"
#include "sdio_sdcard.h"
#include "sdio_sd_interface.h"
#include "stdio.h"//sd卡测试
void sdcard_test(void)
{u8 *pDataBuff = (u8 *)malloc(4096);u32 len;SDIO_Error Status;u32 i;STOP_WATCH_HANDLE mStopWatchHandle;SD_InterfaceInit(&g_SysGlobal.mSD0_Handle, &g_DefaultSD_Interface); //SD卡底层接口初始化while(SD_Init(&g_SysGlobal.mSD0_Handle) != SDIO_OK){SYS_DelayMS(1000);}//扇区读取g_mStopWatchClass.Restart(&mStopWatchHandle);g_mStopWatchClass.Start(&mStopWatchHandle);Status = SD_ReadBlock(&g_SysGlobal.mSD0_Handle, 0, pDataBuff); //读SD卡一个块g_mStopWatchClass.Stop(&mStopWatchHandle);uart_printf("\r\n读取一个扇区耗时:%lluus\r\n", g_mStopWatchClass.GetElapsedUs(&mStopWatchHandle));if(SDIO_OK == Status){uart_printf("读取一个扇区成功:\r\n");//SD卡扇区读取成功for(i = 0;i < 512;i ++){uart_printf("%02X ", pDataBuff[i]);}uart_printf("\r\n");}else{uart_printf("读取一个扇区错误:%d\r\n", Status);}//写单个扇区pDataBuff[10] = 0x35;pDataBuff[20] = 0x68;g_mStopWatchClass.Restart(&mStopWatchHandle);g_mStopWatchClass.Start(&mStopWatchHandle);Status = SD_WriteBlock(&g_SysGlobal.mSD0_Handle, 0, pDataBuff); //写SD卡一个块g_mStopWatchClass.Stop(&mStopWatchHandle);uart_printf("\r\n写一个扇区耗时:%lluus\r\n", g_mStopWatchClass.GetElapsedUs(&mStopWatchHandle));if(SDIO_OK == Status){uart_printf("写一个扇区成功:\r\n");}else{uart_printf("写一个扇区错误:%d\r\n", Status);}//多扇区读取g_mStopWatchClass.Restart(&mStopWatchHandle);g_mStopWatchClass.Start(&mStopWatchHandle);Status = SD_ReadMultiBlocks(&g_SysGlobal.mSD0_Handle, 0, pDataBuff, 2); //读SD卡2个块g_mStopWatchClass.Stop(&mStopWatchHandle);uart_printf("\r\n读取多个扇区耗时:%lluus\r\n", g_mStopWatchClass.GetElapsedUs(&mStopWatchHandle));if(SDIO_OK == Status){uart_printf("读取多个扇区成功:\r\n");//SD卡扇区读取成功for(i = 0;i < 1024;i ++){uart_printf("%02X ", pDataBuff[i]);}uart_printf("\r\n");}else{uart_printf("读取多个扇区错误:%d\r\n", Status);}//多扇区写入pDataBuff[10] ++;pDataBuff[20] ++;pDataBuff[512+10] ++;pDataBuff[512+20] ++;g_mStopWatchClass.Restart(&mStopWatchHandle);g_mStopWatchClass.Start(&mStopWatchHandle);Status = SD_WriteMultiBlocks(&g_SysGlobal.mSD0_Handle, 0, pDataBuff, 2); //写SD卡2个块g_mStopWatchClass.Stop(&mStopWatchHandle);uart_printf("\r\n写多个扇区耗时:%lluus\r\n", g_mStopWatchClass.GetElapsedUs(&mStopWatchHandle));if(SDIO_OK == Status){uart_printf("写多个扇区成功:\r\n");}else{uart_printf("写多个扇区错误:%d\r\n", Status);}//释放内存free(pDataBuff);while(1){SYS_DelayMS(500);}
}
测试结果
NUC970 SD卡驱动(SDIO)相关推荐
- STM32F1与STM32CubeIDE快速入门-SD卡驱动-SDIO+FatFs
SD卡驱动-SDIO+FatFs 文章目录 SD卡驱动-SDIO+FatFs 1.SDIO与FatFs简单介绍 2.SDIO与FatFs配置 3.SDIO与FatFs功能测试 4.代码解析 5.STM ...
- S3C6410裸机SD卡驱动(SDIO模式)
花了几天写了SD卡裸机驱动,现在还不完善,只支持4G以内的卡,以后再加上;现在经过修改可以写入数据了,亲测没问题. S3C6410_SDIO.C #include "s3c6410_syst ...
- sd 卡驱动在2.6内核的编写.sd/mmc/sdio kernel,sd/mmc/sdio 内核
[转帖请注明出处:blog.csdn.net/lanmanck] sd卡驱动主要参照已有的文件即可,2410,9260都挺好.其实写驱动主要是搞清楚工作流程即可.我这里写一些心得与大家分享下,基于2. ...
- rt-thread SDIO驱动框架分析(SD卡驱动\SD Nand驱动)
rt-thread SDIO驱动框架分析之SD卡驱动 文章目录 rt-thread SDIO驱动框架分析之SD卡驱动 1. 前言 2. SDIO通用驱动框架介绍 3. 文件架构分析 4. SDIO设备 ...
- sd 卡驱动--基于高通平台
点击打开链接 内容来自以下博客: http://blog.csdn.net/qianjin0703/article/details/5918041 Linux设备驱动子系统第二弹 - SD卡 (有介绍 ...
- SD卡驱动分析(二)
三.下面分析一下高通的android2.3的代码中SD卡驱动的流程. 在kernel中,SD卡是作为平台设备加入到内核中去的,在/kernel/arch/arm/mach-msm/devices-ms ...
- SD卡驱动分析(一)
Android下的SD卡驱动与标准LINUX下的SD卡驱动好像没有太大的区别,这里就以高通的ANDROID 2.3以代表,来简要分析一下LINUX下SD卡驱动的写法.由于小弟的技术有限,分析的有错的地 ...
- Linux SD卡驱动开发(五) —— SD 卡驱动分析Core补充篇
Core层中有两个重要函数 mmc_alloc_host 用于构造host,前面已经学习过,这里不再阐述:另一个就是 mmc_add_host,用于注册host 前面探测函数s3cmci_probe, ...
- CE下基于Zylonite硬件平台的SD卡驱动开发
摘要:本文结合实际项目(一款以WINCE为操作系统内核的GSM/PHS双模智能手机)对嵌入式系统Windows CE5.0的底层驱动(SD卡)的架构进行了分析和研究,以MARVELL公司提供的基于IN ...
最新文章
- flask中的flask_uploads上传文件
- android动态设置错误页面,Android中替换WebView加载网页失败时的页面
- iOS - OC 与 C 互相操作
- 川大和西南交大计算机考研难易度,2020考研:百所211院校报考难易度分析
- C++学习之Dev-C++安装与调试
- DeveloperAppleHelp
- vue 图片切换动态绑定
- linux 监控软件介绍,Linux中系统整体性能监控工具详细介绍
- 【RecSys】推荐系统和计算广告经典算法论文及实现总结
- c语言 库仑计_bq27520电量计的量产设计
- 技术与教研并驾齐驱,海风教育如何用模式创新定义教育智能新高度?
- python中np是什么意思_了解python中np是做什么的
- safair浏览器 在回调中跳转 window.open 打不开页面 但是有判断,跳转不了
- Cursor攻略,吃个螃蟹
- 数据恢复必备宝典—BMP文件详解
- 白平衡测试—imatest
- 【微信小程序】uniapp开发小程序如何使用微信云托管或云函数进行云开发
- 安全集成服务资质是什么都有哪些等级?申请安全集成服务资质认证有什么好处?
- csdn博客栏目装修大全------如何植入“微信打赏”、“微信公众号”等
- 如果“宜家”是款互联网产品,那它就是款烂产品!
热门文章
- 淘宝最基础的优化:标题优化
- c语言binsearch函数头文件,C++ binary_search()函数详解
- android 修改已建工程的api版本,更改API级Android Studio
- docker java -jar_使用Docker安装Java镜像运行jar包方法
- python 自动输入文字_pyautogui和pyperclip实现自动输入中文
- 程序员一周内了解一个行业的方法
- 第七周OJ—百位数的分解
- HDU4489 动归解决
- bsoj 1512 金明的预算方案(树型DP)
- 【中科三方】高防DNS如何实现对DDoS攻击的流量清洗?