前言

在《STM32 串口通信协议开发实战全攻略》系列中,通信协议的可靠性依赖于 7 字节帧结构的精密设计。本文聚焦SOI、CID1/CID2、LENGTH、INFO、CHKSUM、EOI六大核心字段 ,结合文档规范与标准库代码,解析其底层逻辑与实战要点。
本项目所有功能效果展示视频链接


一、字段 1与7:SOI(起始标志位)、 EOI(结束标志位)

1.设计目的:精准同步帧边界

  • 取值:固定0x7E(文档 2.2 节),二进制01111110,具备唯一性(避免与普通数据冲突)。
  • 核心作用
    过滤无效字节流:当接收端持续收到乱码时,仅当检测到0x7E才开始存储数据。
    支持粘包处理:多帧连续传输时(如7E…EF7E…EF),通过0x7E明确分割帧起始。

2.帧终止的 “句点”

  • 取值:固定0xEF(文档 2.2 节),二进制11101111,与 SOI(0x7E)形成互补对称,便于快速识别。。
  • 核心作用
    明确帧结束:接收端检测到0xEF后,停止当前帧数据采集,触发帧处理逻辑。
    防数据截断:若传输中因干扰导致帧不完整(如缺少 EOI),接收端因未检测到结束符而丢弃无效数据。

3.代码实现

可参考《第一章 STM32 串口通信入门:自定义通信协议设计逻辑》帧解析逻辑部分

二、字段 2-3:CID1 与 CID2(控制标识码)

1.二维指令寻址体系

CID1:主功能分类(文档 2.3 节)

CID1 值 功能分类 典型指令案例
0x46 读取参数 读温度(CID2=01H)、读温度保护参数(02H)
0x47 设置参数 写温度参数(CID2=01H)
0xEE 软件升级指令 跳转 Bootloader(CID2=00H)

CID2:子功能标识 / 响应码(文档 2.4 节)
请求帧:子功能码,如01H= 温度读取,05H= 标定查询。
响应帧:状态码,如00H= 正常,F1H=CID1 错误(主功能码无效)。

2.指令分发与错误处理

//*******************************************************************//
// 函 数 名:MessageManage_PC
// 函数描述:上位机消息管理
// 输入参数:无
// 输出参数:无
// 其    他:无
//*******************************************************************//
void MessageManage_PC(void)
{
	Uart1_Info.Feedback[0] = 0xFF;
	Uart1_Info.Feedback[1] = 0x00;
	Uart1_Info.Feedback[2] = 0x00;

		if (Uart1_Info.RevEnd_Flag && (Uart1_Info.Mhr == 0x7E))
		{
			if (Uart1_Info.ReceiveBuf[Uart1_Info.ReceiveBuf[3] + 4] == CalculateCRC8(&Uart1_Info.ReceiveBuf[1], Uart1_Info.ReceiveBuf[3] + 3)) // 校验通过
			{
				if(Uart1_Info.ReceiveBuf[1] == 0x46) //查询指令
				{
					MessageType = Uart1_Info.ReceiveBuf[2]; // 转存命令类型

					Process_InquiryData(MessageType);
						
				}
				else if(Uart1_Info.ReceiveBuf[1] == 0x47) //控制指令
				{
					for (Public_i = 0; (Public_i < Uart1_Info.ReceiveBuf[3]) && (Public_i < 110); Public_i++)
					{
						Calibrate_Buf[Public_i] = Uart1_Info.ReceiveBuf[Public_i + 4]; // 转存标定数据
					}
					
					Process_ControlData(Uart1_Info.ReceiveBuf[2],Uart1_Info.ReceiveBuf[3]);
				}

				else if(Uart1_Info.ReceiveBuf[1] == 0xEE) //升级指令
				{
					Updata_Flag = 1;
					Uart1_Info.Feedback[2] = 0x00;
					Usart1_Transmitter(Uart1_Info.Feedback,3);
				}
				else
				{
					MessageType = 0;
					CalibrateType = Calibrate_NO;
					Uart1_Info.Feedback[2] = 0xF1; // CID1指令错误
				}
			}
			else
			{
				Uart1_Info.Feedback[2] = 0xF0; // 数据校验错误
				MessageType = 0;
				CalibrateType = Calibrate_NO;
			}
			USART1_Receive_Init();
		}
	
	
	
	if (!MessageType && !CalibrateType && Uart1_Info.Feedback[2] != 0x00)
	{
		
		Usart1_Transmitter(Uart1_Info.Feedback, 3); // 反馈状态
	}

	MessageType = 0;
	

	
	
}

三、字段 4:LENGTH(INFO 字节长度)

1.动态数据的 “安全边界”

  • 取值范围:0x00~0xFF,表示 INFO 字段的字节数(文档 2.5 节)。
  • 核心作用
    防止缓冲区溢出:若 LENGTH=20,接收端仅分配 20 字节空间给 INFO,避免写入越界。
    快速校验数据完整性:接收完成后,若实际 INFO 长度≠LENGTH,直接返回F3H(长度错误)。

2.长度校验代码实现

//*******************************************************************//
// 函 数 名:Process_ControlData
// 函数描述:处理控制指令接收的数据
// 输入参数:Uart1_Info.ReceiveBuf[2],Uart1_Info.ReceiveBuf[3]
// 输出参数:无
// 其    他:无
//*******************************************************************//
void Process_ControlData(uint8_t DataType,uint8_t DataLen)
{
	Uart1_Info.Feedback[0] = 0xFF;
	Uart1_Info.Feedback[1] = 0x00;
	Uart1_Info.Feedback[2] = 0x00;
	switch(DataType)
	{

		case 01:
			if (DataLen == 0x20) // 检查数据长度
			{
				CalibrateType = Calibrate_TempProtect; // 准备更新温度参数
				
			}
			else
			{
				CalibrateType = Calibrate_NO;
				Uart1_Info.Feedback[2] = 0xF3;   //长度错误
				Usart1_Transmitter(Uart1_Info.Feedback, 3); // 反馈状态
			}
			break;
		case 02:
			if (DataLen == 0x20) // 检查数据长度
			{
				CalibrateType = Calibrate_VoltProtect; // 准备更新电压参数
				
			}
			else
			{
				CalibrateType = Calibrate_NO;
				Uart1_Info.Feedback[2] = 0xF3;   //长度错误
				Usart1_Transmitter(Uart1_Info.Feedback, 3); // 反馈状态
			}
			break;
		case 03:
			if (DataLen == 0x01) // 检查数据长度
			{
				CalibrateType = Calibrate_InitParameter; // 准备恢复初始参数
				
			}
			else
			{
				CalibrateType = Calibrate_NO;
				Uart1_Info.Feedback[2] = 0xF3;   //长度错误
				Usart1_Transmitter(Uart1_Info.Feedback, 3); // 反馈状态
			}
			break;
		case 04:
			if (DataLen == 0x08) // 检查数据长度
			{
				CalibrateType = Calibrate_TempAndVolt; // 校准电压温度参数
				
			}
			else
			{
				CalibrateType = Calibrate_NO;
				Uart1_Info.Feedback[2] = 0xF3;   //长度错误
				Usart1_Transmitter(Uart1_Info.Feedback, 3); // 反馈状态
			}
			break;
		case 05:
			if (DataLen == 0x01) // 检查数据长度
			{
				CalibrateType = ControlLed_OpenOrColse; // LED控制
				
			}
			else
			{
				CalibrateType = Calibrate_NO;
				Uart1_Info.Feedback[2] = 0xF3;   //长度错误
				Usart1_Transmitter(Uart1_Info.Feedback, 3); // 反馈状态
			}
			break;
		case 06:
			if (DataLen == 0x01) // 检查数据长度
			{
				CalibrateType = ControlLed_HistoryArea; // LED控制
				
			}
			else
			{
				CalibrateType = Calibrate_NO;
				Uart1_Info.Feedback[2] = 0xF3;   //长度错误
				Usart1_Transmitter(Uart1_Info.Feedback, 3); // 反馈状态
			}
			break;
		default:
			CalibrateType = Calibrate_NO;
			Uart1_Info.Feedback[2] = 0xF2;
			Usart1_Transmitter(Uart1_Info.Feedback, 3); // 反馈状态
			break;
	}
}

典型错误场景

  • 上位机误发LENGTH=03,但 INFO 实际为 2 字节→STM32 返回F3H,提示上位机检查指令格式。
  • 多字节参数传输时(如温度参数需 36 字节,文档 4.2 节),LENGTH 需精准匹配,否则解析失败。

四、字段 5:INFO(数据字段)

1.数据传输的 “集装箱”

  • 内容
    请求帧:控制数据(如温度阈值、校准参数)
    响应帧:应答数据(如温度值、温度参数)
  • 传输规则
    先传高字节,后传低字节(大端序,文档 2.6 节)。

2.数据解析实战:读取与下写

/***********************************************************************/
// 函数名称:SendTemptData
// 函数描述:温度数据上传到上位机
// 输入参数:无
// 输出参数:无
// 其    他:无
/***********************************************************************/
void SendTempData(void)
{
	ShareBuf[0] = 0x7E; // 起始符
	ShareBuf[1] = 0x46; // CID1
	ShareBuf[2] = 0x01; // RNT
	ShareBuf[3] = 0x02; // Len
	
	ShareBuf[4] = ADC_DataInfo.Temp_Info>>8 ; // Data
	ShareBuf[5] = ADC_DataInfo.Temp_Info & 0x00FF; // Data
	
	ShareBuf[6] = CalculateCRC8(&ShareBuf[1], ShareBuf[3] + 3);; // CRC
	ShareBuf[7] = 0xEF; // 结束符
	
	Usart1_Transmitter(ShareBuf, ShareBuf[3] + 6); // 发送数据

}

五、字段 6:CHKSUM(CRC8 校验码)

1.数据可靠性的 “守门人”

  • 校验范围:从 CID1 到 INFO 的所有字节(文档 2.7 节),不包含 SOI、CHKSUM、EOI。
  • 多项式:X^8 + X^2 + X + 1(0x07)

2.代码优化实现

可参考《第一章 STM32 串口通信入门:自定义通信协议设计逻辑》CRC8 校验查表函数部分
性能对比

  • 逐位计算法:每字节需 8 次循环,100 字节数据需 800 次运算。
  • 查表法:直接索引预生成的 256 字节表,100 字节仅需 100 次运算,速度提升 8 倍。

六、后续内容更新

  1. 下期预告
    CSDN栏 模块一章节三《第三章 STM32 串口通信入门:全流程读取指令实现》
  2. 互动福利
    评论区留言 “协议”,赠送《本系列通信协议》PDF。
    B站搜索相同ID:通信翻车员,可查看相关视频讲解

STM32 串口通信协议开发实战全攻略:STM32 串口通信・数据存储・Bootloader 全案例解析

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐