https://gitee.com/xuwenqiang_1989/rt-thread-nano
移植agile_modbus,演示主机程序。
串口:UART4
过程:每隔3秒,读取从机1的10个位数据和10个寄存器数据,将位数据取反,寄存器数据+1后,再发送返回从机。
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放入工程
将agile_modbus/src内的C文件添加进工程
将agile_modbus/inc内的头文件包含进目录
新建模块modbus_poll.c和modbus_poll.h用于演示主机程序,并用同方法添加进工程
开启UART4,配置波特率,数据等参数
配置UART4所用引脚
配置UART4? ?DMA接收
开启UART4中断,取消生成IRQ函数(我们自己编写)。
开启DMA接收中断,取消生成IRQ函数。
生成代码
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_*/
包含头文件
#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接收
}
}
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;
}
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接收
}
}
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;
}
用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);
}
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");
}
}