STM32F103VET6智能手表方案:硬件原理图与蓝牙调试全流程
2026/9/12 13:29:33 网站建设 项目流程

简介:这套基于 STM32F103VET6 的智能手表设计资料,面向电子设计竞赛备赛学生与嵌入式入门开发者,提供可参考的硬件原理图与完整软件源码。硬件部分由 Altium Designer 绘制,覆盖单片机最小系统、BC04-B 蓝牙模块、2.0 寸 TFT 液晶屏、DS18B20 测温电路、电源电路及 J-Link 调试接口等 22 个元件单元,未附带 PCB,但足以支撑电路原理分析、器件选型和二次开发。软件部分以 C 工程为主,包含汇编启动代码、LCD 位图资源、字体库、调试配置文件与说明文档,叠加 454 个 C 源码和 184 个头文件,可梳理从底层驱动到蓝牙通信、界面显示的完整参考链。压缩包共 745 个文件,大小 16.1MB,以源码和头文件为主体,辅以工程配置文件与预览图,便于按模块定位学习。这套资料已有 1287 人学习,适合需要快速掌握 STM32 外设驱动、蓝牙数据交互和 TFT 界面设计思路的开发者作为项目模板使用。

1. 智能手表方案为什么偏偏绕不开STM32F103VET6

看到智能手表,第一反应往往是低功耗 MCU,但大量以学习、竞赛和毕设为主题的“智能手表”项目,核心偏偏是 STM32F103VET6 这颗 100 脚、512KB Flash、64KB SRAM 的经典 Cortex-M3。原因并不难理解:它不追求极致续航,而是把“能不能完整跑起来、原理图能不能看懂、代码能不能改”放在第一位。2.0 寸 TFT 彩屏、HC-05/JDY-31 经典蓝牙模块、锂电池供电,这套组合在 F103 上有大量现成驱动和排错经验,对 5 年以上工程师来说,也适合作为快速原型验证平台,而不是直接照抄量产设计。本文就顺着硬件原理图到软件源码的完整链路,讲清楚每一步怎么定方案、参数怎么设、踩坑时先看哪里。

2. 硬件原理图:先把电源、屏和蓝牙的 I/O 定下来

2.1.1 电源部分常见做法是 LDO,不是 DCDC

3.7V 锂电池经过充电芯片(常见 TP4054 或 ME4054)后,由 LDO 稳压到 3.3V 供整机使用。LDO 首选 XC6206P332MR、RT9013 这类静态电流低、封装小的型号,不要沿用开发板上的 AMS1117:AMS1117 静态功耗偏高、压差大,用在这种便携场景下电池没耗尽就先把电“热”掉了。蓝牙模块若支持 3.6V~6V 供电,直接挂在电池正极反而比接 3.3V 更稳,但前提是模块的 TX/RX 逻辑电平必须和 STM32 兼容,后面会单独说。

功耗账要在画 PCB 之前算清。把 2.0 寸 TFT 背光开到最大时,整机电流通常在 70~110mA 区间,400mAh 电池实际可用容量约 300mAh,亮屏续航只有 3 小时上下,因此原理图阶段就要给背光 PWM、MCU 睡眠、蓝牙模块断电(用 MOSFET 或负载开关)留好控制脚。下面这段脚本可以快速估算不同占空比下的平均电流:

I_mcu = 42e-3 # STM32F103VET6 @72MHz 正常运行 I_bt = 12e-3 # 经典蓝牙 SPP 透传 I_lcd_sleep = 1.5e-3 # 屏静态 I_lcd_bright = 30e-3 # 背光 100% def avg(brightness): # brightness: 0.0~1.0 return I_lcd_sleep + I_mcu + I_bt + brightness * I_lcd_bright for b in (0.2, 0.5, 1.0): cap = 300e-3 print(f"brightness={b:.0%}: {avg(b)*1000:.1f}mA, ~{cap/avg(b)/3600:.1f}h")

参数说明:I_mcu 按 72MHz 全速运行时约 40mA 上下计算,实际开启全部外设会更高;I_bt 取 HC-05 透传状态典型值,连接闲置时会降到 8mA 以下。这里不追求精确,目的是让“背光亮度对续航影响最大”这个结论在原理图阶段就指导引脚分配和 PWM 设计。

2.1.2 TP4054 充电电路和电池电压采样

充电芯片选 TP4054 时,PROG 电阻常见取 1.2kΩ 对应约 500mA 充电电流,但手表空间小、电池容量低,建议降到 200mA 左右,避免充电发热。电池电压采样不要直接接到 STM32 ADC 引脚,电池满电 4.2V 超过 ADC 量程,需要两个 100kΩ 电阻分压后进 PA1,采样值乘 2 还原真实电压。RTC 备用电池一般不需要外加,用 PC14/PC15 接 32.768kHz 晶振给 RTC 提供时钟源即可。

我用一张 I/O 分配表把关键信号固定下来,画原理图时逐项对照:

功能MCU 引脚说明
TFT_SCKPA5SPI1_SCK,72MHz 主频下预分频 8
TFT_MOSIPA7SPI1_MOSI,屏幕数据输入
TFT_MISOPA6SPI1_MISO,读屏 ID 用,可不接
TFT_CSPA4软件 GPIO 片选
TFT_DCPC00 写命令,1 写数据
TFT_RSTPC1GPIO 控制复位
LCD_BLPB1TIM3_CH4 PWM 背光
蓝牙 TXPA9USART1_TX -> 模块 RXD
蓝牙 RXPA10USART1_RX <- 模块 TXD
蓝牙 EN/KEYPA0平常拉低,上电拉高进 AT 模式
电池电压PA1ADC1_IN1,分压后输入
SWDIOPA13调试下载
SWCLKPA14调试下载

注意 PA13/PA14 默认是 SWD 引脚,下载口的 4 根线(VCC、GND、SWDIO、SWCLK)要在原理图上明确引出。BOOT0 接 10kΩ 下拉,BOOT1 直接接 GND,避免误进 bootloader。

2.1.3 蓝牙模块的电平匹配最重要

HC-05 从机模块和 JDY-31 这类“完全兼容 HC-05/06 的蓝牙 3.0 经典蓝牙 SPP 模块”,引脚定义基本一致:VCC、GND、TXD、RXD、STATE、EN/KEY。原理图上最容易犯的错就是把 STM32 的 TX 接到模块的 TX,导致双向都不通。接线必须交叉:STM32 PA9(TX) 连模块 RXD,PA10(RX) 连模块 TXD。至于模块供电,接 3.3V 时 TXD 输出是 3.3V 电平,接 5V 后部分模块 TXD 会到 5V,STM32F103 引脚虽能忍耐 5V 输入,但最好在模块 RXD 与 PA9 之间加 1kΩ 串联电阻做限流,简单可靠。

2.2.1 原理图绘制时最容易忽略的三个细节

第一,TFT 复位脚不能悬空也不能只挂 RC 上电复位,最好由 PC1 GPIO 控制:初始化函数开头拉低 20ms,再拉高,屏幕状态完全可控。第二,蓝牙模块的 EN/KEY 脚不要悬空,悬空在某些模块上会导致上电后自动进入 AT 模式,主从关系混乱,表现为“手机能搜到但连不上”。第三,每颗 IC 的电源脚旁放 0.1μF 去耦电容,TFT 背光供电走线要短粗一点,否则屏幕刷新时亮度会跟着蓝牙收发波动。

2.2.2 屏幕接线和 FPC 排线的影响

2.0 寸 TFT 液晶屏模组一般引出 8 个引脚:VCC、GND、LED、SCK、SDA、RES、DC、CS。LED 背光正极可接 3.3V,也可接 PB1 做 PWM,这里选后者;SCK 和 SDA 分别接 PA5/PA7。如果买到的模组还带 MISO,接 PA6 方便读 ID。排线长度超过 5cm 时,SPI 速率建议从 36MHz 降到 9MHz,否则花屏、白屏会非常难排查,这不是软件时序问题,而是信号完整性。

3. 蓝牙模块选型:HC-05/JDY-31 这类经典蓝牙 SPP 最直接

3.1.1 为什么不用 BLE

BLE 协议在功耗上确实优于经典蓝牙,但代价是开发和排错门槛高不少:手机端要处理 GATT Service/Characteristic,Android 和 iOS 的 API 又不一致,连上之后还有 MTU 协商、分包等问题。对“看时间、收通知、传步数”这个量级的数据量,经典蓝牙 SPP 的透明串口模型足够用,手机端一个普通蓝牙串口 App 就能调试。蓝牙 4.0/5.0 的低功耗优势在手表这种长时间待机场景才有意义,如果你拿的是 STM32F103VET6 开发板做学习原型,先跑通 SPP 再考虑 BLE 是对的顺序。

3.1.2 HC-05 和 JDY-31 怎么选

JDY-31 在引脚上完全兼容 HC-05/06,出厂默认从机角色,支持 AT 指令,价格和货源都稳定;HC-05 的优势是资料多、教程多,搜“hc05蓝牙模块连接不上”能看到大量现成案例。两者关键参数对比如下:

参数HC-05JDY-31
蓝牙协议2.0 SPP3.0 SPP
默认波特率96009600
AT 模式波特率38400(按住按键上电)9600,发 AT 唤醒
工作电压3.6~6V3.6~6V
是否主从一体是,从机主流
输入逻辑电平3.3V/5V 均可3.3V,需要留意

选型建议:如果项目里既有手机调试需求,又有两个模块互连的场景,买 HC-05 主从套装最稳妥;如果只做“手机 App 连表”这一件事,JDY-31 更省事,AT 指令也简单。

3.2.1 用一组 AT 命令把模块固定到 115200

模块默认 9600 波特率,而 STM32 跑 72MHz 时用 115200 更省 CPU,所以上电第一件事是改波特率。HC-05 需要在按住模块上 EN/KEY 按键的同时上电,才会以 38400 进入 AT 模式;JDY-31 则直接发 AT,不需要按键。用 USB-TTL 接好模块后,可以用下面的脚本逐条发送命令并回显:

#!/bin/bash # USB-TTL 设备名按实际修改,HC-05 此时波特率 38400 stty -F /dev/ttyUSB0 38400 raw cs8 -echo while read -r cmd; do echo -ne "$cmd\r" > /dev/ttyUSB0 sleep 0.6 done <<'EOF' AT+ORGL AT+ROLE=1 AT+CMODE=0 AT+BIND=0012,00,123456 AT+UART=115200,0,0 AT+NAME=Watch_TFT AT+RESET EOF

参数说明:AT+ORGL 恢复出厂,清掉模块内部可能残留的配对状态;AT+ROLE=1 设为主机角色,如果是用来连手机,改成 AT+ROLE=0 保持从机;AT+CMODE=0 指定只连 BIND 里的蓝牙地址,设备地址可以先在透传模式下用 AT+ADDR? 查询从机模块得到;AT+UART=115200,0,0 表示波特率 115200、停止位 1、校验位无。最后 AT+RESET 让配置生效。JDY-31 的 AT 命令略有差异,但 AT+UART 和 AT+NAME 语义一致。

3.3.1 hc05蓝牙模块连接不上时先看三个点

“连不上”在论坛里出现频率最高,绝大多数不是蓝牙模块坏了,而是下面三类原因。第一,模块电源功率不足。USB-TTL 的 3.3V 输出通常只有几十毫安,模块搜索和配对瞬间电流会到 30~50mA,电压一掉就“搜索不到或连上秒断”。解决办法是给模块单独的 5V 供电,并且共地。第二,TX/RX 接反。这个太常见,模块的 TXD 必须接另一端串口的 RX,用 USB-TTL 调试时同样按交叉接。第三,波特率不一致。模块已经改成 115200,但串口助手仍按 9600 打开,回显就会是乱码。可以先发一个 AT,如果回复 OK,说明通;如果乱码,把波特率切回 9600 再试。

3.4.1 蓝牙数据传输要定义自己的帧协议

SPP 透传的缺点是没有帧边界,MCU 收到的是一串字节流。为了不把“时间同步”“通知消息”“步数”混在一起,我一般会定义一个带头部、命令、长度、数据和校验的帧结构,放在源码里统一收发:

#define FRAME_MAX_LEN 32 typedef struct { uint8_t head1; // 0xAA uint8_t head2; // 0x55 uint8_t cmd; // 命令字 uint8_t len; // data 长度 uint8_t data[FRAME_MAX_LEN - 6]; uint8_t sum; // 前面所有字节异或和 } bt_frame_t;

接收端用状态机从流中切帧:收到 0xAA 后,下一个字节必须是 0x55,否则丢弃重新搜索帧头;之后按 len 收足数据,最后校验和对不上也丢弃。这样手机 App 和手表约定好同一套协议,蓝牙通道里不管混着多少条消息都不会错位。

4. 2.0 寸 TFT 液晶屏驱动:ST7789 的 SPI 点亮流程

4.1.1 先确认屏的驱动是 ST7789V 还是 ILI9341

市面 2.0 寸 240x320 IPS 彩屏模组,绝大多数用 ST7789V,少部分是 ILI9341。两者引脚完全兼容,但初始化命令和 ID 不同。上电后可以尝试读 ID 区分:ST7789V 用 0x04 命令(RDDID),ILI9341 用 0xD3 命令,读回结果分别是 0x85/0x86/0x88 和 0x93/0x94。不过很多低成本模组没有把 MISO 引出来,读不到 ID 是正常的,最直接的区分办法是看屏背面的丝印,或者直接按 ST7789 初始化流程跑,白屏再换。

4.1.2 ST7789 的最小初始化序列

SPI 通信模式固定为模式 0(CPOL=0、CPHA=0),先初始化 GPIO 和 SPI1,再执行初始化序列。核心初始化代码应包含以下步骤:

static void lcd_reset(void) { LCD_RST_LOW(); // 复位脚拉低 delay_ms(20); LCD_RST_HIGH(); // 拉高退出复位 delay_ms(30); } static void lcd_init_seq(void) { lcd_write_cmd(0x11); // SLPOUT 退出睡眠 delay_ms(120); // 必须等足 120ms lcd_write_cmd(0x36); // MADCTL 扫描方向 lcd_write_data(0x00); // 竖屏 240x320 lcd_write_cmd(0x3A); // COLMOD 颜色格式 lcd_write_data(0x05); // 16bit RGB565 lcd_write_cmd(0x21); // INVON 反色显示 lcd_write_cmd(0x13); // NORON 正常显示模式 delay_ms(10); lcd_write_cmd(0x29); // DISPON 开显示 }

逻辑说明:0x11 之后必须延时 120ms 以上,否则后续设置可能不生效;0x36 的 0x00 对应竖屏,改成 0xC0 是横屏,具体以面板丝印方向为准;0x21 反色这行最容易漏,很多 ST7789 模组不开反色会整体发白。延时要在初始化流程里显式写出来,不能依赖编译器优化掉。

4.2.1 SPI 读写速度和模式怎么定

SPI1 在主频 72MHz 下,BaudRatePrescaler_8 得到 9MHz,对 2.0 寸屏足够快,排线长度短也可以尝试 18MHz。刷新一屏 240x320x2=153KB 的数据,9MHz 下约 17ms,肉眼已经感觉不到明显延迟。如果屏幕刷新出现横条纹,优先降速,再看供电和地线。

常用 ST7789 指令速查如下:

指令含义说明
0x2ACASET 列地址设置 X 窗口
0x2BPASET 行地址设置 Y 窗口
0x2CRAMWR 写显存连续写入 RGB565
0x36MADCTL方向、RGB/BGR 序
0x3ACOLMOD像素格式,0x05 即 16bit
0x21INVON多数模组需要开启反色
4.2.2 用 DMA 刷屏,别一个像素一个像素写

整屏刷新用 DMA 最省 CPU,数据可以从 MCU 内部 SRAM 直接搬进 SPI 发送寄存器。由于 F103VET6 的 SRAM 只有 64KB,不够放 153KB 整屏缓冲,常见做法是准备一块 240x16 或 240x32 的局部行缓冲,按条带刷屏。这里给一个可直接使用的 DMA 发送函数框架:

void lcd_send_dma(uint16_t *buf, uint32_t n) { LCD_CS_LOW(); LCD_DC_HIGH(); DMA_Cmd(DMA1_Channel3, DISABLE); // 清 DMA 通道 DMA_SetCurrDataCounter(DMA1_Channel3, n); // 设置要传的字节数 DMA_Cmd(DMA1_Channel3, ENABLE); // 启动传输 while (DMA_GetFlagStatus(DMA1_FLAG_TC3) == RESET); // 等待完成 DMA_ClearFlag(DMA1_FLAG_TC3); LCD_CS_HIGH(); }
4.2.3 注意:SPI Flash 和屏幕共用 SPI1 时的坑

如果同一颗 MCU 还要接 SPI Flash 或其他 SPI 外设,它们共用 SPI1 总线时,CS 片选必须分开。切换设备之后先确认总线状态,再写命令,否则两个外设都收半截数据。

5. 软件源码组织:裸机事件分发把蓝牙和屏幕串起来

5.1.1 不建议一上来就上 FreeRTOS

手表这种 UI 结构不复杂、任务数量少的项目,裸机状态机比 RTOS 更容易控制和排查。RTOS 带来的线程切换反而引出新问题:蓝牙中断收数据时,TFT 正在刷屏,两个软件模块同时访问 SPI1,要么加锁,要么把刷屏改成“不可打断”的临界区。裸机方案里用事件循环,所有界面更新都集中在主循环执行,中断只负责置标志、丢帧,逻辑简单得多。

5.1.2 事件类型和队列可以这样定义
typedef enum { EV_NONE = 0, EV_KEY, EV_BT_DATA, EV_TICK_1S, EV_LOW_BATT, } event_id_t; typedef struct { event_id_t id; uint8_t payload[20]; uint8_t len; } event_t;

主循环写法:

while (1) { event_t ev = event_queue_pop(); if (ev.id == EV_NONE) { __WFI(); // 没有事件就等待中断 continue; } switch (ev.id) { case EV_TICK_1S: lcd_update_clock(rtc_get_time()); break; case EV_BT_DATA: ble_parse_and_show(&ev); // 解析蓝牙帧并更新界面 break; case EV_LOW_BATT: lcd_draw_battery_alert(); break; default: break; } }

逻辑说明:EV_TICK_1S 由 RTC 秒中断或 1s 定时器产生,屏幕上的时间每秒只更新一块区域而不是整屏;EV_BT_DATA 由 UART 空闲中断产生,中断里只能把字节存进缓冲区,解析放在主循环里做。__WFI() 让 CPU 在无事件时进入等待中断状态,是裸机低功耗最便宜的手段。

5.2.1 蓝牙帧如何翻译成界面动作

蓝牙协议帧里的 cmd 字段可以预留一组命令:0x01 同步时间、0x02 显示通知、0x10 设置步数、0x11 读取电量。收到 0x01 时,从 data 里拆出时分秒,直接写 RTC 寄存器;收到 0x02 时,把 UTF-8 字节存进一个 32 字节的缓冲数组,再调 LCD 显示字符串。通知刷新不要一帧一刷,同一内容在 2 秒内重复到达时直接丢弃,避免屏幕闪动。

5.2.2 显示和蓝牙的数据源要分开

源码里最容易失控的是“全局变量满天飞”。收到蓝牙消息后直接改 LCD 的字符串缓冲区,刷屏函数又在读同一个缓冲区,就会出现半个字的情况。我一般会把界面状态拆成 struct:

typedef struct { uint8_t hour, min, sec; char notify[33]; uint8_t battery; } ui_state_t;

蓝牙只负责更新 ui_state_t 里的数据,LCD 绘制函数只管把这个结构体画出来。这样排错时只要检查“数据有没有被正确写入结构体”,不用在中断和绘图逻辑之间来回翻。

5.3.1 存储分配:不要开整屏显存

F103VET6 的 64KB SRAM 放不下 240x320x2 = 153KB 的 RGB565 整屏缓冲。很多新手把“显存”和“屏幕”绑定,整屏刷新直接 malloc,结果编译能过,运行后几分钟就崩溃。正确做法是局部窗口 + 条带刷新,或者直接操作 GRAM 的 0x2C 命令连续写。绘制一行的像素用行缓冲uint16_t line[240],共 480 字节,64KB SRAM 毫无压力。

5.3.2 编译期精确知道资源占用

在 MDK-ARM 或 GCC 链接脚本里看 Map 文件,Code 段和 RO 段通常 40~80KB,DW(清零段)是全局变量和缓冲。只要 DW 不超过 64KB,就说明 SRAM 没过量。

6. 三个验证手段和一个电池电量查表技巧

6.1 上电三板斧

第一板斧,SWD 能连上且能复位,说明最小系统没问题。第二板斧,用 USB-TTL 直接看串口打印,在系统初始化末尾加一句printf("[BOOT] ok\r\n"),替换重定向到 USART1 或 USART2。第三板斧,让 TFT 显示三个纯色竖条,代码里对屏幕每个窗口填充 R=31、G=63、B=31 三组值;如果颜色正确,说明屏的接线、SPI 和初始化序列都过了。

6.2 蓝牙和手机透传的二次验证

MCU 和手机 App 连不上时,不要先怀疑 MCU 程序。将蓝牙模块 TX/RX 从 PA9/PA10 断开,用 USB-TTL 单独接模块,手机 App 发一条自定义字符串,USB-TTL 串口助手能收到,说明模块和手机这条路是通的。再把手表断电,单独用 USB-TTL 给 MCU 发同样的数据帧,MCU 串口能回显,说明 MCU 接收逻辑正常。最后才接起来查交叉连接和波特率。

6.3 电池电压查表而不是在线算百分比

电池放电曲线不是直线,用公式换算电压百分比误差很大,常见做法是把 3.00V~4.20V 按 0.05V 一个点做查表。PA1 输入电压经过 1:1 分压,ADC 量程 0~3.3V 对应实际 0~6.6V:

uint8_t battery_level(uint16_t adc) { // 下标 0~19 对应 3.00V~4.20V static const uint8_t table[20] = { 0, 2, 5, 9, 15, 22, 32, 43, 55, 66, 77, 85, 90, 94, 97, 98, 99, 99, 100, 100 }; float v = adc * 3.3f / 4095.0f * 2.0f; // 分压还原 int idx = (int)((v - 3.00f) / 0.05f); if (idx < 0) return 0; if (idx > 19) return 100; return table[idx]; }

查表逻辑说明:ADC 是 12 位分辨率,3.3V 参考电压下每 1LSB 约 0.8mV,分压电阻精度取 1% 时整体误差在 3% 以内,足够显示电量百分比。参数 v 低于 3.00V 时一律按 0% 处理,避免关机后电压回升导致电量跳变。这个查表函数要放在 1 秒定时任务里调用,不要每次收到蓝牙查询请求都重新启动 ADC 转换,反而会引入采样抖动。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询