stm32F4平台modbus16位CRC两种软件计算方法代码及性能对比

modbus RTU通讯协议中,需要计算CRC校验值。modbus使用的是16位的CRC校验,多项式为x^{16}+ x^{15} + x^{2}+ 1 ,初始值为FFFF。

CRC即循环冗余校验码(Cyclic Redundancy Check):是数据通信领域中最常用的一种查错校验码,其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。

CRC算法参数模型解释:
NAME:参数模型名称。
WIDTH:宽度,即CRC比特数。
POLY:生成项的简写,以16进制表示。例如:CRC-32即是0x04C11DB7,忽略了最高位的"1",即完整的生成项是0x104C11DB7。
INIT:这是算法开始时寄存器(crc)的初始化预置值,十六进制表示。
REFIN:待测数据的每个字节是否按位反转,True或False。
REFOUT:在计算后之后,异或输出之前,整个数据是否按位反转,True或False。
XOROUT:计算结果与此参数异或后得到最终的CRC值。

在使用stm32F4单片机系统中进行modbus RTU通讯自然也需要计算CRC,有童鞋说了,stm32有硬件CRC,不占系统资源,为什么不用?虽然stm32全系都有硬件CRC,但是大部分硬件设计为32位CRC,为以太网通信设计的:

 其中,常用的F1,F2,F4等只支持固定的多项式和固定的bits,也就是说,无法计算16位CRC……白高兴一场!

硬件不行软件上,目前计算多项式的方法有查表和循环位移的方法两种,搞了个函数,通过宏定义切换两种,单独搞个文件,便于以后使用,头文件:

#ifndef _CRC_H
#define _CRC_H
#define CRC_TABLE 
#ifdef CRC_TABLE
static unsigned char TabH[] = {  //CRC高位字节值表
        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  
    } ;  
static unsigned char TabL[] = {  //CRC低位字节值表
        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  
    } ;
#endif

u16 ModbusCRCCalc(unsigned char *pbuf,unsigned char nlen);

#endif


注释掉CRC_TABLE的定义即可不使用查表法,C实现文件:

#include "crc.h"

u16 ModbusCRCCalc(unsigned char *pbuf,unsigned char nlen)
{
	
#ifdef CRC_TABLE
    unsigned int index;
    unsigned char crch = 0xFF;  //高CRC字节
    unsigned char crcl = 0xFF;  //低CRC字节
    
    while (nlen--)  //计算指定长度的CRC
    {
        index = crch ^ *pbuf++;
        crch = crcl ^ TabH[ index];
        crcl = TabL[ index];
    }
   
    return ((crch<<8) | crcl);  
  
#else

    unsigned int crc = 0xFFFF;
		unsigned char pos,i;
    for (pos = 0; pos < nlen; pos++)
    {
        crc ^= (unsigned int)pbuf[pos]; // XOR byte into least sig. byte of crc
        for (i = 8; i != 0; i--)   // Loop over each bit
        {
            if ((crc & 0x0001) != 0)   // If the LSB is set
            {
                crc >>= 1; // Shift right and XOR 0xA001
                crc ^= 0xA001;
            }
            else // Else LSB is not set
            {
                crc >>= 1;    // Just shift right
            }
        }
    }
 
    //高低字节转换
    crc = ((crc & 0x00ff) << 8) | ((crc & 0xff00) >> 8);
    return crc;
#endif 

}


都说查表法快,占存储空间,直接计算省空间,虽说F4存储空间大,但是还是对比测试一下:

		while(1)
		{
			for(tmpi=0;tmpi<10000;tmpi++)
				ModbusCRCCalc("0123456789",10);
			LED0=!LED0;//计算结束闪灯	
		}			
			

在168M主频情况下,计算10个字节的CRC,结果如下:

查表法40万次用时5秒;

直接计算40万次用时18秒。

看来还是查表法在高配置单片机环境下优先采用。

STM32F4系列微控制器使用Modbus协议进行串口通信,通常涉及到硬件配置、库函数的初始化以及数据的发送和接收。以下是使用C语言编写的一个简单的Modbus RTU(远程终端单元)通讯的基本步骤: 1. **硬件准备**: - 配置STM32F4的USART(通用异步收发器),如USART1、USART3等作为 Modbus通讯端口,连接RS- 包含必要的头文件,如`stm32f4xx_hal_usart.h`和`modbus.h`或自定义Modbus库。 ```c #include "stm32f4xx_hal.h" #include "modbus.h" ``` 3. **Modbus初始化**: - 初始化USART通信参数,比如波特率、校验、停止等。 ```c HAL_UART_Init(&huart1); // 对于USART1为例 ``` 4. **发送请求**: - 编写发送功能码和寄存器地址的数据结构,然后通过库函数封装成Modbus报文并发送。 ```c uint16_t function_code = MODBUS_TCP_REQUEST_READ_HOLDING_REGISTERS; uint16_t address = 0x0001; // 要读取的寄存器地址 uint16_t* data = (uint16_t*)buffer; // buffer用于存储响应 modbus_send_request(function_code, &address, 1, data); ``` 5. **接收响应**: - 开启中断处理程序监听接收到的数据,并解析报文获取结果。 ```c void UART_IRQHandler(void) { if (HAL_UART_GetData(&huart1, &received_data, 1) == HAL_OK) { process_received_data(received_data); } } void process_received_data(uint8_t received_data) { // 解析和处理接收到的Modbus响应数据 } ``` 6. **错误处理**: - 检查通信过程中可能出现的错误,如超时、CRC错误等,并提供相应的处理机制。 注意,这只是一个基本的框架,实际项目可能需要根据具体的Modbus库、设备特性和应用需求进行调整。此外,还需要考虑错误处理、帧同步、序列化/反序列化等因素。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值