1. 为什么我选STM32F103C8T6加淘晶驰T1这套组合搞嵌入式的人手里多少都攥着几块STM32F103C8T6最小系统板这块蓝板子几乎成了入门和做小项目的标配。我这次要做的是把采集到的数据实时显示到一块淘晶驰T1串口屏上做一个能看、能改、能交互的小型数据可视化终端。说白了就是让单片机把传感器数据算好通过串口丢给屏幕屏幕负责把数字、曲线、进度条漂漂亮亮地画出来同时屏幕上按个按钮单片机那边还能收到指令去执行动作。这套方案解决的核心问题很直接很多场景下用电脑看数据太笨重用OLED屏又只能显示几行字而串口屏自带字库、图片、触摸控件开发效率高显示效果也拿得出手。STM32F103C8T6负责逻辑和采集淘晶驰T1负责呈现和交互两者通过UART串口通信分工明确。适合谁看刚学完STM32串口收发、想找个综合项目练手的同学做过一些裸屏驱动、被字库和UI折磨过的开发者还有需要快速搭一个带触摸交互的数据显示终端的工程师。哪怕你之前没碰过串口屏跟着走一遍也能把整套链路跑通。我先把结论放前面这套联调的关键不在STM32端而在串口屏的控件命名和指令格式。很多人卡住是因为屏幕那边控件名写错一个字母或者指令末尾忘了加三个0xFF导致单片机发出去的数据石沉大海。下面我把整个设计思路、硬件连接、代码实现和踩坑记录完整拆开讲。2. 整体方案设计与选型背后的考量2.1 主控为什么是STM32F103C8T6STM32F103C8T6这颗芯片72MHz主频、64KB Flash、20KB SRAM带3个USART、2个SPI、2个I2C对于这种“采集串口通信少量逻辑”的任务来说绰绰有余。我选它主要看三点一是资料多标准库、HAL库、寄存器版本随便挑出问题好查二是价格便宜最小系统板十几块钱坏了不心疼三是引脚够用PA9/PA10做USART1PA2/PA3做USART2一路接屏幕一路留调试或者接其他模块互不打架。这里要提一句市面上有国产替代的F103C8T6引脚和寄存器基本兼容但个别批次的Flash容量标注和实际有出入做量产要留意。自己做项目练手无所谓我手上这块就是普通版本跑起来很稳。2.2 显示端为什么选淘晶驰T1串口屏淘晶驰T1属于入门级串口屏尺寸常见有2.4寸、2.8寸、3.5寸分辨率从320x240到480x320不等。它最大的好处是“所见即所得”在电脑端的USART HMI软件里拖控件、设属性、下载工程屏幕自己就能显示单片机只需要发指令去改变量。相比自己用SPI驱动TFT再移植字库省下的时间不是一点半点。它和单片机之间的协议是自定义的ASCII指令比如改变文本控件txt0的内容发“txt0.txtHello\””加上三个0xFF结束符就行。触摸事件也会以指令形式回传比如按下b0按钮屏幕发“b0 1”加结束符。这种设计让单片机端代码极其简单不用解析复杂协议字符串拼接就能搞定。2.3 通信方式与波特率选择两者之间用UART异步串口TTL电平直连。STM32的TX接屏幕的RXSTM32的RX接屏幕的TX共地。波特率我选115200原因是数据量不大但要求响应快115200在F103上用72MHz主频分频很准误差小。屏幕端在工程设置里把波特率也改成115200两边必须一致否则收到的全是乱码。有人问要不要加电平转换淘晶驰T1的串口是3.3V TTL电平STM32F103的IO也是3.3V直接连就行不用MAX232那种电平转换芯片。但如果你用的是5V的单片机那就得加转换不然可能烧屏幕。2.4 数据可视化的实现层次“数据可视化”这个词听起来大落到这个项目上其实分三层第一层是数值显示把温度、电压这些数字填到文本控件里第二层是图形化用进度条控件显示百分比用波形控件画实时曲线第三层是交互反馈触摸按钮改变单片机的工作模式屏幕上的指示灯跟着变。这三层我都做了后面会分别讲实现方法。3. 硬件连接与工程配置的实操细节3.1 硬件清单与接线表先把要用的东西列清楚避免做到一半发现少东西。器件型号/规格数量备注主控板STM32F103C8T6最小系统板1带8MHz晶振和复位电路串口屏淘晶驰T12.8寸1分辨率320x240下载器ST-Link V21用于烧录STM32程序串口线4Pin杜邦线若干母对母电源5V USB供电1屏幕和主控可共用接线就四根STM32的5V接屏幕的5VGND接GNDPA9USART1_TX接屏幕的RXPA10USART1_RX接屏幕的TX。注意TX和RX要交叉我见过有人TX接TX调了半天以为代码有问题其实是线接错了。提示屏幕的5V和GND最好从主控板取这样上电顺序一致避免屏幕先上电、主控后上电时串口状态不确定导致误触发。3.2 STM32端USART1的配置我用标准库来配因为F103的标准库资料最全寄存器操作也直观。USART1挂在APB2总线上时钟72MHz。配置步骤开GPIOA和USART1时钟PA9配成复用推挽输出PA10配成浮空输入USART1设成115200、8位数据、1位停止、无校验、无硬件流控最后使能收发和USART。关键代码片段如下void USART1_Init(u32 bound) { GPIO_InitTypeDef GPIO_InitStructure; USART_InitTypeDef USART_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); 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); 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_Cmd(USART1, ENABLE); }发送单字节和发送字符串的函数也要写发送字符串时我习惯用指针遍历直到遇到‘\0’。void USART1_SendByte(u8 data) { while(USART_GetFlagStatus(USART1, USART_FLAG_TC) RESET); USART_SendData(USART1, data); } void USART1_SendString(char *str) { while(*str) { USART1_SendByte(*str); } }3.3 淘晶驰T1工程的新建与控件命名打开USART HMI软件新建工程选对型号和分辨率。然后在页面上拖控件。我建了这几个一个文本控件命名t0用来显示温度一个文本控件t1显示电压一个进度条j0显示电量百分比一个波形控件s0画实时曲线两个按钮b0和b1分别控制LED开关。控件命名有讲究名字只能用字母和数字不能有中文和特殊符号而且一旦下载到屏幕里单片机就是靠这个名字去寻址的。我建议命名时带点含义比如t_temp、j_bat比t0、j0好记后期改起来不容易乱。但要注意名字长度别太长屏幕那边有长度限制。3.4 屏幕工程下载与波特率设置在软件里点“下载”选对串口号把工程烧进屏幕。下载完成后屏幕会重启显示你设计的界面。然后在工程的“设置”里把波特率改成115200和STM32一致。这一步很多人忘结果屏幕默认9600单片机发115200两边对不上屏幕上什么都不动。注意每次修改工程重新下载后屏幕里的变量会复位如果单片机在持续发数据下载瞬间可能收到半截指令建议下载时先断开单片机。4. 串口屏指令格式与STM32端代码实现4.1 淘晶驰指令的基本格式淘晶驰的指令是ASCII字符串格式为“控件名.属性值”末尾必须跟三个0xFF作为结束符。比如给文本控件t0赋值25.6发的是t0.txt25.6后面跟0xFF 0xFF 0xFF。注意引号是英文引号值如果是数字可以不加引号但加了也没错。我习惯文本类都加引号数字类不加。触摸事件回传的格式是“控件名 事件类型”比如按下b0回传“b0 1”后面同样跟三个0xFF。事件类型1表示按下0表示弹起。4.2 封装一个发送指令的函数为了代码整洁我封装了一个函数传入控件名、属性和值自动拼出完整指令并加上结束符。void HMI_SendCmd(char *obj, char *attr, char *value) { USART1_SendString(obj); USART1_SendString(.); USART1_SendString(attr); USART1_SendString(); USART1_SendString(value); USART1_SendByte(0xFF); USART1_SendByte(0xFF); USART1_SendByte(0xFF); }调用时比如设置t0的文本为“25.6”HMI_SendCmd(t0, txt, \25.6\);这里值带了转义引号因为C语言里字符串本身要用双引号里面的引号得转义。如果你嫌麻烦也可以直接拼字符串但封装成函数后改起来方便。4.3 数值转字符串的处理单片机采集到的温度、电压是浮点数要转成字符串才能发给屏幕。标准库没有sprintf的浮点支持默认精简版我一般用整数放大法比如温度25.6先乘10变成256再拆成整数部分和小数部分拼字符串。void FloatToString(float val, char *buf) { int int_part (int)val; int dec_part (int)((val - int_part) * 10); if(dec_part 0) dec_part -dec_part; sprintf(buf, %d.%d, int_part, dec_part); }这样避免了浮点sprintf的坑也省Flash空间。实测下来精度够用显示一位小数足够。4.4 波形控件的特殊处理波形控件s0要显示实时曲线指令格式和文本不同。淘晶驰的波形控件用“add”指令往里面加数据点格式是“s0.add值”值范围0到255。比如HMI_SendCmd(s0, add, 128);每加一个点曲线就往左移一格。我定时器里每100ms加一个点这样曲线滚动速度合适。注意值要限制在0到255之间超了屏幕可能不认。4.5 触摸事件的接收与解析STM32端要开USART1接收中断把收到的字节存到缓冲区检测到连续三个0xFF就认为一帧结束然后解析缓冲区内容。解析时判断开头是不是“b0”或“b1”是的话执行对应动作。void USART1_IRQHandler(void) { u8 res; if(USART_GetITStatus(USART1, USART_IT_RXNE) ! RESET) { res USART_ReceiveData(USART1); // 存入缓冲区检测0xFF // ... USART_ClearITPendingBit(USART1, USART_IT_RXNE); } }缓冲区我开了64字节足够放一帧指令。解析时用strstr找“b0”找到就翻转LED同时回发指令让屏幕上的指示灯变色。5. 数据可视化界面的搭建与联调过程5.1 界面布局设计屏幕就320x240空间有限我这样布局顶部一行放两个文本控件左边t0显示温度右边t1显示电压中间放波形控件s0占大半屏底部左边放进度条j0右边放两个按钮b0和b1。字体用默认的字号16颜色白底黑字对比度高阳光下也能看清。进度条j0设了背景色和前景色前景色用绿色值从0到100。波形控件s0设了网格线和背景色看起来像示波器。5.2 联调第一步验证通信链路代码烧进去之前我先用USB转TTL模块单独连屏幕用电脑串口助手发指令确认屏幕能正确响应。发“t0.txttest\””加三个0xFF屏幕上t0立刻变成test说明屏幕端没问题。然后再把STM32接上发同样的指令如果屏幕也变说明STM32端发送正常。这一步很关键能把问题定位在发送端还是接收端。我见过有人直接上整套结果屏幕不亮不知道是线的问题、代码的问题还是屏幕工程的问题排查起来费劲。5.3 联调第二步触摸回传验证屏幕发指令给STM32我用串口助手看STM32收到的数据。按下b0串口助手应该显示“b0 1”加三个0xFF的十六进制。如果收到乱码检查波特率如果收不到检查TX/RX是不是接反了。确认能收到后在中断里加解析逻辑收到b0就翻转PC13的LED最小系统板上自带的LED。看到LED跟着按钮动这条链路就通了。5.4 联调第三步实时数据刷新我让定时器2每100ms触发一次在中断里模拟采集温度用ADC读内部温度传感器或者随便给个变化的值转成字符串发给屏幕同时往波形控件加一个点。屏幕上就能看到数字在跳曲线在滚。这里要注意发送频率。100ms发一次一次几十字节115200波特率下完全够用。如果发太快比如10ms一次屏幕可能处理不过来出现卡顿。我实测50ms一次也很流畅但没必要。5.5 完整主循环与中断配合主循环里我做了两件事一是检测按键如果有物理按键二是处理屏幕发来的指令队列。中断负责收数据和定时发送。整体架构是“中断收、中断发、主循环处理逻辑”这样实时性好也不会阻塞。int main(void) { USART1_Init(115200); TIM2_Init(99, 7199); // 100ms LED_Init(); NVIC_Config(); HMI_SendCmd(t0, txt, \Ready\); while(1) { // 处理接收到的指令 // 其他逻辑 } }6. 常见问题排查与避坑经验实录6.1 屏幕不响应指令的排查顺序遇到屏幕没反应按这个顺序查第一波特率两边是否一致第二TX/RX是否交叉连接第三指令末尾三个0xFF是否发全第四控件名是否和屏幕工程里完全一致大小写敏感第五屏幕工程是否下载成功。我踩过的坑是控件名在屏幕里叫“t0”代码里写成“T0”结果怎么都不动查了半天。6.2 收到乱码或半截指令乱码通常是波特率不对或者晶振不起振。F103最小系统板如果外部晶振没焊好系统会切到内部RC主频变成8MHz分频出来的波特率就偏了。用示波器看PA9波形或者直接量晶振引脚有没有起振。半截指令一般是发送太快屏幕缓冲区溢出降低发送频率或者加延时能解决。6.3 触摸事件误触发有时候没按按钮屏幕却回传了按下事件。这可能是屏幕工程里按钮的“触摸效果”设成了“持续”或者串口干扰。我在按钮属性里把触摸效果改成“瞬变”并且给串口线加了磁环误触发就没了。6.4 波形控件不显示曲线波形控件加数据点值必须在0到255之间。我一开始传了温度值25.6屏幕不认因为带小数点。后来改成传整数把温度映射到0到255范围曲线就出来了。映射公式value (int)((temp - min) / (max - min) * 255)。6.5 常见问题速查表现象可能原因解决方法屏幕无显示供电不足单独给屏幕5V供电指令无响应波特率不一致两边都设115200收到乱码晶振未起振检查晶振和负载电容触摸无回传TX/RX接反交叉连接曲线不滚动发送频率太低提高到100ms一次数值显示不全字符串超长控件宽度调大或缩短文本6.6 几个提升稳定性的小技巧第一串口发送前先判断TC标志确保上一个字节发完再发下一个不然会丢字节。第二接收中断里不要做耗时操作把数据存缓冲区主循环再解析。第三屏幕工程里把不用的控件删掉减少内存占用。第四给STM32的串口引脚加TVS管防止静电打坏。第五长期运行的话定时器里加个看门狗喂狗防止死机。7. 代码组织与后续扩展方向7.1 工程文件结构我把代码分成几个文件main.c放主循环和初始化usart.c放串口配置和收发函数hmi.c放屏幕指令封装timer.c放定时器配置led.c放LED控制。这样结构清晰改哪部分找哪个文件。头文件里用extern声明全局函数避免重复定义。7.2 后续可以加的功能这套框架搭好后扩展很容易。比如加个DHT11温湿度传感器把湿度也显示到屏幕上加个继电器屏幕上按按钮控制继电器开关加个SD卡把数据存下来甚至加个ESP8266把数据传到手机上看。串口屏那边只需要加控件和对应指令STM32端加采集和发送逻辑就行。7.3 关于国产替代芯片的兼容性前面提到国产替代的F103C8T6我实测过几块标准库代码直接烧进去能跑串口、定时器、GPIO都正常。但要注意有些替代片的ADC精度和内部温度传感器有偏差如果做精密采集建议外接基准或者用外部传感器。做显示和交互这种数字逻辑完全没问题。7.4 最后分享一个调试习惯我调试串口通信时习惯在STM32端加一个“回环”功能收到什么就发回什么。这样用串口助手发指令能立刻看到STM32收到了什么判断是发送问题还是接收问题。这个习惯帮我省了很多时间你也可以试试。整套联调下来最深的体会是串口屏的难点不在技术而在细心。控件名、波特率、结束符任何一个细节错了现象都是“没反应”但原因各不相同。把排查顺序理清楚一个个排除问题总能解决。代码我已经跑通屏幕上的数字跳得稳稳的曲线滚得顺顺的按钮按下去LED跟着亮这套东西拿去做课程设计或者小产品原型完全够用。
