经过两天的调试,终于完成了SD卡的上电,初始化,读信息的部分。不说废话,先上图:
![](https://i-blog.csdnimg.cn/blog_migrate/cc27a47c276b0f19195e0633b96d1ca7.png)
上代码(使用标志库):
sdio.c
#include "sdio.h"
uint8_t flag_bit;//完成初始化标志位
CID read_cid;
CID read_csd;
uint32_t rca;
uint32_t size;
uint32_t SD_identify()
{
uint8_t temp;
GPIO_InitTypeDef HGPIO;
SDIO_InitTypeDef HSDIO;
//时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SDIO,ENABLE);
//GPIO初始化
HGPIO.GPIO_Mode=GPIO_Mode_AF;
HGPIO.GPIO_OType=GPIO_OType_PP;
HGPIO.GPIO_Pin=GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11|GPIO_Pin_12;
HGPIO.GPIO_PuPd=GPIO_PuPd_UP;
HGPIO.GPIO_Speed=GPIO_Speed_100MHz;
GPIO_Init(GPIOC,&HGPIO);
HGPIO.GPIO_Mode=GPIO_Mode_AF;
HGPIO.GPIO_OType=GPIO_OType_PP;
HGPIO.GPIO_Pin=GPIO_Pin_2;
HGPIO.GPIO_PuPd=GPIO_PuPd_UP;
HGPIO.GPIO_Speed=GPIO_Speed_100MHz;
GPIO_Init(GPIOD,&HGPIO);
//复用功能配置
GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource10,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource12,GPIO_AF_SDIO);
GPIO_PinAFConfig(GPIOD,GPIO_PinSource2,GPIO_AF_SDIO);
//SDIO初始化
HSDIO.SDIO_BusWide=SDIO_BusWide_1b;
HSDIO.SDIO_ClockBypass=SDIO_ClockBypass_Disable;
HSDIO.SDIO_ClockDiv=118;
HSDIO.SDIO_ClockEdge=SDIO_ClockEdge_Rising;
HSDIO.SDIO_ClockPowerSave=SDIO_ClockBypass_Disable;
HSDIO.SDIO_HardwareFlowControl=SDIO_HardwareFlowControl_Disable;
SDIO_Init(&HSDIO);
temp=sd_power_on();
if(flag_bit==1)
{
read_sdcard_csd();
return temp;
}
else
{
return temp;
}
}
void sd_send_commder(uint32_t commder_int,uint32_t arg,uint32_t response)
{
SDIO_CmdInitTypeDef HSDIO_COMMDER;
HSDIO_COMMDER.SDIO_Argument=arg;
HSDIO_COMMDER.SDIO_CmdIndex=commder_int;
HSDIO_COMMDER.SDIO_CPSM=SDIO_CPSM_Enable;
HSDIO_COMMDER.SDIO_Response=response;
HSDIO_COMMDER.SDIO_Wait=SDIO_Wait_No;
SDIO_SendCommand(&HSDIO_COMMDER);
}
uint8_t sd_power_on()
{
int i;
uint8_t temp;
uint16_t timeout;
uint32_t response;
timeout=0xffff;
SDIO_SetPowerState(SDIO_PowerState_ON);
SDIO_ClockCmd(ENABLE);
for ( i=0; i < 78; i++)
{
sd_send_commder(0,0,SDIO_Response_No);
temp=cmdresponse(0);
}
delay_us(200);
sd_send_commder(0,0,SDIO_Response_No);
temp=cmdresponse(0);
if (temp==sd_ok)
{
delay_us(200);
SDIO->ICR|=(1<<7);
sd_send_commder(8,0x000001AA,SDIO_Response_Short);
temp=cmdresponse7();
if(temp!=sd_ok) return sd_timeout;
delay_us(200);
while (timeout>0)
{
sd_send_commder(55,(uint32_t)0,SDIO_Response_Short);
temp=cmdresponse7();
if(temp!=sd_ok) return temp;
delay_us(200);
sd_send_commder(41,0x40200000,SDIO_Response_Short);
temp=cmdresponse3();
if (temp!=sd_ok) return temp;
response=SDIO_GetResponse(SDIO_RESP1);
if((response>>31)==1) break;
delay_us(200);
timeout--;//上电完成
}
if (timeout==0)
{
return sd_timeout;
}
else
{
flag_bit =1;
return sd_ok;
}
}
else
{
return temp;
}
}
sd_error read_sdcard_csd()
{
uint8_t temp;
uint16_t a;
sd_send_commder(2,(uint32_t)0,SDIO_Response_Long);//获取卡信息
temp=cmdresponse7();
if (temp!=sd_ok) return temp;
read_cid.Rresp1=SDIO->RESP1;
read_cid.Rresp2=SDIO->RESP2;
read_cid.Rresp3=SDIO->RESP3;
read_cid.Rresp4=SDIO->RESP4;
delay_us(200);
sd_send_commder(3,(uint32_t)0,SDIO_Response_Short);//进入传输模式
temp=cmdresponse7();
if (temp!=sd_ok) return temp;
rca=SDIO->RESP1;
rca>>=16;
sd_send_commder(9,(uint32_t)(rca<<16),SDIO_Response_Long);//读取sd卡特殊寄存器
temp=cmdresponse7();
if (temp!=sd_ok) return temp;
read_csd.Rresp1=SDIO->RESP1;
read_csd.Rresp2=SDIO->RESP2;
read_csd.Rresp3=SDIO->RESP3;
read_csd.Rresp4=SDIO->RESP4;
a=(uint16_t)(read_csd.Rresp3>>16);
size=(a+1)*512;
return sd_ok;
}
uint8_t cmdresponse()
{
u32 temp;
uint8_t timeout=0xff;
uint8_t return_data=sd_ok;
while (timeout)
{
temp=SDIO->STA;
if(temp&&(1<<7))break;
timeout--;
}
if(timeout==0)return sd_timeout;
SDIO->ICR=0X5FF;
return return_data;
}
uint8_t cmdresponse7()
{
uint32_t temp=sd_ok;
uint16_t timeout=0xffff;
uint32_t register_read;
while (timeout)
{
register_read=SDIO->STA;
if(register_read&((1<<0)|(1<<2)|(1<<6))) break;
timeout--;
}
if (timeout==0)
{
return sd_timeout;
}
if (((register_read&(1<<6))>>6)==0x01)
{
SDIO->ICR|=(1<<6);
return sd_ok;
}
if (((register_read&(1<<2))>>2)==0x01)
{
SDIO->ICR|=(1<<2);
return sd_timeout;
}
if ((register_read&(1<<0))==0x01)
{
SDIO->ICR|=(1<<0);
return sd_crcerroe;
}
}
uint8_t cmdresponse3()
{
uint32_t temp=sd_ok;
uint16_t timeout=0xffff;
uint32_t register_read;
while (timeout)
{
register_read=SDIO->STA;
if(register_read&((1<<0)|(1<<2)|(1<<6))) break;
timeout--;
}
if (timeout==0)
{
return sd_timeout;
}
if (((register_read&(1<<2))>>2)==0x01)
{
SDIO->ICR|=(1<<2);
return sd_timeout;
}
SDIO->ICR=0X5FF;
return sd_ok;
}
sdio.h
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_sdio.h"
#include "delay.h"
typedef enum
{
sd_ok=0x40,
sd_timeout=0x04,
sd_crcerroe=0x01,
sd_send_ok=0x80
}sd_error;
typedef struct sdio
{
uint32_t Rresp1;
uint32_t Rresp2;
uint32_t Rresp3;
uint32_t Rresp4;
}CID;
extern CID read_cid;
extern CID read_csd;
extern uint32_t rca;
extern uint32_t size;
uint32_t SD_identify(void);
void sd_send_commder(uint32_t commder_int,uint32_t arg,uint32_t response);
uint8_t sd_power_on();
sd_error read_sdcard_csd();
uint8_t cmdresponse();
uint8_t cmdresponse7();
uint8_t cmdresponse3();
中间坑还是挺多的,我把我遇到的坑和解决办法进行罗列
1、用调试器进行调试时,返回值正常,不调试就会返回超时,后加延迟解决。在sd卡协议v2.0版中提到,“两个命令之间至少要有8个时钟的延迟后发生新的命令。
2、发送ACMD41命令后一直返回crc校验错误,这是个大坑!!!。在调试中发现,虽然crc校验位被至位,但命令响应寄存器中已经读到了相印的数据,并且看原子哥的代码中也忽略了crc校验错误位。下面是原子哥的代码
SD_Error CmdResp3Error(void)
{
u32 status;
while(1)
{
status=SDMMC1->STA;
if(status&((1<<0)|(1<<2)|(1<<6)))break;//CRC错误/命令响应超时/已经收到响应(CRC校验成功)
}
if(status&(1<<2)) //响应超时
{
SDMMC1->ICR|=1<<2; //清除命令响应超时标志
return SD_CMD_RSP_TIMEOUT;
}
SDMMC1->ICR=0X5FF; //清除标记
return SD_OK;
}
3、搞错响应类型,会导致crc错误,请各位同仁注意检查响应类型
SDIO_Response_Short //短响应
SDIO_Response_Long //长响应,28位
SDIO_Response_No //无响应
4、上电后要先给卡>74个时钟,然后再发送cmd0
for ( i=0; i < 78; i++)
{
sd_send_commder(0,0,SDIO_Response_No);
temp=cmdresponse(0);
}
欣喜之余,打算最近把fat文件系统移植上去。