银杏科技有限公司旗下技术文档发布平台 |
技术支持电话 | 0379-69926675-801 |
技术支持邮件 | Gingko@vip.163.com |
版本 | 日期 | 作者 | 修改内容 |
V1.0 | 2020-07-04 | gingko | 初次建立 |
实验十三:SDIO实验——读取SD卡信息
一、 实验目的与意义
了解STM32 SDIO结构。
了解STM32 SDIO特征。
掌握SDIO的使用方法。
掌握STM32 HAL库中SDIO属性的配置方法。
掌握KEIL MDK集成开发环境使用方法。
二、 实验设备及平台
-
-
Micro USB线缆。
Keil MDK 开发平台。
STM32CubeMX开发平台。
装有WIN XP(及更高版本)系统的计算机。
三、 实验原理
1、SDIO简介
SDIO在SD标准上定义了一种外设接口。目前,SDIO主要有两类应用——可移动和不可移动。可移动设备作为Palm和Windows Mobile的扩展设备,用来增加蓝牙、照相机、GPS和802.11b功能。不可移动设备遵循相同的电气标准,但不要求符合物理标准。某些手机内包含通过SDIO连接CPU的802.11芯片。此举将“珍贵”的I/O管脚资源用于更重要的功能。
蓝牙、照相机、GPS和802.11b设备有专为它们定义的应用规范。这些应用规范与为PCI和USB设备定义的类规范很相像。它们允许任何宿主设备与任意外设“通话”,只要它们都支持应用规范。
SDIO和SD卡规范间的一个重要区别是增加了低速标准。SDIO卡只需要SPI和1位SD传输模式。低速卡的目标应用是以最小的硬件开支支持低速I/O能力。低速卡支持类似调制解调器、条码扫描仪和GPS接受器等应用。对“组合”卡(存储器+SDIO)而言,全速和4位操作对卡内存储器和SDIO部分都是强制要求的。
2、SDMMC主要特性
SD/SDIO MMC卡主机接口(SDMMC)提供APB2外设总线与多媒体卡(MMC)、SD存储卡以及SDIO卡之间的接口。多媒体卡协会网站上提供了由MMCA技术委员会发布的多媒体卡系统规范。SD卡协会网站上提供了SD存储卡和SDI/O卡系统规范。
SDMMC具有以下特性:
DMA(直接存储器访问)传输不需要占用CPU,可以在存储器至存储器实现高速的数据传输。本实验采用DMA2控制器的数据流0,选用通道0进行数据传输。通过LED的颜色来判断传输是否成功。
3、SDMMC的时钟
SDMMC总共有3个时钟,分别是:
(1)卡时钟(SDMMC_CK):每个时钟周期在命令和数据线上传输1位命令或数据。对于多媒体卡V3.31协议,时钟频率可以在0MHz至20MHz间变化;对于多媒体卡V4.0/4.2协议,时钟频率可以在0MHz至48MHz间变化;对于SD或SDI/O卡,时钟频率可以在0MHz至25MHz间变化。
(2)SDMMC适配器时钟(SDMMCCLK):该时钟用于驱动SDMMC适配器,来自PLL48CK,一般为48Mhz,并用于产生SDMMC_CK时钟(当系统时钟为180M的时候,PLL48CK=45Mhz)。
(3)APB2总线接口时钟(PCLK2):该时钟用于驱动SDMMC的APB2总线接口,其频率为HCLK/2,一般为108MHz。
4、SDMM框图
5、原理图
四、 实验程序
1、主函数
int main(void)
{
/* MCU 配置*/
/* 重置所有外围设备,初始化Flash接口和Systick */
HAL_Init();
/* 配置系统时钟 */
SystemClock_Config();
/* 初始化所有已配置的外围设备 */
MX_GPIO_Init();
MX_DMA_Init();
MX_SDMMC1_SD_Init();
MX_USART6_UART_Init();
usart6.initialize(115200); //串口波特设置
usart6.printf("\x0c"); //清屏
usart6.printf("\033[1;32;40m"); //设置终端字体为绿色
usart6.printf("\r\nHello, I am iCore4!\r\n\r\n"); //串口信息输出
//配置SD接口位宽为4bit用于数据传输
if(HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B) != HAL_OK){
usart6.printf("SD Card Error!\r\n");
while(1){
LED_RED_ON;
HAL_Delay(500);
LED_RED_OFF;
HAL_Delay(500);
}
}else{
usart6.printf("SD Card OK!\r\n");
}
switch(hsd1.SdCard.CardType){
case CARD_SDSC:
usart6.printf("SD CardType\t\t: CARD_SDSC\r\n");
break;
case CARD_SDHC_SDXC:
usart6.printf("SD CardType\t\t: CARD_SDHC_SDXC\r\n");
break;
case CARD_SECURED:
usart6.printf("SD CardType\t\t: CARD_SECURED\r\n");
break;
}
switch(hsd1.SdCard.CardVersion){
case CARD_V1_X:
usart6.printf("SD CardVersion\t: CARD_V1_X\r\n");
break;
case CARD_V2_X:
usart6.printf("SD SD CardVersion\t: CARD_V2_X\r\n");
break;
}
usart6.printf("SD CardCapacity\t\t: %dMB\r\n",(unsigned int)(hsd1.SdCard.BlockNbr * hsd1.SdCard.BlockSize) >> 20);
usart6.printf("SD BlockSize\t\t: %dByte\r\n",hsd1.SdCard.BlockSize);
usart6.printf("SD RelCardAdd\t\t: %d\r\n",hsd1.SdCard.RelCardAdd);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
LED_GREEN_ON;
HAL_Delay(500);
LED_GREEN_OFF;
HAL_Delay(500);
}
/* USER CODE END 3 */
}
2、SD卡初始化
void MX_SDMMC1_SD_Init(void)
{
hsd1.Instance = SDMMC1;
hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; //上升沿
hsd1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE;
//使用 bypass 模式,直接用 HCLK 进行分频得到 SDMMC_CK
hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE;
//空闲时不关闭时钟电源
hsd1.Init.BusWide = SDMMC_BUS_WIDE_1B; //1位数据线
hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE;
//关闭硬件流控
hsd1.Init.ClockDiv = 5; //SD传输时钟频率
if (HAL_SD_Init(&hsd1) != HAL_OK) //初始化SD卡
{
_Error_Handler(__FILE__, __LINE__);
}
HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B); //使能宽总线模式
}
3、HAL_SD_MspInit函数
void HAL_SD_MspInit(SD_HandleTypeDef* sdHandle)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(sdHandle->Instance==SDMMC1)
{
/* SDMMC1时钟使能 */
__HAL_RCC_SDMMC1_CLK_ENABLE();
/**SDMMC1 GPIO配置
PC8 ------> SDMMC1_D0
PC9 ------> SDMMC1_D1
PC10 ------> SDMMC1_D2
PC11 ------> SDMMC1_D3
PC12 ------> SDMMC1_CK
PD2 ------> SDMMC1_CMD
GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11
|GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
/* SDMMC1 DMA 初始化 */
/* SDMMC1 初始化 */
hdma_sdmmc1.Instance = DMA2_Stream3;
hdma_sdmmc1.Init.Channel = DMA_CHANNEL_4; //DMA通道
hdma_sdmmc1.Init.Direction =DMA_PERIPH_TO_MEMORY; //从外围设备传输到内存
hdma_sdmmc1.Init.PeriphInc = DMA_PINC_DISABLE; //不增加外设地址寄存器
hdma_sdmmc1.Init.MemInc = DMA_MINC_ENABLE; //增加内存地址寄存器
hdma_sdmmc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
//外围数据对齐:Word
hdma_sdmmc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
//内存数据对齐:Word
hdma_sdmmc1.Init.Mode = DMA_PFCTRL; //外围流量控制方式
hdma_sdmmc1.Init.Priority = DMA_PRIORITY_LOW; //优先等级:低
hdma_sdmmc1.Init.FIFOMode = DMA_FIFOMODE_ENABLE; //FIFO模式使能
hdma_sdmmc1.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
//FIFO阈值完整配置
hdma_sdmmc1.Init.MemBurst = DMA_MBURST_INC4;
hdma_sdmmc1.Init.PeriphBurst = DMA_PBURST_INC4;
if (HAL_DMA_Init(&hdma_sdmmc1) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
__HAL_LINKDMA(sdHandle,hdmarx,hdma_sdmmc1);
__HAL_LINKDMA(sdHandle,hdmatx,hdma_sdmmc1);
/* SDMMC1中断初始化*/
HAL_NVIC_SetPriority(SDMMC1_IRQn, 1, 0);//中断优先级
HAL_NVIC_EnableIRQ(SDMMC1_IRQn); //使能中断
}
}
4、SD卡信息结构体定义
typedef struct
{
uint32_t CardType; // 类型
uint32_t CardVersion; // 版本
uint32_t Class; // 卡的类
uint32_t RelCardAdd; // 相对卡地址
uint32_t BlockNbr; //以块为单位指定卡容量
uint32_t BlockSize; //块大小(以字节为单位)
uint32_t LogBlockNbr; //以块为单位指定卡逻辑容量
uint32_t LogBlockSize; //指定逻辑块大小(以字节为单位)
}HAL_SD_CardInfoTypeDef;
五、 实验步骤
把仿真器与iCore4的SWD调试口相连(直接相连或者通过转接器相连);
把iCore4通过Micro USB线与计算机相连,为iCore4供电;
打开PuTTY串口终端;
打开Keil MDK 开发环境,并打开本实验工程;
烧写程序到iCore4上;
也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。
六、 实验现象