FreeModbus RTU 从机Hal库裸机移植避坑指南

news/2024/11/13 18:18:01/文章来源:https://www.cnblogs.com/chentuze/p/18544499

首先说明 : FreeModbus 有很多个库!!!! 不同库的实现方法是略有不同的!!!
本次 FreeModbus RTU 移植 主要依据 这个网友分享的工程他人移植的库

你可能会在csdn看到他的文章, 但是完全跟着那个文章走很混乱 而且跟库的文件不一样. 故而 我重新整理了工程, 并写了一个详细的移植教程

1. 下载 FreeModbus RTU 库

FreeModbus RTU 库

2. STM32CubeMX 配置流程

我假设你已经学会使用stm32cubeMX点灯了;

2.1下载模式配置

下载模式配置

2.2 开启外部时钟

开启外部时钟

2.3 定时器配置

定时器配置

2.4 串口配置

串口配置

2.5 中断配置

开启定时器和串口的中断

2.6 配置中断函数(关闭自动生成)

中断函数关闭

2.7 配置时钟

时钟配置

3.库文件导入

3.1 .c文件汇总

_c文件汇总

3.2 .h文件汇总

_h文件索引汇总

3.3 demo文件选择

demo文件选择

4. 移植流程

ok 完成上述步骤后, 你就可以开始正式的移植工作了:

主要需要移植的地方为: portserial.c && porttimer.c && demo.c

4.1 portserial.c

vMBPortSerialEnable() 函数

这个函数要根据传入的参数进行串口接收和关闭中断使能

vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{if(xRxEnable){__HAL_UART_ENABLE_IT(&huart1, UART_IT_RXNE);		}else{__HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);}if(xTxEnable){__HAL_UART_ENABLE_IT(&huart1, UART_IT_TXE);	}else{__HAL_UART_DISABLE_IT(&huart1, UART_IT_TXE);}
}

xMBPortSerialInit() 函数

这个函数主要初始化串口,因为我们已经使用stm32cubeMX配置好串口,所以直接调用就好了, 这样子可以实现配置统一,方便阅读和修改

xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{MX_USART1_UART_Init();return TRUE;
}

xMBPortSerialPutByte () 函数

这个函数主要实现串口发送一个字节, 有点神奇的是我直接调用hal库的函数传输会有bug,通信失败. 具体原因我还没排查到.这里贴那个老哥是实现方法;

xMBPortSerialPutByte( CHAR ucByte )
{USART1->DR = ucByte;return TRUE;
}

xMBPortSerialGetByte() 函数

这个函数主要实现串口接收一个字节, 有点神奇的是我直接调用hal库的函数传输会有bug,通信失败. 具体原因我还没排查到.这里贴那个老哥的实现方法;

xMBPortSerialGetByte( CHAR * pucByte )
{*pucByte = (USART1->DR & (uint16_t)0x00FF);return TRUE;
}

手搓串口1中断函数USART1_IRQHandler()

这个中断函数要在portserial.c中手动添加, 主要是因为它调用了static void prvvUARTTxReadyISR( void ) 和 static void prvvUARTRxISR( void ); 再其他文件调用不了. 所以才要关闭hal库自动生成中断函数在这里手动添加;

void USART1_IRQHandler(void)
{if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)){__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);	prvvUARTRxISR();	}if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TXE))				{__HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_TXE);			prvvUARTTxReadyISR();}
}

4.2 porttimer.c

xMBPortTimersInit() 函数

这个函数主要初始化定时器, 因为我使用的是stm32cubeMX配置好定时器, 所以直接调用就好了, 这样子可以实现配置统一,方便阅读和修改

xMBPortTimersInit( USHORT usTim1Timerout50us )
{MX_TIM4_Init();__HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);      __HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);					return TRUE;
}

vMBPortTimersEnable() 函数

使能定时器 和情况计数器数值

vMBPortTimersEnable(  )
{__HAL_TIM_SET_COUNTER(&htim4, 0);__HAL_TIM_ENABLE(&htim4);
}

vMBPortTimersDisable() 函数

关闭定时器

vMBPortTimersDisable(  )
{__HAL_TIM_DISABLE(&htim4);
}

手搓定时器4中断函数TIM4_IRQHandler()

这个中断函数要在porttimer.c中手动添加, 主要是因为它调用了static void prvvTIMERExpiredISR( void ); 再其他文件调用不了. 所以才要关闭hal库自动生成中断函数在这里手动添加;

void TIM4_IRQHandler(void)
{if(__HAL_TIM_GET_FLAG(&htim4, TIM_FLAG_UPDATE)){__HAL_TIM_CLEAR_FLAG(&htim4, TIM_FLAG_UPDATE);prvvTIMERExpiredISR();}
}

4.3. demo.c

这个文件就是FreeModbus移植的关键位置了, 其通讯都是在调用这些信息;
以下直接贴验证的好代码, 想要自定义就修改前面那四个寄存器就可以;

#include "mb.h"
#include "mbport.h"// 十路输入寄存器
#define REG_INPUT_SIZE  10
uint16_t REG_INPUT_BUF[REG_INPUT_SIZE];// 十路保持寄存器
#define REG_HOLD_SIZE   10
uint16_t REG_HOLD_BUF[REG_HOLD_SIZE];// 十路线圈
#define REG_COILS_SIZE 10
uint8_t REG_COILS_BUF[REG_COILS_SIZE] = {1, 1, 1, 1, 0, 0, 0, 0, 1, 1};// 十路离散量
#define REG_DISC_SIZE  10
uint8_t REG_DISC_BUF[REG_DISC_SIZE] = {1,1,1,1,0,0,0,0,1,1};/// CMD4命令处理回调函数
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{USHORT usRegIndex = usAddress - 1;// 非法检测if((usRegIndex + usNRegs) > REG_INPUT_SIZE){return MB_ENOREG;}// 循环读取while( usNRegs > 0 ){*pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] >> 8 );*pucRegBuffer++ = ( unsigned char )( REG_INPUT_BUF[usRegIndex] & 0xFF );usRegIndex++;usNRegs--;}// 模拟输入寄存器被改变for(usRegIndex = 0; usRegIndex < REG_INPUT_SIZE; usRegIndex++){REG_INPUT_BUF[usRegIndex]++;}return MB_ENOERR;
}/// CMD6、3、16命令处理回调函数
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{USHORT usRegIndex = usAddress - 1;// 非法检测if((usRegIndex + usNRegs) > REG_HOLD_SIZE){return MB_ENOREG;}// 写寄存器if(eMode == MB_REG_WRITE){while( usNRegs > 0 ){REG_HOLD_BUF[usRegIndex] = (pucRegBuffer[0] << 8) | pucRegBuffer[1];pucRegBuffer += 2;usRegIndex++;usNRegs--;}}// 读寄存器else{while( usNRegs > 0 ){*pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] >> 8 );*pucRegBuffer++ = ( unsigned char )( REG_HOLD_BUF[usRegIndex] & 0xFF );usRegIndex++;usNRegs--;}}return MB_ENOERR;
}/// CMD1、5、15命令处理回调函数
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{USHORT usRegIndex   = usAddress - 1;UCHAR  ucBits       = 0;UCHAR  ucState      = 0;UCHAR  ucLoops      = 0;// 非法检测if((usRegIndex + usNCoils) > REG_COILS_SIZE){return MB_ENOREG;}if(eMode == MB_REG_WRITE){ucLoops = (usNCoils - 1) / 8 + 1;while(ucLoops != 0){ucState = *pucRegBuffer++;ucBits  = 0;while(usNCoils != 0 && ucBits < 8){REG_COILS_BUF[usRegIndex++] = (ucState >> ucBits) & 0X01;usNCoils--;ucBits++;}ucLoops--;}}else{ucLoops = (usNCoils - 1) / 8 + 1;while(ucLoops != 0){ucState = 0;ucBits  = 0;while(usNCoils != 0 && ucBits < 8){if(REG_COILS_BUF[usRegIndex]){ucState |= (1 << ucBits);}usNCoils--;usRegIndex++;ucBits++;}*pucRegBuffer++ = ucState;ucLoops--;}}return MB_ENOERR;
}/// CMD2命令处理回调函数
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{USHORT usRegIndex   = usAddress - 1;UCHAR  ucBits       = 0;UCHAR  ucState      = 0;UCHAR  ucLoops      = 0;// 非法检测if((usRegIndex + usNDiscrete) > REG_DISC_SIZE){return MB_ENOREG;}ucLoops = (usNDiscrete - 1) / 8 + 1;while(ucLoops != 0){ucState = 0;ucBits  = 0;while(usNDiscrete != 0 && ucBits < 8){if(REG_DISC_BUF[usRegIndex]){ucState |= (1 << ucBits);}usNDiscrete--;usRegIndex++;ucBits++;}*pucRegBuffer++ = ucState;ucLoops--;}// 模拟离散量输入被改变for(usRegIndex = 0; usRegIndex < REG_DISC_SIZE; usRegIndex++){REG_DISC_BUF[usRegIndex] = !REG_DISC_BUF[usRegIndex];}return MB_ENOERR;
}

5.移植最后一步 main.c 编写

ok 至此你已经完成90%的移植工作了;
现在只需要调用 对应初始化的函数 还有那四个数组就可以了;
贴一个简单版的main.c关键部分

#include "mb.h"
#include "mbport.h"void SystemClock_Config(void);
extern uint16_t REG_HOLD_BUF[10];int main(void)
{HAL_Init();eMBInit(MB_RTU, 0x01, 0, 115200, MB_PAR_NONE); eMBEnable();SystemClock_Config();REG_HOLD_BUF[0] = 0Xff00;MX_GPIO_Init();MX_TIM4_Init();MX_USART1_UART_Init();while (1){eMBPoll();	}
}

6.剩下10%的坑

如果你操作完上面的步骤发现还没能使用, 恭喜你 还有几个坑得改一改:

  1. 使用 MicroLIB 库 且 编译优化等级选择 Level 3(-O3) 重编译;
    如下:

  2. 把你的modbus助手(如 QModBus) 关闭再开启 然后就可以使用了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/833064.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

修改 NIKKE PC 端游戏缓存位置

本文记录如何使用 mklink 命令修改 NIKKE PC 端游戏缓存位置前言 NIKKE 每次版本更新都要下载大约 5~10G 的数据,以至于成为了我 AFK 的一部分原因 [允悲] 但是看游戏安装目录的大小却只有 1G 多,我还奇怪数据存哪去了,看到越来越小的 C 盘的空间才明白,草 搜索了一下后立马…

导包不对如何解决

问题: 这里这个包是自动导入的我们并不需要这个时候导致下面代码报错如何解决 2.解答:比如下面这给词爆红我们需要alt+enter,IDEA 会显示一个选择框,允许您选择 okhttp3.Request。选中后,它将使用您指定的正确包。,这里就是我是重新导入了maven依赖就好了

一图看懂云消息队列 RabbitMQ 版对比开源优势

随着企业对消息队列的性能和稳定性要求越来越高,运维成本也随之增加。 云消息队列 RabbitMQ 版通过架构优化:避免了消息积压导致的内存泄漏和服务器故障等稳定性问题; 解决了分布式系统中的脑裂难题; 并支持弹性伸缩和按量计费,有效降低资源和运维成本!那么,与开源 Rabb…

GIS工具哪家强?五款优质GIS工具箱对比分析

本文将为大家介绍五款功能各异的GIS工具箱,包括GISBox、QGIS、MapTiler、Saga GIS和Whitebox GAT。每款工具箱都有其独特的功能和应用场景,能够满足不同类型的GIS任务需求。无论是数据处理、空间分析、影像处理还是可视化需求,这些工具都能为用户提供丰富的解决方案。本文将…

安装Kibana__基于Windows系统

在此之前,请先安装Elasticsearch并启动一、下载安装包并解压安装https://www.elastic.co/downloads/kibanaterminal进入Kibana安装目录,通过.\bin\kibana.bat指令启动 二、配置后登录Kibana页面 启动成功,访问http://localhost:5601/,页面会要求你填入EnrollmentToken(此…

【Maya 2025软件下载与安装教程 含补丁】

1、安装包 「maya2025」: 链接:https://pan.quark.cn/s/de0d9d452470 提取码:Rhjp 「maya2024」: 链接:https://pan.quark.cn/s/887e52b68f51 提取码:jvyp 「maya2023」: 链接:https://pan.quark.cn/s/71f46b3d26e5 提取码:b6mA 「maya2020」: 链接:https://pan.qua…

Prometheus + Grafana 监控平台搭建

1、下载 prometheus和node_exporter:https://prometheus.io/download/ 下载完后上传到服务器解压 tar -zxvf prometheus-3.0.0-rc.1.linux-amd64.tar.gztar -zxvf node_exporter-1.8.2.linux-amd64.tar.gz2、启动 node_exporter nohup ./node_exporter --web.listen-address…

阿里云可观测 2024 年 10 月产品动态

阿里云可观测 2024 年 10 月产品动态

题解:CF2025E Card Game

在这 权当卡特兰数的复习题吧。不会卡特兰数的可以先看文末。如果没有花色 \(1\) 这道题就很简单了,对于每个别的花色都有 \(C(m)\) 种分配方案。\(C(n)\) 表示卡特兰数的第 \(n\) 项,答案就是乘起来。 发现除了花色 \(1\) 每种花色的牌都是独立的。这启示我们枚举每种牌用了…

应用网关的演进历程和分类

网关作为互联网流量的入口,其形态也在跟随软件架构持续演进迭代中。我们下面就聊一聊网关的演进历程以及在时下火热的 AI 浪潮下,网关又会迸发怎样新的形态。作者:耿蕾蕾(如葑) 唯一不变的是变化,在现代复杂的商业环境中,企业的业务形态与规模往往处于不断变化和扩大之中…

Vue网站发布到iis后提示404页面不可访问

vue重定向和跨域配置:https://zhuanlan.zhihu.com/p/5306882511.安装组件:URL Rewrite:https://www.iis.net/downloads/microsoft/url-rewriteApplication Request Routing:https://www.iis.net/downloads/microsoft/application-request-routing2.新建一个web.config 放到…

fastadmin 数据记录行上添加操作按钮并设置权限

1. 一键 curd 以及配置菜单 编写控制器方法 - 业务逻辑 再次一键生成菜单 - 生成刚刚写审核通过方法的控制器。 2. 自定义控制器中方法。 3. 查看角色组的权限,并授予该角色权限。 4. 前端修改 index 页面,因为需要权限所以需要加上一句话data-operate-log="{:$auth-&g…