您的位置:控制工程论坛网论坛 » 人机界面 » 单片机与人机界面通信方法(工业标准的 Modbus RTU协议)

wqlcd911

wqlcd911   |   当前状态:离线

总积分:0  2024年可用积分:0

注册时间: 0001-01-01

最后登录时间: 0001-01-01

空间 发短消息加为好友

单片机与人机界面通信方法(工业标准的 Modbus RTU协议)

wqlcd911  发表于 2012/3/30 10:14:09      2140 查看 2 回复  [上一主题]  [下一主题]

手机阅读

 

单片机与人机界面通信方法

在工控行业里,经常需要采集一些数据和控制动作,通过PLC来实现的话不但成本昂贵,而且灵活度和扩展都是问题。如果需要独立开发一种特殊功能,又需要连接触摸屏通讯,工程师在这个方面往往需要花费很大功夫,单片机与人机界面触摸屏通讯的最简单,最有效的 2种方法,其实就是分为2种通讯协议,即工业标准的 Modbus RTU协议和工程师自己定义的自由协议。
    我采用了广州微嵌公司(公司网站:http://www.wqlcd.com)的人机界面作为参考,因为其提供了一些技术支持和单片机源代码,此人机界面可支持自由协议,开发工程比较方便。
方案比较:
方案一 modbus—rtu协议:
优点:工业标准通讯协议,具有通用性,,传输数据量大
缺点:需要时间去了解协议的格式和以及按照规定编写通讯程序

(广州微嵌提供MODBUS-RTU源代码,直接移植就可以)
方案二 自由协议:
优点:数据格式客户自己定义,灵活多变,定制性强,可以模拟任何已知报文的通讯协议
缺点:传输数据量不大,通用性不强,移植不方便

工程师可以根据以上两种通讯协议的优缺点来选择理想的方案;

现在先介绍微嵌公司的人机界面的modbus—rtu协议。

首先下载人机界面的组态软件: http://www.wqlcd.com/new/league.asp?keyno=34(现在公司出来了4.0的新版本,增加了不少新功能,所以我以此版本作介绍)

下载安装好软件之后,新建一个工程文件,型号对应的是公司出产产品型号,

然后点击下面的通道选择modbus—rtu协议:

可以通过[新增]扩展通讯接口设置扩展数量由硬件决定,微嵌的人机界面串口既可以做RS232,又可以做RS485,根据客户工程需求接线,通讯协议对应的就是单片机工程师需要用的协议,其中有Modbus rtu协议,自由协议Free Protocol,当然还包括西门子200,台达PLC,欧姆龙,三菱等协议。

我们随便建一个比较简单实用的采集单片机的数据(AD采集)显示在组态软件的点阵数码管(单片机可以通过串口与计算机连接在线模拟,方便开发工程)


 

然后我们要给各通道指定一个寄存器地址,方法实现如下:

所有的通道都设置好后把工程下载到人机界面显示屏里(或在线模拟)。

给单片机板烧录modbus—rtu协议及简单的四路模拟量采集功能:

 

部分单片机modbus—rtu协议源码如下:

/**************************************************************

 监控地址分配如下

D[0] :  DA转换通道0

D[1] :  DA转换通道1

D[2] :  DA转换通道2

D[3] :  DA转换通道3

D[4] :  AD转换,输出模拟信号电压控制51板上的发光二极管D2.

D[5] :  温度传感器D18B20

D[6] :  计数器s      ,0-19s

D[13]-D[17]:虚拟IIC(只要中监控地址中不用到的都可用于虚拟IIC)

D[18]:  输入IO口

D[20]:  输出IO口

D[23]:  计数器10ms,0-9999ms

D[30]: 产生正弦波,可以用历史趋势图监控

D[35]-D[45]:有符号值                                                                          

***********************************************************/

     

#include <system.h>

#include <math.h>

 

#define  NOP() _nop_()  /* 定义空指令 */

#define  TRUE  0xff

#define  FALSE 0

 

unsigned int  Tick_10ms=0;    //时间片计数器

 

//软件界面就是通过下面定义的这2个数组M和D变量来监控单片机的数据变化的。

unsigned char  idata M[32];   //定义8位的数组变量。

short xdata D[100]; //定义16位的数组变量。相当于组态软件内的两个寄存器

    

unsigned int TestDelay1, TestDelay2, TestDelay3,TestDelay4,TempTick;

 

/*************************************************************

* (T0) 10 ms 时钟中断    

;************************************************************/

void T0zd(void) interrupt 1

{

    TH0=0xDC; //11.0592M

    TL0=0x00;

    Tick_10ms++;

}

 

/*************************************************************

*是否超时比较    

;************************************************************/

unsigned char TickOut(unsigned int * tick, unsigned int tickCnt)

{

     if(Tick_10ms <   *tick)

       {

          *tick = Tick_10ms;

       }

     if(Tick_10ms - *tick >= tickCnt) 

     {

        *tick = Tick_10ms;

        return 1;

     }

     return 0;

}

 

/*************************************************************

*正弦波产生    

;************************************************************/

#define  rate 3.14            //圆周率定义

#define  pi  100    //正弦波周期

#define  cp  500    //正弦波的幅度

double at=0;      //

unsigned short get_sin()

{

    unsigned short temp;

      temp=(unsigned short)(cp*sin(at))+cp;//temp为y轴(幅值)数据

      at=at+((2*rate)/pi);   // (2*rate)/pi——圆周公式,求每个像素(x轴)所占的

      return temp;

}

 

//***********************************************************

main()

      int i;

      TMOD|= 0x11; //定时器工作模式为模式1

      TH0=0xDC; //11.0592M

    TL0=0x00; //设置定时器0

      IE    = 0x8A;  //设置中断允许位

    TR0  = 1;  

      for(i= 0;i<100;i++)    //初此化地址值

      {

             D[i] = 0;

      }

 

      D[4] = 0x80;

      D[20] = 0x01;

 

 

      //虚拟IIC

      D[13] = 13;

      D[14] = 14;

      D[15] = 15;

      D[16] = 16;

      D[17] = 17;

 

      for(i=35;i<45;i++) //有符号数

      {

           D[i] = i - 1000;//

      }

      Modbus_Init(110592, 57600, 1, &M[0], &D[0]);

    ////////////////////////////////////////////////////////////////////////

      while(1)

      {      

         Modbus_Handle(Tick_10ms);

          D[32]=D[1]/100;

         if(TickOut(&TestDelay1,40)) //10ms*40定时

         {

             D[20] = D[20]<<1;

              if(D[20] > 0xff)

                 D[20] = 0x01;

             P1 = ~(D[20]);

         }

         if(TickOut(&TestDelay2,10)) //10ms*10定时

         {

             D[18] = P0;

              ad_da(); //D[0],D[1],D[2],D[3],D[4]

              D[30] = get_sin();  //产生正弦波,可以用历史趋势图监控

         }

       if(TickOut(&TestDelay3,100)) //10ms*100定时

         {

            

              D[6]++; //秒计数器0-19

              if(D[6]>19)

                      D[6] = 0; 

         }

         if(TickOut(&TempTick,120)) //120ms*10定时

         {

             D[5] =GetTemp();

         }

 

         if(TickOut(&TestDelay4,10)) //10ms*1定时

         {

             D[23]++;   //计数器   毫秒计数器0-9999

              if(D[23]>9999)

              {

              D[23] = 0;

              }

         }

 

      }

}

 

 

技术支持QQ:315033726

 

1楼 0 0 回复
  • autech2000

    autech2000   |   当前状态:在线

    总积分:55  2024年可用积分:0

    注册时间: 2012-05-26

    最后登录时间: 2012-05-26

    空间 发短消息加为好友

    autech2000   发表于 2012/5/26 9:17:59

    Autech奥堤司真彩人机 最人性化 性价比高的选择
    2楼 回复本楼

    引用 autech2000 2012/5/26 9:17:59 发表于2楼的内容

  • steelen

    steelen   |   当前状态:在线

    总积分:501  2024年可用积分:0

    注册时间: 2007-01-25

    最后登录时间: 2014-12-09

    空间 发短消息加为好友

    steelen   发表于 2012/5/29 15:13:01

    这段代码有问题的。

    没有处理3.5倍字符时间,定时精度太差

    3楼 回复本楼

    引用 steelen 2012/5/29 15:13:01 发表于3楼的内容

总共 , 当前 /