|   银杏科技有限公司旗下技术文档发布平台   | 
	
	
		| 技术支持电话 | 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 模式,单步运行或设置断点验证程序逻辑。
 
 
六、 实验现象