本帖最后由 susutata 于 2023-5-8 11:19 编辑
APM32外设SDIO和SD卡的配置 - 下
01 APM32的SDIO接口 以下内容以APM32F407xx系列MCU为例。
SDIO的结构APM32的SDIO由SDIO适配器和APB2接口组成。其中,SDIO适配器提供主机功能,包括SD时钟、发送命令和数据传输等。而APB2接口用于控制SDIO适配器的寄存器,并可产生中断和DMA请求。
SDIO的时钟SDIOCLK是SDIO适配器的时钟,频率为48MHz。
PCLK2是APB2总线的时钟。
SDIO_CK
是SDIO接口与SD卡同步的时钟,时钟源是SDIOCLK = 48MHz。当使能BYPASS模式时,SDIO_CK = SDIOCLK = HCLK。而禁止BYPASS时,SDIO_CK = SDIOCLK / (2 + CLKDIV)。
> 注意 1.配置时钟时,要留意不能超过25MHz 2.PCLK >= (3 / 8) x SDIO_CK
SDIO适配器SDIO 适配器是SD卡系统中的主机端。共由五个单元组成,分别是: 控制单元 适配器寄存器单元 命令路径单元 数据路径单元 数据FIFO
1.适配器寄存器和 FIFO 使用 APB2 总线时钟域 (PCLK2) 2.控制单元、命令路径和数据路径使用 SDIO 适配器时钟域 (SDIOCLK)
控制单元控制单元由电源管理子单元和时钟管理子单元组成。电源管理子单元会在断电阶段和上电阶段中禁止卡总线输出信号。而时钟管理子单元负责生成和控制 SDIO_CK 信号。
命令路径命令路径单元向卡发送命令并从卡接收响应。而命令的格式可以看上篇中的“SD总线协议 - 命令格式”章节。 > 命令路径以半双工模式运行,因此CMD线可以发送或者接收命令和响应
命令路径状态机当发送命令和接收响应时,会启动CPSM状态机。 > 命令超时为64个SDIO_CK 时钟周期的固定值
响应SDIO 支持两种响应类型,以适配SD卡的响应。两种类型均使用 CRC 错误校验。包括: 响应的格式可以看上篇中的“SD总线协议 - 响应”章节。
数据路径数据路径子单元负责主机与卡相互传输数据。如果使能了 4 位宽度的总线模式,则 使用所有四个数据信号线 (SDIO_D[3:0]) 在每个时钟周期内传输 4 个数据位,8位宽度则是8个数据位。数据包的格式可以看上述“SD总线协议 - 数据包”章节。 > 未使能宽总线模式时,则每个时钟周期使用 SDIO_D0传输一位数据位
数据路径状态机DPSM 以 SDIO_CK 的频率运**总线信号上的数据与 SDIO_CK 的上升沿保持同步。
数据FIFO数据 FIFO子单元是一个数据缓冲器,带有发送和接收单元。传输 FIFO和接收FIFO是互斥关系的。传输或接收的标志可以触发中断或DMA请求。 > 数据 FIFO是在 APB2 时钟域 (PCLK2) 中运行的,所以来自 SDIO 时钟域 (SDIOCLK) 子单元中的所有信号都将要重新同步
02 SD卡的配置和应用SD卡热插拔检测SD卡的热插拔可以通过CD/DAT3信号脚来检测,实现的步骤如下。 1.将CD/DAT3 信号连在MCU中断引脚上,并通过510K电阻下拉,而MCU配置为高电平触发中断; 2.在没有SD卡插入时,该信号为低电平,一但有SD卡插入,SD卡内部通过50KΩ把 DATA3 信号拉高至高电平,MCU随即产生一个中断,再在中断服务函数里写处理逻辑,从而实现热插拔的功能。
SD卡的初始化SD卡的初始化一般分为“SDIO初始化”、“上电”、“获取卡信息”和“设置数据传输模式”四个过程。具体过程可以查看上篇中的“SD总线协议 - 卡识别模式”章节。
SDIO初始化初始化SDIO为1位总线宽度,频率不超过400KHz。 /* SDIO Initialization Frequency (400KHz max) */
#define SDIO_INIT_CLK_DIV ((uint8_t)0x76) /* 48MHz / (SDMMC_INIT_CLK_DIV + 2) < 400KHz */
SDIO_Config_T sdioConfigStructure;;
/* Default SDIO peripheral configuration for SD card initialization */
sdioConfigStructure.clockEdge = SDIO_CLOCK_EDGE_RISING;
sdioConfigStructure.clockBypass = SDIO_CLOCK_BYPASS_DISABLE;
sdioConfigStructure.clockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
sdioConfigStructure.busWide = SDIO_BUS_WIDE_1B;
sdioConfigStructure.hardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
sdioConfigStructure.clockDiv = SDIO_INIT_CLK_DIV;
/* Initialize SDIO peripheral interface with default configuration */
SDIO_Config(&sdioConfigStructure);
SD卡上电SD卡的上电过程如下图所示。注意上电后要给74个clock,给足够长的时间SD卡准备。
SD_ERROR_T errorStatus = SD_OK;
/* Disable SDIO Clock */
SDIO_DisableClock(hsd);
/* Set Power State to ON */
SDIO_ConfigPowerState(SDIO_POWER_STATE_ON);
/* Enable SDIO Clock */
SDIO_EnableClock(hsd);
/* power on SD card */
errorStatus = SD_PowerON();
获取卡信息
if(errorStatus == SD_OK)
{
/** Initialize SD card */
errorStatus = SD_InitializeCards();
}
if(errorStatus == SD_OK)
{
/** Get SD card info */
errorStatus = SD_GetCardInfo(&SDCardInfo);
}
if(errorStatus == SD_OK)
{
/** Select and enable SD card */
errorStatus = SD_CardSelect((uint32_t)(SDCardInfo.RCA << 16));
}
设置SDIO工作在数据传输模式
if(errorStatus==SD_OK)
{
/** Set SDIO bus wide as 4B */
errorStatus = SD_EnableWideBusOperation(SDIO_BUS_WIDE_4B);
}
SD卡的读写操作
void SD_SingleBlockTest(void)
{
uint32_t sdSize;
uint8_t sta = SD_OK;
long long sectorAddress = 0;
uint8_t buffer[512];
/** Fill the data to be written */
for(sdSize = 0; sdSize < 512; sdSize++)
{
buffer[sdSize] = sdSize + 1 ;
}
/** Write one sector data to sector 0 address */
if((sta = SD_WriteBlock(buffer,sectorAddress,512)) == SD_OK)
{
printf("write success\r\n");
Delay_ms(50);
}
else
{
printf("Write Error Status:%d\r\n",sta);
}
memset(buffer,0,512);
if((sta = SD_ReadBlock(buffer,sectorAddress,512)) == SD_OK)
{
/** Print sector 0 data */
printf("Sector 0 Data:\r\n");
for(sdSize = 1; sdSize <= 512; sdSize++)
{
printf("%X ",buffer[sdSize - 1]);
if((sdSize != 0) && ((sdSize % 16) == 0))
{
printf("\r\n");
}
}
printf("Sector 0 Data Ended\r\n");
}
else
{
printf("Read Error Status %d\r\n",sta);
}
printf("Single Block Test OK\r\n");
}
参考文献
1. SD Card association 2.《Sandiskmanual-SecureDigital2.2》 3.《SD Specifications Part 1 Physical Layer Simplified Specification》
|