单片机
返回首页

stm32裸机移植FreeModbus

2022-07-14 来源:csdn

简单记录一下步骤。


官方下载freemodbus-v1.6.zip源码,然后把源码中的modbus文件夹、demoBAREport文件夹导入工程。


先是一波无脑导,把文件夹里所有文件导入。


存储区配置,单片机上定义的起始地址要比实际通信过程中读写的地址+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

 *

 * 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

 *

 * 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移植过程基本就完成了。

进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

最新器件
精选电路图
  • 基于IC555的可变PWM振荡器电路

  • 优化电路板布局的简单方法

  • 如何使用LED驱动器LM3915制作振动计

  • 分享一个电网倾角计电路

  • 电谐波图形均衡器示意图

  • 一种构建12V和230V双直流电源的简单方法

    相关电子头条文章