验证环境
- Win10 64位
- Keil MDK 5.30
- ART-Pi 开发板:STM32H750XBH6开发板
- 工程:最小RT-Thread 系统,版本:RT-Thread v4.1.0 released
环境搭建
- 首先需要配置好ART-Pi的WIFI,这部分配置方式可以查看前面的文章
- 本次验证TCP 通信,基于libmodbus 实现 modbus-tcp 从机功能
- 配置使能 libmodbus 软件包
- 使用RT-Thread ENV 工具:
pkgs --update
,把软件包更新(下载)到本地 工程packages目录
- 这里把 libmodbus 软件里面的 tcp 例程复制到工程的 applications 目录,目的是可以修改这个例程,如果在软件包中修改,不利于保存
修复编译
libmodbus软件包修改如下:
- modbus.c 中:
#include <errno.h> -> #include <sys/errno.h>
- modbus-rtu.c中:
#include <dfs_select.h> -> #include <sys/select.h>
- modbus-private.h 中:增加
#include <sys/select.h>
测试例程
- 这里作为从机,例程默认为从机,初步看了下,主机可以读取【线圈】与【保持寄存器】,这部分例程可以简单的修改下,利于主机验证功能
#include "modbus_tcp_test.h"
#include <modbus.h>
#include <stdio.h>
#include <string.h>
//#include <dfs_posix.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sal_socket.h>
// static void test_thread(void *param)
// {
// uint16_t tab_reg[64] = {0};
// modbus_t *ctx = RT_NULL;
// ctx = modbus_new_tcp("192.168.1.103", 601, AF_INET);
// modbus_set_slave(ctx, 3);
// modbus_set_response_timeout(ctx, 0, 1000000);
// _mbtcp_start:
// if(modbus_connect(ctx) < 0)
// goto _mbtcp_restart;
// int num = 0;
// while (1)
// {
// memset(tab_reg, 0, 64 * 2);
// int regs = modbus_read_registers(ctx, 0, 20, tab_reg);
// if(regs < 0)
// goto _mbtcp_restart;
// printf("-------------------------------------------\n");
// printf("[%4d][read num = %d]", num, regs);
// num++;
// int i;
// for (i = 0; i < 20; i++)
// {
// printf("<%#x>", tab_reg[i]);
// }
// printf("\n");
// printf("-------------------------------------------\n");
// rt_thread_mdelay(1000);
// }
// _mbtcp_restart:
// //7-关闭modbus端口
// modbus_close(ctx);
// rt_thread_mdelay(2000);
// goto _mbtcp_start;
// //8-释放modbus资源
// modbus_free(ctx);
// }
#define MAX_CLIENT_NUM 3
#define CLIENT_TIMEOUT 10 //单位 s
typedef struct
{
int fd;
rt_tick_t tick_timeout;
}client_session_t;
static void test_thread(void *param)
{
int server_fd = -1;
modbus_t *ctx = NULL;
modbus_mapping_t *mb_mapping = NULL;
client_session_t client_session[MAX_CLIENT_NUM];
for (int i = 0; i < MAX_CLIENT_NUM; i++)
{
client_session[i].fd = -1;
client_session[i].tick_timeout = rt_tick_get() + rt_tick_from_millisecond(CLIENT_TIMEOUT * 1000);
}
int max_fd = -1;
fd_set readset;
int rc;
struct timeval select_timeout;
select_timeout.tv_sec = 1;
select_timeout.tv_usec = 0;
ctx = modbus_new_tcp(RT_NULL, 1502, AF_INET);
RT_ASSERT(ctx != RT_NULL);
mb_mapping = modbus_mapping_new(MODBUS_MAX_READ_BITS, 0,
MODBUS_MAX_READ_REGISTERS, 0);
RT_ASSERT(mb_mapping != RT_NULL);
mb_mapping->tab_registers[0] = 'R';
mb_mapping->tab_registers[1] = 'T';
mb_mapping->tab_registers[2] = '-';
mb_mapping->tab_registers[3] = 'T';
mb_mapping->tab_registers[4] = 'h';
mb_mapping->tab_registers[5] = 'r';
mb_mapping->tab_registers[6] = 'e';
mb_mapping->tab_registers[7] = 'a';
mb_mapping->tab_registers[8] = 'd';
_mbtcp_start:
server_fd = modbus_tcp_listen(ctx, 1);
if (server_fd < 0)
goto _mbtcp_restart;
while (1)
{
max_fd = -1;
FD_ZERO(&readset);
FD_SET(server_fd, &readset);
if(max_fd < server_fd)
max_fd = server_fd;
for (int i = 0; i < MAX_CLIENT_NUM; i++)
{
if(client_session[i].fd >= 0)
{
FD_SET(client_session[i].fd, &readset);
if(max_fd < client_session[i].fd)
max_fd = client_session[i].fd;
}
}
rc = select(max_fd + 1, &readset, RT_NULL, RT_NULL, &select_timeout);
if(rc < 0)
{
goto _mbtcp_restart;
}
else if(rc > 0)
{
if(FD_ISSET(server_fd, &readset))
{
int client_sock_fd = modbus_tcp_accept(ctx, &server_fd);
if(client_sock_fd >= 0)
{
int index = -1;
for (int i = 0; i < MAX_CLIENT_NUM; i++)
{
if(client_session[i].fd < 0)
{
index = i;
break;
}
}
if(index >= 0)
{
client_session[index].fd = client_sock_fd;
client_session[index].tick_timeout = rt_tick_get() + rt_tick_from_millisecond(CLIENT_TIMEOUT * 1000);
}
else
{
close(client_sock_fd);
}
}
}
for (int i = 0; i < MAX_CLIENT_NUM; i++)
{
if(client_session[i].fd >= 0)
{
if(FD_ISSET(client_session[i].fd, &readset))
{
uint8_t query[MODBUS_TCP_MAX_ADU_LENGTH];
modbus_set_socket(ctx, client_session[i].fd);
rc = modbus_receive(ctx, query);
if (rc > 0)
{
mb_mapping->tab_registers[9] = rt_tick_get() % 1000;
mb_mapping->tab_registers[10] = rt_tick_get() / 1000;
rc = modbus_reply(ctx, query, rc, mb_mapping);
if(rc < 0)
{
close(client_session[i].fd);
client_session[i].fd = -1;
}
else
{
client_session[i].tick_timeout = rt_tick_get() + rt_tick_from_millisecond(CLIENT_TIMEOUT * 1000);
}
}
else
{
close(client_session[i].fd);
client_session[i].fd = -1;
}
}
}
}
}
// 客户端超时未收到数据断开
for(int i =0;i<MAX_CLIENT_NUM;i++)
{
if(client_session[i].fd >= 0)
{
//超时
if((rt_tick_get() - client_session[i].tick_timeout) < (RT_TICK_MAX / 2))
{
close(client_session[i].fd);
client_session[i].fd = -1;
}
}
}
}
_mbtcp_restart:
if(server_fd >= 0)
{
close(server_fd);
server_fd = -1;
}
for(int i =0;i<MAX_CLIENT_NUM;i++)
{
if(client_session[i].fd >= 0)
{
close(client_session[i].fd);
client_session[i].fd = -1;
}
}
rt_thread_mdelay(5000);
goto _mbtcp_start;
modbus_free(ctx);
}
static int tcp_test_init(void)
{
rt_thread_t tid;
tid = rt_thread_create("mbtcp_test",
test_thread, RT_NULL,
2048,
12, 10);
if (tid != RT_NULL)
rt_thread_startup(tid);
return RT_EOK;
}
//INIT_APP_EXPORT(tcp_test_init);
MSH_CMD_EXPORT(tcp_test_init, tcp_test_init);
- 【注意点】因为使用wifi,我还没有搞清楚如何自动连接【路由器】自动上网,所以把modbus-tcp 例程改为了shell 命令,手动执行
功能验证
- 编译通过后,下载到开发板
- wifi 连接路由器,联网功能要通
- 系统启动与操作步骤如下:
___ ______ _____ ______ _ ______ _____ _____ _____
/ _ \ | ___ \|_ _| | ___ \(_) | ___ \/ _ \/ _ \|_ _|
/ /_\ \| |_/ / | | ______ | |_/ / _ | |_/ /| | | || | | | | |
| _ || / | | |______|| __/ | | | ___ \| | | || | | | | |
| | | || |\ \ | | | | | | | |_/ /\ \_/ /\ \_/ / | |
\_| |_/\_| \_| \_/ \_| |_| \____/ \___/ \___/ \_/
Powered by RT-Thread.
[D/drv.sdram] sdram init success, mapped at 0xC0000000, size is 33554432 bytes, data width is 16
[I/I2C] I2C bus [i2c2] registered
\ | /
- RT - Thread Operating System
/ | \ 4.1.0 build Apr 2 2022 20:57:46
2006 - 2022 Copyright by RT-Thread team
lwIP-2.0.3 initialized!
[I/sal.skt] Socket Abstraction Layer initialize success.
[I/SFUD] Find a Winbond flash chip. Size is 16777216 bytes.
[I/SFUD] norflash0 flash device is initialize success.
[I/SFUD] Probe SPI flash norflash0 by SPI device spi10 success.
[D/FAL] (fal_flash_init:47) Flash device | norflash0 | addr: 0x00000000 | len: 0x01000000 | blk_size: 0x00001000 |initialized finish.
[I/FAL] ==================== FAL partition table ====================
[I/FAL] | name | flash_dev | offset | length |
[I/FAL] -------------------------------------------------------------
[I/FAL] | wifi_image | norflash0 | 0x00000000 | 0x00080000 |
[I/FAL] | bt_image | norflash0 | 0x00080000 | 0x00080000 |
[I/FAL] | download | norflash0 | 0x00100000 | 0x00200000 |
[I/FAL] | easyflash | norflash0 | 0x00300000 | 0x00100000 |
[I/FAL] | filesystem | norflash0 | 0x00400000 | 0x00c00000 |
[I/FAL] =============================================================
[I/FAL] RT-Thread Flash Abstraction Layer initialize success.
[I/FAL] The FAL block device (filesystem) created successfully
filesystem mount to '/'
msh />
msh />
msh />
msh />[I/WWD] wifi initialize done. wiced version 3.3.1
[I/WLAN.dev] wlan init success
[I/WLAN.lwip] eth device init ok name:w0
msh />wifi scan /* 搜索wifi 热点 */
SSID MAC security rssi chn Mbps
------------------------------- ----------------- -------------- ---- --- ----
42e6ab17 c8:f7:42:e6:ab:17 WPA2_MIXED_PSK -42 6 72
TP-LINK_7788 64:6e:97:8c:77:88 WPA2_AES_PSK -59 11 450
402 c2:d2:47:c0:66:57 WPA2_AES_PSK -62 6 144
402_Wi-Fi5 c2:d2:47:c0:66:65 WPA2_AES_PSK -63 6 144
CMCC-76ka ac:a4:6e:66:a8:d0 WPA2_MIXED_PSK -67 1 144
BreakDown 9c:9d:7e:43:cb:39 WPA2_MIXED_PSK -70 7 144
114501 64:6e:97:a4:c7:f6 WPA2_AES_PSK -79 11 300
ChinaNet-3NZ5 7c:fa:a0:10:d0:c0 WPA2_AES_PSK -85 3 144
msh />
msh />
msh />wifi join TP-LINK_7788 xxxxx /* 连接无线路由器,注意账号密码 */
[W/WWD] Join the network not authenticated!
[I/WWD] trying to reconnect...
[I/WLAN.mgnt] wifi connect success ssid:TP-LINK_7788
msh />
msh />
msh />[I/WLAN.lwip] Got IP address : 192.168.0.106
msh />
msh />ifconfig
network interface device: w0 (Default)
MTU: 1500
MAC: c0 84 7d 97 dd 02
FLAGS: UP LINK_UP INTERNET_UP DHCP_ENABLE ETHARP BROADCAST IGMP
ip address: 192.168.0.106
gw address: 192.168.0.1
net mask : 255.255.255.0
dns server #0: 192.168.1.1
dns server #1: 192.168.0.1
msh />
msh />tcp_test_init /* 网络通了,就启动libmodbus tcp 例程 */
功能验证
- 电脑端开启:
Modbus Poll
modbus 主机工具
- 设置连接方式,注意端口号默认:1502
- 功能验证初步通过
- 偶尔有通信错误,并能检测出来,这说明了网络通信,不能保证百分百通信准确,需要应用协议进行判断
小结
- 通过搭建libmodbus TCP 通信例程,熟悉嵌入式TCP 通信