当前位置: 首页 > news >正文

【AT32】 AT32 移植 Freemodbus 主站

基于野火开发板 at32f437zgt6芯片 和at32 官方开发工具
移植了网上一套开源的freemodbus 主站
这里对modbus 协议不做过多的讲解 主要已实现代码为主

AT32 Work Bench

参考之前我之前的配置 与stm32cubemx软件差不多
注意485芯片的收发脚配置即可

AT32 IDE

说实话这软件太垃圾了 elf文件不能生成就 报错不能生成 具体原因都不能跳转过去 有的地方函数不存在 或者写错根本不提示 比stm32 cubeide差了不是一点半点

freemodbus 文件架构讲解 需要的文件如下
在这里插入图片描述
用户主要就是要修改port层 函数 任何一个中间件都是如此
先修改porttimer层 开启一个定时器将时钟设置为 20Khz

在这里插入图片描述
主要修改以下函数 我这里使用的tim6 这是第一个最简单的普通定时器

/** FreeModbus Libary: RT-Thread Port* Copyright (C) 2013 Armink <armink.ztl@gmail.com>** This library is free software; you can redistribute it and/or* modify it under the terms of the GNU Lesser General Public* License as published by the Free Software Foundation; either* version 2.1 of the License, or (at your option) any later version.** This library is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU* Lesser General Public License for more details.** You should have received a copy of the GNU Lesser General Public* License along with this library; if not, write to the Free Software* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA** File: $Id: porttimer_m.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions$*//* ----------------------- Platform includes --------------------------------*/
#include "port.h"
#include "wk_tmr.h"
#include <stdio.h>
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mb_m.h"
#include "mbport.h"
#include "mbconfig.h"#define Modbus_TIM TMR6#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
/* ----------------------- Variables ----------------------------------------*/
static USHORT usT35TimeOut50us;static void prvvTIMERExpiredISR(void);
static void timer_timeout_ind(void* parameter);/* ----------------------- static functions ---------------------------------*/
static void prvvTIMERExpiredISR(void);/* ----------------------- Start implementation -----------------------------*/
BOOL xMBMasterPortTimersInit(USHORT usTimeOut50us)
{usT35TimeOut50us = usTimeOut50us;tmr_cnt_dir_set(Modbus_TIM, TMR_COUNT_UP);tmr_period_buffer_enable(Modbus_TIM, FALSE);tmr_base_init(Modbus_TIM, usTimeOut50us, 14399); //初始化时钟频率为20Khz 288Mhz时钟tmr_counter_enable(Modbus_TIM, TRUE);tmr_primary_mode_select(Modbus_TIM, TMR_PRIMARY_SEL_RESET);tmr_interrupt_enable(Modbus_TIM, TMR_OVF_INT, TRUE);return TRUE;
}void vMBMasterPortTimersT35Enable()
{/* Set current timer mode, don't change it.*/vMBMasterSetCurTimerMode(MB_TMODE_T35);/* 设置T3.5超时时间 */tmr_period_value_set(Modbus_TIM, usT35TimeOut50us - 1);/* 清除计数器和标志位 */tmr_counter_value_set(Modbus_TIM, 0);tmr_flag_clear(Modbus_TIM, TMR_OVF_FLAG);/* 使能定时器 */tmr_counter_enable(Modbus_TIM, TRUE);
}void vMBMasterPortTimersConvertDelayEnable()
{/* Set current timer mode, don't change it.*/vMBMasterSetCurTimerMode(MB_TMODE_CONVERT_DELAY);/* 设置转换延时时间 */tmr_period_value_set(Modbus_TIM, MB_MASTER_DELAY_MS_CONVERT * 20 - 1);/* 清除计数器和标志位 */tmr_counter_value_set(Modbus_TIM, 0);tmr_flag_clear(Modbus_TIM, TMR_OVF_FLAG);/* 使能定时器 */tmr_counter_enable(Modbus_TIM, TRUE);
}void vMBMasterPortTimersRespondTimeoutEnable()
{/* Set current timer mode, don't change it.*/vMBMasterSetCurTimerMode(MB_TMODE_RESPOND_TIMEOUT);/* 设置响应超时时间 */tmr_period_value_set(Modbus_TIM, MB_MASTER_TIMEOUT_MS_RESPOND * 20 - 1);/* 清除计数器和标志位 */tmr_counter_value_set(Modbus_TIM, 0);tmr_flag_clear(Modbus_TIM, TMR_OVF_FLAG);/* 使能定时器 */tmr_counter_enable(Modbus_TIM, TRUE);
}void vMBMasterPortTimersDisable()
{/* 禁用定时器 */tmr_counter_enable(Modbus_TIM, FALSE);
}void prvvTIMERExpiredISR(void)
{(void) pxMBMasterPortCBTimerExpired();
}static void timer_timeout_ind(void* parameter)
{prvvTIMERExpiredISR();
}
/*** @brief This function handles TIM4 global interrupt.*/
void Modbus_TIM_GLOBAL_IRQHandler(void)
{if(tmr_flag_get(Modbus_TIM, TMR_OVF_FLAG) != RESET){/* 清除中断标志位 */tmr_flag_clear(Modbus_TIM, TMR_OVF_FLAG);//printf("TIM_OVER\r\n");gpio_bits_set(GPIOA, GPIO_PINS_11);/* 调用Modbus定时器超时处理函数 */prvvTIMERExpiredISR();}
}#endif

注意Modbus_TIM_GLOBAL_IRQHandler需要放在 tim6的中断服务函数中调用

然后修改 portserial_m.c文件 这个是串口端口的修改

/** FreeModbus Libary: RT-Thread Port* Copyright (C) 2013 Armink <armink.ztl@gmail.com>** This library is free software; you can redistribute it and/or* modify it under the terms of the GNU Lesser General Public* License as published by the Free Software Foundation; either* version 2.1 of the License, or (at your option) any later version.** This library is distributed in the hope that it will be useful,* but WITHOUT ANY WARRANTY; without even the implied warranty of* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU* Lesser General Public License for more details.** You should have received a copy of the GNU Lesser General Public* License along with this library; if not, write to the Free Software* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA** File: $Id: portserial_m.c,v 1.60 2013/08/13 15:07:05 Armink add Master Functions $*/#include "port.h"/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "mbconfig.h"
#include "wk_usart.h"
#include "at32f435_437.h"
#include "at32f435_437_usart.h"
#include "wk_system.h"
#if MB_MASTER_RTU_ENABLED > 0 || MB_MASTER_ASCII_ENABLED > 0
/* ----------------------- Static variables ---------------------------------*/
/*    定义 485收发控制脚 */
#define MODBUS_MASTER_USE_CONTROL_PIN       TRUE
#define MODBUS_MASTER_GPIO_PORT		          GPIOB
#define MODBUS_MASTER_GPIO_PIN		          GPIO_PINS_3
/* ----------------------- Defines ------------------------------------------*/
/* serial transmit event */
#define EVENT_SERIAL_TRANS_START    (1<<0)/* ----------------------- static functions ---------------------------------*/
static void prvvUARTTxReadyISR(void);
static void prvvUARTRxISR(void);//static void serial_soft_trans_irq(void* parameter);/* ----------------------- Start implementation -----------------------------*/
BOOL xMBMasterPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,eMBParity eParity)
{
#if 1/* 配置串口参数 */usart_type* rs485_port;if(ucPORT ==1)rs485_port = USART1;if(ucPORT ==2)rs485_port = USART2;if(ucPORT ==3)rs485_port = USART3;if(ucPORT ==4)rs485_port = UART4;/* 配置校验位 */switch(eParity){case MB_PAR_NONE:usart_parity_selection_config(rs485_port, USART_PARITY_NONE);usart_init(rs485_port, ulBaudRate, USART_DATA_8BITS, USART_STOP_1_BIT);break;case MB_PAR_ODD:usart_parity_selection_config(rs485_port, USART_PARITY_ODD);usart_init(rs485_port, ulBaudRate, USART_DATA_9BITS, USART_STOP_1_BIT);break;case MB_PAR_EVEN:usart_parity_selection_config(rs485_port, USART_PARITY_EVEN);usart_init(rs485_port, ulBaudRate, USART_DATA_9BITS, USART_STOP_1_BIT);break;default:return FALSE;}usart_transmitter_enable(rs485_port, TRUE);usart_receiver_enable(rs485_port, TRUE);/* 使能串口 */usart_enable(rs485_port, TRUE);
#endif    return TRUE;
}void vMBMasterPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{
/* If xRXEnable enable serial receive interrupts. If xTxENable enable* transmitter empty interrupts.*/if(xRxEnable){#if defined(MODBUS_MASTER_USE_CONTROL_PIN)	wk_delay_ms(1);gpio_bits_reset(MODBUS_MASTER_GPIO_PORT, MODBUS_MASTER_GPIO_PIN);wk_delay_ms(2);#endifusart_interrupt_enable(USART2,USART_RDBF_INT,TRUE);  //开启接收中断}else{#if defined(MODBUS_MASTER_USE_CONTROL_PIN)wk_delay_ms(1);gpio_bits_set(MODBUS_MASTER_GPIO_PORT, MODBUS_MASTER_GPIO_PIN);wk_delay_ms(2);#endifusart_interrupt_enable(USART2,USART_RDBF_INT,FALSE); //关闭接收中断}if(xTxEnable){#if defined(MODBUS_MASTER_USE_CONTROL_PIN)wk_delay_ms(1);gpio_bits_set(MODBUS_MASTER_GPIO_PORT, MODBUS_MASTER_GPIO_PIN);wk_delay_ms(2);#endifusart_interrupt_enable(USART2,USART_TDBE_INT,TRUE);}else{#if defined(MODBUS_MASTER_USE_CONTROL_PIN)	wk_delay_ms(1);gpio_bits_reset(MODBUS_MASTER_GPIO_PORT, MODBUS_MASTER_GPIO_PIN);wk_delay_ms(2);#endifusart_interrupt_enable(USART2,USART_TDBE_INT,FALSE);}
}void vMBMasterPortClose(void)
{/* 关闭串口 */usart_enable(USART2, FALSE);return;
}BOOL xMBMasterPortSerialPutByte(CHAR ucByte)
{/* 发送一个字节 */usart_data_transmit(USART2, (uint16_t) ucByte);//uart_data_tx(USART1,ucByte);return TRUE;
}BOOL xMBMasterPortSerialGetByte(CHAR * pucByte)
{/* 接收一个字节 */*pucByte = usart_data_receive(USART2);return TRUE;
}/* * Create an interrupt handler for the transmit buffer empty interrupt* (or an equivalent) for your target processor. This function should then* call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that* a new character can be sent. The protocol stack will then call * xMBPortSerialPutByte( ) to send the character.*/
void prvvUARTTxReadyISR(void)
{/* 更新状态机 */pxMBMasterFrameCBTransmitterEmpty();
}/* * Create an interrupt handler for the receive interrupt for your target* processor. This function should then call pxMBFrameCBByteReceived( ). The* protocol stack will then call xMBPortSerialGetByte( ) to retrieve the* character.*/
void prvvUARTRxISR(void)
{/* 更新状态机 */pxMBMasterFrameCBByteReceived();
}/*** @brief This function handles USART4 global interrupt.*/
void Modbus_IRQHandler(void)
{//串口接收中断if(usart_interrupt_flag_get(USART2, USART_RDBF_FLAG) != RESET){prvvUARTRxISR(); //串口接收中断调用函数usart_flag_clear(USART2, USART_RDBF_FLAG);}//发送中断if(usart_interrupt_flag_get(USART2, USART_TDBE_FLAG) != RESET){prvvUARTTxReadyISR(); 	//串口发送中断调用函数usart_flag_clear(USART2, USART_TDBE_FLAG);}
}
#endif

同样的中断函数需要放在串口的中断调用

然后创建应用层调用初始化 和启动 函数
在这里插入图片描述
在这里插入图片描述

	for(;;){poll_status = eMBMasterPoll();//更新事件wk_delay_ms(5);printf_count = printf_count+5;if(poll_status != MB_ENOERR ){printf("Poll_ERROR Type %d ",poll_status);}//应用层事件switch(APP_RUN_STATE){case 0x03 : //读保持寄存器{eMBMasterReqErrCode status;status =  eMBMasterReqReadHoldingRegister(1,0,10 ,10);if(status==MB_MRE_NO_ERR){
//				__NOP;__asm__ volatile ("nop"); //调试点}APP_RUN_STATE =0;break;}case 0x10 : //写多个保持寄存器{eMBMasterReqWriteMultipleHoldingRegister( 1,0,10,rs485_write_data,10);APP_RUN_STATE =0;break;}default :break;}if(printf_count >= 1000) //每隔一s打印信息{for(int i = 0;i<10;i++){printf("%d",usMRegHoldBuf[0][i]);}printf("\n");APP_RUN_STATE = 0x03; //重新读取保持寄存器信息printf_count=0;}}

在主函数循环调用eMBMasterPoll函数
在这里要注意 调用一个请求函数后 必须释放线程给eMBMasterPoll函数
带调用否则会一直通讯失败 这是很多人失败的原因

实验效果

使用modbus slave 软件模拟一个从站 然后将收到的数据串口打印
在这里插入图片描述
可以看到通讯成功 而且可以修改寄存器的值 串口打印的也会改变

参考文件

代码仓库

相关文章:

  • ssti刷刷刷
  • threejs小案例——贴图翻转
  • 华为云Flexus+DeepSeek征文|SpringBoot开发实战:基于ModelArts Studio高效集成DeepSeek大模型服务
  • 归并排序:分治思想的优雅实现
  • 后端框架(1):Mybatis
  • 在 C# 中将 DataGridView 数据导出为 CSV
  • ROS资料推荐学习
  • xss-labs靶场第11-14关基础详解
  • Microsoft Azure 服务4月更新告示
  • handsome主题美化及优化:10.1.0最新版 - 1
  • 网站运维基础 | 2. cms介绍及wordpress的搭建
  • 大模型——Crawl4AI使用JsonCssExtractionStrategy进行结构化数据提取
  • 真题卷001——算法备赛
  • PCIeSwitch 学习
  • MySQL的触发器
  • 本土DevOps革命:Gitee如何撬动中国企业的数字化转型新动能
  • 哈希表的实现02
  • 【C++/Qt shared_ptr 与 线程池】合作使用案例
  • 非受控组件在React中的使用场景有哪些?
  • node.js文件系统(fs) - 创建文件、打开文件、写入数据、追加数据、读取数据、创建目录、删除目录
  • 上海一保租房社区亮相,首批546套房源可拎包入住
  • 【社论】打破“隐形高墙”,让老年人更好融入社会
  • 俄方代表团抵达土耳其,俄乌直接谈判有望于当地时间上午重启
  • Offer触手可及,2025上海社会组织联合招聘专场活动正寻找发光的你
  • 普京确定俄乌谈判俄方代表团名单
  • 七部门:进一步增强资本市场对于科技创新企业的支持力度