龙芯技术社区
直播中

jyaxz

12年用户 430经验值
擅长:嵌入式技术
私信 关注
[2K系列]

【广东龙芯2K500先锋板试用体验】+试用4 Modbus-RTU主机

试用4 Modbus-RTU主机

使用libmodbus库实现一个modbus-rtu的主机测试,并将获取的设备信息写入到redis实时数据库中。

Libmodbus库的编译

下载libmodbus库

在libmodbus官网下载libmodbus库,网址为:https//libmodbus.org/download/

交叉编译

解压刚才下载的库压缩包,

unzip libmodbus-master.zip

生成Makefile:

./configure --host=loongarch64-linux- --prefix=~/2k500/libmodbus/install

在执行配置的配过成中,会出现如下图所示的错误提示。
image.png

解决的方法:

由于龙芯的处理器配置描述在系统中不存在,所以会出现这样的提示。需要更换包含龙芯信息的如下两个文件config.guess和config.sub。将下载的两个文件替换掉/usr/share/misc中的原有两个文件即可。

执行make & make install 后,可编译出所需的库文件。

部署

在执行了make install后,在我们执行的目录中会建立一个install目录,其中包含三个文件夹,尤其是lib下的文件,放置到2k500的相应目录下,我们就完成了部署。

image.png

hiredis库

为了在程序中直接操作redis数据,我们需要一个接口库。通过网络查询,发现hiredis这个C语言的操作库对于我们来说还是非常的方便的。

Hiredis是Redis数据库的一个极简C客户端库。

它是极简主义的,因为它只增加了对协议的最小支持,但同时它使用了一个高级的类似printf的API。

除了支持发送命令和接收应答之外,它还附带了一个与I/O层分离的应答解析器。它是一个流解析器,设计为易于重用,例如,它可以用于更高级别的语言绑定,以实现高效的应答解析。

Hiredis只支持二进制安全的Redis协议,所以你可以使用它与任何版本的Redis >= 1.2.0。

这个库附带了多个api。有同步API、异步API和应答解析API。

交叉编译

源码下载:

https://github.com/redis/hiredis(官方提供)

也可以在gitee上找到源码,下载速度也快,推荐。

将下载源码解压到ubuntu的开发环境各种,执行如下命令完成编译:

make CC=loongarch64-linux-gnu-gcc

将库文件和头文件分别提取出来备用。

部署

我们需要将编译生成的动态库复制到ls2k500系统中的 /usr/lib目录下,并且需要修改为:

cd /usr/lib
mv libhiredis.so libhiredis.so.1.1.1-dev
ln –s libhiredis.so.1.1.1-dev libhiredis.so

实现modbus主站功能与仿真子站通讯

程序目录建立

建立主站程序目t3(目录随便),建立目录结果如下图所示:
image.png

其中包括inc(头文件目录),lib(库文件目录),src(源文件目录),Makefile文件。

代码实现

程序代码包含两个部分,分别为Modbus主站数据获取功能代码,和redis数据库操作代码。
1)modbus主站代码

利用libmodbus库实现主站功能,主要步骤。
Modbus对象创建:

// 创建modbus对象
ctx = modbus_new_rtu(ip_or_device, 115200, 'N', 8, 1);

从站站号设置:

// 设置从站号
if (use_backend ==RTU) {
        modbus_set_slave(ctx,SERVER_ID);
}

响应时间设置(100ms):

// 设置超时时间 100ms
modbus_set_response_timeout(ctx, 0, 100 * 1000);

连接:

// 连接
if (modbus_connect(ctx) == -1) {
     fprintf(stderr, "Connectionfailed: %s\n",      modbus_strerror(errno)); 
     modbus_free(ctx);
     return -1;
}

根据设备信息,建立数据存储内存空间:这里主要读取输入状态,保持寄存器和输入寄存器。

// 创建设备数据存储空间
/* Allocate and initialize the memory to store the bits */

nb_points = (UT_BITS_NB > UT_INPUT_BITS_NB) ? UT_BITS_NB : UT_INPUT_BITS_NB;

tab_rp_bits = (uint8_t *) calloc(nb_points, sizeof(uint8_t));
/*Allocate and initialize the memory to store the registers */

nb_points = (UT_REGISTERS_NB > UT_INPUT_REGISTERS_NB) ? UT_REGISTERS_NB: UT_INPUT_REGISTERS_NB;

tab_rp_registers= (uint16_t *) calloc(nb_points, sizeof(uint16_t));

获取数据的循环主体(简略):

// 完成设备数据获取
while(1)
{

/** DISCRETE INPUTS **/

rc = modbus_read_input_bits(ctx,UT_INPUT_BITS_ADDRESS, UT_INPUT_BITS_NB, tab_rp_bits);

/** HOLDING REGISTERS **/

rc = modbus_read_registers(ctx, UT_REGISTERS_ADDRESS, UT_REGISTERS_NB, tab_rp_registers);
/** INPUT REGISTERS **/

rc = modbus_read_input_registers(ctx, UT_INPUT_REGISTERS_ADDRESS, UT_INPUT_REGISTERS_NB,   tab_rp_registers);
usleep(1000 * 1000);

}

清理现场,关闭连接:

/*Close the connection */
modbus_close(ctx);
modbus_free(ctx);

2)redis数据操作代码

利用hiredis库完成redis数据的操作,hiredis的各个函数的具体使用网上挺多的,这里就不详细描述了。主要实现步骤:

连接redis数据库:

//连接redis,若出错redisContext.err会设置为1,redisContext.errstr会包含描述错误信息

redisContext *redis_handle = redisConnect("172.17.100.102", 6379);
if (redis_handle == NULL || redis_handle->err)
{

     if (redis_handle)
     {
           printf("Error:%s\n", redis_handle->errstr);
    }
    else
    {
           printf("can't allocate redis context\n");
     }

      return -1;
}

输入数据库密码:

reply  = (redisReply *)redisCommand(redis_handle, "AUTH %s", passwd);


if( reply == NULL)
{
printf("Error: AUTH error!\n");
redisFree(redis_handle);
printf("redisFree\n");
return -1;
}
if( !(reply->type ==
REDIS_REPLY_STATUS && memcmp(reply->str, "OK", 2) == 0) )
{
printf("Error: AUTH error!\n");
freeReplyObject(reply);
redisFree(redis_handle);
printf("redisFree\n");

return -1;
}

读取的离散输入数据写入(数据库中键值为BIT+数字):

for (i = 0; i <UT_INPUT_BITS_NB; i++) {
//set
reply = (redisReply *)redisCommand(redis_handle , "SET BIT%d%02X", i, tab_rp_bits[i]);

if (reply!=NULL && reply->type==REDIS_REPLY_STATUS)
{
printf("%s\n", reply->str);
}
freeReplyObject(reply);
}

读取的保持寄存器写入(数据库中键值为HOLD_REG+数字):

for (i = 0; i <UT_REGISTERS_NB; i++) {

//set
reply = (redisReply *)redisCommand(redis_handle , "SET HOLD_REG%d%04X", i, tab_rp_registers[i]);
if (reply!=NULL && reply->type==REDIS_REPLY_STATUS)
{
     printf("%s\n", reply->str);
}

freeReplyObject(reply);
}

读取的输入寄存器写入(数据库中键值为INPUT_REG+数字):

for (i = 0; i <UT_INPUT_REGISTERS_NB; i++) {
//set

reply = (redisReply *)redisCommand(redis_handle , "SET INPUT_REG%d%04X", i, tab_rp_registers[i]);
if (reply!=NULL && reply->type==REDIS_REPLY_STATUS)
{
     printf("%s\n", reply->str);
}

freeReplyObject(reply);
}

modbusslave模拟子站软件

网上搜索可以找到modbus的子站仿真软件,用来配合调试比较方便。

image.png

实测运行效果

image.png

Modbus slave模拟保持寄存器的存储状态。

image.png

Modbus主机获取modbus辅机保持寄存器数据内容。
image.png

通过treenms观察redis数据库中的保持寄存器内容。

更多回帖

发帖
×
20
完善资料,
赚取积分