1. 硬件准备与环境搭建第一次接触STM32和串口屏的时候我也被各种接线和配置搞得晕头转向。后来发现只要把硬件环境搭建好后面的工作就会顺利很多。这里我以STM32F103C8T6和陶晶驰T0系列串口屏为例手把手带你完成准备工作。核心硬件清单STM32F103C8T6开发板俗称蓝莓派陶晶驰T0系列2.4寸串口屏杜邦线若干建议使用彩色线方便区分USB转TTL模块用于调试5V/2A电源适配器注意串口屏的供电很关键电流不足会导致屏幕闪烁或无法正常工作。我实测用电脑USB口供电时经常出问题后来改用独立电源就稳定了。接线其实特别简单记住三线法就行串口屏的5V接STM32的5V串口屏的TX接STM32的PA10(RX)串口屏的RX接STM32的PA9(TX)串口屏的GND接STM32的GND这里有个坑我踩过有些开发板的PA9/PA10被USB接口占用了如果发现串口通信异常可以尝试改用其他串口比如USART2PA2/PA3。2. 串口通信配置详解串口配置是项目的核心我花了三天时间才搞明白各种参数的含义。下面这段配置代码是我优化过的版本加入了详细的注释void uart_init(u32 bound){ // 时钟配置 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); // GPIO初始化 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; // TX引脚 GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; // 复用推挽输出 GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_10; // RX引脚 GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; // 浮空输入 GPIO_Init(GPIOA, GPIO_InitStructure); // 中断配置 NVIC_InitTypeDef NVIC_InitStructure; NVIC_InitStructure.NVIC_IRQChannel USART1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority 1; NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStructure); // 串口参数设置 USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate bound; // 波特率 USART_InitStructure.USART_WordLength USART_WordLength_8b; // 8位数据 USART_InitStructure.USART_StopBits USART_StopBits_1; // 1位停止位 USART_InitStructure.USART_Parity USART_Parity_No; // 无校验 USART_InitStructure.USART_Mode USART_Mode_Rx | USART_Mode_Tx; // 收发模式 USART_Init(USART1, USART_InitStructure); USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // 开启接收中断 USART_Cmd(USART1, ENABLE); // 使能串口 }实际项目中我发现有几个关键点需要注意波特率一定要和屏幕保持一致通常是9600或115200中断优先级设置不当会导致系统卡死GPIO模式配置错误是最常见的通信失败原因3. 串口屏指令封装技巧直接发送原始指令既麻烦又容易出错我总结了一套高效的指令封装方法。先看最基本的发送函数// 发送字符串指令 void HMISendStr(const char *cmd) { while(*cmd) { USART_SendData(USART1, *cmd); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); } // 发送结束符 USART_SendData(USART1, 0xFF); USART_SendData(USART1, 0xFF); USART_SendData(USART1, 0xFF); while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) RESET); }基于这个基础函数我们可以封装更高级的功能// 清屏并设置背景色 void HMIClearScreen(uint16_t color) { char cmd[20]; sprintf(cmd, cls %s, color RED ? RED : color GREEN ? GREEN : color BLUE ? BLUE : BLACK); HMISendStr(cmd); } // 设置文本控件内容 void HMISetText(uint8_t id, const char *text) { char cmd[50]; sprintf(cmd, t%d.txt\%s\, id, text); HMISendStr(cmd); } // 页面切换 void HMIGotoPage(uint8_t page) { char cmd[10]; sprintf(cmd, page %d, page); HMISendStr(cmd); }在实际项目中我建议把常用指令都封装成函数这样主程序会非常简洁HMIClearScreen(BLUE); HMISetText(0, 温度:25.6℃); HMISetText(1, 湿度:60%);4. 完整项目实战演练现在我们把所有知识点串联起来实现一个完整的温湿度监控界面。假设我们已经用DHT11传感器获取了数据重点看HMI部分的实现。工程结构project/ ├── drivers/ │ ├── uart.c │ └── hmi.c ├── inc/ │ ├── uart.h │ └── hmi.h └── main.cmain.c中的关键代码int main(void) { // 初始化系统 SystemInit(); delay_init(); uart_init(9600); DHT11_Init(); // 初始化HMI界面 HMIClearScreen(WHITE); HMISetText(0, 智能环境监测系统); HMISetText(1, 温度:--.-℃); HMISetText(2, 湿度:--%); HMIDrawLine(10, 40, 230, 40, BLUE); // 画分隔线 while(1) { if(DHT11_Read()) { char buf[20]; // 更新温度显示 sprintf(buf, 温度:%.1f℃, DHT11_GetTemp()); HMISetText(1, buf); // 更新湿度显示 sprintf(buf, 湿度:%d%%, DHT11_GetHum()); HMISetText(2, buf); } delay_ms(2000); // 2秒刷新一次 } }调试技巧先用串口助手测试指令是否正确添加调试打印确认数据发送时机使用逻辑分析仪抓取通信波形遇到问题时先检查电源稳定性我在这部分实现时遇到过一个典型问题屏幕偶尔会显示乱码。后来发现是因为连续发送指令太快解决方案是在关键指令之间添加10ms的延时。