基于STM32F103的SD卡数据记录器实战开发指南在物联网和嵌入式系统开发中数据记录功能是许多项目的核心需求。无论是环境监测、设备运行日志还是实验数据采集都需要可靠地将传感器数据存储到非易失性存储器中。本文将详细介绍如何使用STM32F103C8T6开发板正点原子Mini板和SPI接口构建一个完整的SD卡数据记录系统。1. 项目规划与硬件准备1.1 系统架构设计一个完整的SD卡数据记录器通常包含以下几个关键组件微控制器单元STM32F103C8T6作为主控芯片存储模块通过SPI接口连接的SD卡数据采集模块模拟或数字传感器文件系统FATFS实现文件管理辅助调试串口输出调试信息硬件连接示意图STM32引脚SD卡引脚功能描述PA4CS片选信号PA5SCK时钟信号PA6MISO主机输入PA7MOSI主机输出3.3VVCC电源正极GNDGND电源地1.2 开发环境搭建确保已安装以下软件工具STM32CubeMX V6.6.1或更新版本Keil MDK-ARM V5.29或兼容IDE串口调试工具如Putty、Tera Term文本编辑器推荐VS Code或Notepad提示建议使用最新版本的STM32CubeMX以获得更好的兼容性和更多功能支持。2. STM32CubeMX工程配置2.1 基础外设配置首先在STM32CubeMX中创建新工程选择STM32F103C8T6芯片然后进行以下配置RCC配置高速外部时钟HSECrystal/Ceramic Resonator低速外部时钟LSEDisableSYS配置DebugSerial WireTimebase SourceSysTick时钟树配置HCLK设置为72MHz最大工作频率APB1 Prescaler设置为236MHzAPB2 Prescaler设置为172MHz2.2 SPI接口配置SD卡通过SPI1接口通信配置如下hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_1EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_256; // 初始化低速 hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial 10;2.3 FATFS文件系统配置在Middleware中启用FATFS配置如下参数FATFS ModeEnabledUse DMADisabledVolume0Max Sector Size512File SystemFAT32Use long file namesDisabled3. 数据记录器核心代码实现3.1 SD卡底层驱动SD卡通信需要实现以下基本功能// SPI速度设置函数 void SPI1_SetSpeed(u8 prescaler) { assert_param(IS_SPI_BAUDRATE_PRESCALER(prescaler)); __HAL_SPI_DISABLE(hspi1); hspi1.Instance-CR1 0XFFC7; hspi1.Instance-CR1 | prescaler; __HAL_SPI_ENABLE(hspi1); } // SPI读写单字节函数 u8 SPI1_ReadWriteByte(u8 TxData) { u8 Rxdata; HAL_SPI_TransmitReceive(hspi1, TxData, Rxdata, 1, 1000); return Rxdata; }3.2 文件系统操作封装为简化上层应用我们封装几个常用文件操作函数// 初始化文件系统 FRESULT init_filesystem(void) { static FATFS fs; return f_mount(fs, , 1); } // 创建新数据文件 FRESULT create_data_file(const char* filename) { FIL file; FRESULT res f_open(file, filename, FA_CREATE_NEW | FA_WRITE); if(res FR_OK) f_close(file); return res; } // 追加数据到文件 FRESULT append_to_file(const char* filename, const char* data) { FIL file; FRESULT res f_open(file, filename, FA_OPEN_APPEND | FA_WRITE); if(res FR_OK) { UINT bw; f_printf(file, %s\r\n, data); f_close(file); } return res; }3.3 数据记录任务实现创建一个周期性记录任务每秒采集并存储一次数据void data_logger_task(void) { // 初始化文件系统 if(init_filesystem() ! FR_OK) { printf(File system init failed!\r\n); return; } // 创建数据文件 char filename[32]; sprintf(filename, DATA_%04d%02d%02d.csv, get_year(), get_month(), get_day()); if(create_data_file(filename) ! FR_OK) { printf(Create file failed!\r\n); return; } // 写入CSV表头 append_to_file(filename, Timestamp,Temperature,Humidity,Pressure); // 主循环 while(1) { // 获取传感器数据 float temp read_temperature(); float humi read_humidity(); float press read_pressure(); // 格式化数据 char data_line[128]; sprintf(data_line, %02d:%02d:%02d,%.2f,%.2f,%.2f, get_hour(), get_minute(), get_second(), temp, humi, press); // 写入文件 if(append_to_file(filename, data_line) ! FR_OK) { printf(Write data failed!\r\n); } // 每秒记录一次 HAL_Delay(1000); } }4. 系统优化与问题排查4.1 SPI通信速度优化SD卡在不同工作阶段需要不同的通信速度初始化阶段低速模式SPI_BAUDRATEPRESCALER_256数据传输阶段高速模式SPI_BAUDRATEPRESCALER_4或更高// SD卡初始化时设置为低速 void SD_SPI_SpeedLow(void) { SPI1_SetSpeed(SPI_BAUDRATEPRESCALER_256); } // 正常工作时设置为高速 void SD_SPI_SpeedHigh(void) { SPI1_SetSpeed(SPI_BAUDRATEPRESCALER_4); }4.2 文件系统稳定性处理长时间数据记录需要考虑以下问题文件大小管理定期创建新文件避免单个文件过大异常处理SD卡拔出检测和重新挂载数据完整性确保每次写入操作完成// 检查SD卡状态 uint8_t check_sd_card(void) { if(SD_Init() ! 0) return 0; // 初始化失败 if(f_mount(NULL, , 0) ! FR_OK) return 0; // 卸载失败 return 1; // 状态正常 } // 定期检查文件大小并创建新文件 void manage_file_size(void) { static uint32_t file_counter 0; FILINFO fno; if(f_stat(current_filename, fno) FR_OK) { if(fno.fsize MAX_FILE_SIZE) { file_counter; sprintf(current_filename, DATA_%04d.csv, file_counter); create_data_file(current_filename); } } }4.3 常见问题排查指南问题现象可能原因解决方案SD卡初始化失败接线错误/供电不足检查接线确保3.3V供电稳定文件创建失败卡未格式化/文件系统损坏使用电脑格式化SD卡为FAT32数据写入不完整SPI速度过高/缓冲区不足降低SPI速度增加写入缓冲区频繁写入失败文件未正常关闭确保每次f_open后都有对应的f_close5. 高级功能扩展5.1 多文件并行记录对于需要分类记录的数据可以实现多文件同时记录typedef struct { FIL file; char filename[32]; uint32_t max_size; } data_logger_t; // 初始化多个记录器 data_logger_t temp_logger, humi_logger, press_logger; void init_multi_loggers(void) { strcpy(temp_logger.filename, TEMP_LOG.CSV); temp_logger.max_size 1024*1024; // 1MB strcpy(humi_logger.filename, HUMI_LOG.CSV); humi_logger.max_size 1024*1024; strcpy(press_logger.filename, PRESS_LOG.CSV); press_logger.max_size 1024*1024; }5.2 数据压缩存储对于需要长期记录的应用可以实现简单的数据压缩// 简易数据压缩函数 void compress_data(float *data, uint8_t *output) { uint16_t temp (uint16_t)(data[0] * 100); uint16_t humi (uint16_t)(data[1] * 100); uint16_t press (uint16_t)(data[2] * 10); output[0] temp 8; output[1] temp 0xFF; output[2] humi 8; output[3] humi 0xFF; output[4] press 8; output[5] press 0xFF; }5.3 断电保护机制实现基本的数据保护功能定期同步使用f_sync强制写入物理设备状态保存记录最后写入位置恢复机制系统启动时检查未完成写入// 安全写入函数 FRESULT safe_write(FIL* fp, const void* buff, UINT btw) { FRESULT res f_write(fp, buff, btw, bw); if(res FR_OK) res f_sync(fp); return res; }在实际项目中SD卡数据记录器的稳定性至关重要。经过多次测试发现合理设置SPI时序参数和文件系统缓存大小能显著提高系统可靠性。当需要长时间连续记录时建议定期检查文件系统状态并做必要的维护操作。