如何用单片机实现Modbus通信协议?

2025-09发布3次浏览

Modbus通信协议是一种广泛应用于工业自动化领域的串行通信协议,它简单、高效,支持多种设备间的数据交换。单片机作为一种常用的嵌入式控制器,可以通过编程实现Modbus通信协议。以下是使用单片机实现Modbus通信协议的详细步骤和方法:

1. 了解Modbus协议

Modbus协议主要分为两种模式:串行通信模式(RTU)和串行通信模式(ASCII)。RTU模式使用二进制格式传输数据,而ASCII模式使用文本格式传输数据。在实现Modbus通信之前,需要明确使用的模式,并了解Modbus协议的基本帧结构,包括设备地址、功能码、数据字段和校验码等。

2. 硬件连接

将单片机的串行通信接口(如UART)与Modbus从设备的串行通信接口连接。通常需要连接的引脚包括TX(发送)、RX(接收)、GND(地线)和可能的RTS/CTS(请求发送/清除发送)等。根据实际应用场景选择合适的通信速率(如9600 bps)和数据格式(如8数据位、无校验位、1停止位)。

3. 软件实现

在单片机上实现Modbus通信协议需要完成以下几个关键步骤:

3.1 初始化串行通信接口

使用单片机的内置UART模块,配置波特率、数据位、校验位和停止位等参数。例如,在STM32单片机上可以使用HAL库函数进行初始化:

UART_HandleTypeDef huart1;
void MX_USART1_UART_Init(void)
{
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 9600;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  HAL_UART_Init(&huart1);
}

3.2 编写Modbus帧发送函数

Modbus帧包括设备地址、功能码、数据字段和校验码。发送函数需要将这些字段按照Modbus协议格式组装成帧,并通过串行接口发送。以下是一个简单的RTU模式发送函数示例:

void ModbusRTUSend(uint8_t address, uint8_t function_code, uint16_t *data, uint8_t data_length) {
    uint8_t frame[256];
    uint8_t checksum = 0;
    uint16_t length = 1 + 1 + data_length + 2; // 设备地址 + 功能码 + 数据长度 + 校验码

    frame[0] = address;
    frame[1] = function_code;
    for (uint8_t i = 0; i < data_length; i++) {
        frame[2 + i] = (uint8_t)(data[i] >> 8);
        frame[3 + i] = (uint8_t)(data[i] & 0xFF);
        checksum += frame[2 + i];
        checksum += frame[3 + i];
    }
    frame[2 + data_length] = (uint8_t)(checksum & 0xFF);
    frame[3 + data_length] = (uint8_t)(checksum >> 8);

    HAL_UART_Transmit(&huart1, frame, length, 1000);
}

3.3 编写Modbus帧接收函数

接收函数需要从串行接口读取Modbus帧,并解析出设备地址、功能码、数据字段和校验码。以下是一个简单的RTU模式接收函数示例:

uint8_t ModbusRTUReceive(uint8_t *address, uint8_t *function_code, uint16_t *data, uint8_t *data_length) {
    uint8_t frame[256];
    uint8_t checksum = 0;
    uint16_t length = 0;

    if (HAL_UART_Receive(&huart1, frame, 1, 1000) != HAL_OK) return 0;
    *address = frame[0];
    if (HAL_UART_Receive(&huart1, frame, 1, 1000) != HAL_OK) return 0;
    *function_code = frame[0];

    length = 1 + 1;
    checksum = *address + *function_code;
    if (HAL_UART_Receive(&huart1, &length, 1, 1000) != HAL_OK) return 0;
    *data_length = length - 1 - 2; // 减去功能码和校验码长度
    for (uint8_t i = 0; i < *data_length; i++) {
        if (HAL_UART_Receive(&huart1, &frame[i], 1, 1000) != HAL_OK) return 0;
        checksum += frame[i];
    }
    if (HAL_UART_Receive(&huart1, &frame[*data_length], 1, 1000) != HAL_OK) return 0);
    checksum += frame[*data_length];
    if (HAL_UART_Receive(&huart1, &frame[*data_length + 1], 1, 1000) != HAL_OK) return 0);
    checksum += frame[*data_length + 1];

    if (checksum != 0) return 0;

    for (uint8_t i = 0; i < *data_length; i++) {
        data[i] = (uint16_t)(frame[2 + i] << 8) | frame[3 + i];
    }

    return 1;
}

3.4 校验码计算

Modbus协议使用CRC校验码来确保数据的完整性。可以使用现成的CRC计算库来计算校验码,例如:

uint16_t CRC16(uint8_t *data, uint8_t length) {
    uint16_t crc = 0xFFFF;
    for (uint8_t i = 0; i < length; i++) {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc = crc >> 1;
            }
        }
    }
    return crc;
}

4. 测试与调试

完成上述步骤后,需要进行测试与调试,确保单片机能够正确地发送和接收Modbus帧。可以使用Modbus测试工具(如Modbus poller)来验证通信的正确性。

5. 应用扩展

在实际应用中,可以根据需要扩展Modbus通信功能,例如支持多种Modbus功能码(如读取保持寄存器、写入单个寄存器等),实现多线程通信,或者通过网络接口(如Modbus TCP)实现网络通信。