二、硬件环境准备:手表主控芯片选型与存储规划

好,咱们进入实战的第二关。硬件环境准备这块,说白了就是回答三个问题:用什么脑子(主控)、往哪记东西(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,日志系统不能太贪心。

我一般分三个缓冲区:

  1. 紧急日志缓冲区(4KB):放在SRAM1,速度最快。用于记录系统崩溃前的最后信息。这个缓冲区永不回收,专门给HardFault_Handler用。
  2. 实时日志缓冲区(16KB):放在SRAM2,双缓冲设计。一个缓冲区在写,另一个在刷入Flash。这样日志写入不会阻塞主任务。
  3. 批量上传缓冲区(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

好了,硬件环境准备就这些。下一章咱们开始写代码,把日志系统真正跑起来。到时候你会发现,前面这些规划做得越细,后面写代码就越顺。