文章目录
-
- 一、开发背景与核心目标
- 二、开发环境与硬件准备
-
- [2.1 软件环境](#2.1 软件环境)
- [2.2 硬件清单](#2.2 硬件清单)
- [2.3 硬件接线说明](#2.3 硬件接线说明)
- [三、STM32 UART底层配置(基于STM32CubeMX)](#三、STM32 UART底层配置(基于STM32CubeMX))
-
- [3.1 新建工程](#3.1 新建工程)
- [3.2 基础配置](#3.2 基础配置)
- [3.3 工程生成](#3.3 工程生成)
- 四、核心代码编写
-
- [4.1 代码文件结构说明](#4.1 代码文件结构说明)
- [4.2 文件1:usart.h(扩展CubeMX生成的头文件)](#4.2 文件1:usart.h(扩展CubeMX生成的头文件))
- [4.3 文件2:usart.c(扩展CubeMX生成的源文件)](#4.3 文件2:usart.c(扩展CubeMX生成的源文件))
- [4.4 文件3:hmi.h(串口屏指令封装头文件)](#4.4 文件3:hmi.h(串口屏指令封装头文件))
- [4.5 文件4:hmi.c(串口屏指令封装源文件)](#4.5 文件4:hmi.c(串口屏指令封装源文件))
- [4.6 文件5:main.c(主函数,业务逻辑实现)](#4.6 文件5:main.c(主函数,业务逻辑实现))
- 五、串口屏组态配置(迪文DGUS为例)
-
- [5.1 组态软件安装与工程新建](#5.1 组态软件安装与工程新建)
- [5.2 页面与控件配置](#5.2 页面与控件配置)
-
- [5.2.1 页面1(初始页面)配置](#5.2.1 页面1(初始页面)配置)
- [5.2.2 页面2(切换页面)配置](#5.2.2 页面2(切换页面)配置)
- [5.3 工程下载到串口屏](#5.3 工程下载到串口屏)
- 六、调试流程与技巧
-
- [6.1 整体调试流程(Mermaid流程图)](#6.1 整体调试流程(Mermaid流程图))
- [6.2 分步调试技巧](#6.2 分步调试技巧)
-
- [6.2.1 第一步:STM32 UART自收发调试](#6.2.1 第一步:STM32 UART自收发调试)
- [6.2.2 第二步:STM32与串口屏通信调试](#6.2.2 第二步:STM32与串口屏通信调试)
- [6.2.3 第三步:复杂界面调试](#6.2.3 第三步:复杂界面调试)
- 七、复杂HMI界面扩展建议
- 总结
一、开发背景与核心目标
你想要实现基于STM32的串口屏HMI界面开发,核心是掌握STM32 UART通信的底层配置、串口屏指令交互逻辑,以及复杂HMI界面的调试方法。本教程从零基础角度出发,涵盖从硬件接线、STM32工程搭建、代码编写到串口屏配置、联调调试的全流程,确保你能一步步模仿实现可落地的串口屏控制方案。
二、开发环境与硬件准备
2.1 软件环境
- STM32CubeMX(版本:6.9.0+):用于STM32工程初始化配置
- Keil MDK-ARM(版本:5.36+):用于代码编译、下载
- 串口屏组态软件(以迪文DGUS为例,通用逻辑适用于其他串口屏)
- 串口助手(SSCOM):用于调试UART通信
- USB转TTL模块驱动
2.2 硬件清单
- STM32F103C8T6最小系统板(最易获取的入门款)
- 迪文DGUS串口屏(分辨率800*480,通用款)
- USB转TTL模块(CH340/PL2303)
- 杜邦线若干
- 5V电源适配器(给串口屏供电)
2.3 硬件接线说明
| STM32F103 | USB转TTL | 串口屏 | 功能说明 |
|---|---|---|---|
| 3.3V | VCC | - | 给USB转TTL供电(若模块支持3.3V) |
| GND | GND | GND | 共地,必须连接,否则通信乱码 |
| PA9(TX) | RX | RX | STM32发送数据到串口屏/串口助手 |
| PA10(RX) | TX | TX | STM32接收串口屏/串口助手数据 |
| - | - | VCC | 接5V电源(串口屏通常需5V供电) |
注意:STM32的UART电平为3.3V,串口屏若为5V电平,需确认是否需要电平转换;大部分串口屏支持3.3V/5V兼容,可直接连接。
三、STM32 UART底层配置(基于STM32CubeMX)
3.1 新建工程
- 打开STM32CubeMX,点击"New Project"
- 在搜索框输入"STM32F103C8T6",选择对应芯片,点击"Start Project"
- 弹出"MCU Selector"确认框,点击"OK"
3.2 基础配置
- RCC配置:点击"RCC",在"HSE"栏选择"Crystal/Ceramic Resonator"(外部晶振),提供精准时钟
- SYS配置:点击"SYS",在"Debug"栏选择"Serial Wire"(SWD调试,占用引脚少)
- UART配置 :
- 点击"USART1",模式选择"Asynchronous"(异步通信)
- 参数配置(串口屏通用参数):
- 波特率:9600(可根据串口屏配置调整,需一致)
- 数据位:8
- 停止位:1
- 校验位:None
- 流控:None
- 使能"NVIC Settings"中的"USART1 global interrupt"(开启UART中断,用于接收数据)
3.3 工程生成
- 点击"Project Manager",配置:
- Project Name:STM32_UART_HMI
- Project Location:选择保存路径
- Toolchain/IDE:选择"MDK-ARM v5"
- 点击"Code Generator",勾选"Generate peripheral initialization as a pair of '.c/.h' files per peripheral"(模块化生成代码)
- 点击"GENERATE CODE",生成工程后点击"Open Project"打开Keil工程
四、核心代码编写
4.1 代码文件结构说明
本次开发核心代码文件:
usart.h/usart.c:UART底层驱动(CubeMX生成基础代码,需扩展)hmi.h/hmi.c:串口屏指令封装与交互逻辑main.c:主函数,实现业务逻辑与串口屏通信
4.2 文件1:usart.h(扩展CubeMX生成的头文件)
c
#ifndef __USART_H
#define __USART_H
#include "stm32f1xx_hal.h"
/* 宏定义 */
#define USART_BUFFER_SIZE 128 // 串口接收缓冲区大小
/* 全局变量声明 */
extern uint8_t USART1_RxBuffer[USART_BUFFER_SIZE]; // 接收缓冲区
extern uint16_t USART1_RxLen; // 接收数据长度
extern uint8_t USART1_RxFlag; // 接收完成标志
/* 函数声明 */
void MX_USART1_UART_Init(void); // CubeMX生成的初始化函数
void USART1_SendData(uint8_t *pData, uint16_t len); // 串口发送数据函数
void USART1_SendString(char *str); // 串口发送字符串函数
#endif /* __USART_H */
4.3 文件2:usart.c(扩展CubeMX生成的源文件)
c
#include "usart.h"
/* 全局变量定义 */
UART_HandleTypeDef huart1;
uint8_t USART1_RxBuffer[USART_BUFFER_SIZE] = {0};
uint16_t USART1_RxLen = 0;
uint8_t USART1_RxFlag = 0;
/**
* @brief USART1初始化函数(CubeMX生成)
* @param 无
* @retval 无
*/
void MX_USART1_UART_Init(void)
{
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* 开启串口接收中断(空闲中断+字节中断) */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE); // 接收非空中断
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 空闲中断
HAL_UART_Receive_IT(&huart1, USART1_RxBuffer, 1); // 启动中断接收
}
/**
* @brief UART底层初始化(CubeMX生成,如需修改引脚可在此调整)
* @param huart: UART句柄
* @retval 无
*/
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART1)
{
/* USART1时钟使能 */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO配置
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART1中断配置 */
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
}
}
/**
* @brief UART底层反初始化
* @param huart: UART句柄
* @retval 无
*/
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==USART1)
{
__HAL_RCC_USART1_CLK_DISABLE();
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
HAL_NVIC_DisableIRQ(USART1_IRQn);
}
}
/**
* @brief 串口1发送数据(字节数组)
* @param pData: 待发送数据指针
* @param len: 发送长度
* @retval 无
*/
void USART1_SendData(uint8_t *pData, uint16_t len)
{
HAL_UART_Transmit(&huart1, pData, len, 100); // 超时时间100ms
}
/**
* @brief 串口1发送字符串
* @param str: 待发送字符串指针
* @retval 无
*/
void USART1_SendString(char *str)
{
uint16_t len = 0;
while(str[len] != '\0') // 计算字符串长度
{
len++;
}
HAL_UART_Transmit(&huart1, (uint8_t*)str, len, 100);
}
/**
* @brief USART1中断服务函数
* @param 无
* @retval 无
*/
void USART1_IRQHandler(void)
{
uint32_t temp_flag = 0;
uint32_t temp;
temp_flag = __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE); // 获取空闲中断标志
if((temp_flag != RESET))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1); // 清除空闲中断标志
HAL_UART_DMAStop(&huart1); // 停止DMA接收(若使用DMA,此处需保留)
// 获取已接收数据长度
temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
USART1_RxLen = USART_BUFFER_SIZE - temp;
USART1_RxFlag = 1; // 置位接收完成标志
// 重新启动中断接收
HAL_UART_Receive_IT(&huart1, USART1_RxBuffer, USART_BUFFER_SIZE);
}
HAL_UART_IRQHandler(&huart1); // 调用HAL库中断处理函数
}
/**
* @brief 错误处理函数
* @param 无
* @retval 无
*/
void Error_Handler(void)
{
__disable_irq();
while(1)
{
// 错误时可添加指示灯闪烁等提示
}
}
4.4 文件3:hmi.h(串口屏指令封装头文件)
c
#ifndef __HMI_H
#define __HMI_H
#include "usart.h"
/* 串口屏指令宏定义(以迪文DGUS为例) */
// 写寄存器指令格式:0x5A 0xA5 0x05 0x82 寄存器高字节 寄存器低字节 数据高字节 数据低字节 校验和
#define DGUS_WRITE_REG_HEAD 0x5A
#define DGUS_WRITE_REG_TAIL 0xA5
#define DGUS_WRITE_REG_CMD 0x82
/* 常用寄存器地址(根据串口屏组态配置调整) */
#define REG_SCREEN_PAGE 0x0000 // 页面切换寄存器
#define REG_TEXT_DISPLAY 0x0010 // 文本显示寄存器
#define REG_NUM_DISPLAY 0x0020 // 数字显示寄存器
#define REG_BUTTON_STATUS 0x0030 // 按键状态寄存器
/* 函数声明 */
void HMI_SendWriteRegCmd(uint16_t reg_addr, uint16_t data); // 写寄存器指令
void HMI_SwitchScreen(uint16_t page_num); // 切换串口屏页面
void HMI_DisplayText(uint16_t text_id, char *text); // 显示文本
void HMI_DisplayNumber(uint16_t num_id, uint16_t num); // 显示数字
void HMI_ParseRxData(void); // 解析串口屏接收数据
#endif /* __HMI_H */
4.5 文件4:hmi.c(串口屏指令封装源文件)
c
#include "hmi.h"
#include <string.h>
/**
* @brief 计算迪文串口屏指令校验和
* @param buf: 指令数组
* @param len: 指令长度(不含校验和字节)
* @retval 校验和
*/
static uint8_t DGUS_CalcCheckSum(uint8_t *buf, uint16_t len)
{
uint8_t sum = 0;
for(uint16_t i=0; i<len; i++)
{
sum += buf[i];
}
return sum;
}
/**
* @brief 发送写寄存器指令到串口屏
* @param reg_addr: 寄存器地址(16位)
* @param data: 写入的数据(16位)
* @retval 无
*/
void HMI_SendWriteRegCmd(uint16_t reg_addr, uint16_t data)
{
uint8_t cmd_buf[9] = {0}; // 写寄存器指令共9个字节
uint8_t check_sum = 0;
// 填充指令头
cmd_buf[0] = DGUS_WRITE_REG_HEAD;
cmd_buf[1] = DGUS_WRITE_REG_TAIL;
cmd_buf[2] = 0x05; // 数据长度(固定5字节:指令+地址+数据)
// 填充写寄存器指令
cmd_buf[3] = DGUS_WRITE_REG_CMD;
// 填充寄存器地址(高字节在前)
cmd_buf[4] = (reg_addr >> 8) & 0xFF;
cmd_buf[5] = reg_addr & 0xFF;
// 填充写入数据(高字节在前)
cmd_buf[6] = (data >> 8) & 0xFF;
cmd_buf[7] = data & 0xFF;
// 计算校验和
check_sum = DGUS_CalcCheckSum(cmd_buf, 8);
cmd_buf[8] = check_sum;
// 发送指令到串口屏
USART1_SendData(cmd_buf, 9);
}
/**
* @brief 切换串口屏页面
* @param page_num: 页面编号(如0x0001对应第1页)
* @retval 无
*/
void HMI_SwitchScreen(uint16_t page_num)
{
HMI_SendWriteRegCmd(REG_SCREEN_PAGE, page_num);
}
/**
* @brief 显示数字到串口屏指定位置
* @param num_id: 数字显示控件ID(对应寄存器地址偏移)
* @param num: 要显示的数字(0-65535)
* @retval 无
*/
void HMI_DisplayNumber(uint16_t num_id, uint16_t num)
{
uint16_t reg_addr = REG_NUM_DISPLAY + num_id;
HMI_SendWriteRegCmd(reg_addr, num);
}
/**
* @brief 显示文本到串口屏指定位置(简化版,支持固定长度文本)
* @param text_id: 文本显示控件ID(对应寄存器地址偏移)
* @param text: 要显示的字符串(最长16个字符)
* @retval 无
*/
void HMI_DisplayText(uint16_t text_id, char *text)
{
uint8_t text_buf[32] = {0};
uint16_t reg_addr = REG_TEXT_DISPLAY + text_id;
uint16_t text_len = strlen(text);
// 文本指令需按串口屏格式封装(此处简化,实际需根据屏的协议调整)
// 步骤1:先发送文本长度
HMI_SendWriteRegCmd(reg_addr, text_len);
// 步骤2:发送文本内容(逐字节)
for(uint16_t i=0; i<text_len; i++)
{
HMI_SendWriteRegCmd(reg_addr + 1 + i, (uint16_t)text[i]);
}
}
/**
* @brief 解析串口屏发送的接收数据(如按键状态)
* @param 无
* @retval 无
*/
void HMI_ParseRxData(void)
{
if(USART1_RxFlag == 1) // 检测到接收完成
{
// 按键状态解析示例:串口屏按下按键后发送0x5A 0xA5 0x03 0x83 0x00 0x30 0xXX
if(USART1_RxBuffer[0] == 0x5A && USART1_RxBuffer[1] == 0xA5)
{
switch(USART1_RxBuffer[3])
{
case 0x83: // 按键状态指令
switch(USART1_RxBuffer[5])
{
case 0x01: // 按键1按下
HMI_SwitchScreen(0x0002); // 切换到第2页
break;
case 0x02: // 按键2按下
HMI_DisplayNumber(0x0000, 1234); // 显示数字1234
break;
default:
break;
}
break;
default:
break;
}
}
// 清空接收缓冲区和标志
memset(USART1_RxBuffer, 0, USART_BUFFER_SIZE);
USART1_RxLen = 0;
USART1_RxFlag = 0;
}
}
4.6 文件5:main.c(主函数,业务逻辑实现)
c
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "hmi.h"
/* 全局变量 */
uint16_t test_num = 0; // 测试数字,用于循环显示
void SystemClock_Config(void);
int main(void)
{
/* 初始化HAL库 */
HAL_Init();
/* 配置系统时钟(72MHz) */
SystemClock_Config();
/* 初始化所有配置的外设 */
MX_GPIO_Init();
MX_USART1_UART_Init();
/* 初始化串口屏 */
HMI_SwitchScreen(0x0001); // 默认显示第1页
HMI_DisplayText(0x0000, "STM32 HMI Test"); // 显示初始文本
/* 主循环 */
while (1)
{
/* 解析串口屏数据(如按键指令) */
HMI_ParseRxData();
/* 循环更新数字显示(每500ms更新一次) */
test_num++;
if(test_num > 9999)
{
test_num = 0;
}
HMI_DisplayNumber(0x0000, test_num);
HAL_Delay(500); // 延时500ms
}
}
/**
* @brief 系统时钟配置函数
* @param 无
* @retval 无
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* 配置外部高速晶振(HSE) */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* 配置系统时钟源、AHB/APB分频 */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
#ifdef USE_FULL_ASSERT
/**
* @brief 输出断言失败的文件名和行号
* @param file: 断言失败的文件名指针
* @param line: 断言失败的行号
* @retval 无
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* 可添加自定义打印信息,如串口输出断言位置 */
}
#endif /* USE_FULL_ASSERT */
五、串口屏组态配置(迪文DGUS为例)
5.1 组态软件安装与工程新建
- 安装迪文DGUS组态软件(DGUS Tool)
- 打开软件,点击"新建工程",选择对应串口屏型号(如800*480)
- 设置串口参数:波特率9600,数据位8,停止位1,校验位无(与STM32一致)
5.2 页面与控件配置
5.2.1 页面1(初始页面)配置
- 点击"页面管理",新增页面1,命名为"初始页面"
- 添加"文本显示控件":
- 位置:X=100, Y=100,宽度=200,高度=50
- 配置:关联寄存器地址0x0010(对应STM32的REG_TEXT_DISPLAY),字体大小24,颜色白色
- 添加"数字显示控件":
- 位置:X=100, Y=200,宽度=200,高度=50
- 配置:关联寄存器地址0x0020(对应STM32的REG_NUM_DISPLAY),显示位数4,颜色白色
- 添加"按键控件":
- 按键1:位置X=100, Y=300,关联寄存器地址0x0030,按下时发送指令0x5A 0xA5 0x03 0x83 0x00 0x30 0x01
- 按键2:位置X=200, Y=300,关联寄存器地址0x0030,按下时发送指令0x5A 0xA5 0x03 0x83 0x00 0x30 0x02
5.2.2 页面2(切换页面)配置
- 新增页面2,命名为"切换页面"
- 添加"文本显示控件":位置X=100, Y=100,显示"Page 2",字体大小32,颜色白色
5.3 工程下载到串口屏
- 用USB线连接串口屏到电脑
- 点击组态软件的"下载"按钮,选择"全部数据",等待下载完成
- 断开USB,将串口屏按硬件接线连接到STM32
六、调试流程与技巧
6.1 整体调试流程(Mermaid流程图)
确认GND共地、TX/RX交叉连接
无编译错误
STM32能收发数据
组态数据下载成功
文本/数字显示正常
通信异常
按键切换页面/更新数字
检查波特率/校验和
硬件接线检查
STM32代码编译下载
串口助手调试UART通信
串口屏组态下载
STM32与串口屏联调
功能验证
按键功能测试
排查通信问题
复杂界面扩展
最终调试完成
6.2 分步调试技巧
6.2.1 第一步:STM32 UART自收发调试
-
将STM32的PA9(TX)和PA10(RX)短接
-
在main函数中添加代码:
cUSART1_SendString("Test UART\r\n"); HAL_Delay(1000); -
编译下载代码,用串口助手连接USB转TTL,波特率9600
-
若串口助手能收到"Test UART",说明UART发送正常;若在中断中能接收并回显,说明接收正常
6.2.2 第二步:STM32与串口屏通信调试
- 断开TX/RX短接,按硬件接线连接串口屏
- 在main函数中只保留
HMI_SwitchScreen(0x0001); - 下载代码后,若串口屏显示页面1,说明基本通信正常
- 若通信乱码:
- 检查波特率是否一致(STM32和串口屏必须相同)
- 确认GND是否共地(最常见原因)
- 检查TX/RX是否接反(STM32的TX接串口屏的RX,反之亦然)
6.2.3 第三步:复杂界面调试
- 文本显示异常:
- 检查寄存器地址是否匹配(组态软件与STM32宏定义)
- 确认文本编码格式(串口屏通常支持GB2312)
- 按键无响应:
- 用串口助手监听串口屏发送的指令,确认指令格式是否与STM32解析逻辑一致
- 检查STM32中断是否开启,接收标志是否正常置位
- 数据更新卡顿:
- 减少主循环中的延时,或使用定时器代替
HAL_Delay - 优化串口发送逻辑,避免频繁发送大量指令
- 减少主循环中的延时,或使用定时器代替
七、复杂HMI界面扩展建议
- 多页面管理:通过寄存器地址区分不同页面的控件,在STM32中维护当前页面状态,避免跨页面指令冲突
- 数据缓存:在STM32中建立数据缓存区,批量更新串口屏数据,减少UART通信次数
- 异常处理:添加通信超时检测,若串口屏无响应,自动重发初始化指令
- 触摸屏校准:在组态软件中完成触摸屏校准,确保按键点击精准
- 图形显示:使用串口屏的图片显示控件,预先将图片下载到串口屏Flash,通过STM32发送指令切换显示
总结
- 串口屏开发的核心是保证STM32 UART通信的稳定性,重点关注波特率、共地、TX/RX接线这三个关键要素,这是通信正常的基础。
- 串口屏指令交互需严格遵循对应厂商的协议(如迪文DGUS的校验和、指令格式),STM32端需封装标准化的指令发送函数,降低调试难度。
- 调试需遵循"分步验证"原则:先验证UART本身,再验证基本通信,最后调试复杂控件,遇到问题优先排查硬件接线和参数一致性。