Modbus协议完全资料与程序解析
for(i=8; i> ACC = ACC >> ds1302-> ds1302-> ds1302-> ds1302-> ds1302-> ds1302-> ds1302-> ds1302->timestr[0] = ds1302-> ds1302->timestr[1] = ds1302-> ds1302-> ds1302-> ds1302-> ds1302->timestr[3] = ds1302-> ds1302->timestr[4] = ds1302-> ds1302-> ds1302-> ds1302-> ds1302->timestr[6] = ds1302-> ds1302->timestr[7] = ds1302-> ds1302-> ds1302-> ds1302-> ds1302-> ds1302-> ds1302->datestr[2] = ds1302-> ds1302->datestr[3] = ds1302-> ds1302-> ds1302-> ds1302-> ds1302-> ds1302-> ds1302->datestr[5] = ds1302-> ds1302->datestr[6] = ds1302-> 5、程序,以下位机为程序对象,主要使用c语言编写,首先,先从变量入手,既然modbus接受以帧为单位,所以就要设置两个缓冲区,用来接收数据,我们这里使用数组来存储接收来的数据Modbus_send_buf[Modbus_max_send_buf];//数据发送缓冲 和 Modbus_recevie_buf[Modbus_max_recevie_buf];//数据接收缓冲 ,其中Modbus_max_send_buf,和Modbus_max_recevie_buf ,为宏定义,这样可以方便的修改一帧最大的存储数据。有了发送接收缓冲,就可以写中断函数了,进入中断后,首先做一些必要的工作,清ES ,判断IR,清IR,做完后,就可以开始接收数据了,但有个问题?如果设备处于空闲状态,那么接收数据后按命令执行,但如果当设备正在执行指令的时候,则不应该再继续的接收指令,那样的话,会让程序进入混乱状态。所以要在基础工作做完后,增加一个判断,来确定设备的忙闲。if((Modbus_cmd_flag == 0) && (Modbus_exe_flag == 0)),判断完以后就可以继续下面的工作了。如果通讯中包含奇偶校验的话,那么则判断奇偶校验。下面就是接收数据。Modbus_recevie_buf[Modbus_recevie_count] = SBUF; ,将接收来的数据存入数组并记录存入的数据个数Modbus_recevie_count,由于modbus是通过时间来判断一帧的结束的,所以在程序中,必须要有一个定时器函数,这个定时器用来判断程序是正在接受,还是已经接受完成了。所以中断的最后所做的是计数器自加Modbus_recevie_count++;,定时器清0 Modbus_timeout_cnt = 0; ,将设备状态转入接收状态Modbus_recevie_flag = 1;。此时,串口中断的工作就完成了。 下面开始分析定时器,定时器的目的其实就1个,判断一帧是否接收完毕,如果完毕,则进入下一步。在定时器中断函数中,首先要对定时器值进行初始化,这个就不多说了,然后是判断程序是否处于接受状态if(Modbus_recevie_flag == 1),这个状态只有在串口中断函数中才会被置位,其他的情况不会被置位。若程序不是接收状态,则直接跳出定时器中断,若程序处于接收状态,则定时计数自加Modbus_timeout_cnt++;,自加后进入判断if(Modbus_timeout_cnt >= Modbus_max_timeout_cnt),判断的值即为modbus接收一帧传输完成所需要的时间间隔。至于是多少时间,可以通过修改Modbus_max_timeout_cnt来确定。可以将定时器终端设置为1ms1次,在9600的情况下将超时时间设为4,#define Modbus_max_timeout_cnt 4,这样如果串口中断不在接收数据时,定时计数将不会清0,当到达设定的超时时间后即判断接收结束,转向命令解析状态。 接收来的数据可以经过一个函数来执行,同时也可以经过两个函数,解析与执行两步来分别执行。我喜欢后者,因为这样可以把解析的过程和执行的过程分开来写。程序显得更加清晰与明朗。 在主函数中就执行1个函数, while(1) { Modbus_proc(); } 这个函数是经过打包的两个函数,进入这个函数 void Modbus_proc() { Modbus_cmd(); Modbus_exe(); } 可以看到,程序分为cmd解析,exe执行。 Cmd 命令解析函数 有这么几个问题是需要判断的,命令解析状态,接收来的数据个数,crc,地址,这几个问题是命令解析时需要注意的,顺序可以稍做变化。但最好是这个顺序。 首先判断程序是否处于命令解析状态if(Modbus_cmd_flag == 1)。命令解析状态标志只有在超时后置位,其他情况下不置位。之后是判断接收数据是否大于4字节,if(Modbus_recevie_count > 4)。当程序接收数据小于4字节则说明接收发生错误,抛弃它。下一步则是判断crc校验,由于crc在一帧的最后两位,所以crc应该取缓冲的最后两位 modbus_crc_h=Modbus_recevie_buf[Modbus_recevie_count-2]; modbus_crc_l = Modbus_recevie_buf[Modbus_recevie_count-1]; 然后将取来的数据合并成一个16位数据,得到接收的crc modbus_crc = ((unsigned int)(modbus_crc_h) < 8) | modbus_crc_l; 重新计算1帧的crc,得到自己的crc modbus_crc_b = crc16(Modbus_recevie_buf,Modbus_recevie_count - 2); 最后进行对比,将自己算的crc和接收的crc进行比较,来判断接收的数据是否正确。 if( modbus_crc_b == modbus_crc ) 在crc判断正确后,就可以判断地址了 if(Modbus_recevie_buf[0] == Modbus_addr) // Modbus_addr为一个宏定义的本机地址,若多机可以在此处修改。 当地址,crc,等全判断正确以后,就可以判断最重要的功能码了。由于功能码很多,所以1可以用宏定义来定义功能码增加程序的可读性,2可以利用switch来命令的模式 #define
Modbus协议完全资料程序解 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)