尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

给STM32外挂个“U盘”:用W25Q64存储传感器数据(CubeMX+FatFS)

给STM32外挂个“U盘”:用W25Q64存储传感器数据(CubeMX+FatFS) STM32实战用W25Q64构建可插拔数据存储系统CubeMXFatFS全解析在物联网设备开发中数据存储常面临一个尴尬局面要么使用昂贵的SD卡模块要么忍受EEPROM的有限容量。而SPI Flash芯片W25Q64恰好提供了折中方案——8MB容量、低成本、SPI接口但如何让它用起来像U盘一样方便本文将完整展示从底层驱动到文件系统的全栈实现。1. 硬件架构设计当SPI Flash遇上FatFS1.1 存储介质选型对比存储类型容量范围擦写寿命接口类型典型成本EEPROM4KB-512KB100万次I2C/SPI中SPI Flash1MB-128MB10万次SPI低SD卡1GB-128GB5000次SDIO/SPI中高W25Q64的8MB容量足以存储温湿度日志1秒/次约180天语音记录8k采样率约16分钟工业参数每帧100字节约8万条1.2 文件系统选型关键// FatFS模块结构示例 typedef struct { FATFS fs; // 文件系统对象 FIL file; // 文件对象 FRESULT res; // 操作结果 UINT bw; // 读写字节数 } fatfs_handle_t;注意NOR Flash需考虑磨损均衡建议预留5%空间作缓冲2. CubeMX工程配置从SPI到RTC时间戳2.1 SPI接口配置要点时钟分频根据芯片规格选择W25Q64最高104MHz系统时钟80MHz时选择4分频得20MHzCPOL/CPHA设置Mode 0CPOL0, CPHA0Mode 3CPOL1, CPHA1硬件NSS建议禁用改用GPIO控制2.2 RTC配置示例// 生成带时间戳的文件名 void get_timestamp_name(char* name) { RTC_DateTypeDef date; RTC_TimeTypeDef time; HAL_RTC_GetTime(hrtc, time, RTC_FORMAT_BIN); HAL_RTC_GetDate(hrtc, date, RTC_FORMAT_BIN); sprintf(name, %02d%02d%02d.csv, date.Date, time.Hours, time.Minutes); }3. FatFS移植实战让Flash变身U盘3.1 diskio.c关键函数实现// SPI Flash底层读写接口 DRESULT disk_read(BYTE pdrv, BYTE* buff, LBA_t sector, UINT count) { W25Q_Read(sector * FLASH_SECTOR_SIZE, buff, count * FLASH_SECTOR_SIZE); return RES_OK; } // 擦除后写入NOR Flash特性 DRESULT disk_write(BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count) { W25Q_SectorErase(sector); W25Q_Write(sector * FLASH_SECTOR_SIZE, buff, count * FLASH_SECTOR_SIZE); return RES_OK; }3.2 文件操作封装示例FRESULT log_sensor_data(float temp, float humi) { char buffer[64]; fatfs_handle_t fh; sprintf(buffer, %.1f,%.1f\n, temp, humi); f_mount(fh.fs, , 1); f_open(fh.file, datalog.csv, FA_OPEN_APPEND | FA_WRITE); f_write(fh.file, buffer, strlen(buffer), fh.bw); f_close(fh.file); return fh.res; }4. 高级优化技巧从基础存储到工业级应用4.1 磨损均衡实现方案循环队列法将Flash分为16个逻辑区512KB/区使用最后扇区存储当前区号标记动态映射表在RAM中维护逻辑-物理地址映射每次写操作选择擦除次数最少的块4.2 掉电保护策略// 关键数据写入流程 void safe_write(uint32_t addr, void* data, uint32_t size) { uint8_t crc calculate_crc(data, size); W25Q_Write(addr, data, size); W25Q_Write(addr size, crc, 1); // 写入校验值 // 验证写入 uint8_t verify_crc; W25Q_Read(addr size, verify_crc, 1); if(verify_crc ! crc) { // 触发异常处理 } }5. 实战案例构建温湿度监测系统5.1 数据采集存储流程初始化传感器如SHT30配置RTC获取时间戳创建CSV文件头首次运行循环执行读取传感器数据追加记录到文件每小时生成新文件5.2 USB导出功能实现void export_data_to_usb(void) { DIR dir; FILINFO fno; f_opendir(dir, /); while(f_readdir(dir, fno) FR_OK) { if(!fno.fname[0]) break; if(strstr(fno.fname, .csv)) { // 通过USB虚拟串口发送文件内容 send_file_via_usb(fno.fname); } } f_closedir(dir); }在完成多个工业现场部署后发现最实用的改进是增加文件名日期前缀——这让现场维护人员能快速定位问题时段的数据。例如20240815_故障记录.csv这样的命名规范比单纯的序号更直观。
返回列表