Files
DTU-LCD/Middlewares/Modbus/CRC16.c
2026-01-24 20:03:14 +08:00

161 lines
7.6 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/******************************************************************************
* @file CRC16.c
* @brief Modbus CRC-16 校验算法实现
* @details 本文件实现了 Modbus 协议规定的 CRC-16 校验功能,采用查表法进行快速计算。
* 包括 CRC 计算、CRC 校验两个接口,适用于 Modbus RTU 等串口通信的数据完整性校验。
* @author 阜阳师范大学物电学院
* @version V0.1
* @date 2026.1.24
* @note 算法CRC-16/MODBUS多项式 0xA001即 0x8005 的反转形式)
* 初始值0xFFFF
* 查表法auchCRCHi[]、auchCRCLo[] 各 256 字节,用于快速计算
******************************************************************************/
#include "CRC16.h"
/**
* @brief CRC-16 高字节查找表auchCRCHi
* @note 基于 Modbus CRC-16 多项式 0xA001 预计算得到
* 索引 = (当前 CRC 高字节) XOR (当前数据字节)
* 表项共 256 个,对应 0x00~0xFF 的索引值
* 与 auchCRCLo[] 配合使用,实现逐字节查表计算 CRC
*/
const uint8_t auchCRCHi[] =
{
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};
/**
* @brief CRC-16 低字节查找表auchCRCLo
* @note 与 auchCRCHi[] 配对使用,共同完成 Modbus CRC-16 查表计算
* 表项共 256 个,索引计算方式与 auchCRCHi[] 相同
*/
const uint8_t auchCRCLo[] =
{
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};
/* ============================================================================
* CRC-16 计算与校验函数
* ============================================================================ */
/**
* @brief 计算 Modbus CRC-16 校验值
* @param puchMsg 待校验数据缓冲区指针(字节数组)
* @param usDataLen 参与校验的数据长度(字节数)
* @note 采用查表法计算 Modbus CRC-16
* 1. 初始 CRC 为 0xFFFF高字节 uchCRCHi=0xFF低字节 uchCRCLo=0xFF
* 2. 对每个数据字节:索引 = (当前 CRC 高字节) XOR (当前数据字节)
* 3. 查表更新CRC 高字节 = (CRC 低字节) XOR auchCRCHi[索引]
* CRC 低字节 = auchCRCLo[索引]
* 4. 返回 16 位 CRC高字节在前与 Modbus 帧中 CRC 低字节在后的存储顺序一致,
* 调用方如需附到帧尾,需注意字节序)
* 本函数不包含 CRC 域本身,仅对 puchMsg 前 usDataLen 字节计算 CRC。
* @retval uint16_t 计算得到的 16 位 CRC 校验值
*/
uint16_t Calculate_CRC(uint8_t *puchMsg, uint16_t usDataLen)
{
uint8_t uchCRCHi; /* CRC 高字节 */
uint8_t uchCRCLo; /* CRC 低字节 */
uint8_t uIndex; /* 查表索引 */
uint16_t i; /* 用于组合最终 CRC 的临时变量 */
/* Modbus CRC 初始值0xFFFF */
uchCRCHi = 0xFF;
uchCRCLo = 0xFF;
/* 逐字节参与 CRC 计算 */
while (usDataLen--)
{
/* 计算查表索引:当前 CRC 高字节与数据字节异或 */
uIndex = uchCRCHi ^ (*puchMsg++);
/* 查表更新 CRC 高字节 */
uchCRCHi = uchCRCLo ^ auchCRCHi[uIndex];
/* 查表更新 CRC 低字节 */
uchCRCLo = auchCRCLo[uIndex];
}
/* 将高、低字节组合为 16 位 CRC 值(高字节在高 8 位) */
i = (uint16_t)uchCRCHi;
i = i << 8;
i = i | uchCRCLo;
return i;
}
/**
* @brief 校验 Modbus 帧中的 CRC-16
* @param buf 完整 Modbus 帧缓冲区(含 CRC 域)
* @param len 帧长度(字节数),包含最后 2 字节 CRC
* @note 假定 CRC 存放在帧尾buf[len-2] 为 CRC 高字节buf[len-1] 为 CRC 低字节。
* 对 buf 前 (len-2) 字节计算 CRC与帧中 CRC 比较:
* - 相等返回 1表示校验通过
* - 不等返回 0表示校验失败
* 调用前需确保 len >= 2否则存在越界访问风险。
* @retval uint8_t TRUE=校验通过FALSE=校验失败
*/
uint8_t Check_CRC(uint8_t *buf, uint16_t len)
{
uint16_t crc; /* 从帧中解析出的 CRC 值 */
uint16_t value; /* 对数据部分计算得到的 CRC 值 */
/* 从帧尾取出 CRC高字节在前低字节在后 */
crc = buf[len - 2];
crc = crc << 8;
crc = crc | buf[len - 1];
/* 对除 CRC 外的数据计算 CRC */
value = Calculate_CRC(buf, len - 2);
if(crc == value)
return TRUE; /* 校验通过 */
else
return FALSE; /* 校验失败 */
}