STM32单线串口通讯实战(一):物理层拓扑与STM32G0硬件配置

1. 前言:为什么选择单线串口?

在工业现场、家电板间通讯(如显示板与主控板)或舵机控制中,我们经常遇到 IO 资源紧张或布线成本敏感的场景。

  • 传统 UART:TX + RX + GND(3线),全双工。

  • 单线 UART:Data + GND(2线),半双工。

虽然少了一根线,但软件复杂度却呈指数级上升:由于收发共用物理通道,必须在物理层解决"打架"(总线冲突)的问题,在链路层解决"时序"(收发切换)的问题。

本系列将以高性价比的 STM32G0 系列为例,从底层电路到 RTOS 架构,彻底讲透单线串口的设计。


2. 三种物理层拓扑详解

要写驱动,首先得知道电路是怎么连的。单线串口主要有三种硬件实现方式:

方案 A:STM32 内部半双工 (Internal Half-Duplex) ------ 推荐 G0 使用

这是 STM32 最"原生"的玩法。通过设置寄存器(HDSEL),芯片内部会将 TX 和 RX 信号在物理上短接,并自动处理输出驱动。

  • 硬件连接

    • 仅使用 TX 引脚作为双向数据线。

    • RX 引脚被释放,可做普通 GPIO 用。

  • 适用场景:板内短距离通讯、私有总线。

  • 优点:无需外部元器件,节省 IO。

方案 B:GPIO 控制外部收发器 (External Control)

类似于 RS485 的 TTL 版本。使用三态缓冲器(如 74LVC1G125)来增强驱动能力。

  • 硬件连接:需要额外的 GPIO 控制方向。

  • 适用场景:长距离通讯、环境干扰大、需要强推挽驱动的场合。

  • 缺点 :软件需要处理繁琐的 DIR 引脚切换时序(Turnaround Time)。

方案 C:开漏输出 + 上拉 (Open-Drain / Diode) ------ 多机通讯经典

利用"线与"(Wired-AND)逻辑。所有设备的 TX 配置为开漏输出(Open-Drain),总线上挂一个上拉电阻。

  • 硬件连接:STM32 的 TX 设为 Open-Drain,RX 内部连接到 TX(或外部短接)。

  • 关键点:总线平时靠电阻拉高。任何一个设备拉低,总线即为低。

  • 适用场景:多主(Multi-Master)通讯、低成本多机组网。

3. STM32G0 的实战配置 (CubeMX & HAL)

针对 STM32G0 ,我们重点演示最常用的 方案 A (内部半双工)方案 C (开漏模式)

3.1 STM32CubeMX 配置步骤

  1. Pinout & Configuration:

    • 进入 Connectivity -> USARTx

    • Mode : 选择 Single Wire (Half-Duplex)

    • 注意:此时你在引脚图上会看到只有 TX 引脚点亮,RX 引脚消失(或变灰)。

  2. Parameter Settings (关键):

    • Baud Rate: 根据需求设置(如 115200)。

    • Word Length: 通常 8 Bits。

    • Parity: None。

    • Stop Bits: 1。

  3. GPIO Settings (避坑点):

    • 点击 GPIO Settings 标签页。

    • GPIO Mode : 确认为 Alternate Function Open Drain (开漏复用) 还是 Push Pull

      • 如果总线上有外部上拉电阻(方案 C) :必须选 Open Drain

      • 如果是点对点且无上拉(方案 A) :可选 Push Pull,但建议 Open Drain 配合内部上拉以防冲突。

    • GPIO Pull-up/Pull-down : 建议选择 Pull-up (即使外部有电阻,内部上拉也能提供双重保障,防止初始化瞬间的干扰)。

3.2 STM32G0 特有的"黑科技"配置

G0 系列的 USART_CR2USART_CR1 寄存器里藏着几个好用的功能,可以在 CubeMX 的 Advanced Settings 或代码中开启:

  • TX/RX Pin Swapping (SWAP):

    • 场景:画 PCB 时手抖,把 TX 和 RX 线走反了,或者为了绕开过孔想互换引脚。

    • 代码huart1.AdvancedInit.Swap = UART_ADVFEATURE_SWAP_ENABLE;

    • 效果:物理引脚定义互换,无需割线。

  • Logical Inversion (TXINV/RXINV):

    • 场景:如果你用了简单的三极管做外部驱动,电平逻辑会反转(输入 1 变 0)。

    • 代码huart1.AdvancedInit.TxPinLevelInvert = UART_ADVFEATURE_TXINV_ENABLE;

    • 效果:MCU 自动把发出去的数据取反,软件层看到的还是正常数据。


4. 物理层避坑指南 (必看)

这是无数工程师调试单线串口时"翻车"的地方。

4.1 上拉电阻与波形失真 (RC Time Constant)

开漏模式 (Open-Drain) 下,逻辑"0"是芯片内部 MOS 管强拉地的(下降沿很陡峭),但逻辑"1"是靠电阻拉上去的(上升沿是电容充电曲线)。

  • 现象:如果你用示波器看,波形像"鲨鱼鳍"。

  • 后果:如果电阻太大或线太长(电容大),上升沿太缓,导致采样点电平未达到阈值(VIH),数据出错。

工程推荐值表

波特率 (Baud) 推荐上拉电阻 (R) 适用场景
9600 bps 4.7kΩ - 10kΩ 长距离,低功耗
115200 bps 1kΩ - 2.2kΩ 短距离,追求速度
1 Mbps+ 330Ω - 680Ω 极短距离,注意 MCU 灌电流限制

4.2 5V 容忍与电平转换

STM32G0 是 3.3V 器件,但很多单线传感器(如水温、超声波)是 5V 供电。

  • 技巧 :G0 的许多 IO 口是 FT (Five Volt Tolerant) 的。

  • 操作

    1. 查阅 Datasheet,确认所选 TX 引脚标有 FT 字样。

    2. 配置为 Open-Drain 模式。

    3. 上拉电阻拉到 5V(而不是 3.3V)。

  • 结果:直接实现了双向 3.3V <-> 5V 电平转换,无需任何转换芯片!

4.3 总线空闲状态

  • UART 协议规定,空闲状态(Idle)必须是高电平

  • 初始化陷阱 :在 HAL_UART_Init 完成之前,GPIO 可能会浮动。务必在 HAL_UART_MspInit 中,尽早开启上拉(Pull-up),确保上电瞬间总线不会误产生"低电平毛刺",否则从机会误以为那是 Start Bit。


5. 核心代码预览 (HAL 库)

以下是基于 HAL 库的初始化精简代码,展示了如何启用半双工模式。

/* UART1 Initialization Function */

void MX_USART1_UART_Init(void)

{

huart1.Instance = USART1;

huart1.Init.BaudRate = 115200;

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; // 注意:部分HAL版本此处需配合下面的 HalfDuplex 调用

huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;

huart1.Init.OverSampling = UART_OVERSAMPLING_16;

// G0 特性配置:如果需要单线且只用TX引脚

// HAL_HalfDuplex_Init 会自动处理 HDSEL 位

if (HAL_HalfDuplex_Init(&huart1) != HAL_OK)

{

Error_Handler();

}

}

/* GPIO Configuration (在 stm32g0xx_hal_msp.c 中) */

void HAL_UART_MspInit(UART_HandleTypeDef* huart)

{

GPIO_InitTypeDef GPIO_InitStruct = {0};

if(huart->Instance==USART1)

{

__HAL_RCC_USART1_CLK_ENABLE();

__HAL_RCC_GPIOA_CLK_ENABLE();

// PA9 -> USART1_TX

GPIO_InitStruct.Pin = GPIO_PIN_9;

GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; // Open-Drain 模式,推荐!

GPIO_InitStruct.Pull = GPIO_PULLUP; // 内部上拉,双重保险

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;

GPIO_InitStruct.Alternate = GPIO_AF1_USART1;

HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

// 注意:在 Half-Duplex 模式下,通常不需要配置 RX 引脚 (PA10)

}

}

/*******************************************
* Description:
* 本文为作者《嵌入式开发基础与工程实践》系列文之一。
* 关注我即可订阅后续内容更新,采用异步推送机制。
* 转发本文可视为广播分发,有助于信息传播至更多节点。
*******************************************/

相关推荐
weixin_307779131 天前
MATLAB动态演示流体扩散仿真模拟的简单例子
开发语言·matlab
Hello_wshuo1 天前
记RP2040使用Arduino+platformio开发配置
linux·嵌入式硬件·arduino
zhaokuner1 天前
12-深层模型与重构-DDD领域驱动设计
java·开发语言·设计模式·架构
学好statistics和DS1 天前
make all in Makefile
c语言
weixin_440730501 天前
java面向对象OPP-三大特性
java·开发语言·python
蕨蕨学AI1 天前
【Wolfram语言】37 布局与显示
开发语言·wolfram
m0_502724951 天前
在Qt中激活已运行的应用程序实例
开发语言·qt
沐知全栈开发1 天前
Kotlin 对象表达式/声明
开发语言
RaLi和夕1 天前
硬件电路设计学习笔记2.三极管基极为什么要加上下拉电阻?以及三级管的最大耐压
笔记·嵌入式硬件·学习