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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值