用户工具

站点工具


sdio实验_读取sd卡信息
银杏科技有限公司旗下技术文档发布平台
技术支持电话0379-69926675-801
技术支持邮件Gingko@vip.163.com
版本 日期 作者 修改内容
V1.0 2020-07-04 gingko 初次建立

实验十三:SDIO实验——读取SD卡信息

一、 实验目的与意义

  1. 了解STM32 SDIO结构。
  2. 了解STM32 SDIO特征。
  3. 掌握SDIO的使用方法。
  4. 掌握STM32 HAL库中SDIO属性的配置方法。
  5. 掌握KEIL MDK集成开发环境使用方法。

二、 实验设备及平台

  1. iCore4 双核心板点击购买
  2. JLINK(或相同功能)仿真器点击购买
  3. Micro USB线缆。
  4. Keil MDK 开发平台。
  5. STM32CubeMX开发平台。
  6. 装有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具有以下特性:
    • 完全兼容多媒体卡系统规范版本4.2。卡支持三种不同数据总线模式:1位(默认)4位和8位
    • 完全兼容先前版本的多媒体卡(向前兼容性)
    • 完全兼容SD存储卡规范版本2.0
    • 完全兼容SDI/O卡规范版本2.0:卡支持两种不同的数据总线模式:1位(默认)和4位
    • 对于8位模式,数据传输高达48MHz
    • 数据和命令输出使能信号,控制外部双向驱动程序
  • 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框图

  • SDMMC由两部分组成:
    • (1)SDMMC适配器块提供特定于MMC/SD/SDI/O卡的所有功能,如时钟生成单元、命令和数据传输。
    • (2)APB2接口访问SDMMC适配器寄存器,并且生成中断和DMA请求信号。
  • 默认情况下,SDMMC_D0用于数据传输。初始化后,主机可以更改数据总线宽度。如果多媒体卡连接到总线,则SDMMC_D0、SDMMC_D[3:0]或SDMMC_D[7:0]可以用于数据传输。MMCV3.31或更低版本仅支持1位数据,因此只能使用SDMMC_D0。如果SD或SDI/O卡连接到总线,则主机可以将数据传输配置为使用SDMMC_D0或SDMMC_D[3:0]。所有数据线均以推挽模式运行。

5、原理图

  • STM32F767上带有SDIO控制器,iCore4核心板上将SDIO接到TF卡座上。本实验将Micro SD卡插入卡座上即可。实验原理图如下:

四、 实验程序

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); //使能宽总线模式
}
  • 对于该函数,主要调用函数HAL_SD_Init进行SD卡初始化流程并获取卡信息,HAL_SD_Init函数内部先通过调用HAL库静态函数SD_Initialize_Cards来发送CMD2和CMD3,获得CID寄存器内容和SD卡的相对地址(RCA),并通过CMD9,获取CSD寄存器内容,完成SD卡的初始化流程。然后调用HAL_SD_Get_CardInfo函数来获取卡信息保存在入口参数SDCardInfo中。

3、HAL_SD_MspInit函数

  • HAL_SD_MspInit函数,该函数主要由三个作用:第一是使能相应时钟,第二是初始化 SDMMC 相关 IO 口模式和映射,第三是在DMA模式下初始化 DMA 配置以及设置 NVIC。
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;
  • 主函数中就是使用此结构体来获取SD卡的信息,并将其打印在串口终端上。

五、 实验步骤

  1. 把仿真器与iCore4的SWD调试口相连(直接相连或者通过转接器相连);
  2. 把iCore4通过Micro USB线与计算机相连,为iCore4供电;
  3. 打开PuTTY串口终端;
  4. 打开Keil MDK 开发环境,并打开本实验工程;
  5. 烧写程序到iCore4上;
  6. 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。

六、 实验现象

  • 在终端显示屏上可以看到Micro SD卡的信息,如下图:

sdio实验_读取sd卡信息.txt · 最后更改: 2022/03/22 10:19 由 sean