[STM32F1] 用STM32F103完成对SD卡数的读写

[复制链接]
3233|33
 楼主| 结合国际经验 发表于 2023-10-19 12:36 | 显示全部楼层 |阅读模式
一、前言
由于咱们使用的是STM32F103C8T6的最小系统并没有SDIO口,所以想要外接存储设备对数据进行存储必须使用SPI对我们SD卡中的数据进行读写。

二、题目要求
掌握SD卡协议原理,用STM32F103完成对SD卡的数据读取(fat文件模式)。

三、SD卡协议了解
1、SDIO协议简介
SD卡(Secure Digital Memory Card)在我们的生活中已经非常普遍了,控制器对SD卡进行读写通信操作一般有两种通信接口可选,一种是 SPI接口,另外一种就是 SDIO接口。SDIO 全称是 安全数字输入/输出接口,多媒体卡(MMC)、SD卡、SD I/O卡 都有 SDIO接口。STM32F103系列控制器有一个 SDIO主机接口,它可以与 MMC卡、SD卡、SD I/O卡 以及 CE-ATA 设备进行数据传输。

 楼主| 结合国际经验 发表于 2023-10-19 12:36 | 显示全部楼层
2、SD卡物理结构
一般SD卡包括有存储单元、存储单元接口、电源检测、卡及接口控制器和接口驱动器 5个部分。

876946530b25650a34.png
 楼主| 结合国际经验 发表于 2023-10-19 12:36 | 显示全部楼层
存储单元是存储数据部件,存储单元通过存储单元接口与卡控制单元进行数据传输;
电源检测单元保证SD卡工作在合适的电压下,如出现掉电或上状态时,它会使控制单元和存储单元接口复位;
卡及接口控制单元控制SD卡的运行状态,它包括有8个寄存器;
接口驱动器控制SD卡引脚的输入输出。
SD卡总共有8个寄存器,用于设定或表示SD卡信息。
 楼主| 结合国际经验 发表于 2023-10-19 12:37 | 显示全部楼层
这些寄存器只能通过对应的命令访问,SDIO定义64个命令,每个命令都有特殊意义,可以实现某一特定功能,SD卡接收到命令后,根据命令要求对SD卡内部寄存器进行修改,程序控制中只需要发送组合命令就可以实现SD卡的控制以及读写操作。
 楼主| 结合国际经验 发表于 2023-10-19 12:38 | 显示全部楼层
3、SD卡寄存器列表
840106530b2a6b8aa8.png
 楼主| 结合国际经验 发表于 2023-10-19 12:38 | 显示全部楼层
4、SD卡初始化(SPI模式)
SPI操作模式下:在SD卡收到复位命令时,CS为有效电平(低电平),则SPI模式被启用,在发送CMD之前要先发送74个时钟,64个为内部供电上升时间,10个用于SD卡同步;之后才能开始CMD操作,在初始化时CLK时钟不能超过400KHz。

1、初始化与SD卡连接的硬件条件(MCU的SPI配置,IO口配置);
2、上电延时(>74个CLK);
3、复位卡(CMD0),进入IDLE状态;
4、发送CMD8,检查是否支持2.0协议;
5、根据不同协议检查SD卡(命令包括:CMD55、CMD41、CMD58和CMD1等);
6、取消片选,发多8个CLK,结束初始化
这样我们就完成了对SD卡的初始化,注意末尾发送的8个CLK是提供SD卡额外的时钟,完成某些操作。通过SD卡初始化,我们可以知道SD卡的类型(V1、V2、V2HC或者MMC),在完成了初始化之后,就可以开始读写数据了。
 楼主| 结合国际经验 发表于 2023-10-19 12:39 | 显示全部楼层
5、SD卡读写(SPI模式)
1、发送CMD17;
2、接收卡响应R1;
3、接收数据起始令牌0XFE;
4、接收数据;
5、接收2个字节的CRC,如果不使用CRC,这两个字节在读取后可以丢掉。
6、禁止片选之后,发多8个CLK;
以上就是一个典型的读取SD卡数据过程,SD卡的写于读数据差不多,写数据通过CMD24来实现,具体过程如下:
1、发送CMD24;
2、接收卡响应R1;
3、发送写数据起始令牌0XFE;
4、发送数据;
5、发送2字节的伪CRC;
6、禁止片选之后,发多8个CLK;
以上就是一个典型的写SD卡过程。
 楼主| 结合国际经验 发表于 2023-10-19 12:42 | 显示全部楼层
四、使用CubeMX创建工程
整体管脚配置预览

625086530b39a1ec04.png
 楼主| 结合国际经验 发表于 2023-10-19 12:43 | 显示全部楼层
先配置SYS

541916530b3d856801.png
 楼主| 结合国际经验 发表于 2023-10-19 12:43 | 显示全部楼层
配置PA4如图所示
770136530b3e8a259a.png
 楼主| 结合国际经验 发表于 2023-10-19 12:43 | 显示全部楼层
这里不改名也可以,改名只是为了方便我们**它

配置USART1
386666530b40466800.png
 楼主| 结合国际经验 发表于 2023-10-19 12:45 | 显示全部楼层
配置SPI1为全双工主模式

824806530b48353104.png
 楼主| 结合国际经验 发表于 2023-10-19 12:46 | 显示全部楼层
配置FATFS

362656530b4c041bac.png
 楼主| 结合国际经验 发表于 2023-10-19 12:47 | 显示全部楼层
工程配置

114726530b4d3bec34.png
 楼主| 结合国际经验 发表于 2023-10-19 12:47 | 显示全部楼层
注:这里一定要修改堆栈的大小,太小了会使我们无法读取SD卡的数据,直接导致我们的程序跑飞

现在生成代码即可。
 楼主| 结合国际经验 发表于 2023-10-19 12:47 | 显示全部楼层
五、程序的编写
首先点击下载我们需要的代码
提取码:1111
下载完以后需要将这两个.C和.H文件复制到自己的工程目录下

454126530b4fcd8f31.png
 楼主| 结合国际经验 发表于 2023-10-19 12:48 | 显示全部楼层
紧接着咱们打开自己的MDK在其中添加刚刚复制过来的两个文件
799996530b52f6f584.png
 楼主| 结合国际经验 发表于 2023-10-19 12:49 | 显示全部楼层
添加文件并修改接口里面的内容映射到SPI上,可以直接将以下代码复制粘贴过去


  1.   uint8_t res;
  2.         res = SD_init();//SD_Initialize()
  3.                          if(res)//STM32 SPI的bug,在sd卡操作失败的时候如果不执行下面的语句,可能导致SPI读写异常
  4.                         {
  5.                                 SPI_setspeed(SPI_BAUDRATEPRESCALER_256);
  6.                                 spi_readwrite(0xff);//提供额外的8个时钟
  7.                                 SPI_setspeed(SPI_BAUDRATEPRESCALER_2);
  8.                         }
  9.         if(res)return  STA_NOINIT;
  10.         else return RES_OK; //初始化成功
 楼主| 结合国际经验 发表于 2023-10-19 12:49 | 显示全部楼层
 楼主| 结合国际经验 发表于 2023-10-19 12:49 | 显示全部楼层
main.c:
  1. /* USER CODE BEGIN Header */
  2. /**
  3.   ******************************************************************************
  4.   * [url=home.php?mod=space&uid=288409]@file[/url]           : main.c
  5.   * [url=home.php?mod=space&uid=247401]@brief[/url]          : Main program body
  6.   ******************************************************************************
  7.   * @attention
  8.   *
  9.   * <h2><center>&copy; Copyright (c) 2019 STMicroelectronics.
  10.   * All rights reserved.</center></h2>
  11.   *
  12.   * This software component is licensed by ST under Ultimate Liberty license
  13.   * SLA0044, the "License"; You may not use this file except in compliance with
  14.   * the License. You may obtain a copy of the License at:
  15.   *                             www.st.com/SLA0044
  16.   *
  17.   ******************************************************************************
  18.   */
  19. /* USER CODE END Header */

  20. /* Includes ------------------------------------------------------------------*/
  21. #include "main.h"
  22. #include "fatfs.h"

  23. /* Private includes ----------------------------------------------------------*/
  24. /* USER CODE BEGIN Includes */
  25. #include "SDdriver.h"




  26. /* USER CODE END Includes */

  27. /* Private typedef -----------------------------------------------------------*/
  28. /* USER CODE BEGIN PTD */

  29. /* USER CODE END PTD */

  30. /* Private define ------------------------------------------------------------*/
  31. /* USER CODE BEGIN PD */

  32. /* USER CODE END PD */

  33. /* Private macro -------------------------------------------------------------*/
  34. /* USER CODE BEGIN PM */

  35. /* USER CODE END PM */

  36. /* Private variables ---------------------------------------------------------*/
  37. SPI_HandleTypeDef hspi1;

  38. UART_HandleTypeDef huart1;

  39. /* USER CODE BEGIN PV */

  40. /* USER CODE END PV */

  41. /* Private function prototypes -----------------------------------------------*/
  42. void SystemClock_Config(void);
  43. static void MX_GPIO_Init(void);
  44. static void MX_SPI1_Init(void);
  45. static void MX_USART1_UART_Init(void);
  46. /* USER CODE BEGIN PFP */

  47. /* USER CODE END PFP */

  48. /* Private user code ---------------------------------------------------------*/
  49. /* USER CODE BEGIN 0 */
  50. int fputc(int ch, FILE *f)   
  51. {
  52.     HAL_UART_Transmit(&huart1, (unsigned char *)&ch, 1, 0xFFFF);   
  53.     return ch;
  54. }
  55. uint16_t uart_value[3];
  56. uint8_t aRxBuffer1;        //uart rx buff





  57. void WritetoSD(BYTE write_buff[],uint8_t bufSize);
  58. char SD_FileName[] = "hello.txt";
  59. uint8_t WriteBuffer[] = "01 窝室嫩叠\r\n";

  60. //uint8_t test_sd =0;        //用于测试格式化
  61. uint8_t write_cnt =0;        //写SD卡次数




  62. void WritetoSD(BYTE write_buff[],uint8_t bufSize)
  63. {
  64.         FATFS fs;
  65.         FIL file;
  66.         uint8_t res=0;
  67.         UINT Bw;       
  68.        
  69.         res = SD_init();                //SD卡初始化
  70.        
  71.         if(res == 1)
  72.         {
  73.                 printf("SD卡初始化失败! \r\n");               
  74.         }
  75.         else
  76.         {
  77.                 printf("SD卡初始化成功! \r\n");               
  78.         }
  79.        
  80.         res=f_mount(&fs,"0:",1);                //挂载
  81.        
  82. //        if(test_sd == 0)                //用于测试格式化
  83.         if(res == FR_NO_FILESYSTEM)                //没有文件系统,格式化
  84.         {
  85. //                test_sd =1;                                //用于测试格式化
  86.                 printf("没有文件系统! \r\n");               
  87.                 res = f_mkfs("", 0, 0);                //格式化sd卡
  88.                 if(res == FR_OK)
  89.                 {
  90.                         printf("格式化成功! \r\n");               
  91.                         res = f_mount(NULL,"0:",1);                 //格式化后先取消挂载
  92.                         res = f_mount(&fs,"0:",1);                        //重新挂载       
  93.                         if(res == FR_OK)
  94.                         {
  95.                                 printf("SD卡已经成功挂载,可以进进行文件写入测试!\r\n");
  96.                         }       
  97.                 }
  98.                 else
  99.                 {
  100.                         printf("格式化失败! \r\n");               
  101.                 }
  102.         }
  103.         else if(res == FR_OK)
  104.         {
  105.                 printf("挂载成功! \r\n");               
  106.         }
  107.         else
  108.         {
  109.                 printf("挂载失败! \r\n");
  110.         }       
  111.        
  112.         res = f_open(&file,SD_FileName,FA_OPEN_ALWAYS |FA_WRITE);
  113.         if((res & FR_DENIED) == FR_DENIED)
  114.         {
  115.                 printf("卡存储已满,写入失败!\r\n");               
  116.         }
  117.        
  118.         f_lseek(&file, f_size(&file));//确保写词写入不会覆盖之前的数据
  119.         if(res == FR_OK)
  120.         {
  121.                 printf("打开成功/创建文件成功! \r\n");               
  122.                 res = f_write(&file,write_buff,bufSize,&Bw);                //写数据到SD卡
  123.                 if(res == FR_OK)
  124.                 {
  125.                         printf("文件写入成功! \r\n");                       
  126.                 }
  127.                 else
  128.                 {
  129.                         printf("文件写入失败! \r\n");
  130.                 }               
  131.         }
  132.         else
  133.         {
  134.                 printf("打开文件失败!\r\n");
  135.         }       
  136.        
  137.         f_close(&file);                                                //关闭文件               
  138.         f_mount(NULL,"0:",1);                 //取消挂载
  139.        
  140. }


  141. void Get_SDCard_Capacity(void)
  142. {
  143.         FRESULT result;
  144.         FATFS FS;
  145.         FATFS *fs;
  146.         DWORD fre_clust,AvailableSize,UsedSize;  
  147.         uint16_t TotalSpace;
  148.         uint8_t res;
  149.        
  150.         res = SD_init();                //SD卡初始化
  151.         if(res == 1)
  152.         {
  153.                 printf("SD卡初始化失败! \r\n");               
  154.         }
  155.         else
  156.         {
  157.                 printf("SD卡初始化成功! \r\n");               
  158.         }
  159.        
  160.         /* 挂载 */
  161.         res=f_mount(&FS,"0:",1);                //挂载
  162.         if (res != FR_OK)
  163.         {
  164.                 printf("FileSystem Mounted Failed (%d)\r\n", result);
  165.         }

  166.         res = f_getfree("0:", &fre_clust, &fs);  /* 根目录 */
  167.         if ( res == FR_OK )
  168.         {
  169.                 TotalSpace=(uint16_t)(((fs->n_fatent - 2) * fs->csize ) / 2 /1024);
  170.                 AvailableSize=(uint16_t)((fre_clust * fs->csize) / 2 /1024);
  171.                 UsedSize=TotalSpace-AvailableSize;              
  172.                 /* Print free space in unit of MB (assuming 512 bytes/sector) */
  173.                 printf("\r\n%d MB total drive space.\r\n""%d MB available.\r\n""%d MB  used.\r\n",TotalSpace, AvailableSize,UsedSize);
  174.         }
  175.         else
  176.         {
  177.                 printf("Get SDCard Capacity Failed (%d)\r\n", result);
  178.         }               
  179. }


  180. /* USER CODE END 0 */

  181. /**
  182.   * @brief  The application entry point.
  183.   * @retval int
  184.   */
  185. int main(void)
  186. {
  187.   /* USER CODE BEGIN 1 */

  188.   /* USER CODE END 1 */
  189.   

  190.   /* MCU Configuration--------------------------------------------------------*/

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

  193.   /* USER CODE BEGIN Init */

  194.   /* USER CODE END Init */

  195.   /* Configure the system clock */
  196.   SystemClock_Config();

  197.   /* USER CODE BEGIN SysInit */

  198.   /* USER CODE END SysInit */

  199.   /* Initialize all configured peripherals */
  200.   MX_GPIO_Init();
  201.   MX_SPI1_Init();
  202.   MX_FATFS_Init();
  203.   MX_USART1_UART_Init();
  204.   /* USER CODE BEGIN 2 */
  205.        
  206.         HAL_UART_Receive_IT(&huart1,&aRxBuffer1,1);         //enable uart       

  207.         printf(" main \r\n");

  208.         Get_SDCard_Capacity();        //得到使用内存并选择格式化



  209.   /* USER CODE END 2 */

  210.   /* Infinite loop */
  211.   /* USER CODE BEGIN WHILE */
  212.   while (1)
  213.   {
  214.                
  215.                
  216.                
  217.                 WritetoSD(WriteBuffer,sizeof(WriteBuffer));               

  218.                
  219.                
  220. //                HAL_Delay(500);
  221.                 WriteBuffer[0] = WriteBuffer[0] +0;
  222.                 WriteBuffer[1] = WriteBuffer[1] +1;
  223.                 write_cnt ++;
  224.                
  225.                 while(write_cnt > 5)
  226.                 {       
  227.                         printf(" while \r\n");
  228.                         HAL_Delay(500);
  229.                 }               
  230.                
  231.                
  232.                
  233.                
  234.                
  235.                
  236.                
  237.                
  238.                
  239.                
  240.     /* USER CODE END WHILE */

  241.     /* USER CODE BEGIN 3 */
  242.   }
  243.   /* USER CODE END 3 */
  244. }

  245. /**
  246.   * @brief System Clock Configuration
  247.   * @retval None
  248.   */
  249. void SystemClock_Config(void)
  250. {
  251.   RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  252.   RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  253.   /** Initializes the CPU, AHB and APB busses clocks
  254.   */
  255.   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  256.   RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  257.   RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
  258.   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  259.   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI_DIV2;
  260.   RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL16;
  261.   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  262.   {
  263.     Error_Handler();
  264.   }
  265.   /** Initializes the CPU, AHB and APB busses clocks
  266.   */
  267.   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
  268.                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  269.   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  270.   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2;
  271.   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  272.   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

  273.   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
  274.   {
  275.     Error_Handler();
  276.   }
  277. }

  278. /**
  279.   * @brief SPI1 Initialization Function
  280.   * @param None
  281.   * @retval None
  282.   */
  283. static void MX_SPI1_Init(void)
  284. {

  285.   /* USER CODE BEGIN SPI1_Init 0 */

  286.   /* USER CODE END SPI1_Init 0 */

  287.   /* USER CODE BEGIN SPI1_Init 1 */

  288.   /* USER CODE END SPI1_Init 1 */
  289.   /* SPI1 parameter configuration*/
  290.   hspi1.Instance = SPI1;
  291.   hspi1.Init.Mode = SPI_MODE_MASTER;
  292.   hspi1.Init.Direction = SPI_DIRECTION_2LINES;
  293.   hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
  294.   hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
  295.   hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
  296.   hspi1.Init.NSS = SPI_NSS_SOFT;
  297.   hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;
  298.   hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
  299.   hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
  300.   hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  301.   hspi1.Init.CRCPolynomial = 10;
  302.   if (HAL_SPI_Init(&hspi1) != HAL_OK)
  303.   {
  304.     Error_Handler();
  305.   }
  306.   /* USER CODE BEGIN SPI1_Init 2 */

  307.   /* USER CODE END SPI1_Init 2 */

  308. }

  309. /**
  310.   * @brief USART1 Initialization Function
  311.   * @param None
  312.   * @retval None
  313.   */
  314. static void MX_USART1_UART_Init(void)
  315. {

  316.   /* USER CODE BEGIN USART1_Init 0 */

  317.   /* USER CODE END USART1_Init 0 */

  318.   /* USER CODE BEGIN USART1_Init 1 */

  319.   /* USER CODE END USART1_Init 1 */
  320.   huart1.Instance = USART1;
  321.   huart1.Init.BaudRate = 115200;
  322.   huart1.Init.WordLength = UART_WORDLENGTH_8B;
  323.   huart1.Init.StopBits = UART_STOPBITS_1;
  324.   huart1.Init.Parity = UART_PARITY_NONE;
  325.   huart1.Init.Mode = UART_MODE_TX_RX;
  326.   huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  327.   huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  328.   if (HAL_UART_Init(&huart1) != HAL_OK)
  329.   {
  330.     Error_Handler();
  331.   }
  332.   /* USER CODE BEGIN USART1_Init 2 */

  333.   /* USER CODE END USART1_Init 2 */

  334. }

  335. /**
  336.   * @brief GPIO Initialization Function
  337.   * @param None
  338.   * @retval None
  339.   */
  340. static void MX_GPIO_Init(void)
  341. {
  342.   GPIO_InitTypeDef GPIO_InitStruct = {0};

  343.   /* GPIO Ports Clock Enable */
  344.   __HAL_RCC_GPIOA_CLK_ENABLE();

  345.   /*Configure GPIO pin Output Level */
  346.   HAL_GPIO_WritePin(SD_CS_GPIO_Port, SD_CS_Pin, GPIO_PIN_RESET);

  347.   /*Configure GPIO pin : SD_CS_Pin */
  348.   GPIO_InitStruct.Pin = SD_CS_Pin;
  349.   GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  350.   GPIO_InitStruct.Pull = GPIO_NOPULL;
  351.   GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  352.   HAL_GPIO_Init(SD_CS_GPIO_Port, &GPIO_InitStruct);

  353. }

  354. /* USER CODE BEGIN 4 */

  355. /* USER CODE END 4 */

  356. /**
  357.   * @brief  This function is executed in case of error occurrence.
  358.   * @retval None
  359.   */
  360. void Error_Handler(void)
  361. {
  362.   /* USER CODE BEGIN Error_Handler_Debug */
  363.   /* User can add his own implementation to report the HAL error return state */

  364.   /* USER CODE END Error_Handler_Debug */
  365. }

  366. #ifdef  USE_FULL_ASSERT
  367. /**
  368.   * @brief  Reports the name of the source file and the source line number
  369.   *         where the assert_param error has occurred.
  370.   * @param  file: pointer to the source file name
  371.   * @param  line: assert_param error line source number
  372.   * @retval None
  373.   */
  374. void assert_failed(uint8_t *file, uint32_t line)
  375. {
  376.   /* USER CODE BEGIN 6 */
  377.   /* User can add his own implementation to report the file name and line number,
  378.      tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  379.   /* USER CODE END 6 */
  380. }
  381. #endif /* USE_FULL_ASSERT */

  382. /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

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

本版积分规则

66

主题

775

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部