跳至正文
首页 » FreeMODBUS 在 STM32 上的移植(标准外设STD库)

FreeMODBUS 在 STM32 上的移植(标准外设STD库)

Modbus是一种串行通信协议,最早应用于PLC通信,现已经成为工业领域通信协议的业界标准。Freemodbus是针对嵌入式应用的通用Modbus协议移植。本文将介绍使用标准外设STD库实现 FreeMODBUS 在 STM32 上的移植。

导入项目

从 FreeMODBUS 官方网站下载库文件。在 demo > BARE 中导入下图中的文件及头文件至项目中。其中 modbus.c为新建文件,用于存储示例程序中 main.c 的内容。

项目结构 FreeMODBUS 在 STM32 上的移植

项目配置

须在 Options for target > Target > Code Generation 中勾选 Use MicroLIB 选项。否则程序可能会出现卡死,汇编卡在

0x8xxxxxxx  BEAB BKPT 0xAB

在 Options for target > C/C++ > Preprocessor Symbols > Define 中增加 NDEBUG,注意各项间用 , 连接。否则编译过程会报错

.\Objects\project.axf: Error: L6218E: Undefined symbol __aeabi_assert (referred from mbrtu.o).

串口配置

串口配置相关函数在 portserial.c 中。vMBPortSerialEnablexMBPortSerialInitxMBPortSerialPutBytexMBPortSerialGetByte 分别是在串口进行使能、初始化、发送字节、接受字节的调用函数,在相关行为触发时调用。我们需要根据实际情况加入配置程序。

点击查看源码
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */
		// enable rx interrupt
		if(xRxEnable == TRUE)	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
		else									USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
		
		// enable tx interrupt
		if(xTxEnable == TRUE)	USART_ITConfig(USART1, USART_IT_TC, ENABLE);
		else									USART_ITConfig(USART1, USART_IT_TC, DISABLE);
}

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
		usart1_init((int)ulBaudRate);
	
    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. */
	
		USART_SendData(USART1, 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(USART1);
	
    return TRUE;
}

上面程序中除了串口初始化中调用的是自己的函数,其他的几个函数调用的都是库函数,可以直接使用。

定时器配置

定时器相关函数位于 porttimer.c 文件中,结构和串口配置函数类似。不同的是定时器必须配置中断。

点击查看源码
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
		timer2_init(usTim1Timerout50us);
	
    return TRUE;
}


inline void
vMBPortTimersEnable(  )
{
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
		TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
		TIM_SetCounter(TIM2, 0x0000);
		TIM_Cmd(TIM2, ENABLE);
}

inline void
vMBPortTimersDisable(  )
{
    /* Disable any pending timers. */
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
		TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
		TIM_SetCounter(TIM2, 0x0000);
		TIM_Cmd(TIM2, 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.
 */
void prvvTIMERExpiredISR( void )
{
    ( void )pxMBPortCBTimerExpired(  );
}

同样,除了初始化函数外,都直接调用的库函数,代码可直接使用。

此外,在定时器中断还需要调用 prvvTIMERExpiredISR

点击查看源码
// modbus timer
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
	{
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
		
		prvvTIMERExpiredISR();
	}
}

寄存器配置

寄存器配置函数在官方例程中是直接写在 main.c 中,我们可以根据自己的情况放置。我专门新建了一个文件 modbus.c ,将寄存器配置相关函数放置其中。

点击查看源码
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
#include "modbus.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- Defines ------------------------------------------*/
#define REG_INPUT_START   1000
#define REG_INPUT_NREGS   4
#define REG_HOLDING_START 1000
#define REG_HOLDING_NREGS 130

/* ----------------------- Static variables ---------------------------------*/
static USHORT   usRegInputStart = REG_INPUT_START;
static USHORT   usRegInputBuf[REG_INPUT_NREGS];
static USHORT   usRegHoldingStart = REG_HOLDING_START;
static USHORT   usRegHoldingBuf[REG_HOLDING_NREGS];

/* ----------------------- Start implementation -----------------------------*/
void modbus_init( void )
{

		/* Initialize Protocol Stack. */
		eMBInit( MB_RTU, 0x0A, 0, 9600, MB_PAR_NONE );
		eMBEnable( );
	
		return;
}

void modbus_run( void )
{
		( void )eMBPoll(  );
		
		return;
}

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 )
        {
            /* Pass current register values to the protocol stack. */
        case MB_REG_READ:
            while( usNRegs > 0 )
            {
                *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 );
                *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF );
                iRegIndex++;
                usNRegs--;
            }
            break;

            /* Update current register values with new values from the
             * protocol stack. */
        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;
}

modbus_init 是FreeMODBUS 相关初始化程序;modbus_run 为运行函数,在程序空闲时调用;

排故

程序可能会出现卡死,汇编码停在 0x8xxxxxxx BEAB BKPT 0xAB

须在 Options for target > Target > Code Generation 中勾选 Use MicroLIB 选项。

编译报错 Undefined symbol __aeabi_assert

完整错误信息

.\Objects\project.axf: Error: L6218E: Undefined symbol __aeabi_assert (referred from mbrtu.o).

在 Options for target > C/C++ > Preprocessor Symbols > Define 中增加 NDEBUG,注意各项间用 , 连接。

仅回复一包数据,之后不再响应

在 mbrtu.c 中 eMBRTUSend 函数

/* Activate the transmitter. */
eSndState = STATE_TX_XMIT;

之后添加


xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
pucSndBufferCur++;  
usSndBufferCount--;

参考

发表评论

您的电子邮箱地址不会被公开。