STM32实现Modbus RTU协议详解

        使用stm32f407封装modbus RTU协议的数据帧格式

1、 modbus协议

        Modbus协议是一种用于工业控制的网络通讯协议。同一条通信线上只有一个主设备,多个从设备,最多可以有247个从机设备


        Modbus协议 定义了四种寄存器:

        线圈(Coil):1bit,可读可写

        离散输入(Discrete Input):1bit,只读

        保持寄存器(Holding Register):1byte,可读可写

        输入寄存器(Input Register):1byte,只读


        modbus的帧格式:

  • 地址域:主机要访问的从机的地址。
  • 功能码:主机对从机实现的操作,功能码有很多,不同的功能码也对应操作不同类型的寄存器。(比如读线圈0x01、读保持寄存器0x03等等)。
  • 根据功能的不同,以及传输的数据为请求数据还是响应数据的不同,会有不同的内容。
  • 差错校验:为保障传输数据的准确性,modbus会进行差错校验,如Modbus CRC16校验等。

  • 2、代码实现

            使用stm32f407封装modbus RTU协议的数据帧格式,stm32(从机)跟modbus poll(主机)进行通信(这里直接使用串口助手是一样的效果)。

            2.1 串口配置

            串口就正常配置,1停止位、8数据位、无校验位、1停止位。开启接收中断空闲中断。但一般来说,它应保持在至少3.5个字符时间的范围内,以确保数据帧的正确区分和接收。这里直接使用空闲中断来代替这个时间间隔。

    #define USART1_RX_MAXLEN 256
    uint8_t usart1_RxData[USART1_RX_MAXLEN];   //串口接收数据缓冲区
    uint16_t usart1_Rxlen = 0;            //串口接收到数据的长度
    uint8_t usart1_RxFlag = 0;            //进入空闲中断后置为1

    // 串口1中断 
    void USART1_IRQHandler(void)
    {
        if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
        {
            //通过对 USART_DR 寄存器执行读入操作将该位RXNE清零。
            usart1_RxData[usart1_Rxlen] = USART_ReceiveData(USART1);
            usart1_Rxlen += 1;
            USART_ClearITPendingBit(USART1, USART_IT_RXNE);
        }
        
        if(USART_GetITStatus(USART1, USART_IT_IDLE) == SET)
        {
            //该位由软件序列清零(读入 USART_SR 寄存器,然后读入 USART_DR 寄存器)
            USART1->SR;
            USART1->DR;
            usart1_RxFlag = 1;
        }
    }

            2.2 modbus相关定义

    typedef enum {  
      READ_COILS = 0x01,                                              // 读线圈  
      READ_DISCRETE_INPUTS = 0x02,                        // 读离散输入  
      READ_HOLDING_REGISTERS = 0x03,                  // 读保持寄存器
       READ_INPUT_REGISTERS = 0x04,                       // 读输入寄存器  
      WRITE_SINGLE_COIL = 0x05,                                 // 写单个线圈  
       WRITE_SINGLE_REGISTER = 0x06,                      // 写单个保持寄存器  
      WRITE_MULTIPLE_COILS = 0x0F,                           // 写多个线圈  
      WRITE_MULTIPLE_REGISTERS = 0x10,                 // 写多个保持寄存器  
      // 其他功能码  
      DIAGNOSTIC = 0x08,  
    }FUNCTION_CODE;           // 枚举定义的功能码

    typedef struct {
        uint8_t send_buf[256];                                     //     从机要发送的数据
        uint8_t recv_buf[256];                                        //     从机接收到的数据
    }SLAVE_DATA;       // 从机相关的数据

    SLAVE_DATA slave_data;

    #define SLAVE_ADDRESS  0x01                        // 当前从机的地址

    以下是寄存器的定义(数据可自己修改验证)

    // 线圈
    #define    COILS_MAXNUM    32
    static uint8_t Coils[COILS_MAXNUM] = {1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0};
    // 离散量输入
    #define    DISCRETE_INPUTS_MAXNUM    32
    static uint8_t Discrete_Inputs[DISCRETE_INPUTS_MAXNUM] = {1, 1, 0, 0, 1, 1, 1, 0, 0, 1};
    // 保持寄存器
    #define HOLDING_REG_MAXNUM    32
    static uint16_t Holding_REG[HOLDING_REG_MAXNUM] = {0xA0A0, 0xB0B0, 0xC0C0, 0xD0D0, 0xE0E0, 0xF0F0, 0};
    // 输入寄存器
    #define    INPUT_REG_MAXNUM    32
    static uint16_t Input_REG[INPUT_REG_MAXNUM] = {0x1A1A, 0x1B1B, 0x1C1C, 0x1D1D, 0x1E1E, 0x1F1F, 0};

    2.3  常用功能码实现

    2.3.1 准备

    /**
      * @brief  清除从机数据的数据
      * @param  无
      * @retval 无
      */
    void Modbus_clear_SlaveData(void)
    {
        memset(slave_data.send_buf, 0, sizeof(slave_data.send_buf));
        memset(slave_data.recv_buf, 0, sizeof(slave_data.recv_buf));
    }

    /**
      * @brief  判断接受的CRC数据是否正确
      * @param  无
      * @retval 1,正确 0,不正确
      */
    uint8_t Modbus_Judge_CRC(unsigned char *data, unsigned int len)
    {
        // 由于ModBus_CRC16()这个函数 
        uint16_t recv_crc = (uint16_t)((data[len – 2] << 8) | (data[len – 1]));
        uint16_t crc = ModBus_CRC16(data, len – 2);
        
        if(recv_crc == crc) 
            return 1;
        return 0;
    }

    /**
      * @brief  判断接受的从机地址 是不是 当前从机地址
      * @param  无
      * @retval 1,是 0,不是
      */
    uint8_t Modbus_Judge_SlaveAddress(void)
    {
        if(slave_data.recv_buf[0] == SLAVE_ADDRESS)
        {
            return 1;
        }
        return 0;
    }

    /**
      * @brief  识别接收到的功能码
      * @param  无
      * @retval 功能码
      */
    uint8_t Modbus_Judge_FuncCode(void)
    {
        return slave_data.recv_buf[1];
    }

    这里补充个CRC校验

    /*
      * @name   CRC_Check
      * @brief  CRC校验
      * @param  data->   len->长度
      * @retval CRC校验值      
    */
    uint16_t ModBus_CRC16(unsigned char *data, unsigned int len)
    {
        unsigned int i, j, tmp, CRC16;

        CRC16 = 0xFFFF;             //CRC寄存器初始值
        for (i = 0; i < len; i++)
        {
            CRC16 ^= data[i];
            for (j = 0; j < 8; j++)
            {
                tmp = (unsigned int)(CRC16 & 0x0001);
                CRC16 >>= 1;
                if (tmp == 1)
                {
                    CRC16 ^= 0xA001;    //异或多项式
                }
            }
        }
            return (CRC16 << 8) | (CRC16 >> 8);  
    }

    需要进行CRC校验,可以点这里   ——>   CRC校验

    2.3.2 功能码:01H-读线圈状态

    描述:读从机线圈寄存器,位操作,可读单个或者多个;

    发送指令: 假设从机地址位0x01,寄存器开始地址0x0023,寄存器结束抵制0x0038,总共读取21个线圈。协议图如下:(这里的寄存器数量低8位应该是0x15才对

    响应: 返回数据的每一位对应线圈状态,1-ON,0-OFF,如下图; 

    上表中data1表示0x0023-0x002a的线圈状态,data1的最低位代表最低地址的线圈状态,可以理解为小端模式;

    data2表示地址0x002b-0x0033的线圈状态,如下表:

    data3表示地址0x0034-0x0038的线圈状态,不够8位,字节高位填充为0,如下表: 

    /**
      * @brief  读线圈  
      * @param  无
      * @retval 功能码
      */
    void Modbus_Function_01(void)
    {
        uint16_t Coils_start_Address = (uint16_t)((slave_data.recv_buf[2]<<8))|(slave_data.recv_buf[3]); // 线圈起始地址
        uint16_t Read_Coils_cnt = (uint16_t) ((slave_data.recv_buf[4]<<8 )| (slave_data.recv_buf[5]));  //    线圈数量
        
        slave_data.send_buf[0] = SLAVE_ADDRESS;  // 从机地址
        slave_data.send_buf[1] = READ_COILS;    //    功能码
        
        if((Read_Coils_cnt%8) == 0)  //整除
            slave_data.send_buf[2] = (uint8_t)(Read_Coils_cnt/8);
        else
            slave_data.send_buf[2] = (uint8_t)(Read_Coils_cnt/8) + 1;//返回字节数

        uint16_t k = 3;  // 跳过前面从机地址、 功能码 、 返回字节数
      for (uint16_t i = Coils_start_Address; i < Coils_start_Address + Read_Coils_cnt; i += 8)  
      {  
          slave_data.send_buf[k] = 0x00;  
          for (uint8_t j = 0; j < 8 && i + j < Coils_start_Address + Read_Coils_cnt; j++)  
          {  
              if (Coils[i + j] == 0x01)  
              {  
                  slave_data.send_buf[k] |= (1 << j);  
              }  
          }  
          k++;  
      }  
        
        uint16_t crc = ModBus_CRC16(slave_data.send_buf, 3 + slave_data.send_buf[2]);
        
        slave_data.send_buf[3 + slave_data.send_buf[2]] = (uint8_t)(crc >> 8);
        slave_data.send_buf[3 + slave_data.send_buf[2] + 1] = (uint8_t)(crc);
        
        MyUsart1_SendArray(slave_data.send_buf, 3 + slave_data.send_buf[2] + 1+ 1);
        
        Modbus_clear_SlaveData();
    }

    效果:寄存器的定义和数据初始化在  2.2(绿色为主机,白色从机)

    2.3.3 功能码:02H-读离散输入状态

    读离散输入跟读线圈格式一样,这里不过多介绍

    /**
      * @brief  读离散输入  
      * @param  无
      * @retval 无
      */
    void Modbus_Function_02(void)
    {
        uint16_t Discrete_Inputs_start_Address = (uint16_t)((slave_data.recv_buf[2]<<8))|(slave_data.recv_buf[3]);
        uint16_t Discrete_Inputs_cnt = (uint16_t) ((slave_data.recv_buf[4]<<8 )| (slave_data.recv_buf[5]));
        
        slave_data.send_buf[0] = SLAVE_ADDRESS;
        slave_data.send_buf[1] = READ_DISCRETE_INPUTS;
        
        if((Discrete_Inputs_cnt % 8) == 0)  //整除
        {
            slave_data.send_buf[2] = (uint8_t)(Discrete_Inputs_cnt/8);
        }
        else
        {
            slave_data.send_buf[2] = (uint8_t)(Discrete_Inputs_cnt/8) + 1;//返回数据的字节数
        }

            uint16_t k = 3;  
        for (uint16_t i = Discrete_Inputs_start_Address; i < Discrete_Inputs_start_Address + Discrete_Inputs_cnt; i += 8)  
        {  
            slave_data.send_buf[k] = 0x00;  
            for (uint8_t j = 0; j < 8 && (i + j < Discrete_Inputs_start_Address + Discrete_Inputs_cnt); j++)  
            {  
                if (Discrete_Inputs[i + j] == 0x01)  
                {  
                    slave_data.send_buf[k] |= (1 << j);  
                }  
            }  
            k++;  
        }  
        
        uint16_t crc = ModBus_CRC16(slave_data.send_buf, 3 + slave_data.send_buf[2]);
        
        slave_data.send_buf[3 + slave_data.send_buf[2]] = (uint8_t)(crc >> 8);
        slave_data.send_buf[3 + slave_data.send_buf[2] + 1] = (uint8_t)(crc);
        
        MyUsart1_SendArray(slave_data.send_buf, 3 + slave_data.send_buf[2] + 1+ 1);
        
        Modbus_clear_SlaveData();
    }

    效果:寄存器的定义和数据初始化在  2.2(绿色为主机,白色从机)

    2.3.4 功能码:03H-读保持寄存器

    描述:读保持寄存器,字节指令操作,可读单个或者多个; 
    发送指令: 从机地址0x01,保持寄存器起始地址0x0032,读2个保持寄存器

    响应:

    /**
      * @brief  读保持寄存器
      * @param  无
      * @retval 无
      */
    void Modbus_Function_03(void)
    {
        uint16_t Holding_REG_start_Address = (uint16_t)((slave_data.recv_buf[2]<<8))|(slave_data.recv_buf[3]);
        uint16_t Read_Holding_REG_cnt = (uint16_t) ((slave_data.recv_buf[4]<<8 )| (slave_data.recv_buf[5]));
        
        slave_data.send_buf[0] = SLAVE_ADDRESS;
        slave_data.send_buf[1] = READ_HOLDING_REGISTERS;
        slave_data.send_buf[2] = Read_Holding_REG_cnt * 2; //返回数据的字节数
        
        for(uint16_t k = 3, i = Holding_REG_start_Address; i <=Holding_REG_start_Address+Read_Holding_REG_cnt; i += 1, k += 2)
        {
            slave_data.send_buf[k] = (uint8_t)(Holding_REG[i] >> 8);
            slave_data.send_buf[k+1] = Holding_REG[i];
        }
        
        uint16_t crc = ModBus_CRC16(slave_data.send_buf, 3 + Read_Holding_REG_cnt * 2);
        
        slave_data.send_buf[3 + (Read_Holding_REG_cnt * 2)] = (uint8_t)(crc >> 8);
        slave_data.send_buf[3 + (Read_Holding_REG_cnt * 2) + 1] = (uint8_t)(crc);
        
        MyUsart1_SendArray(slave_data.send_buf, 3 + (Read_Holding_REG_cnt * 2) + 1 + 1);
        
        Modbus_clear_SlaveData();
    }

    效果:寄存器的定义和数据初始化在  2.2(绿色为主机,白色从机)

    2.3.5 功能码:04H-读输入寄存器

    同读保持寄存器格式一致,这里就不作介绍了

    /**
      * @brief  读输入寄存器
      * @param  无
      * @retval 无
      */
    void Modbus_Function_04(void)
    {
        uint16_t Input_REG_start_Address = (uint16_t)((slave_data.recv_buf[2]<<8))|(slave_data.recv_buf[3]);
        uint16_t Input_REG_cnt = (uint16_t) ((slave_data.recv_buf[4]<<8 )| (slave_data.recv_buf[5]));
        
        slave_data.send_buf[0] = SLAVE_ADDRESS;
        slave_data.send_buf[1] = READ_INPUT_REGISTERS;
        slave_data.send_buf[2] = Input_REG_cnt * 2; //返回数据的字节数
        
        for(uint16_t k = 3, i = Input_REG_start_Address; i <=Input_REG_start_Address+Input_REG_cnt; i += 1, k += 2)
        {
            slave_data.send_buf[k] = (uint8_t)(Input_REG[i] >> 8);
            slave_data.send_buf[k+1] = Input_REG[i];
        }
        
        uint16_t crc = ModBus_CRC16(slave_data.send_buf, 3 + Input_REG_cnt * 2);
        
        slave_data.send_buf[3 + (Input_REG_cnt * 2)] = (uint8_t)(crc >> 8);
        slave_data.send_buf[3 + (Input_REG_cnt * 2) + 1] = (uint8_t)(crc);
        
        MyUsart1_SendArray(slave_data.send_buf, 3 + (Input_REG_cnt * 2) + 1 + 1);
        
        Modbus_clear_SlaveData();
    }

    效果:寄存器的定义和数据初始化在  2.2(绿色为主机,白色从机)

    2.3.6 功能码:05H-写单个线圈

    描述:写单个线圈,位操作,只能写一个,写0xff00表示设置线圈状态为ON,写0x0000表示设置线圈状态为OFF 
    发送指令: 设置0x0032线圈为ON;

    响应: 同发送指令

    void Modbus_Function_05(void)
    {
        uint16_t Holding_REG_start_Address = (uint16_t)((slave_data.recv_buf[2]<<8))|(slave_data.recv_buf[3]);
        
        slave_data.send_buf[0] = SLAVE_ADDRESS;
        slave_data.send_buf[1] = WRITE_SINGLE_COIL;
        
        // 高 低8位地址
        slave_data.send_buf[2] = slave_data.recv_buf[2];
        slave_data.send_buf[3] = slave_data.recv_buf[3];
        
        // 高 低8位数据
        uint16_t coils_value = (uint16_t)((slave_data.recv_buf[4] << 8)|(slave_data.recv_buf[5]));
        
        if(coils_value == 0xff00)
        {
            Coils[Holding_REG_start_Address] = 0x01;
            slave_data.send_buf[4] = (uint8_t)0xff;
            slave_data.send_buf[5] = (uint8_t)0x00;
        }
        else if(coils_value == 0x0000)
        {
            Coils[Holding_REG_start_Address] = 0x00;
            slave_data.send_buf[4] = (uint8_t)0x00;
            slave_data.send_buf[5] = (uint8_t)0x00;
        }
        
        uint16_t crc = ModBus_CRC16(slave_data.send_buf, 6);
        slave_data.send_buf[6] = (uint8_t)(crc >> 8);
        slave_data.send_buf[7] = (uint8_t)(crc);
        
        MyUsart1_SendArray(slave_data.send_buf, 7 + 1);
        
        Modbus_clear_SlaveData();
    }

    效果:寄存器的定义和数据初始化在  2.2(绿色为主机,白色从机)

    2.3.7 功能码:06H-写单个保持寄存器

    描述:写单个保持寄存器,字节指令操作,只能写一个; 
    发送指令: 写0x0032保持寄存器为0x1232;

    响应:同发送指令;

    /**
      * @brief  写单个保持寄存器 
      * @param  无
      * @retval 无
      */
    void Modbus_Function_06(void)
    {
        uint16_t Holding_REG_start_Address = (uint16_t)((slave_data.recv_buf[2]<<8))|(slave_data.recv_buf[3]);
        
        slave_data.send_buf[0] = SLAVE_ADDRESS;
        slave_data.send_buf[1] = WRITE_SINGLE_REGISTER;

        // 高 低8位地址
        slave_data.send_buf[2] = slave_data.recv_buf[2];
        slave_data.send_buf[3] = slave_data.recv_buf[3];
        
        // 高 低8位数据
        Holding_REG[Holding_REG_start_Address] = (uint16_t)((slave_data.recv_buf[4] << 8)|(slave_data.recv_buf[5]));
        
        slave_data.send_buf[4] = (uint8_t)(Holding_REG[Holding_REG_start_Address]>>8 );
        slave_data.send_buf[5] = (uint8_t)(Holding_REG[Holding_REG_start_Address]);
        
        uint16_t crc = ModBus_CRC16(slave_data.send_buf, 6);
        slave_data.send_buf[6] = (uint8_t)(crc >> 8);
        slave_data.send_buf[7] = (uint8_t)(crc);
        
        MyUsart1_SendArray(slave_data.send_buf, 7 + 1);
        
        Modbus_clear_SlaveData();
    }

    效果:寄存器的定义和数据初始化在  2.2(绿色为主机,白色从机)

    2.3.8 功能码:0FH-写多个线圈

    描述:写多个线圈寄存器。若数据区的某位值为“1”表示被请求的相应线圈状态为ON,若某位值为“0”,则为状态为OFF。 
    发送指令: 线圈地址为0x04a5,写12个线圈, 

     上图中DATA1为0x0c,表示: 

    DATA2为0x02,不够8位,字节高位填充0: 

    响应

    /**
      * @brief  写多个线圈
      * @param  无
      * @retval 无  uint16_t Input_REG_cnt = (uint16_t) ((slave_data.recv_buf[4]<<8 )| (slave_data.recv_buf[5]));
      */
    void Modbus_Function_15(void)
    {
        uint16_t Coils_start_Address = (uint16_t)((slave_data.recv_buf[2]<<8))|(slave_data.recv_buf[3]);
        uint16_t Coils_cnt = (uint16_t) ((slave_data.recv_buf[4]<<8 )| (slave_data.recv_buf[5]));   // 写线圈的数量
        uint8_t Byte_cnt = (uint8_t)(slave_data.recv_buf[6]<<8);   //  后面数据 有多少个字节
        
        
        uint8_t k = 7; // 跳过地址和数量字段  
      
      for (uint16_t i = 0; i < Coils_cnt; i++)  
      {  
          uint8_t byte_index = i / 8;  // 字节偏移
          uint8_t bit_index = i % 8;   //位偏移
      
          if ((slave_data.recv_buf[k + byte_index] & (0x01 << bit_index)) != 0)  
          {  
              Coils[Coils_start_Address + i] = 1;  
          }  
          else  
          {  
              Coils[Coils_start_Address + i] = 0;  
          }  
      }  
        slave_data.send_buf[0] = SLAVE_ADDRESS;
        slave_data.send_buf[1] = WRITE_MULTIPLE_COILS;
        
        // 高 低8位地址
        slave_data.send_buf[2] = slave_data.recv_buf[2];
        slave_data.send_buf[3] = slave_data.recv_buf[3];
        
        // 高 低8位寄存器数量
        slave_data.send_buf[4] = slave_data.recv_buf[4];
        slave_data.send_buf[5] = slave_data.recv_buf[5];
        
        // 字节数量
        slave_data.send_buf[6] = slave_data.recv_buf[6];
        
        
        uint16_t crc = ModBus_CRC16(slave_data.send_buf, 6);
        slave_data.send_buf[7] = (uint8_t)(crc >> 8);
        slave_data.send_buf[8] = (uint8_t)(crc);
        
        MyUsart1_SendArray(slave_data.send_buf, 7 + 2); 
        
        Modbus_clear_SlaveData();
    }

    效果:寄存器的定义和数据初始化在  2.2(绿色为主机,白色从机)

    2.3.9 功能码:10H-写多个保持寄存器

    描述:写多个保持寄存器,字节指令操作,可写多个; 
    发送指令: 保持寄存器起始地址为0x0034,写2个寄存器4个字节的数据;

    响应: 

    void Modbus_Function_16(void)
    {
        uint16_t Holding_REG_start_Address = (uint16_t)((slave_data.recv_buf[2]<<8))|(slave_data.recv_buf[3]);
        uint16_t Holding_REG_cnt = (uint16_t)((slave_data.recv_buf[4]<<8))|(slave_data.recv_buf[5]);
        uint8_t Byte_cnt = (uint8_t)(slave_data.recv_buf[6]<<8);   //  后面数据 有多少个字节
        
        slave_data.send_buf[0] = SLAVE_ADDRESS;
        slave_data.send_buf[1] = WRITE_MULTIPLE_REGISTERS;

        // 高 低8位地址
        slave_data.send_buf[2] = slave_data.recv_buf[2];
        slave_data.send_buf[3] = slave_data.recv_buf[3];
        
        uint16_t k = 7; // 跳过 1地址+1功能码+2寄存器起始地址+2寄存器数量+1数据数量
        
        for(uint16_t i = 0; i < Holding_REG_cnt; i++) 
        {
            Holding_REG[Holding_REG_start_Address + i] =(uint16_t)((slave_data.recv_buf[k] << 8)| slave_data.recv_buf[k+1]);
            k+=2;
        }
        
        // 高 低8位寄存器数量
        slave_data.send_buf[4] = slave_data.recv_buf[4];
        slave_data.send_buf[5] = slave_data.recv_buf[5];
        
        uint16_t crc = ModBus_CRC16(slave_data.send_buf, 6);
        slave_data.send_buf[6] = (uint8_t)(crc >> 8);
        slave_data.send_buf[7] = (uint8_t)(crc);
        
        MyUsart1_SendArray(slave_data.send_buf, 6 + 2);
        
        Modbus_clear_SlaveData();
    }

    效果:寄存器的定义和数据初始化在  2.2(绿色为主机,白色从机)

    2.3.9  集成起来

    当串口接收完数据,就进行下面的逻辑判断。

    int main(void)
    {
        while(1)
        {
            if(usart1_RxFlag == 1) //接收到数据
            {
                Modbus_Event();     // 这个函数在下面
                usart1_Rxlen = 0;  
                usart1_RxFlag = 0;// 清标志位
            }
        }
    }

    /**
      * @brief  Modbus总体逻辑
      * @param  无
      * @retval 功能码
      */
    int8_t Modbus_Event(void)
    {
        uint8_t func_code;
        // 将串口接收到的数据,放入 slave_data.recv_buf中  
        memcpy(slave_data.recv_buf , usart1_RxData, sizeof(usart1_RxData[0])*usart1_Rxlen);
        
        // 验证从机地址是否正确
        if(Modbus_Judge_SlaveAddress() == 0) return 1;
        
        // 验证CRC校验是否正确
        if(Modbus_Judge_CRC(usart1_RxData, usart1_Rxlen) == 0) return 2;
        
        // 得到数据帧的功能码
        func_code = Modbus_Judge_FuncCode();
        
        switch(func_code)  
        {
            case READ_COILS:
                Modbus_Function_01();
                break;
            
            case READ_DISCRETE_INPUTS:
                Modbus_Function_02();
                break;
            
            case READ_HOLDING_REGISTERS:
                Modbus_Function_03();
                break;
            
            case READ_INPUT_REGISTERS:
                Modbus_Function_04();
                break;
            
            case WRITE_SINGLE_COIL:
                Modbus_Function_05();
                break;
            
            case WRITE_SINGLE_REGISTER:
                Modbus_Function_06();
                break;
            case WRITE_MULTIPLE_COILS:
                Modbus_Function_15();
                break;
            
            case WRITE_MULTIPLE_REGISTERS:
                Modbus_Function_16();
                break;
            
            default :
                break;
        }
        return 0;
    }

            

    作者:Rocket279

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32实现Modbus RTU协议详解

    发表回复