3384|7

205

帖子

0

TA的资源

纯净的硅(初级)

【国民技术低功耗系列N32L43x测评】06.基于USART实现Agile Modbus主从通讯 [复制链接]

概述

Agile Modbus是轻量型Modbus协议栈,遵循Apache-2.0许可,满足用户任何场景下的使用需求,具有如下特性:

  • 支持RTU和TCP协议,使用纯C开发,不涉及任何硬件接口,可在任何形式的硬件上直接使用
  • 由于其使用纯C开发、不涉及硬件,完全可以在串口上跑TCP协议,在网络上跑RTU协议
  • 支持符合Modbus格式的自定义协议
  • 同时支持多主机和多从机
  • 使用简单,只需要将RTU或TCP句柄初始化好后,调用相应API进行组包和解包即可

 

资源占用情况

开发环境

FLASH

RAM

KEIL MDK

7192

0

GCC

13970

24

 

硬件环境准备

1.jpg

 

在N32L43X系列MCU上使用Agile Modbus

我们使用MCU的USART2引脚(PA2/PA3)与RS485模块进行连接,然后将RS485模块的AB与USB转RS485工具进行连接,在电脑端使用串口调试助手进行辅助测试。

2.jpg 对于USART2除了时行常规配置外(115200/N/8/1),另外使能串口接收中断,将收到的数据存放在队列中,使能接收空闲中断,在接收完成后置接收完成标志位,供agile modbus使用,具体实现代码如下所示:

/* Private variables ---------------------------------------------------------*/
uint8_t RS485_IDLEF = 0;

/*******************************************************************************
 * [url=home.php?mod=space&uid=159083]@brief[/url] * @param       
 * @retval      
 * [url=home.php?mod=space&uid=1020061]@attention[/url] *******************************************************************************/
void RS485_Init(void)
{
    GPIO_InitType   GPIO_InitStructure;
    NVIC_InitType   NVIC_InitStructure;
    USART_InitType  USART_InitStructure;

    QUEUE_INIT(QUEUE_RS485_RX_IDX);

    RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOA, ENABLE);
    RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_USART2, ENABLE);

    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    GPIO_InitStruct(&GPIO_InitStructure);
    GPIO_InitStructure.Pin              = GPIO_PIN_2;
    GPIO_InitStructure.GPIO_Mode        = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Alternate   = GPIO_AF4_USART1;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    GPIO_InitStruct(&GPIO_InitStructure);
    GPIO_InitStructure.Pin              = GPIO_PIN_3;
    GPIO_InitStructure.GPIO_Pull        = GPIO_Pull_Up;
    GPIO_InitStructure.GPIO_Alternate   = GPIO_AF4_USART1;
    GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);

    USART_StructInit(&USART_InitStructure);
    USART_InitStructure.BaudRate            = 115200;
    USART_InitStructure.WordLength          = USART_WL_8B;
    USART_InitStructure.StopBits            = USART_STPB_1;
    USART_InitStructure.Parity              = USART_PE_NO;
    USART_InitStructure.HardwareFlowControl = USART_HFCTRL_NONE;
    USART_InitStructure.Mode                = USART_MODE_RX | USART_MODE_TX;
    USART_Init(USART2, &USART_InitStructure);

    USART_ClrFlag(USART2, USART_FLAG_RXDNE);
    USART_ConfigInt(USART2, USART_INT_RXDNE, ENABLE);

    USART_ClrFlag(USART2, USART_FLAG_IDLEF);
    USART_ConfigInt(USART2, USART_INT_IDLEF, ENABLE);

    USART_Enable(USART2, ENABLE);
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void USART2_IRQHandler(void)
{
    if(USART_GetIntStatus(USART2, USART_INT_IDLEF) != RESET)
    {
        uint8_t RxData = USART_ReceiveData(USART2);
        RS485_IDLEF = 1;
    }

    if(USART_GetIntStatus(USART2, USART_INT_RXDNE) != RESET)
    {
        QUEUE_WRITE(QUEUE_RS485_RX_IDX, USART_ReceiveData(USART2));
        USART_ClrIntPendingBit(USART2, USART_INT_RXDNE);
    }
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void RS485_SendData(uint8_t Data)
{
    USART_SendData(USART2, Data);
    while (USART_GetFlagStatus(USART2, USART_FLAG_TXDE) == RESET);
}

 

 

主机实现步骤

  • 使用agile_modbus_rtu_init函数初始化RTU环境
  • 使用agile_modbus_set_slave设置从机地址
  • 清空接收缓存
  • 使用agile_modbus_serialize_xxx打包请求数据
  • 发送数据
  • 等待数据接收完成
  • 使用agile_modbus_deserialize_xxx解析响应的数据
  • 用户处理得到的数据

 

主机实现代码

/* Private variables ---------------------------------------------------------*/
agile_modbus_rtu_t rtu_master;


/* Private variables ---------------------------------------------------------*/
uint8_t rtu_master_send_buf[AGILE_MODBUS_MAX_ADU_LENGTH];
uint8_t rtu_master_recv_buf[AGILE_MODBUS_MAX_ADU_LENGTH];


/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/* Exported variables --------------------------------------------------------*/
extern uint8_t RS485_IDLEF;


/* Exported function prototypes ----------------------------------------------*/


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void rtu_master_init(void)
{
    agile_modbus_rtu_init(&rtu_master, rtu_master_send_buf, sizeof(rtu_master_send_buf),
                                       rtu_master_recv_buf, sizeof(rtu_master_recv_buf));

    agile_modbus_set_slave(&rtu_master._ctx, 1);
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void rtu_master_handler(void)
{
    uint16_t hold_register[10];

    static int state = 0;

    switch(state)
    {
        case 0:
        {
            int send_len = 0;

            send_len = agile_modbus_serialize_read_registers(&rtu_master._ctx, 0, 10);

            if(send_len)
            {
                for(int i = 0; i < send_len; i++)
                {
                    RS485_SendData(rtu_master._ctx.send_buf[i]);
                }

                state = 1;
            }
        }
        break;

        case 1:
        {
            if(RS485_IDLEF == 1)
            {
                RS485_IDLEF = 0;

                int recv_len = 0;

                while(QUEUE_EMPTY(QUEUE_RS485_RX_IDX) == 0)
                {
                    rtu_master._ctx.read_buf[recv_len++] = QUEUE_READ(QUEUE_RS485_RX_IDX);
                }

#if 0
                printf("\r\nrecv_len[%d] : ", recv_len);

                for(int i = 0; i < recv_len; i++)
                {
                    printf("0x%02x ", rtu_master._ctx.read_buf[i]);
                }

                printf("\r\n");
#endif

                if(recv_len)
                {
                    int rc = agile_modbus_deserialize_read_registers(&rtu_master._ctx, recv_len, hold_register);

                    if(rc >= 0) 
                    {
                        printf("\r\nHold Register : \r\n");

                        for(int i = 0; i < 10; i++)
                        {
                            printf("0x%02x ", hold_register[i]);
                        }

                        printf("\r\n");
                    }
                }

                state = 0;
            }
        }
        break;

        default:
            break;
    }
}

 

 

主机测试结果

3.png

 

从机实现步骤

  • 实现agile_modbus_slave_callback_t类型回调函数
  • 使用agile_modbus_rtu_init初始化RTU环境
  • 使用agile_modbus_set_slave设置从机地址
  • 等待数据接收完成
  • 使用agile_modbus_slave_handler处理请求数据
  • 清空接收缓存(可选)
  • 发送数据

 

 

从机实现代码

/* Private define ------------------------------------------------------------*/
#define TAB_MAX_NUM     10


/* Private macro -------------------------------------------------------------*/


/* Private variables ---------------------------------------------------------*/
agile_modbus_rtu_t rtu_slave;


/* Private variables ---------------------------------------------------------*/
uint8_t rtu_slave_send_buf[AGILE_MODBUS_MAX_ADU_LENGTH];
uint8_t rtu_slave_recv_buf[AGILE_MODBUS_MAX_ADU_LENGTH];


/* Private variables ---------------------------------------------------------*/
static uint8_t  _tab_bits           [TAB_MAX_NUM] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
static uint8_t  _tab_input_bits     [TAB_MAX_NUM] = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1};
static uint16_t _tab_registers      [TAB_MAX_NUM] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
static uint16_t _tab_input_registers[TAB_MAX_NUM] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};


/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/* Exported variables --------------------------------------------------------*/
extern uint8_t RS485_IDLEF;


/* Exported function prototypes ----------------------------------------------*/


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
int rtu_slave_callback(agile_modbus_t *ctx, struct agile_modbus_slave_info *slave_info)
{
    int function = slave_info->sft->function;
    int ret = 0;

    switch(function)
    {
        case AGILE_MODBUS_FC_READ_COILS:
        case AGILE_MODBUS_FC_READ_DISCRETE_INPUTS:
        {
            int address = slave_info->address;
            int nb = slave_info->nb;
            int send_index = slave_info->send_index;
            int is_input = (function == AGILE_MODBUS_FC_READ_DISCRETE_INPUTS);

            for(int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
            {
                if(now_address >= 0 && now_address < TAB_MAX_NUM)
                {
                    int index = now_address - 0;
                    agile_modbus_slave_io_set(ctx->send_buf + send_index, i, is_input ? _tab_input_bits[index] : _tab_bits[index]);
                }
            }
        }
        break;

        case AGILE_MODBUS_FC_READ_HOLDING_REGISTERS:
        case AGILE_MODBUS_FC_READ_INPUT_REGISTERS:
        {
            int address = slave_info->address;
            int nb = slave_info->nb;
            int send_index = slave_info->send_index;
            int is_input = (function == AGILE_MODBUS_FC_READ_INPUT_REGISTERS);

            for(int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
            {
                if(now_address >= 0 && now_address < TAB_MAX_NUM)
                {
                    int index = now_address - 0;
                    agile_modbus_slave_register_set(ctx->send_buf + send_index, i, is_input ? _tab_input_registers[index] : _tab_registers[index]);
                }
            }
        }
        break;

        case AGILE_MODBUS_FC_WRITE_SINGLE_COIL:
        case AGILE_MODBUS_FC_WRITE_MULTIPLE_COILS:
        {
            int address = slave_info->address;

            if(function == AGILE_MODBUS_FC_WRITE_SINGLE_COIL)
            {
                if (address >= 0 && address < TAB_MAX_NUM)
                {
                    int index = address - 0;
                    int data  = *((int *)slave_info->buf);

                    _tab_bits[index] = data;
                }
            }
            else
            {
                int nb = slave_info->nb;

                for(int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
                {
                    if(now_address >= 0 && now_address < TAB_MAX_NUM)
                    {
                        int index = now_address - 0;
                        uint8_t status = agile_modbus_slave_io_get(slave_info->buf, i);

                        _tab_bits[index] = status;
                    }
                }
            }
        }
        break;

        case AGILE_MODBUS_FC_WRITE_SINGLE_REGISTER:
        case AGILE_MODBUS_FC_WRITE_MULTIPLE_REGISTERS:
        {
            int address = slave_info->address;

            if(function == AGILE_MODBUS_FC_WRITE_SINGLE_REGISTER)
            {
                if (address >= 0 && address < TAB_MAX_NUM)
                {
                    int index = address - 0;
                    int data = *((int *)slave_info->buf);

                    _tab_registers[index] = data;
                }
            }
            else
            {
                int nb = slave_info->nb;

                for(int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
                {
                    if(now_address >= 0 && now_address < TAB_MAX_NUM)
                    {
                        int index = now_address - 0;
                        uint16_t data = agile_modbus_slave_register_get(slave_info->buf, i);

                        _tab_registers[index] = data;
                    }
                }
            }
        }
        break;

        case AGILE_MODBUS_FC_MASK_WRITE_REGISTER:
        {
            int address = slave_info->address;

            if(address >= 0 && address < TAB_MAX_NUM)
            {
                int index = address - 0;
                uint16_t data = _tab_registers[index];

                uint16_t and_op = (slave_info->buf[0] << 8) + slave_info->buf[1];
                uint16_t or_op  = (slave_info->buf[2] << 8) + slave_info->buf[3];

                data = (data & and_op) | (or_op &(~and_op));

                _tab_registers[index] = data;
            }
        }
        break;

        case AGILE_MODBUS_FC_WRITE_AND_READ_REGISTERS:
        {
            int address = slave_info->address;
            int nb = (slave_info->buf[0] << 8) + slave_info->buf[1];

            uint16_t address_write = (slave_info->buf[2] << 8) + slave_info->buf[3];

            int nb_write = (slave_info->buf[4] << 8) + slave_info->buf[5];
            int send_index = slave_info->send_index;

            /* Write first. 7 is the offset of the first values to write */
            for(int now_address = address_write, i = 0; now_address < address_write + nb_write; now_address++, i++)
            {
                if(now_address >= 0 && now_address < TAB_MAX_NUM)
                {
                    int index = now_address - 0;
                    uint16_t data = agile_modbus_slave_register_get(slave_info->buf + 7, i);

                    _tab_registers[index] = data;
                }
            }

            /* and read the data for the response */
            for(int now_address = address, i = 0; now_address < address + nb; now_address++, i++)
            {
                if(now_address >= 0 && now_address < TAB_MAX_NUM)
                {
                    int index = now_address - 0;
                    agile_modbus_slave_register_set(ctx->send_buf + send_index, i, _tab_registers[index]);
                }
            }
        }
        break;

        default:
        {
            ret = -AGILE_MODBUS_EXCEPTION_ILLEGAL_FUNCTION;
        }
        break;
    }

    return ret;
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void rtu_slave_init(void)
{
    agile_modbus_rtu_init(&rtu_slave, rtu_slave_send_buf, sizeof(rtu_slave_send_buf),
                                      rtu_slave_recv_buf, sizeof(rtu_slave_recv_buf));

    agile_modbus_set_slave(&rtu_slave._ctx, 1);
}


/*******************************************************************************
 * @brief       
 * @param       
 * @retval      
 * @attention   
*******************************************************************************/
void rtu_slave_handler(void)
{
    if(RS485_IDLEF == 1)
    {
        RS485_IDLEF = 0;

        int recv_len = 0;

        while(QUEUE_EMPTY(QUEUE_RS485_RX_IDX) == 0)
        {
            rtu_slave._ctx.read_buf[recv_len++] = QUEUE_READ(QUEUE_RS485_RX_IDX);
        }

#if 1
        printf("\r\nrecv_len[%d] : ", recv_len);

        for(int i = 0; i < recv_len; i++)
        {
            printf("0x%02x ", rtu_slave._ctx.read_buf[i]);
        }

        printf("\r\n");
#endif

        if(recv_len)
        {
            int send_len = agile_modbus_slave_handle(&rtu_slave._ctx, recv_len, 1, rtu_slave_callback, NULL);

            if(send_len)
            {
                for(int i = 0; i < send_len; i++)
                {
                    RS485_SendData(rtu_slave._ctx.send_buf[i]);
                }
            }
        }
    }
}

 

 

从机测试结果

4.png

 

附件

软件工程源代码: Template.zip (44.58 MB, 下载次数: 403)

最新回复

收藏一下   详情 回复 发表于 2022-7-22 17:31

赞赏

1

查看全部赞赏

个人签名We are a team and we work as a team !

回复
举报

6491

帖子

9

TA的资源

版主

这工程很有实用性,先学习了,谢谢分享!

回复

205

帖子

0

TA的资源

纯净的硅(初级)

lugl4313820 发表于 2022-7-19 07:55 这工程很有实用性,先学习了,谢谢分享!

个人签名We are a team and we work as a team !

回复

1

帖子

0

TA的资源

一粒金砂(初级)

lugl4313820 发表于 2022-7-19 07:55 这工程很有实用性,先学习了,谢谢分享!

666666666666666666666


回复

7013

帖子

2

TA的资源

五彩晶圆(中级)

为啥keil和gcc差的那么多。

个人签名

默认摸鱼,再摸鱼。2022、9、28


回复

301

帖子

2

TA的资源

一粒金砂(中级)

是开源的吗?要版权不

 


回复

205

帖子

0

TA的资源

纯净的硅(初级)

dwdsp 发表于 2022-7-21 08:46 是开源的吗?要版权不  

有链接(https://gitee.com/RT-Thread-Mirror/agile_modbus),自己研究哈

个人签名We are a team and we work as a team !

回复

296

帖子

0

TA的资源

一粒金砂(中级)

收藏一下


回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

关闭
站长推荐上一条 1/10 下一条

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表