STM32H7系列HAL库+CRC校验+串口发送数据

1.CRC有很多种类:目前文章只讲CRC-16校验Modbus RTU的使用,也就是多项式0xA001校准的使用。

——用到的管脚: 

PC10     ------> UART4_TX
PC11     ------> UART4_RX

——使用串口打印,发送数据00 10 00 01 00 01 02 03 E8 AA AF,使用CRC校验之后再发送数据。

——芯片:STM32H723ZGT6型号。

下面以一个特定的数据为例子。这个是我硬件控制1000= 0x03E8(要写入的值1000)

为了发送特定的Modbus RTU请求,如你所示的00 10 00 01 00 01 02 03 E8 AA AF,我们需要理解这串数据的结构。假定这是一个写多个寄存器(功能码0x10)的Modbus RTU帧,结构如下:

  • Slave ID: 0x00
  • Function Code: 0x10(写多个寄存器)
  • Starting Address: 0x0001
  • Number of Registers: 0x0001
  • Byte Count: 0x02(接下来的字节数)
  • Register Values: 0x03E8(要写入的值)
  • CRC: 0xAAAF(帧末尾的两字节校验码)

2.STM32Cubemax配置

——选择自己需要的芯片之后进入配置界面——

——配置管脚模式——

PC10     ------> UART4_TX
PC11     ------> UART4_RX

——选择配置模式——选择异步通信——我这里选的是异步通信——

串口通信一般有几种模式

  1. 同步串口通信:数据的发送和接收是在固定的时钟脉冲下进行的,以确保数据的准确传输。

  2. 异步串口通信:数据的传输不需要通过时钟信号进行同步。数据传输的开始和结束由特殊的起始位和停止位来标志。

  3. 半双工通信:交替地发送和接收数据,但不能同时进行发送和接收操作。这种通信方式类似于对讲机,一方发言时另一方需要倾听,不能同时进行双向通信。

  4. 全双工通信:同时进行发送和接收数据的操作,实现双向通信。这种通信方式通常需要独立的发送和接收通道,可以实现更高效的数据传输。

——配置通信参数——波特率、数据位、停止位——数据位8位,停止位2位,波特率是9600。这个按通信双方协议定。

我这里发送数据,那个设备规定的停止位是2位,数据位是8位,波特率是9600。

——中断全局选择——

——配置时钟——

按上一篇那个时钟配置——配置主时钟自动生成就可以,用到哪个模块就配置哪个模块的时钟。

——最后生成代码——

3.代码部分


#define UART_HANDLE huart4
// RS485发送和接收使能的假设实现,需要根据实际硬件调整
#define RS485_TRANSMIT_ENABLE()  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_10, GPIO_PIN_SET)
#define RS485_RECEIVE_ENABLE()   HAL_GPIO_WritePin(GPIOC, GPIO_PIN_11, GPIO_PIN_RESET)

uint16_t CRC16(const uint8_t *buffer, uint16_t buffer_length);
void ModbusRTU_SendData(const uint8_t* data, uint16_t data_length);//函数声明

//基于A001计算的,多项式。CRC表
const uint16_t crcTable[256] = 
{
    0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
    0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
    0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
    0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
    0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
    0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
    0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
    0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
    0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
    0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
    0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
    0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
    0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
    0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
    0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
    0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
    0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
    0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
    0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
    0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
    0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
    0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
    0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
    0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
    0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
    0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
    0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
    0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
    0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
    0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
    0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
    0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};


//计算函数
uint16_t CRC16(const uint8_t* buffer, uint16_t buffer_length) 
{
    uint16_t crc = 0xFFFF;
    for (uint16_t pos = 0; pos < buffer_length; pos++) 
    {
        uint8_t index = (crc ^ buffer[pos]) & 0xFF;
        crc = (crc >> 8) ^ crcTable[index];
    }
    return crc;
}

void SendModbusRequest(void) 
//写到主函数里头,可以循环发送数据00 10 00 01 00 01 02 03 E8 AA AF
{
    uint8_t request[] = {0x00, 0x10, 0x00, 0x01, 0x00, 0x01, 0x02, 0x03, 0xE8};
    uint16_t crc = CRC16(request, sizeof(request));  // 计算CRC
    uint8_t complete_request[sizeof(request) + 2];
    memcpy(complete_request, request, sizeof(request));
    complete_request[sizeof(request)] = crc & 0xFF;  // CRC低字节
    complete_request[sizeof(request) + 1] = (crc >> 8) & 0xFF;  // CRC高字节

    // 发送完整的Modbus RTU请求
    ModbusRTU_SendData(complete_request, sizeof(complete_request));
}

void ModbusRTU_SendData(const uint8_t* data, uint16_t data_length) 
{
    // 通过RS485发送数据
    RS485_TRANSMIT_ENABLE();
    HAL_UART_Transmit(&UART_HANDLE, (uint8_t*)data, data_length, 100);
    RS485_RECEIVE_ENABLE();
}

——最后编译一下,有些数据错误警告处理一下,主要流程就是这样,然后调试处理一下细节,

连接对应串口TX,连接到串口的RX,调试程序就可以看到现象收到你所写的数据00 10 00 01 00 01 02 03 E8 AA AF。

4.调试结果

——我这里显示的是我自己编写的内容,至于你想显示什么,自己改变打印的数据的内容。也可以在这个基础上修改封装函数。

——这个是接收数据的窗口,出来显示效果。

以上仅仅属于本人学习心得,可供学习参考,禁止商用~

  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值