平台:STM32ZET6(核心板)+ST-LINK/V2+SD卡+USB串口线
工程介绍:主要文件在USER组中,bsp_sdio_sdcard.c,bsp_sdio_sdcard.h和main.c,另外FatFs是用来后面移植文件系统使用的,对于本节内容暂时不需要。bsp_sdio_sdcard.c和bsp_sdio_sdcard.h文件主要参考教材《STM32库开发实战指南——基于STM32F03》。教材中部分省略内容来自于其他网友总结,在此表示感谢。
本文想大概讲一下SD卡读写实验的具体步骤,以及最重要的是一个细节问题,它使我在这个问题上耗了两天的时间。
大概流程如下:
1.硬件设计:
因为在该平台的板子上已经存在了一个SD卡接口,因此,不需要额外的连线,只需要一条串口连接线连接到电脑上输出调试信息即可。
2.软件设计:
操作的大致流程:
1》初始化相关的GPIO及SDIO外设;
2》配置SDIO基本通信环境进入卡识别模式,通过几个命令处理得到卡类型;
3》如果是可用卡就进入数据传输模式,接下来可以进行读写和擦差操作。
具体的步骤:
因为官方固件库有实现的案例,所以我们不妨”拿来主义“。其实bsp_sdio_sdcard.c和bsp_sdio_sdcard.h就是对stm32_eval_sdio_sd.c和stm32_eval_sdio_sd.h文件的修改和借鉴。我是直接把名字改了,然后在源文件的基础上做了修改。
2.1 GPIO的初始化和DMA配置(以下函数位于bsp_sdio_sdcard.c和bsp_sdio_sdcard.h文件中,其大部分内容来自于上面固件库的例程stm32_eval_sdio_sd.c和stm32_eval_sdio_sd.h的修改和添加)
//宏定义
//SDIO_FIOF地址=SDIO地址+0x80至SDIO地址+0xfc
#define SDIO_FIFO_ADDRESS ((uint32_t) 0x40018080)
//SDIO初始化时钟频率,最大400kHz
#define SDIO_INIT_CLK_DIV ((uint8_t) 0xB2)
//数据传输时钟频率,最大25MHz
#define SDIO_TRANSFER_CLK_DIV ((uint8_t) 0x02)//数据读写速率
//SDIO传输使用的模式,可选DMA模式或者普通模式
#define SD_DMA_MODE ((uint32_t) 0x00000000)
//#define SD_POLLING_MODE ((uint32_t) 0x00000002)
//SD卡检测GPIO引脚
#define SD_DETECT_GPIO_PORT GPIOC
#define SD_DETECT_PIN GPIO_Pin_13
//GPIO初始化,开启时钟
static void GPIO_Configuration()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);//开启时钟
//配置PC8/9/10/11/12引脚(D0/1/2/3和CLK)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//设置复用模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
//配置PD2为CMD引脚
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD, &GPIO_InitStructure);
//使能SDIO AHB时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, ENABLE);
//使能DMA时钟
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
}
//DMA传输配置,接收数据
void SD_DMA_RxConfig(uint32_t* BufferDST, uint32_t BufferSize)
{
DMA_InitTypeDef DMA_InitStructure;
//清除DMA标志位
DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4);
//配置前禁止DMA
DMA_Cmd(DMA2_Channel4, DISABLE);
//DMA传输配置
//外设地址
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
//目标地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferDST;
//外设为源地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
//除以4,把字转换成字节单位
DMA_InitStructure.DMA_BufferSize = BufferSize / 4;
//外设地址不增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
//存储目标地址
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//外设数据大小为字
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
//存储数据大小
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
//不循环,一次
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
//通道优先级为高
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
//非存储器至存储器模式
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
//初始化DMA
DMA_Init(DMA2_Channel4, &DMA_InitStructure);
//使能DMA通道
DMA_Cmd(DMA2_Channel4, ENABLE);
}
//为SDIO发送数据配置DMA2的通道2的请求
void SD_DMA_TxConfig(uint32_t *BufferSRC, uint32_t BufferSize)
{
DMA_InitTypeDef DMA_InitStructure;
//清除DMA标志位
DMA_ClearFlag(DMA2_FLAG_TC4 | DMA2_FLAG_TE4 | DMA2_FLAG_HT4 | DMA2_FLAG_GL4);
//配置前禁止DMA
DMA_Cmd(DMA2_Channel4, DISABLE);
//DMA传输配置
//外设地址
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SDIO_FIFO_ADDRESS;
//存储地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)BufferSRC;
//外设为目的地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
//除以4,把字转换成字节单位
DMA_InitStructure.DMA_BufferSize = BufferSize / 4;
//外设地址不增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
//存储目标地址
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
//外设数据大小为字
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
//存储数据大小
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
//不循环,一次
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
//通道优先级为高
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
//非存储器至存储器模式
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
//初始化DMA
DMA_Init(DMA2_Channel4, &DMA_InitStructure);
//使能DMA通道
DMA_Cmd(DMA2_Channel4, ENABLE);
}
//配置中断控制器
void SDIO_NVIC_Configuration()
{
NVIC_InitTypeDef NVIC_InitStructure;
//优先级组选择
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
//选择USART的中断源
NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
//配置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
//配置子优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
//使能中断
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
//初始化配置
NVIC_Init(&NVIC_InitStructure);
}
2.2 SDIO初始化
/**
* @brief Initializes the SD Card and put it into StandBy State (Ready for data
* transfer).
* @param None
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_Init(void)
{
SD_Error errorstatus = SD_OK;
//配置SDIO中断优先级
SDIO_NVIC_Configuration();
//SDIO外设底层引脚初始化
GPIO_Configuration();
//对SDIO所有的寄存器进行复位
SDIO_DeInit();
//上电,并进行卡识别流程,确认卡的操作电压
errorstatus = SD_PowerON();
//识别不成功
if (errorstatus != SD_OK)
{
/*!< CMD Response TimeOut (wait for CMDSENT flag) */
return(errorstatus);
}
//识别成功,对SD卡进行初始化
errorstatus = SD_InitializeCards();
//初始化失败
if (errorstatus != SD_OK)
{
/*!< CMD Response TimeOut (wait for CMDSENT flag) */
return(errorstatus);
}
//进入数据传输模式,提高读写速度
/*!< Configure the SDIO peripheral */
/*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_TRANSFER_CLK_DIV) */
/*!< on STM32F2xx devices, SDIOCLK is fixed to 48MHz */
SDIO_InitStructure.SDIO_ClockDiv = SDIO_TRANSFER_CLK_DIV;
SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
SDIO_Init(&SDIO_InitStructure);
if (errorstatus == SD_OK)
{
/*----------------- Read CSD/CID MSD registers ------------------*/
errorstatus = SD_GetCardInfo(&SDCardInfo);
}
if (errorstatus == SD_OK)
{
/*----------------- Select Card --------------------------------*/
errorstatus = SD_SelectDeselect((uint32_t) (SDCardInfo.RCA << 16));
}
if (errorstatus == SD_OK)
{
//为了提高读写,开启四位模式
errorstatus = SD_EnableWideBusOperation(SDIO_BusWide_4b);
}
return(errorstatus);
}
与之相对的还有一个SD_DeInit()函数,复位寄存器。
void SD_LowLevel_DeInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/*!< Disable SDIO Clock */
SDIO_ClockCmd(DISABLE);
/*!< Set Power State to OFF */
SDIO_SetPowerState(SDIO_PowerState_OFF);
/*!< DeInitializes the SDIO peripheral */
SDIO_DeInit();
/*!< Disable the SDIO AHB Clock */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_SDIO, DISABLE);
/*!< Configure PC.08, PC.09, PC.10, PC.11, PC.12 pin: D0, D1, D2, D3, CLK pin */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOC, &GPIO_InitStructure);
/*!< Configure PD.02 CMD line */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
//对SDIO所有的寄存器进行复位
void SD_DeInit(void)
{
SD_LowLevel_DeInit();
}
SD_PowerOn()函数用于查询卡的工作电压和时钟控制配置,没有修改。可以看一下。/**
* @brief Enquires cards about their operating voltage and configures
* clock controls.
* @param None
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_PowerON(void)
{
SD_Error errorstatus = SD_OK;
uint32_t response = 0, count = 0, validvoltage = 0;
uint32_t SDType = SD_STD_CAPACITY;
/*!< Power ON Sequence -----------------------------------------------------*/
/*!< Configure the SDIO peripheral */
/*!< SDIOCLK = HCLK, SDIO_CK = HCLK/(2 + SDIO_INIT_CLK_DIV) */
/*!< on STM32F2xx devices, SDIOCLK is fixed to 48MHz */
/*!< SDIO_CK for initialization should not exceed 400 KHz */
SDIO_InitStructure.SDIO_ClockDiv = SDIO_INIT_CLK_DIV;
SDIO_InitStructure.SDIO_ClockEdge = SDIO_ClockEdge_Rising;
SDIO_InitStructure.SDIO_ClockBypass = SDIO_ClockBypass_Disable;
SDIO_InitStructure.SDIO_ClockPowerSave = SDIO_ClockPowerSave_Disable;
SDIO_InitStructure.SDIO_BusWide = SDIO_BusWide_1b;
SDIO_InitStructure.SDIO_HardwareFlowControl = SDIO_HardwareFlowControl_Disable;
SDIO_Init(&SDIO_InitStructure);
/*!< Set Power State to ON */
SDIO_SetPowerState(SDIO_PowerState_ON);
/*!< Enable SDIO Clock */
SDIO_ClockCmd(ENABLE);
/*!< CMD0: 复位所有的SD卡,使其进入空闲状态 ---------------------------------------------------*/
/*!< No CMD response required */
SDIO_CmdInitStructure.SDIO_Argument = 0x0;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_GO_IDLE_STATE;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_No;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;//关闭等待中断
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;//CPSM在发送数据之前等待数据传输结束
SDIO_SendCommand(&SDIO_CmdInitStructure);
//检测是否正确接收到Cmd0
errorstatus = CmdError();
if (errorstatus != SD_OK)
{
/*!< CMD Response TimeOut (wait for CMDSENT flag) */
return(errorstatus);
}
/*!< CMD8: SEND_IF_COND ----------------------------------------------------*/
/*!< Send CMD8 to verify SD card interface operating condition */
/*!< Argument: - [31:12]: Reserved (shall be set to '0')
- [11:8]: Supply Voltage (VHS) 0x1 (Range: 2.7-3.6 V)
- [7:0]: Check Pattern (recommended 0xAA) */
/*!< CMD Response: R7 */
SDIO_CmdInitStructure.SDIO_Argument = SD_CHECK_PATTERN;
SDIO_CmdInitStructure.SDIO_CmdIndex = SDIO_SEND_IF_COND;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
//检查是否接收到该命令
errorstatus = CmdResp7Error();
if (errorstatus == SD_OK)
{
CardType = SDIO_STD_CAPACITY_SD_CARD_V2_0; /*!< SD Card 2.0 */
SDType = SD_HIGH_CAPACITY;
}
else
{
//有相应就是SD CARD 2.0,此时无响应,代表是1.x或MMC
/*!< CMD55 */
SDIO_CmdInitStructure.SDIO_Argument = 0x00;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
}
//检查是SD卡,还是MMC卡,或者是不支持的卡
/*!< CMD55 */
SDIO_CmdInitStructure.SDIO_Argument = 0x00;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
/*!< If errorstatus is Command TimeOut, it is a MMC card */
/*!< If errorstatus is SD_OK it is a SD card: SD card 2.0 (voltage range mismatch)
or SD card 1.x */
if (errorstatus == SD_OK)
{
/*!< SD CARD */
/*!< Send ACMD41 SD_APP_OP_COND with Argument 0x80100000 */
while ((!validvoltage) && (count < SD_MAX_VOLT_TRIAL))
{
/*!< SEND CMD55 APP_CMD with RCA as 0 */
SDIO_CmdInitStructure.SDIO_Argument = 0x00;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_APP_CMD;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_APP_CMD);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
SDIO_CmdInitStructure.SDIO_Argument = SD_VOLTAGE_WINDOW_SD | SDType;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_APP_OP_COND;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp3Error();
if (errorstatus != SD_OK)
{
return(errorstatus);
}
response = SDIO_GetResponse(SDIO_RESP1);
validvoltage = (((response >> 31) == 1) ? 1 : 0);
count++;
}
if (count >= SD_MAX_VOLT_TRIAL)
{
errorstatus = SD_INVALID_VOLTRANGE;
return(errorstatus);
}
if (response &= SD_HIGH_CAPACITY)
{
CardType = SDIO_HIGH_CAPACITY_SD_CARD;
}
}/*!< else MMC Card */
return(errorstatus);
}
初始化所有的卡或者单个卡进入就绪状态,没有修改。
/**
* @brief Intialises all cards or single card as the case may be Card(s) come
* into standby state.
* @param None
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_InitializeCards(void)
{
SD_Error errorstatus = SD_OK;
uint16_t rca = 0x01;
if (SDIO_GetPowerState() == SDIO_PowerState_OFF)
{
errorstatus = SD_REQUEST_NOT_APPLICABLE;
return(errorstatus);
}
if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
{
/*!< Send CMD2 ALL_SEND_CID */
SDIO_CmdInitStructure.SDIO_Argument = 0x0;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ALL_SEND_CID;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp2Error();
if (SD_OK != errorstatus)
{
return(errorstatus);
}
CID_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
CID_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
CID_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
CID_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
}
if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_SECURE_DIGITAL_IO_COMBO_CARD == CardType)
|| (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
{
/*!< Send CMD3 SET_REL_ADDR with argument 0 */
/*!< SD Card publishes its RCA. */
SDIO_CmdInitStructure.SDIO_Argument = 0x00;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_REL_ADDR;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp6Error(SD_CMD_SET_REL_ADDR, &rca);
if (SD_OK != errorstatus)
{
return(errorstatus);
}
}
if (SDIO_SECURE_DIGITAL_IO_CARD != CardType)
{
RCA = rca;
/*!< Send CMD9 SEND_CSD with argument as card's RCA */
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)(rca << 16);
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SEND_CSD;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Long;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp2Error();
if (SD_OK != errorstatus)
{
return(errorstatus);
}
CSD_Tab[0] = SDIO_GetResponse(SDIO_RESP1);
CSD_Tab[1] = SDIO_GetResponse(SDIO_RESP2);
CSD_Tab[2] = SDIO_GetResponse(SDIO_RESP3);
CSD_Tab[3] = SDIO_GetResponse(SDIO_RESP4);
}
errorstatus = SD_OK; /*!< All cards get intialized */
return(errorstatus);
}
2.3 数据操作
SD卡数据擦除操作,基本没有修改。
/**
* @brief Allows to erase memory area specified for the given card.
* @param startaddr: the start address.
* @param endaddr: the end address.
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_Erase(uint32_t startaddr, uint32_t endaddr)
{
SD_Error errorstatus = SD_OK;
uint32_t delay = 0;
__IO uint32_t maxdelay = 0;
uint8_t cardstate = 0;
/*!< Check if the card coomnd class supports erase command */
if (((CSD_Tab[1] >> 20) & SD_CCCC_ERASE) == 0)
{
errorstatus = SD_REQUEST_NOT_APPLICABLE;
return(errorstatus);
}
//延时,根据时钟分频设置来计算
maxdelay = 120000 / ((SDIO->CLKCR & 0xFF) + 2);
if (SDIO_GetResponse(SDIO_RESP1) & SD_CARD_LOCKED)
{
errorstatus = SD_LOCK_UNLOCK_FAILED;
return(errorstatus);
}
if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
{
startaddr /= 512;
endaddr /= 512;
}
/*!< According to sd-card spec 1.0 ERASE_GROUP_START (CMD32) and erase_group_end(CMD33) */
if ((SDIO_STD_CAPACITY_SD_CARD_V1_1 == CardType) || (SDIO_STD_CAPACITY_SD_CARD_V2_0 == CardType) || (SDIO_HIGH_CAPACITY_SD_CARD == CardType))
{
/*!< Send CMD32 SD_ERASE_GRP_START with argument as addr */
SDIO_CmdInitStructure.SDIO_Argument = startaddr;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_START;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_START);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
/*!< Send CMD33 SD_ERASE_GRP_END with argument as addr */
SDIO_CmdInitStructure.SDIO_Argument = endaddr;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SD_ERASE_GRP_END;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_SD_ERASE_GRP_END);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
}
/*!< Send CMD38 ERASE */
SDIO_CmdInitStructure.SDIO_Argument = 0;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_ERASE;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_ERASE);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
for (delay = 0; delay < maxdelay; delay++)
{}
/*!< Wait till the card is in programming state */
errorstatus = IsCardProgramming(&cardstate);
while ((errorstatus == SD_OK) && ((SD_CARD_PROGRAMMING == cardstate) || (SD_CARD_RECEIVING == cardstate)))
{
errorstatus = IsCardProgramming(&cardstate);
}
return(errorstatus);
}
数据写入操作,需要修改,关键,否则可能出错。
/**
* @brief Allows to write one block starting from a specified address in a card.
* The Data transfer can be managed by DMA mode or Polling mode.
* @note This operation should be followed by two functions to check if the
* DMA Controller and SD Card status.
* - SD_ReadWaitOperation(): this function insure that the DMA
* controller has finished all data transfer.
* - SD_GetStatus(): to check that the SD Card has finished the
* data transfer and it is ready for data.
* @param writebuff: pointer to the buffer that contain the data to be transferred.
* @param WriteAddr: Address from where data are to be read.
* @param BlockSize: the SD card Data block size. The Block size should be 512.
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_WriteBlock(uint8_t *writebuff, uint32_t WriteAddr, uint16_t BlockSize)
{
SD_Error errorstatus = SD_OK;
TransferError = SD_OK;
TransferEnd = 0;
StopCondition = 0;
//复位SDIO配置
SDIO->DCTRL = 0x0;
if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
{
BlockSize = 512;
WriteAddr /= 512;
}
//----------------------在库的基础上添加的,没有此段将容易卡死在DMA检测中--------------------------
/*!< Send CMD16 设置块大小 */
SDIO_CmdInitStructure.SDIO_Argument = BlockSize;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
//----------------------------------------------------------------------------------------------
/*!< Send CMD24 WRITE_SINGLE_BLOCK */
SDIO_CmdInitStructure.SDIO_Argument = WriteAddr;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_WRITE_SINGLE_BLOCK;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_WRITE_SINGLE_BLOCK);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
//配置SDIO写数据寄存器
SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToCard;
SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;//开启数据通道状态机
SDIO_DataConfig(&SDIO_DataInitStructure);
SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);//开启数据传输结束中断
SD_DMA_TxConfig((uint32_t *)writebuff, BlockSize);//配置DMA,跟Rx类似
SDIO_DMACmd(ENABLE);//使能SDIO的DMA请求
return(errorstatus);
}
/**
* @brief This function waits until the SDIO DMA data transfer is finished. 写操作等待函数
* This function should be called after SDIO_WriteBlock() and
* SDIO_WriteMultiBlocks() function to insure that all data sent by the
* card are already transferred by the DMA controller.
* @param None.
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_WaitWriteOperation(void)
{
SD_Error errorstatus = SD_OK;
while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
{}
if (TransferError != SD_OK)
{
return(TransferError);
}
/*!< Clear all the static flags */
SDIO_ClearFlag(SDIO_STATIC_FLAGS);
return(errorstatus);
}
数据读取操作,需要修改,关键,否则可能出错。
/**
* @brief Allows to read one block from a specified address in a card. The Data
* transfer can be managed by DMA mode or Polling mode.
* @note This operation should be followed by two functions to check if the
* DMA Controller and SD Card status.
* - SD_ReadWaitOperation(): this function insure that the DMA
* controller has finished all data transfer.
* - SD_GetStatus(): to check that the SD Card has finished the
* data transfer and it is ready for data.
* @param readbuff: pointer to the buffer that will contain the received data
* @param ReadAddr: Address from where data are to be read.
* @param BlockSize: the SD card Data block size. The Block size should be 512.
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_ReadBlock(uint8_t *readbuff, uint32_t ReadAddr, uint16_t BlockSize)
{
SD_Error errorstatus = SD_OK;
TransferError = SD_OK;
TransferEnd = 0;//清除传输结束标志位,在中断服务中置一
StopCondition = 0;
SDIO->DCTRL = 0x0;
if (CardType == SDIO_HIGH_CAPACITY_SD_CARD)
{
BlockSize = 512;
ReadAddr /= 512;
}
//----------------------在库的基础上添加的,没有此段将容易卡死在DMA检测中--------------------------
/*!< Send CMD16 设置块大小 */
SDIO_CmdInitStructure.SDIO_Argument = BlockSize;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_SET_BLOCKLEN;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_SET_BLOCKLEN);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
//----------------------------------------------------------------------------------------------
SDIO_DataInitStructure.SDIO_DataTimeOut = SD_DATATIMEOUT;
SDIO_DataInitStructure.SDIO_DataLength = BlockSize;
SDIO_DataInitStructure.SDIO_DataBlockSize = (uint32_t) 9 << 4;
SDIO_DataInitStructure.SDIO_TransferDir = SDIO_TransferDir_ToSDIO;
SDIO_DataInitStructure.SDIO_TransferMode = SDIO_TransferMode_Block;
SDIO_DataInitStructure.SDIO_DPSM = SDIO_DPSM_Enable;
SDIO_DataConfig(&SDIO_DataInitStructure);
/*!< Send CMD17 READ_SINGLE_BLOCK */
SDIO_CmdInitStructure.SDIO_Argument = (uint32_t)ReadAddr;
SDIO_CmdInitStructure.SDIO_CmdIndex = SD_CMD_READ_SINGLE_BLOCK;
SDIO_CmdInitStructure.SDIO_Response = SDIO_Response_Short;
SDIO_CmdInitStructure.SDIO_Wait = SDIO_Wait_No;
SDIO_CmdInitStructure.SDIO_CPSM = SDIO_CPSM_Enable;
SDIO_SendCommand(&SDIO_CmdInitStructure);
errorstatus = CmdResp1Error(SD_CMD_READ_SINGLE_BLOCK);
if (errorstatus != SD_OK)
{
return(errorstatus);
}
SDIO_ITConfig(SDIO_IT_DATAEND, ENABLE);
SDIO_DMACmd(ENABLE);
SD_DMA_RxConfig((uint32_t *)readbuff, BlockSize);
return(errorstatus);
}
/**
* @brief This function waits until the SDIO DMA data transfer is finished.
* This function should be called after SDIO_ReadMultiBlocks() function
* to insure that all data sent by the card are already transferred by
* the DMA controller.
* @param None.
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_WaitReadOperation(void)
{
SD_Error errorstatus = SD_OK;
while ((SD_DMAEndOfTransferStatus() == RESET) && (TransferEnd == 0) && (TransferError == SD_OK))
{}
if (TransferError != SD_OK)
{
return(TransferError);
}
return(errorstatus);
}
2.4 中断服务函数,此处需要提醒的是,这个中断处理函数的位置推荐也放在
bsp_sdio_sdcard.c文件中,因为这样的话可以防止忘记,Keil会自动地根据它的名称识别出它是一个中断函数,无论该函数位于哪个文件中,这个请放心。
//SDIO中断服务函数
void SDIO_IRQHandler()
{
//中断相关的处理
SD_ProcessIRQSrc();
}
/**
* @brief Allows to process all the interrupts that are high. 由中断服务程序调用,负责中断相关的处理
* @param None
* @retval SD_Error: SD Card Error code.
*/
SD_Error SD_ProcessIRQSrc(void)
{
if (StopCondition == 1)//发送读取/多块读写命令时置一
{
SDIO->ARG = 0x0;//命令参数寄存器
SDIO->CMD = 0x44C;//命令寄存器
TransferError = CmdResp1Error(SD_CMD_STOP_TRANSMISSION);//开启命令状态机短响应
}
else
{
TransferError = SD_OK;
}
SDIO_ClearITPendingBit(SDIO_IT_DATAEND);//清中断
SDIO_ITConfig(SDIO_IT_DATAEND, DISABLE);//关闭SDIO中断使能
TransferEnd = 1;
return(TransferError);
}
2.5 测试函数
#include "sdio_test.h"
#include "stdio.h"
#include "bsp_sdio_sdcard.h"
#include "stm32f10x.h"
#define BLOCK_SIZE 512
#define NUMBER_OF_BLOCKS 32
#define MULTI_BUFFER_SIZE (BLOCK_SIZE * NUMBER_OF_BLOCKS)//缓冲区大小
uint8_t Buffer_Block_Tx[BLOCK_SIZE];
uint8_t Buffer_Block_Rx[BLOCK_SIZE];
uint8_t Buffer_MultiBlock_Tx[MULTI_BUFFER_SIZE];
uint8_t Buffer_MultiBlock_Rx[MULTI_BUFFER_SIZE];
SD_Error Status;
//测试状态
typedef enum
{
FAILED=0,
PASSED=!FAILED
} TestStatus;
volatile TestStatus EraseStatus = FAILED, TransferStatus1 = FAILED, TransferStatus2 = FAILED;
//声明外部变量
extern SD_CardInfo SDCardInfo;
//是否擦除成功
TestStatus eBuffercmp(uint8_t* pBuffer,uint32_t BufferLength)
{
while(BufferLength--)
{
if((*pBuffer!=0xFF) && (*pBuffer!=0x00))//擦除后是0xff或0x00
{
return FAILED;
}
pBuffer++;
}
return PASSED;
}
/*
*函数名:Buffercmp
*描述:比较两个缓冲区中的数据是否相等
*输入:-pBuffer1,-pBuffer2:要比较的缓冲区的指针*-BufferLength缓冲区长度*输出:-PASSED相等*-FAILED不等*/
TestStatus Buffercmp(uint8_t* pBuffer1,uint8_t* pBuffer2,uint32_t BufferLength)
{
while(BufferLength--)
{
if(*pBuffer1 != *pBuffer2)
{
return FAILED;
}
pBuffer1++;
pBuffer2++;
}
return PASSED;
}
/*
*函数名:Fill_Buffer
*描述:在缓冲区中填写数据
*输入:-pBuffer要填充的缓冲区
* -BufferLength要填充的大小
* -Offset填在缓冲区的第一个值
*输出:无
*/
void Fill_Buffer(uint8_t* pBuffer,uint32_t BufferLength,uint32_t Offset)
{
uint16_t index=0;
/*Putin global buffer some values*/
for(index=0;index<BufferLength;index++)
{
pBuffer[index] = index + Offset;
}
}
/*
*函数名:SD_EraseTest
*描述:擦除数据测试
*输入:无
*输出:无
*/
void SD_EraseTest(void)
{
/*-------------------BlockErase------------------------------------------*/
if(Status==SD_OK)
{
Status=SD_Erase(0x00,(BLOCK_SIZE*NUMBER_OF_BLOCKS));//第一个参数为擦除起始地址,第二个参数为擦除结束地址
}
if(Status==SD_OK)
{
//读取刚刚擦除的区域
Status=SD_ReadMultiBlocks(Buffer_MultiBlock_Rx,0x00,BLOCK_SIZE,NUMBER_OF_BLOCKS);
/*Check if the Transfer is finished*/
Status=SD_WaitReadOperation();//循环查询dma传输是否结束
/*Wait until end of DMA transfer*/
while(SD_GetStatus()!=SD_TRANSFER_OK);
}
/*Check the correctness of erased blocks*/
if(Status==SD_OK)
{
//把擦除区域读出来对比
EraseStatus=eBuffercmp(Buffer_MultiBlock_Rx,MULTI_BUFFER_SIZE);
}
if(EraseStatus==PASSED)
{
printf("擦除测试成功!\r\n");
}
else
{
printf("擦除测试失败!\r\n");
}
}
//单块读写测试
void SD_SingleBlockTest()
{
//向数组填充要写入的数据
Fill_Buffer(Buffer_Block_Tx, BLOCK_SIZE, 0x320F);
if(Status == SD_OK)
{
//将BLOCK_SIZE个字节写入SD卡的0地址
Status = SD_WriteBlock(Buffer_Block_Tx, 0x00, BLOCK_SIZE);
//检查传输
Status = SD_WaitWriteOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
}
if(Status == SD_OK)
{
//从SD卡的0地址读取512个字节的数据
Status = SD_ReadBlock(Buffer_Block_Rx, 0x00, BLOCK_SIZE);
//检查传输
Status = SD_WaitWriteOperation();
while(SD_GetStatus() != SD_TRANSFER_OK);
}
//校验数据是否一致
if(Status == SD_OK)
{
TransferStatus1 = Buffercmp(Buffer_Block_Tx, Buffer_Block_Rx, BLOCK_SIZE);
}
if(TransferStatus1 == PASSED)
{
printf("单块读写测试成功!\r\n");
}
else
{
printf("单块读写测试失败!\r\n");
}
}
//SD卡测试函数
void SD_Test()
{
//SD卡使用SDIO中断及DMA中断接收数据,中断服务程序位于"bsp_sdio_sdcard.c"中
Status = SD_Init();
if(Status != SD_OK)
{
printf("SD卡初始化失败!请确保SD卡插在开发板上,或者换一张SD卡测试!\r\n");
}
else
{
printf("SD卡初始化成功!\r\n");
printf("SD_cid:%d \r\n", (SDCardInfo.SD_cid.ManufacturerID));
printf("CardBlockSize:%d \r\n", SDCardInfo.CardBlockSize);
printf("CardCapacity:%d \r\n", SDCardInfo.CardCapacity);
}
if(Status == SD_OK)
{
//擦除测试
SD_EraseTest();
//单块读写测试
SD_SingleBlockTest();
}
}
2
.6 重要提醒
因为检测SD卡是否正确插入的宏SD_PRESENT和SD_NOT_PRESENT是ST官方的原SD驱动程序以一个输入引脚电平判断SD卡是否插入的,但是我们硬件中没有使用到该引脚,所以我们应该把ST驱动中原来用来进行引脚检测部分的代码删除掉,但代码中,我们还是保留了宏SD_PRESENT和SD_NOT_PRESENT的定义。
用于代码检测的具体位置:
修改方案:
其实就是不做检查,利用初始化是否成功来代替检查过程,就可以了。
删除步骤:
/**
* @brief Detect if SD card is correctly plugged in the memory slot.
* @param None
* @retval Return if SD is detected or not
*/
uint8_t SD_Detect(void)
{
__IO uint8_t status = SD_PRESENT;
/*!< Check GPIO to detect SD */
if (GPIO_ReadInputDataBit(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) != Bit_RESET)
{
status = SD_NOT_PRESENT;
}
return status;
}
//来自于测试文件中的外部全局变量
extern SD_Error Status;
/**
* @brief Returns the current card's state.
* @param None
* @retval SDCardState: SD Card Error or SD Card Current State.
*/
SDCardState SD_GetState(void)
{
uint32_t resp1 = 0;
//原SD卡驱动程序以一个输入引脚的电平判断SD卡是否正确插入,由于我们硬件中没有使用该引脚,所以我们的程序需要在这里屏蔽掉SD驱动中原来引脚电平检测部分的代码
//if(SD_Detect()== SD_PRESENT)
if(Status == SD_OK)
{
if (SD_SendStatus(&resp1) != SD_OK)
{
return SD_CARD_ERROR;
}
else
{
return (SDCardState)((resp1 >> 9) & 0x0F);
}
}
else
{
return SD_CARD_ERROR;
}
}
你可能会缺少的代码:
/**
* @brief Gets the cuurent sd card data transfer status.
* @param None
* @retval SDTransferState: Data Transfer state.
* This value can be:
* - SD_TRANSFER_OK: No data transfer is acting
* - SD_TRANSFER_BUSY: Data transfer is acting
*/
SDTransferState SD_GetStatus(void)
{
SDCardState cardstate = SD_CARD_TRANSFER;
cardstate = SD_GetState();
if (cardstate == SD_CARD_TRANSFER)
{
return(SD_TRANSFER_OK);
}
else if(cardstate == SD_CARD_ERROR)
{
return (SD_TRANSFER_ERROR);
}
else
{
return(SD_TRANSFER_BUSY);
}
}
就是这些,下一节将介绍向SD卡移植FatFs。