用户工具

站点工具


sdio实验_读取sd卡信息

差别

这里会显示出您选择的修订版和当前版本之间的差别。

到此差别页面的链接

两侧同时换到之前的修订记录 前一修订版
后一修订版
前一修订版
sdio实验_读取sd卡信息 [2019/12/21 11:01]
zhangzheng
sdio实验_读取sd卡信息 [2022/03/22 10:19] (当前版本)
sean
行 1: 行 1:
-[[http://www.cnblogs.com/xiaomagee/p/7427731.html]]+ 
 +|  **银杏科技有限公司旗下技术文档发布平台** ​ |||| 
 +|技术支持电话|**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集成开发环境使用方法。 
 +==== 二、 实验设备及平台 ==== 
 +  - iCore4 双核心板[[https://item.taobao.com/item.htm?​spm=a1z10.1-c-s.w4004-22598974120.15.5923532fsFrHiE&​id=551864196684|点击购买]]。 
 +  - JLINK(或相同功能)仿真器[[https:​//item.taobao.com/​item.htm?​id=554869837940|点击购买]]。 
 +  - 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具有以下特性: 
 +    * 完全兼容多媒体卡系统规范版本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框图 === 
 +{{ :​icore4:​icore4_arm_hal_13_1.png?​direct |}} 
 +  * 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卡插入卡座上即可。实验原理图如下: 
 +{{ :​icore4:​icore4_arm_hal_13_2.png?​direct |}} 
 +==== 四、 实验程序 ==== 
 + 
 +=== 1、主函数 === 
 +<code c> 
 +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 */ 
 + 
 +
 + 
 + 
 +</​code>​  
 +=== 2、SD卡初始化 === 
 +<code c> 
 +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);​ //​使能宽总线模式 
 +
 + 
 +</​code>​ 
 +  * 对于该函数,主要调用函数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。 
 +<code c> 
 +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);​ //​使能中断 
 +  } 
 +
 +</​code>​ 
 +=== 4、SD卡信息结构体定义 === 
 +<code c> 
 +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;​ 
 +</​code>​ 
 +  * 主函数中就是使用此结构体来获取SD卡的信息,并将其打印在串口终端上。 
 +==== 五、 实验步骤 ==== 
 +  - 把仿真器与iCore4的SWD调试口相连(直接相连或者通过转接器相连); 
 +  - 把iCore4通过Micro USB线与计算机相连,为iCore4供电; 
 +  - 打开PuTTY串口终端; 
 +  - 打开Keil MDK 开发环境,并打开本实验工程; 
 +  - 烧写程序到iCore4上; 
 +  - 也可以进入Debug 模式,单步运行或设置断点验证程序逻辑。 
 +==== 六、 实验现象 ==== 
 +  * 在终端显示屏上可以看到Micro SD卡的信息,如下图:  
 +{{ :​icore4:​icore4_arm_hal_13_3.png?​direct |}}
sdio实验_读取sd卡信息.1576897313.txt.gz · 最后更改: 2019/12/21 11:01 由 zhangzheng