4、UART串口通信基础:配置USART1,实现STM32与PC串口助手的数据收发

好,咱们进入第四讲。这一讲要聊的是嵌入式开发里最基础、也最常用的通信方式——UART串口。说白了,就是让STM32和你的电脑“说上话”。

我刚开始学STM32的时候,第一个调通的外设就是串口。为啥?因为串口是调试的“眼睛”。你程序跑没跑、数据对不对、哪里卡住了,全靠它把信息吐出来。所以这一章,咱们把USART1配置好,实现和PC串口助手的双向通信。

4.1 什么是UART?为什么它这么重要?

UART的全称是Universal Asynchronous Receiver/Transmitter,通用异步收发器。注意“异步”这两个字——它不需要时钟线,只用两根数据线(TX和RX)就能通信。

它的工作方式,我打个比方:就像两个人约定好,每隔一秒钟说一个字。你说“我”,我听到“我”;你说“是”,我听到“是”。这个“每隔一秒钟”就是波特率。只要双方约定的速度一致,就能准确通信。

UART通信的核心参数:

  • 波特率(Baud Rate):每秒传输的符号数,常见的有9600、115200等
  • 数据位(Data Bits):一帧数据中有效数据的位数,通常是8位
  • 停止位(Stop Bits):一帧数据结束的标志位,常见1位或2位
  • 校验位(Parity Bit):可选,用于简单的错误检测

我在项目中遇到过一件事:有个同事调了半天串口,数据全是乱码。最后发现是波特率设错了——他代码里写的是115200,但串口助手设的是9600。你想想看,一个说快一个说慢,能对上才怪。

4.2 USART1的硬件连接

STM32的USART1,通常挂在PA9(TX)和PA10(RX)这两个引脚上。当然,不同型号的芯片可能不一样,具体要看数据手册。

连接方式很简单:

  • STM32的TX(PA9) → PC串口助手的RX
  • STM32的RX(PA10) → PC串口助手的TX
  • GND → GND(这个别忘了,我吃过亏)

注意:STM32的IO口电平是3.3V,而有些USB转串口模块是5V的。虽然大部分模块能兼容,但稳妥起见,最好确认一下电平匹配。我曾经因为没注意这个,烧坏过一个引脚的驱动电路。

4.3 配置USART1的步骤

配置USART1,说白了就三步:

  1. 使能时钟:GPIO时钟和USART1时钟都要打开
  2. 配置GPIO:PA9设为复用推挽输出,PA10设为浮空输入(或上拉输入)
  3. 配置USART:设置波特率、数据位、停止位,使能收发

我个人习惯用标准库或者HAL库来配置。下面给一个标准库的示例:

// 1. 使能时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

// 2. 配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
// TX - PA9 复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// RX - PA10 浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);

// 3. 配置USART1
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;        // 波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;  // 8位数据位
USART_InitStructure.USART_StopBits = USART_StopBits_1;      // 1位停止位
USART_InitStructure.USART_Parity = USART_Parity_No;         // 无校验
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 收发都使能
USART_Init(USART1, &USART_InitStructure);

// 4. 使能USART1
USART_Cmd(USART1, ENABLE);

嗯,这里要注意一点:波特率不是随便设的。它和系统时钟有关。比如系统时钟是72MHz,你设个115200,分频后误差很小。但如果你设个250000,可能误差就大了。我建议用常用的波特率:9600、19200、38400、115200。

4.4 发送和接收数据

配置好了,怎么发数据?很简单,往USART1的数据寄存器里写就行。

// 发送一个字节
void USART1_SendByte(uint8_t data)
{
    USART_SendData(USART1, data);
    // 等待发送完成
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

// 发送字符串
void USART1_SendString(char* str)
{
    while(*str)
    {
        USART1_SendByte(*str++);
    }
}

接收呢?有两种方式:查询和中断。查询方式就是不断检查有没有数据来,简单但浪费CPU。中断方式更高效,但配置稍复杂。

先看查询方式:

// 接收一个字节(查询方式)
uint8_t USART1_ReceiveByte(void)
{
    // 等待接收完成
    while(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
    return USART_ReceiveData(USART1);
}

中断方式需要配置NVIC,使能USART1接收中断:

// 使能接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
NVIC_EnableIRQ(USART1_IRQn);

// 中断服务函数
void USART1_IRQHandler(void)
{
    if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        uint8_t data = USART_ReceiveData(USART1);
        // 处理接收到的数据
        // 比如:USART1_SendByte(data);  // 回显
    }
}

我的经验:调试阶段用查询方式就够了,简单直观。但正式产品里,一定要用中断或者DMA。不然CPU光顾着等串口,啥也干不了。

4.5 常见问题与避坑指南

串口通信看着简单,但坑不少。我列几个常见的:

  • 乱码:多半是波特率不匹配,或者时钟配置错了。检查一下系统时钟是不是你预期的值。
  • 收不到数据:先检查接线,TX和RX有没有交叉?GND连了没?我曾经因为没连GND,折腾了半小时。
  • 数据丢帧:接收端处理太慢,下一个字节来了,上一个还没读走。解决办法是用中断或DMA,或者加大缓冲区。
  • 第一个字节丢失:USART刚使能时,状态寄存器可能还没准备好。可以在使能后加个小延时,或者先发一个空字节。

特别注意:USART的TXE标志和TC标志不一样。TXE表示数据已经移到移位寄存器,可以写下一个了。TC表示数据已经发送完成。如果你要切换RS485的方向,一定要等TC标志,而不是TXE。这个坑我踩过,导致RS485通信时最后一个字节发不出去。

4.6 实战:让STM32和PC“对话”

好了,理论说完了,咱们动手。目标:STM32上电后,每隔1秒发送"Hello from STM32!",同时把PC发来的数据原样返回(回显)。

主程序大概这样:

int main(void)
{
    // 初始化
    USART1_Init(115200);
    
    while(1)
    {
        USART1_SendString("Hello from STM32!\r\n");
        Delay_ms(1000);
        
        // 检查是否有数据收到
        if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
        {
            uint8_t data = USART_ReceiveData(USART1);
            USART1_SendByte(data);  // 回显
        }
    }
}

用串口助手(比如SSCOM、Putty、或者ST官方的串口工具)打开对应端口,波特率设115200,数据位8,停止位1,无校验。你应该能看到STM32不断发来的消息。你在发送区打个字,它也会原样返回。

如果你看到的是乱码,先别慌。检查一下:波特率对不对?时钟配置对不对?接线有没有问题?八成是这三个原因之一。

这一章的内容就到这里。串口是嵌入式开发的“基本功”,一定要亲手调通。下一章,咱们要在这个基础上,让STM32去读取颗粒物传感器的数据,然后通过串口发出来。嗯,那才是咱们课程的核心。