1. 从零规划一块STM32手表:整体设计思路与方案选型
做一块STM32手表,听起来像是电子爱好者绕不开的一个"毕业级"项目,但真正动手之后你会发现,难点从来不是"能不能点亮屏幕",而是怎么在有限的体积、功耗和成本里,把一堆互相打架的需求捏合到一起。我从第一版焊在洞洞板上的"砖头表",到后来打样了四版PCB,中间踩的坑足够写一本小册子。这篇内容就把整个从选型到落地的过程拆开讲,尽量把每个决策背后的"为什么"说清楚,让你少走弯路。
先说清楚这个项目到底适合谁。如果你是完全零基础、连Keil都没装过,那建议先拿最小系统板点个LED、跑通串口,再回来看这块表;如果你已经能独立完成基于STM32的毕业设计级别的项目,那这篇内容里的电源管理、低功耗调度、屏幕刷新这些细节,应该能帮你把作品从"能跑"提升到"能戴出门"。核心关键词就三个:STM32、手表、低功耗。整块表围绕这三件事展开,其他都是配角。
1.1 为什么是STM32,而不是别的方案
很多人第一反应是"手表这么小的东西,用ESP32或者nRF52不香吗"。这个问题我认真对比过,结论是:看你的目标。
如果你的目标是做一个能联网、能推送通知的智能手表,那带无线功能的芯片确实更省事。但如果你像我一样,核心诉求是把STM32这套体系吃透——时钟树、定时器、DMA、低功耗模式、外设驱动——那STM32是更好的老师。它的资料密度、社区体量、外设丰富度,是其他平台比不了的。而且STM32的型号跨度极大,从F0到H7,你可以用同一套开发思路,从低端做到高端,这种延续性对学习者非常友好。
具体到手表这个场景,我最终选的是STM32L4系列(比如L431、L451这类)。原因很直接:手表是电池供电,待机功耗是生死线。L4系列在Stop模式下能做到微安级,RTC走时加LCD刷新,整机平均电流压到几十微安是可行的,这意味着200mAh的小电池能撑好几天。相比之下,F1系列虽然便宜、资料多,但低功耗表现差一个数量级,做出来的表基本一天一充,体验很差。
提示:如果你只是想练手、不追求续航,F103C8T6最小系统板完全够用,成本能压到十几块。但如果你想做出"能戴"的表,从一开始就选L4或者G0系列,别等做完再返工。
1.2 手表的整体架构怎么拆
一块STM32手表,本质上是一个"感知—处理—显示—交互"的闭环系统。我把它拆成五个模块来看:
- 主控与时钟:STM32负责所有逻辑调度,外部挂一颗32.768kHz晶振给RTC,保证走时精度。
- 显示:主流选择是SPI接口的TFT彩屏(如ST7789驱动的1.14寸或1.3寸屏),或者更省电的段码LCD、Sharp Memory LCD。
- 电源:锂电池加充电管理芯片(如TP4056或更省电的BQ系列),再配LDO或DC-DC给MCU供电。
- 交互:按键、旋转编码器,或者电容触摸。手表上按键最可靠,触摸在潮湿环境下容易误触。
- 传感与扩展:加速度计(计步、抬腕亮屏)、心率传感器、蜂鸣器(震动马达更好但更费电)。
这五个模块里,显示和电源是决定成败的两座大山。屏幕刷新率高了费电,低了卡顿;电源设计不好,要么充不进电,要么待机漏电。我第一版就是栽在电源上,LDO选了个静态电流几百微安的型号,结果待机电流比MCU本身还大,白白浪费了L4的低功耗优势。
1.3 方案选型的几个关键取舍
选型阶段我列了一张对比表,把几个核心决策点摊开看:
| 决策点 | 方案A | 方案B | 我的选择与理由 |
|---|---|---|---|
| 主控 | STM32F103 | STM32L431 | 选L431,低功耗是手表刚需 |
| 屏幕 | SPI TFT彩屏 | Sharp Memory LCD | 选TFT,资料多、成本低,先用彩屏跑通再考虑换 |
| 供电 | LDO | DC-DC | 选LDO,纹波小、电路简单,静态电流选1uA级的 |
| 充电 | TP4056 | BQ24074 | 选TP4056,便宜好买,够用 |
| 走时 | 内部RTC | 外部32.768kHz晶振 | 必须外部晶振,内部RC漂移一天能差几分钟 |
这张表不是标准答案,而是我当时的思考路径。比如屏幕这一项,Sharp Memory LCD的功耗确实诱人,静态显示几乎不耗电,但它的驱动资料少、价格高、采购周期长,对第一次做表的人来说门槛太高。我建议先用TFT把整个系统跑通,等你有把握了再换屏优化续航。
2. 硬件设计细节:从原理图到PCB的实操要点
硬件这块,我踩的坑比软件多得多。软件写错了可以改,硬件焊错了要么飞线要么重打板,时间和金钱成本都高。所以这一章我把原理图设计和PCB布局的关键点讲透,尤其是那些"文档里不会写、但实际会要命"的细节。
2.1 最小系统与时钟电路
STM32最小系统就四样东西:电源、复位、晶振、启动模式。听起来简单,但每一项都有讲究。
电源部分,每个VDD引脚旁边必须放一个100nF的去耦电容,紧贴引脚放置,这是铁律。我见过有人把去耦电容放在板子另一头,结果MCU跑着跑着就复位,查了半天才发现是电源噪声。另外VDDA(模拟电源)要单独用磁珠或电感隔离,再配一个1uF加100nF的组合,否则ADC采样会飘。
晶振部分,手表需要两颗晶振:一颗8MHz(或16MHz)给主时钟,一颗32.768kHz给RTC。这里有个大坑:32.768kHz晶振的负载电容必须匹配。晶振规格书里会写负载电容CL,比如12.5pF,那么两个匹配电容C1、C2的计算要考虑PCB寄生电容(一般3~5pF)。公式是:
C1 = C2 = 2 × (CL - Cstray)假设CL=12.5pF,Cstray取3pF,那C1=C2=2×(12.5-3)=19pF,取标准值18pF或20pF。这个电容选错了,RTC要么不走,要么走时不准。我第一次就是随手放了两个22pF,结果一天慢好几分钟,换了电容才正常。
注意:32.768kHz晶振的走线要尽量短,下方铺地要挖空(不要走其他信号),否则容易受干扰停振。这是很多新手做表走时不准的根本原因。
2.2 屏幕接口与背光控制
我用的是ST7789驱动的1.14寸IPS屏,SPI接口,分辨率135×240。接线就几根:SCLK、MOSI、CS、DC、RST,再加背光控制。这里的关键是背光。
屏幕背光通常是几颗LED并联,电流能到20~60mA,这是手表里最耗电的部分。直接接电源常亮,续航直接崩。我的做法是用一个NPN三极管或者MOS管控制背光通断,PWM调光。PWM频率要避开人眼可见的闪烁范围,一般1kHz以上,我用的2kHz,占空比根据环境光调节。
SPI的时钟频率也要注意。ST7789手册标称最高能到62.5MHz,但实际走线长了、干扰大了,跑太高会花屏。我实测在L431上跑到20MHz很稳,再高就要看PCB布局了。如果你发现屏幕偶尔闪一下或者有噪点,先把SPI降速试试。
2.3 电源管理与充电电路
电源是手表的命脉,我把它单独拎出来讲。整个电源链路是这样的:
USB 5V → 充电管理(TP4056) → 锂电池(3.7V) → LDO(3.3V) → STM32及外设TP4056负责恒流恒压充电,充电电流通过PROG引脚上的电阻设定。公式是:
Icharge = 1200 / Rprog (单位:安培,Rprog单位:欧姆)比如要500mA充电电流,Rprog = 1200/0.5 = 2400Ω,取2.4kΩ。但手表电池一般就100~300mAh,充电电流别超过0.5C,否则电池发热、寿命缩短。我用的200mAh电池,充电电流设200mA,Rprog取6kΩ。
LDO的选择是重点。普通LDO静态电流动辄几百微安,而STM32L4在Stop模式下才1~2微安,LDO的漏电直接把低功耗优势吃掉了。我选的是静态电流1uA级别的LDO,比如某些低功耗系列。选型时一定要翻手册看"Iq"这个参数,别只看输出电压和电流能力。
提示:锂电池电压范围是3.0~4.2V,而STM32和外设要3.3V。如果用LDO,电池降到3.3V以下时输出就跟着降,虽然STM32能工作到1.8V,但屏幕和传感器可能就不行了。所以要么选低压差LDO,要么在电量低时主动降亮度、关外设。
2.4 PCB布局的实战经验
PCB布局这块,我总结了三条血泪教训:
第一,模拟和数字分区。晶振、ADC参考、传感器这些模拟部分,和SPI、USB这些数字部分要分开布局,地平面尽量完整,不要被信号线割裂。
第二,电池和充电电路远离敏感信号。充电时TP4056会发热,而且开关噪声大,别把它放在晶振或者传感器旁边。
第三,按键和连接器要考虑结构。手表空间小,按键位置、屏幕排线方向、充电口位置,都要和外壳一起考虑。我第二版就是PCB画完了才发现按键被表壳挡住了,只能重新打板。
板子厚度建议用0.8mm甚至0.6mm,方便塞进表壳。如果空间实在紧张,可以考虑软硬结合板,但成本会上去。
3. 软件开发:从环境搭建到核心功能实现
硬件跑通之后,软件才是真正花时间的地方。这一章我按开发顺序讲:先把环境搭好,再点亮屏幕,然后做UI,最后处理低功耗和走时。每一步都有具体的代码思路和踩坑记录。
3.1 开发环境搭建与工程配置
开发环境我用的是Keil MDK + STM32CubeMX的组合。CubeMX负责生成初始化代码,Keil负责写业务逻辑。这个组合的好处是时钟树、外设初始化这些容易出错的部分交给工具,你专注在应用层。
CubeMX里几个关键配置:
- 时钟树:L431最高80MHz,但手表不需要跑满。我平时跑在16MHz或24MHz,需要刷屏时临时升频,这样平均功耗低。
- RTC:使能外部32.768kHz晶振,开启闹钟和唤醒功能。
- 低功耗:使能Stop模式,配置唤醒源(RTC闹钟、外部中断)。
- SPI:配置为全双工主机模式,时钟极性相位按屏幕手册设置。
生成代码后,我习惯把外设驱动分层:bsp_开头的是板级驱动(屏幕、按键、电源),app_开头的是应用逻辑(UI、计步、菜单)。这样换硬件时只改bsp层,应用层不动。
注意:CubeMX生成的代码里,
SystemClock_Config和MX_xxx_Init这些函数不要手动改,重新生成会被覆盖。要加自己的初始化,写在main函数里MX_xxx_Init之后。
3.2 屏幕驱动与UI刷新策略
ST7789的驱动不难,难的是刷新策略。手表UI有个特点:大部分区域是静态的(表盘背景、日期),只有小部分在变(秒针、时间数字)。如果每帧全屏刷新,SPI带宽和功耗都受不了。
我的做法是局部刷新加脏矩形。维护一个"脏区域"列表,只有内容变化的区域才重绘。比如秒针移动,只刷新秒针经过的那一小块。这样每秒的SPI数据量从几十KB降到几百字节,功耗差好几倍。
具体实现上,ST7789支持设置显示窗口(CASET/RASET命令),你可以只往指定区域写数据。我封装了一个lcd_flush_area(x, y, w, h, buffer)函数,配合一个简单的脏矩形管理,效果很好。
表盘绘制我用的是预渲染加图层的思路。背景图、刻度这些静态元素,开机时画一次,之后不动。指针、数字这些动态元素,每次只重绘变化部分。指针用画线算法(Bresenham)直接画,比贴图省内存。
如果你要做复杂UI,可以考虑移植LVGL。LVGL功能强大,但对手表这种小屏、低功耗场景有点重,RAM占用和刷新开销都不小。我的建议是:简单表盘自己画,复杂菜单再考虑LVGL。
3.3 低功耗调度与RTC走时
低功耗是手表的灵魂,这块我花了最多时间调优。核心思路是:能睡就睡,该醒才醒。
STM32L4的低功耗模式有几个层级:Sleep、Stop、Standby。手表待机时用Stop模式最合适,RTC还在走,RAM保持,唤醒后能快速恢复。Stop模式下电流能到1~2微安,加上LDO和屏幕的漏电,整机待机控制在10微安以内是现实的。
调度逻辑是这样的:
- 正常显示时,MCU跑在低频,屏幕刷新。
- 无操作几秒后,关背光,进入Stop模式。
- RTC闹钟每秒唤醒一次,更新时间,如果秒针需要动就刷新,否则继续睡。
- 按键中断唤醒,处理交互。
这里有个坑:唤醒后的时钟恢复。从Stop模式唤醒后,系统时钟会切回HSI(内部RC),需要重新配置回原来的时钟源。如果忘了这步,SPI频率会变,屏幕可能花屏。CubeMX生成的SystemClock_Config可以在唤醒后重新调用,但要注意RTC时钟源别被改掉。
RTC走时精度方面,外部32.768kHz晶振本身精度就不错,但温度变化会影响。如果追求更准,可以用RTC的校准寄存器(CALIB)做微调,或者用MCU内部温度传感器做温度补偿。不过对普通手表来说,外部晶振加合理负载电容,一天误差几秒已经够用了。
3.4 按键交互与菜单逻辑
手表的交互空间很小,按键数量有限,所以菜单逻辑要设计得"扁平"。我用的是状态机加短按长按的方案:
- 短按:切换功能(表盘→计步→设置→返回)
- 长按:进入/退出当前功能的子菜单
- 双击:快捷操作(比如亮屏)
状态机用enum定义各个界面状态,按键事件在中断里标记,主循环里处理。这样逻辑清晰,也不会在中断里做耗时操作。
按键消抖我用的是定时器扫描加状态判断,而不是简单的延时。延时消抖会阻塞主循环,影响低功耗调度。具体做法是每10ms扫描一次按键电平,连续几次相同才认为是有效动作。
提示:手表按键少,容易误操作。建议加一个"操作确认"机制,比如设置里改时间,需要长按确认才生效,避免口袋里误触改了设置。
4. 调试、烧录与常见问题排查
做完硬件和软件,真正的考验才刚开始。这一章我把调试过程中遇到的典型问题整理出来,配上排查思路,希望能帮你快速定位问题。
4.1 烧录与调试工具的选择
STM32烧录最常用的是ST-Link,便宜好用,配合STM32CubeProgrammer或者Keil都能烧。但手表项目有个特殊问题:SWD引脚可能被复用。
手表IO紧张,有时候会把SWDIO、SWCLK复用成普通IO。一旦复用,下次就烧不进去了。解决办法有两个:一是保留SWD引脚不复用,二是用BOOT0拉高进入系统存储器启动,通过串口或USB烧录。我建议新手保留SWD,等完全定型了再考虑复用。
如果遇到"STM32无法识别USB设备"或者ST-Link连不上,先检查这几点:
- 目标板供电是否正常(量一下3.3V)
- SWD线是否太长(建议10cm以内)
- 是否启用了读保护(RDP),读保护会导致连接失败,需要用CubeProgrammer解除
- 复位引脚是否被拉低
4.2 常见问题速查表
我把调试中遇到的问题整理成一张表,方便你对照排查:
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 屏幕花屏/闪屏 | SPI太快、走线干扰 | 降低SPI频率试试 | 降速、加屏蔽、缩短走线 |
| RTC走时不准 | 负载电容不匹配 | 查晶振规格书算电容 | 换匹配电容,检查走线 |
| 待机电流大 | LDO静态电流高、外设漏电 | 分段测量电流 | 换低功耗LDO,关外设电源 |
| 唤醒后死机 | 时钟未恢复 | 检查唤醒后时钟配置 | 重新调用时钟配置函数 |
| 按键误触发 | 消抖不足、干扰 | 示波器看按键波形 | 加RC滤波、软件消抖 |
| 充电发热 | 充电电流过大 | 量充电电流 | 调大Rprog电阻降电流 |
| 烧录失败 | SWD被复用、读保护 | 检查引脚配置 | 保留SWD或BOOT0启动 |
这张表里的每一条,我都是真金白银踩出来的。比如"待机电流大"这条,我一开始以为是MCU的问题,后来用万用表分段测,才发现是LDO的静态电流就有200微安,换了LDO之后待机电流直接从200多微安降到8微安。
4.3 低功耗调试的独家技巧
低功耗调试有个技巧:分段测量。不要一上来就测整机电流,那样你根本不知道是谁在耗电。正确做法是:
- 先只焊MCU和最小系统,测待机电流,确认MCU本身没问题。
- 加上LDO,再测,看LDO贡献多少。
- 加上屏幕,测关背光和开背光的差异。
- 加上传感器,逐个确认。
这样一层层剥,问题出在哪一目了然。我用的是一块带微安档的万用表,或者用专门的功耗分析仪。如果没有专业设备,用一个10Ω的采样电阻加示波器看压降也能估算。
还有一个容易被忽略的点:未使用引脚的处理。STM32的未使用GPIO如果悬空,输入缓冲器可能会因为电平不定而漏电。正确做法是把未使用引脚配置为模拟输入或者输出低,别让它们悬空。
4.4 从"能跑"到"能戴"的最后一公里
最后聊聊怎么把作品从"能跑"提升到"能戴"。这中间差的不是技术,是细节。
外壳:3D打印是最快的方案,但打印件手感一般。可以试试树脂打印或者亚克力切割。表带用现成的20mm或22mm标准表带,接口做个简单的生耳结构就行。
佩戴舒适度:表壳边缘要倒角,别硌手。屏幕和表壳之间要密封,至少防个汗水。充电口用磁吸或者pogo pin,比插拔USB方便。
续航实测:别信理论计算,实际戴几天看。我第一版理论算能撑5天,实际戴了2天就没电,原因是抬腕亮屏太频繁。后来把亮屏逻辑改成"只在特定角度和特定时间段亮",续航才达标。
固件升级:如果想做OTA,STM32需要bootloader加app的分区设计。bootloader负责接收新固件并写入app区,app区负责业务逻辑。这块比较复杂,建议先把基础功能做稳再考虑。
我个人在实际操作中的体会是,做STM32手表最难的从来不是某个技术点,而是平衡。功耗和性能要平衡,成本和体验要平衡,开发速度和完成度要平衡。你不可能一次做到完美,但每做一版,你对这套系统的理解就深一层。第一版能亮屏走时就是胜利,第二版优化续航,第三版打磨外观,慢慢就成型了。
最后再分享一个小技巧:把每次调试遇到的问题和解决方案记下来,做成自己的"错题本"。我做了四版表,错题本里记了上百条,现在再遇到类似问题,翻一翻就能找到答案。这个习惯比任何教程都管用。