OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 3570|回复: 13

发一个关于modbus-rtu通讯的大致思路及主要模块

[复制链接]

5

主题

34

帖子

0

精华

初级会员

Rank: 2

积分
107
金钱
107
注册时间
2014-3-24
在线时间
19 小时
发表于 2017-9-18 14:15:35 | 显示全部楼层 |阅读模式
最近一直工作需要在学习modbus协议,小白一名吧,找了很多资料,但是依旧无从下手,最终借鉴着前辈的足迹,写下如下个人学习经验,仅供参考,欢迎指正!
首先,说一点modbus,这个看起来就头大,洋洋洒洒好多页,其实我想说一点,把它当作一个规定就好,按照它的要求去发送自己的功能代码。
一般模式:{0x00,0x03,0x00,0x05,0x00,0x03,0x00,0x00}; //从机地址,功能码,寄存器高,寄存器低,读寄存器个数高,读寄存器个数低,CRC低,CRC高--------具体这句代码是要做什么,这个就是modbus的内容,必须要知道(不知道就要去看噢!)。
直奔主题:思路和代码对应
第一步:拟定指令协议--其实就是我们要利用modbus来做什么。就是上面的一般模式那行(建议自己做一个文档来记录说明)。
第二步:找合适的数据结构来装我们要操作的数据(接收缓冲区)。
一般是结构体,根据自己需要来安排,如我的就是以下:
typedef struct
{
      bool     MesgRecvState;
      bool     MesgExecState;

     uint16_t MesgLength;
      uint8_t  MesgData[MB_ADU_MAX_LEN];  
}MBMESG_Typedef;
第三步:轮训发送指令。
    while(1)
    {
       if(TIM2StateType.T2State==true)//0.5S   
     {
          static uint16_t send_count=0;
      
         if(send_count%3==0)
        {
                    Modbus_Require_Communication(); //串口4与传感器--485(与本帖相关的是这个,另外两个是其他的)   
        }
        else if(send_count%3==1)
        {
                 IOBoard1_SendDataToIOBoard2(); //串口2与IO板2
               }
        else if(send_count%3==2)
        {
                     IOBoard1_SendDataToIOBoard3(); //串口3与IO板3
               }      
        send_count++;
        
        TIM2StateType.T2State=false;
     }
     
     Modbus_Response_Communication();
     
     ADU2_Response_Communication();
     
       ADU3_Response_Communication();
     
          if(TIM2StateType.T3State==true) //1.0S
     {
               IOBoard1_SendDatatoCKBoard(); //串口1与程控板
         TIM2StateType.T3State=false;
          }
          //IWDG_Feed();     
    }
这个是一个主要的框架吧,“ Modbus_Require_Communication(); //串口4与传感器--485”,与本帖相关的是这个,另外两个是其他的通讯。  
第四步:数据接收。
在上一步,我们把指令协议发出去了,就是说我们通过modbus协议向其他外设要东西了,那么其他设备遵循modbus协议就要返回相应的数据,于是,我们就要接收数据。(这是我们的重点)
我这里是通过串口中断来接收的:
if(USART_GetITStatus(UART4, USART_IT_RXNE) != RESET)
{
        recv_data = USART_ReceiveData(UART4);   
       Modbus_Message_Input_Data(&ModbusMessage, TIM7, recv_data);   //具体在下面
       USART_ClearITPendingBit(UART4, USART_IT_RXNE);  
}  
void Modbus_Message_Input_Data(MBMESG_Typedef *MBMESGx,TIM_TypeDef* TIMx, uint16_t Data)
{
     if(MBMESGx->MesgRecvState==false)//接收缓冲区能接收吗?
   {
          if(MBMESGx->MesgLength<MB_ADU_MAX_LEN)//接收的数据长度,超过最大接收数据长度了吗?
     {
               if(MBMESGx->MesgLength==0)//第一个数据,要开启定时器
        {
                    TIM_Cmd(TIMx, ENABLE);
               }
        
           MBMESGx->MesgData[MBMESGx->MesgLength]=Data;
            MBMESGx->MesgLength++;
         TIM_SetCounter(TIMx,0);//清定时器
          }
     else
     {
               MBMESGx->MesgRecvState=true;
     }
}
通过定时器中断来判断(3.5t),接收一帧数据是否完成,如下:
void TIM7_IRQHandler(void)
{
  if(TIM_GetITStatus(TIM7,TIM_IT_Update)!=RESET)
  {
     TIM_Cmd(TIM7,DISABLE); //关闭定时器7
     TIM_SetCounter(TIM7,0);//定时器7清零
   
        Modebus_Receive_Set(&ModbusMessage);
     Modbus_ADU_Copy_Message(&ModbusMessage);
     Modbus_Message_Rest(&ModbusMessage);
   
    TIM_ClearITPendingBit(TIM7, TIM_IT_Update);  //清除TIM7更新中断标志  
  }
}
中断中的函数:
void Modbus_ADU_Receive_Set(MBADU_Typedef *MBADUx)//置接收完成标志
{
     MBADUx->ADURecvState=true;
}
void Modbus_ADU_Rest(MBADU_Typedef *MBADUx)//清接收缓冲
{
     MBADUx->ADURecvState=false;
    MBADUx->ADUExecState=false;

     MBADUx->ADULength=0;

    memset(MBADUx->ADUData,0,MB_ADU_MAX_LEN);
}
MBADU_Typedef *Modbus_Receive_Index(void)//找寻可接收结构体
{  
    uint8_t index=0;
    uint8_t flag_sum=0;

    for(index=0; index<MB_MAX_ADU_QUEUE; index++)
    {
          flag_sum = MBADUQueue[index].ADURecvState;//????????---flag_sum += MBADUQueue[index].ADURecvState;
     }
   
   if(flag_sum==MB_MAX_ADU_QUEUE)
   {
          return NULL;
     }
   
    for(index=0; index<MB_MAX_ADU_QUEUE; index++)
     {
          if(MBADUQueue[index].ADURecvState==false)
          {      
               return &MBADUQueue[index];
          }
     }
     return NULL;
}
void Modbus_ADU_Copy_Message(MBMESG_Typedef *MBMESGx)//数据赋值
{
     MBADU_Typedef *ADUx=NULL;
    ADUx=Modbus_Receive_Index();
    if(ADUx==NULL) return;

    ADUx->ADURecvState=MBMESGx->MesgRecvState;

    ADUx->ADULength=MBMESGx->MesgLength;

    ADUx->ADURecvState=true;

     memcpy(ADUx->ADUData,MBMESGx->MesgData,MBMESGx->MesgLength);
}
其实以这步做的就是:通过数据帧之间的间隔来判断数据是否接收完成,接收完成之后,从接收缓冲区中把数据存到另外一个待处理缓冲区。
第五步:数据发送。
void Modbus_Response_Communication(void)
{
     MBADU_Typedef *ExeADUx=NULL;
    ExeADUx=ModBus_Execte_Index();
    if(ExeADUx==NULL) return;

     Modbus_ADU_Analysis(ExeADUx);

    Modbus_ADU_Rest(ExeADUx);
}

MBADU_Typedef *ModBus_Execte_Index(void)//查询待处理缓冲区
{
    uint8_t index=0;

     for(index=0; index<MB_MAX_ADU_QUEUE; index++)
     {
          if(MBADUQueue[index].ADURecvState==true)
          {      
               return &MBADUQueue[index];
          }
     }
     return NULL;
}

void Modbus_ADU_Analysis(MBADU_Typedef *MBADUx)//对所接收的数据判断
{
    if(Modbus_ReceiveIsOk(MBADUx)==true)
   {
          if(Modbus_LengthIsOk(MBADUx)==true)
     {
               if(Modbus_SlaveIsOk(MBADUx)==true)
        {
                    if(Modbus_CRCIsOk(MBADUx)==true)
          {
                         uint8_t control_code=0;
           
              control_code=Modbus_ControlCode(MBADUx);
              switch(control_code)
              {
                              case MB_READ_MULTI_HOLDRESG:
                   Modbbus_Read_Multi_HoldRegs(&ModbusRegs, MBADUx);
                                   break;
               
                              case MB_READ_MULTI_INPUTRESG:
                   Modbus_Read_Multi_InputRegs(&ModbusRegs, MBADUx);
                                   break;
               
               default:
                   break;
                         }         
                    }
               }
          }
     }
}

void Modbus_ADU_Rest(MBADU_Typedef *MBADUx)//清待处理缓冲区
{
     MBADUx->ADURecvState=false;
    MBADUx->ADUExecState=false;

     MBADUx->ADULength=0;

    memset(MBADUx->ADUData,0,MB_ADU_MAX_LEN);
}

至此,就结束一轮处理。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

120

主题

7878

帖子

13

精华

资深版主

Rank: 8Rank: 8

积分
12012
金钱
12012
注册时间
2013-9-10
在线时间
427 小时
发表于 2017-9-18 15:55:06 | 显示全部楼层
回复 支持 1 反对 0

使用道具 举报

5

主题

34

帖子

0

精华

初级会员

Rank: 2

积分
107
金钱
107
注册时间
2014-3-24
在线时间
19 小时
 楼主| 发表于 2017-9-21 09:03:27 | 显示全部楼层

大神光临,不甚荣幸。班门弄斧,见谅见谅!
回复 支持 反对

使用道具 举报

2

主题

31

帖子

0

精华

初级会员

Rank: 2

积分
177
金钱
177
注册时间
2017-8-9
在线时间
30 小时
发表于 2017-10-3 15:29:16 | 显示全部楼层
楼主能不能把你的modbus程序发给我一个参考下
回复 支持 反对

使用道具 举报

5

主题

34

帖子

0

精华

初级会员

Rank: 2

积分
107
金钱
107
注册时间
2014-3-24
在线时间
19 小时
 楼主| 发表于 2017-10-9 11:46:14 | 显示全部楼层
冰也自在山 发表于 2017-10-3 15:29
楼主能不能把你的modbus程序发给我一个参考下

都在上面了呀,你先参照看一下。
回复 支持 反对

使用道具 举报

5

主题

34

帖子

0

精华

初级会员

Rank: 2

积分
107
金钱
107
注册时间
2014-3-24
在线时间
19 小时
 楼主| 发表于 2017-10-9 11:51:43 | 显示全部楼层
冰也自在山 发表于 2017-10-3 15:29
楼主能不能把你的modbus程序发给我一个参考下

这个是我自己写的,基于modbus-rtu轮训4个传感器的工程。我手里只有一个传感器,所以测试的时候也只能测一个。你可以看看。

HGDtest-Ver4.rar

5.31 MB, 下载次数: 355

modbusrtu

回复 支持 反对

使用道具 举报

2

主题

31

帖子

0

精华

初级会员

Rank: 2

积分
177
金钱
177
注册时间
2017-8-9
在线时间
30 小时
发表于 2017-10-11 17:33:30 | 显示全部楼层
huangbo265419 发表于 2017-10-9 11:51
这个是我自己写的,基于modbus-rtu轮训4个传感器的工程。我手里只有一个传感器,所以测试的时候也只能测 ...

万分感谢
回复 支持 反对

使用道具 举报

2

主题

31

帖子

0

精华

初级会员

Rank: 2

积分
177
金钱
177
注册时间
2017-8-9
在线时间
30 小时
发表于 2017-10-11 17:34:29 | 显示全部楼层
huangbo265419 发表于 2017-10-9 11:51
这个是我自己写的,基于modbus-rtu轮训4个传感器的工程。我手里只有一个传感器,所以测试的时候也只能测 ...

这个例程的效果是怎样的呢
回复 支持 反对

使用道具 举报

27

主题

95

帖子

0

精华

高级会员

Rank: 4

积分
790
金钱
790
注册时间
2014-7-7
在线时间
155 小时
发表于 2017-10-12 08:55:44 | 显示全部楼层
谢谢分享
回复 支持 反对

使用道具 举报

5

主题

34

帖子

0

精华

初级会员

Rank: 2

积分
107
金钱
107
注册时间
2014-3-24
在线时间
19 小时
 楼主| 发表于 2017-10-14 10:40:25 | 显示全部楼层
冰也自在山 发表于 2017-10-11 17:34
这个例程的效果是怎样的呢

效果就是通过485向传感器发指令,传感器返回数据呀。图片是3个传感器返回的数据。通讯协议就是modbus-rtu。

捕获.PNG
回复 支持 反对

使用道具 举报

13

主题

71

帖子

0

精华

高级会员

Rank: 4

积分
696
金钱
696
注册时间
2017-8-14
在线时间
131 小时
发表于 2017-10-14 11:07:27 | 显示全部楼层
谢谢分享,
yi?我的二哈哪去了
回复 支持 反对

使用道具 举报

2

主题

31

帖子

0

精华

初级会员

Rank: 2

积分
177
金钱
177
注册时间
2017-8-9
在线时间
30 小时
发表于 2017-10-16 10:45:57 | 显示全部楼层
huangbo265419 发表于 2017-10-14 10:40
效果就是通过485向传感器发指令,传感器返回数据呀。图片是3个传感器返回的数据。通讯协议就是modbus-rtu ...

谢谢你
回复 支持 反对

使用道具 举报

5

主题

34

帖子

0

精华

初级会员

Rank: 2

积分
107
金钱
107
注册时间
2014-3-24
在线时间
19 小时
 楼主| 发表于 2017-10-27 14:19:49 | 显示全部楼层
test···

HGDtest-Ver7 - test.rar

426.82 KB, 下载次数: 161

test

回复 支持 反对

使用道具 举报

3

主题

31

帖子

0

精华

初级会员

Rank: 2

积分
73
金钱
73
注册时间
2017-10-27
在线时间
2 小时
发表于 2017-10-27 16:49:38 | 显示全部楼层
最好找现成的modbus协议。
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-9 03:33

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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