ModbusCRC16校验 示例代码

作者: Herman Ye @Galbot @Auromix
测试环境: Ubuntu20.04
更新日期: 2023/08/30
1 @Auromix 是一个机器人爱好者开源组织。
2 本文在更新日期经过测试,确认有效。

笔者出于学习交流目的,

给出以下ModbusCRC16校验常用的四种函数以及完整示例代码:

1.计算CRC

注意: 此处在末尾进行了高低位交换,可根据需求删减代码交换高低位顺序。

cpp 复制代码
unsigned short calculateModbusCRC16(const vector<uint8_t> &data) {
    int length = data.size();
    unsigned short CRC = 0xFFFF; // initial value
    for (int i = 0; i < length; i++) {
        CRC = CRC ^ data[i]; // XOR byte into least sig. byte of crc
        for (int j = 0; j < 8; j++) {
            if (CRC & 1) {
                CRC >>= 1;
                CRC ^= 0xA001;
            } else {
                CRC >>= 1;
            }
        }
    }
	unsigned short swappedCRC = ((CRC >> 8) & 0xFF) | ((CRC & 0xFF) << 8);
    return swappedCRC;
}

2.添加CRC校验位

注意: 此处进行了高低位交换,可根据需求删减代码交换高低位顺序。

cpp 复制代码
void addModbusCRC16(vector<uint8_t> &data) {
    unsigned short crc = calculateModbusCRC16(data);
    
    // Append CRC bytes to the data vector
	
    data.push_back((crc >> 8) & 0xFF);   // MSB
    data.push_back(crc & 0xFF);          // LSB

}

3.删除CRC校验位

cpp 复制代码
void removeModbusCRC16(vector<uint8_t> &dataWithCRC) {
    int length = dataWithCRC.size();
    // Error check
    if (length < 2) {
        cerr << "Invalid data length" << endl;
        return;
    }
    // Delete CRC at the end
    dataWithCRC.resize(length - 2);
}

4.比较CRC校验位

cpp 复制代码
bool compareModbusCRC16(const vector<uint8_t> &dataWithCRC) {
    int length = dataWithCRC.size();
	// Error check
    if (length < 2) {
        cerr << "Invalid data length" << endl;
        return false;
    }
    
	// Get data without CRC
    vector<uint8_t> dataWithoutCRC(dataWithCRC.begin(), dataWithCRC.end() - 2);

	// Calculate
    unsigned short calculatedCRC = calculateModbusCRC16(dataWithoutCRC);
	
	// Get original CRC
    unsigned short originalCRC = (dataWithCRC[length - 2] << 8) | dataWithCRC[length - 1];
    
    return originalCRC == calculatedCRC;
}

5.完整示例代码

cpp 复制代码
#include <iostream>
#include <vector>
#include <iomanip>
using namespace std;

unsigned short calculateModbusCRC16(const vector<uint8_t> &data) {
    int length = data.size();
    unsigned short CRC = 0xFFFF; // initial value
    for (int i = 0; i < length; i++) {
        CRC = CRC ^ data[i]; // XOR byte into least sig. byte of crc
        for (int j = 0; j < 8; j++) {
            if (CRC & 1) {
                CRC >>= 1;
                CRC ^= 0xA001;
            } else {
                CRC >>= 1;
            }
        }
    }
	unsigned short swappedCRC = ((CRC >> 8) & 0xFF) | ((CRC & 0xFF) << 8);
    return swappedCRC;
}

void addModbusCRC16(vector<uint8_t> &data) {
    unsigned short crc = calculateModbusCRC16(data);
    
    // Append CRC bytes to the data vector
	
    data.push_back((crc >> 8) & 0xFF);   // MSB
    data.push_back(crc & 0xFF);          // LSB

}

void removeModbusCRC16(vector<uint8_t> &dataWithCRC) {
    int length = dataWithCRC.size();
    // Error check
    if (length < 2) {
        cerr << "Invalid data length" << endl;
        return;
    }
    // Delete CRC at the end
    dataWithCRC.resize(length - 2);
}

bool compareModbusCRC16(const vector<uint8_t> &dataWithCRC) {
    int length = dataWithCRC.size();
	// Error check
    if (length < 2) {
        cerr << "Invalid data length" << endl;
        return false;
    }
    
	// Get data without CRC
    vector<uint8_t> dataWithoutCRC(dataWithCRC.begin(), dataWithCRC.end() - 2);

	// Calculate
    unsigned short calculatedCRC = calculateModbusCRC16(dataWithoutCRC);
	
	// Get original CRC
    unsigned short originalCRC = (dataWithCRC[length - 2] << 8) | dataWithCRC[length - 1];
	
	// Log
    cout<< "ModbusCRC16 original: "<<hex<< originalCRC<< endl;
	cout<< "ModbusCRC16 calculated: "<<hex<< calculatedCRC<< endl;
    return originalCRC == calculatedCRC;
}

int main() {
	// Example data 1
    vector<uint8_t> deviceData1 = {
        0x01, 0x10, 0x00, 0x02, 0x00, 0x06, 0x0C, 0x41, 0x20,
        0x00, 0x00, 0x42, 0xC8, 0x00, 0x00, 0x42, 0x48, 0x00, 0x00,0x84, 0xC1
    }; // Example CRC: 0x84, 0xC1

	// Print original data
    cout << "Original data 1: ";
    for (uint8_t byte : deviceData1) {
        cout << hex << uppercase << setw(2) << setfill('0') << (int)byte << " ";
    }
    cout << endl;
	bool comparedResult=compareModbusCRC16(deviceData1);
	if (comparedResult)
		cout<<"Compared result: "<<"TRUE"<<endl;
	else
    	cout<<"Compared result: "<<"FALSE"<<endl;
	
	// Example data 2
	cout<<endl;
	vector<uint8_t> deviceData2 = {
		0x01, 0x06, 0x00, 0x00, 0x01, 0x02, 0x02
	};// Example CRC: 0xDA, 0xC7
	
    cout << "Original data 2: ";
    for (uint8_t byte : deviceData2) {
        cout << hex << uppercase << setw(2) << setfill('0') << (int)byte << " ";
    }
    cout << endl;
	
    // Add CRC and print modified data
    addModbusCRC16(deviceData2);
    cout << "Add CRC to original data 2: ";
    for (uint8_t byte : deviceData2) {
        cout << hex << uppercase << setw(2) << setfill('0') << (int)byte << " ";
    }
    cout << endl;

	// Remove CRC from data
	removeModbusCRC16(deviceData2);
    cout << "Remove CRC from modified data 2: ";
    for (uint8_t byte : deviceData2) {
        cout << hex << uppercase << setw(2) << setfill('0') << (int)byte << " ";
    }
    cout << endl;	
    return 0;
}
相关推荐
xuanzdhc2 小时前
Linux 基础IO
linux·运维·服务器
愚润求学2 小时前
【Linux】网络基础
linux·运维·网络
bantinghy2 小时前
Linux进程单例模式运行
linux·服务器·单例模式
小和尚同志3 小时前
29.4k!使用 1Panel 来管理你的服务器吧
linux·运维
帽儿山的枪手3 小时前
为什么Linux需要3种NAT地址转换?一探究竟
linux·网络协议·安全
shadon1789 天前
回答 如何通过inode client的SSLVPN登录之后,访问需要通过域名才能打开的服务
linux
小米里的大麦9 天前
014 Linux 2.6内核进程调度队列(了解)
linux·运维·驱动开发
LyaJpunov9 天前
深入理解 C++ volatile 与 atomic:五大用法解析 + 六大高频考点
c++·面试·volatile·atomic
小灰灰搞电子9 天前
Qt PyQt与PySide技术-C++库的Python绑定
c++·qt·pyqt
算法练习生9 天前
Linux文件元信息完全指南:权限、链接与时间属性
linux·运维·服务器