单片机与PLC 之间通信的研究

  • 来源:消费电子
  • 关键字:单片机,PLC,通信
  • 发布时间:2021-04-07 11:02

  【摘 要】在工业自动化控制领域会经常用到PLC 与触摸屏传感器等通信,很多厂商已在通信协议方面和大多数触摸屏传感器生产厂商做好匹配。但当某些场合数据的采集方案是采用的单片机就无法直接进行协议的匹配,本文研究了PLC 与单片机通信方面进行匹配的方法。

  在很多工业自动化控制设备中,需要进行现场数据的采集,来配合其他设备实现工艺参数的调节和控制,在现场控制设备中有多钟多样的数据采集模块,有些产品无法直接进行协议的匹配。本文着重介绍单片机与PLC 通信的方案。首先,说下硬件接口电路的设计,PLC 配置了标准的RS- 232 接口或者RS-485 接口。以RS-232 接口为例它采用的是EIA/TIA-232-E 电平,而单片机的工作电平是TTL 或者CMOS 电平, 这两种不同的电平肯定是不能进行互相通信的,所以我们需要一个电路进行电平转换。比较常用的是 MAX232 串行通信芯片,MAX232 芯片集成双RS-232 驱动接收器并且采用5V 供电,便于与单片机系统采用统一电源进行工作。

  硬件接口电路完成后我们来研究下二者实现通信的软件部分如何设计。软件部分我们采用的Modbus 通信协议。之所以使用Modbus 通信协议是因为它目前应用反面不错的应用层协议,它不仅简洁也比较完善。对于之前没有了解过 Modbus 协议层的,最好不要直接移植Modbus,应该先去学习Modbus 文档,经过充分学习了该协议的内容以后,再按照协议要求开发软件部分,比如PLC 的中Modbus 部分与单片机中的Modbus 部分。虽然单片机系统的通信协议可以由编程人员自己开发,但是通过实践应用发现自己定制的协议问题很多,尤其是需要扩展的时候极为困难。所以去借鉴他人的经验当然是一个很好的途径。借鉴他人成熟的经过验证的代码,可以大大减少调试时间。Modbus 通信协议主要有三种模式,分别为ASCII 码通信模式、RTU 通信模式、 TCP 通信模式等,这个协议定义了控制器,这样我们就能认识和使用这个消息结构,这几种协议规定了消息、数据的结构、命令和应答的方式,数据的通信格式用主/ 从通信方式,数据请求消息由主设备发出,然后从设备接收到正确消息后就可以响应主设备的请求;主设备端当然,也可以去修改从设备端的数据,这样就实现双向的读写。接下来我们以RTU 模式为例来讲解,一帧数据由消息头和编码数据组成。而且每帧数据的发送要大于等于3.5 个字符的间隔时间来开始。当波特率固定时,这个时间间隔是比较容易实现的。一帧数据传输的第一个域是设备的地址。每一个设备的地址都是唯一的,在通信期间网络上的所有设备一直侦测总线。当接收到第一个数据时,收到数据的设备都进行解码,判断是否和自己的地址一样。一帧数据传输完之后,结尾以一个大于3.5 个字符时间的间隔表示一帧数据结束。新的一帧数据可在这个时间间隔以后开始。但是如果在一帧数据传输完成以前有大于1.5 个字符的间隔时间,就会导致接收设备刷新完成这个不完整的消息,就会认定接下来的数据是一个新消息的地址。这样就会导致接收到的数据都是错误的。一组数据帧的格式具体如下:初始结构应该大于等于4 个字节的时间,地址码占用1 个字节长度,功能码占用1 个字节长度,数据区根据用户需求占用的字节长度自定,错误校检占用2 字节长度,结束结构大于等于4 个字节的时间。地址码是一帧数据的第一个字节。这个字节数据是由用户设定的,是从机接收信息的地址。所以每个从机的地址码一定是唯一的,如果有雷同的地址码就会导致两台或多台从机端收到同样的信息指令。主机发送的过来的地址码是主机数据要发给从机的地址,对应地址的从机收到信息后回传从机地址以表示数据接收完成。功能码是一个数据帧的第二个字节。协议规定功能号为 1 到127。那么这些功能码到底是什么意思呢?其实很简单就是作为主机请求发送数据命令,用这些功能码来告诉从机去做什么。如果从机发送的功能码大于127,则表明从机没有正确接收到指令或发送出错。数据区是用户自定义的。数据区的数据是根据现场要求来传输的自定义数据。具体数据区的数值是用户在现场应用中自定义的数值。CRC 校验码冗余循环码总共有2 个字节,CRC 校验码是主设备计算,置于发送信息的尾部。当从机接收到后再重新计算CRC 码,计算完成后的结果来和接收到的CRC 校验码进行对比,如果两者不同,则说明接收到的数据出错。以上是RTU 模式的数据格式。

  最后我们再来说下单片机编程的CRC 校验如何实现,因为PLC 的CRC 校验可以通过软件设置好就可以了,但是单片机需要自己写程序来实现校验。通过上文我们知道 CRC 校验码包含两个字节,也就是一个16 位的二进制值码。首先定义一个全“1”的16 位寄存器,然后处理消息寄存器中的第一个字节数据。取一个字符和寄存器内容按位相或,然后结果向右移一位,最高有效位补0。提取最低有效位是1 还是0,如果最低有效位是1,则要将寄存器单独和预置的值相或一下,如果最低有效位的值是0,就不需要进行任何的操作。这样一个字节完成后,重复以上步骤。最终得到的寄存器中的值,就是消息中所有的字节数据都执行之后的CRC 值。接下来以一段例程进行讲解,首先包含2 个头文件#include,#include。定义一个发送缓冲区unsignedcharbuf[10],定义一个接收缓冲区unsignedcharrece[10],然后我们来定义一个串口初始化的函数voidserialinit(){scon=0x50; 设置串口工作方式1,10 位异步通讯模式,pcon=0x00;设置波特率不倍增 PCON=0X00;设置串口工作方式TMOD=0X20;设置定时器初值TH1=0XFA,TL1=0XFA;启动定时器TR1=1;} 再定义一个CRC 计算程序unsignedintcrc16(unsingedchar*m sg,unsignedinttalen),函数体按照上述步骤补充完毕即可。接下来我们看主函数main(){ 调用子函数对串口进行初始化serialinit(),设置一个发送的站号send_buf[0]=0x05,发送一个读操作的功能码send_buf[1]=0x03, 发送字节数send_buf[2]=0x02, 设置第一个寄存器数据高字节地址send_buf[3]=0x00,设置第一个寄存器数据低字节地址send_buf[4]=0x01, 然后计算crc 校验码crc_data=crc16 (send_buf,5)计算完成后把crc 计算结果存储到发送缓冲区send_buf[6]=crcdata>>8, 存储的是低8 位,send_ buf[5]=crc_data 存储的是高8 位} 然后进入无限循环程序 while(1){ 判断是否接收到数据if(RI){RI=0;如果接收到数据清除接收中断标志位,if(rcec_c<8){rcec_buf[rcec_ c]=SBUF;把接收缓冲区中的数据读出来存到第一个数组位,然后数据个数加一rcec_c++;直到8 个数据接收完成} 如果8 个数据接收完成后,对第一个数据中的地址进行判断,如果正确说明是发送给此机的数据},接下里就可以把其他的数据读出来进行解析和处理了}综合上面所述的程序结构,我们基本上了解了整个接收程序的流程及写法。同样的道理如果需要主机向外发送数据时,我们只需要按照步骤再写一个发送程序即可,所以我们在单片机端程序按照以上步骤调试modbus 通信协议即可实现正常通信。

关注读览天下微信, 100万篇深度好文, 等你来看……