[3]rt-thread-nano agile_modbus主机例程

发布时间:2024年01月05日

gitee代码:

https://gitee.com/xuwenqiang_1989/rt-thread-nano

简介

移植agile_modbus,演示主机程序。

串口:UART4

过程:每隔3秒,读取从机1的10个位数据和10个寄存器数据,将位数据取反,寄存器数据+1后,再发送返回从机。

1.下载agile_modbus包

agile_modbus作者博客:http://github.loogg.cn/agile_packages/agile_modbus/#_1-3%E3%80%81%E8%AE%B8%E5%8F%AF%E8%AF%81

agile_modbus github下载地址:https://github.com/loogg/agile_modbus

下载后,把agile_modbus放入工程

2.工程配置

将agile_modbus/src内的C文件添加进工程

将agile_modbus/inc内的头文件包含进目录

新建模块modbus_poll.c和modbus_poll.h用于演示主机程序,并用同方法添加进工程

3.在Cubemx中配置UART4

开启UART4,配置波特率,数据等参数

配置UART4所用引脚

配置UART4? ?DMA接收

开启UART4中断,取消生成IRQ函数(我们自己编写)。

开启DMA接收中断,取消生成IRQ函数。

生成代码

4.modbus_poll.h

modbus_poll.h程序入下图

#ifndef __MODBUS_POLL_H_
#define __MODBUS_POLL_H_

#include "main.h"
#include "board.h"

#include "agile_modbus.h"

int modbus_poll_init(void);

#endif /*__MODBUS_H_*/

5.modbus_poll.c

包含头文件

#include "modbus_poll.h"

声明和定义接收和发送缓冲区

#define UART_PORT huart4                               // 串口
#define UART_REC_MAX_SIZE AGILE_MODBUS_MAX_ADU_LENGTH  // 接收缓冲大小
#define UART_SEND_MAX_SIZE AGILE_MODBUS_MAX_ADU_LENGTH // 发送缓冲大小
static uint8_t rx_buff[UART_REC_MAX_SIZE];             // 接收缓冲区
static uint32_t rx_size;                               // 接收数据长度
static uint8_t tx_buff[UART_SEND_MAX_SIZE];            // 发送缓冲区

定义agile_modbus需要的变量

static uint16_t hold_reg[10];               // 数据寄存器
static uint8_t coil_reg[10];                // 位寄存器
static agile_modbus_rtu_t ctx_rtu;          // rtu结构体
static agile_modbus_t *ctx = &ctx_rtu._ctx; // modbus 结构体

定义接收用信号量,线程句柄。

声明数据发送函数,数据接收函数,线程入口函数。

static rt_sem_t usart_rec_sem = RT_NULL; // 接收信号量

static int modbus_usart_read(rt_int32_t time);                                // 数据发送
static void modbus_usart_send(uint8_t *buff, uint32_t size, rt_int32_t time); // 数据接收

static rt_thread_t modbus_thread = RT_NULL;       // 线程句柄
static void modbus_thread_entry(void *parameter); // 线程入口

modbus_poll.c完整代码:

/* agile_modbus rtu poll sample*/
/* agile modbus rtu 主机程序*/
/* 2023-12-30*/
/* by Qaaa*/

#include "modbus_poll.h"

#define UART_PORT huart4                               // 串口
#define UART_REC_MAX_SIZE AGILE_MODBUS_MAX_ADU_LENGTH  // 接收缓冲大小
#define UART_SEND_MAX_SIZE AGILE_MODBUS_MAX_ADU_LENGTH // 发送缓冲大小
static uint8_t rx_buff[UART_REC_MAX_SIZE];             // 接收缓冲区
static uint32_t rx_size;                               // 接收数据长度
static uint8_t tx_buff[UART_SEND_MAX_SIZE];            // 发送缓冲区

static uint16_t hold_reg[10];               // 数据寄存器
static uint8_t coil_reg[10];                // 位寄存器
static agile_modbus_rtu_t ctx_rtu;          // rtu结构体
static agile_modbus_t *ctx = &ctx_rtu._ctx; // modbus 结构体

static rt_sem_t usart_rec_sem = RT_NULL; // 接收信号量

static int modbus_usart_read(rt_int32_t time);                                // 数据发送
static void modbus_usart_send(uint8_t *buff, uint32_t size, rt_int32_t time); // 数据接收

static rt_thread_t modbus_thread = RT_NULL;       // 线程句柄
static void modbus_thread_entry(void *parameter); // 线程入口

/* modbus 初始化*/
int modbus_poll_init(void)
{
    usart_rec_sem = rt_sem_create("usart_sem",
                                  0,
                                  RT_IPC_FLAG_FIFO);              // 创建接收信号量
    __HAL_UART_CLEAR_IDLEFLAG(&UART_PORT);                        // 清除接收空闲中断标志
    __HAL_UART_ENABLE_IT(&UART_PORT, UART_IT_IDLE);               // 使能接收空闲中断
    HAL_UART_Receive_DMA(&UART_PORT, rx_buff, UART_REC_MAX_SIZE); // 开启DMA接收

    // agile_modbus init
    modbus_thread = rt_thread_create("modbus_thread",
                                     modbus_thread_entry,
                                     RT_NULL,
                                     1024,
                                     4,
                                     20); // 创建任务线程
    if (modbus_thread != RT_NULL)
        rt_thread_startup(modbus_thread); // 启动线程
    else
        return -1;

    return 0;
}

/* 任务线程入口函数*/
static void modbus_thread_entry(void *parameter)
{
    agile_modbus_rtu_init(&ctx_rtu, tx_buff, sizeof(tx_buff), rx_buff, sizeof(rx_buff)); // rtu初始化
    agile_modbus_set_slave(ctx, 1);                                                      // 设置地址
    while (1)
    {
        rt_thread_mdelay(3000); // 每次主机读取间隔

        /*hold reg 数据寄存器读取*/
        int send_len = agile_modbus_serialize_read_registers(ctx, 0, 10); // 格式化读取数据的发送内容
        modbus_usart_send(ctx->send_buf, send_len, 100);                  // 发送读取数据
        int read_len = modbus_usart_read(1000);                           // 等待slave返回数据
        if (read_len == 0)                                                // 读取超时
        {
            rt_kprintf("read hold reg timout\n");
            continue; // 继续下次循环
        }

        int rc = agile_modbus_deserialize_read_registers(ctx, read_len, hold_reg); // 将收到的salve数据格式化进数据缓冲
        if (rc < 0)                                                                // 格式化失败
        {
            rt_kprintf("read hold reg failed\n");
            if (rc != -1)
                rt_kprintf("error code:%d", -128 - rc);
            continue; // 继续下次循环
        }

        rt_kprintf("hold reg:\n"); // 打印收到的数据
        for (int i = 0; i < 10; i++)
        {
            rt_kprintf("hold_reg[%d]:0x%04x\n", i, hold_reg[i]);
        }

        /* 写入寄存器数据*/
        for (int i = 0; i < 10; i++)
        {
            hold_reg[i]++;
        }
        send_len = agile_modbus_serialize_write_registers(ctx, 0, 10, hold_reg); // 格式化写入数据的发送内容
        modbus_usart_send(ctx->send_buf, send_len, 100);                         // 串口发送
        read_len = modbus_usart_read(1000);                                      // 等待从机返回数据
        if (read_len == 0)                                                       // 读取超时
        {
            rt_kprintf("write hold reg timout\n");
            continue;
        }

        rc = agile_modbus_deserialize_write_registers(ctx, 10); // 检查从机应答数据
        if (rc < 0)
        {
            rt_kprintf("write hold reg failed\n");
            if (rc != -1)
                rt_kprintf("error code:%d", -128 - rc);
            continue; // 继续下次循环
        }
        rt_kprintf("write hold reg success\n");
        rt_kprintf("\r\n\r\n\r\n");

        /*coil reg 位读取*/
        send_len = agile_modbus_serialize_read_bits(ctx, 0, 10);
        modbus_usart_send(ctx->send_buf, send_len, 100);
        read_len = modbus_usart_read(1000);
        if (read_len == 0)
        {
            rt_kprintf("read coil reg timout\n");
            continue;
        }

        rc = agile_modbus_deserialize_read_bits(ctx, read_len, coil_reg);
        if (rc < 0)
        {
            rt_kprintf("read coil reg failed\n");
            if (rc != -1)
                rt_kprintf("error code:%d", -128 - rc);
            continue;
        }

        rt_kprintf("coil reg:\n");
        for (int i = 0; i < 10; i++)
        {
            rt_kprintf("coil_reg[%d]:%d\n", i, coil_reg[i]);
        }

        /* 写入位数据*/
        for (int i = 0; i < 10; i++)
        {
            coil_reg[i] ^= 1;
        }
        send_len = agile_modbus_serialize_write_bits(ctx, 0, 10, coil_reg); // 格式化写入数据的发送内容
        modbus_usart_send(ctx->send_buf, send_len, 100);                    // 串口发送
        read_len = modbus_usart_read(1000);                                 // 等待从机返回数据
        if (read_len == 0)                                                  // 读取超时
        {
            rt_kprintf("write coil reg timout\n");
            continue;
        }

        rc = agile_modbus_deserialize_write_bits(ctx, 10); // 检查从机应答数据
        if (rc < 0)
        {
            rt_kprintf("write coil reg failed\n");
            if (rc != -1)
                rt_kprintf("error code:%d", -128 - rc);
            continue; // 继续下次循环
        }
        rt_kprintf("write coil reg success\n");
        rt_kprintf("\r\n\r\n\r\n");
    }
}

/* 串口读取*/
/* int: 数据帧长度 */
/* time: 等待数据时间*/
static int modbus_usart_read(rt_int32_t time)
{
    rt_err_t uwRet = RT_EOK;
    uwRet = rt_sem_take(usart_rec_sem, time); // 获取接收空闲中断信号量
    if (uwRet == RT_EOK)                      // 收到数据帧,返回数据帧长度,数据在rx_buff内
    {
        return rx_size;
    }

    return 0;
}

/* 串口发送*/
/* *buff:待发送数据缓冲指针*/
/* size:待发送数据长度*/
/* time: 发送超时时间*/
static void modbus_usart_send(uint8_t *buff, uint32_t size, rt_int32_t time)
{
    if ((buff == NULL) || (size == 0U))
    {
        return;
    }
    HAL_UART_Transmit(&UART_PORT, buff, size, time);
}

/* 串口中断函数*/
void UART4_IRQHandler(void)
{
    uint32_t tmp;

    if (__HAL_UART_GET_FLAG(&UART_PORT, UART_FLAG_IDLE) != RESET) // 检测接收空闲中断是否挂起
    {
        __HAL_UART_CLEAR_IDLEFLAG(&UART_PORT); // 清除空闲中断标志
        tmp = UART_PORT.Instance->ISR;         // 根据手册清除空闲中断标志操作
        tmp = UART_PORT.Instance->RDR;
        tmp++;
        HAL_UART_DMAStop(&UART_PORT);                                          // 停止DMA接收
        rx_size = UART_REC_MAX_SIZE - __HAL_DMA_GET_COUNTER(UART_PORT.hdmarx); // 计算接收到的数据长度
        rt_sem_release(usart_rec_sem);                                         // 释放接收信号量
        HAL_UART_Receive_DMA(&UART_PORT, rx_buff, UART_REC_MAX_SIZE);          // 重新开启DMA接收
    }
}

5.1?modbus初始化函数

1.创建DMA接收用信号量 usart_sem

2.使能接收空闲中断 UART_IT_IDLE

3.开启DMA接收

4.创建modbus_poll线程

5.启动modbus_poll线程

/* modbus 初始化*/
int modbus_poll_init(void)
{
    usart_rec_sem = rt_sem_create("usart_sem",
                                  0,
                                  RT_IPC_FLAG_FIFO);              // 创建接收信号量
    __HAL_UART_CLEAR_IDLEFLAG(&UART_PORT);                        // 清除接收空闲中断标志
    __HAL_UART_ENABLE_IT(&UART_PORT, UART_IT_IDLE);               // 使能接收空闲中断
    HAL_UART_Receive_DMA(&UART_PORT, rx_buff, UART_REC_MAX_SIZE); // 开启DMA接收

    // agile_modbus init
    modbus_thread = rt_thread_create("modbus_thread",
                                     modbus_thread_entry,
                                     RT_NULL,
                                     1024,
                                     4,
                                     20); // 创建任务线程
    if (modbus_thread != RT_NULL)
        rt_thread_startup(modbus_thread); // 启动线程
    else
        return -1;

    return 0;
}

5.2 串口接收中断处理

1.串口进入空闲中断,说明数据桢接收完毕。

2.清除空闲中断。

3.停止串口DMA,停止接收新数据。

4.计算接收到的数据帧长度,写入rx_size。

5.释放信号量uart_rec_sem。

6.重新启动DMA接收。

/* 串口中断函数*/
void UART4_IRQHandler(void)
{
    uint32_t tmp;

    if (__HAL_UART_GET_FLAG(&UART_PORT, UART_FLAG_IDLE) != RESET) // 检测接收空闲中断是否挂起
    {
        __HAL_UART_CLEAR_IDLEFLAG(&UART_PORT); // 清除空闲中断标志
        tmp = UART_PORT.Instance->ISR;         // 根据手册清除空闲中断标志操作
        tmp = UART_PORT.Instance->RDR;
        tmp++;
        HAL_UART_DMAStop(&UART_PORT);                                          // 停止DMA接收
        rx_size = UART_REC_MAX_SIZE - __HAL_DMA_GET_COUNTER(UART_PORT.hdmarx); // 计算接收到的数据长度
        rt_sem_release(usart_rec_sem);                                         // 释放接收信号量
        HAL_UART_Receive_DMA(&UART_PORT, rx_buff, UART_REC_MAX_SIZE);          // 重新开启DMA接收
    }
}

5.3 串口数据读取

1.获取接收信号量usart_rec_sem。(数据帧接收完毕,在中断中释放)。

2.成功获取,返回接收数据帧长度。(数据在缓冲rx_buff中)。

/* 串口读取*/
/* int: 数据帧长度 */
/* time: 等待数据时间*/
static int modbus_usart_read(rt_int32_t time)
{
    rt_err_t uwRet = RT_EOK;
    uwRet = rt_sem_take(usart_rec_sem, time); // 获取接收空闲中断信号量
    if (uwRet == RT_EOK)                      // 收到数据帧,返回数据帧长度,数据在rx_buff内
    {
        return rx_size;
    }

    return 0;
}

5.4 串口数据发送

用HAL库发送数据。

/* 串口发送*/
/* *buff:待发送数据缓冲指针*/
/* size:待发送数据长度*/
/* time: 发送超时时间*/
static void modbus_usart_send(uint8_t *buff, uint32_t size, rt_int32_t time)
{
    if ((buff == NULL) || (size == 0U))
    {
        return;
    }
    HAL_UART_Transmit(&UART_PORT, buff, size, time);
}

5.4 modbus线程处理?

1.agile_modbus RTU模式初始化,配置接收和发送缓冲。

2.设置modbus地址为1。

    agile_modbus_rtu_init(&ctx_rtu, tx_buff, sizeof(tx_buff), rx_buff, sizeof(rx_buff)); // rtu初始化
    agile_modbus_set_slave(ctx, 1);                                                      // 设置地址

?1.格式化读取从机寄存器需要发送的数据帧格式。

2.发送读取从机寄存器的数据帧。

3.等待从机答复。

        /*hold reg 数据寄存器读取*/
        int send_len = agile_modbus_serialize_read_registers(ctx, 0, 10); // 格式化读取数据的发送内容
        modbus_usart_send(ctx->send_buf, send_len, 100);                  // 发送读取数据
        int read_len = modbus_usart_read(1000);                           // 等待slave返回数据
        if (read_len == 0)                                                // 读取超时
        {
            rt_kprintf("read hold reg timout\n");
            continue; // 继续下次循环
        }

1.将收到从机的答复数据帧,反格式化写入到保存数据中。

2.打印收到的数据。

        int rc = agile_modbus_deserialize_read_registers(ctx, read_len, hold_reg); // 将收到的salve数据格式化进数据缓冲
        if (rc < 0)                                                                // 格式化失败
        {
            rt_kprintf("read hold reg failed\n");
            if (rc != -1)
                rt_kprintf("error code:%d", -128 - rc);
            continue; // 继续下次循环
        }

        rt_kprintf("hold reg:\n"); // 打印收到的数据
        for (int i = 0; i < 10; i++)
        {
            rt_kprintf("hold_reg[%d]:0x%04x\n", i, hold_reg[i]);
        }

?1.将收到的从机寄存器数据+1。

2.格式化写入从机寄存器数据帧格式。

3.发送写入从机寄存器数据帧。

4.等待从机应答。

5.检查从机应答。

6.写入从机寄存器成功,打印写入寄存器成功信息。

        /* 写入寄存器数据*/
        for (int i = 0; i < 10; i++)
        {
            hold_reg[i]++;
        }
        send_len = agile_modbus_serialize_write_registers(ctx, 0, 10, hold_reg); // 格式化写入数据的发送内容
        modbus_usart_send(ctx->send_buf, send_len, 100);                         // 串口发送
        read_len = modbus_usart_read(1000);                                      // 等待从机返回数据
        if (read_len == 0)                                                       // 读取超时
        {
            rt_kprintf("write hold reg timout\n");
            continue;
        }

        rc = agile_modbus_deserialize_write_registers(ctx, 10); // 检查从机应答数据
        if (rc < 0)
        {
            rt_kprintf("write hold reg failed\n");
            if (rc != -1)
                rt_kprintf("error code:%d", -128 - rc);
            continue; // 继续下次循环
        }
        rt_kprintf("write hold reg success\n");
        rt_kprintf("\r\n\r\n\r\n");

?位数据的读取和写入,同寄存器操作。

附上完整线程代码:

/* 任务线程入口函数*/
static void modbus_thread_entry(void *parameter)
{
    agile_modbus_rtu_init(&ctx_rtu, tx_buff, sizeof(tx_buff), rx_buff, sizeof(rx_buff)); // rtu初始化
    agile_modbus_set_slave(ctx, 1);                                                      // 设置地址
    while (1)
    {
        rt_thread_mdelay(3000); // 每次主机读取间隔

        /*hold reg 数据寄存器读取*/
        int send_len = agile_modbus_serialize_read_registers(ctx, 0, 10); // 格式化读取数据的发送内容
        modbus_usart_send(ctx->send_buf, send_len, 100);                  // 发送读取数据
        int read_len = modbus_usart_read(1000);                           // 等待slave返回数据
        if (read_len == 0)                                                // 读取超时
        {
            rt_kprintf("read hold reg timout\n");
            continue; // 继续下次循环
        }

        int rc = agile_modbus_deserialize_read_registers(ctx, read_len, hold_reg); // 将收到的salve数据格式化进数据缓冲
        if (rc < 0)                                                                // 格式化失败
        {
            rt_kprintf("read hold reg failed\n");
            if (rc != -1)
                rt_kprintf("error code:%d", -128 - rc);
            continue; // 继续下次循环
        }

        rt_kprintf("hold reg:\n"); // 打印收到的数据
        for (int i = 0; i < 10; i++)
        {
            rt_kprintf("hold_reg[%d]:0x%04x\n", i, hold_reg[i]);
        }

        /* 写入寄存器数据*/
        for (int i = 0; i < 10; i++)
        {
            hold_reg[i]++;
        }
        send_len = agile_modbus_serialize_write_registers(ctx, 0, 10, hold_reg); // 格式化写入数据的发送内容
        modbus_usart_send(ctx->send_buf, send_len, 100);                         // 串口发送
        read_len = modbus_usart_read(1000);                                      // 等待从机返回数据
        if (read_len == 0)                                                       // 读取超时
        {
            rt_kprintf("write hold reg timout\n");
            continue;
        }

        rc = agile_modbus_deserialize_write_registers(ctx, 10); // 检查从机应答数据
        if (rc < 0)
        {
            rt_kprintf("write hold reg failed\n");
            if (rc != -1)
                rt_kprintf("error code:%d", -128 - rc);
            continue; // 继续下次循环
        }
        rt_kprintf("write hold reg success\n");
        rt_kprintf("\r\n\r\n\r\n");

        /*coil reg 位读取*/
        send_len = agile_modbus_serialize_read_bits(ctx, 0, 10);
        modbus_usart_send(ctx->send_buf, send_len, 100);
        read_len = modbus_usart_read(1000);
        if (read_len == 0)
        {
            rt_kprintf("read coil reg timout\n");
            continue;
        }

        rc = agile_modbus_deserialize_read_bits(ctx, read_len, coil_reg);
        if (rc < 0)
        {
            rt_kprintf("read coil reg failed\n");
            if (rc != -1)
                rt_kprintf("error code:%d", -128 - rc);
            continue;
        }

        rt_kprintf("coil reg:\n");
        for (int i = 0; i < 10; i++)
        {
            rt_kprintf("coil_reg[%d]:%d\n", i, coil_reg[i]);
        }

        /* 写入位数据*/
        for (int i = 0; i < 10; i++)
        {
            coil_reg[i] ^= 1;
        }
        send_len = agile_modbus_serialize_write_bits(ctx, 0, 10, coil_reg); // 格式化写入数据的发送内容
        modbus_usart_send(ctx->send_buf, send_len, 100);                    // 串口发送
        read_len = modbus_usart_read(1000);                                 // 等待从机返回数据
        if (read_len == 0)                                                  // 读取超时
        {
            rt_kprintf("write coil reg timout\n");
            continue;
        }

        rc = agile_modbus_deserialize_write_bits(ctx, 10); // 检查从机应答数据
        if (rc < 0)
        {
            rt_kprintf("write coil reg failed\n");
            if (rc != -1)
                rt_kprintf("error code:%d", -128 - rc);
            continue; // 继续下次循环
        }
        rt_kprintf("write coil reg success\n");
        rt_kprintf("\r\n\r\n\r\n");
    }
}

6.演示

文章来源:https://blog.csdn.net/qq_29356039/article/details/135396960
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。