开放原子训练营(第四季)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.功能演示

  • 23
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值