先参考了一波人家的移植过程,但是细节地方写的不是很详细。
参考链接
这里有个pc端的工具使用教程。
Modbus测试工具ModbusPoll与Modbus Slave使用方法
于是自己简单记录一下步骤。
官方下载freemodbus-v1.6.zip源码,然后把源码中的modbus文件夹、demo\BARE\port文件夹导入工程。
先是一波无脑导,把文件夹里所有文件导入。
存储区配置,单片机上定义的起始地址要比实际通信过程中读写的地址+1。
#include "mb.h"
#include "mbport.h"
#include "mbutils.h"
//输入寄存器
#define REG_INPUT_START 1001
#define REG_INPUT_NREGS 2
static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS];
//保持寄存器
#define REG_HOLDING_START 2001
#define REG_HOLDING_NREGS 1
static USHORT usRegHoldingStart = REG_HOLDING_START;
static USHORT usRegHoldingBuf[REG_HOLDING_NREGS];
//输出线圈点
#define REG_COILS_START 3001
#define REG_COILS_SIZE 4
static USHORT usRegCoilsStart = REG_COILS_START;
static UCHAR ucRegCoilsBuf[REG_COILS_SIZE/8+(REG_COILS_SIZE%8?1:0)];
//离散输入点
#define REG_DISCRETE_START 4001
#define REG_DISCRETE_SIZE 8
static USHORT usRegDiscreteStart = REG_DISCRETE_START;
static UCHAR ucRegDiscreteBuf[REG_DISCRETE_SIZE/8+(REG_DISCRETE_SIZE%8?1:0)];
主程序就是标准的运行三连+IO读写。
eStatus = eMBInit( MB_RTU, addr, 0, 9600, MB_PAR_EVEN );
/* Enable the Modbus Protocol Stack. */
eStatus = eMBEnable( );
for( ;; )
{
( void )eMBPoll( );
usRegInputBuf[0]=Get_Adc_Average(8,10);
usRegInputBuf[1]=Get_Adc_Average(9,10);
ucRegDiscreteBuf[0]=(GPIOB->IDR&0xFFFFFF00)>>8;
GPIOB->ODR=GPIOB->ODR&0xFFFFFF0F;
GPIOB->ODR=GPIOB->ODR|ucRegCoilsBuf[0]<<4;
}
支持的4种读写function扔在主函数后面就行了。
/**
* @brief 输入寄存器处理函数,输入寄存器可读,但不可写。
* @param pucRegBuffer 返回数据指针
* usAddress 寄存器起始地址
* usNRegs 寄存器长度
* @retval eStatus 寄存器状态
*/
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
eMBErrorCode eStatus = MB_ENOERR;
int16_t iRegIndex;
//查询是否在寄存器范围内
//为了避免警告,修改为有符号整数
if( ( (int16_t)usAddress >= REG_INPUT_START ) \
&& ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
{
//获得操作偏移量,本次操作起始地址-输入寄存器的初始地址
iRegIndex = ( int16_t )( usAddress - REG_INPUT_START );
//逐个赋值
while( usNRegs > 0 )
{
//赋值高字节
*pucRegBuffer++ = ( uint8_t )( usRegInputBuf[iRegIndex] >> 8 );
//赋值低字节
*pucRegBuffer++ = ( uint8_t )( usRegInputBuf[iRegIndex] & 0xFF );
//偏移量增加
iRegIndex++;
//被操作寄存器数量递减
usNRegs--;
}
}
else
{
//返回错误状态,无寄存器
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* @brief 保持寄存器处理函数,保持寄存器可读,可读可写
* @param pucRegBuffer 读操作时--返回数据指针,写操作时--输入数据指针
* usAddress 寄存器起始地址
* usNRegs 寄存器长度
* eMode 操作方式,读或者写
* @retval eStatus 寄存器状态
*/
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
eMBRegisterMode eMode )
{
//错误状态
eMBErrorCode eStatus = MB_ENOERR;
//偏移量
int16_t iRegIndex;
//判断寄存器是不是在范围内
if( ( (int16_t)usAddress >= REG_HOLDING_START ) \
&& ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )
{
//计算偏移量
iRegIndex = ( int16_t )( usAddress - REG_HOLDING_START );
switch ( eMode )
{
//读处理函数
case MB_REG_READ:
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] >> 8 );
*pucRegBuffer++ = ( uint8_t )( usRegHoldingBuf[iRegIndex] & 0xFF );
iRegIndex++;
usNRegs--;
}
break;
//写处理函数
case MB_REG_WRITE:
while( usNRegs > 0 )
{
usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
break;
}
}
else
{
//返回错误状态
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* @brief 线圈寄存器处理函数,线圈寄存器可读,可读可写
* @param pucRegBuffer 读操作---返回数据指针,写操作--返回数据指针
* usAddress 寄存器起始地址
* usNRegs 寄存器长度
* eMode 操作方式,读或者写
* @retval 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;
usBitOffset += 8;
}
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* @brief 开关输入寄存器处理函数,开关输入寄存器,可读
* @param pucRegBuffer 读操作---返回数据指针,写操作--返回数据指针
* usAddress 寄存器起始地址
* usNRegs 寄存器长度
* eMode 操作方式,读或者写
* @retval 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;
}
portserial.c就是和stm32相关的硬件实现接口,初始化那里可以直接调用RS485_Init(9600);
/*
* 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"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "rs485.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)
{
//使能接收和接收中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
//MAX485操作 低电平为接收模式
//GPIO_ResetBits(GPIOD,GPIO_Pin_8);
RS485_TX_EN=0;
}
else
{
USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
//MAX485操作 高电平为发送模式
//GPIO_SetBits(GPIOD,GPIO_Pin_8);
RS485_TX_EN=1;
}
if(xTxEnable)
{
//使能发送完成中断
USART_ITConfig(USART2, USART_IT_TC, ENABLE);
}
else
{
//禁止发送完成中断
USART_ITConfig(USART2, USART_IT_TC, DISABLE);
}
}
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
RS485_Init(9600); //初始化RS485
return TRUE;
//return 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. */
//发送数据
USART_SendData(USART2, 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 = USART_ReceiveData(USART2);
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( );
}
void USART2_IRQHandler(void)
{
if(USART_GetITStatus(USART2, USART_IT_RXNE) == SET)
{
prvvUARTRxISR();
//清除中断标志位
USART_ClearITPendingBit(USART2, USART_IT_RXNE);
}
//发生完成中断
if(USART_GetITStatus(USART2, USART_IT_TC) == SET)
{
prvvUARTTxReadyISR();
//清除中断标志
USART_ClearITPendingBit(USART2, USART_IT_TC);
}
}
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"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
#include "timer.h"
/* ----------------------- static functions ---------------------------------*/
static void prvvTIMERExpiredISR( void );
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
uint16_t PrescalerValue = 0;
PrescalerValue = (uint16_t) (SystemCoreClock / 20000) - 1;
TIM3_Int_Init(usTim1Timerout50us,PrescalerValue);
return TRUE;
return FALSE;
}
//inline void
void vMBPortTimersEnable( )
{
/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
//设定定时器4的初始值
TIM_SetCounter(TIM3,0x0000);
//定时器4启动
TIM_Cmd(TIM3, ENABLE);
}
//inline void
void vMBPortTimersDisable( )
{
/* Disable any pending timers. */
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
TIM_ITConfig(TIM3, TIM_IT_Update, DISABLE);
TIM_SetCounter(TIM3,0x0000);
//关闭定时器3
TIM_Cmd(TIM3, DISABLE);
}
/* 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 TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
//清除定时器T3溢出中断标志位
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
prvvTIMERExpiredISR( );
}
}
modbus移植过程基本就完成了。