
开放原子训练营(第四季)TobudOS- 添加agile_modbus功能包并演示modbus rtu主机模式
开放原子训练营(第四季)TobudOS- 添加agile_modbus功能包并演示modbus rtu主机模式
atomgit代码:
https://atomgit.com/woxyzzz/TobudOS
简介
基于TobudOS添加agile_modbus功能包并演示modbus rtu主机模式。
如何移植tobudOS请查看博客:http://t.csdnimg.cn/LK6Hl
1.设置cubemx
1.启用UART4。
2.设置好波特率。
1.根据开发板,设置好串口引脚。
1.开启UART4的DMA接收,我们将采用DMA接收+IDEL空闲中断来完成数据帧的接收。
1.生成代码。
2. agile_modbus代码下载和设置
1.从github上下载好agile_modbus的代码包
https://github.com/loogg/agile_modbus
2.在工程目录下新建agile_modbus文件夹,并把下载的代码放进去。
3.在KEIL中设置好agile_modbus的代码路径
把agile_modbus/src中的文件添加进来。
4.将agile_modbus/inc的头文件路径包含进来。
3.modbus功能声明和定义
1.在User文件夹下新建modbus.c和modbus.h功能模块,添加进工程.
2. modbus.h中包含需要的头文件。
#ifndef __MODBUS_H
#define __MODBUS_H
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
#include "stdio.h"
#include "tos_k.h"
#include "agile_modbus.h"
void modubs_init(void);
#endif
3. modbus.c中添加跟UART4相关的发送和接收缓冲区代码。
#include "modbus.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]; // 发送缓冲区
4.添加需要读取的寄存器和位数据缓冲,agile_modbus需要的结构体,接收用信号量,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 k_sem_t usart_rec_sem; // 接收信号量
static k_task_t modbus_thread; // 线程句柄
#define MODBUS_THREAD_STACK_SIZE 1024
uint8_t modbus_thread_stack[MODBUS_THREAD_STACK_SIZE]; // LED任务内存空间
static void modbus_thread_entry(void *arg); // 线程入口
static int modbus_usart_read(k_tick_t time); // 数据发送
static void modbus_usart_send(uint8_t *buff, uint32_t size, uint32_t time); // 数据接收
5.添加modbus_init函数做任务和功能的初始化,由main函数引用.
void modubs_init(void)
{
tos_sem_create(&usart_rec_sem, 0);
__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接收
tos_task_create(&modbus_thread,
"modbus",
modbus_thread_entry,
NULL,
2,
modbus_thread_stack,
MODBUS_THREAD_STACK_SIZE,
20);
}
6.编写串口的发送和接收函数,还有串口数据帧接收完成时的空闲中断函数。
发生空闲中断-》数据帧接收完成-》释放信号量-》收到信号量
/* 串口读取*/
/* int: 数据帧长度 */
/* time: 等待数据时间*/
static int modbus_usart_read(k_tick_t time)
{
k_err_t uwRet = K_ERR_NONE;
uwRet = tos_sem_pend(&usart_rec_sem, time); // 获取接收空闲中断信号量
if (uwRet == K_ERR_NONE) // 收到数据帧,返回数据帧长度,数据在rx_buff内
{
return rx_size;
}
return 0;
}
/* 串口发送*/
/* *buff:待发送数据缓冲指针*/
/* size:待发送数据长度*/
/* time: 发送超时时间*/
static void modbus_usart_send(uint8_t *buff, uint32_t size, uint32_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); // 计算接收到的数据长度
tos_sem_post(&usart_rec_sem); // 释放接收信号量
HAL_UART_Receive_DMA(&UART_PORT, rx_buff, UART_REC_MAX_SIZE); // 重新开启DMA接收
}
}
4.modbus功能编写
1.对modbus rtu模式进行初始化
agile_modbus_rtu_init(&ctx_rtu, tx_buff, sizeof(tx_buff), rx_buff, sizeof(rx_buff)); // rtu初始化
agile_modbus_set_slave(ctx, 1); // 设置地址
2.在任务循环中按步骤完成功能:
【1】间隔3000ms读取从机数据
【2】将读取从机寄存器的数据帧通过agile_modbus_serialize_read_registers格式化到tx_buff中,并发送出去。
【3】等待从机回复数据,并将收到的数据通过agile_modbus_deserialize_read_registers按数据帧格式填入到hold_reg[]缓冲区,完成寄存器的读取,并打印出来。
【4】将读取到的寄存器数据值+1,通过agile_modbus_serialize_write_registers写入到从机中,完成数据的写入,下次读取将读到+1的数值。
【5】对位数据的操作与寄存器的操作类似。
下面是完整的modbus rtu主机模式任务代码:
static void modbus_thread_entry(void *arg) // 线程入口
{
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)
{
tos_task_delay(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) // 读取超时
{
printf("read hold reg timout\r\n");
continue; // 继续下次循环
}
int rc = agile_modbus_deserialize_read_registers(ctx, read_len, hold_reg); // 将收到的salve数据格式化进数据缓冲
if (rc < 0) // 格式化失败
{
printf("read hold reg failed\r\n");
if (rc != -1)
printf("error code:%d", -128 - rc);
continue; // 继续下次循环
}
printf("hold reg:\r\n"); // 打印收到的数据
for (int i = 0; i < 10; i++)
{
printf("hold_reg[%d]:0x%04x\r\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) // 读取超时
{
printf("write hold reg timout\r\n");
continue;
}
rc = agile_modbus_deserialize_write_registers(ctx, 10); // 检查从机应答数据
if (rc < 0)
{
printf("write hold reg failed\r\n");
if (rc != -1)
printf("error code:%d", -128 - rc);
continue; // 继续下次循环
}
printf("write hold reg success\r\n");
printf("\r\r\n\r\r\n\r\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)
{
printf("read coil reg timout\r\n");
continue;
}
rc = agile_modbus_deserialize_read_bits(ctx, read_len, coil_reg);
if (rc < 0)
{
printf("read coil reg failed\r\n");
if (rc != -1)
printf("error code:%d", -128 - rc);
continue;
}
printf("coil reg:\r\n");
for (int i = 0; i < 10; i++)
{
printf("coil_reg[%d]:%d\r\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) // 读取超时
{
printf("write coil reg timout\r\n");
continue;
}
rc = agile_modbus_deserialize_write_bits(ctx, 10); // 检查从机应答数据
if (rc < 0)
{
printf("write coil reg failed\r\n");
if (rc != -1)
printf("error code:%d", -128 - rc);
continue; // 继续下次循环
}
printf("write coil reg success\r\n");
printf("\r\r\n\r\r\n\r\r\n");
}
}
5.功能演示
更多推荐
所有评论(0)