二、硬件环境准备:手表主控芯片选型与存储规划
好,咱们进入实战的第二关。硬件环境准备这块,说白了就是回答三个问题:用什么脑子(主控)、往哪记东西(Flash分区)、干活时临时放哪(RAM缓冲区)。这三个点要是没想清楚,后面代码写得再漂亮也是白搭。
2.1 主控芯片选型:STM32U5 vs ESP32
我个人习惯,选芯片先看日志系统的核心需求。智能手表的日志系统,说白了就三点:低功耗、够快、存储可靠。
STM32U5 是我在高端手表项目里常用的选择。为什么?
- TrustZone 硬件隔离:日志缓冲区可以放在安全区,防止普通应用乱写乱改。我在一个医疗级手表项目里,就靠这个特性通过了数据安全审计。
- LPUART + DMA:日志输出几乎不占CPU。我记得有一次调试蓝牙协议栈,日志打印频率很高,但CPU负载只多了不到2%。
- 功耗表现:待机时日志系统可以完全关闭,唤醒后200μs内恢复。嗯,这个对智能手表太重要了。
ESP32 呢?更适合原型验证和功能复杂的手表。
- 双核 + WiFi/BT:日志可以实时上传到手机或云端。我做过一个运动手表,日志直接通过BLE传到手机App上分析。
- 大RAM(520KB):缓冲区可以给得很宽裕。但要注意,ESP32的RTC内存只有8KB,掉电保存的日志量有限。
- 价格优势:成本敏感型产品首选。
我的建议:量产产品选STM32U5,原型验证选ESP32。如果你像我一样既要低功耗又要无线功能,可以考虑STM32U5 + 外挂BLE芯片的方案。
2.2 Flash存储分区规划
Flash分区这事,我曾经吃过亏。一开始图省事,日志和固件放一起,结果一次日志写爆了,直接导致OTA升级失败。从那以后,我定了个规矩:日志区必须独立分区。
以STM32U5的2MB Flash为例,我一般这样分:
| 分区名称 | 起始地址 | 大小 | 用途 |
|---|---|---|---|
| Bootloader | 0x0800 0000 | 128KB | 启动引导、固件校验 |
| App Firmware | 0x0802 0000 | 1MB | 主程序代码 |
| Config | 0x0812 0000 | 64KB | 用户设置、校准参数 |
| Log Area | 0x0813 0000 | 512KB | 循环日志存储 |
| OTA Temp | 0x081B 0000 | 320KB | 升级包暂存 |
小技巧:日志区我用的是循环覆盖策略。每次写之前检查剩余空间,如果不够就擦除最旧的那个块。这样手表用几年,日志也不会撑爆Flash。
分区时要注意对齐。Flash擦除是按扇区来的,STM32U5的扇区大小是2KB。所以每个分区大小最好是2KB的整数倍。不然你写日志时,可能会不小心擦掉隔壁分区的数据。嗯,这个坑我替你们踩过了。
2.3 RAM缓冲区分配策略
RAM分配,说白了就是「省着用」。智能手表的RAM通常只有256KB到512KB,日志系统不能太贪心。
我一般分三个缓冲区:
- 紧急日志缓冲区(4KB):放在SRAM1,速度最快。用于记录系统崩溃前的最后信息。这个缓冲区永不回收,专门给HardFault_Handler用。
- 实时日志缓冲区(16KB):放在SRAM2,双缓冲设计。一个缓冲区在写,另一个在刷入Flash。这样日志写入不会阻塞主任务。
- 批量上传缓冲区(8KB):放在SRAM3,用于缓存待上传到手机的日志。满了就通过BLE发送。
注意:千万别把日志缓冲区放在DTCM区域。DTCM虽然快,但DMA访问不了。我有个同事就因为这个,日志DMA传输一直失败,查了两天才发现是内存区域选错了。
缓冲区大小的计算公式,我一般这样算:
// 假设日志平均长度80字节,写入频率10条/秒
// 实时缓冲区需要至少能存2秒的数据
buffer_size = 80 * 10 * 2 = 1600字节
// 加上双缓冲开销,取整到4KB比较稳妥
// 紧急缓冲区要能存下最后20条日志
emergency_size = 80 * 20 = 1600字节
// 加上栈回溯信息,4KB刚好
为什么会这样算?你想想看,如果缓冲区太小,日志写入频繁触发Flash操作,功耗就上去了。如果太大,又浪费宝贵的RAM。我个人习惯是「够用就行,留20%余量」。
避坑指南:我曾经在一个项目里,把日志缓冲区设得太大(64KB),结果导致UI动画掉帧。后来发现是日志DMA和LCD的DMA抢带宽。解决办法是把日志缓冲区挪到慢速RAM区域,把快速RAM留给显示系统。
最后说一句,RAM分配不是一成不变的。我建议在代码里用宏定义控制缓冲区大小,方便后期调优:
#define LOG_EMERGENCY_BUF_SIZE 4096
#define LOG_REALTIME_BUF_SIZE 16384
#define LOG_UPLOAD_BUF_SIZE 8192
// 编译时检查总大小
#if (LOG_EMERGENCY_BUF_SIZE + LOG_REALTIME_BUF_SIZE + LOG_UPLOAD_BUF_SIZE) > 32768
#error "日志缓冲区总大小不能超过32KB!"
#endif
好了,硬件环境准备就这些。下一章咱们开始写代码,把日志系统真正跑起来。到时候你会发现,前面这些规划做得越细,后面写代码就越顺。