完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
FreeModbus移植 经验分享一 为什么要移植Freemodbus
为什么要移植Freemodbus,这个问题需要从两个方面来回答。第一,modbus是一个非常好的应用层协议,它很简洁也相对完善。对于还没有接触过modbus的朋友来说,我非常不建议直接移植freemodbus,应该耐心的从modbus文档入手,并充分把握身边的所有资源,例如PLC的中modbus部分。第二,其实嵌入式系统的通信协议可以自己制定,但是通过实践发现自己定制的协议漏洞百出,尤其是扩展极为困难。我始终认为借鉴他人的经验是很好的途径。借鉴他人成熟的代码,可以减少调试的时间,实现的功能也多了不少。 个人观点,仅供参考。 freemodbus小提示 freemodbus只能使用从机功能。freemodbus更适合嵌入式系统,虽然例子中也有WIN32的例子,如果想要做PC机程序并实现主机功能,推荐使用另一个modbus库——NMODBUS,使用C#开发。同样WINFORM也可以通过自己编写串口代码实现modbus功能,但是这会花费很长的时间,可能是一周也可能是一个月,如果使用现成的代码库,那么开发时间可能只有10分钟。 整理的MODBUS 协议整理..zip 二 freeemodbus中如何通过串口发送和接收数据 freemodbus通过串口中断的方式接收和发送数据。采用这种做法我想可以节省程序等待的时间,并且也短充分使用CPU的资源。串口中断接收毋庸置疑,在中断服务函数中把数据保存在数组中,以便稍后处理。但是串口发送中断使用哪种形式?串口发送中断至少有两种方式,第一种,数据寄存器空中断,只要数据寄存器为空并且中断屏蔽位置位,那么中断就会发生;第二种,发送完成中断,若数据寄存器的数据发送完成并且中断屏蔽位置位,那么中断也会发送。我非常建议各位使用串口发送完成中断。freemodbus多使用RS485通信中,从机要么接收要么发送,多数情况下从机处于接收状态,要有数据发送时才进入发送状态。进入发送状态时,数据被一个一个字节发送出去,当最后一个字节被发送出去之后,从机再次进入接收状态。如果使用发送寄存器为空中断,还需要使用其他的方法才可以判断最后一个字节的数据是否发送完成。如果使用数据寄存器为空中断,那么将很有可能丢失最后一个字节。(马潮老师的AVR图书中也推荐使用发送完成中断,交流性质的文章,就没有参考文献了。) 二 freemodbus中如何判断帧结束 大家应该清楚,modbus协议中没有明显的开始符和结束符,而是通过帧与帧之间的间隔时间来判断的。如果在指定的时间内,没有接收到新的字符数据,那么就认为收到了新的帧。接下来就可以处理数据了,首当其冲的就是判断帧的合法性。Modbus通过时间来判断帧是否接受完成,自然需要单片机中的定时器配合。 三 整体代码 下面给出一个STM32平台上使用FREEMODBUS最简单的例子,操作保持寄存器,此时操作指令可以为03,06和16; view plaincopy to clipboardprint?
先给大家一个整体的印象,先让大家会使用FREEMODBUS,再详细描述细节 //保持寄存器起始地址 #define REG_HOLDING_START 0x0000 //保持寄存器数量 #define REG_HOLDING_NREGS 8 这两个宏定义,决定了保持寄存器的起始地址和总个数。需要强调的是,modbus寄存器的地址有两套规则,一套称为PLC地址,为5位十进制数,例如40001。另一套是协议地址,PLC地址40001意味着该参数类型为保持寄存器,协议地址为0x0000,这里面有对应关系,去掉PLC地址的最高位,然后剩下的减1即可。这会存在一个问题,PLC地址30002和PLC地址40002的协议地址同为0x0001,此时访问时是不是会冲突呢。亲们,当然不会了,30001为输入寄存器,需要使用04指令访问,而40001为保持寄存器,可以使用03、06和16指令访问。所以,用好modbus还是要熟悉协议本生,切不可着急。 //保持寄存器内容 uint16_t usRegHoldingBuf[REG_HOLDING_NREGS] = {0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e}; 接下来定义了保持寄存器的内容,在这里请大家注意了,保持寄存器为无符号16位数据。在测试的情况下,我随便找了一些数据进行测试。看数据的本质似乎看不出说明规律,但是usRegHoldingBuf却是以16进制保存了浮点数。 view plaincopy to clipboardprint?
接下来就进入主函数部分。有三个FREEMODBUS提供的函数,eMBInit,eMBEnable和eMBPoll。eMBInit为modbus的初始化函数,eMBEnable为modbus的使能函数,而eMBPoll为modbus的查询函数,eMBPoll也是非常单纯的函数,查询是否有数据帧到达,如果有数据到达,便进行相依的处理。再次观察这几个函数,只有eMBInit有很多的参数,这些参数和位于系统底层的硬件有关, view plaincopy to clipboardprint?
最后,如果收到一个有效的数据帧,那么就可以开始处理了。 第一步,判断寄存器的地址是否在合法的范围内。 if( ( (int16_t)usAddress >= REG_HOLDING_START ) && ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) ) 第二步,判断需要操作寄存器的偏移地址。 给个例子可以迅速的说明问题,例如访问寄存器的起始地址为0x0002,保持寄存器的起始地址为0x0000,那么这个访问的偏移量为2,程序就从保持寄存器数组的第2个(从0开始)开始操作。 第三步,读写操作分开处理 case MB_REG_READ: while( usNRegs > 0 ) { *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] >> 8 ); *pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] & 0xFF ); iRegIndex++; usNRegs--; } break; 以读操作为例,代码不多说了,请大家注意操作的顺序。保持寄存器以16位形式保存,但是modbus通信时以字节为单位,高位字节数据在前,低位数据字节在后。 四 串口相关部分代码编写 串口部分的代码编写比较常规,主要有三个函数,串口初始化,串口数据发送和串口数据接收。除了以上三个函数之外,还有串口中断服务函数。 view plaincopy to clipboardprint?
传入的参数有端口号,波特率,数据位和校验位,可以根据实际的情况修改代码。在这里我并没有修改其他参数,至于传入的波特率是有效的。除了配置串口的相关参数之外,还需要配置串口的中断优先级。最后,由于使用485模式,还需要一个发送接收控制端,该IO配置为推挽输出模式。 view plaincopy to clipboardprint?
由于485使用半双工模式,从机一般处于接收状态,有数据发送时才会进入发送模式。在FreeModbus中有专门的控制接收和发送状态的函数,在这里不但可以打开或关闭接收和发送中断,还可以控制485收发芯片的发送接收端口。代码非常简单,但是还是建议各位使用发送完成中断。 BOOL xMBPortSerialPutByte( CHAR ucByte ) { //发送数据 USART_SendData(USART1, ucByte); return TRUE; } BOOL xMBPortSerialGetByte( CHAR * pucByte ) { //接收数据 *pucByte = USART_ReceiveData(USART1); return TRUE; } xMBPortSerialPutByte和xMBPortSerialGetByte两个函数用于串口发送和接收数据,在这里只要调用STM32的库函数即可。 static void prvvUARTTxReadyISR( void ) { //mb.c eMBInit函数中 //pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM //发送状态机 pxMBFrameCBTransmitterEmpty(); } static void prvvUARTRxISR( void ) { //mb.c eMBInit函数中 //pxMBFrameCBByteReceived = xMBRTUReceiveFSM //接收状态机 pxMBFrameCBByteReceived(); } void USART1_IRQHandler(void) { //发生接收中断 if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET) { prvvUARTRxISR(); //清除中断标志位 USART_ClearITPendingBit(USART1, USART_IT_RXNE); } //发生完成中断 if(USART_GetITStatus(USART1, USART_IT_TC) == SET) { prvvUARTTxReadyISR(); //清除中断标志 USART_ClearITPendingBit(USART1, USART_IT_TC); } } 若进入串口中断服务函数,则要调用FreeModbus中响应的函数,串口接收中断服务函数对应prvvUARTRxISR(),其代码如下 view plaincopy to clipboardprint?
在prvvUARTRxISR中又调用了pxMBFrameCBByteReceived(),其实pxMBFrameCBTransmitterEmpty()并不是一个函数,而是一个函数指针。其定义如下,请注意函数指针的声明和函数声明的区别。 BOOL( *pxMBFrameCBTransmitterEmpty ) ( void ); 在mb.c文件的eMBInit函数完成赋值。一般情况下都会选择RTU模式,那么pxMBFrameCBByteReceived就和xMBRTUReceiveFSM等价了, pxMBFrameCBByteReceived = xMBRTUReceiveFSM; 同理,若发生串口发送完成中断,该中断服务函数对应prvvUARTTxReadyISR,其代码如下 view plaincopy to clipboardprint?
在prvvUARTTxReadyISR中又调用了pxMBFrameCBTransmitterEmpty(),pxMBFrameCBTransmitterEmpty也是函数指针,在eMBInit函数完成赋值,它等价于xMBRTUTransmitFSM。 特别提醒,由于我使用的是串口发送完成中断,想要进入该中断服务函数,需要发送一个字节的数据并启动串口发送中断,代码还需要少许修改。在mbRTU.c的eMBRTUSend中稍作修改,代码如下。 view plaincopy to clipboardprint? view plaincopy to clipboardprint?
写到这里给位可能看的不是很明白,建议研究一下FreeModbus的源码,稍作一些修改使用起来才会更加方便。 工程代码,IAR 5.5 V3.4库函数 请配合MODBUS POLL工具使用,会有更好的效果! |
|
相关推荐
8 个讨论
|
|
谢谢分享谢谢分享谢谢分享
谢谢分享谢谢分享 谢谢分享 谢谢分享谢谢分享 谢谢分享 谢谢分享 谢谢分享谢谢分享谢谢分享 谢谢分享 谢谢分享 谢 谢 谢谢分享 谢谢分享 谢 谢 谢谢分享 谢谢分享 分 分 谢谢分享 谢谢分享 享 享 谢谢分享 谢谢分享 谢 谢 谢谢分享 谢谢分享 谢 谢 谢 谢谢分享 谢谢分享 分 分 谢谢 谢谢分享 谢谢分享 享 享 谢谢分 谢谢分享 谢谢分享 谢 谢 谢谢分 谢谢分享 谢 谢 谢谢分谢谢分享 谢 谢 谢谢谢谢 分 分 谢谢 享 享 |
|
|
|
|
|
这种零零碎碎的知识,没有办法学习啊。。。 看样子楼主只是简单的了解了一些modbus基础知识,但是不够系统和全面。 其实,modbus虽然比较简单,但是如果不注意有很多坑, 特别是寄存器的位数,大小端处理,浮点数,长整数的处理等等。 目前也没有什么合适的书籍系统介绍,帮助理解的。 刚刚搜了一下,只有最近清华出版的《Modbus软件开发实战指南》不错, 应该是Modbus开发方面第一书,很系统很全面,各种代码都是开源的。 内容着重讲述如何快速入门并精通Modbus软件开发技术, 适用于初学Modbus通信协议的读者,可以看看。 京东或者淘宝搜索:Modbus软件开发实战指南 应该能看到吧。. |
|
|
|
|
|
嵌入式学习-飞凌嵌入式ElfBoard ELF 1板卡-LCD显示图片编程示例之介绍mmap
238 浏览 0 评论
《DNESP32S3使用指南-IDF版_V1.6》第二章 常用的C语言知识点
629 浏览 0 评论
【RA-Eco-RA2E1-48PIN-V1.0开发板试用】(第三篇)ADC采集+PWM输出
552 浏览 0 评论
《DNK210使用指南 -CanMV版 V1.0》第四十五章 人脸识别实验
552 浏览 0 评论
1074 浏览 0 评论
【youyeetoo X1 windows 开发板体验】少儿AI智能STEAM积木平台
11764 浏览 31 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-23 02:19 , Processed in 0.568117 second(s), Total 48, Slave 41 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号