前言
FreeMODBUS是专门针对嵌入式系统实现MODBUS的免费协议。这里简单分享一下stm32移植FreeMODBUS。
一、下载地址
官网:https://www.embedded-experts.at/en/freemodbus-downloads/
二、步骤
1.移植
解压压缩包后,将modbus文件夹复制到keil工程里。
下面是我工程下的文件夹。
打开demo文件夹,再打开BARE文件夹,将port文件夹和demo原文件复制到keil工程里。
打开用STM32CubeMX生成的工程,将刚刚的文件夹里的源文件导入到工程,头文件添加路径。
移植完成,接下来就是修改接口函数。
2.修改
因为移植裸机的freemodbus,所以只需修改port文件夹的portserial.c和porttimer.c。
下面是我的portserial.c,对应的是stm32的串口。我的串口初始化不在这个portserial.c文件里,直接在STM32CubeMX里的main.c生成好了,所以xMBPortSerialInit这个函数里啥都没写。
以下代码只需将宏定义的MODBUS_UART的串口修改,我这里是串口2。
/*
* 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"
#include "usart.h"
#include "stm32f2xx_hal.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#define MODBUS_UART huart2
/* ----------------------- static functions ---------------------------------*/
void prvvUARTTxReadyISR( void );
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 == TRUE) {
__HAL_UART_ENABLE_IT(&MODBUS_UART, UART_IT_RXNE);
} else {
__HAL_UART_DISABLE_IT(&MODBUS_UART, UART_IT_RXNE);
}
if(xTxEnable == TRUE) {
__HAL_UART_ENABLE_IT(&MODBUS_UART, UART_IT_TC);
} else {
__HAL_UART_DISABLE_IT(&MODBUS_UART, UART_IT_TC);
}
}
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
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. */
HAL_UART_Transmit_IT(&MODBUS_UART, (uint8_t *)&ucByte, 1);
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.
*/
static uint8_t temp = 0;
HAL_UART_Receive_IT(&MODBUS_UART, &temp, 1);
*pucByte = temp;
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 )
{
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.
*/
void prvvUARTRxISR( void )
{
pxMBFrameCBByteReceived( );
}
然后将prvvUARTTxReadyISR和prvvUARTRxISR两个函数放入回调函数中。
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
if(huart->Instance == USART2){
prvvUARTTxReadyISR();
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart->Instance == USART2){
prvvUARTRxISR();
}
}
下面是我的porttimer.c,对应的是stm32的定时器。只需将宏定义的MODBUS_TIM的定时器和初始化修改,我这里是定时器6。注意,定时器的分频和装载值要根据不同频率修改,我这里是60MHz。
/*
* 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"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#define MODBUS_TIM htim6
/* ----------------------- static functions ---------------------------------*/
void prvvTIMERExpiredISR( void );
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
/* USER CODE BEGIN TIM5_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
MODBUS_TIM.Instance = TIM6;
MODBUS_TIM.Init.Prescaler = 3000 - 1;//60MHz/20kHz=3000
MODBUS_TIM.Init.CounterMode = TIM_COUNTERMODE_UP;
MODBUS_TIM.Init.Period = usTim1Timerout50us - 1;//wbPeriod;
MODBUS_TIM.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&MODBUS_TIM) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&MODBUS_TIM, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
return TRUE;
}
inline void
vMBPortTimersEnable( )
{
/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
__HAL_TIM_SET_COUNTER(&MODBUS_TIM, 0);
HAL_TIM_Base_Start_IT(&MODBUS_TIM);
}
inline void
vMBPortTimersDisable( )
{
/* Disable any pending timers. */
HAL_TIM_Base_Stop_IT(&MODBUS_TIM);
}
/* 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( );
}
同理,将函数prvvTIMERExpiredISR放入定时器的回调函数中。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM6) {
prvvTIMERExpiredISR();
}
}
打开demo.c。将里面的main函数注释。
/*
* FreeModbus Libary: BARE Demo Application
* Copyright (C) 2006 Christian Walter <wolti@sil.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; 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"
/* ----------------------- Defines ------------------------------------------*/
#define REG_INPUT_START 1000
#define REG_INPUT_NREGS 4
#define REG_HOLDING_START ( 0x0700 )
#define REG_HOLDING_NREGS ( 20 )
/* ----------------------- Static variables ---------------------------------*/
static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS] = {0x1000, 0x1001, 0x1002, 0x1003};
static USHORT usRegHoldingStart = REG_HOLDING_START;
static USHORT usRegHoldingBuf[REG_HOLDING_NREGS] = {0};
/* ----------------------- Start implementation -----------------------------*/
//int
//main( void )
//{
// eMBErrorCode eStatus;
// eStatus = eMBInit( MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN );
// /* Enable the Modbus Protocol Stack. */
// eStatus = eMBEnable( );
// for( ;; )
// {
// ( void )eMBPoll( );
// /* Here we simply count the number of poll cycles. */
// usRegInputBuf[0]++;
// }
//}
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++ =
( 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;
if( ( usAddress >= REG_HOLDING_START ) && ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )
{
iRegIndex = ( int )( usAddress - usRegHoldingStart );
switch ( eMode )
{
case MB_REG_READ:
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 );
*pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF );
iRegIndex++;
usNRegs--;
}
break;
case MB_REG_WRITE:
while( usNRegs > 0 )
{
usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
eMBRegisterMode eMode )
{
return MB_ENOREG;
}
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
return MB_ENOREG;
}
如果是FREERTOS,则将上面注释掉的内容写入对应的任务里,别忘了加osDelay。裸机的话则放进STM32生成的main.c里,这里就不将代码展示出来了。eMBInit的波特率要对应,不然定时器的间隔时间会有问题。
至此,FreeMODBUS移植完毕,大家可以使用MODBUS调试助手来测试。485地址注意要和初始化eMBInit的地址对应哦。
总结
以上就是今天要讲的内容,本文简单介绍了FreeMODBUS的移植,如有不对的地方,欢迎大家指正。