找回密码
 立即注册
查看: 801|回复: 6

Modbus RTU 从机处理机制 跟风 赚点积分

[复制链接]
  • 打卡等级:以坛为家I
  • 打卡总天数:375
  • 最近打卡:2025-04-26 15:51:33
已绑定手机

27

主题

301

回帖

822

积分

高级会员

积分
822
发表于 2024-4-15 09:16:36 | 显示全部楼层 |阅读模式
本帖最后由 小坤 于 2024-4-15 09:28 编辑

1. 首先就是基础的串口配置就不说 。
2. 接收超时机制根据实际的波特兰进行配置,3.5个字节~4个字节都是可以的。串口波特率可以修改的话,接收超时时间可以做成自动计算或者查表类。
3.接收解析:
void Modebus_PC_task(void)
{
    if(UART3.Rx_OK)                 //接收完成
    {
                  //关闭接收
        Modbus.CRC_data = crc16( UART3.Rx_buff, (UART3.Rx_counter - 2));            //计算CRC校验值
        Modbus.CRC_H = Modbus.CRC_data >> 8;
        Modbus.CRC_L = Modbus.CRC_data & 0xFF;                                      //拆分高低位
        if((UART3.Rx_buff[UART3.Rx_counter - 2] == Modbus.CRC_H) && (UART3.Rx_buff[UART3.Rx_counter - 1] == Modbus.CRC_L))  //判断CRC校验是否正确
        {
            Modbus.address = UART3.Rx_buff[0];                                      //地址
            if((Modbus.address == Sys_addr)||(Modbus.address == Broad_addr))        //判断地址
            {
                Modbus.CMD = UART3.Rx_buff[1];                                      //功能码
                Modbus.Reg_addr =  UART3.Rx_buff[2]*256+UART3.Rx_buff[3];           //寄存器地址
                switch(Modbus.CMD )
                {
                     case 0x03 : PC_ReadReg();              break;                   //读寄存器
                     case 0x06:
                     case 0x10:  PC_WriteReg();             break;                   //写寄存器
                  //default:  Send_Err(Err_CMD);      break;                         //异常功能码
                }
            }
        }
       // Delay_Ms(1);
        memset(&UART3.Rx_buff, 0, sizeof(UART3.Rx_buff));                             //内存清零
        UART3.Rx_OK = 0;                                                              //状态赋值
        UART3.Rx_counter = 0;                                                         //清零
              // 打开接收
    }
}

4. 读寄存器
void  PC_ReadReg(void)
{
    Buff_u16_u8 Buff;
    uint8_t buff_n[255];
    uint16_t i=0;
    uint8_t n=0;

    Modbus.Reg_Len = UART3.Rx_buff[4]*256+UART3.Rx_buff[5];             //寄存器长度
    if((Modbus.Reg_Len<1)||(Modbus.Reg_Len>125))    return;             //寄存器个数范围   1<=寄存器数量<=125
    if(Modbus.Reg_addr+Modbus.Reg_Len >  Reg_len)   return;             //起始地址+寄存器长度<=总寄存器数量

    buff_n[n++] = Sys_addr ;                            //地址
    buff_n[n++] = Modbus.CMD;                           //功能码
    buff_n[n++] = Modbus.Reg_Len*2;                     //数据长度
    for(i=Modbus.Reg_addr; i<(Modbus.Reg_addr+Modbus.Reg_Len); i++ )
    {
        Buff.Dat = Modbus.Reg;
        buff_n[n++] = Buff.Byte[1];
        buff_n[n++] = Buff.Byte[0];                    //数据
    }
    Modbus.CRC_data = crc16( buff_n, n);
    buff_n[n++] = Modbus.CRC_data>>8;
    buff_n[n++] = Modbus.CRC_data&0xFF;                 //校验
    UART3_Send_n(buff_n, n);                            //中断发送
}


5.写寄存器
void PC_WriteReg()
{
    Buff_u16_u8 Buff;
    uint8_t buff_n[10];
    uint16_t i=0;
    uint8_t n=0;

    if( Modbus.CMD == 0X06 )                         //写单个寄存器
    {
        if( Modbus.Reg_addr <=  Reg_len)            //判断地址是否合规
        {
            Buff.Byte[1] = UART3.Rx_buff[4];
            Buff.Byte[0] = UART3.Rx_buff[5];         //获取数据
            Modbus.Reg[Modbus.Reg_addr] = Buff.Dat;          //寄存器赋值
        }
    }

    if( Modbus.CMD == 0X10 )        //写多个寄存器
    {
        Modbus.Reg_Len = UART3.Rx_buff[4]*256+UART3.Rx_buff[5];
        if((Modbus.Reg_Len<1)||(Modbus.Reg_Len>123))    return;         //寄存器个数范围   1<=寄存器数量<=123
        if(Modbus.Reg_Len*2!=UART3.Rx_buff[6])   return;                //数据字节数据范围  Modbus.Reg_Len*2
        if(Modbus.Reg_addr+Modbus.Reg_Len >  Reg_len)   return;        //起始地址+寄存器长度<=总寄存器数量

        for(i=Modbus.Reg_addr; i<(Modbus.Reg_addr+Modbus.Reg_Len); i++ )
        {
                Buff.Byte[1] = UART3.Rx_buff[n+7];
                n++;
                Buff.Byte[0] = UART3.Rx_buff[n+7];;
                n++;
                Modbus.Reg = Buff.Dat;            //寄存器赋值
        }
    }

    for(n=0; n<6; n++)
    {
        buff_n[n] = UART3.Rx_buff[n];       //返回值
    }
    Modbus.CRC_data = crc16(buff_n, n);
    buff_n[n++] = Modbus.CRC_data>>8;
    buff_n[n++] = Modbus.CRC_data&0xFF;     //校验
    UART3_Send_n(buff_n, n);                //发送
}


以上就是从机的读写机制;



结构体声明
/****************   Modbus定义 **************************************/
typedef struct
{
    volatile  uint8_t   address;                //地址
    volatile  uint8_t    CMD;                    //功能码
    volatile  uint16_t   Reg_addr;               //寄存器地址
    volatile  uint16_t   Reg_Len;                //寄存器长度
//   volatile  uint16_t   Read_Reg[Reg_len];      //读寄存器数组
//   volatile  uint16_t   Write_Reg[Reg_len];     //写寄存器数组
    volatile  uint16_t   Reg[Reg_len];     //写寄存器数组

    volatile  uint16_t   CRC_data;               //CRC校验
    volatile  uint8_t    CRC_H;                  //CRC H
    volatile  uint8_t    CRC_L;                  //CRC L
    volatile  uint16_t   Err_code;               //异常码
}Modbus_conf;

数据 共用体
/****************   共用体 *****************************/
typedef union
{
      unsigned int   Dat;
      unsigned char  Byte[2];
}Buff_u16_u8;

寄存器枚举
enum MODBUS_REG
{
    Start_Reg = 0,
    Sys_addr_Reg,     //设备地址
/**************** 1号  ***************************/
    Status_1_Reg,     //状态
    Power_1_Reg,      //功率大小
    Mode_1_Reg,       //模式
    Time_1_Reg,       //时间
    Temp_1_Reg,       //温度
/**************** 2号  ***************************/
    Status_2_Reg,     //状态
    Power_2_Reg,      //功率大小
    Mode_2_Reg,       //模式
    Time_2_Reg,       //时间
    Temp_2_Reg,       //温度
    Reg_len         //寄存器长度
};

可以参考的串口配置
typedef struct
{
    uint8_t state;                 //状态
    uint8_t RXDEnable;              //接收使能
    uint8_t TXDEnable;              //接收使能
    uint8_t TX_over_flag;           //发送完成标识
    uint8_t rx_buffer_overflow;     //接收溢出:
    char rx_over_time;              //接收溢出时间
    uint8_t rx_err;                 //校收出错
    uint8_t Rx_OK;                  //校收完成
    //接收
    uint8_t Rx_buff[RX_Len];//接收缓冲区
    uint8_t Rx_wr_index;                      //接收写指针
    uint8_t Rx_rd_index;                      //接收读指针
    uint8_t Rx_counter;                       //接收计数
    //发送
    uint8_t Tx_buff[TX_Len]; //发送缓冲区
    uint8_t Tx_Length;                        //需发送的数据长度
    uint8_t Tx_counter;                       //发送计数
}Typedef_COMx;

谢谢,勿喷,挣积分


回复

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:435
  • 最近打卡:2025-04-30 08:43:23

33

主题

2351

回帖

4860

积分

论坛元老

积分
4860
发表于 2024-4-15 09:21:16 | 显示全部楼层
RTU?
参考例程并不是对技术参 考手册的补充,而是对技术参 考手册的解释。
技术参 考手册不应该需要参考例程作为补充,而是解释成了参考例程的样子
回复

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:375
  • 最近打卡:2025-04-26 15:51:33
已绑定手机

27

主题

301

回帖

822

积分

高级会员

积分
822
发表于 2024-4-15 09:28:48 | 显示全部楼层

是的,写错了,修正
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:375
  • 最近打卡:2025-04-26 15:51:33
已绑定手机

27

主题

301

回帖

822

积分

高级会员

积分
822
发表于 2024-4-21 13:05:13 | 显示全部楼层
୧(๑•̀◡•́๑)૭
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家II
  • 打卡总天数:519
  • 最近打卡:2025-05-02 07:55:51
已绑定手机

46

主题

1694

回帖

2544

积分

金牌会员

积分
2544
发表于 2024-4-21 14:34:10 | 显示全部楼层
回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:55
  • 最近打卡:2025-03-14 15:41:37
已绑定手机

18

主题

25

回帖

162

积分

注册会员

积分
162
发表于 2024-5-15 17:10:20 | 显示全部楼层
支持楼主,学习了!谢谢1
回复 支持 反对

使用道具 举报 送花

  • 打卡等级:以坛为家I
  • 打卡总天数:305
  • 最近打卡:2025-04-07 14:23:16

1

主题

30

回帖

1297

积分

金牌会员

积分
1297
发表于 2024-6-11 11:18:47 | 显示全部楼层
感谢分享
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-5-2 22:10 , Processed in 0.139517 second(s), 85 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表