最近一客户要求使用STC12C5A60S2实现Modbus Rtu协议与KEPServerEx V4.0软件通信,采集单片机P2口每位的状态,设置P0口每位的状态,实现三路AD转换其中一路采集的是C02的浓度,以及使用SHT10获取温度和湿度。KEPServerEx V4.0使用TCP通信,而单片机使用的是串口RS232通信,所以增加了TCP转RS232的模块。

本程序相对比较简单,STH10有现成的代码,AD转换直接官方提供的程序,主要需要实现的就是Modbus RTU通信协议的实现。根据对方的要求,P0,P2口的设置需要一位一位的读取或设置,如果按Modbus的内容,可以使用设置线圈状态和读取线圈状态,而AD和温度湿度获取需要读取寄存器,这样一来就需要实现3个功能即对线圈设置线圈就读取寄存器。为了减少功能的实现数量,读取线圈和读取AD等数据都使用读取寄存器的03功能,为了方便能够位下位机传数据所以设置线圈使用06写单个寄存器。这样就只需要实现2个功能即可。在我的前一篇博客中已经介绍了使用单片机与PC进行通信,当时实现了03功能,本次只需要实现06功能即可。

06功能码的通信格式:
 上位机发送数据格式  地址,功能码,数据地址高位,数据地址低位,数据高位,数据低位,CRC低位,CRC 高位,
 下位机回应数据格式  地址,功能码,数据地址高位,数据地址低位,数据高位,数据低位,CRC低位,CRC 高位,

所以接收到上位机的命令后,只需要判断地址是否为本机地址,功能码是否为相应的06即可,如果是则对后面的数据进行解析,将相应的数据写入对应的寄存器即可。由于上位机每次发来的是对应P0口某一位的状态,所以需要有8个字节来对应8个位。而返回的信息就比较简单了,如果没有错误就直接将接收到的命令返回即可。

unsigned char createRespond_M_6(unsigned char *respondMessage,unsigned char* _messageReceived ,unsigned int* _registerData)
{
unsigned char i;
unsigned char numberOfPoints = ;
unsigned char _slaveAddress = ;
unsigned char bytesToSend = ;
unsigned char startAddress=;
unsigned char crcCalculation[]={,,}; unsigned int Address = _messageReceived[]; //将其转换为int型,由于使用的寄存器地址最多1,20个,所以只取低位即可,高位永远为0,丢弃 _registerData[Address ] = _messageReceived[]<<;
_registerData[Address ] |= _messageReceived[]; for(i = ; i < ; i ++)
{
respondMessage[i] = _messageReceived[i];
} return ;
}

由于在通信中要么是读取寄存器的值,要么是读取寄存器的做(其实就是开辟的一个整型数组),那么我们要实现设置P0口的状态和获取P2口的状态就需要在程序中根据需要,不停地更新寄存器的值或者从寄存器中读取数据写入到相应的端口即可,当然AD和温度湿度也是一样的道理。

在主函数的整个while大循环中加入以下代码:

        P2 = 0XFF;
//八路输入由P2口决定,随时更新其寄存器的值,若主机要返回,则直接返回相应寄存器的值 registerData[] = P2&0x01; registerData[] = (P2&0x02)>>;
registerData[] = (P2&0x04)>>;
registerData[] = (P2&0x08)>>;
registerData[] = (P2&0x10)>>;
registerData[] = (P2&0x20)>>;
registerData[] = (P2&0x40)>>;
registerData[] = (P2&0x80)>>; //八路输出由P0输出,只需要由主机设置寄存器值即可更新寄存器的值 。
P0_0 = registerData[]&0x01 ; P0_1 = registerData[]&0x01 ;
P0_2 = registerData[]&0x01 ;
P0_3 = registerData[]&0x01 ;
P0_4 = registerData[]&0x01 ;
P0_5 = registerData[]&0x01 ;
P0_6 = registerData[]&0x01 ;
P0_7 = registerData[]&0x01 ; GetTempAndHumi(); //检查串口数据并使用ModBus协议进行检验,正确则返回数据,返回的数据在registerData,可以在其他的函数中改变相应的值。
CheckModBusAndRespondToHost();

对于AD的数据,由于AD的结果是使用的中断方式而不是查询所以在主循环中就看不到了,在中断函数中直接设置寄存器的值,在需要时直接返回。这部分使用了10的AD转换, 所以需要做相应的转换:代码如下:

void adc_isr() interrupt  using
{
ADC_CONTR &= !ADC_FLAG; //Clear ADC interrupt flag registerData[ch+] = ADC_RES<<;
registerData[ch+] |= ADC_LOW2<<;
registerData[ch+] = registerData[ch+] >>; //if you want show 10-bit result, uncomment next line
// SendData(ADC_LOW2); //Show ADC low 2-bit result if (++ch > ) ch = ; //switch to next channel
ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ADC_START | ch;
}

在这里只使用了3路AD转换。
在使用Modbus RTU数据进行寄存器的读写时需要注意的是在上位机比如组态王、KEPServerEx等,在设置寄存器地址时是从1开始,而发送的命令是从0开始,

读写寄存器都位于4区,所以我们 设置的地址一般都为40001.....40002等,对应寄存器0    1。

以下是在KEPServerEx设置的每个端口即AD等所对应的寄存器地址,

测试结果:

在这里AD全为1023是因为开发板全部接了上拉所以都为1023。

在这次开发完成后,客户反应AD测量结果不准确,由于我并没有在客户的现场,无法得知道具体如何,所以我的第一反应是STC12C5A60S2的AD基准电源使用的是VCC,可能是VCC电源与5V差距比较远而引起的,建议客户增加电池做为基准或者提高电源的精度,但对方说比较困难。后来我问他如何发现不准确的,他说是和别的系统测量的结果进行比较的,并且给我发来了他们的传感器资料,我看了一下资料,突然发现传感器输出是0-2v,而STC的AD是0-5V,一下就明白了这中间的问题,让客户将结果X2.5后就正常了。

C51应用 Modbs Rtu协议实现与KEPServerEx 通信的更多相关文章

  1. ModBus通信协议的【Modbus RTU 协议使用汇总】

    1.RTU模式 当控制器设为在Modbus网络上以RTU(远程终端单元)模式通信,在消息中的每个8Bit字节包含两个4Bit的十六进制字符.这种方式的主要优点是:在同样的波特率下,可比ASCII方式传 ...

  2. Modbus RTU 协议使用汇总

    原创地址:https://blog.csdn.net/u012166958/article/details/64920144 标准的Modbus 口是使用RS-232C 兼容串行接口,它定义了连接口的 ...

  3. Modbus TCP和Modbus Rtu协议的区别 转

    http://blog.csdn.net/educast/article/details/9177679   Modbus rtu和Modbus tcp两个协议的本质都是MODBUS协议,都是靠MOD ...

  4. 调试备忘录-RS485 MODBUS RTU协议简述

    目录--点击可快速直达 目录 写在前面 先简单说下什么是MODBUS? 参考文章 写在前面 最近在做和物联网有关的小项目,有一个传感器通讯用到了RS485 MODBUS RTU协议,所以就写个随笔记录 ...

  5. SSL、TLS协议格式、HTTPS通信过程、RDP SSL通信过程(缺heartbeat)

    SSL.TLS协议格式.HTTPS通信过程.RDP SSL通信过程   相关学习资料 http://www.360doc.com/content/10/0602/08/1466362_30787868 ...

  6. winform 客户端采用HTTP协议与服务端通信

    本来从来没有仔细研究过Http协议,今天因为公司业务需求,调试了半天,终于现在会Winform用Http协议与服务端通信了,其中常用的有POST和Get方式: 仔细看了人人网和新浪等大部分都是采用GE ...

  7. winform 客户端 HTTP协议与服务端通信以及解决中文乱码

    本来从来没有仔细研究过Http协议,今天因为公司业务需求,调试了半天,终于现在会Winform用Http协议与服务端通信了,其中常用的有POST和Get方式: 仔细看了人人网和新浪等大部分都是采用GE ...

  8. c#基于TCP/IP、CIP协议的欧姆龙PLC通信

    一.关于CIP协议 CIP通信是Common Industrial Protocl(CIP)的简称,它是一个点到点的面向对象协议,能够实现工业器件(传感器,执行器)之间的连接,和高等级的控制器之间的连 ...

  9. modbus rtu 协议转DLT645-2007和DLT645-1997电表协议转换器定制,

    现场会碰到现场数据为Modbus协议,但是后台系统为DLT645协议系统,本模块支持将工业ModbusRtu协议转换为电表国标协议DLT645协议,支持1997和2007俩种标准,只需要进行简单的配置 ...

随机推荐

  1. LINUX 文件系统JBD ----深入理解Fsync

    http://www.cnblogs.com/hustcat/p/3283955.html http://www.cnblogs.com/zengkefu/p/5639200.html http:// ...

  2. LINUX系统全部参数 sysctl -a + 网络参数设置

    http://blog.lifeibo.com/?p=380 1.sysctl sysctl命令被用于在内核运行时动态地修改内核的运行参数,可用的内核参数在目录/proc/sys中 [root@ser ...

  3. Linux学习笔记总结--配置iptables防火墙

    将原有的iptables 文件保存一份 cp -p /etc/sysconfig/iptables /etc/sysconfig/iptables.bak 清空现有的规则 iptables -F ip ...

  4. vi 使用笔记

    基本A 当前行追加J 去除本行和下一行之间的换行符(写CSS利器)~ 光标所在处的字符进行大小写互换* 向前搜索目前光标所在的单词# 向后搜索目前光标所在的单词% 查找与光标所在处相匹配的反括号, 包 ...

  5. 【Android】数据库的简单应用——升级数据库

    假如我们已经创建好了一个数据库,随着功能需求的增加,想在数据库中再添加一个表,如果直接在之前的代码中插入一个表,会发现创建表失败,这是因为该数据库已经存在.该如何解决呢? 1.卸载程序,重新编译安装. ...

  6. HttpModule,HttpHandler,HttpHandlerFactory

    HttpModule:Http模块,可以在页面处理前后.应用程序初始化.出错等时候加入自己的事件处理程序. HttpHandler:Http处理程序,处理页面请求 HttpHandlerFactory ...

  7. CentOS 6.7安装Java JDK

    1.下载Java JDK 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.htm ...

  8. C#开发的进化史

    1.数据类型的进化 C#1中实现Product类型代码 public class Product { string name; public string Name { get { return na ...

  9. 开源 android

    Android经典的开源项目其实非常多,但是国内的博客总是拿着N年前的一篇复制来复制去,实在是不利于新手学习.今天爬爬把自己熟悉的一些开源项目整理起来,希望能对Android开发同学们有所帮助.另外, ...

  10. Js的History对象

    History回顾 window.history表示window对象的历史记录 window.history的简单回顾 历史记录中前进/后退,移动到指定历史记录点 window.history.bac ...