第二章 STM32 串口通信入门:通信协议核心字段深度解析
“一套基于真实工业协议的 STM32 通信开发实战指南,涵盖从串口基础到 Bootloader 升级的全流程, EEPROM 存储、Flash 故障记录等功能实现,可直接用于项目开发或竞赛实战。
·
文章目录
前言
在《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 倍。
六、后续内容更新
- 下期预告 :
CSDN栏 模块一章节三《第三章 STM32 串口通信入门:全流程读取指令实现》 - 互动福利 :
评论区留言 “协议”,赠送《本系列通信协议》PDF。
B站搜索相同ID:通信翻车员,可查看相关视频讲解
更多推荐



所有评论(0)