STM32 学习 —— 个人学习笔记9-2(USART串口数据包 & 串口收发 HEX 及 文本 数据包)

声明

文中内容为观看 BiliBili 视频【STM32入门教程-2023版 细致讲解 中文字幕】后学习并扩展总结。

本文章为个人学习使用,版面观感若有不适请谅解,文中知识仅代表个人观点,若出现错误,欢迎各位批评指正。

一、数据模式及数据包

1.1 数据模式

在数字通信系统中,发送方与接收方通过编码 - 译码 的双向映射实现字符与二进制数据的转换,这一过程构成了信息可靠传输的核心基础。以字符 'A'为例,发送方将其编码为十六进制值 0x41(对应二进制 01000001),并以原始比特流形式传输至接收方;接收方则通过译码操作将 0x41还原为字符 'A',完成信息的完整闭环传递。

在此过程中,数据的显示模式直接影响对传输内容的解读维度:十六进制模式(HEX 模式)/ 二进制模式 以原始数据形式呈现,直观展示通信链路中传输的比特序列或其十六进制等价形式,可精准定位传输过程中的比特错误与数据完整性;文本模式(字符模式)则以编码后的字符形式呈现,更贴近人类可读的信息语义,便于理解传输内容的实际含义 。二者分别从物理层数据传输应用层语义 表达两个层面,共同构成了数字通信系统中数据观测与分析的完整视角,为协议调试、故障排查及性能优化提供了互补的技术手段。

1.2 HEX 数据包 及 数据包接收
  • 固定包长 HEX 数据包格式

    固定包长 HEX 数据包采用帧头 - 数据 - 帧尾 的定长封装结构,以特定字节作为帧边界标识:帧头固定为 0xFF,帧尾固定为 0xFE,中间承载 4 字节定长有效载荷。该格式通过帧头 0xFF与帧尾 0xFE的字节序列实现帧同步与边界划分,有效载荷长度严格固定为 4 字节,无需额外长度字段即可完成帧解析,适用于对传输时延确定性要求较高的场景。典型数据包序列为 0xFF 0x01 0x02 0x03 0x04 0xFE,其中 0xFF为帧起始标识, 0x01~0x04为有效数据, 0xFE为帧结束标识。

  • 可变包长 HEX 数据包格式

    可变包长 HEX 数据包同样采用帧头 - 数据 - 帧尾 的封装范式,帧头 0xFF与帧尾 0xFE的边界标识规则与固定包长格式保持一致,但有效载荷长度可动态变化。该格式通过帧头 0xFF标记帧起始,帧尾 0xFE标记帧结束,有效载荷长度由应用层逻辑或协议约定动态调整,无需在帧结构中显式携带长度字段,仅依赖帧尾 0xFE完成帧边界判定。典型数据包序列为 0xFF 0x01 0x02 0x03 0x04 0xFE(4 字节载荷)或 0xFF 0x05 0x06 0x07 0xFE(3 字节载荷),适用于数据长度可变的异步通信场景。

  • HEX 数据包接收状态机流程

    HEX 数据包接收采用 有限状态机(FSM) 实现帧同步与解析,核心状态定义如下:

    状态 S=0(等待包头): 系统初始状态,持续监听接收字节流,仅当检测到帧头字节 0xFF时,状态迁移至 S=1(接收数据);若收到其他字节,则保持 S=0 状态。

    状态 S=1(接收数据): 进入数据接收阶段,累计接收有效载荷字节。对于固定包长格式,需累计接收 4 字节有效数据;对于可变包长格式,持续接收直至触发帧尾检测。若未收满约定长度的有效数据或收到非帧尾字节,则保持 S=1 状态;当收满约定长度数据时,状态迁移至 S=2(等待包尾)。

    状态 S=2(等待包尾): 等待帧尾字节 0xFE的到来,若检测到 0xFE,则判定当前帧接收完成,状态重置为 S=0(等待包头),并向上层提交完整数据包;若收到其他字节,则保持 S=2 状态,直至检测到0xFE或超时丢弃当前帧。

    该状态机通过字节级别的状态跃迁,实现了对固定 / 可变包长 HEX 数据包的可靠同步与解析,有效避免了帧黏连与帧丢失问题,保障了串行通信的帧完整性。

1.3 文本数据包 及 数据包接收
  • 固定包长文本数据包格式

    固定包长文本数据包采用帧头 - 数据 - 帧尾 的定长封装范式,以字符 @作为帧起始标识(帧头),以 \r\n(回车换行符)作为帧结束标识(帧尾),中间承载 3 字节固定长度的有效文本载荷。该格式通过 @\r\n的字符序列实现帧同步与边界划分,有效载荷长度严格限定为 3 字节,无需额外长度字段即可完成帧解析,适用于对传输时延确定性、数据完整性要求较高的文本通信场景。典型数据包序列为 @ABC\r\n,其中 @为帧起始标识, A/B/C为有效文本数据, \r\n为帧结束标识。

  • 可变包长文本数据包格式

    可变包长文本数据包同样遵循帧头 - 数据 - 帧尾 的封装结构,帧头 @与帧尾 \r\n的边界标识规则与固定包长格式保持一致,但有效载荷长度可动态变化。该格式以 @标记帧起始,以 \r\n标记帧结束,有效载荷长度由应用层业务逻辑动态调整,无需在帧结构中显式携带长度字段,仅依赖 \r\n序列完成帧边界判定。典型数据包序列为 @ABC\r\n(3 字节载荷)或 @DE\r\n(2 字节载荷),适用于数据长度可变的异步文本通信场景,如串口调试、传感器文本数据上报等。

  • 文本数据包接收状态机流程

    文本数据包接收采用 有限状态机(FSM) 实现帧同步与解析,核心状态定义如下::

    状态 S=0(等待包头): 系统初始状态,持续监听接收字符流,仅当检测到帧头字符 @时,状态迁移至 S=1(接收数据等待包尾);若收到其他字符,则保持 S=0 状态。

    状态 S=1(接收数据等待包尾): 进入数据接收阶段,累计接收有效文本载荷。对于固定包长格式,需累计接收 3 字节有效数据;对于可变包长格式,持续接收直至检测到 \r字符。若收到其他字符,则保持 S=1 状态;当检测到 \r字符时,状态迁移至 S=2(等待包尾)。

    状态 S=2(等待包尾): 等待帧尾第二个字符 \n的到来,若检测到 \n,则判定当前帧接收完成,状态重置 为 S=0(等待包头),并向上层提交完整文本数据包;若收到其他字符,则保持 S=2 状态,直至检测到 \n或超时丢弃当前帧。

    该状态机通过字符级别的状态跃迁,实现了对固定 / 可变包长文本数据包的可靠同步与解析,有效避免了帧黏连与帧丢失问题,保障了串行文本通信的帧完整性。

二、串口收发 HEX 及 文本 数据包

2.1 串口收发 HEX 数据包的实现
  • 首先,按下图接线方式,搭建面包板电路连接 OLED 显示屏,并将 USB 转串口的 TXD 和 RXD 分别与 PA2 和 PA3 连接,然后将 DAP-Link / ST-Link 连接到 STM32 最小系统板上,为使 OLED 显示屏的 VCC 和 GND 正确连接正负极,请先连接对应正负极跳线(或直接使用 GPIO 口进行供电)。

  • 直接复制先前演示的已有文件目录,重命名并双击后缀名为 .uvprojx 的文件打开工程文件,并对 main.c 进行修改,工程中所使用的全部头文件其详细内容已放于文末。

    #include "stm32f10x.h" // Device header
    #include "Serial_HEX.h"
    #include "OLED.h"
    #include "Key.h"

    uint8_t KeyNum;

    int main(void)
    {
    OLED_Init();
    Key_Init();
    Serial_Init();

    复制代码
      OLED_ShowString(1, 1, "TxPacket");
      OLED_ShowString(3, 1, "RxPacket");
      
      Serial_TxPacket[0] = 0x01;
      Serial_TxPacket[1] = 0x02;
      Serial_TxPacket[2] = 0x03;
      Serial_TxPacket[3] = 0x04;
                  
      while (1)
      {   
          KeyNum = Key_GetNum();
          if (KeyNum == 1){            
              Serial_TxPacket[0] ++;
              Serial_TxPacket[1] ++;
              Serial_TxPacket[2] ++;
              Serial_TxPacket[3] ++;
              
              Serial_SendPacket();
              
              OLED_ShowHexNum(2, 1, Serial_TxPacket[0], 2);
              OLED_ShowHexNum(2, 4, Serial_TxPacket[1], 2);
              OLED_ShowHexNum(2, 7, Serial_TxPacket[2], 2);
              OLED_ShowHexNum(2, 10, Serial_TxPacket[3], 2);
          }
          
          if (Serial_GetRxFlag() == 1){
              OLED_ShowHexNum(4, 1, Serial_RxPacket[0], 2);
              OLED_ShowHexNum(4, 4, Serial_RxPacket[1], 2);
              OLED_ShowHexNum(4, 7, Serial_RxPacket[2], 2);
              OLED_ShowHexNum(4, 10, Serial_RxPacket[3], 2);
          }
      }

    }

  • 打开串口助手(串口助手软件 请在【江协科技】视频下方下载) ,按照预设的数据包格式,在串口助手的发送区域编辑数据 "FF 11 22 33 44 FE" 并点击发送后,即可在 RxPacket 区域下方查看到所发送的数据包内容。

2.2 串口收发文本数据包的实现
  • 首先,按下图接线方式,搭建面包板电路连接 OLED 显示屏,并将 USB 转串口的 TXD 和 RXD 分别与 PA2 和 PA3 连接,然后将 DAP-Link / ST-Link 连接到 STM32 最小系统板上,为使 OLED 显示屏的 VCC 和 GND 正确连接正负极,请先连接对应正负极跳线(或直接使用 GPIO 口进行供电)。

  • 直接复制先前演示的已有文件目录,重命名并双击后缀名为 .uvprojx 的文件打开工程文件,并对 main.c 进行修改,工程中所使用的全部头文件其详细内容已放于文末。
    注:文末 LED 代码中保留了 void LED_Turn(void);功能的实现,感兴趣的可以尝试将 LED 灯状态转换添加进来,并通过设定 LED 初始状态以及 FLAG 标记位返回当前 LED 灯的状态信息(即:当前 LED 是开还是关)。

    #include "stm32f10x.h" // Device header
    #include "Serial_TEXT.h"
    #include "OLED.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){
                  LED_ON();
                  Serial_SendString("LED_ON_OK\r\n");
                  OLED_ShowString(2, 1, "                ");
                  OLED_ShowString(2, 1, "LED_ON_OK");
              } else if (strcmp(Serial_RxPacket, "LED_OFF") == 0){
                  LED_OFF();
                  Serial_SendString("LED_OFF_OK\r\n");
                  OLED_ShowString(2, 1, "                ");
                  OLED_ShowString(2, 1, "LED_OFF_OK");
              } else {
                  Serial_SendString("ERROR_COMMAND\r\n");
                  OLED_ShowString(2, 1, "                ");
                  OLED_ShowString(2, 1, "ERROR_COMMAND");
              }
              
              Serial_RxFlag = 0;
              
          }
      }

    }

  • 打开串口助手(串口助手软件 请在【江协科技】视频下方下载) ,按照预设的数据包格式,在串口助手的发送区域编辑数据 "@LED_ON + 换行" 并点击发送后,即可观察到 LED 灯亮。


    注:文末 LED.h中保留了void LED_Turn(void);功能的实现,可尝试完善 LED_TURN 部分内容(输入 @LED_TURN 转换当前 LED 状态,输入 @GET_STATE 获取当前 LED 开关状态。)。

三、演示代码关联的头文件与源文件说明

  • OLED 相关头文件请从 STM32 学习 ------ 个人学习笔记4(OLED 显示屏及调试工具) 文末查看,此处不重复展示。

  • Delay 相关头文件请从 STM32 学习 ------ 个人学习笔记3-1(GPIO 输出) 文末查看,此处不重复展示。

  • Key.c

    #include "stm32f10x.h" // Device header
    #include "Delay.h"

    void Key_Init(void)
    {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

    复制代码
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOB, &GPIO_InitStructure);

    }

    uint8_t Key_GetNum(void)
    {
    uint8_t KeyNum = 0;
    if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)
    {
    Delay_ms(20);
    while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
    Delay_ms(20);
    KeyNum = 1;
    }

    复制代码
      return KeyNum;

    }

  • Key.h

    #ifndef __KEY_H
    #define __KEY_H

    void Key_Init(void);
    uint8_t Key_GetNum(void);

    #endif

  • LED.c

    #include "stm32f10x.h" // Device header

    void LED_Init(void)
    {
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    复制代码
      GPIO_InitTypeDef GPIO_InitStructure;
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_InitStructure);
      
      GPIO_SetBits(GPIOA, GPIO_Pin_6);

    }

    void LED_ON(void)
    {
    GPIO_ResetBits(GPIOA, GPIO_Pin_6);
    }

    void LED_OFF(void)
    {
    GPIO_SetBits(GPIOA, GPIO_Pin_6);
    }

    void LED_Turn(void)
    {
    if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_6) == 0)
    {
    GPIO_SetBits(GPIOA, GPIO_Pin_6);
    }
    else
    {
    GPIO_ResetBits(GPIOA, GPIO_Pin_6);
    }
    }

  • LED.h

    #ifndef __LED_H
    #define __LED_H

    void LED_Init(void);
    void LED_ON(void);
    void LED_OFF(void);
    void LED_Turn(void);

    #endif

  • Serial_HEX.c

    #include "stm32f10x.h" // Device header
    #include <stdio.h>
    #include <stdarg.h>

    uint8_t Serial_TxPacket[4];
    uint8_t Serial_RxPacket[4];
    uint8_t Serial_RxFlag;

    void Serial_Init(void){
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    复制代码
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    
      
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
      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_3;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_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(USART2, &USART_InitStructure);
      
      USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
      
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      NVIC_Init(&NVIC_InitStructure);
      
      USART_Cmd(USART2, ENABLE);

    }

    void Serial_SendByte(uint8_t Byte){
    USART_SendData(USART2, Byte);
    while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
    }

    void Serial_SendArray(uint8_t *Array, uint16_t Length){
    uint16_t i;
    for(i=0; i<Length; i++){
    Serial_SendByte(Array[i]);
    }
    }

    void Serial_SendString(char *String){
    uint8_t i;
    for(i=0; String[i] != '\0'; i++){
    Serial_SendByte(String[i]);
    }
    }

    uint32_t Serial_Pow(uint32_t X, uint32_t Y){
    uint32_t Result = 1;
    while(Y--){
    Result *=X;
    }
    return Result;
    }

    void Serial_SendNumber(uint32_t Number, uint8_t Length){
    uint8_t i;
    for(i=0; i<Length; i++){
    Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + 0x30);
    }
    }

    int fputc(int ch, FILE *f){
    Serial_SendByte(ch);
    return ch;
    }

    void Serial_printf(char *format, ...){
    char String[100];
    va_list arg;
    va_start(arg, format);
    vsprintf(String, format, arg);
    va_end(arg);
    Serial_SendString(String);
    }

    void Serial_SendPacket(void){
    Serial_SendByte(0xFF);
    Serial_SendArray(Serial_TxPacket, 4);
    Serial_SendByte(0xFE);
    }

    uint8_t Serial_GetRxFlag(void){
    if(Serial_RxFlag==1){
    Serial_RxFlag = 0;
    return 1;
    }
    return 0;
    }

    void USART2_IRQHandler(void){
    static uint8_t RxState = 0;
    static uint8_t pRxState = 0;
    if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET){
    uint8_t RxData = USART_ReceiveData(USART2);

    复制代码
          if (RxState == 0){
              if (RxData == 0xFF){
                  RxState =1;
                  pRxState = 0;
              }
          }else if (RxState == 1){
              Serial_RxPacket[pRxState] = RxData;
              pRxState ++;
              if (pRxState >= 4){
                  RxState = 2;
              }
          }else if (RxState == 2){
              if (RxData == 0xFE){
                  RxState =0;
                  Serial_RxFlag = 1;
              }
          }
          
          USART_ClearITPendingBit(USART2, USART_IT_RXNE);
      }

    }

  • Serial_HEX.h

    #ifndef __SERIAL_HEX_H
    #define __SERIAL_HEX_H

    #include <stdio.h>

    extern uint8_t Serial_TxPacket[];
    extern uint8_t Serial_RxPacket[];

    void Serial_Init(void);
    void Serial_SendByte(uint8_t Byte);
    void Serial_SendArray(uint8_t *Array, uint16_t Length);
    void Serial_SendString(char *String);
    void Serial_SendNumber(uint32_t Number, uint8_t Length);
    int fputc(int ch, FILE *f);
    void Serial_printf(char *format, ...);

    void Serial_SendPacket(void);

    uint8_t Serial_GetRxFlag(void);

    #endif

  • Serial_TEXT.c

    #include "stm32f10x.h" // Device header
    #include <stdio.h>
    #include <stdarg.h>

    char Serial_RxPacket[100];
    uint8_t Serial_RxFlag;

    void Serial_Init(void){
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    复制代码
      RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);    
      
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
      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_3;
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      GPIO_Init(GPIOA, &GPIO_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(USART2, &USART_InitStructure);
      
      USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
      
      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
      NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
      NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
      NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
      NVIC_Init(&NVIC_InitStructure);
      
      USART_Cmd(USART2, ENABLE);

    }

    void Serial_SendByte(uint8_t Byte){
    USART_SendData(USART2, Byte);
    while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
    }

    void Serial_SendArray(uint8_t *Array, uint16_t Length){
    uint16_t i;
    for(i=0; i<Length; i++){
    Serial_SendByte(Array[i]);
    }
    }

    void Serial_SendString(char *String){
    uint8_t i;
    for(i=0; String[i] != '\0'; i++){
    Serial_SendByte(String[i]);
    }
    }

    uint32_t Serial_Pow(uint32_t X, uint32_t Y){
    uint32_t Result = 1;
    while(Y--){
    Result *=X;
    }
    return Result;
    }

    void Serial_SendNumber(uint32_t Number, uint8_t Length){
    uint8_t i;
    for(i=0; i<Length; i++){
    Serial_SendByte(Number / Serial_Pow(10, Length - i - 1) % 10 + 0x30);
    }
    }

    int fputc(int ch, FILE *f){
    Serial_SendByte(ch);
    return ch;
    }

    void Serial_printf(char *format, ...){
    char String[100];
    va_list arg;
    va_start(arg, format);
    vsprintf(String, format, arg);
    va_end(arg);
    Serial_SendString(String);
    }

    void USART2_IRQHandler(void){
    static uint8_t RxState = 0;
    static uint8_t pRxState = 0;
    if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET){
    uint8_t RxData = USART_ReceiveData(USART2);

    复制代码
          if (RxState == 0){
              if (RxData == '@' && Serial_RxFlag == 0){
                  RxState =1;
                  pRxState = 0;
              }
          }else if (RxState == 1){
              if (RxData == '\r'){
                  RxState = 2;
              } else {
                  Serial_RxPacket[pRxState] = RxData;
                  pRxState ++;
              }            
          }else if (RxState == 2){
              if (RxData == '\n'){
                  RxState =0;
                  Serial_RxPacket[pRxState] = '\0';
                  Serial_RxFlag = 1;
              }
          }
          
          USART_ClearITPendingBit(USART2, USART_IT_RXNE);
      }

    }

  • Serial_TEXT.h

    #ifndef __SERIAL_TEXT_H
    #define __SERIAL_TEXT_H

    #include <stdio.h>

    extern char Serial_RxPacket[];
    extern uint8_t Serial_RxFlag;

    void Serial_Init(void);
    void Serial_SendByte(uint8_t Byte);
    void Serial_SendArray(uint8_t *Array, uint16_t Length);
    void Serial_SendString(char *String);
    void Serial_SendNumber(uint32_t Number, uint8_t Length);
    int fputc(int ch, FILE *f);
    void Serial_printf(char *format, ...);

    #endif


文中部分知识参考:B 站 ------ 江协科技;百度百科

相关推荐
celeste03102 小时前
Redis Summary
linux·运维·服务器·redis·笔记
会编程的李较瘦2 小时前
【C语言程序设计学习】一、C语法基础
c语言·开发语言·学习
zzh0812 小时前
nginx安全笔记
笔记·nginx·安全
困死,根本不会2 小时前
【C 语言】指针学习笔记:从底层原理到实战应用
c语言·开发语言·笔记·学习·算法
努力努力再努力...3 小时前
学习Multipath多路径
学习
小郝 小郝3 小时前
51 与32 单片机LED控制详解
c语言·开发语言·经验分享·学习·51单片机
白掰虾3 小时前
一分钟上手STM32CubeMX2——STM32C5点灯
stm32·单片机·嵌入式硬件·stm2cubemx2·stm32c5
金山几座3 小时前
C#学习记录-类(Class)
开发语言·学习·c#
CHENJIAMIAN PRO3 小时前
3D Tiles 2.0 技术审查整理笔记
笔记·3d