微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > Modbus协议完全资料与程序解析

Modbus协议完全资料与程序解析

时间:12-01 来源:互联网 点击:

下面开始分析定时器,定时器的目的其实就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();

循环后就可以进入

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top