通辽市网站建设_网站建设公司_漏洞修复_seo优化
2026/1/9 20:28:44 网站建设 项目流程

基本借鉴来自:

https://blog.csdn.net/qq_33954661/article/details/151179820

鉴于网上很多文章都写得不清不楚或者就是动不动就收费,这很恶心,就这么点移植步骤还要神神秘秘的,有辱斯文,有的阅读让读者很不舒服,本人经过反复推敲,看了很多文章之后,再根据自己的理解移植了freemodbus,如果在移植过程中有什么问题欢迎留言评论,大家一起进步,共勉。(如果移植过程中哪里有问题,没提到的可以评论区留言,看到我会回复 并修改这个文章)

一、需要准备的东西

1、stm32F407IGT6开发板(本移植是基于HAL库的,只要是stm32平台应该都适用)
2、freemodbus-v1.6 软件包(网上有很多这个可以搜索就能下载)
3、modbus调试工具 modbus Poll
4、开发工具 MDK5,以及STM32CubeMX。

如果是标准库的道友可以看正点原子的论坛,里面有一个前辈已经移植好并且成功了,我也是参阅他的文章做的部分移植链接在这里http://www.openedv.com/forum.php?mod=viewthread&tid=297921&highlight=freemodbus

二、开发步骤

1.使用stm32cubeMX生成代码
步骤如下

1、配置时钟源

2、配置烧录引脚

3、配置定时器2

该定时器用于接收modbusRTU的3.5个字节判断是否超时,目标:每个计数周期为 50μs,以匹配 FreeModbus 的 50μs 单位超时机制。

记得使能中断,多亏网友的提示,忘记需要使能中断,不然会导致modbus通讯超时

4、主频时钟配置

5、串口配置,这里使用的是串口3 用其他串口也可以,在移植freemodbus的时候修改底层配置函数即可。

6、生成代码,记得勾上生成头文件那,主要是方便查看代码以及添加头文件等

7、将下载好的软件包放到工程中,方便移植


8、在core文件夹中新建modbus文件夹


9在modbus文件夹中分别将freemodbus中用到的文件添加进去 不建议全部加进去,因为不美观显得工程很杂乱,而且在工程添加的时候容易出错。



最终像这样在这里插入图片描述
10、打开MDK工程将之前放到目录下的文件统统添加进去(这里不多讲怎么添加,最终结果如下图)

添加PATH

至此,文件移植完毕=======================================================================
现在开始修改底层文件

1、主要最关键的四个文件是

我们一个一个的来修改
首先是 portevent.c文件中(几乎不需要怎么修改)

/* * FreeModbus Libary: BARE Port * Copyright (C) 2006 Christian Walter <wolti@sil.at> * * 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$ */ /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- Variables ----------------------------------------*/ static eMBEventType eQueuedEvent; static BOOL xEventInQueue; /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortEventInit( void ) { xEventInQueue = FALSE; return TRUE; } BOOL xMBPortEventPost( eMBEventType eEvent ) { xEventInQueue = TRUE; eQueuedEvent = eEvent; return TRUE; } BOOL xMBPortEventGet( eMBEventType * eEvent ) { BOOL xEventHappened = FALSE; if( xEventInQueue ) { *eEvent = eQueuedEvent; xEventInQueue = FALSE; xEventHappened = TRUE; } return xEventHappened; }

重点来了 修改port serial.c文件的修改 (网上看到的大多是标准库的移植,有的函数名字不一样,用的时候要注意)

特别是发送和接收函数,不能用HAL库自带的收发函数(想认真看的就看一下不看就跳过)

在 FreeModbus 中,ucRTUBuf 是协议栈用于缓存接收帧的缓冲区,正常情况下应存储一整帧的完整数据(从地址字节到 CRC 校验字节)。如果每次只能收到最后一个字节,通常是由于帧接收逻辑没有正确累积字节,导致前序字节被覆盖或未被正常存储。具体原因可从以下几个方面排查: 1. 接收中断处理不完整,仅最后一个字节触发了协议栈缓存 FreeModbus 依赖每接收一个字节就触发一次 pxMBFrameCBByteReceived 回调,并通过 xMBPortSerialGetByte 读取字节后存入 ucRTUBuf。如果前序字节的中断未被正确处理,会导致只有最后一个字节被缓存。 可能原因: 串口接收中断被意外禁用:例如在中断处理中,未正确重新使能接收中断(如 __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE)),导致仅第一个字节触发中断,后续字节无法触发,直到最后一个字节 “侥幸” 触发中断。 中断优先级过低:其他高优先级中断占用过多时间,导致前序字节的中断被忽略,仅最后一个字节的中断被响应。 排查方法: 在 pxMBFrameCBByteReceived 回调中添加打印(或通过调试器观察),确认是否每个字节都触发了该回调。若只有最后一个字节触发,则说明中断处理存在漏触发问题。 2. ucRTUBuf 缓冲区索引未正确递增,前序字节被覆盖 FreeModbus 内部通过一个索引变量(如 ucRTUBufPos)记录当前接收字节在 ucRTUBuf 中的位置,每接收一个字节,索引递增一次。如果索引未正确递增(或被意外重置),会导致新字节始终覆盖 ucRTUBuf 的第一个位置,最终只保留最后一个字节。 可能原因: 协议栈状态机异常:例如在接收过程中,由于帧格式错误(如 CRC 校验失败),协议栈提前重置了 ucRTUBufPos 索引,导致后续字节从起始位置重新存储,仅最后一个字节被保留。 自定义代码干扰:若在用户代码中意外修改了 ucRTUBufPos 或 ucRTUBuf 的地址,会破坏缓冲区的累积逻辑。 排查方法: 通过调试器跟踪 ucRTUBufPos 的值,观察每接收一个字节后是否递增(正常应从 0→1→2…)。若索引始终为 0 或被重置,则说明状态机或索引被异常干扰。 3. T35 超时定时器配置错误,帧提前结束 Modbus RTU 通过T35 超时(3.5 个字符时间)判断一帧结束。如果定时器超时时间过短,会导致协议栈在接收完整帧前就判定 “帧结束”,并重置接收缓冲区,此时若后续还有字节到达,会被当作新帧的第一个字节,最终只保留最后一个字节。 可能原因: 定时器超时时间计算错误:例如波特率为 9600 时,1 个字符时间为 11 位(1 位起始 + 8 位数据 + 1 位停止 + 1 位校验),即 11/9600 ≈ 1.146ms,T35 应为 3.5×1.146≈4.01ms。若配置为 1ms,会导致提前超时。 定时器未正确重启:每接收一个字节后,协议栈需重启 T35 定时器(确保超时从 “最后一个字节接收完成” 开始计算)。若未重启,会导致中间字节触发超时,提前结束帧。 排查方法: 检查 xMBPortTimersInit 中定时器的超时时间是否正确(根据波特率计算),并确认 pxMBFrameCBByteReceived 回调中是否触发了定时器重启(FreeModbus 默认会在接收字节后重启定时器)。 4. xMBPortSerialGetByte 函数读取错误,仅最后一个字节被正确获取 若 xMBPortSerialGetByte 函数读取字节的时机错误(如未及时读取导致数据被覆盖),会导致前序字节无效,仅最后一个字节被正确存入缓冲区。 可能原因: 读取寄存器延迟:xMBPortSerialGetByte 未在 pxMBFrameCBByteReceived 触发后立即读取 DR 寄存器,导致新字节覆盖了 DR 中的旧数据(UART 接收新字节时会自动覆盖 DR)。 函数实现错误:例如仍在使用 HAL_UART_Receive 等阻塞函数,导致前序字节读取超时,仅最后一个字节被成功读取。 排查方法: 在 xMBPortSerialGetByte 中添加打印(或调试观察),确认每次调用是否正确读取到当前字节(而非 0xFF 或固定值)。若前序字节为无效值,说明读取逻辑存在问题。 总结排查步骤 确认每个接收字节都触发了 pxMBFrameCBByteReceived 回调(中断未漏触发); 跟踪 ucRTUBufPos 索引,确认其随字节接收递增(未被重置); 验证 T35 定时器超时时间是否正确(符合当前波特率的 3.5 个字符时间); 检查 xMBPortSerialGetByte 函数是否每次都正确读取 DR 寄存器中的当前字节。 通过以上步骤,通常能定位到 “仅最后一个字节被缓存” 的具体原因,核心是确保每个字节都被正确接收、读取并按顺序存入 ucRTUBuf,且帧结束判断(T35 超时)符合协议规范。
/* * FreeModbus Libary: BARE Port * Copyright (C) 2006 Christian Walter <wolti@sil.at> * * 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$ */ #include "port.h" //uesr include start #include "main.h" #include "usart.h" //uesr include end /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- static functions ---------------------------------*/ //static void prvvUARTTxReadyISR( void ); //static void prvvUARTRxISR( void ); /* ----------------------- Start implementation -----------------------------*/ void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) { /* If xRXEnable enable serial receive interrupts. If xTxENable enable * transmitter empty interrupts. */ if(xRxEnable)//接收使能 { // 使能RXNE(接收非空)中断 __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE); } else//接收失能 { // 禁用RXNE中断 __HAL_UART_DISABLE_IT(&huart3, UART_IT_RXNE); } if(xTxEnable)//发送使能 { // 使能TXE(接收非空)中断 __HAL_UART_ENABLE_IT(&huart3, UART_IT_TXE); } else { // 禁用TXE中断 __HAL_UART_DISABLE_IT(&huart3, UART_IT_TXE); } } BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { (void)ucPORT; //不修改串口号 (void)ucDataBits; //不修改数据位长度 (void)eParity; //不修改检验格式 huart3.Instance = USART3; huart3.Init.BaudRate = ulBaudRate; huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.StopBits = UART_STOPBITS_1; huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Mode = UART_MODE_TX_RX; huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart3.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart3) != HAL_OK) { Error_Handler(); } return TRUE; } BOOL xMBPortSerialPutByte( CHAR ucByte ) { /* Put a byte in the UARTs transmit buffer. This function is called * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been * called. */ /* 等待发送缓冲区为空(TXE标志置位) */ while (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) == RESET) { // 超时处理(可选,避免死等) // 若长时间未置位,可返回FALSE表示发送失败 } /* 直接写入发送寄存器,触发发送 */ huart3.Instance->DR = (uint8_t)ucByte; return TRUE; } BOOL xMBPortSerialGetByte( CHAR *pucByte ) { /* Return the byte in the UARTs receive buffer. This function is called * by the protocol stack after pxMBFrameCBByteReceived( ) has been called. */ *pucByte = (CHAR)(huart3.Instance->DR & 0x00FF); // HAL_UART_Receive(&huart3,(uint8_t *)pucByte,1,100); 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. */ static void prvvUARTTxReadyISR( void ) { pxMBFrameCBTransmitterEmpty( ); } /* 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. */ static void prvvUARTRxISR( void ) { pxMBFrameCBByteReceived( ); }

porttimer.c文件的修改

/* * FreeModbus Libary: BARE Port * Copyright (C) 2006 Christian Walter <wolti@sil.at> * * 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$ */ /* ----------------------- Platform includes --------------------------------*/ #include "port.h" #include "tim.h" #include "main.h" /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- static functions ---------------------------------*/ static void prvvTIMERExpiredISR( void ); /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { /* USER CODE BEGIN TIM2_Init 0 */ /* USER CODE END TIM2_Init 0 */ TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; /* USER CODE BEGIN TIM2_Init 1 */ /* USER CODE END TIM2_Init 1 */ htim2.Instance = TIM2; htim2.Init.Prescaler = 8400-1; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = usTim1Timerout50us-1; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } // 清除TIM3的溢出(更新)中断标志位 __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); // 关闭TIM3的溢出(更新)中断(失能中断) __HAL_TIM_DISABLE_IT(&htim2, TIM_IT_UPDATE); // // 禁用TIM3(停止定时器计数) // HAL_TIM_Base_Stop(&htim2); return TRUE; } inline void vMBPortTimersEnable( ) { /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ // /* 假设已定义定时器句柄 htim2(对应 TIM2),需在外部声明 */ // extern TIM_HandleTypeDef htim2; /* 清除定时器更新中断标志位(对应 TIM_ClearITPendingBit) */ __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); /* 使能定时器更新中断(对应 TIM_ITConfig) */ __HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE); /* 重置定时器计数器值为 0(对应 TIM_SetCounter) */ __HAL_TIM_SET_COUNTER(&htim2, 0x0000); /* 启动定时器(对应 TIM_Cmd(ENABLE)) */ HAL_TIM_Base_Start(&htim2); } inline void vMBPortTimersDisable( ) { /* Disable any pending timers. */ /* 清除定时器更新中断标志位(对应 TIM_ClearITPendingBit) */ __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); /* 使能定时器更新中断(对应 TIM_ITConfig) */ __HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE); /* 重置定时器计数器值为 0(对应 TIM_SetCounter) */ __HAL_TIM_SET_COUNTER(&htim2, 0x0000); /* 停止定时器(对应 TIM_Cmd(ENABLE)) */ HAL_TIM_Base_Stop(&htim2); } /* Create an ISR which is called whenever the timer has expired. This function * must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that * the timer has expired. */ static void prvvTIMERExpiredISR( void ) { ( void )pxMBPortCBTimerExpired( ); } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { // 确认是目标定时器(TIM2)的更新中断 if (htim == &htim2) { // 清除TIM2的溢出(更新)中断标志位 __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); prvvTIMERExpiredISR(); } }

mb.c文件不需要修改

port.c文件的修改 具体算法有兴趣的可以自行研究

#include "mb.h" /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" #include "main.h" #include "port.h" #include "mbutils.h" /* ----------------------- Defines ------------------------------------------*/ /* ----------------------- Defines ------------------------------------------*/ #define REG_INPUT_START 0x0001 #define REG_INPUT_NREGS 50 #define REG_HOLDING_START 0x0000 #define REG_HOLDING_NREGS 50 #define REG_COILS_START 0x0000 #define REG_COILS_SIZE 50 #define REG_DISCRETE_START 0x0000 #define REG_DISCRETE_SIZE 50 //寄存器数量 /* ----------------------- Static variables ---------------------------------*/ uint16_t usRegInputBuf[REG_INPUT_NREGS] = {0x8001,0x7002,0x6003,0x5004,0x4005,0x3006,0x2007,0x1008}; uint16_t usRegHoldingBuf[REG_HOLDING_NREGS]={0x0810,0x0720,0x0630,0x0540,0x0450,0x0360,0x0270,0x0180}; uint8_t ucRegCoilsBuf[REG_COILS_SIZE]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}; uint8_t ucRegDiscreteBuf[REG_DISCRETE_SIZE]={0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08}; /* ----------------------- Static variables ---------------------------------*/ /* ----------------------- Variables ----------------------------------------*/ int VIC_Temp; /* ----------------------- Start implementation -----------------------------*/ void EnterCriticalSection( ) { __ASM volatile("cpsid i"); } void ExitCriticalSection( ) { __ASM volatile("cpsie i"); } eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) { eMBErrorCode eStatus = MB_ENOERR; int iRegIndex; if( ( usAddress >= REG_INPUT_START ) \ && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) ) { iRegIndex = ( int )( usAddress - REG_INPUT_START ); while( usNRegs > 0 ) { *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 ); *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF ); iRegIndex++; usNRegs--; } } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) { eMBErrorCode eStatus = MB_ENOERR; int iRegIndex; // u16 *PRT=(u16*)pucRegBuffer; if( ( (int16_t)usAddress >= REG_HOLDING_START ) && ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) ) { iRegIndex = ( int )( usAddress - REG_HOLDING_START ); switch ( eMode ) { case MB_REG_READ: while( usNRegs > 0 ) { // *PRT++ = __REV16(usRegHoldingBuf[iRegIndex++]); //数据序转 REV16.W *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 ); *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF ); iRegIndex++; usNRegs--; } break; case MB_REG_WRITE: while( usNRegs > 0 ) { // usRegHoldingBuf[iRegIndex++] = __REV16(*PRT++); //数据序转 REV16.W usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8; usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++; iRegIndex++; usNRegs--; } break; } } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) { eMBErrorCode eStatus = MB_ENOERR; //寄存器个数 int16_t iNCoils = ( int16_t )usNCoils; //寄存器偏移量 int16_t usBitOffset; //检查寄存器是否在指定范围内 if( ( (int16_t)usAddress >= REG_COILS_START ) && ( usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE ) ) { //计算寄存器偏移量 usBitOffset = ( int16_t )( usAddress - REG_COILS_START ); switch ( eMode ) { //读操作 case MB_REG_READ: while( iNCoils > 0 ) { *pucRegBuffer++ = xMBUtilGetBits( ucRegCoilsBuf, usBitOffset, ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ) ); iNCoils -= 8; usBitOffset += 8; } break; //写操作 case MB_REG_WRITE: while( iNCoils > 0 ) { xMBUtilSetBits( ucRegCoilsBuf, usBitOffset, ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ), *pucRegBuffer++ ); iNCoils -= 8; } break; } } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) { eMBErrorCode eStatus = MB_ENOERR; //操作寄存器个数 int16_t iNDiscrete = ( int16_t )usNDiscrete; //偏移量 uint16_t usBitOffset; //判断寄存器时候再制定范围内 if( ( (int16_t)usAddress >= REG_DISCRETE_START ) && ( usAddress + usNDiscrete <= REG_DISCRETE_START + REG_DISCRETE_SIZE ) ) { //获得偏移量 usBitOffset = ( uint16_t )( usAddress - REG_DISCRETE_START ); while( iNDiscrete > 0 ) { *pucRegBuffer++ = xMBUtilGetBits( ucRegDiscreteBuf, usBitOffset, ( uint8_t)( iNDiscrete > 8 ? 8 : iNDiscrete ) ); iNDiscrete -= 8; usBitOffset += 8; } } else { eStatus = MB_ENOREG; } return eStatus; } /*freemodbus作者使用了assert,故增加如下代码,同时 *options for target 对话框,target页面,勾选Use MicroLib */ #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t* file, uint32_t line) { /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* Infinite loop */ while (1) { } } #else void __aeabi_assert(const char * x1, const char * x2, int x3) { (void)x3; } #endif

新建port.h如果没有的话

/* * FreeModbus Libary: BARE Port * Copyright (C) 2006 Christian Walter <wolti@sil.at> * * 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$ */ #ifndef _PORT_H #define _PORT_H #include <assert.h> #include <inttypes.h> #define INLINE inline #define PR_BEGIN_EXTERN_C extern "C" { #define PR_END_EXTERN_C } #define ENTER_CRITICAL_SECTION( ) EnterCriticalSection() #define EXIT_CRITICAL_SECTION( ) ExitCriticalSection() //#define assert (expr) typedef uint8_t BOOL; typedef unsigned char UCHAR; typedef char CHAR; typedef uint16_t USHORT; typedef int16_t SHORT; typedef uint32_t ULONG; typedef int32_t LONG; void EnterCriticalSection(void); void ExitCriticalSection(void); #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #endif

串口中断函数的修改 在stm32f4xx_it.c文件中

/** * @brief This function handles USART3 global interrupt. */ void USART3_IRQHandler(void) { /* USER CODE BEGIN USART3_IRQn 0 */ // 在HAL库处理前,优先处理Modbus需要的标志位 if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_RXNE) != RESET && __HAL_UART_GET_IT_SOURCE(&huart3, UART_IT_RXNE) != RESET) { pxMBFrameCBByteReceived(); // 调用Modbus接收回调 __HAL_UART_CLEAR_FLAG(&huart3, UART_FLAG_RXNE); // 清除标志位 tx_cnt = (tx_cnt + 1) % 1000; // 计数(确保原子操作,简单场景可忽略) } if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_TXE) != RESET && __HAL_UART_GET_IT_SOURCE(&huart3, UART_IT_TXE) != RESET) { pxMBFrameCBTransmitterEmpty(); // 调用Modbus发送回调 __HAL_UART_CLEAR_FLAG(&huart3, UART_FLAG_TXE); // 清除标志位 rx_cnt = (rx_cnt + 1) % 1000; // 计数 } /* USER CODE END USART3_IRQn 0 */ HAL_UART_IRQHandler(&huart3); /* USER CODE BEGIN USART3_IRQn 1 */ /* USER CODE END USART3_IRQn 1 */ }

至此所有步骤移植,完成,
然后main.c中编写如下代码即可开始测试

/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "mb.h" #include "mbport.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART3_UART_Init(); MX_TIM4_Init(); MX_TIM2_Init(); /* USER CODE BEGIN 2 */ // HAL_TIM_Base_Start_IT(&htim4); eMBInit( MB_RTU, 0x0A, 0, 9600, MB_PAR_NONE ); eMBEnable( ); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ HAL_GPIO_TogglePin(RUN_LED_GPIO_Port,RUN_LED_Pin); HAL_Delay(150); ( void )eMBPoll( );//不断查询数据帧 } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 168; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 4; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */

打开在线仿真,以及modbus调试工具
测试结果如下图所示

modbus调试软件正常无报错,且数据同步正常

基本借鉴来自:

https://blog.csdn.net/qq_33954661/article/details/151179820

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询