freeModbus代码解读及移植笔记

freeModbus的代码库还是很好用的,本人在wince和C8051F410下均移植成功(只用到RTU模式)。但freeModbus提供的文档比较少,只能对照着Modbus协议一点点试着读懂源代码。下面是阅读代码期间的跟踪笔记:

  1、eMBErrorCode为枚举类型变量,代表错误码,共有8个错误代号。常用的是MB_ENOERR,即没有错误。

  2、eMBMode枚举类型变量代表设备的工作模式,分别是MB_RTU、MB_ASCII和MB_TCP。

  3、eMBEventType枚举类型变量定义了event的类型,分别是EV_READY,代表Startup启动完成;EV_FRAME_RECEIVED代表接收到帧;EV_EXECUTE代表执行功能函数;EV_FRAME_SENT代表帧已发送。

  4、eMBParity枚举类型变量代表奇偶校验选项,分别是MB_PAR_NONE无校验,MB_PAR_ODD奇校验,和MB_PAR_EVEN偶校验。

  5、mb.c文件中的静态变量ucMBAddress存储设备地址,此变量在eMBInit函数中初始化。

  6、在C51Modbus中将freeModbus库中的 源码进行了更改,例如尽量不使用函数指针,而是直接调用相关功能函数,根据eMBCurrentMode中的工作模式,来判断调用哪个函数。在freeModbus库中某些函数声明前加上reentrant,这是Keil编译器特有的关键词。这样做带来的一个不足是:不能动态绑定函数,从而导致库代码失去可移植性。这样做是C51编译器与ANSI标准不兼容的特殊性导致的。

  7、ENTER_CRITICAL_SECTION()和EXIT_CRITICAL_SECTION()宏,实际上就是关闭和打开全局中断。

  8、带xMBPort前缀的函数都属于port layer层,也就是独立于ModBus协议栈。

  9、freeModbus库中函数名称的第一个字母表示返回值类型,例如e表示返回enum枚举类型;v表示void无返回值;x表示BOOL布尔类型。注意这条规则并不是总成立,但主要函数基本上还是符合此规则的。第一个字母后的MB代表是属于ModBus协议栈的函数。

  10、port.h文件中宏#define F_MCU 定义了单片机的工作频率。需要用其值计算Uart0定时器和Tick定时器的重装入值。

  11、在程序主函数main中,使用协议栈的方法是:

  eStatus = eMBInit( MB_RTU, 0x0A, 0, 9600, MB_PAR_EVEN );

  /* Enable the Modbus Protocol Stack. */

  eStatus = eMBEnable( );

  for( ;; )

  {

  ( void )eMBPoll( );

  ……

  }

  12、在port layer层的xMBPortSerialInit函数中,需要根据传入的波特率、奇偶校验、数据位长度设置来配置Uart0及其使用的定时器。

  13、在port layer层的vMBPortSerialEnable函数中配置接收和发送使能,由于在单片机的寄存器SCON0中只有接收使能控制位REN0,而没有发送使能控制位,所以在portserial.c文件中又定义了一个TxEnable变量,用来表示发送的使能状态。若同时关闭接收和发送,则要关闭Uart0中断,即让ES0 = 0。

  14、eMBRTUInit函数中的变量usTimerT35_50us代表如果50us进行一次Tick的话,T35超时的Tick次数。这个公式很重要:

  usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );

  函数xMBPortTimersInit要以变量usTimerT35_50us为传入参数,对T35超时定时器进行设置。

  15、在mbrtu.c文件中定义了两个状态变量,一个是接收状态变量eRcvState,为eMBRcvState枚举类型,有4个状态,在使能ModBus协议栈后赋予STATE_RX_INIT,即初始状态;另一个是发送状态变量eSndState,为eMBSndState枚举类型,有两个状态,初始化为发送idle状态,即STATE_TX_IDLE。

  16、mb.c文件中的eMBState状态变量为枚举类型,代表设备的工作状态,有3种状态,分别是“未初始化”、 “使能”和“禁止”状态。调用完eMBInit 函数后要调用eMBEnable函数来使能ModBus协议栈,在其中将eMBState状态变量从“未初始化状态”变为“使能状态”,然后使能串口和打开T35定时器。

  17、如果T35定时器超时并产生中断,则要调用xMBRTUTimerT35Expired函数,其内部是一个状态机转换的switch,根据当前接收状态来通过xMBPortEventPost发送事件通知,然后关闭T35定时器,并将当前接收状态设置为STATE_RX_IDLE。

  18、eMBException枚举型变量表示Exception的类型,共有10种Exception,在ModBus协议中有定义。

  19、在eMBPoll( )中,首先通过xMBPortEventGet函数取event,如果没有则退出,若有event的话便根据event类型进行相应处理。EV_READY是在协议栈初始化后xMBRTUTimerT35Expired函数发出来的,表示startup完成;EV_FRAME_RECEIVED是xMBRTUTimerT35Expired函数在T35超时后发出的,表示已经收到了一帧,需要进行成帧处理,调用eMBRTUReceive函数;EV_EXECUTE是在处理EV_FRAME_RECEIVED过程中最后一步,如果此帧的地址符合本机地址,则发出EV_EXECUTE事件,进行应用层的处理。

  20、在eMBRTUReceive函数中首先查看帧大小是否符合要求,然后进行CRC校验。此函数的原型是:

  eMBRTUReceive( UCHAR * pucRcvAddress, UCHAR ** pucFrame, USHORT * pusLength )

  第一个参数是为了返回帧中的地址,也就是帧中第一个字节;第二个传入的参数以后要当做数组来使用,所以用了指针的指针类型;第三个参数表示PDU的长度,也就是帧中除去地址字节和CRC校验字节后的长度。

  21、在eMBPoll( )中处理EV_EXECUTE事件,首先从PDU中提取出FunctionCode,然后根据FunctionCode找到相应的处理函数。xMBFunctionHandler结构体类型变量xFuncHandlers中定义了各个FunctionCode对应的处理函数pxHandler,函数的第一个参数ucMBFrame是PDU的存储地址,第二个参数usLength返回PDU的长度。如果帧不是一个广播帧,则需要设备发出一个回复,如果前面有错误发生,则要回复一个错误报告帧。

  22、在Keil中程序需要使用大模式编译,否则会出现error c249: 'data': segment too large的错误。

  23、若使用波特率为9600,则 t3.5= ( 11 * 3.5 ) / 9600 = 4.01 ms。 不能使用8位模式的Timer,因为11.0590MHz主频在最大48分频后,最长的超时时间为1.11ms,不能满足T35的超时要求。


24、freeModbus 1.5库,在使用过程中发现了一个bug,即如果在PDU中发送的寄存器数据长度与要读写的寄存器的数量不符,只要CRC校验正确,freeModbus便不会检测出来。例如写多个寄存器命令中,标明写寄存器的数量为2,也就是后面接的数据长度为4,但随后的数据只为2个字节,即一个寄存器的数据,freeModbus不会发现此错误,同时会将后面的CRC校验值认作是写第二个寄存器的数据。这个bug实际上是通过Modbus调试精灵1.024的一个写多寄存器bug发现的。

  • 4
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值