软件调试总结之FreeModbus移植

        在研发某个电子产品时,由于需要用到FreeModbus协议,所以对FreeModbus协议进行了系统的自我学习,包括FreeModbus协议,移植方法等内容。前段时间一直在忙于该产品开发,所以未及时的对其进行总结和归纳,现占用自我的一点休息时间将FreeModbus的移植方法、移植时遇到的一些问题及解决方法总结、归纳一下并分享给大家,借用鲁迅的“拿来主义”思想。希望大家通过阅读这篇文章,能够达到对FreeModbus协议“拿来就能用”的状态,与此同时也希望能够解答大家的一些疑惑。

一、为何采用Modbus协议,而不是自己定义协议?

        Modbus是非常适合嵌入式系统应用的一个通用协议,既简洁又完善。而自己定义的嵌入式协议,一般来说扩展性不好,软件Bug也比较多。本人始终认为站在巨人的肩膀上,我们不但可以走的更远,也会走的更快(即移植成熟的代码可以减少调试时间、提高工作效率)。另外强烈建议在进行FreeModbus协议移植前,最好对FreeModbus协议有一个全面的了解,可通过查看或阅读Modbus协议说明文档了解。

二、modbus协议

        为了更好的理解、移植和应用FreeModbus协议,个人觉得至少应该了解以下几点:

        1、FreeModbus协议采用主从通讯方式,支持一个主机多个从机,并且遵从一发一回,不发不回的通讯原则。

        2、FreeModbus协议传输方式采用“大头字节”传输方式,即是先传输高字节数据再传输低字节数据。

        3、FreeModbus协议有RTU和ASCII两种通讯方式,由于RTU通讯模式的数据吞吐量比较大,所以一般会优先考虑采用RTU通讯传输模式

        4、RTU传输帧的格式是:1位起始位、8位数据位、1位校验位、1位停止位,总共11位。

        5、FreeModbus协议中没有明显的开始符和结束符,而是通过帧与帧之间的间隔时间来判断的。如果在指定的时间内没有接收到新的字符数据,那么就认为收到了新的帧。

        6、FreeModbus寄存器的地址分为PLC地址协议地址,PLC地址为5位十进制数,比如40001等。协议地址是去掉PLC地址的最高位,然后减去1,比如PLC地址为40001,那么对于的协议地址为0x0000(40001为保持寄存器的地址)。同一个协议地址不同PLC寄存器地址的访问是通过功能码来区分的,所以不会存在访问冲突的问题。

        7、FreeModbus协议中有线圈寄存器、离散量寄存器、保持寄存器和输入寄存器,注意:线圈和离散量寄存器是位寄存器,保持寄存器为无符号16位数据。

        8、FreeModbus是一种通用的软件协议,所有不用担心是否能在带操作系统的嵌入式软件中移植使用,当然裸机的嵌入式软件也支持。

三、FreeModbus移植方法

        下面将以实际项目中在STM32F4处理器上成功移植的FreeModbus协议为例分步骤介绍FreeModbus协议的移植方法:

        1、首先去官网下载最新的FreeModbus协议源码,本人移植的是FreeModbus-V1.5版本。这样做的好处一方面保证源码的正确性,另一方面技术更新换代比较快,最新的源码应该是经过多次老版本源码优化的结果。

        2、FreeModbus-V1.5解压后的情况如图1所示,主要有demo、modbus、tools、doc四个文件夹。其中tools为上位机测试modbus程序,doc为一些说明文件。有用的是demo以及modbus。打开demo,没有看到stm32的工程文件,有一个叫BARE的文件夹,打开BARE文件后的情况如图2所示,是一些不包括任何处理器的部分源代码,我们只需将modbus中的文件,BARE中port文件中的文件移植到已建的工程中即可。

软件调试总结之FreeModbus移植_第1张图片
图1 FreeModbus-V1.5解压后情况
软件调试总结之FreeModbus移植_第2张图片
图2 BARE中文件

        3、FreeModbus协议的底层移植主要包括串口和定时器的移植。定时器的移植主要有定时器初始化和35uS定时中断函数等。串口的移植工作主要包括串口的初始化、发送和接收中断函数,RS485的发送和接收选择管脚的初始化和使能函数等。

软件调试总结之FreeModbus移植_第3张图片
图3 RS485发送接收管脚初始化
软件调试总结之FreeModbus移植_第4张图片
图4 RS485发送和接收使能函数

        4、FreeModbus协议的顶层函数移植主要是线圈的读写函数、离散量的读函数、输入寄存器的度函数和保持寄存器的读写函数等,当然还有一些自己编写的解析FreeModbus协议的函数。

软件调试总结之FreeModbus移植_第5张图片
软件调试总结之FreeModbus移植_第6张图片
图5 线圈寄存器操作函数
软件调试总结之FreeModbus移植_第7张图片
图6 离散量寄存器操作函数1

四、常见问题及解决方法

1、最后一个字节发送不出去的问题

(1)问题现象:

        FreeModbus移植完成后,首先利用串口调试助手对移植的串口通讯情况进行了检查,通过观察串口调试助手上接收到的数据发现没有最后一个字节数据(测试程序知道自己要发送的数据内容)。

(2)解决方法:

        出现该问题后,首先对串口的初始化、RS485的发送和接收使能管脚的初始化进行了排查,发现的问题点1是RS485的发送接收使能管脚时钟没有配置正确,由于采用的是STM32F4系列处理器,GPIO是挂在AHB1总线上的,所以调用APB1相关的处理来初始化时钟是不对的。出现的问题点2是本人使用的是发送寄存器空中断导致最后一个字节没有发送成功,由于当时项目比较急,只是在FreeModbus 协议的源码中稍微作了修改,实现最后一个字节的数据成功发送。修改地方如图23所示。不过在这里个人强烈建议使用串口发送和接收中断,一方面可以充分利用MCU的资源,也可以减少等待时间;发送中断最好用发送完成中断,如果使用发送寄存器空中断,还需要判断最后一个字节数据发送完成与否,否则导致最后一个字节数据发送不出去。

软件调试总结之FreeModbus移植_第8张图片
图7 不推荐解决方法

2、PLC地址和协议地址的问题

(1)问题现象:

        FreeModbus移植完成后,利用modbus-poll软件对其进行调试时发现,利用自己编写的FreeModbus协议解析函数操作0x0001地址时,而对应变化的地址是0x0000,当时是直接把自己定义的PLC地址的高位给去掉了。

(2)解决方法:

        如果您知道FreeModbus协议有PLC地址和协议地址之分,并且协议地址是PLC地址的高位去掉后再减去1的话,那么这个问题就不是个什么问题。

3、assert()函数未定义报错的问题

(1)问题现象:

        FreeModbus移植完成后,就直接在Keil软件中对整个工程文件进行编译,编译后提示assert()未定义。

(2)解决方法:

        由于之前未遇到过此类问题,所以去百度上搜索了一下,最后经过验证出现此类问题的原因是MicroLib不支assert()函数,即是工程文件使用了微库,在target中钩掉USE MicroLIB后再次编译通过。

五、总结

        通过此次移植FreeModbus协议,并完成对其的调试,经过反思觉得自己还有不足的地方,现就反思的解决问题思路进行一下归纳和总结,希望对大家有所帮助。解决问题的思路是:采用先发散后收敛的问题解决思维方式,发散就是头脑风暴进行各种假设,收敛即是对出现该问题的各种假设进行验证,逐一排查,验证方法是采用剥洋葱式思维。利用Keil的在线调试技术进行在线实时调试,根据调试中各个函数返回的错误码进行问题排查,进而找到问题的根源,最后把问题解决掉。此次移植FreeModbus协议的解决问题心得是:不要一味的假设,还需要进行验证,而验证最好的助手就是仪器仪表,另外能用示波器的地方尽量不用万用表,因为通过示波器能够观看到信号变化的整个过程,而万用表做不到。

你可能感兴趣的:(软件调试总结之FreeModbus移植)