目录
[1. 什么是累加和校验](#1. 什么是累加和校验)
[2. 解决的核心痛点](#2. 解决的核心痛点)
[3. 典型工业落地场景](#3. 典型工业落地场景)
[1. 基础累加原理](#1. 基础累加原理)
[2. 8 位 / 16 位 算法区别](#2. 8 位 / 16 位 算法区别)
[3. 工业级溢出机制](#3. 工业级溢出机制)
[1. 封装设计](#1. 封装设计)
[2. 接口设计](#2. 接口设计)
[3. 鲁棒约束](#3. 鲁棒约束)
[4. 线程安全](#4. 线程安全)
前言:
嵌入式串口、无线、总线短帧通信场景中,CRC 校验算法运算量大、代码体积大、占用资源多,对于 8 位 / 16 位资源受限单片机完全冗余。
累加和校验凭借代码极简、运算极速、零内存开销,成为工业轻量级通信、传感器上报、设备交互最通用的基础校验方式。
本篇手写一套可直接商用的工业级累加和组件,支持 8bit/16bit 双模式,全参数安全校验、可重入、无全局变量、裸机 RTOS 全平台适配,彻底解决小型设备数据完整性校验问题。
一、累加和校验核心本质与应用场景
1. 什么是累加和校验
累加和校验是嵌入式最基础的数据校验算法,原理为将一帧数据所有字节逐字节累加,利用数值累加结果的唯一性标识数据完整性。
发送端:计算整帧数据累加和,附加在帧尾发送。
接收端:对接收数据重新累加,与帧尾校验值比对,一致则数据合法,不一致判定为干扰丢包。
核心特性
- 纯遍历累加,无复杂运算,MCU 耗时极低
- 无需缓存、无需查表、不占用 RAM
- 支持任意长度数据帧
- 自然溢出生效,无需额外逻辑处理
2. 解决的核心痛点
- 解决低端单片机算力不足问题:规避 CRC 复杂异或、移位运算。
- 解决短帧通信校验冗余问题:几十字节以内短报文,累加和性价比最高。
- 解决自定义协议无标准校验工具问题:统一项目校验规范,杜绝五花八门手写校验。
- 解决低功耗设备延时问题:校验耗时极短,不影响休眠与实时性。
- 解决代码臃肿问题:百行以内极简实现,不占用 Flash 资源。
3. 典型工业落地场景
- 串口自定义通信协议校验。
- LoRa、蓝牙、WiFi 短帧无线报文校验。
- 传感器单次上报数据完整性验证。
- 设备本地配置、日志、参数简单校验存储。
- 工控板简易点对点通信、下位机上报协议。
二、核心实现原理
1. 基础累加原理
定义初始累加值为 0,从数据首地址开始,逐字节叠加至累加变量,遍历结束后得到最终校验值。
2. 8 位 / 16 位 算法区别
8 位累加和 使用 uint8_t 累加,自动 256 模溢出,占用帧尾空间最小,适合 20 字节以内短帧。
16 位累加和 使用 uint16_t 累加,65536 模溢出,容错率更低、重复概率极小,适合中长数据帧。
3. 工业级溢出机制
嵌入式工程中累加和溢出不做纠错处理。
原因:收发双方运算规则完全一致,溢出时机、溢出数值完全同步,不影响校验比对结果,同时最大程度简化代码、提升运行效率。
三、工业级设计规范
1. 封装设计
完全无全局变量,纯函数式实现。 只读数据运算,不修改原始缓冲区数据。 支持多线程、多中断并发调用,天然可重入。
2. 接口设计
| 接口 | 功能说明 |
|---|---|
| check_sum8 | 8 位字节累加和校验计算 |
| check_sum16 | 16 位字节累加和校验计算 |
3. 鲁棒约束
对空指针、零长度数据做安全拦截,杜绝死机。
变量严格限定无符号整形,避免符号位干扰计算。 指针后置自增遍历,代码高效精简。
4. 线程安全
无静态变量、无全局变量、无状态依赖。
中断、任务并发调用完全安全,无需互斥锁、无需关中断。
四、完整可复用源码
1、checksum.h
#ifndef CHECKSUM_H
#define CHECKSUM_H
#include <stdint.h>
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief 8位累加和校验
* @param data 数据源缓冲区
* @param len 数据有效长度
* @return 8位累加校验和
*/
uint8_t check_sum8(const uint8_t *data, size_t len);
/**
* @brief 16位累加和校验
* @param data 数据源缓冲区
* @param len 数据有效长度
* @return 16位累加校验和
*/
uint16_t check_sum16(const uint8_t *data, size_t len);
#ifdef __cplusplus
}
#endif
#endif
2、checksum.c
#include "checksum.h"
uint8_t check_sum8(const uint8_t *data, size_t len)
{
/* 安全拦截空指针与空数据 */
if (data == NULL || len == 0)
{
return 0;
}
uint8_t sum = 0;
while (len--)
{
sum += *data++;
}
return sum;
}
uint16_t check_sum16(const uint8_t *data, size_t len)
{
if (data == NULL || len == 0)
{
return 0;
}
uint16_t sum = 0;
while (len--)
{
sum += *data++;
}
return sum;
}
五、实战演示
#include <stdio.h>
#include "checksum.h"
int main(void)
{
/* 模拟串口接收数据帧 */
uint8_t test_frame[] = {0x01, 0x06, 0x00, 0x10, 0x00, 0x01, 0x00, 0x0A};
uint8_t sum8 = check_sum8(test_frame, sizeof(test_frame));
uint16_t sum16 = check_sum16(test_frame, sizeof(test_frame));
printf("8位累加和结果: 0x%02X\r\n", sum8);
printf("16位累加和结果: 0x%04X\r\n", sum16);
return 0;
}
六、进阶优化方向
-
反码累加和优化 原始累加和容错低,可将最终结果取反,大幅提升错误识别率,工业设备常用。
-
分段累加优化 超长数据包可分段累加,防止单次遍历耗时过长,适配实时性高的系统。
-
查表加速优化 高频大数据校验场景,可预建累加表,将遍历计算转为查表读取,速度翻倍。
-
累加和 + 长度双校验 增加数据长度校验,规避 "数据错位但累加和一致" 的极端错误。
七、面试考点与易错坑点
1.面试问答
Q1:累加和校验的原理是什么?工业上为什么允许自然溢出?
答:逐字节累加数据所有内容得到校验值。收发双方计算规则完全一致,溢出数值、溢出进位完全同步,比对结果依然有效,溢出无需处理,同时最大化精简代码、提高运行速度。
Q2:累加和校验和 CRC 校验核心区别是什么?项目如何选型?
答:累加和无复杂运算、速度最快、资源最小,但容错率低;CRC 通过多项式异或,抗干扰强、可靠性高。 短帧、低干扰、低端单片机、低功耗场景用累加和;长帧、工业强干扰、高可靠通信必须用 CRC。
Q3:累加和校验存在哪些致命缺陷?
答:存在纠错盲区:多字节同时增减相同数值,累加和不变,无法识别错误;数据整体错位、整体偏移也可能校验正常,安全性远低于 CRC。
Q4:为什么本组件可以做到中断与多线程安全?
答:组件无全局变量、无静态变量、无状态缓存,所有变量均为函数局部栈变量,每次调用独立运算,互不干扰,天然可重入。
Q5:8 位累加和与 16 位累加和如何选型?
答:20 字节以内极短帧、节省帧空间选 8 位;20 字节以上中长帧、降低校验碰撞概率、提升可靠性选 16 位。
Q6:项目中如何提升累加和校验的可靠性?
答:可以采用「累加和取反」「累加和 + 数据长度双校验」「双重累加算法」,低成本提升校验准确率,兼顾速度与可靠性。
2.常见坑点
- 未做空指针判断,传入 NULL 导致硬件 HardFault。
- 数据长度传递错误,少算 / 多算字节导致校验完全错位。
- 高干扰工业环境单独使用累加和,导致数据出错无法识别。
- 混用有符号变量累加,符号位溢出导致校验值错乱。
总结
累加和校验是嵌入式工程师必须掌握的最轻量、最高效的数据校验手段,资源占用几乎为零、适配所有低端单片机、适配所有短帧通信场景。
虽然可靠性弱于 CRC,但在绝大多数普通设备通信、本地数据校验场景中完全够用,性价比极高。本组件工业级稳健封装,可直接商用落地,是嵌入式底层通用核心工具组件。
创作不易,如果对你有帮助,欢迎点赞、收藏、转发。