打印
[其他ST产品]

stm32裸机移植FreeModbus

[复制链接]
1403|34
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
略略u|  楼主 | 2023-7-26 17:01 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

官方下载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)];



使用特权

评论回复
沙发
略略u|  楼主 | 2023-7-26 17:01 | 只看该作者
主程序就是标准的运行三连+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;
    }

使用特权

评论回复
板凳
略略u|  楼主 | 2023-7-26 17:02 | 只看该作者
支持的4种读写function扔在主函数后面就行了。
/**
* [url=home.php?mod=space&uid=247401]@brief[/url] 输入寄存器处理函数,输入寄存器可读,但不可写。
* @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;
}


使用特权

评论回复
地板
略略u|  楼主 | 2023-7-26 17:02 | 只看该作者
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);
    }

}

使用特权

评论回复
5
略略u|  楼主 | 2023-7-26 17:02 | 只看该作者
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( );
  }
}

使用特权

评论回复
6
略略u|  楼主 | 2023-7-26 17:02 | 只看该作者
modbus移植过程基本就完成了。

使用特权

评论回复
7
earlmax| | 2023-9-9 17:05 | 只看该作者
有谁做过modbus/TCP 在 stm32 上的移植

使用特权

评论回复
8
phoenixwhite| | 2023-9-9 19:11 | 只看该作者
对于较小的STM32微控制器,内存可能是有限的资源。因此,在移植FreeModbus时,需要关注内存使用和分配,以确保合理利用可用的内存空间。

使用特权

评论回复
9
rosemoore| | 2023-9-9 22:25 | 只看该作者
FreeModbus需要使用串口进行通信。在移植前,需要确认STM32的串口配置是否与FreeModbus的要求相匹配。包括串口波特率、数据位、停止位和校验位等。

使用特权

评论回复
10
averyleigh| | 2023-9-9 22:38 | 只看该作者
Modbus通信通常使用UART或者其他串口通信方式,所以需要正确配置中断服务例程以处理接收和发送数据。确保在接收到数据时触发UART接收中断,并在发送数据完成时触发相应的中断。

使用特权

评论回复
11
Henryko| | 2023-9-10 23:03 | 只看该作者
移植后还剩余多少资源啊

使用特权

评论回复
12
1988020566| | 2023-9-12 15:42 | 只看该作者
STM32裸机移植FreeModbus?

使用特权

评论回复
13
lzmm| | 2023-9-12 15:53 | 只看该作者
需要修改port.c和portserial.c两个文件。

使用特权

评论回复
14
belindagraham| | 2023-9-12 16:01 | 只看该作者
配置正确的串口 用于与Modbus设备进行通信。

使用特权

评论回复
15
mnynt121| | 2023-9-12 16:36 | 只看该作者
根据Modbus协议解析接收到的数据,并根据需要进行相应的处理和响应。

使用特权

评论回复
16
dspmana| | 2023-9-12 17:12 | 只看该作者
根据具体的应用需求,对FreeModbus进行性能优化,例如调整定时器中断频率、优化数据传输等。

使用特权

评论回复
17
beacherblack| | 2023-9-12 17:20 | 只看该作者
尽量用标准的C写代码               

使用特权

评论回复
18
i1mcu| | 2023-9-12 17:59 | 只看该作者
FreeModbus有多个版本,如FreeModbus v3.0、FreeModbus v4.0等,需要根据实际需求选择合适的版本。

使用特权

评论回复
19
jonas222| | 2023-9-12 19:54 | 只看该作者
需要对FreeModbus的源码进行修改,以适应STM32的架构。

使用特权

评论回复
20
iyoum| | 2023-9-12 20:57 | 只看该作者
stm32freemodbus从站地址怎么设置

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

78

主题

603

帖子

0

粉丝