ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32F103VET6+2.0寸TFT彩屏+蓝牙模块打造智能手表:从原理图到固件全解析

STM32F103VET6+2.0寸TFT彩屏+蓝牙模块打造智能手表:从原理图到固件全解析 简介面向嵌入式开发者与智能硬件爱好者的完整参考资料围绕STM32F103VET6与2.0寸TFT屏、蓝牙模块提供从ALTIUM原理图到软件源码的全套设计。资源为ZIP压缩包共1075个文件、约36.99MB其中C/H源代码覆盖引导加载、实时操作系统驱动、图形用户界面库、蓝牙通信库以及时间显示、通知处理、健康监测等应用层逻辑ALTIUM相关文件则呈现原理图与PCB布局另有BMP图片、PDF与DOC文档辅助理解整体结构。这颗基于Cortex-M3内核的芯片主频72MHz内置512KB闪存和64KB SRAM资源中针对其丰富外设接口的扩展设计值得深入学习。已有292人学习下载适合项目移植、毕业设计和产品原型验证。通过分析原理图与源码可掌握GPIO、SPI/UART等外设的配置方法理解电池管理、TFT显示驱动、蓝牙数据收发和系统任务调度的实现路径对提升嵌入式开发和低功耗穿戴应用能力有明显价值。1. 从一块开发板到一个能戴在手腕上的 TFT 彩屏手表拿到这个 zip 的工程师通常不是为了看一款商品级手表的拆解而是想用 STM32F103VET6、2.0 寸 TFT 彩屏和蓝牙模块自己拼出一个能跑通的最小智能手表系统。标题里真正要拆的是三件事ALTIUM 原理图决定硬件怎么连软件源码决定屏幕亮不亮、蓝牙稳不稳而“2.0寸TFT”和“蓝牙”这两项决定了大部分引脚分配和通信协议。下面按硬件设计、固件驱动、蓝牙实战、排错进阶的顺序把整套方案讲透给的可执行步骤能直接照抄也会把引脚映射、电平转换、HC-05 连接不上这类常见问题提前说破。适合做毕业设计、开源硬件复刻以及想把别人工程移植到自己板子上的嵌入式开发。2. STM32F103VET6 与 2.0 寸 TFT、蓝牙的硬件链路设计2.1 为什么是 VET6引脚余量和双通信通道STM32F103VET6 是 LQFP100 封装内置 512KB Flash 和 64KB SRAM对比最常见的 C8T648 引脚 / 64KB Flash多出来的引脚和存储空间在手表场景非常关键。手表固件里通常要放中文字库和图标资源64KB Flash 会很紧张512KB 可以把它们直接放进内部 Flash省掉外部 Flash 的成本和布线。更重要的是通信资源一个 SPI1 接 TFT 彩屏一个 USART2 接蓝牙还能留 USART1 做调试日志三路互不影响。实践中我更倾向于把 TFT 挂在 SPI1、蓝牙挂在 USART2、调试日志放 USART1这样即使屏幕刷新占满 CPU蓝牙数据也有独立的中断入口。以一块 2.0 寸、240×320、主控为 ST7789V 的 SPI 接口 TFT 液晶屏为例常见引脚映射如下功能信号STM32F103VET6 引脚说明SPI1 时钟TFT_SCLKPA5分频后约 18MHzSPI1 主出从入TFT_MOSIPA7屏幕单输入MISO 可不接SPI 片选TFT_CSPA4软件控制低电平有效数据/命令选择TFT_DCPB0低电平发命令高电平发数据复位TFT_RSTPB1低电平复位背光TFT_BLPB10也可以用 PWM 调亮度2.2 蓝牙模块选型与 ALTIUM 原理图的电平关系标题只写了“蓝牙”没有说是 BLE 还是经典蓝牙。带 ALTIUM 原理图和源码的手表项目绝大多数用的是 HC-05/HC-06 这类基于 CSR 芯片的经典蓝牙 3.0 模块走 SPP 串口协议。原因很现实STM32F103 原生没有 BLE 协议栈外挂 SPP 模块在固件里只需要处理串口收发代码量最小。HC-05 支持主从一体比 HC-06 更适合做手表它能被手机连接也能在另一块板子上做主设备主动回连STATE 引脚还能直接指示连接状态硬件上只需一根 GPIO 线。原理图设计时要注意电平。很多 HC-05 模块板载了 3.3V LDOVCC 可以接 5V但串口逻辑电平仍然是 3.3V。如果参考设计里单片机是 5V必须在 TX 到蓝牙 RXD 之间串联 1kΩ 电阻限流。STM32F103 的 GPIO 是 3.3V这里没有 5V 耐受问题但我仍会在模块 RXD 上串一个 100Ω 电阻用来抑制反射。供电方面外部电源先接到 AMS1117-3.3 或 RT9013输出 3.3V 再分给 MCU、蓝牙模块和 TFT 背光。蓝牙模块在广播或回连时峰值电流会到 3050mATFT 背光 2040mA整机稳态电流约 100mA选择 LDO 时不要只看额定电流还要看压差。2.3 ALTIUM 原理图绘制去耦、guard ring 和射频处理用 ALTIUM 画这块板子最容易出问题的地方不是连线而是电源完整性和地平面。每个电源引脚旁边放 100nF 去耦电容是基本要求位置尽量靠近引脚过孔到电容再到电源脚的回路要短。蓝牙射频部分如果和 TFT 数据线、背光 PWM 线长距离并行容易把辐射耦合进参考时钟。常见做法是在模块下方铺连续地不要走太细的地线射频电源加一个 LC 滤波。晶振和模拟信号附近可以画一圈接地环也就是很多人说的 guard ring在 Altium Designer 里用 Polygon Pour 或 Isolate 区域实现设置 0.3mm 以上间距并打上一圈过孔能明显降低来自周围走线的耦合干扰。2.4 固件侧的引脚初始化要和原理图保持一致原理图连线正确只是第一步屏能亮还必须让固件里的 GPIO 复用和原理图一一对应。下面这段是初始化 TFT 的 SPI1 和蓝牙串口 USART2 的代码对应 2.1 小节的引脚表static void lcd_pin_init(void) { GPIO_InitTypeDef gpio; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_SPI1, ENABLE); GPIO_StructInit(gpio); gpio.GPIO_Mode GPIO_Mode_AF_PP; // SPI1_SCK/MOSI 复用推挽 gpio.GPIO_Speed GPIO_Speed_50MHz; gpio.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7; GPIO_Init(GPIOA, gpio); gpio.GPIO_Mode GPIO_Mode_Out_PP; // 软件控制 CS/DC/RST/BL gpio.GPIO_Speed GPIO_Speed_2MHz; gpio.GPIO_Pin GPIO_Pin_4; GPIO_Init(GPIOA, gpio); gpio.GPIO_Pin GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_10; GPIO_Init(GPIOB, gpio); }注意 PA4 是 SPI1 的硬件 NSS 引脚如果直接配置成复用功能SPI 主机可能自动产生片选动作这里把它改成普通推挽输出由软件拉低选中屏幕是最稳妥的做法。SCK 和 MOSI 为什么用AF_PP而不是Out_PPSPI 外设接管引脚后数据由外设移位寄存器驱动普通推挽输出无法产生 SPI 时序。蓝牙串口初始化对应 USART2 的 PA2/PA3void bt_uart_init(uint32_t baud) { USART_InitTypeDef usart; GPIO_InitTypeDef gpio; RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); gpio.GPIO_Pin GPIO_Pin_2; // PA2 USART2_TX gpio.GPIO_Mode GPIO_Mode_AF_PP; GPIO_Init(GPIOA, gpio); gpio.GPIO_Pin GPIO_Pin_3; // PA3 USART2_RX gpio.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, gpio); USART_StructInit(usart); usart.USART_BaudRate baud; usart.USART_WordLength USART_WordLength_8b; usart.USART_Parity USART_Parity_No; USART_Init(USART2, usart); USART_Cmd(USART2, ENABLE); }HC-05/06 出厂默认波特率常见为 9600 或 38400第一次上电时先看模块背面丝印。之后用 AT 指令把波特率改成 115200这里的baud参数也要同步改否则出现配对成功但数据乱码。参数说明USART_WordLength_8b和USART_Parity_No是 8N1这是 HC-05 的默认帧格式如果模块被配置成了偶校验这里要一起改否则进入 AT 模式也会一直 ERROR。3. TFT 彩屏驱动与蓝牙串口数据通道的软件源码实现3.1 2.0 寸 TFT 彩屏的初始化序列和最小显示代码2.0 寸模块常见主控是 ST7789V 或 ILI9341驱动差别只在初始化序列。以 ST7789V 为例上电后先复位再发 Sleep Out 和 Display On然后设置像素格式。下面是一段能直接放进工程的初始化代码static void lcd_write_cmd(uint8_t cmd) { LCD_DC_LOW(); // DC0传输命令 lcd_spi_write(cmd); LCD_DC_HIGH(); // 恢复为数据模式 } static void lcd_write_data(uint8_t data) { LCD_DC_HIGH(); // DC1传输数据 lcd_spi_write(data); } void lcd_init(void) { lcd_reset(); delay_ms(120); lcd_write_cmd(0x11); // SLPOUT退出睡眠 delay_ms(120); lcd_write_cmd(0x3A); lcd_write_data(0x05); // 16bit/pixelRGB565 lcd_write_cmd(0x36); lcd_write_data(0x00); // MADCTL扫描方向和 RGB 顺序 lcd_write_cmd(0x29); // DISPON开显示 lcd_fill_screen(0x0000); // 全屏清黑 }关键参数在0x3A和0x36。0x3A后面的0x05表示 16 位色深对应固件里的uint16_t颜色值如果写成0x0618 位大部分 SPI 屏幕在 RGB565 模式下颜色会变得很怪。0x36的 MADCTL 值直接控制扫描方向和 RGB 顺序屏幕上下反时把0x00改成0xC0左右反改成0xA0颜色偏红偏蓝时尝试0x08这类带 RGB 交换位的值。这个参数不用每次都重烧固件可以做成编译宏在不同批次屏幕间切换。区域填充是显示的核心void lcd_fill_rect(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1, uint16_t color) { uint32_t i; lcd_write_cmd(0x2A); // 设置列地址 lcd_write_data(x0 8); lcd_write_data(x0 0xFF); lcd_write_data(x1 8); lcd_write_data(x1 0xFF); lcd_write_cmd(0x2B); // 设置行地址 lcd_write_data(y0 8); lcd_write_data(y0 0xFF); lcd_write_data(y1 8); lcd_write_data(y1 0xFF); lcd_write_cmd(0x2C); // RAMWR开始写显存 LCD_DC_HIGH(); for (i 0; i (uint32_t)((x1 - x0 1) * (y1 - y0 1)); i) lcd_spi_write16(color); // 先高字节后低字节 }lcd_spi_write16要按大端序先发高字节再发低字节ST7789V 的显存写时序就是这样要求的。如果用lcd_spi_write(color 0xFF)先发低字节全屏会出现水平条纹或颜色错位。3.2 蓝牙串口接收的环形缓冲区设计蓝牙模块把手机发来的数据通过串口逐字节送给 STM32如果主循环里用轮询接收屏幕刷新时会丢帧。手表源码里最常见的方案是环形缓冲区让 USART2 中断把字节放进去主循环在空闲时取出来解析。#define BT_RX_BUF_SIZE 256 static uint8_t bt_rx_buf[BT_RX_BUF_SIZE]; static volatile uint16_t bt_rx_head 0; static volatile uint16_t bt_rx_tail 0; void USART2_IRQHandler(void) { if (USART_GetITStatus(USART2, USART_IT_RXNE)) { bt_rx_buf[bt_rx_head] USART_ReceiveData(USART2); bt_rx_head (bt_rx_head 1) % BT_RX_BUF_SIZE; USART_ClearITPendingBit(USART2, USART_IT_RXNE); } } uint8_t bt_poll_byte(void) { uint8_t ch; if (bt_rx_head bt_rx_tail) return 0; ch bt_rx_buf[bt_rx_tail]; bt_rx_tail (bt_rx_tail 1) % BT_RX_BUF_SIZE; return ch; }中断里只写bt_rx_head主循环只读bt_rx_tail两个指针不会同时被修改所以不需要临界区保护。缓冲区取 256 字节是因为手表控制指令和状态上报都很短如果以后要支持 OTA 或大批量数据这个方案会被高速填满应改成 DMA 环形队列让 USART2 的 DMA 搬运直接写内存。3.3 命令解析与 UI 联动光有字节流还不够还要定义协议。简单做法手机 App 发送以\r\n结尾的文本命令STM32 收到完整一行再解析而不逐字节判断。#define CMD_TIME TIME #define CMD_NOTIFY NOTICE void bt_handle_packet(uint8_t *pkt, uint16_t len) { if (len 6) return; pkt[len] \0; if (strncmp((char *)pkt, CMD_TIME, 4) 0 pkt[4] :) { lcd_draw_string(0, 16, (char *)pkt 5, COLOR_BLACK); // 刷新时间区 } else if (strncmp((char *)pkt, CMD_NOTIFY, 6) 0) { lcd_draw_notify((char *)pkt 7); // 通知条 } }这一段要说明先用\r\n分帧再把命令名和参数分开。直接对USART2收到的每个字节调用lcd_draw_string会让屏幕闪烁因为一个字符串要多少帧才能收完。注意pkt[len] \0前要确保len小于缓冲区容量否则会越界写。4. HC-05 蓝牙配对、自动回连与测距参数的实战设置4.1 用 AT 指令把 HC-05 配置成手表端从机HC-05 上电前按住模块上的按钮再上电会进入 AT 模式此时模块的 RXD 接 USB-TTL 的 TXVCC 接 5VGND 共地。默认波特率常见是 38400。用 Linux 串口终端逐条发送stty -F /dev/ttyUSB0 38400 cs8 -cstopb -parenb echo -e ATNAMEWatch\r\n /dev/ttyUSB0 echo -e ATPSWD8888\r\n /dev/ttyUSB0 echo -e ATROLE0\r\n /dev/ttyUSB0 echo -e ATUART115200,0,0\r\n /dev/ttyUSB0完成下面几个关键配置目标AT 指令说明设备名称ATNAMEWatch手机扫描时显示的名称配对码ATPSWD8888手机配对时输入的 PIN角色ATROLE00从机1主机配对模式ATCMODE11允许任意设备连接0仅绑定的地址串口参数ATUART115200,0,0波特率、停止位、校验位参数说明ATCMODE1适合开发阶段任意手机都能连量产建议改成ATCMODE0并配合ATBIND手机蓝牙地址防止附近其他手机抢占连接。ATUART里的后两位是停止位和校验位0,0表示 1 位停止位、无校验必须与固件里的 8N1 保持一致。提示HC-05 对 AT 指令的响应是OK\r\n或ERROR(0)\r\n。如果发送后没反应先确认 KEY 或 EN 引脚是否已经拉到 3.3V部分模块只有在上电时检测该引脚才会进入 AT 模式运行中再拉高无效。4.2 自动连接和连接状态监测手表从机模式是被手机连接的所以“自动连接”通常由 App 侧完成。固件需要做的是准确知道当前是否已连接以便 UI 显示连接状态。HC-05 的 STATE 引脚空闲时为低电平配对后为高电平可以把它接到 STM32 的一个 GPIO 上void bt_monitor_task(void) { static uint8_t last_state 0xFF; uint8_t s GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_12); if (s ! last_state) { if (s Bit_SET) lcd_draw_string(0, 8, Connected, COLOR_GREEN); else lcd_draw_string(0, 8, Disconnected, COLOR_RED); last_state s; } }如果板子上没接 STATE 引脚也可以在应用层定时发ATSTATE?查询但查询指令会占用串口并且 HC-05 在透传模式下不一定会响应 AT 指令取决于模块固件版本。所以最可靠的还是硬件状态引脚这也是标题里那份 ALTIUM 原理图通常会把蓝牙模块状态脚引出来的原因。4.3 蓝牙测距的 RSSI 模型和信号阈值蓝牙测距在手表里的典型用途是靠近解锁或离开锁屏。HC-05 模块本身不暴露 RSSI常见做法是让手机 App 读取蓝牙 RSSI再把等级通过串口回传给 STM32F103VET6。Android 的BluetoothDevice接口可以直接拿到extraRSSIiOS 对经典蓝牙 SPP 的 RSSI 支持有限所以这一功能通常只在 Android App 端做。常见信号衰减模型是d 10^((A - RSSI) / (10 * n))其中 A 是 1 米处平均 RSSI一般在 -55dBm-62dBmn 是环境衰减指数空旷场地取 2.0室内有遮挡取 2.53.5。这个公式算出的距离波动很大手表上不适合显示精确米数更适合映射成“近/中/远”三档RSSI 大于 -50dBm靠近点亮屏幕。RSSI 在 -65dBm 到 -50dBm正常距离保持显示。RSSI 小于 -65dBm认为离开停显并进入低功耗。实际调试时可以在 App 里持续打印 RSSI 原始值再调阈值。注意 RSSI 受蓝牙模块天线和外壳影响每版硬件都要重新标定不要照抄网上的阈值。5. HC-05 连不上、Altium 异形封装和网盘源码落地的三个技巧5.1 HC-05 蓝牙模块连接不上的排查顺序如果手机搜不到设备先看模块 LED慢闪表示未连接快闪通常是进入 AT 模式或正在配对常亮表示已连接。搜得到但连不上优先查ATPSWD和ATCMODE。连上但发数据没反应检查 RX/TX 是否交叉、模块波特率是否与固件一致。用 USB-TTL 单独测模块之前把模块的 KEY 引脚悬空或接 GND确保它不在 AT 模式。5.2 Altium Designer 异形封装与拼板手表外壳多是圆形PCB 板框不能是默认矩形。在 Altium Designer 里用 Design Board Shape 重新绘制画弧线时按 ShiftSpace 切换走线模式。异形封装比如焊盘非矩形可以在 PCB Library 里放置圆弧和自定义焊盘记得在 Rules 里把 Solder Mask Expansion 设置到封装属性一致。拼板时用 V-cut 适合直线边圆弧边用邮票孔拼板发布 Gerber 前把拼板文件单独输出。5.3 让网盘下载的源码在本地跑起来从网盘下载的 ALTIUM 工程和固件源码经常遇到三个问题原理图库文件缺失、编译器版本不一致、端口配置对不上自己板子。打开 Altium 工程后如果卡顿或经常崩溃先清理缓存在 Preferences 里找到缓存管理并清除或者直接删掉用户目录下 Altium 的 Cache 文件夹注意删除前备份工程。固件源码如果是用老版本标准外设库写的先确认system_stm32f10x.c里的时钟配置和你的晶振一致常见的 8MHz 外部晶振对应HSE_VALUE8000000。最后检查芯片型号和引脚映射再做第一次下载。本文还有配套的精品资源点击获取
返回列表