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

相关推荐
猫猫的小茶馆14 小时前
【Linux 驱动开发】七. 中断下半部
linux·arm开发·驱动开发·stm32·嵌入式硬件·mcu
时见先生19 小时前
Python库和conda搭建虚拟环境
开发语言·人工智能·python·自然语言处理·conda
a努力。20 小时前
国家电网Java面试被问:混沌工程在分布式系统中的应用
java·开发语言·数据库·git·mysql·面试·职场和发展
Yvonne爱编码20 小时前
Java 四大内部类全解析:从设计本质到实战应用
java·开发语言·python
wqwqweee20 小时前
Flutter for OpenHarmony 看书管理记录App实战:搜索功能实现
开发语言·javascript·python·flutter·harmonyos
SongYuLong的博客20 小时前
TL-WR710N-V2.1 硬改刷机OpenWRT源码编译固件
linux·物联网·网络协议
yongui4783420 小时前
基于MATLAB的NALM锁模光纤激光器仿真实现
开发语言·matlab
-To be number.wan21 小时前
Python数据分析:numpy数值计算基础
开发语言·python·数据分析
沃尔特。1 天前
直流无刷电机FOC控制算法
c语言·stm32·嵌入式硬件·算法
CW32生态社区1 天前
CW32L012的PID温度控制——算法基础
单片机·嵌入式硬件·算法·pid·cw32