找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 18412|回复: 8
打印 上一主题 下一主题
收起左侧

stm32 SDIO读取SD卡扇区,使用HAL库

[复制链接]
跳转到指定楼层
楼主
【猪圈丶嗨情歌的开发分享】
今天给大家带来的是我这几天学习的SDIO,用STM32系列芯片的SDIO读取SD卡的扇区。本人原创,这里转贴到51黑。

我使用的工具
开发平台:正点原子探索者STM32F407开发板
硬件:使用了NUCLEO-F446RE开发板的ST-Link作为调试器、SD卡、数据线、开发板的电源适配器、DELL一体机
软件:STM32CubeMX、Keil V5、串口助手

学习的知识点
1、使用STM32CubeMX配置SDIO
2、在Keil中初始化SDIO
3、读取SD卡状态、卡信息
4、sprintf函数的使用

共享的资源
完整的工程文件
SDIO.zip (9.16 MB, 下载次数: 160)

写在前面的话
上次STM32F469I开发板申请的活动吃了不少苦头啊,手里没有什么精华帖,只能跑去申请小鲜肉分块的开发板了。结果这个活动异常火爆,最后1000+的人参与。这次决心做好开发分享系列的帖子,把自己平时学习STM32开发中的干货写成帖子在论坛里分享给大家。希望支持的坛友多多回复,给我顶一个人气。


准备工作
本帖不解决硬件连接问题,如果硬件方面有问题最好自行搜索相关资料。
SD卡的通信方式有的是用SPI,我们这里是SDIO,速度更快更好用。如果你的SD卡模块有MOSI、MISO这些字样,那说明这些是SPI读写用的模块,不适合这篇帖子的分享内容。如果使用的是现成的开发板,请参考开发板的手册确定SD卡的通信方式。
如果你确定了SD卡的通信方式为SDIO无误并且正确连接了SD卡和芯片,然后记录下了SD卡的引脚和芯片的连接方式那么准备工作就基本完成了。

STM32CubeMX中配置SDIO

我们来看看左边的红框,在外设里面找到SDIO、SYS、USART1(看自己的开发板来选择。还有我忘了打开这个分支,USART1我选的是第一个选项)。在下拉框里面选择和上图一样的选项,这样可以保证后面的项目一致。
我们在左边选择好了以后,可以看到右边的芯片引脚有一些是绿色的,就说明这些引脚有配置被激活了。



我们现在来玩一玩这个时钟的配置窗口,这个在Pinout标签页旁边大家自行点开。
那么时钟怎么配置呢?我一般是主频给我来最大,只要保证这里的输入框都是蓝色的就行了,如果是红色的就说明频率高了这个就要自己调整一下了。
剩下的内容就已经确定了,不需要我们来配置。如果你自己有什么别的想法的话,就自己动手来配置一下。

在Keil中初始化SDIO

我们现在要初始化SDIO来驱动我们的SD卡工作,现在贴出我在main函数中的初始化代码。下面的代码都是Cube自动生成的,这些代码就可以初始化我们的SDIO了。
  1.     /* Initialize all configured peripherals */
  2.       MX_GPIO_Init();
  3.       MX_SDIO_SD_Init();
  4.       MX_USART1_UART_Init();
复制代码

添加一些必要的初始化代码(12月14号追加)

为了保证SDIO正常工作,我们还要添加两个函数来初始化。这两个函数Cube没有事先给我们调用,我们要自己动一次手。在工程的文件树Application/User里面找到sdio.c文件,找到第一个void MX_SDIO_SD_Init(void)函数。我们把我们需要的初始化代码贴进去。
加入的这两行函数才是真正的初始化了SDIO,前面都是在配置一个结构体变量。最后的两行代码需要我们自己添加进来,这两个函数的定义大家可以自行查询。这两行代码没有写在用户代码保护模块里面,所以下一次STM32CubeMX生成代码的时候这里的代码会被删除,请悉知。
  1.     void MX_SDIO_SD_Init(void)
  2.     {

  3.       hsd.Instance = SDIO;
  4.       hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
  5.       hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
  6.       hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
  7.       hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
  8.       hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  9.       hsd.Init.ClockDiv = 0;

  10.             HAL_SD_Init(&hsd, &SDCardInfo);
  11.       HAL_SD_WideBusOperation_Config(&hsd, SDIO_BUS_WIDE_4B);
  12.     }
复制代码


下面我贴出我在while循环里面的代码,分析一下这些代码。
首先我是用了HAL_SD_GetStatus函数来读取SD卡的状态,这些状态只有SD_TRANSFER_OK、SD_TRANSFER_BUSY、SD_TRANSFER_ERROR这三种。
  1. State = HAL_SD_GetStatus(&hsd);
复制代码

接下来的条件语句里面,我用几个if来决定不同的状态应该做些什么。
1、现在SD是OK的状态,我们可以对SD卡进行读写操作
在读取扇区的数据之前,发送一个SD Card OK的消息。然后调用HAL_SD_ReadBlocks函数来读取扇区的内容。这个函数的第四个参数必须是512,因为目前还不支持其他的扇区大小。


该函数的返回值表明了读取操作的是否成功,如果为0表示成功,那么我们就发送一个Sector read is OK的消息。如果没有成功,则发送一个ERROR的消息同时用break来终止此次循环。


在接下来的for循环里面,我们把读取的32位扇区信息转化成8位的扇区信息并且使用sprintf函数格式化成字符串。
关于32位转化为8位这里不做过多的赘述,主要是C语言数据类型转换的时候丢失一部分信息和位运算的知识,大家自己看一下。

sprintf函数的使用
这里要特别提一下这个函数,这是C标准库中的一个函数,在我们的STM32平台上也有实现。这个函数可以把数据格式化为字符串,把数据作为字符的形式显示出来非常的实用。
想了解详细的函数使用方法,请自行百度一下。这里简单的介绍一下。
该函数的第一个参数是我们要转换的内容存储的指针,转换好的字符串存储在这个指针指向的空间里。第二个字符串就是格式化字符串,这个字符串里面要包含转换字符。我在代码里面使用的是%X这个转换字符,可以把数据用大写的16进制显示。后面的参数是和前面的转换字符一一对应的待转换变量,直接传递参数就可以了,不需要指针。


转换好了字符串之后,我就可以挨个发送这些字符。发送完之后就循环一直等待就行了,不用反复读取。
  • 原型
  • int sprintf( char *buffer, const char *format, [ argument] … );
  • 参数列表
  • buffer:char型指针,指向将要写入的字符串的缓冲区。
  • format:格式化字符串。
  • [argument]...:可选参数,可以是任何类型的数据。
  • 返回值:字符串长度(strlen)

  1.     if( State == 0)
  2.                     {
  3.                             HAL_UART_Transmit(&huart1, (uint8_t *)"SD Card OK\n", 11, 500);                                             
  4.                             if(HAL_SD_ReadBlocks(&hsd, pReadBuffer, 0x00000000, 512, 1) == 0)
  5.                             HAL_UART_Transmit(&huart1, (uint8_t *)"Sector read is OK\n", 18, 500);
  6.                             else
  7.                             {
  8.                                     HAL_UART_Transmit(&huart1, (uint8_t *)"ERROR\n", 6, 500);
  9.                                     break;
  10.                             }
  11.                   
  12.                             for(int i = 0;i < 128;i++)
  13.                             {
  14.                                     for(int j = 0;j < 4;j++)
  15.                                     {
  16.                                             SendBuffer = pReadBuffer[i];
  17.                                             pReadBuffer[i] >>= 8;
  18.                                             sprintf((char*)&Char, "%X", SendBuffer);
  19.                                             HAL_UART_Transmit(&huart1, &Char, 1, 500);
  20.                                     }
  21.                            
  22.                             }
  23.                             while(1);
  24.                     }
复制代码
2、SD卡处于繁忙的状态
这个时候我们就发送一个Busy的消息就可以了,等待1S钟进行下一次SD卡状态的读取。
  1.     else if(State == 1)
  2.                     {
  3.                             HAL_UART_Transmit(&huart1, (uint8_t *)"Busy\n", 5, 500);        
  4.                     }
复制代码
3、SD卡状态读取错误
SD状态返回为错误,这基本上就是SD卡没有插入,要么就是卡坏掉了。这个时候如果我们不进行任何操作就进行下一次判断的话,就算是在插入SD卡不管怎么读取都是返回ERROR。所以我们在读取到这个状态的时候,调用一次HAL_SD_Init(&hsd, &SDCardInfo);这个函数来清除之前的状态(这个是我自己想的,不知道有没有卵用)。这样子操作的话,在下一次插入SD卡的时候就可以返回为OK的值并且可以读写。

最后用了一个延时函数来限制扫描SD卡的速度。
  1.     else {
  2.                             HAL_UART_Transmit(&huart1, (uint8_t *)"Error\n", 6, 500);
  3.                             HAL_SD_Init(&hsd, &SDCardInfo);
  4.                     }
  5.             
  6.                     HAL_Delay(1000);
复制代码

实际工作效果


我预先拔掉了SD卡,过了一会我在插上去,所以在串口上看到的就是这样的效果。


  1. /**
  2.   ******************************************************************************
  3.   * File Name          : main.c
  4.   * Description        : Main program body
  5.   ******************************************************************************
  6.   *
  7.   * COPYRIGHT(c) 2015 STMicroelectronics
  8.   *
  9.   * Redistribution and use in source and binary forms, with or without modification,
  10.   * are permitted provided that the following conditions are met:
  11.   *   1. Redistributions of source code must retain the above copyright notice,
  12.   *      this list of conditions and the following disclaimer.
  13.   *   2. Redistributions in binary form must reproduce the above copyright notice,
  14.   *      this list of conditions and the following disclaimer in the documentation
  15.   *      and/or other materials provided with the distribution.
  16.   *   3. Neither the name of STMicroelectronics nor the names of its contributors
  17.   *      may be used to endorse or promote products derived from this software
  18.   *      without specific prior written permission.
  19.   *
  20.   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21.   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22.   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23.   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  24.   * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25.   * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  26.   * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  27.   * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28.   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29.   * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30.   *
  31.   ******************************************************************************
  32.   */
  33. /* Includes ------------------------------------------------------------------*/
  34. #include "stm32f4xx_hal.h"
  35. #include "sdio.h"
  36. #include "usart.h"
  37. #include "gpio.h"

  38. /* USER CODE BEGIN Includes */

  39. /* USER CODE END Includes */

  40. /* Private variables ---------------------------------------------------------*/

  41. /* USER CODE BEGIN PV */
  42. /* Private variables ---------------------------------------------------------*/

  43. /* USER CODE END PV */

  44. /* Private function prototypes -----------------------------------------------*/
  45. void SystemClock_Config(void);

  46. /* USER CODE BEGIN PFP */
  47. /* Private function prototypes -----------------------------------------------*/

  48. /* USER CODE END PFP */

  49. /* USER CODE BEGIN 0 */

  50. /* USER CODE END 0 */

  51. int main(void)
  52. {

  53.   /* USER CODE BEGIN 1 */
  54.         uint8_t State = 0;
  55.         uint32_t pReadBuffer[128] = { 0 };
  56.         uint8_t SendBuffer = { 0 };
  57.         uint8_t Char = 0;
  58.   /* USER CODE END 1 */

  59.   /* MCU Configuration----------------------------------------------------------*/

  60.   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  61.   HAL_Init();

  62.   /* Configure the system clock */
  63.   SystemClock_Config();

  64.   /* Initialize all configured peripherals */
  65.   MX_GPIO_Init();
  66.   MX_SDIO_SD_Init();
  67.   MX_USART1_UART_Init();

  68.   /* USER CODE BEGIN 2 */
  69.         HAL_UART_Transmit(&huart1, (uint8_t *)"Initializing is OK\n", 19, 500);
  70.         
  71.   /* USER CODE END 2 */

  72.   /* Infinite loop */
  73.   /* USER CODE BEGIN WHILE */
  74.   while (1)
  75.   {
  76.   /* USER CODE END WHILE */

  77.   /* USER CODE BEGIN 3 */
  78.                 State = HAL_SD_GetStatus(&hsd);
  79.                
  80.                 if( State == 0)
  81.                 {
  82.                         HAL_UART_Transmit(&huart1, (uint8_t *)"SD Card OK\n", 11, 500);                                                //在这里我们读取SD卡的信息
  83.                         if(HAL_SD_ReadBlocks(&hsd, pReadBuffer, 0x00000000, 512, 1) == 0)
  84.                         HAL_UART_Transmit(&huart1, (uint8_t *)"Sector read is OK\n", 18, 500);
  85.                         else
  86.                         {
  87.                                 HAL_UART_Transmit(&huart1, (uint8_t *)"ERROR\n", 6, 500);
  88.                                 break;
  89.                         }
  90.                
  91.                         for(int i = 0;i < 128;i++)
  92.                         {
  93.                                 for(int j = 0;j < 4;j++)
  94.                                 {
  95.                                         SendBuffer = pReadBuffer[i];
  96.                                         pReadBuffer[i] >>= 8;
  97.                                         sprintf((char*)&Char, "%X", SendBuffer);
  98.                                         HAL_UART_Transmit(&huart1, &Char, 1, 500);
  99.                                 }
  100.                         
  101.                         }
  102.                         while(1);
  103.                 }
  104.                 else if(State == 1)
  105.                 {
  106.                         HAL_UART_Transmit(&huart1, (uint8_t *)"Busy\n", 5, 500);        
  107.                 }
  108.                 else {
  109.                         HAL_UART_Transmit(&huart1, (uint8_t *)"Error\n", 6, 500);
  110.                         HAL_SD_Init(&hsd, &SDCardInfo);
  111.                 }
  112.         
  113.                 HAL_Delay(1000);
  114.   }
  115.   /* USER CODE END 3 */

  116. }

  117. /** System Clock Configuration
  118. */
  119. void SystemClock_Config(void)
  120. {

  121.   RCC_OscInitTypeDef RCC_OscInitStruct;
  122.   RCC_ClkInitTypeDef RCC_ClkInitStruct;

  123.   __PWR_CLK_ENABLE();

  124.   __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  125.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  126.   RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  127.   RCC_OscInitStruct.HSICalibrationValue = 16;
  128.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  129.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  130.   RCC_OscInitStruct.PLL.PLLM = 8;
  131.   RCC_OscInitStruct.PLL.PLLN = 144;
  132.   RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  133.   RCC_OscInitStruct.PLL.PLLQ = 6;
  134.   HAL_RCC_OscConfig(&RCC_OscInitStruct);

  135.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1
  136.                               |RCC_CLOCKTYPE_PCLK2;
  137.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  138.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  139.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  140.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
  141.   HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4);

  142.   HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);

  143.   HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);

  144.   /* SysTick_IRQn interrupt configuration */
  145.   HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
  146. }

  147. /* USER CODE BEGIN 4 */

  148. /* USER CODE END 4 */

  149. #ifdef USE_FULL_ASSERT

  150. /**
  151.    * @brief Reports the name of the source file and the source line number
  152.    * where the assert_param error has occurred.
  153.    * @param file: pointer to the source file name
  154.    * @param line: assert_param error line source number
  155.    * @retval None
  156.    */
  157. void assert_failed(uint8_t* file, uint32_t line)
  158. {
  159.   /* USER CODE BEGIN 6 */
  160.   /* User can add his own implementation to report the file name and line number,
  161.     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  162.   /* USER CODE END 6 */

  163. }

  164. #endif

  165. /**
  166.   * @}
  167.   */

  168. /**
  169.   * @}
  170. */

  171. /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
复制代码



写在后面的话
注意!注意!注意!本人不是什么工程师,只不过是爱好嵌入式开发的学生一枚,如果你发现在这个帖子中的错误请及时提醒我。如果对本帖的内容有什么疑问请在下方留言,我会经常过来逛论坛的。

下一次给大家带来的可能就是FatFs了,这样我们就可以读写文件了。

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:135931 发表于 2016-8-3 23:33 | 只看该作者
可以用么
回复

使用道具 举报

板凳
ID:124289 发表于 2016-8-4 13:55 | 只看该作者
瞧瞧看,学习学习
回复

使用道具 举报

地板
ID:95511 发表于 2017-3-10 15:50 | 只看该作者
正好用下来看看 不过连续写 总是死机呢
回复

使用道具 举报

5#
ID:93398 发表于 2017-6-17 10:00 | 只看该作者
正好需要 下载看看
回复

使用道具 举报

6#
ID:185053 发表于 2017-12-9 12:18 | 只看该作者
学习学习
回复

使用道具 举报

7#
ID:387979 发表于 2018-8-20 01:05 | 只看该作者
看看线
回复

使用道具 举报

8#
ID:355702 发表于 2019-9-6 14:57 | 只看该作者
这个程序貌似用不了额。。。初始化的时候就卡死了
回复

使用道具 举报

9#
ID:661945 发表于 2022-1-4 09:32 | 只看该作者
最近也在用到SD卡读写
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表