三十二、STM32的USART(串口收发文本数据包) - 实践

前言:串口(USART)是嵌入式工程中使用频率最高的通信接口。不论是调试输出、模块控制、板间通信,都离不开串口。本篇文章带你从零实现一个 串口文本数据包协议收发案例

  • 数据包格式: @MSG\r\n

  • 通过中断 + 状态机解析完整数据包

  • 将收到的文本指令显示到 OLED

  • 收到如 "LED_ON" 指令自动执行动作,并回传执行结果

目录

一、接线图

二、功能说明

三、串口文本数据包格式设计

四、串口初始化(9600 8N1)

五、串口接收数据包 —— 状态机解析核心

六、OLED 显示收发内容

七、根据指令执行动作(LED 控制)

八、完整 main.c

九、上位机调试方法

十、总结


一、接线图

二、功能说明

本项目实现如下功能:

1. 上位机发送文本指令

格式:

@LED_ON\r\n
@LED_OFF\r\n

2. STM32 接收完整数据包并解析

解析内容存入:

char Serial_RxPacket[100];

3. OLED 显示收到的文本命令

LED_ON
LED_OFF

4. 执行对应操作

5. 串口回传执行结果

LED_ON_OK
LED_OFF_OK
ERROR_COMMAND

三、串口文本数据包格式设计

我们采用一个简单、稳定、安全的文本协议格式:

@内容\r\n

例如:

@LED_ON\r\n

为什么选 @

  • 特殊字符,不易混淆

  • 便于状态机识别

  • 文本协议常用格式

四、串口初始化(9600 8N1)

以下代码完成 GPIO、USART、NVIC 中断初始化:

void Serial_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    USART_InitTypeDef USART_InitStructure;
    USART_InitStructure.USART_BaudRate = 9600;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1, &USART_InitStructure);
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_Init(&NVIC_InitStructure);
    USART_Cmd(USART1, ENABLE);
}

五、串口接收数据包 —— 状态机解析核心

状态机三阶段:

完整中断解析代码:

void USART1_IRQHandler(void)
{
    static uint8_t RxState = 0;
    static uint8_t pRxPacket = 0;
    if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
    {
        uint8_t RxData = USART_ReceiveData(USART1);
        if (RxState == 0)
        {
            if (RxData == '@' && Serial_RxFlag == 0)
            {
                RxState = 1;
                pRxPacket = 0;
            }
        }
        else if (RxState == 1)
        {
            if (RxData == '\r')
            {
                RxState = 2;
            }
            else
            {
                Serial_RxPacket[pRxPacket] = RxData;
                pRxPacket++;
            }
        }
        else if (RxState == 2)
        {
            if (RxData == '\n')
            {
                RxState = 0;
                Serial_RxPacket[pRxPacket] = '\0';
                Serial_RxFlag = 1;
            }
        }
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
    }
}

为什么需要状态机?

  • 串口数据是一个字节一个字节来的

  • 不能保证一次收到全部数据

  • 协议解析必须严格依靠状态机实现

六、OLED 显示收发内容

OLED_ShowString(1, 1, "TxPacket");
OLED_ShowString(3, 1, "RxPacket");

显示接收到的内容:

OLED_ShowString(4, 1, "                ");
OLED_ShowString(4, 1, Serial_RxPacket);

七、根据指令执行动作(LED 控制)

if (strcmp(Serial_RxPacket, "LED_ON") == 0)
{
    LED1_ON();
    Serial_SendString("LED_ON_OK\r\n");
}
else if (strcmp(Serial_RxPacket, "LED_OFF") == 0)
{
    LED1_OFF();
    Serial_SendString("LED_OFF_OK\r\n");
}
else
{
    Serial_SendString("ERROR_COMMAND\r\n");
}

并在 OLED 显示执行结果:

OLED_ShowString(2, 1, "                ");
OLED_ShowString(2, 1, "LED_ON_OK");

八、完整 main.c

#include "stm32f10x.h"
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "LED.h"
#include "string.h"
int main(void)
{
    OLED_Init();
    LED_Init();
    Serial_Init();
    OLED_ShowString(1, 1, "TxPacket");
    OLED_ShowString(3, 1, "RxPacket");
    while (1)
    {
        if (Serial_RxFlag == 1)
        {
            OLED_ShowString(4, 1, "                ");
            OLED_ShowString(4, 1, Serial_RxPacket);
            if (strcmp(Serial_RxPacket, "LED_ON") == 0)
            {
                LED1_ON();
                Serial_SendString("LED_ON_OK\r\n");
                OLED_ShowString(2, 1, "LED_ON_OK");
            }
            else if (strcmp(Serial_RxPacket, "LED_OFF") == 0)
            {
                LED1_OFF();
                Serial_SendString("LED_OFF_OK\r\n");
                OLED_ShowString(2, 1, "LED_OFF_OK");
            }
            else
            {
                Serial_SendString("ERROR_COMMAND\r\n");
                OLED_ShowString(2, 1, "ERROR_COMMAND");
            }
            Serial_RxFlag = 0;
        }
    }
}

九、上位机调试方法

打开串口助手,设置:

  • 9600 波特率

  • 文本模式(发送字符串)

  • 必须发送结束符 \r\n

@LED_ON\r\n
@LED_OFF\r\n

即可看到:

  • LED 点亮 / 熄灭

  • OLED 显示命令

  • STM32 回传 LED_ON_OK

十、总结

本项目完整展示了:

  • GPIO + USART + NVIC 初始化

  • 串口文本协议设计

  • 状态机解析数据包

  • OLED 显示系统

  • 文本指令控制 LED

  • 串口回传执行结果

posted @ 2025-12-17 15:01  clnchanpin  阅读(46)  评论(0)    收藏  举报