# STM32H7S78-DK 开发套件三周目评测:简单声音采集保存之使用 SD 卡读写的实现与分析
该项目包括了保存录音、按键控制、串口控制、频率成分分析与分类等功能,逐步进行完成。首先进行sd读写的开。在嵌入式开发中,尤其在无操作系统的裸机环境下,SD卡是实现大容量数据存储的重要组件。
## 1. 硬件连接
在开始CubeMX配置前,需要先了解引脚的配置。
### 1.1 数据引脚连接
- **D0 - D3**:将SD卡的数据引脚D0-D3分别连接到STM32的PC8、PC9、PC10、PC11。
- **CMD**:将SD卡的CMD引脚连接到STM32的PD2,用于命令传输。
- **CLK**:将SD卡的CLK引脚连接到STM32的PC12,提供时钟信号。
- **SD_Detect**:将SD卡的SD_Detect引脚连接至GPIO(PM14),用于检测SD卡插入状态。
具体连接示例代码:
```c
// 配置引脚,在CubeMX中生成的初始化代码中可见
void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
// Enable clock for GPIOC and GPIOD
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOD_CLK_ENABLE();
__HAL_RCC_GPIOM_CLK_ENABLE();// 使能PM14引脚所在的GPIO时钟
// 配置SDMMC引脚:CMD (PD2), CLK (PC12), D0 (PC8), D1 (PC9), D2 (PC10), D3 (PC11)
GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | 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(GPIOC, &GPIO_InitStruct);
// 配置SD_Detect引脚 (PM14)
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP; // 内部上拉电阻
HAL_GPIO_Init(GPIOM, &GPIO_InitStruct);
}
```
## 2. CubeMX 配置与代码实现
CubeMX能够简化SD卡和文件系统的配置,我们将通过它来生成SD卡的初始化代码,并使用FATFS文件系统以实现文件操作。这里选择FATFS文件系统方式除了是可以直接配置简单快捷外,使用eprom还有一些缺点:
1.没有文件系统,sd卡在电脑等设备无法读取。
2.读写速度慢、读写区域小。
### 2.1 CubeMX 配置步骤
#### 2.1.1 SDMMC 接口设置
1. 在CubeMX中打开**Peripherals -> SDMMC1**接口。
2. 将其模式设置为**4-bit**模式,以提升数据传输速率。
3. 在**Clock Configuration**中将SDMMC1的时钟频率调整为48MHz,以保证传输速率和稳定性。
#### 2.1.2 启用 FATFS 文件系统
1. 在**Middlewares**中启用FATFS支持,选择SDMMC1作为接口,以实现基于文件系统的操作。
2. 生成初始化代码,其中`fatfs.c`文件包含了文件系统初始化和读写的基本接口。
### 2.2 基本代码示例
接下来使用FATFS文件系统在SD卡上进行文件创建、写入和读取:
```c
#include "fatfs.h"
#include "sdmmc.h"
#include <stdio.h>
void SD_Test() {
FATFS fs;
FIL file;
FRESULT res;
UINT bw;
// 挂载文件系统
res = f_mount(&fs, "0:", 1);
if (res == FR_OK) {
// 创建并写入文件
res = f_open(&file, "0:/test.txt", FA_CREATE_ALWAYS | FA_WRITE);
if (res == FR_OK) {
char writeData[] = "Hello, SD Card!";
f_write(&file, writeData, sizeof(writeData), &bw);
f_close(&file);
}
// 读取文件
res = f_open(&file, "0:/test.txt", FA_READ);
if (res == FR_OK) {
char readData;
f_read(&file, readData, sizeof(readData), &bw);
printf("Read data: %s\n", readData);
f_close(&file);
}
// 卸载文件系统
f_mount(NULL, "0:", 1);
}
}
```
## 3. 不同 SD 卡读写方式与代码分析
当然在不同场景和需求下,对sd卡的读写操作应该有适当改变,才能提高效率、节省资源。
### 3.1 基于文件系统的标准读写
- **优点**:CubeMX自动生成FATFS代码,简化了文件操作流程,适用于低频存储。比如传感器的数据记录等。
- **缺点**:由于文件系统需要索引和管理结构,写入速度会受影响。
```c
void SD_WriteRead_FileSystem() {
FATFS fs;
FIL file;
UINT bw, br;
char writeData[] = "File System Write Test";
char readData;
// 挂载文件系统
f_mount(&fs, "0:", 1);
// 写入文件
f_open(&file, "0:/filesystem.txt", FA_CREATE_ALWAYS | FA_WRITE);
f_write(&file, writeData, sizeof(writeData), &bw);
f_close(&file);
// 读取文件
f_open(&file, "0:/filesystem.txt", FA_READ);
f_read(&file, readData, sizeof(readData), &br);
f_close(&file);
printf("Read Data: %s\n", readData);
f_mount(NULL, "0:", 1);
}
```
### 3.2 扇区级块读写
- **优点**:直接操作扇区避免了文件系统开销,适合高频数据写入。比如音视频、网络文件等。
- **缺点**:需要手动管理数据位置和扇区分配,编程难度大。
```c
void SD_WriteBlock(uint32_t sector, uint8_t *data, uint32_t count) {
if (HAL_SD_WriteBlocks(&hsd1, data, sector, count, HAL_MAX_DELAY) == HAL_OK) {
printf("Block write success!\n");
}
}
void SD_ReadBlock(uint32_t sector, uint8_t *data, uint32_t count) {
if (HAL_SD_ReadBlocks(&hsd1, data, sector, count, HAL_MAX_DELAY) == HAL_OK) {
printf("Block read success!\n");
}
}
```
### 3.3 缓存 + 文件系统混合模式
- **优点**:使用缓存减少频繁写入,提升写入效率,适合音频等连续数据存储。
- **缺点**:占用较多RAM资源,需考虑系统内存限制。
```c
#define BUFFER_SIZE 512
uint8_t cacheBuffer;
void SD_WriteCache(const char *data) {
static uint32_t offset = 0;
while (*data) {
cacheBuffer = *data++;
if (offset >= BUFFER_SIZE) {
SD_WriteBlock(0, cacheBuffer, BUFFER_SIZE / 512); // 以块为单位写入
offset = 0;
}
}
}
```
## 4. 音频数据存储的优化方案
考虑到录音是一个时效性很强的操作,所以对存储效率要求很高,下面是两个优化的方案。
### 4.1 双缓冲技术
设置两个缓冲区,交替写入SD卡以减小写入延时。
```c
uint8_t bufferA;
uint8_t bufferB;
volatile uint8_t *currentBuffer = bufferA;
void Audio_WriteToSD() {
if (currentBuffer == bufferA) {
SD_WriteBlock(0, bufferA, BUFFER_SIZE / 512);
currentBuffer = bufferB;
} else {
SD_WriteBlock(0, bufferB, BUFFER_SIZE / 512);
currentBuffer = bufferA;
}
}
```
### 4.2 文件分段存储
音频数据按时间或大小分段存储,避免大文件操作带来的开销。
```c
void SaveAudioSegment(uint8_t *data, size_t size, uint8_t segmentNum) {
char filename;
sprintf(filename, "
audio_segment_%d.wav", segmentNum);
f_open(&file, filename, FA_CREATE_ALWAYS | FA_WRITE);
f_write(&file, data, size, &bw);
f_close(&file);
}
``` <p></p>
<p>哥们,你这帖子在那干讲,没开发板跑的实物图啊。{:1_131:}</p>
cc1989summer 发表于 2024-11-2 13:03
哥们,你这帖子在那干讲,没开发板跑的实物图啊。
<p>后面加,哈哈哈</p>
页:
[1]