外贸网站收到询盘淘客网站建设视频
news/
2025/9/30 21:35:14/
文章来源:
外贸网站收到询盘,淘客网站建设视频,标书制作教程全过程,一个公司的网址怎么弄目录
一、modbus简介
二、功能码01、02
三、modbus解析
四、功能码03、04
五、功能码05
六、功能码06
七、功能码16 一、modbus简介 我们在网上查阅modbus的资料发现很多很杂#xff0c;modbus-RTU ASCII TCP等等#xff0c;还有跟PLC结合的#xff0c;地址还分1开…目录
一、modbus简介
二、功能码01、02
三、modbus解析
四、功能码03、04
五、功能码05
六、功能码06
七、功能码16 一、modbus简介 我们在网上查阅modbus的资料发现很多很杂modbus-RTU ASCII TCP等等还有跟PLC结合的地址还分1开头的4开头的搞得有点懵那其实是各行业有各自差异化的规则而已实际上modbus就是 地址码功能码数据区校验码 了这是核心具体可以看这篇比较简洁。modbus rtu六种功能码详细解析-电子发烧友网 对于我们物联网领域而言就是方便在主机端接入其它厂家的传感器或执行器设备就是了比如温湿度、PH计等等厂家在生产的时候产品定位就是配套应用商所以modbus协议基本是标配只是不同厂家的传感器modbus的数据地址定义不一样而已当然也有少数厂家用自定义协议非要用的话就得多费时间去了解它的协议了那modbus协议的传感器我们如果有驱动的话请求或解析就可以直接复用了下图是TB上随便搜索的485温湿度传感器的通讯协议。 另外modbus对于我们来讲物理层一般都是RS485的当然了要用RS232或TTL串口甚至是4G、LORA也都是可以的。modbus协议使用的时候一般是主机发送请求从机回复结果的流程主机可以请求传感器数据也可以设置内容比如控制继电器开关一应一答每次轮询根据波特率要有一定间隔一般几十到几百毫秒像RS485的波特率不要太高一般是9600如果距离有几百米建议更低4800或2400。在实际工程项目中主机跟从机的485通讯经常会有莫名其妙的问题(通讯不上或者乱码)这个就要靠经验解决了。 那我们写modbus驱动文件的意义在哪里要怎么写就跟之前用过的mqtt协议一样我们这个驱动程序主要作用就是组合报文和解析报文这样当你拿到一个厂家的modbus传感器的时候直接调用这个驱动文件的函数就可以请求数据了最后再根据厂家对数据的定义进行应用层解析就行了。接下来我们就几个常用的功能码做详细介绍。
二、功能码01、02 01的作用就是读取开关量输出状态02的作用是读取IO输入状态其实都差不多返回的数据中正常每个bit位代表各自的开关/输入状态比如数据如下
请求01 02 00 00 00 04 79 C9
返回01 02 01 0B E0 4F
这里面的含义是请求起始数据地址为0x0000的4个输入状态返回的是地址码01、功能码02、数据长度01、数据区0B和CRC校验码E0 4F那对于应用层来讲有用的数据就是0B了我们现在把0B换成二进制显示是 0000 1011那么这个数据正常理解就是1、2、4路输入触发3路正常具体的要以厂家提供的资料为准。功能码01也是一个道理的。这里面如果你请求的寄存器数量小于8个那从机会返回一个字节数据每个bit代表一个寄存器状态多余的是高位无效一般用0代替。
下面是具体的请求代码 /* 描述 :modbus 0x01的报文组合
输入 :
输出 : */
u16 drv_modbus_send_fun01(u8 slave_addr, u16 reg_start, u16 reg_num, u8 *make_buff, u16 make_size)
{if(make_size20){return 0;}u16 make_len0;u16 crcValue;make_buff[make_len]slave_addr;make_buff[make_len]0x01;make_buff[make_len]reg_start8;make_buff[make_len]reg_start;make_buff[make_len]reg_num8;make_buff[make_len]reg_num;crcValuedrv_crc16(make_buff, make_len);make_buff[make_len]crcValue8;make_buff[make_len]crcValue;return make_len;
}/* 描述 :modbus 0x02的报文组合
输入 :
输出 : */
u16 drv_modbus_send_fun02(u8 slave_addr, u16 reg_start, u16 reg_num, u8 *make_buff, u16 make_size)
{if(make_size20){return 0;}u16 make_len0;u16 crcValue;make_buff[make_len]slave_addr;make_buff[make_len]0x02;make_buff[make_len]reg_start8;make_buff[make_len]reg_start;make_buff[make_len]reg_num8;make_buff[make_len]reg_num;crcValuedrv_crc16(make_buff, make_len);make_buff[make_len]crcValue8;make_buff[make_len]crcValue;return make_len;
} 代码的核心就是根据从机地址、寄存器起始地址、寄存器数量来组合报文应用层再把这个报文发送出去至于是使用RS485还是RS232都是可以的驱动层不关心。
三、modbus解析 对于协议解析就很有讲究了我看到的大部分解析代码都是数据包丢进解析函数里然后函数直接就CRC校验出错就返回了这样的解析代码其实稳定性不太好因为实际传输的时候会经常莫名其妙的数据头或尾巴多出个00或FF或者其它数据但是完整正确的数据包又在里面这样CRC校验肯定是错的所以这里我把解析函数升级了下这样鲁棒性会好点。
/* 描述 : modbus 基础数据解析
输入 :
输出 : */
u16 drv_modbus_parse_base(u8 slave_addr, u8 fun_code, u8 *in_buff, u16 in_len, u8 *out_buff, u16 out_size)
{u16 recv_lenin_len, crcValue;u8 *pDatain_buff;u8 data_len0;if(recv_len4 || recv_len250)return 0;for(u8 i0;i4recv_len;i,pData){if(pData[0]slave_addr pData[1]fun_code)//比较地址和功能码{data_lenpData[2];crcValuepData[data_len3]8|pData[data_len4];if(crcValuedrv_crc16(pData, data_len3)){if(data_lenout_size){memcpy(out_buff, pData[3], data_len);}else{data_len0;} break;} } }return data_len;
} 首先要声明的是这个代码适合读取数据的功能码比如01、02、03和04对于设置功能码05、06成功了直接返回相同报文可以用字符串匹配的方式进行确认这里先略过。这段代码的核心是匹配用户需要的从机地址和功能码这就相当于数据标识头了有了这个以后至少可以过滤掉前面无用的干扰数据了之后的第三字节就是数据长度了有了这个长度值就能够准确地做CRC校验了 校验成功之后把数据区的内容复制出来就可以了剩下的是应用层的事情了。 对于功能码01和02对应的解析函数就是调用上面的函数就行了具体如下。 /* 描述 : modbus 0x01 数据解析
输入 :
输出 : */
u16 drv_modbus_parse_fun01(u8 slave_addr, u8 *in_buff, u16 in_len, u8 *out_buff, u16 out_size)
{return drv_modbus_parse_base(slave_addr, 0x01, in_buff, in_len, out_buff, out_size);
}/* 描述 : modbus 0x02 数据解析
输入 :
输出 : */
u16 drv_modbus_parse_fun02(u8 slave_addr, u8 *in_buff, u16 in_len, u8 *out_buff, u16 out_size)
{return drv_modbus_parse_base(slave_addr, 0x02, in_buff, in_len, out_buff, out_size);
}四、功能码03、04 03和04功能码比较相似差别在于03是保持寄存器可读可写04是输入寄存器只读。比如热敏温度头的数据一般就用04来读取主机不能改变空调的设定温度一般用03来读取同时可以用06来设置更改。具体组合报文代码如下 /* 描述 :modbus 0x03的报文组合
输入 :
输出 : */
u16 drv_modbus_send_fun03(u8 slave_addr, u16 reg_start, u16 reg_num, u8 *make_buff, u16 make_size)
{if(make_size20){return 0;}u16 make_len0;u16 crcValue;make_buff[make_len]slave_addr;make_buff[make_len]0x03;make_buff[make_len]reg_start8;make_buff[make_len]reg_start;make_buff[make_len]reg_num8;make_buff[make_len]reg_num;crcValuedrv_crc16(make_buff, make_len);make_buff[make_len]crcValue8;make_buff[make_len]crcValue;return make_len;
}/* 描述 :modbus 0x04的报文组合
输入 :
输出 : */
u16 drv_modbus_send_fun04(u8 slave_addr, u16 reg_start, u16 reg_num, u8 *make_buff, u16 make_size)
{if(make_size20){return 0;}u16 make_len0;u16 crcValue;make_buff[make_len]slave_addr;make_buff[make_len]0x04;make_buff[make_len]reg_start8;make_buff[make_len]reg_start;make_buff[make_len]reg_num8;make_buff[make_len]reg_num;crcValuedrv_crc16(make_buff, make_len);make_buff[make_len]crcValue8;make_buff[make_len]crcValue;return make_len;
}跟01、02功能码是差不多的解析代码也是类似
/* 描述 : modbus 0x03 数据解析
输入 :
输出 : */
u16 drv_modbus_parse_fun03(u8 slave_addr, u8 *in_buff, u16 in_len, u8 *out_buff, u16 out_size)
{return drv_modbus_parse_base(slave_addr, 0x03, in_buff, in_len, out_buff, out_size);
}/* 描述 : modbus 0x04 数据解析
输入 :
输出 : */
u16 drv_modbus_parse_fun04(u8 slave_addr, u8 *in_buff, u16 in_len, u8 *out_buff, u16 out_size)
{return drv_modbus_parse_base(slave_addr, 0x04, in_buff, in_len, out_buff, out_size);
} 应用层数据解析以文章开头的温湿度为例调用drv_modbus_parse_fun03函数后out_buff内的数据就是02 92 FF 9B四个字节具体的转换如下图所示。 五、功能码05 其作用是设置单路输出比如第二路继电器开开就往寄存器内设置FF 00关就设置00 00设置成功后就直接返回原数据。那么对于05功能码的返回要如何处理呢两个选择一个是用我的工程里drv_common.c的memstr函数做匹配看下返回的数据包里有没有包含刚才设置的数据串另一个选择是直接忽略有没有设置成功不要在这里观测而是用01功能码实时读取输出状态值如果状态不匹配要怎么处理由应用层自己决定比如重复执行3次后仍然失败那就向用户发端出故障信息人工介入等等。
/* 描述 :modbus 0x05的报文组合
输入 :
输出 : */
u16 drv_modbus_send_fun05(u8 slave_addr, u16 reg_start, u16 reg_value, u8 *make_buff, u16 make_size)
{if(make_size20){return 0;}u16 make_len0;u16 crcValue;make_buff[make_len]slave_addr;make_buff[make_len]0x05;make_buff[make_len]reg_start8;make_buff[make_len]reg_start;make_buff[make_len]reg_value8;make_buff[make_len]reg_value; crcValuedrv_crc16(make_buff, make_len);make_buff[make_len]crcValue8;make_buff[make_len]crcValue;return make_len;
}六、功能码06 06和03对应的寄存器是一样的03读06写比如空调预设温度、净化器预设转速等这些都可以叫保持寄存器。有点区别是 03可以批量读取连续的寄存器06只能单个设置06的具体代码如下 /* 描述 :modbus 0x06的报文组合
输入 :
输出 : */
u16 drv_modbus_send_fun06(u8 slave_addr, u16 reg_start, u16 reg_value, u8 *make_buff, u16 make_size)
{if(make_size20){return 0;}u16 make_len0;u16 crcValue;make_buff[make_len]slave_addr;make_buff[make_len]0x06;make_buff[make_len]reg_start8;make_buff[make_len]reg_start;make_buff[make_len]reg_value8;make_buff[make_len]reg_value; crcValuedrv_crc16(make_buff, make_len);make_buff[make_len]crcValue8;make_buff[make_len]crcValue;return make_len;
}06的返回解析跟05类似正常直接忽略就行了用03功能码去监测到底有没有设置成功。 七、功能码16 16是十进制的也就是16进制的0x10功能码是06的扩展它可以批量设置寄存器稍微复杂点具体看如下代码
/* 描述 : modbus 0x10的报文组合
输入 :
输出 : */
u16 drv_modbus_send_fun16(u8 slave_addr, u16 reg_start, u16 reg_num, u8 *reg_data, u8 *make_buff, u16 make_size)
{u16 make_len0;u16 crcValue;u8 data_lenreg_num*2;if(make_size10data_len)return 0;make_buff[make_len]slave_addr;make_buff[make_len]0x10;make_buff[make_len]reg_start8;make_buff[make_len]reg_start; //寄存器起始地址make_buff[make_len]reg_num8;make_buff[make_len]reg_num; //寄存器数量make_buff[make_len]data_len;//数据区长度memcpy(make_buff[make_len], reg_data, data_len);//数据区make_lendata_len;crcValuedrv_crc16(make_buff, make_len);make_buff[make_len]crcValue8;make_buff[make_len]crcValue;return make_len;
} 返回也是忽略就行了用03去读取监测。 modbus的解析大概就是这样了完整版的内容比较多但是根据平时项目的使用频率来看常用的就这些了其他的要学习只能自己再找找资料了。
具体代码在这里下载https://download.csdn.net/download/ypp240124016/89091325
工程原来上传过了自己添加驱动程序测试就行了。https://download.csdn.net/download/ypp240124016/89044525 本项目的交流QQ群:701889554
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/923279.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!