STM32F1与STM32CubeIDE快速入门-SD卡驱动-SDIO+FatFs

SD卡驱动-SDIO+FatFs

文章目录

1、SDIO与FatFs简单介绍

安全数字输入/输出 (Secure Digital Input/Output,SDIO) 是可互换存储器的标准设备。使用 SDIO 卡,用户可以选择在便携式或非便携式存储器之间交换数据。通常,SDIO 卡就像小型计算机或功能强大的软件应用程序,主要用于智能手机、蓝牙适配器、全球定位系统 (GPS) 接收器、电视调谐器、相机、扫描仪、录音机,甚至指纹读取器。 SDIO是SD卡的高级设备。可以说SDIO是SD卡和I/O输出设备的混合体。

内存可以存储在各种设备上,如便携式和非便携式、外部和内部等。SDIO 是其中一种形式。它是一种较小的安全数字设备,可插入智能手机、笔记本电脑、移动电话等电子便携设备的“插槽”中。 SDIO 卡具有广泛的存储容量,并具有双向传输数据的能力。每张 SDIO 卡都为完美的功能而设计。

FatFs 是用于小型嵌入式系统的通用 FAT/exFAT 文件系统模块。 FatFs 模块是按照 ANSI C (C89) 编写的,与磁盘 I/O 层完全分离。 因此,它独立于平台。 它可以集成到资源有限的小型微控制器中,例如8051、PIC、AVR、ARM、Z80、RX等。

2、SDIO与FatFs配置

STM32CubeIDE创建工程、系统配置、调试配置,在这里不再做介绍,请参考:

1)SDIO配置

在这里插入图片描述

SDIO工作模式:

  • SD 1 bit
  • SD 4 bits Wide bus
  • MMC 1 bit
  • MMC 4 bits Wide bus
  • MMC 8 bits Wide bus

不同版本MultiMediaCard协议,SDIO的时钟频率不同:

  • MultiMediaCard v3.31:0 到 20 MHz 之间
  • MultiMediaCard v4.0/4.2:0 到 48 MHz 之间
  • 对于SD/SD I/O 卡:0 到 25 MHz 之间

SDIO 使用两个时钟信号:

  • SDIO 适配器时钟(SDIOCLK = 48MHz 来自 PLL (PLL48CLK) 的特定输出)
  • APB2 总线时钟(PCLK2)

PCLK2 与 SDIO_CLK 之 clock 频率必须遵循此条件:Frequency PCLK2 ≥ (3/8) * Frequency SDIO_CK

在上面的配置中,SDIOCLK clock divide factor的值不同的SDK卡可能不同,需要尝试。

注意:如果在SD挂载成功后,文件读写不成功,可能需要将 SDIO hardware flow control配置项开启。本次使用的SD卡就遇到此情况

2)SDIO开启中断与DMA功能配置

在这里插入图片描述

在这里插入图片描述

注意,SDIO的中断优先级必须比DMA的中断优先级高

3)FatFs配置

在这里插入图片描述

检测引脚配置

在这里插入图片描述

如果硬件配置了SDIO检测引脚,需要指定并配置为上拉模式;如果没有,则不需要配置。本次实例使用的开板没有配置SDIO检测引脚。

4)配置堆栈

如果系统堆栈配置过小,会导致FatFs初始化不成功,因此需要更改堆栈大小。配置如下:

在这里插入图片描述

最后,保存配置并生成代码。

3、SDIO与FatFs功能测试

main.c文件中实现SDIO与FatFs功能测试,添加代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
/\* USER CODE BEGIN Header \*/
/\*\*
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
\* @file : main.c
\* @brief : Main program body
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
\* @attention
\*
\* Copyright (c) 2022 STMicroelectronics.
\* All rights reserved.
\*
\* This software is licensed under terms that can be found in the LICENSE file
\* in the root directory of this software component.
\* If no LICENSE file comes with this software, it is provided AS-IS.
\*
\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*
\*/
/\* USER CODE END Header \*/
/\* Includes ------------------------------------------------------------------\*/
#include "main.h"
#include "dma.h"
#include "fatfs.h"
#include "sdio.h"
#include "usart.h"
#include "gpio.h"

/\* Private includes ----------------------------------------------------------\*/
/\* USER CODE BEGIN Includes \*/
#include <stdio.h>
/\* USER CODE END Includes \*/

/\* Private typedef -----------------------------------------------------------\*/
/\* USER CODE BEGIN PTD \*/

/\* USER CODE END PTD \*/

/\* Private define ------------------------------------------------------------\*/
/\* USER CODE BEGIN PD \*/
/\* USER CODE END PD \*/

/\* Private macro -------------------------------------------------------------\*/
/\* USER CODE BEGIN PM \*/

/\* USER CODE END PM \*/

/\* Private variables ---------------------------------------------------------\*/

/\* USER CODE BEGIN PV \*/
FATFS fs; /\* FatFs 文件系统对象 \*/
FIL file; /\* 文件对象 \*/
FRESULT f_res; /\* 文件操作结果 \*/
UINT fnum; /\* 文件成功读写数量 \*/
BYTE ReadBuffer[1024] = { 0 }; /\* 读缓冲区 \*/
BYTE WriteBuffer[] = /\* 写缓冲区 \*/
"STM32F103 & STM32CubeIDE SDIO Demo!\r\n";

/\* USER CODE END PV \*/

/\* Private function prototypes -----------------------------------------------\*/
void SystemClock\_Config(void);
/\* USER CODE BEGIN PFP \*/

/\* USER CODE END PFP \*/

/\* Private user code ---------------------------------------------------------\*/
/\* USER CODE BEGIN 0 \*/

/\* USER CODE END 0 \*/

/\*\*
\* @brief The application entry point.
\* @retval int
\*/
int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_SDIO\_SD\_Init();
MX\_DMA\_Init();
MX\_USART1\_UART\_Init();
MX\_FATFS\_Init();
/\* USER CODE BEGIN 2 \*/
printf("\r\n\*\*\*\*\*\* FatFs Example \*\*\*\*\*\*\r\n\r\n");

//挂载文件系统
f_res = f\_mount(&fs, "/", 1);

printf("\r\n\*\*\*\*\*\* Mount FatFs File System \*\*\*\*\*\*\r\n");
// 检查是否SD卡是否已经格式化
if (f_res == FR_NO_FILESYSTEM) {
printf(
"The SD card does not yet have a file system and is about to be formatted...\r\n");
/\* 格式化SD卡 \*/
f_res = f\_mkfs("/", 0, 0);
if (f_res == FR_OK) {
printf("The SD card successfully formatted the file system\r\n");
/\* 格式化后,先取消挂载 \*/
f_res = f\_mount(NULL, "/", 1);
/\* 重新挂载 \*/
f_res = f\_mount(&fs, "/", 1);
} else {
printf("The format failed\r\n");
while (1)
;
}
} else if (f_res != FR_OK) {
printf(" mount error : %d \r\n", f_res);
while (1)
;
} else {
printf(" mount sucess!!! \r\n");
}

printf("\r\n\*\*\*\*\*\* FatFs File Operation \*\*\*\*\*\*\r\n");
//打开并创建文件(不存在时)
f_res = f\_open(&file, "/test.txt", FA_CREATE_ALWAYS | FA_WRITE);
if (f_res == FR_OK) {
printf(" open file sucess!!! \r\n");
// 将数据写入文件
printf("\r\n\*\*\*\*\*\* Write data to the text files \*\*\*\*\*\*\r\n");
f_res = f\_write(&file, WriteBuffer, sizeof(WriteBuffer), &fnum);
if (f_res == FR_OK) {
printf(" write file sucess!!! (%d)\n", fnum);
printf(" write Data : %s\r\n", WriteBuffer);
} else {
printf(" write file error : %d\r\n", f_res);
}
/\*关闭文件\*/
f\_close(&file);
} else {
printf(" open file error : %d\r\n", f_res);
}

printf("\r\n\*\*\*\*\*\* Read data from the text files \*\*\*\*\*\*\r\n");
// 打开文件
f_res = f\_open(&file, "/test.txt", FA_OPEN_EXISTING | FA_READ);
if (f_res == FR_OK) {
printf(" open file sucess!!! \r\n");
f_res = f\_read(&file, ReadBuffer, sizeof(ReadBuffer), &fnum);
if (f_res == FR_OK) {
printf("read sucess!!! (%d)\n", fnum);
printf("read Data : %s\r\n", ReadBuffer);
} else {
printf(" read error!!! %d\r\n", f_res);
}
} else {
printf(" open file error : %d\r\n", f_res);
}
/\* 关闭文件 \*/
f\_close(&file);
/\*卸载文件系统 \*/
f\_mount(NULL, "/", 1);

/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/

/\* USER CODE BEGIN 3 \*/
}
/\* USER CODE END 3 \*/
}

/\*\*
\* @brief System Clock Configuration
\* @retval None
\*/
void SystemClock\_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };

/\*\* Initializes the RCC Oscillators according to the specified parameters
\* in the RCC\_OscInitTypeDef structure.
\*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL\_RCC\_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error\_Handler();
}

/\*\* Initializes the CPU, AHB and APB buses clocks
\*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK
| RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

if (HAL\_RCC\_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
Error\_Handler();
}
}

/\* USER CODE BEGIN 4 \*/

/\* USER CODE END 4 \*/

/\*\*
\* @brief This function is executed in case of error occurrence.
\* @retval None
\*/
void Error\_Handler(void) {
/\* USER CODE BEGIN Error\_Handler\_Debug \*/
/\* User can add his own implementation to report the HAL error return state \*/
\_\_disable\_irq();
while (1) {
}
/\* USER CODE END Error\_Handler\_Debug \*/
}

#ifdef USE\_FULL\_ASSERT
/\*\*
\* @brief Reports the name of the source file and the source line number
\* where the assert\_param error has occurred.
\* @param file: pointer to the source file name
\* @param line: assert\_param error line source number
\* @retval None
\*/
void assert\_failed(uint8\_t \*file, uint32\_t line)
{
/\* USER CODE BEGIN 6 \*/
/\* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) \*/
/\* USER CODE END 6 \*/
}
#endif /\* USE\_FULL\_ASSERT \*/

4、代码解析

首先,定义变量:

1
2
3
4
5
6
7
8
FATFS fs;                       /\* FatFs 文件系统对象 \*/
FIL file; /\* 文件对象 \*/
FRESULT f_res; /\* 文件操作结果 \*/
UINT fnum; /\* 文件成功读写数量 \*/
BYTE ReadBuffer[1024] = {0}; /\* 读缓冲区 \*/
BYTE WriteBuffer[] = /\* 写缓冲区 \*/
"STM32F103 & STM32CubeIDE SDIO Demo!\r\n";

在各硬件系统初始化完成后,挂载文件系统:

1
2
3
//挂载文件系统
f_res = f\_mount(&fs, "/", 1);

如果SD卡没有格式化,则需要格式化为FatFs文件系统

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
if (f_res == FR_NO_FILESYSTEM) {
printf(
"The SD card does not yet have a file system and is about to be formatted...\r\n");
/\* 格式化SD卡 \*/
f_res = f\_mkfs("/", 0, 0);
if (f_res == FR_OK) {
printf("The SD card successfully formatted the file system\r\n");
/\* 格式化后,先取消挂载 \*/
f_res = f\_mount(NULL, "/", 1);
/\* 重新挂载 \*/
f_res = f\_mount(&fs, "/", 1);
} else {
printf("The format failed\r\n");
while (1)
;
}
}

如果挂载文件系统失败,则停止所有操作

1
2
3
4
5
6
else if (f_res != FR_OK) {
printf(" mount error : %d \r\n", f_res);
while (1)
;
}

文件系统挂载成功后,就可以对文件进行读写操作了。打开文件,并写入操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
f_res = f\_open(&file, "/test.txt", FA_CREATE_ALWAYS | FA_WRITE);
if (f_res == FR_OK) {
printf(" open file sucess!!! \r\n");
// 将数据写入文件
printf("\r\n\*\*\*\*\*\* Write data to the text files \*\*\*\*\*\*\r\n");
f_res = f\_write(&file, WriteBuffer, sizeof(WriteBuffer), &fnum);
if (f_res == FR_OK) {
printf(" write file sucess!!! (%d)\n", fnum);
printf(" write Data : %s\r\n", WriteBuffer);
} else {
printf(" write file error : %d\r\n", f_res);
}
/\*关闭文件\*/
f\_close(&file);
} else {
printf(" open file error : %d\r\n", f_res);
}


函数f_open用于打开文件,如果打开成功,则返回FR_OK,否则,返回错误代码。

函数f_write用于向文件写数据。如果写文件成功,则返回FR_OK,否则,返回错误代码。

当文件写数据完成后,需要通过调用f_close函数,对文件进行操作。如果打开文件过多,会导致后期文件读写失败。支持打开最多文件数量,在FatFs配置中的FS_LOCK(Numbers of files openned simultaneously)中设置,默认值 为2

文件内容读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
f_res = f\_open(&file, "/test.txt", FA_OPEN_EXISTING | FA_READ);
if (f_res == FR_OK) {
printf(" open file sucess!!! \r\n");
f_res = f\_read(&file, ReadBuffer, sizeof(ReadBuffer), &fnum);
if (f_res == FR_OK) {
printf("read sucess!!! (%d)\n", fnum);
printf("read Data : %s\r\n", ReadBuffer);
} else {
printf(" read error!!! %d\r\n", f_res);
}
} else {
printf(" open file error : %d\r\n", f_res);
}
/\* 关闭文件 \*/
f\_close(&file);

函数f_read用于读取文件内容,将读取的数据储存到缓存中。

最后,卸载文件系统:

1
2
f\_mount(NULL, "/", 1);

运行结果如下:

在这里插入图片描述

5、STM32F1与STM32CubeIDE系列文章

5.1 STM32F1与STM32CubeIDE快速入门

5.2 STM32F1与STM32CubeIDE编程实例

文章来源: https://iotsmart.blog.csdn.net/article/details/124745643