当前位置: 首页 > news >正文

【STM32】USART串口(下)

【STM32】USART(下)

  • 四、串口收发HEX数据包
    • 4.1 HEX数据包
    • 4.2 HEX数据包接收
  • 五、串口收发文本数据包
    • 5.1 文本数据包
    • 5.2 文本数据包接收
  • 六、串口收发数据包程序
    • 6.1 串口收发HEX数据包
    • 6.2 串口收发文本数据包

四、串口收发HEX数据包

4.1 HEX数据包

1. 固定包长,含包头包尾
在这里插入图片描述
包头:通常由特定的字节(如示例中的 0xFF)来标识数据包的开始,起到 “标记起始” 的作用,让接收方能够识别一个新数据包的开端。
固定包长:每个数据包包含的数据字节数量是固定的。比如图中 “数据包 1”,从包头 0xFF 开始,后面跟着固定数量的字节(0x01、0x02、0x03、0x04),再加上包尾 0xFE,整体长度固定。这种方式的优点是接收方可以很容易地预测每个数据包的长度,便于数据的解析和处理。
包尾:由特定字节(如示例中的 0xFE)标识数据包的结束,用于确认一个数据包的完整接收。

2.可变包长,含包头包尾
在这里插入图片描述

包头:同样由特定字节(如示例中的 0xFF)标识数据包的开始,功能与固定包长中的包头一致。
可变包长:每个数据包包含的数据字节数量不固定。如图中 “数据包 1” 和 “数据包 2” 的长度不同,“数据包 1” 包含较多字节,“数据包 2” 包含较少字节。这种方式的灵活性更高,适用于数据内容长度不固定的场景。
包尾:也由特定字节(如示例中的 0xFE)标识数据包的结束,确保接收方能够判断每个可变长度数据包的结束位置。

实际应用举例

  • 固定包长:像自动售货机定期上传库存,每次传 “饮料种类、剩余数量、价格”3 类固定数据,数据包长度固定,方便后台按固定格式快速解析,不用判断长度。
  • 可变包长:如外卖平台的订单消息,订单有时是 “单点一份餐”,有时是 “多商品 + 备注”,内容长度不定,用可变包长能灵活传输不同长度的订单信息,靠包头包尾确定每个订单的起止。

4.2 HEX数据包接收

状态机是程序中用于解析这类数据包的 “状态流转逻辑”—— 通过不同状态的切换,确保准确接收、解析数据包。
在这里插入图片描述
状态机的三个核心状态
状态机通过 S(状态变量)的取值,区分等待包头接收数据等待包尾三个阶段,每个阶段对应不同的行为:

1. 状态 S = 0:等待包头

  • 行为:程序处于“监听包头”的状态,持续检查是否收到包头字节 0xFF
  • 流转逻辑
    • 若收到0xFF 的“其他数据”:保持在 S = 0 状态,继续等待包头。
    • 若收到 0xFF:切换到 S = 1(接收数据)状态,开始接收数据包的有效数据。

2. 状态 S = 1:接收数据

  • 行为:程序开始接收数据包的“有效数据部分”(如图中包头 0xFF 后的 0x010x02 等字节)。
  • 流转逻辑
    • 未收够固定数量的有效数据(图中示例是“4个数据”,需结合具体场景定义数量):保持在 S = 1 状态,继续接收数据。
    • 收够固定数量的有效数据:切换到 S = 2(等待包尾)状态,准备验证包尾。
    • 若收到非有效数据的“其他数据”:可能触发错误处理(图中未细讲,实际场景中可能重置状态或报警)。

3. 状态 S = 2:等待包尾

  • 行为:程序处于“监听包尾”的状态,等待接收包尾字节 0xFE,以确认数据包完整。
  • 流转逻辑
    • 若收到0xFE 的“其他数据”:可能触发错误处理(如数据包不完整,需重置状态重新接收)。
    • 若收到 0xFE:认为当前数据包接收完整且有效,可以对数据进行后续处理(如图中“陀螺仪数据”的解析、使用);之后,状态机重置为 S = 0,开始下一个数据包的接收流程。

五、串口收发文本数据包

5.1 文本数据包

1. 固定包长,含包头包尾
在这里插入图片描述
包头:由特定字符 @ 标识,作为每个数据包的 “起始标记”,让接收方知道 “新数据包开始了”。
固定包长:每个数据包的长度固定。如图中 “数据包 1”,从包头 @ 开始,包含固定数量的内容(A、B、C),再加上包尾 \r\n(回车 + 换行,是常见的文本结束标记),整体长度一致。
优点:接收方可以 “按固定长度切割数据”,解析简单,不易出错。
包尾:由 \r\n 标识,作为每个数据包的 “结束标记”,让接收方知道 “当前数据包结束了”。

2. 可变包长,含包头包尾
在这里插入图片描述
包头:同样由 @ 标识,功能与 “固定包长” 的包头一致(标记数据包起始)。
可变包长:每个数据包的长度不固定。如图中 “数据包 1” 包含 A、B、C,“数据包 2” 包含 D、E,长度明显不同。
优点:灵活性高,适合 “数据内容长度不固定” 的场景(比如有时传 3 个字符,有时传 2 个字符)。
包尾:同样由 \r\n 标识,确保接收方能够判断 “可变长度的数据包何时结束”。

实际应用举例

  • 固定包长:比如 “每 10 秒传一次环境传感器数据,每次传温度、湿度、气压 3 个值”,数据长度固定,用固定包长更方便。
  • 可变包长:比如 “用户输入的命令,有时是短命令(如help),有时是长命令(如configure device xxx)”,数据长度不固定,用可变包长更灵活。

5.2 文本数据包接收

在这里插入图片描述
状态机的三个核心状态
状态机通过 S(状态变量)的取值,区分等待包头接收数据等待包尾等待包尾三个阶段,每个阶段对应不同的行为:

1. 状态 S = 0:等待包头

  • 行为:程序处于“监听包头 @”的状态,持续检查是否收到包头字符 @
  • 流转逻辑
    • 若收到@ 的“其他数据”:保持在 S = 0 状态,继续等待包头。
    • 若收到 @:切换到 S = 1(接收数据等待包尾)状态,开始接收数据包的“有效数据部分”。

2. 状态 S = 1:接收数据等待包尾

  • 行为:程序开始接收数据包的“有效数据”(如图中 @ 后的 ABCDE 等字符),同时等待包尾的第一个字符 \r
  • 流转逻辑
    • 若收到\r 的“其他数据”:保持在 S = 1 状态,继续接收有效数据。
    • 若收到 \r:切换到 S = 2(等待包尾)状态,准备验证包尾的第二个字符 \n

3. 状态 S = 2:等待包尾

  • 行为:程序处于“监听包尾的第二个字符 \n”的状态,确认数据包是否完整。
  • 流转逻辑
    • 若收到\n 的“其他数据”:可能触发错误处理(比如数据包不完整,需重置状态重新接收)。
    • 若收到 \n:认为当前数据包接收完整且有效,可以对数据进行后续处理(比如提取 ABCDE 做业务逻辑);之后,状态机重置为 S = 0,开始下一个数据包的接收流程。

六、串口收发数据包程序

6.1 串口收发HEX数据包

采用固定包长,含包头包尾,其中包头为FF,固定4字节,包尾为FE

接线图
在这里插入图片描述

  • Serial.h 模块代码
#ifndef __SERIAL_H
#define __SERIAL_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);
void Serial_Printf(char *format, ...);void Serial_SendPacket(void);
uint8_t Serial_GetRxFlag(void);#endif
  • Serial.c 模块代码
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>uint8_t Serial_TxPacket[4];				//定义发送数据包数组,数据包格式:FF 01 02 03 04 FE
uint8_t Serial_RxPacket[4];				//定义接收数据包数组
uint8_t Serial_RxFlag;					//定义接收数据包标志位/*** 函    数:串口初始化* 参    数:无* 返 回 值:无*/
void Serial_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*GPIO初始化*/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);					//将PA9引脚初始化为复用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA10引脚初始化为上拉输入/*USART初始化*/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;	//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位USART_Init(USART1, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART1/*中断输出配置*/USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//选择配置NVIC的USART1线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设/*USART使能*/USART_Cmd(USART1, ENABLE);								//使能USART1,串口开始运行
}/*** 函    数:串口发送一个字节* 参    数:Byte 要发送的一个字节* 返 回 值:无*/
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1, Byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}/*** 函    数:串口发送一个数组* 参    数:Array 要发送数组的首地址* 参    数:Length 要发送数组的长度* 返 回 值:无*/
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for (i = 0; i < Length; i ++)		//遍历数组{Serial_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:串口发送一个字符串* 参    数:String 要发送字符串的首地址* 返 回 值:无*/
void Serial_SendString(char *String)
{uint8_t i;for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止{Serial_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:次方函数(内部使用)* 返 回 值:返回值等于X的Y次方*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{uint32_t Result = 1;	//设置结果初值为1while (Y --)			//执行Y次{Result *= X;		//将X累乘到结果}return Result;
}/*** 函    数:串口发送数字* 参    数:Number 要发送的数字,范围:0~4294967295* 参    数:Length 要发送数字的长度,范围:0~10* 返 回 值:无*/
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 + '0');	//依次调用Serial_SendByte发送每位数字}
}/*** 函    数:使用printf需要重定向的底层函数* 参    数:保持原始格式即可,无需变动* 返 回 值:保持原始格式即可,无需变动*/
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数return ch;
}/*** 函    数:自己封装的prinf函数* 参    数:format 格式化字符串* 参    数:... 可变的参数列表* 返 回 值:无*/
void Serial_Printf(char *format, ...)
{char String[100];				//定义字符数组va_list arg;					//定义可变参数列表数据类型的变量argva_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中va_end(arg);					//结束变量argSerial_SendString(String);		//串口发送字符数组(字符串)
}/*** 函    数:串口发送数据包* 参    数:无* 返 回 值:无* 说    明:调用此函数后,Serial_TxPacket数组的内容将加上包头(FF)包尾(FE)后,作为数据包发送出去*/
void Serial_SendPacket(void)
{Serial_SendByte(0xFF);Serial_SendArray(Serial_TxPacket, 4);Serial_SendByte(0xFE);
}/*** 函    数:获取串口接收数据包标志位* 参    数:无* 返 回 值:串口接收数据包标志位,范围:0~1,接收到数据包后,标志位置1,读取后标志位自动清零*/
uint8_t Serial_GetRxFlag(void)
{if (Serial_RxFlag == 1)			//如果标志位为1{Serial_RxFlag = 0;return 1;					//则返回1,并自动清零标志位}return 0;						//如果标志位为0,则返回0
}/*** 函    数:USART1中断函数* 参    数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行*           函数名为预留的指定名称,可以从启动文件复制*           请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void USART1_IRQHandler(void)
{static uint8_t RxState = 0;		//定义表示当前状态机状态的静态变量static uint8_t pRxPacket = 0;	//定义表示当前接收数据位置的静态变量if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断{uint8_t RxData = USART_ReceiveData(USART1);				//读取数据寄存器,存放在接收的数据变量/*使用状态机的思路,依次处理数据包的不同部分*//*当前状态为0,接收数据包包头*/if (RxState == 0){if (RxData == 0xFF)			//如果数据确实是包头{RxState = 1;			//置下一个状态pRxPacket = 0;			//数据包的位置归零}}/*当前状态为1,接收数据包数据*/else if (RxState == 1){Serial_RxPacket[pRxPacket] = RxData;	//将数据存入数据包数组的指定位置pRxPacket ++;				//数据包的位置自增if (pRxPacket >= 4)			//如果收够4个数据{RxState = 2;			//置下一个状态}}/*当前状态为2,接收数据包包尾*/else if (RxState == 2){if (RxData == 0xFE)			//如果数据确实是包尾部{RxState = 0;			//状态归0Serial_RxFlag = 1;		//接收数据包标志位置1,成功接收一个数据包}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);		//清除标志位}
}
  • main.c 模块代码
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "Key.h"uint8_t KeyNum;			//定义用于接收按键键码的变量int main(void)
{/*模块初始化*/OLED_Init();		//OLED初始化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)				//按键1按下{Serial_TxPacket[0] ++;		//测试数据自增Serial_TxPacket[1] ++;Serial_TxPacket[2] ++;Serial_TxPacket[3] ++;Serial_SendPacket();		//串口发送数据包Serial_TxPacketOLED_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);}}
}

6.2 串口收发文本数据包

采用不固定包长,含包头包尾,其中包头为@,包尾为\r,\n

接线图
在这里插入图片描述

  • Serial.h 模块代码
#ifndef __SERIAL_H
#define __SERIAL_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);
void Serial_Printf(char *format, ...);#endif
  • Serial.c 模块代码
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>char Serial_RxPacket[100];				//定义接收数据包数组,数据包格式"@MSG\r\n"
uint8_t Serial_RxFlag;					//定义接收数据包标志位/*** 函    数:串口初始化* 参    数:无* 返 回 值:无*/
void Serial_Init(void)
{/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);	//开启USART1的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	//开启GPIOA的时钟/*GPIO初始化*/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);					//将PA9引脚初始化为复用推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);					//将PA10引脚初始化为上拉输入/*USART初始化*/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;	//停止位,选择1位USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位USART_Init(USART1, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART1/*中断输出配置*/USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断/*NVIC中断分组*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2/*NVIC配置*/NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;		//选择配置NVIC的USART1线NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设/*USART使能*/USART_Cmd(USART1, ENABLE);								//使能USART1,串口开始运行
}/*** 函    数:串口发送一个字节* 参    数:Byte 要发送的一个字节* 返 回 值:无*/
void Serial_SendByte(uint8_t Byte)
{USART_SendData(USART1, Byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);	//等待发送完成/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}/*** 函    数:串口发送一个数组* 参    数:Array 要发送数组的首地址* 参    数:Length 要发送数组的长度* 返 回 值:无*/
void Serial_SendArray(uint8_t *Array, uint16_t Length)
{uint16_t i;for (i = 0; i < Length; i ++)		//遍历数组{Serial_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:串口发送一个字符串* 参    数:String 要发送字符串的首地址* 返 回 值:无*/
void Serial_SendString(char *String)
{uint8_t i;for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止{Serial_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据}
}/*** 函    数:次方函数(内部使用)* 返 回 值:返回值等于X的Y次方*/
uint32_t Serial_Pow(uint32_t X, uint32_t Y)
{uint32_t Result = 1;	//设置结果初值为1while (Y --)			//执行Y次{Result *= X;		//将X累乘到结果}return Result;
}/*** 函    数:串口发送数字* 参    数:Number 要发送的数字,范围:0~4294967295* 参    数:Length 要发送数字的长度,范围:0~10* 返 回 值:无*/
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 + '0');	//依次调用Serial_SendByte发送每位数字}
}/*** 函    数:使用printf需要重定向的底层函数* 参    数:保持原始格式即可,无需变动* 返 回 值:保持原始格式即可,无需变动*/
int fputc(int ch, FILE *f)
{Serial_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数return ch;
}/*** 函    数:自己封装的prinf函数* 参    数:format 格式化字符串* 参    数:... 可变的参数列表* 返 回 值:无*/
void Serial_Printf(char *format, ...)
{char String[100];				//定义字符数组va_list arg;					//定义可变参数列表数据类型的变量argva_start(arg, format);			//从format开始,接收参数列表到arg变量vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中va_end(arg);					//结束变量argSerial_SendString(String);		//串口发送字符数组(字符串)
}/*** 函    数:USART1中断函数* 参    数:无* 返 回 值:无* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行*           函数名为预留的指定名称,可以从启动文件复制*           请确保函数名正确,不能有任何差异,否则中断函数将不能进入*/
void USART1_IRQHandler(void)
{static uint8_t RxState = 0;		//定义表示当前状态机状态的静态变量static uint8_t pRxPacket = 0;	//定义表示当前接收数据位置的静态变量if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)	//判断是否是USART1的接收事件触发的中断{uint8_t RxData = USART_ReceiveData(USART1);			//读取数据寄存器,存放在接收的数据变量/*使用状态机的思路,依次处理数据包的不同部分*//*当前状态为0,接收数据包包头*/if (RxState == 0){if (RxData == '@' && Serial_RxFlag == 0)		//如果数据确实是包头,并且上一个数据包已处理完毕{RxState = 1;			//置下一个状态pRxPacket = 0;			//数据包的位置归零}}/*当前状态为1,接收数据包数据,同时判断是否接收到了第一个包尾*/else if (RxState == 1){if (RxData == '\r')			//如果收到第一个包尾{RxState = 2;			//置下一个状态}else						//接收到了正常的数据{Serial_RxPacket[pRxPacket] = RxData;		//将数据存入数据包数组的指定位置pRxPacket ++;			//数据包的位置自增}}/*当前状态为2,接收数据包第二个包尾*/else if (RxState == 2){if (RxData == '\n')			//如果收到第二个包尾{RxState = 0;			//状态归0Serial_RxPacket[pRxPacket] = '\0';			//将收到的字符数据包添加一个字符串结束标志Serial_RxFlag = 1;		//接收数据包标志位置1,成功接收一个数据包}}USART_ClearITPendingBit(USART1, USART_IT_RXNE);		//清除标志位}
}
  • main.c 模块代码
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Serial.h"
#include "LED.h"
#include "string.h"int main(void)
{/*模块初始化*/OLED_Init();		//OLED初始化LED_Init();			//LED初始化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);				//OLED清除指定位置,并显示接收到的数据包/*将收到的数据包与预设的指令对比,以此决定将要执行的操作*/if (strcmp(Serial_RxPacket, "LED_ON") == 0)			//如果收到LED_ON指令{LED1_ON();										//点亮LEDSerial_SendString("LED_ON_OK\r\n");				//串口回传一个字符串LED_ON_OKOLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "LED_ON_OK");				//OLED清除指定位置,并显示LED_ON_OK}else if (strcmp(Serial_RxPacket, "LED_OFF") == 0)	//如果收到LED_OFF指令{LED1_OFF();										//熄灭LEDSerial_SendString("LED_OFF_OK\r\n");			//串口回传一个字符串LED_OFF_OKOLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "LED_OFF_OK");			//OLED清除指定位置,并显示LED_OFF_OK}else						//上述所有条件均不满足,即收到了未知指令{Serial_SendString("ERROR_COMMAND\r\n");			//串口回传一个字符串ERROR_COMMANDOLED_ShowString(2, 1, "                ");OLED_ShowString(2, 1, "ERROR_COMMAND");			//OLED清除指定位置,并显示ERROR_COMMAND}Serial_RxFlag = 0;			//处理完成后,需要将接收数据包标志位清零,否则将无法接收后续数据包}}
}

有关【STM32】USART串口(下)就到这,希望对你有所帮助,感谢观看!

http://www.dtcms.com/a/407217.html

相关文章:

  • AI 原生应用:重构内容创作的 “智能工厂” 革命
  • 桐乡住房和城乡规划建设局网站i深圳网站建设
  • 安装Neo4j5.26.12社区版本(2025年)
  • Python项目--交互式VR教育应用开发
  • 使用Comate全栈开发一个Python学习网站
  • 网站推广软件工具百度竞价被换着ip点击
  • 前端老旧项目全面性能优化指南与面试攻略
  • 破局与进化:火山引擎Data Agent从落地实践到架构未来
  • 网站不用工具开发建设易语言怎么做ifa网站填表
  • 云手机出现的意义都有哪些
  • 网站 设计 文档wordpress 打开速度
  • Python 基于 MinIO 的文件上传服务与图像处理核心实践
  • 余姚网站开发什么是手机网站
  • 9.25 深度学习7
  • 成都网站制作成都网站维护
  • 上传的网站打不开网站建设公司有哪些原
  • 【论文阅读】纯视觉语言动作(VLA)模型:全面综述
  • python做网站的优势网络营销推广方法ppt
  • 未来工厂构建蓝图:从IT/OT割裂到数据驱动的实践全解析
  • wamp:phpmyadmin访问被拒
  • 一级a做爰电影免费观看网站wordpress 评论邮箱改成电话
  • Excel——常用函数三
  • gitlab runner 里面使用harbor私仓
  • gitlab操作技巧
  • 番禺网站优化平台搜索公众号
  • 20250925让荣品RD-RK3588-MID开发板的Android13系统在长按3s才弹出关机对话框
  • 做视频资源网站有哪些内容学仿网站
  • Asymptotic Notation: Big-Oh, Big-Omega, Big-Theta, Small-Oh, Small-Omega
  • Bugku-TLS
  • 焦作住房和城乡建设厅网站做网站网站代理的犯法么