开源⼀套MODBUS主机代码(带讲解分析)
最近有⽤到modbus主机部分,⽹上搜索了⼀圈,没到好⽤的现成的开源代码。之前⽤过freemodbus,只有从机的源代码是免费的,其他的都需要商业授权。既然这样,那就⾃⼰动⼿,丰⾐⾜⾷……⾃⼰编写个modbus的主机代码,并且开源出来。
modbus就不多介绍了,是⼯业上常⽤的通信协议。在物理层, Modbus 串⾏链路系统可以使⽤不同的物理接⼝(RS485、
RS232)。最常⽤的是RS485 两线制接⼝。
modbus⼜是⼀个主从协议:同⼀时刻,在总线上只能有⼀个主机,其他作为从机。并且从机不会主动发数据,都是主机给从机发⼀条,从机才会回复⼀条。所以主机的发送和接收也是不同步进⾏的。如下流程:
主机发送——》等待从机应答——》接收到从机应答——》应答处理——》回到初始状态
以上的主要流程请记好,也是编写modbus主机代码的指导⽅针。下⾯再看下modbus是如何区分⼀帧⼀帧的数据的(这⾥只是介绍RTU模式的,ASSCII暂不做讨论)。如下图所⽰,帧1与帧2之间要⾄少有3.5个字符的时间,我们把它简称为3.5T。意思也就是在收到数据直到总线上空闲超出了3.5t,就意味着这⼀
帧数据接收完成了。我们就可以把刚才收到的⼀帧进⾏CRC计算解析看⼀帧数据接收是否正确。
实际上除了如上规定的帧与帧之间必须间隔3.5T外,在⼀帧内字符字符之间要⼩于1.5T。如下图所⽰:
但是在实际运⾏测试中,这种字符之间超1.5T的情况概率很⼩,即便出现也能在CRC校验时把错误的帧过滤掉。所以我编写的modbus主机并没有进⾏字符之间是否超过1.5T的校验。⽽只做了3.5T的帧区分。⾄此基本概念介绍完了,先看下我编写的modbus主机的主要状态流程图:
如上图所⽰,对应代码中定义的如下状态,状态的切换也是modbus主机的主要核⼼机制:
typedef enum
{
MBH_STATE_IDLE=0X00,
MBH_STATE_TX,
MBH_STATE_TX_END,
MBH_STATE_RX,
MBH_STATE_RX_CHECK,
MBH_STATE_EXEC,
MBH_STATE_REC_ERR,      //接收错误状态
MBH_STATE_TIMES_ERR,    //传输
}mb_host_state;
这篇⽂章主要针对代码的核⼼思想进⾏分析,处理细节在这⾥不做陈述,请⾃⼰参考源码,下⾯就针对核⼼的⼀个个状态进⾏讨论:
MODBUS主机空闲状态(MBH_STATE_IDLE)
这个很好理解,在⼀上电后就会进⼊到空闲状态。当对接收数据处理完或者读写从机错误次数达到最⼤值得时候也会回到这个初始状态。只有状态处于IDLE时才能发起新的⼀轮对从机的读写。
MODBUS主机发送状态(MBH_STATE_TX)
从状态切换图可以看得出来,当主机需要对从机进⾏读写的时候就会⾸先进⼊TX状态,该状态下回打开串⼝TX中断,然后通过发送中断把⼀帧数据发送完成。
MODBUS发送完成(MBH_STATE_TX_END)
当⼀帧数据发送完成时就会进⼊到TX_END状态。这时候主机发给从机的命令已经完成了,就等待从机的应答了。打开串⼝接收中断切换到下⼀个状态去
MODBUS接收(MBH_STATE_RX)
进⼊到RX状态下,会完成对从机回应的⼀帧数据的接收,都是在串⼝接收中断中处理。如果3.5T超时了就证明接收⼀帧完成,切换到下⼀个状态进⾏校验处理
MODBUS接收检查(MBH_STATE_RX_CHECK)
在rx check状态下主要完成对接收⼀帧数据的合法性和CRC进⾏计算,看接收是否正确。如果接收正确了,就进⼊到回调处理。如果校验错误就进⼊到接收错误状态。
MODBUS回调处理(MBH_STATE_EXEC)
不管是对主机的读还是写,从机都会回复。在该状态下根据从机回复的不同功能码就会执⾏不同的回调函数。执⾏完回调再次回到初始的IDLE状态,这样主机对从机的⼀次读或者⼀次写就完成了。
MODBUS接收错误(MBH_STATE_REC_ERR)
当接收校验错误的时候就会进⼊到该状态,在该状态下会对错误的次数进⾏计数。如果连续错误次数超过了最⼤值,就进⼊到超过错误次数状态。如果还没达到最⼤错误次数,就重新回到tx状态,重头再执⾏⼀遍对从机的读写动作。
MODBUS超错误次数(MBH_STATE_REC_ERR)
经过⼏次对从机的读写操作都失败以后就进⼊到该状态,在该状态下执⾏超错误次数回调,之后回到最初的IDLE状态等待下⼀次的读写操作。
最后是对源码⽂件⽬录进⾏⼀个⼤概的说明,整个modbus主机代码如下构成:
void mbh_init(uint32_t baud,uint8_t parity);
int8_t mbh_send(uint8_t add,uint8_t cmd,uint8_t *data,uint8_t data_len);
void mbh_poll(void);
void mbh_timer3T5Isr(void);
void mbh_uartRxIsr(void);
void mbh_uartTxIsr(void);
源代码电影讲解
#include "mb_include.h"
static const uint8_t aucCRCHi[] = {
static const uint8_t aucCRCHi[] = {
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,    0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40,
0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
0x00, 0xC1, 0x81, 0x40
};
static const uint8_t aucCRCLo[] = {
0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7,    0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,    0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9,    0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,    0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,   
0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, 0x3C, 0xFC, 0xFD, 0x3D,    0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,    0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF,    0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1,
0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB,    0x69, 0xA9, 0xA8, 0x68, 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,    0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,    0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97,
0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。