☰
STM32F103C8T6与淘晶驰T1串口屏联调实战:从接线到数据可视化
2026/9/27 23:26:15 网站建设 项目流程

1. 项目缘起与整体设计思路

STM32F103C8T6这块芯片在嵌入式圈子里算是老熟人了,价格便宜、资料丰富、性能够用,拿来做工控面板、仪器仪表、智能家居中控都挺合适。但很多朋友卡在同一个地方:数据算出来了,怎么直观地显示出来?用OLED吧,屏幕小、信息量有限;用TFT彩屏加触摸吧,GUI移植又是个大工程。串口屏就是在这个夹缝里杀出来的一条路——屏幕自己带主控和字库,MCU只需要通过串口发指令,就能显示数字、文字、曲线、进度条,开发效率极高。

这次我拿淘晶驰T1系列串口屏和STM32F103C8T6最小系统板做了一次完整的联调,从接线、供电、电平匹配,到指令协议、变量绑定、数据刷新,再到实际调试中踩到的坑,全部走了一遍。淘晶驰T1这块屏性价比不错,2.4寸到7寸都有,电阻触摸和电容触摸可选,配套的USART HMI上位机软件可以拖拽式做界面,不需要写一行GUI代码。STM32这边用标准库或者HAL库都行,我用的是标准库,因为F103C8T6的资源跑标准库更轻快,Flash和RAM都省着用。

整个项目的核心目标很明确:STM32采集数据(比如温度、电压、转速),通过串口把数据推送到T1屏上显示,同时屏幕上的触摸按钮能把控制指令回传给STM32。这个双向通信链路打通了,后面做任何数据可视化项目都是在这个骨架上添砖加瓦。

适合谁来参考?如果你手头有STM32F103C8T6最小系统板,或者正在选型串口屏做项目,又或者你之前只用过OLED、LCD1602,想升级到彩色触摸屏,这篇内容应该能帮你省下不少查资料和试错的时间。我尽量把每个环节的“为什么”讲清楚,不只是给代码,而是让你理解背后的逻辑,这样换一块屏、换一个MCU也能快速迁移。

2. 硬件选型与连接细节拆解

2.1 为什么选STM32F103C8T6加淘晶驰T1这个组合

STM32F103C8T6是ARM Cortex-M3内核,72MHz主频,64KB Flash,20KB RAM,封装LQFP48,引脚够用又不浪费。市面上最小系统板大概十几块钱,国产替代型号也很多,比如GD32F103、APM32F103,引脚和寄存器基本兼容,标准库代码改改就能跑。对于串口屏这种应用,MCU的负担很轻,主要工作就是格式化字符串、发串口数据、解析触摸事件,F103C8T6绰绰有余。

淘晶驰T1系列串口屏内部集成了显示驱动和触摸控制器,对外只暴露一个串口接口,默认波特率9600,可配置到115200甚至更高。屏幕的界面工程在PC端用USART HMI软件设计,编译后通过SD卡或者串口下载到屏幕里。运行时,屏幕等待MCU发来的指令,比如“把t0文本框的内容改成25.6”,或者“切换到页面2”。触摸事件发生时,屏幕主动往串口发数据,比如“页面0上的b0按钮被按下了”。

这个组合最大的好处是解耦:界面设计归界面设计,业务逻辑归业务逻辑。你不需要在MCU里跑LVGL或者emWin,省下了大量Flash和RAM,也省下了移植GUI的时间。对于中小规模的项目,这是性价比最高的方案。

2.2 硬件连接清单与接线表

先列一下我这次用到的物料:

  • STM32F103C8T6最小系统板一块
  • 淘晶驰T1系列串口屏一块(我用的2.4寸电阻触摸,型号T1-24,分辨率320x240)
  • USB转TTL模块一个(CH340或CP2102都行,用于给MCU下载程序和调试)
  • 杜邦线若干
  • 5V电源一个(可以用USB供电,但要注意电流)

接线是第一个容易出问题的地方。串口屏的供电范围一般是5V,而STM32F103C8T6的IO口是3.3V电平。淘晶驰T1的串口引脚在出厂时默认是3.3V TTL电平,但有些批次或者型号可能标注5V耐受。稳妥的做法是:屏幕的VCC接5V,GND接GND,屏幕的TX接MCU的RX,屏幕的RX接MCU的TX。如果屏幕串口电平是5V,而MCU的RX引脚不是5V耐受的,中间需要加电平转换电路,最简单的就是分压电阻或者电平转换模块。

我实测淘晶驰T1的串口电平是3.3V,直接和STM32F103C8T6对接没有问题。但如果你用的是其他品牌的串口屏,一定要先查手册确认电平。PA9和PA10是STM32F103C8T6的USART1引脚,PA9是TX,PA10是RX。接线时注意交叉:MCU的PA9接屏幕的RX,MCU的PA10接屏幕的TX。

串口屏引脚STM32F103C8T6引脚说明
VCC5V屏幕供电,电流约200mA
GNDGND共地,必须接
TXPA10屏幕发,MCU收
RXPA9屏幕收,MCU发

注意:屏幕的背光电流较大,如果直接用最小系统板上的5V引脚供电,要确认板子上的稳压芯片能扛住。有些最小系统板的5V是从USB直接过来的,问题不大;但如果是从3.3V稳压器取电,可能会烧稳压器。

2.3 供电与电平匹配的实操经验

供电这块我踩过一个坑:一开始用ST-Link的3.3V给整个系统供电,屏幕背光一亮,ST-Link就掉线了。原因是屏幕背光瞬间电流超过了ST-Link的供电能力。后来换成独立的5V USB供电,MCU和屏幕分别从同一个5V源取电,问题解决。

电平匹配方面,虽然淘晶驰T1标称3.3V TTL,但我在示波器上看过它的TX波形,高电平大概3.2V,低电平接近0V,和STM32的3.3V逻辑完全兼容。如果你不放心,可以在TX线上串一个1k电阻,RX线上用10k和20k电阻分压,把5V降到3.3V。但大多数情况下直接连就行。

还有一个细节:串口屏的GND一定要和MCU的GND接在一起。我见过有人只接TX、RX和VCC,不接GND,结果通信时好时坏,因为电平没有参考地,逻辑判断会漂移。

3. 串口屏界面设计与变量绑定

3.1 USART HMI软件快速上手

淘晶驰的USART HMI软件是免安装的,下载后直接运行。新建工程时选择对应的屏幕型号和方向,然后就可以拖控件了。常用的控件有:文本(t)、数字(n)、按钮(b)、进度条(j)、波形图(s)、双状态按钮(bt)等。

我这次做了一个简单的数据监控界面:页面上放三个数字控件,分别显示温度、电压、转速;放一个波形图控件显示温度趋势;放两个按钮,一个控制LED亮灭,一个切换页面。控件命名有讲究,比如温度数字控件命名为t0,电压命名为t1,转速命名为t2,波形图命名为s0,按钮命名为b0和b1。这些名字在MCU代码里会用到。

界面设计完后,点击“编译”按钮,软件会生成一个.tft文件。然后通过SD卡或者串口下载到屏幕里。串口下载时,屏幕需要进入下载模式,一般是上电时按住某个区域或者通过软件发送下载指令。具体操作看屏幕型号的说明书。

3.2 变量绑定与指令格式解析

淘晶驰串口屏的指令格式是ASCII字符串,以十六进制0xFF 0xFF 0xFF结尾。比如要让t0显示“25.6”,MCU发送的字符串是:

t0.txt="25.6"

后面跟上三个0xFF。注意,这里的txt是控件的属性名,不同控件属性不同。数字控件用val属性,文本控件用txt属性。比如:

n0.val=256

表示把数字控件n0的值设为256。波形图控件用add指令添加数据点:

add s0,0,256

表示往波形图s0的通道0添加一个值为256的数据点。

触摸事件方面,当按钮被按下时,屏幕会往串口发送类似这样的数据:

0x65 0x00 0x01 0x01 0xFF 0xFF 0xFF

其中0x65是触摸事件的起始码,后面跟着页面号、控件号、事件类型。具体协议要看淘晶驰的官方文档,不同系列可能略有差异。我一般会在MCU里写一个解析函数,把触摸事件提取出来,然后根据页面号和控件号执行对应的操作。

提示:串口屏的指令字符串不需要以回车换行结尾,但必须以三个0xFF结尾。很多新手忘记加0xFF,导致屏幕没反应。

3.3 界面设计中的几个实用技巧

第一个技巧:给控件命名时用有意义的缩写,比如temp、volt、speed,而不是t0、t1、t2。虽然名字长一点,但代码可读性高很多。淘晶驰支持较长的控件名,不用担心。

第二个技巧:数字控件的小数位数可以在属性里设置。比如温度要显示一位小数,就把vvs(小数位数)设为1。这样MCU发送整数256,屏幕自动显示25.6,省去了在MCU里做浮点转字符串的麻烦。

第三个技巧:波形图控件的通道数和数据深度要提前规划好。通道数决定了能同时显示几条曲线,数据深度决定了横轴能显示多少个点。我一般设2个通道、100个数据点,够用了。

第四个技巧:按钮的触摸事件可以选择“按下”和“弹起”两种触发方式。如果要做长按功能,可以用“弹起”事件配合定时器判断。如果只是简单开关,用“按下”就行。

4. STM32端代码实现与数据推送

4.1 串口初始化与重定向

STM32F103C8T6的USART1挂在APB2总线上,时钟频率72MHz。初始化时,波特率设为115200,和屏幕保持一致。数据位8位,停止位1位,无校验。标准库的初始化代码如下:

void USART1_Init(u32 bound) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE); // PA9 TX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); // PA10 RX GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); USART_InitStructure.USART_BaudRate = bound; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_Init(USART1, &USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); USART_Cmd(USART1, ENABLE); }

为了能用printf方便地格式化字符串,我重定向了fputc函数:

int fputc(int ch, FILE *f) { while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); USART_SendData(USART1, (u8)ch); return ch; }

这样就能用printf往串口屏发指令了。但要注意,printf默认带缓冲,如果数据量大,可能会阻塞。我一般用sprintf先把字符串格式化到缓冲区,然后用DMA或者中断发送,避免阻塞主循环。

4.2 数据格式化与指令封装

假设我采集到的温度是浮点数25.6,电压是3.3,转速是1500。我需要把它们转成屏幕能识别的指令。淘晶驰的数字控件可以直接接收整数值,小数位数在屏幕端设置。所以温度25.6可以乘以10变成256发送,屏幕端设置一位小数,显示出来就是25.6。

封装一个发送函数:

void HMI_SendString(char *str) { while(*str) { while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); USART_SendData(USART1, *str++); } // 发送结束符 0xFF 0xFF 0xFF while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); USART_SendData(USART1, 0xFF); while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); USART_SendData(USART1, 0xFF); while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); USART_SendData(USART1, 0xFF); }

然后更新温度显示:

char buf[32]; sprintf(buf, "t0.txt=\"%d.%d\"", temp_int, temp_dec); HMI_SendString(buf);

或者用数字控件:

sprintf(buf, "n0.val=%d", temp_int * 10 + temp_dec); HMI_SendString(buf);

波形图添加数据点:

sprintf(buf, "add s0,0,%d", temp_int * 10 + temp_dec); HMI_SendString(buf);

注意:sprintf在标准库中比较占Flash,如果Flash紧张,可以用轻量级的格式化函数,或者手动拼接字符串。F103C8T6有64KB Flash,一般够用,但如果同时跑其他功能,就要注意优化。

4.3 触摸事件解析与响应

串口屏的触摸事件数据通过USART1的RX引脚进来,我在中断里接收,存到一个环形缓冲区,然后在主循环里解析。触摸事件的协议格式如下:

  • 起始码:0x65
  • 页面号:1字节
  • 控件号:1字节
  • 事件类型:1字节(0x01表示按下,0x00表示弹起)
  • 结束码:0xFF 0xFF 0xFF

解析函数:

void HMI_ParseTouch(u8 *data, u8 len) { if(len < 7) return; if(data[0] != 0x65) return; u8 page = data[1]; u8 control = data[2]; u8 event = data[3]; if(page == 0 && control == 0 && event == 0x01) { // 页面0的b0按钮被按下 GPIO_SetBits(GPIOC, GPIO_Pin_13); // 点亮LED } else if(page == 0 && control == 1 && event == 0x01) { // 页面0的b1按钮被按下 GPIO_ResetBits(GPIOC, GPIO_Pin_13); // 熄灭LED } }

实际项目中,我建议把触摸事件和业务逻辑分开,用一个事件队列来传递。中断里只负责把数据存入队列,主循环里再处理。这样避免在中断里做耗时操作,保证系统的实时性。

5. 联调过程中的常见问题与排查技巧

5.1 通信失败排查流程

联调时最常见的问题就是屏幕没反应。我总结了一个排查流程:

第一步,检查供电。用万用表量屏幕的VCC和GND,确认电压在4.5V到5.5V之间。如果电压偏低,屏幕可能不工作或者背光闪烁。

第二步,检查接线。TX和RX是否交叉连接?GND是否共地?杜邦线是否接触不良?我遇到过杜邦线内部断线的情况,外表看不出来,换一根就好了。

第三步,检查波特率。屏幕默认波特率是9600,我代码里设的是115200,如果不一致,通信肯定失败。可以在USART HMI软件里修改屏幕波特率,或者用指令修改。

第四步,用示波器或者逻辑分析仪看波形。如果MCU的TX有波形,但屏幕没反应,可能是屏幕的RX引脚坏了,或者电平不匹配。如果MCU的TX没有波形,那就是代码问题,检查USART初始化是否正确。

第五步,用串口助手直接给屏幕发指令。把USB转TTL模块的TX接屏幕的RX,GND接GND,然后在串口助手里发送t0.txt="test"加三个0xFF,看屏幕有没有反应。如果有反应,说明屏幕没问题,问题在MCU端。

5.2 数据刷新卡顿与丢包处理

当数据刷新频率较高时,比如每10ms更新一次波形图,可能会出现卡顿或者丢包。原因通常是串口发送阻塞了主循环。解决办法有几个:

一是降低刷新频率。波形图不需要每10ms更新一次,100ms更新一次肉眼看起来已经很流畅了。

二是用DMA发送。把要发送的字符串放到DMA缓冲区,启动DMA传输,CPU不用等待。F103C8T6的USART1支持DMA发送,配置起来也不复杂。

三是优化字符串格式化。sprintf比较耗时,可以自己写一个简单的整数转字符串函数,速度会快很多。

四是增加串口波特率。115200不够就上到256000或者512000,但要注意屏幕是否支持。淘晶驰T1系列最高支持到921600,但实际用下来115200最稳定。

5.3 屏幕触摸不灵敏或误触发

电阻触摸屏需要校准。淘晶驰的屏幕出厂时已经校准过,但如果发现触摸位置偏移,可以在USART HMI软件里重新校准。校准方法是进入屏幕的校准模式,依次点击屏幕上的几个十字标记。

误触发通常是因为触摸事件没有去抖。我一般在解析触摸事件后,加一个50ms的延时确认,或者用状态机判断按下和弹起的时间间隔。如果时间太短,就认为是干扰。

另外,屏幕的触摸区域和显示区域可能有偏差,设计界面时按钮不要做得太小,至少40x40像素,方便手指点击。

5.4 常见问题速查表

现象可能原因解决方法
屏幕白屏或黑屏供电不足或背光损坏检查5V供电,测量背光电压
屏幕有显示但无触摸触摸校准丢失或触摸芯片故障重新校准,或更换屏幕
通信无反应波特率不匹配或接线错误统一波特率,检查TX/RX交叉
数据刷新慢串口阻塞或格式化耗时降低刷新率,用DMA发送
触摸事件乱码中断接收丢包或协议解析错误增加缓冲区,检查结束符
波形图不显示通道号或数据格式错误检查add指令的通道号和值范围

提示:淘晶驰的官方论坛和QQ群里有大量案例,遇到问题先搜一下,大概率有人已经踩过同样的坑。

6. 项目扩展与个人实操体会

这套联调框架打通之后,扩展方向很多。比如把温度传感器换成DS18B20或者DHT11,把电压采集换成INA226,把转速采集换成霍尔传感器,数据源变了,但串口屏的显示逻辑基本不用大改。再比如增加历史数据存储功能,用STM32的Flash模拟EEPROM,把关键数据存下来,屏幕切到历史页面时再读出来显示。

我还试过用串口屏做简单的PID参数设置界面,屏幕上放几个按钮和数字输入框,MCU收到参数后更新PID控制器的系数。这样不用重新烧录程序就能调参,现场调试效率高很多。

个人体会最深的一点是:串口屏的指令协议一定要封装成函数,不要到处写printf。我一开始图省事,直接在业务代码里拼字符串,后来界面改版,控件名变了,要改几十处地方。后来封装了HMI_SetText、HMI_SetValue、HMI_AddWave这些函数,界面改版只需要改函数内部的控件名映射,业务代码一行不动。

另一个体会是:屏幕的界面设计要留余量。比如数字控件的位置和大小,要考虑到数字位数变化时不会溢出。我见过有人把温度显示控件做得刚好够两位整数,结果温度超过99度就显示不全了。多留一点空间,或者用右对齐,能避免很多显示问题。

最后分享一个小技巧:在USART HMI软件里,可以给控件设置初始值,这样屏幕刚上电时不会显示空白或者乱码。MCU启动后先发一遍所有控件的初始值,然后再进入正常刷新循环。这样用户体验会好很多,不会看到屏幕闪一下才出数据。

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

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

立即咨询