STM32 UART 通信详解

通用异步收发传输器(UART)是STM32微控制器中最基础、最常用的串行通信接口之一。它通过简单的两根信号线(TX和RX)实现全双工异步数据交换,广泛应用于与PC调试、传感器模块、蓝牙/Wi-Fi模块等的通信。

一、UART协议基础

1. 核心特性

UART是一种异步串行全双工的通信协议。其物理层仅需两根信号线:

  • TX (Transmit):数据发送线,输出。
  • RX (Receive) :数据接收线,输入。
    通信双方没有共享的时钟信号,同步完全依赖于预先约定好的波特率 (Baud Rate)

2. 数据帧格式

每一帧数据都遵循固定的结构,依次包含以下部分:

  1. 起始位:1位逻辑低电平(0),标志一帧数据的开始。
  2. 数据位:实际传输的有效数据,长度通常为8位(1字节),也可配置为5-9位。
  3. 校验位(可选):1位,用于简单的错误检测。常见模式有奇校验、偶校验或无校验。
  4. 停止位:1位、1.5位或2位逻辑高电平(1),标志一帧数据的结束。

最常用的配置是 8N1,即8位数据位、无校验、1位停止位。

3. 波特率

波特率定义为每秒传输的码元(比特)数量,单位是bps(比特每秒)。通信双方必须设置相同的波特率,否则无法正确解码数据。常见的波特率有9600、19200、115200等,其中115200因其较高的速度在调试中尤为常用。

二、STM32中的USART外设

STM32芯片中集成的实际上是USART (Universal Synchronous/Asynchronous Receiver/Transmitter) 外设。它与UART的关键区别在于:USART既支持异步模式(即UART功能),也支持同步模式(需要额外的时钟线SCLK)。在大多数异步通信应用中,我们使用的就是其UART功能。

主要特性

  • 支持全双工异步通信。
  • 可编程的波特率,通过小数波特率发生器实现高精度配置。
  • 可配置的数据字长度(8或9位)、停止位(1或2位)、校验位。
  • 支持硬件流控制(RTS/CTS)、多处理器通信。
  • 支持利用DMA进行高效数据传输,减轻CPU负担。
  • 支持多种工作模式,如单线半双工、LIN总线、智能卡、IrDA等。

三、硬件连接与电平转换

STM32的GPIO引脚工作电压通常为3.3V(TTL电平)。若要与其电平标准不同的设备(如PC的RS-232接口为±12V,或USB接口)通信,必须进行电平转换。

典型连接方案(与PC通信)

  1. STM32TX 引脚连接至USB转TTL模块 (如CH340、CP2102)的RX引脚。
  2. STM32RX 引脚连接至USB转TTL模块的TX引脚。
  3. STM32GND 与USB转TTL模块的GND必须连接,以确保共地。
  4. 将USB转TTL模块插入PC的USB端口。

四、软件配置步骤(以STM32F103 USART1为例)

配置UART通信通常包含以下几个核心步骤,无论是使用标准库、HAL库还是寄存器操作,其逻辑是相通的。

1. 使能时钟

首先需要开启USART外设及其对应GPIO引脚所在总线的时钟。

复制代码
// 使能GPIOA和USART1的时钟(它们挂载在APB2总线上)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);

2. 配置GPIO引脚

将USART的TX引脚(如PA9)配置为复用推挽输出 ,RX引脚(如PA10)配置为浮空输入或上拉输入。

复制代码
GPIO_InitTypeDef GPIO_InitStruct;
// 配置TX (PA9)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);

// 配置RX (PA10)
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOA, &GPIO_InitStruct);

3. 配置USART参数

初始化USART外设,设置波特率、数据位、停止位、校验位等关键参数。

复制代码
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 使能发送和接收
USART_Init(USART1, &USART_InitStruct);

4. 使能USART

完成配置后,使能USART外设。

复制代码
USART_Cmd(USART1, ENABLE);

5. (可选)配置中断

如果希望以中断方式接收数据,提高程序效率,需要配置NVIC并开启接收中断。

复制代码
// 使能USART1接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

// 配置NVIC(嵌套向量中断控制器)
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
... // 设置优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);

然后在中断服务函数 USART1_IRQHandler() 中处理接收到的数据。

五、数据收发函数

1. 发送数据(查询方式)

通过查询状态寄存器标志位,等待发送数据寄存器为空后写入数据。

复制代码
void UART_SendByte(uint8_t data) {
    USART_SendData(USART1, data); // 将数据写入数据寄存器(DR)
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); // 等待发送完成
}

2. 接收数据(查询方式)

查询接收数据寄存器非空标志位,等待数据到达后读取。

复制代码
uint8_t UART_ReceiveByte(void) {
    while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET); // 等待接收到数据
    return USART_ReceiveData(USART1); // 从数据寄存器(DR)读取数据
}

3. 使用HAL库(更简洁)

若使用STM32CubeMX和HAL库,配置过程被极大简化,收发函数也更易用。

  • 阻塞式发送HAL_UART_Transmit(&huart1, &data, 1, 100);
  • 阻塞式接收HAL_UART_Receive(&huart1, &data, 1, 100);
  • 中断式接收HAL_UART_Receive_IT(&huart1, &data, 1); 并在回调函数 HAL_UART_RxCpltCallback 中处理数据。

六、实战示例:串口回环测试

一个简单的测试程序是"回环"或"回显",即MCU将接收到的每一个字节数据立即发送回去。

复制代码
int main(void) {
    // 系统初始化、时钟配置、UART初始化(略)
    uint8_t received_data;
    while (1) {
        if (UART_ReceiveByte(&received_data)) { // 假设有接收函数
            UART_SendByte(received_data); // 将收到的数据发回
        }
    }
}

使用串口调试助手(如XCOM、SSCOM)连接到对应的COM口,设置正确的波特率(如115200),发送任意字符,应能收到相同的字符回显。

七、常见问题与调试

  1. 无数据或乱码 :最常见的原因是波特率不匹配 。请确保STM32程序设置的波特率与PC端串口调试助手的设置完全一致。其次检查接线是否正确 (TX接RX,RX接TX)以及共地是否连接。

  2. 无法进入中断:检查NVIC配置是否正确,中断服务函数名称是否拼写错误,以及是否在初始化时使能了接收中断。

  3. 使用printf重定向 :通过重写 fputc 函数,可以方便地使用 printf 函数通过串口格式化输出数据,极大提升调试效率。

    复制代码
    int fputc(int ch, FILE *f) {
        HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
        return ch;
    }

总结

STM32的UART通信是嵌入式开发的基石。掌握其协议原理、硬件连接方法、以及通过库函数进行初始化和数据收发的编程技巧,是进行设备间通信和系统调试的关键第一步。从简单的查询式回环测试开始,逐步尝试中断接收、DMA传输以及自定义通信协议,能够构建出更高效、更稳定的嵌入式通信系统。

相关推荐
LDR0062 小时前
乐得瑞 LDR6020P,重新定义 Type-C 多口方案
嵌入式硬件
苏灵凯2 小时前
智能环境监测终端全栈设计:从单片机到微信小程序,手把手搞定!
单片机·嵌入式硬件·mcu·物联网·微信小程序·小程序·蓝牙模块
济6172 小时前
STM32串口通信实战|从基础到实战(发送 + 接收控制 LED)---STM32 HAL库专栏
stm32·嵌入式·stm32hal库编程
福尔摩斯张2 小时前
一文搞懂74HC595芯片(附详细使用方法)
linux·服务器·网络·单片机·嵌入式硬件
LCG元2 小时前
串口屏快速开发:STM32 UART通信,复杂HMI界面调试技巧
stm32·单片机·嵌入式硬件
从零点2 小时前
如何在cmake中添加自己的项目文件夹文件
嵌入式硬件
广州灵眸科技有限公司2 小时前
瑞芯微(EASY EAI)RV1126B 人脸98关键点算法识别
开发语言·科技·嵌入式硬件·物联网·算法·php
myron66882 小时前
基于STM32LXXX的数字电位器(TPL0501-100DCNR)驱动应用程序设计
stm32·单片机·嵌入式硬件
篮子里的玫瑰2 小时前
FreeRTOS:信号量与互斥量在DMA串口发送中的实战剖析
stm32·单片机·嵌入式硬件·算法