freemodbus移植
基于freemodbus1.6
使用HAL库
软件:stm32cubemx stm32cubeide
后续会更新标准库的移植。以及rtos下的移植(尽量)
下载freemodbus1.6
这个获取方法网上到处都是,不细说了。
cubemx新建工程
新建工程只列出了与移植freemodbus相关的设置
这里我使用的是485通信,所以额外使能了一个引脚

使能一个定时器,这里我用的是tim2。并且开始定时器2中断

其他设置如下图,参数其实设什么无所谓,因为后面要改的,我们并不用系统的初始化函数。

然后使能一个串口,我这里用的串口1,参数其实设什么无所谓,因为后面要改的,

这里可以把串口1和定时器2的最前面的取消勾选,就不会生成他们的初始化函数,不勾也没有太大关系,因为我们的函数在他之后,会覆盖掉系统的设置。

另外在中断优先级设置中,将串口优先级设置高于定时器2,数字越小越高。

相关的中断处理函数也要生成。

然后就可以generate code!生成代码。
代码修改
首先我们在我们项目的根目录中新建一个freemodbus文件夹,文件夹中再建一个modbus文件夹,一个port文件夹。

把你最开始下载下来的freemodbus中modbus文件夹中的内容复制到你刚才的modbus文件夹中,

把你最开始下载下来的freemodbus中demo/bare路径下的内容全部复制到你刚才的port文件夹中

然后我们进入cubeide,右键项目->属性,配置头文件和源文件路径。

把如图六个头文件路径添加。

把如图最下面两个源文件路径添加。

先在port.h文件中补充这两个宏定义,这是HAL库的全局中断开启、关闭函数。

ok,然后我们修改portserail.c
这两个函数前面的static标志去掉。

vMBPortSerialEnable函数修改如下- void
- vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
- {
- /* If xRXEnable enable serial receive interrupts. If xTxENable enable
- * transmitter empty interrupts.
- */
- if (xRxEnable) //将串口收发中断和modbus联系起来,下面的串口改为自己使能的串口
- {
- __HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE); //我用的是串口2,故为&huart2
- HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_RESET);//
- }
- else
- {
- __HAL_UART_DISABLE_IT(&huart2,UART_IT_RXNE);
- HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_SET);//
- }
- if (xTxEnable)
- {
- HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_SET);//
- __HAL_UART_ENABLE_IT(&huart2,UART_IT_TXE);
- }//
- else
- {
- HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_RESET);//
- __HAL_UART_DISABLE_IT(&huart2,UART_IT_TXE);
- }
- }
复制代码 串口初始化函数如下- BOOL
- xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
- {
- huart2.Instance = USART2;
- huart2.Init.BaudRate = ulBaudRate;
- huart2.Init.StopBits = UART_STOPBITS_1;
- huart2.Init.Mode = UART_MODE_TX_RX;
- huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
- huart2.Init.OverSampling = UART_OVERSAMPLING_16;
- switch(eParity)
- {
- // 奇校验
- case MB_PAR_ODD:
- huart2.Init.Parity = UART_PARITY_ODD;
- huart2.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
- break;
- // 偶校验
- case MB_PAR_EVEN:
- huart2.Init.Parity = UART_PARITY_EVEN;
- huart2.Init.WordLength = UART_WORDLENGTH_9B; // 带奇偶校验数据位为9bits
- break;
- // 无校验
- default:
- huart2.Init.Parity = UART_PARITY_NONE;
- huart2.Init.WordLength = UART_WORDLENGTH_8B; // 无奇偶校验数据位为8bits
- break;
- }
- return HAL_UART_Init(&huart2) == HAL_OK ? TRUE : FALSE;
- }
复制代码 收发字节函数如下- 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. */
- HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_SET);//
- if(HAL_UART_Transmit (&huart2 ,(uint8_t *)&ucByte,1,10) != HAL_OK )
- return FALSE ;//HAL_UART_Transmit最后一位形参为最大发送时间,
- //超出改时间退出发送,可能导致485发送失败,可稍微长一点。
- else
- 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.
- */
- HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_RESET);
- if(HAL_UART_Receive (&huart2,(uint8_t *)pucByte,1,10) != HAL_OK )
- return FALSE ;
- else
- return TRUE;
- }
复制代码 然后我们修改porttimer.c
首先依旧去掉这个函数前的static标志,方便之后调用,函数声明和函数实体前的static都要去掉

然后修改这几个函数:
定时器初始化函数- BOOL
- xMBPortTimersInit( USHORT usTim1Timerout50us )
- {
- TIM_ClockConfigTypeDef sClockSourceConfig = {0};
- TIM_MasterConfigTypeDef sMasterConfig = {0};
- htim2.Instance = TIM2;
- htim2.Init.Prescaler = 3599; // 50us记一次数
- htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
- htim2.Init.Period = usTim1Timerout50us-1; // usTim1Timerout50us * 50即为定时器溢出时间
- htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
- htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
- if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
- {
- return FALSE;
- }
- sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
- if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
- {
- return FALSE;
- }
- sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
- sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
- if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
- {
- return FALSE;
- }
- return TRUE;
- }
复制代码 然后是定时器开启、关闭、中断服务函数- inline void
- vMBPortTimersEnable( )
- {
- /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
- __HAL_TIM_CLEAR_IT(&htim2,TIM_IT_UPDATE);//避免程序一上电就进入定时器中断
- __HAL_TIM_ENABLE_IT(&htim2,TIM_IT_UPDATE);
- __HAL_TIM_SET_COUNTER(&htim2, 0); // 清空计数器
- __HAL_TIM_ENABLE(&htim2); // 使能定时器
- }
- inline void
- vMBPortTimersDisable( )
- {
- /* Disable any pending timers. */
- __HAL_TIM_DISABLE(&htim2); // 禁能定时器
- __HAL_TIM_SET_COUNTER(&htim2,0);
- __HAL_TIM_DISABLE_IT(&htim2,TIM_IT_UPDATE);
- __HAL_TIM_CLEAR_IT(&htim2,TIM_IT_UPDATE);
- }
- /* 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.
- */
- void prvvTIMERExpiredISR( void )
- {
- ( void )pxMBPortCBTimerExpired( );
- }
复制代码 最后还有两处修改,有的教程中并没有提到这两处修改,应该是与vMBPortSerialEnable中使用USART_IT_TC还是USART_IT_TXE中断标志有关,如果使用USART_IT_TC中断的话需要添加这两处修改,就我目前使用USART_IT_TXE中断标志的情况下,加上这两处修改也并无问题,待后续研究明白了再更新- //启动第一次发送,进入发送完成中断
- xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
- pucSndBufferCur++; /* next byte in sendbuffer. */
- usSndBufferCount--;
- //添加代码end
复制代码- //插入代码begin
- if(eStatus==MB_ENOERR)
- {
- xMBRTUTransmitFSM(); //发送一帧数据中第一个字节出发发送完成中断
- }
- //插入代码end
复制代码

在系统的中断处理.c中添加以下的函数声明,有些教程是自己写的中断处理,这里我们还是用系统自己的。

定时器中断处理函数:- void TIM2_IRQHandler(void)
- {
- /* USER CODE BEGIN TIM2_IRQn 0 */
- HAL_NVIC_ClearPendingIRQ(TIM2_IRQn);
- /* USER CODE END TIM2_IRQn 0 */
- HAL_TIM_IRQHandler(&htim2);
- /* USER CODE BEGIN TIM2_IRQn 1 */
- /* USER CODE END TIM2_IRQn 1 */
- }
复制代码 串口中断函数:- void USART2_IRQHandler(void)
- {
- /* USER CODE BEGIN USART2_IRQn 0 */
- if(__HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_RXNE)!= RESET)
- {
- prvvUARTRxISR();//接收中断
- }
- if(__HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_TXE)!= RESET)
- {
- prvvUARTTxReadyISR();//发送中断
- }
- HAL_NVIC_ClearPendingIRQ(USART2_IRQn);
- /* USER CODE END USART2_IRQn 0 */
- HAL_UART_IRQHandler(&huart2);
- /* USER CODE BEGIN USART2_IRQn 1 */
- /* USER CODE END USART2_IRQn 1 */
- }
复制代码 在文档末尾user code 代码段添加定时器中断回调函数:- /* USER CODE BEGIN 1 */
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
- {
- /* NOTE : This function Should not be modified, when the callback is needed,
- the __HAL_TIM_PeriodElapsedCallback could be implemented in the user file
- */
- if(htim->Instance == TIM2)
- {
- prvvTIMERExpiredISR( );
- }
- }
- /* USER CODE END 1 */
复制代码 回到main函数

首先包含几个头文件
在Private define段添加如下:- /* Private define ------------------------------------------------------------*/
- /* USER CODE BEGIN PD */
- //输入寄存器起始地址
- #define REG_INPUT_START 0x0001
- //输入寄存器数量
- #define REG_INPUT_NREGS 8
- //保持寄存器起始地址
- #define REG_HOLDING_START 0x0001
- //保持寄存器数量
- #define REG_HOLDING_NREGS 8
- //线圈起始地址
- #define REG_COILS_START 0x0001
- //线圈数量
- #define REG_COILS_SIZE 16
- //离散寄存器起始地址
- #define REG_DISCRETE_START 0x0001
- //离散寄存器数量
- #define REG_DISCRETE_SIZE 16
- /* USER CODE END PD */
复制代码 Private variables段添加如下:- /* Private variables ---------------------------------------------------------*/
- /* USER CODE BEGIN PV */
- //输入寄存器内容
- uint16_t usRegInputBuf[REG_INPUT_NREGS] = {0x1000,0x1001,0x1002,0x1003,0x1004,0x1005,0x1006,0x1007};
- //输入寄存器起始地址
- uint16_t usRegInputStart = REG_INPUT_START;
- //保持寄存器内容
- uint32_t usRegHoldingBuf[REG_HOLDING_NREGS] = {0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e};
- //保持寄存器起始地址
- uint16_t usRegHoldingStart = REG_HOLDING_START;
- //线圈状态
- uint8_t ucRegCoilsBuf[REG_COILS_SIZE / 8] = {0x01,0x02};
- //离散输入状态
- uint8_t usRegDiscreteBuf[REG_DISCRETE_SIZE / 8] = {0x01,0x02};
- uint8_t testfalg=0;
- extern unsigned char NUM [];
- /* USER CODE END PV */
复制代码 Private function prototypes段添加如下:- /* USER CODE BEGIN PFP */
- /**
- * @Brief : 读输入寄存器处理函数,功能码04
- * @param pucRegBuffer 保存输入寄存器值的缓存
- * @param usAddress 寄存器地址
- * @param usNRegs 读取个数
- * @return?eMBErrorCode?
- */
- 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 - usRegInputStart );
- while( usNRegs > 0 )
- {
- *pucRegBuffer++ = ( UCHAR )( usRegInputBuf[iRegIndex] >> 8 );
- *pucRegBuffer++ = ( UCHAR )( usRegInputBuf[iRegIndex] & 0xFF );
- iRegIndex++;
- usNRegs--;
- }
- }
- else
- {
- eStatus = MB_ENOREG;
- }
- return eStatus;
- }
- /**
- * @Brief : 读保持寄存器处理函数,功能码03
- * @param pucRegBuffer
- * @param usAddress
- * @param usNRegs
- * @param eMode
- * @return?eMBErrorCode?
- */
- eMBErrorCode
- eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
- {
- eMBErrorCode eStatus = MB_ENOERR;
- int iRegIndex;
- if((usAddress >= REG_HOLDING_START)&&\
- ((usAddress+usNRegs) <= (REG_HOLDING_START + REG_HOLDING_NREGS)))
- {
- iRegIndex = (int)(usAddress - usRegHoldingStart);
- switch(eMode)
- {
- case MB_REG_READ://�??? MB_REG_READ = 0
- while(usNRegs > 0)
- {
- *pucRegBuffer++ = (uint8_t)(usRegHoldingBuf[iRegIndex] >> 8);
- *pucRegBuffer++ = (uint8_t)(usRegHoldingBuf[iRegIndex] & 0xFF);
- iRegIndex++;
- usNRegs--;
- }
- break;
- case MB_REG_WRITE://�??? MB_REG_WRITE = 1
- while(usNRegs > 0)
- {
- usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
- usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
- iRegIndex++;
- usNRegs--;
- }
- }
- }
- else//错误
- {
- eStatus = MB_ENOREG;
- }
- return eStatus;
- }
- /**
- *****************************************************************************
- * @Name : 操作线圈
- *
- * @Brief : 对应功能�???0x01 -> eMBFuncReadCoils
- * 0x05 -> eMBFuncWriteCoil
- * 0x15 -> 写多个线�??? eMBFuncWriteMultipleCoils
- *
- * @Input : *pucRegBuffer:数据缓冲区,响应主机用
- * usAddress: 寄存器地�???
- * usNRegs: 操作寄存器个�???
- * eMode: 功能�???
- *
- * @Output : none
- *
- * @Return : Modbus状�?�信�???
- *****************************************************************************
- **/
- eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
- {
- eMBErrorCode eStatus = MB_ENOERR;
- int iNCoils = ( int )usNCoils;
- unsigned short usBitOffset;
- /* Check if we have registers mapped at this block. */
- if( ( usAddress >= REG_COILS_START ) && ( usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE ) )
- {
- usBitOffset = ( unsigned short )( usAddress - REG_COILS_START );
- switch ( eMode )
- {
- /* Read current values and pass to protocol stack. */
- case MB_REG_READ:
- while( iNCoils > 0 )
- {
- *pucRegBuffer++ = xMBUtilGetBits( ucRegCoilsBuf, usBitOffset, ( unsigned char )( iNCoils > 8 ? 8 : iNCoils ) );
- iNCoils -= 8;
- usBitOffset += 8;
- }
- break;
- /* Update current register values. */
- case MB_REG_WRITE:
- while( iNCoils > 0 )
- {
- xMBUtilSetBits( ucRegCoilsBuf, usBitOffset, ( unsigned char )( iNCoils > 8 ? 8 : iNCoils ), *pucRegBuffer++ );
- iNCoils -= 8;
- usBitOffset += 8;
- }
- break;
- }
- }
- else
- {
- eStatus = MB_ENOREG;
- }
- return eStatus;
- }
- /**
- *****************************************************************************
- * @Name : 操作离散寄存�???
- *
- * @Brief : 对应功能�???0x02 -> eMBFuncReadDiscreteInputs
- *
- * @Input : *pucRegBuffer:数据缓冲区,响应主机用
- * usAddress: 寄存器地�???
- * usNRegs: 操作寄存器个�???
- *
- * @Output : none
- *
- * @Return : Modbus状�?�信�???
- *****************************************************************************
- **/
- //eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
- //{
- // pucRegBuffer = pucRegBuffer;
- // return MB_ENOREG;
- //}
- eMBErrorCode
- eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
- {
- eMBErrorCode eStatus = MB_ENOERR;
- short iNDiscrete = ( short )usNDiscrete;
- USHORT usBitOffset;
- /* Check if we have registers mapped at this block. */
- if( ( usAddress >= REG_DISCRETE_START ) && ( usAddress + usNDiscrete <= REG_DISCRETE_START + REG_DISCRETE_SIZE ) )
- {
- usBitOffset = ( USHORT )( usAddress - REG_DISCRETE_START );
- while( iNDiscrete > 0 )
- {
- *pucRegBuffer++ =
- xMBUtilGetBits( usRegDiscreteBuf, usBitOffset,( UCHAR )( iNDiscrete > 8 ? 8 : iNDiscrete ) );
- iNDiscrete -= 8;
- usBitOffset += 8;
- }
- }
- else
- {
- eStatus = MB_ENOREG;
- }
- return eStatus;
- }
- /* USER CODE END PFP */
复制代码 新建变量estatus 前后说的这些添加内容必须夹在user code字段中,否则cubemx修改工程重新生成代码后,你修改的内容会消失

初始化、使能

因为前面在串口初始化中,串口选择是被我写死的,不能通过这里的第三个形参去选择使用串口几,但是通过简单修改可以实现自由配置
最后在main函数中启动轮询就可以了

最后你把想传出去的数据放在各个寄存器中,外部modbus主机就可以查询到你放入寄存器的数据了
- /* Private variables ---------------------------------------------------------*/
- /* USER CODE BEGIN PV */
- //输入寄存器内容
- uint16_t usRegInputBuf[REG_INPUT_NREGS] = {0x1000,0x1001,0x1002,0x1003,0x1004,0x1005,0x1006,0x1007};
- //输入寄存器起始地址
- uint16_t usRegInputStart = REG_INPUT_START;
- //保持寄存器内容
- uint32_t usRegHoldingBuf[REG_HOLDING_NREGS] = {0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e};
- //保持寄存器起始地址
- uint16_t usRegHoldingStart = REG_HOLDING_START;
- //线圈状态
- uint8_t ucRegCoilsBuf[REG_COILS_SIZE / 8] = {0x01,0x02};
- //离散输入状态
- uint8_t usRegDiscreteBuf[REG_DISCRETE_SIZE / 8] = {0x01,0x02};
- uint8_t testfalg=0;
- extern unsigned char NUM [];
- /* USER CODE END PV */
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |