(直播)基于Freemodbus的灵活应用--Freemodbus的使用技巧和简单改造

先前,我分享了基于stm32cubemx软件生成针对freemodbus的程序办法和设置
同时也分享了Freemodbus移植到hal库的整个过程
这里不再讲述,可以参考以前发的这个帖子
http://www.stm32cube.com/question/19
 
这次重点是讲下移植完之后的freemodbus的使用问题
 
按照以前的办法生成TIm4和usart6的基础程序
我们将上次分享的移植好的工程文件打开
复制modbus和modbus_user文件夹到新生成工程文件下,并添加到工程里面去,如下图

图片1.png

工程添加完毕,打开main.c文件,添加头文件
#include "mb.h"
#include "mbport.h"
此时,编译整个工程会出现有4个错误提示,如下图

图片2.png

提示未定义这几个函数,,所以,打开mb.h文件,找到里面的这几个函数,将他们复制到main.c文件里面,如下图

图片3.png

 
这是,再次编译,不会有错误提示了.
但是,现在还没有成功移植到自己的工程,要修改一些东西
先看TIM4的定时器,这个提供modbus的超时判定,如果你的工程中到到的事其他的定时器,那么就要打开porttimer.c文件,将里面的所有的htim4替换为你的定时器,比如定时器2的htim2
还没完,这里还用到串口和定时器的重担回调callback
所以需要打开中断处理文件 stm32f4xx_it.c 文件

图片4.png

以上是 串口发送 / 串口接收 / 定时器超时 的modbus处理函数,他们分别要加入到相应的callback中

图片5.png

记录到这里还有个portserial.c文件需要说明下,里面包含了modbus用到的串口操作,所以
如果你的工程用的其他串口而不是huart6,那么必须更改里面的到你的串口号,还有一点,我串口用cubemx生成的,所以配置都已经设置好了是115200,80,1所以,如果你的波特率不是这个的话,还要修改定时器htim4的配置,因为这个定时器超时时间是modbus根据波特率来设定的,具体的可以参考以前写过的一个帖子
http://www.stm32cube.com/question/179
修改到这里,基本的移植算是结束,编译.
二楼继续!!
已邀请:

admin

赞同来自: 聚恒 jk407529339

二楼
下面我们在main.c文件里初始化并使能modbus

图片6.png

为了便于以后的更新维护,我们新建一个modbus_deal.c文件.里面准备存放上面提到的四个协议处理函数,再建一个modbus_deal.h 文件

图片7.png

同时把原先main.c中的那四个函数剪切过来

图片8.png

main.c中的头文件更改为
#include "modbus_deal.h"
添加modbus_deal.c 到工程中,编译

图片9.png

 
下面开始正题
我们的应用主要是针对一些了的数据,所以类似读写数组的操作
于是,主要针对eMBRegHoldingCB()函数进行改造,如下图

图片10.png

 
以下看 三楼!!!
 

admin

赞同来自: 聚恒 jk407529339

三楼
为了测试是否成功配置modbus,我们先测试读取
所以,我们在函数中定义一个指针变量
uint8_t *p_data;
我们将eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
                 eMBRegisterMode eMode )
协议取得到的指针变量和刚才定义的指针变量指向重合
并且在读取判定里面添加如下测试数据:

图片11.png

接下来,我们编译并烧录,运行,打开串口工具,发送如下,并查看返回的数据

图片12.png

我们发送:01 03 00 00 00 0A C5 CD
含义是:从机地址-------功能号------数据地址------读取数据个数---- CRC校验
读取编号为01的设备数据,从0000地址开始,读取10*2个字节的数据
注意这里的10*2因为读取是按照16位来的,所以换算成字节要乘以2
于是我们得到了返回的数据:
01 03 14 00 01 02 03 04 05 06 07 08 09 00 00 00 00 00 00 00 00 00 00 CA 11 
从01设备返回了0x14即20个字节数据,分别是
00 01 02 03 04 05 06 07 08 09 00 00 00 00 00 00 00 00 00 00
很显然这个不是我们要的数据,所以修改读取5*2的字节数
再次发送:
图片13.png

得到了,我们刚刚定义的数据
如果我们修改发送数据为读取从0x04开始的6个数据
代码为:01 03 00 04 00 03 44 0A
我们得到:01 03 06 00 01 02 03 04 05 2F CE 
很显然,得到了6个数据是不错,但是并不是我们设定的从0x04开始的
这是因为我们没有用到
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
                 eMBRegisterMode eMode )
中的usAddress 这个是协议解析之后得到的起始地址变量.
 
看四楼!!

admin

赞同来自:

四楼
如果,我不想/不能定义读取的长度,怎么才能返回有效长度呢?
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
                 eMBRegisterMode eMode )
中的usNRegs 如果直接在函数中修改,显然是不对的,
利用指针来修改是可行的,所以要修改eMBRegHoldingCB()这个函数

我们打开mb.h 文件,将里面的usNRegs 改为指针类型

图片14.png

打开我们自己创建的文件 modbus_deal.c 将其也改为指针类型

图片15.png

打开 mbfuncholding.c文件
修改以下两处地方

图片16.png


图片17.png


图片18.png


图片19.png

保存,
我们将modbus_deal.c文件里面测试代码修改为

图片20.png

上面代码的意思是,无论上位机发送的代码要求读取多长的数据,我们只读取3*2个字节数据
我们测试一下:

图片21.png

上面的例子是上位机要求读取id是01的设备的从0000开始的5*2个字节数据
但是返回的是01 03 0A 00 01 02 03 04 05 E3 CE
从01返回0A个数据字节,分别是 00 01 02 03 04 05
显然数据是我们代码里面需求的,但是这个长度字节0A是按照我们上位机发送的指令计算来的,并不是实际的长度,为了和数据长度同步,那么我们可以删除0A 这个被定义为长度的项
.然后在有效数组里面添加长度定义
 
看五楼!!
 
 

admin

赞同来自:

五楼
 
假定我们先删除了下面图片所示的一行代码:

图片22.png

上面代码就是定义的由上位机下发指定的长度计算得到返回数组长度的方程,删除.
编译后,
图片23.png

此时就没有了标准modbus的返回协议了.
这时返回的数据长度就可以在用户代码里面自己定义了,
此时我们又再次注意到一个问题:
我们修改*usNRegs = 4; 这个的时候,每次返回的都是偶数个数据字节
如果我要返回任意数个字节怎么做?
打开mbfuncholding.c文件
图片24.png

将对应的文件代码删除*2,这样每次我们就可以按照我们自己要返回的字节数进行返回了.
例如我们修改*usNRegs = 7;

图片25.png

可以看到我们得到了想要的7个字节的数据

以上这样修改,使得获取数据的代码更加的灵活.
 

admin

赞同来自:

六楼
 
以上修改仅仅只是读取的时候返回数据灵活了,但是,还是发送方面还是要完全按照modbus协议格式才行,下面我们对接收进行灵活处理.
 

图片26.png


图片27.png

仅仅保留callback函数,其他都注释掉.
这样做的好处是,在modbus_deal.c中操作读取就可以像操作数组一样简单了.
例如
 

图片28.png

如上图添加了代码,意思是接收到的数据都存在pucRegBuffer 这个指针的位置,
我们可以像操作数组一样取数据,然后处理.
例如:01 03 06 A0 F2
上面要读取id是01的设备,读取6个字节,于是返回了
01 03 11 22 33 44 55 66 B9 AA 
11 22 33 44 55 66 这6个字节
 
注意:我们先前已经在外部定义了一个数组
uint8_t data[10]={0x11,0x22,...,0x99};
这里在说明一下:
modbus接受到01 03 06 A0 F2 这个数据,会先依据协议解析
所以pucRegBuffer存储了06
例如是:01 03 06 07 08 09 XX XX
那么pucRegBuffer[0]=06,pucRegBuffer[1]=07,pucRegBuffer[2]=08,pucRegBuffer[3]=09,
一次类推

admin

赞同来自:

为了方便使用写多个寄存器需要摒弃掉原来的协议,
打开mbfuncholding.c文件
找到eMBFuncWriteMultipleHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
修改里面的代码.
此处注意,需要手工添加一个

            *usLen = MB_PDU_FUNC_OFF;
                        *usLen += 1;
的命令,因为返回的帧里面包含了写入操作码

图片29.png


图片30.png

修改modbus_deal.c里面的写入函数
图片31.png

当有写入操作的时候,即返回ok这个标识

图片32.png

 

在路上

赞同来自: crystalkj

赞一个,正是我需要的。
 
问下 如果想直接操作GPIO的端口的状态如何操作,是用eMBRegCoilsCB()来改造么??可否加个QQ

进盛0000

赞同来自:

厉害啊,刚出来工作的项目就涉及到Modbus,借助楼主的分析,明天的调试进度又可以跟进了,哎再过几天都不敢见到经理了,谢谢楼主的无私

聚恒

赞同来自:

根据楼主的教程,freemodbus可以成功反馈数据,但是第二次测试时,每次有数据发送,可以发生中断程序一直停留在eMBPoll( void )中eEvent的判断上1479889697929363.jpg,重新写了一个串口中断程序测试发送串口发送的数据是完全没有问题的,请楼主帮忙看一下这是什么原因,灰常感谢!不胜涕零!


wansaiyon

赞同来自:

您好,已经成功移植您的方法,现在使用中有两个问题请教:1.我想把接收的usRegHoldingBuf[]中的数据保存到FLASH,想根据接收的信息来保存,如只有接收到01 02 03 04 05 06 07 08时,才将03 04两个信息保存起来,请问要修改的话在哪里修改?2.现在usRegHoldingBuf[]索引的最大长度是多少,我现在只能用到usRegHoldingBuf[7000],不能用到usRegHoldingBuf[8000].

wansaiyon

赞同来自:

楼主 这个寄存器数组usRegHoldingBuf[]最大长度能够到多少啊,

穿越时空

赞同来自:

学习了,楼主

zjczm

赞同来自:

很不错。谢谢。学习一下。

tmooc

赞同来自:

感谢

993347045qq - 80前奋斗中的农民工

赞同来自:

写的很详细,受教了。感谢!

要回复问题请先登录注册