Modbus协议完全资料与程序解析
下面开始分析定时器,定时器的目的其实就1个,判断一帧是否接收完毕,如果完毕,则进入下一步。在定时器中断函数中,首先要对定时器值进行初始化,这个就不多说了,然后是判断程序是否处于接受状态if(Modbus_recevie_flag == 1),这个状态只有在串口中断函数中才会被置位,其他的情况不会被置位。若程序不是接收状态,则直接跳出定时器中断,若程序处于接收状态,则定时计数自加Modbus_timeout_cnt++;,自加后进入判断if(Modbus_timeout_cnt > 首先判断程序是否处于命令解析状态if(Modbus_cmd_flag == 1)。命令解析状态标志只有在超时后置位,其他情况下不置位。之后是判断接收数据是否大于4字节,if(Modbus_recevie_count > 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-> Modbus_read_coil 0x01 //功能码01 读可读写数字量寄存器(线圈状态): switch (Modbus_recevie_buf[1]) { case Modbus_read_coil: Modbus_mode = Modbus_read_coil; break; …… default: //非法命令准备报异常 return ; break; } Modbus_exe_flag = 1; 解析后,将执行标志置位即可。 Exe 执行函数, 执行函数在解析函数后面,而不是在里面,所以,若没有解析,照样可以进入执行函数,但由于执行函数中有判断执行标志位if( modbus_crc_b == modbus_crc ),所以若标志为0,则直接退出函数。若标志为1,则执行Modbus_mode中对应的函数函数中依然用switch来选择具体功能函数 switch(Modbus_mode) //通过判断模式来进行对响应的发送 { case Modbus_read_coil: read_coil_proc(); break; …… default: return; break; } 这样的做的话,就可以吧解析函数,执行函数和具体的实施函数分开来弄,层次多多少少要清晰一些 下面就是针对01,02,03,04,05,06,15,16几个功能码的执行及返回进行说明 在说明各功能函数之前,先说说响应。 上面说的那两个函数只不过是对一帧的外围进行解析与判断,至于具体的参数,还需要功能函数去解析与返回,功能函数要做的事情有3个,1个是参数的解析,2是执行,3是返回响应。 先说响应,响应是有特点的,第一个字节肯定是自己的本机地址,第二个字节肯定是功能码,最后两个字节肯定是crc校验,所以说,在发送缓冲中,基本上4个字节已经定死了 Modbus_send_buf[0] = Modbus_addr; Modbus_send_buf[1] = Modbus_read_input_reg; //相应的功能码,每个功能寒暑都不一样 再经过执行函数最后算crc modbus_crc = crc16(Modbus_send_buf,temp); //计算发送crc数据 Modbus_send_buf[temp] = modbus_crc >> 8; //计算 temp++; Modbus_send_buf[temp] = modbus_crc & 0xff; //return num 高位 5.1 01 读线圈状态 #define Modbus_read_coil 0x01 其实表面上挺难理解的,啥线圈啥的,但你仔细看看就可以了解,就是读输出数字量,如果你写下位机的话,其实就是控制读取输出io,说白了,就是把目前的io输出状态返回给主机。这些io连接的可能是继电器,也可能是一些开关之类的东西,也就是些数字信号。读数字输出信号。 计算机发送命令:[设备地址] [命令号01] [起始寄存器地址高8位] [低8位] [读取的寄存器数高8位] [低8位] 设备响应:[设备地址] [命令号01] [返回的字节个数][数据1][数据2]...[数据n][CRC校验的低8位] [CRC校验的高8位] 简单的说就是返回所有的输出io的值,放在一个或者几个字节里,可以用判断的方法来实现,当然,也可以用与或的方式实现。 if(P1_0 == 1) { temp |= (1<8); } else { temp &= (1<8); } 将temp的值放入第四个缓冲区,当然这根据设备的io口,编程时就已经确定了的。接下来就可以进行crc计算了。最后发送即可。 Modbus_send_buf[3] = temp; modbus_crc = crc16(Modbus_send_buf,4); Modbus_send_buf[4] = modbus_crc >> 8; Modbus_send_buf[5] = modbus_crc & 0xff; //return num 高位 5.2 02 读只可读数字量寄存器(输入状态) 基本上和01意思差不多,只不过这个功能码返回的数据是输入io的数据,和01的区别是01可读可改,而02只可读不可改。也就是输入的状态。数据不可由设备本身控制。程序方面和01程序一样。 5.3 03读可读写模拟量寄存器(保持寄存器) 说简单点就是读da,da属于模拟量,也可以输出,但是以模拟量的方式来进行传输的 计算机发送命令:[设备地址] [命令号03] [起始寄存器地址高8位] [低8位] [读取的寄存器数高8位] [低8位] [CRC校验的低8位] [CRC校验的高8位] 设备响应:[设备地址] [命令号03] [返回的字节个数][数据1][数据2]...[数据n][CRC校验的低8位] [CRC校验的高8位] 其中返回字节个数,为读取寄存器数乘2 写程序时,首先要注意数据个数,temp = Modbus_recevie_buf[5];一般寄存器个数不会超过255,个数取读取寄存器个数的低八位即可。返回即乘2,temp = temp < 1;,下面要做的就是一个循环for(i = 0;i < temp ; i += 2),把需要的数据放入发送数组。其内容是 Modbus_send_buf[i+3]=(data_v&0xff00)>>8; Modbus_send_buf[i+4]=data_v&0x0ff; 由于帧的前面3个是地址,功能码,和返回字节个数,所以循环从第四个数据开始存放。data_v为读取的数据,在程序中还需要其他语句配合。比如:data_v = updateValue(); 循环后就可以进入
Modbus协议完全资料程序解 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)