LIBMODBUS的主从机测试例程

发布于 2019-09-26 17:07:47
去年研究了一下Free Modbus 想实现多串口同时跑modbus 顺便把Free Modbus的源码撸了一遍,今年RTT对libmodbus 有了软件包支持,想想也该用上这个了,通过ENV配置了之后很快就用上了,但是提供的例程是基于主站的,顺便把从站也测试了,直接上代码。

查看更多

关注者
0
被浏览
1.8k
20 个回答
whj467467222
whj467467222 2019-09-26
    本帖最后由 whj467467222 于 2019-9-26 17:43 编辑


这个代码是RTT libmodbus 源码仓库里的,这个是主机例程。我增加了一些自己注释。
#include "modbus_rtu_test.h"
#include "modbus.h"
#include "stdio.h"
#include "string.h"

#define RS485_RE GET_PIN(G, 8) //485控制引脚

/*轮询任务*/
static void test_thread(void *param)
{
uint16_t tab_reg[64] = {0}; //读取数据的缓存区
modbus_t *ctx = RT_NULL;
ctx = modbus_new_rtu("/dev/uart2", 115200, 'N', 8, 1); //根据你要用的那个串口修改这个格式 "/dev/uart1"
modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
modbus_rtu_set_rts(ctx, RS485_RE, MODBUS_RTU_RTS_UP); //设置485的控制脚
modbus_set_slave(ctx, 3); //设置从机站号3
modbus_connect(ctx); //连接
modbus_set_response_timeout(ctx, 0, 1000000);//超时时间
int num = 0;
while (1)
{
memset(tab_reg, 0, 64 * 2);//把缓存区的数据格式化为0;
int regs = modbus_read_registers(ctx, 0, 20, tab_reg); //读取3号从站的0地址开始的20个长度的数据,并把数据存放在tab_reg
/*打印数据*/
printf("-------------------------------------------\n");
printf("[%4d][read num = %d]", num, regs);
num++;
int i;
for (i = 0; i < 20; i++)
{
printf("<%#x>", tab_reg
    );
    }
    printf("\n");
    printf("-------------------------------------------\n");
    rt_thread_mdelay(5000);
    }
    //7-关闭modbus端口
    modbus_close(ctx);

    //8-释放modbus资源
    modbus_free(ctx);
    }

    static int rtu_test_init(void)
    {
    rt_pin_mode(RS485_RE, PIN_MODE_OUTPUT);
    rt_thread_t tid;
    tid = rt_thread_create("test",
    test_thread, RT_NULL,
    2048,
    12, 10);
    if (tid != RT_NULL)
    rt_thread_startup(tid);
    return RT_EOK;
    }
    INIT_APP_EXPORT(rtu_test_init);


whj467467222
whj467467222 2019-09-26
    本帖最后由 whj467467222 于 2019-9-26 17:50 编辑


这个是从机例程
#include "modbus_rtu_test.h"
#include "modbus.h"
#include "stdio.h"
#include "string.h"

#define RS485_RE GET_PIN(A, 8)

static modbus_mapping_t *mb_mapping = NULL;

static void test_thread(void *param)
{
int rc;
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
modbus_t *ctx = RT_NULL;
ctx = modbus_new_rtu("/dev/uart1", 115200, 'N', 8, 1);
modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485);
modbus_rtu_set_rts(ctx, RS485_RE, MODBUS_RTU_RTS_UP);
modbus_set_slave(ctx, 1);
modbus_connect(ctx);
modbus_set_response_timeout(ctx, 0, 1000000);
int num = 0;
mb_mapping = modbus_mapping_new(MODBUS_MAX_READ_BITS, 0,
MODBUS_MAX_READ_REGISTERS, 0);
if (mb_mapping == NULL) {
rt_kprintf("Failed to allocate the mapping: %s\n",
modbus_strerror(errno));
modbus_free(ctx);
return ;
}
mb_mapping->tab_registers[0] = 1;//数据缓存区
mb_mapping->tab_registers[1] = 2;
mb_mapping->tab_registers[2] = 3;
mb_mapping->tab_registers[3] = 4;
mb_mapping->tab_registers[4] = 5;
mb_mapping->tab_registers[5] = 6;

while (1)
{
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];

rc = modbus_receive(ctx, query);
if (rc > 0) {
modbus_reply(ctx, query, rc, mb_mapping);
}
}
}
static int rtu_test_init(void)
{
rt_pin_mode(RS485_RE, PIN_MODE_OUTPUT);
rt_thread_t tid;
tid = rt_thread_create("test",
test_thread, RT_NULL,
2048,
12, 10);
if (tid != RT_NULL)
rt_thread_startup(tid);
return RT_EOK;
}
INIT_APP_EXPORT(rtu_test_init);

mishi
mishi 2019-11-22
whj467467222 发表于 2019-9-26 17:44
这个是从机例程


请问一下楼主 再rt_thread中用libmodbus 485的时候串口2是配置成硬件流控制(hardware flow control rs485)吗?
whj467467222
whj467467222 2019-11-23
官方文档有如下解释
注意事项
RT_DEVICE_FLAG_STREAM:流模式用于向串口终端输出字符串:当输出的字符是 "\n" (对应 16 进制值为 0x0A)时,自动在前面输出一个 "\r"(对应 16 进制值为 0x0D) 做分行。
流模式 RT_DEVICE_FLAG_STREAM 可以和接收发送模式参数使用或 “|” 运算符一起使用。
zwm
zwm 2019-11-23
怎么修改读写控制的引脚呢!
RAiny
RAiny 2020-01-10
:Q不知道为什么,我用FreeModbus读出超过4个寄存器的值就读不出来,三个角可以,写寄存器,20个都没问题。后来换成libModbus 秒出:Q:Q:Q
whj467467222
whj467467222 2020-01-11
RAiny 发表于 2020-1-10 21:16
不知道为什么,我用FreeModbus读出超过4个寄存器的值就读不出来,三个角可以,写寄存器,20个都没问题。 ...


【MODBUS】流程分析 希望我的这篇帖子能帮到你
chenxb
chenxb 2020-02-17
whj467467222 发表于 2020-1-11 11:14
【MODBUS】流程分析 希望我的这篇帖子能帮到你


非常棒,给你点赞
newphj
newphj 2020-03-03
谢谢楼主,双从机已经跑起来
dingwave_2000
dingwave_2000 2020-05-09
从机模式下用这个函数响应:modbus_reply(ctx, query, rc, mb_mapping);收到的是这样的:
01 81 02 C1 91
01 82 02 C1 61
01 82 02 C1 61
01 83 01 80 F0
感觉是报错代码,如何才能返回想返回的数据呢?请大神指点,不知如何把想要返回的数据赋进去?
whj467467222
whj467467222 2020-05-10
dingwave_2000 发表于 2020-5-9 23:36
从机模式下用这个函数响应:modbus_reply(ctx, query, rc, mb_mapping);收到的是这样的:
01 81 02 C1 91
...


show you code
dingwave_2000
dingwave_2000 2020-05-10
whj467467222 发表于 2020-5-10 09:21
show you code


现返回的格式正确,发:01 01 00 13 00 25 0C 14
返回:01 01 05 00 00 00 00 00 91 52


返回的内容都是0,如何把自己想返回的数据加进去呢?



1589074073(1).png
xingchen8910
xingchen8910 2020-06-03

去年研究了一下Free Modbus 想实现多串口同时跑modbus 顺便把Free Modbus的源码撸了一遍,今年RTT对libmodbus 有了软件包支持,想想也该用上这个了,通过ENV配置了之后很快就用上了,但是提供的例程是基于主站的,顺便把从站也测试了,直接上代码。

楼主,你好,我在测试libmodbus时遇到一个问题,我的硬件平台是STM32F103VExx,IDE用的RTT Studio,测试的是RTU Slave例程,在不加“通讯超时监测”时,测试通过,没问题,读写都可以,后来我自己加了“通讯超时监测”,大概方法是开一个周期定时器,然后监测1s内有无数据中断,如有,超时报警,但现在遇到问题了,程序刚启动还能都正常跑,跑一会就会跳入超时报警,好一会之后通讯又恢复,如此往复,我测试的通讯周期是100ms,modbus slave的线程时间片是10,优先级12,不知道问题出在哪里还望大侠指点,感谢!相关代码如下,其他部分都是参考你发的从机例程:
static int rtu_slave_init(void){
......
......
/* 创建定时器 1 周期定时器*/
timer1 = rt_timer_create("timer1", timeout1,
RT_NULL, 1000,
RT_TIMER_FLAG_PERIODIC);
/* 启动定时器 1*/
if (timer1 != RT_NULL)
rt_timer_start(timer1);
.......
.......
}
/* 定时器1超时函数 */
static void timeout1(void *parameter)
{
rt_kprintf("periodic timer is timeout %d\n", cnt);
if(data_rev_flag==TRUE){
data_rev_flag = FALSE;
rt_kprintf("data received on time !\n");
}
else{
board_reset();
rt_kprintf("communication timeout !\n");
}
}
我在调试的时候发现,如果报超时报警,我每次暂停程序都停在IDLE线程
whj467467222
whj467467222 2020-06-04
xingchen8910 发表于 2020-6-3 15:55
楼主,你好,我在测试libmodbus时遇到一个问题,我的硬件平台是STM32F103VExx,IDE用的RTT Studio,测试的 ...


modbus_set_byte_timeout 用这个去设置一下时间
yinliang
yinliang 2020-06-04
楼主,我现在出现 undefined reference to `modbus_new_rtu'错误,该加的都加了,只是我的工程是基于芯片创建,是不是要改为基于BSP呀
whj467467222
whj467467222 2020-06-04
yinliang 发表于 2020-6-4 14:51
楼主,我现在出现 undefined reference to `modbus_new_rtu'错误,该加的都加了,只是我的工程是基于芯片创 ...


看看为什么没定义啊
yinliang
yinliang 2020-06-12
楼主 我现在改用FREEMODBUS了,问一下现在要mb_slave_samlpe启动从机,有没办法上电直接启动呀
whj467467222
whj467467222 2020-06-12
yinliang 发表于 2020-6-12 11:17
楼主 我现在改用FREEMODBUS了,问一下现在要mb_slave_samlpe启动从机,有没办法上电直接启动呀 ...


你直接调用函数就行了啊
xingchen8910
xingchen8910 2020-06-16
whj467467222 发表于 2020-6-4 10:32
modbus_set_byte_timeout 用这个去设置一下时间

设置了,没用,后来我去掉了定时器超时检查,改为信号量超时检查就没再出问题了,但目前还不是很清楚具体问题出在哪里?
/*modbus heart beat based on semaphore*/
static void heart_beat(void *param){
static rt_err_t result;
while(1)
{
result = rt_sem_take(dynamic_sem, 1000);
if(result!=RT_EOK){
board_reset();
rt_kprintf("thread2 take semaphore failed! data received timeout !\n");
}
else {
if(data_rev_flag==TRUE){
data_rev_flag = FALSE;
rt_kprintf("data received on time !\n");
rt_kprintf("thread_heart_beat take a semaphore.\r\n");
}
}
}
}
yufanyufan77
yufanyufan77 2020-06-21
    本帖最后由 yufanyufan77 于 2020-6-21 01:49 编辑



rc = modbus_receive(ctx, query);
if (rc > 0)
{
modbus_reply(ctx, query, rc, mb_mapping);
}
else
{
rt_kprintf("rec data len is 0!\n");
modbus_flush(ctx);
}


从机这样写会稳定一些

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友