2026/3/30 1:34:49
网站建设
项目流程
基础建设期刊在哪个网站可以查,电商前期投资要多少钱,榆林网站制作,搜索引擎优化策略应该包括基本借鉴来自#xff1a;
https://blog.csdn.net/qq_33954661/article/details/151179820
鉴于网上很多文章都写得不清不楚或者就是动不动就收费#xff0c;这很恶心#xff0c;就这么点移植步骤还要神神秘秘的#xff0c;有辱斯文#xff0c;有的阅读让读者很不舒服https://blog.csdn.net/qq_33954661/article/details/151179820鉴于网上很多文章都写得不清不楚或者就是动不动就收费这很恶心就这么点移植步骤还要神神秘秘的有辱斯文有的阅读让读者很不舒服本人经过反复推敲看了很多文章之后再根据自己的理解移植了freemodbus如果在移植过程中有什么问题欢迎留言评论大家一起进步共勉。如果移植过程中哪里有问题没提到的可以评论区留言看到我会回复 并修改这个文章一、需要准备的东西1、stm32F407IGT6开发板本移植是基于HAL库的只要是stm32平台应该都适用2、freemodbus-v1.6 软件包网上有很多这个可以搜索就能下载3、modbus调试工具 modbus Poll4、开发工具 MDK5,以及STM32CubeMX。如果是标准库的道友可以看正点原子的论坛里面有一个前辈已经移植好并且成功了我也是参阅他的文章做的部分移植链接在这里http://www.openedv.com/forum.php?modviewthreadtid297921highlightfreemodbus二、开发步骤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 woltisil.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.146msT35 应为 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 woltisil.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 woltisil.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 woltisil.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