首页 > 其他分享 >FreeModbus学习——eMBInit初始化

FreeModbus学习——eMBInit初始化

时间:2024-07-24 18:25:12浏览次数:13  
标签:初始化 eStatus MB UCHAR void eMBInit RTU FreeModbus

FreeModbus版本:1.6
在mb.c文件中
先看一下静态变量的定义

/* ----------------------- Static variables ---------------------------------*/

static UCHAR    ucMBAddress;
static eMBMode  eMBCurrentMode;

ucMBAddress是从机地址,eMBCurrentMode是Modbus 的工作模式,如下:

    typedef enum
{
    MB_RTU,                     /*!< RTU transmission mode. */
    MB_ASCII,                   /*!< ASCII transmission mode. */
    MB_TCP                      /*!< TCP mode. */
} eMBMode;

我这里只使用RTU模式,在include/mbconfig.h文件中有一些宏定义,可以配置打开,我这里只打开了RTU使能。
在这里插入图片描述
eMBState 代表Modbus的工作状态,定义后赋值为STATE_NOT_INITIALIZED未初始化状态。

static enum
{
    STATE_ENABLED,
    STATE_DISABLED,
    STATE_NOT_INITIALIZED
} eMBState = STATE_NOT_INITIALIZED;

静态变量定义下面还有一些静态函数指针变量

/* Functions pointer which are initialized in eMBInit( ). Depending on the
 * mode (RTU or ASCII) the are set to the correct implementations.
 */
static peMBFrameSend peMBFrameSendCur;
static pvMBFrameStart pvMBFrameStartCur;
static pvMBFrameStop pvMBFrameStopCur;
static peMBFrameReceive peMBFrameReceiveCur;
static pvMBFrameClose pvMBFrameCloseCur;

简单点讲,以static peMBFrameSend peMBFrameSendCur;为例,可以理解为定义了一个变量peMBFrameSendCur,而它的类型是peMBFrameSend 。
那么它是什么呢,是函数指针类型Functions pointer。
也就是说peMBFrameSendCur可以指向一个函数,可以把一个函数赋值给它(参数和返回值要保持一致)。

函数原型定义在mbframe.h文件中

/* ----------------------- Prototypes  0-------------------------------------*/
typedef void    ( *pvMBFrameStart ) ( void );

typedef void    ( *pvMBFrameStop ) ( void );

typedef eMBErrorCode( *peMBFrameReceive ) ( UCHAR * pucRcvAddress,
                                            UCHAR ** pucFrame,
                                            USHORT * pusLength );

typedef eMBErrorCode( *peMBFrameSend ) ( UCHAR slaveAddress,
                                         const UCHAR * pucFrame,
                                         USHORT usLength );

typedef void( *pvMBFrameClose ) ( void );

在mb.c文件中还定义了几个回调函数指针,这里只用到了前三个,分别是字节接收,发送空,定时器溢出回调函数。
刚开始时我一直不理解这个名字pxMBFrameCBByteReceived 该怎么个意思。
MB是Modbus,Frame是帧,CB是回调函数CallBack。

/* Callback functions required by the porting layer. They are called when
 * an external event has happend which includes a timeout or the reception
 * or transmission of a character.
 */
BOOL( *pxMBFrameCBByteReceived ) ( void );
BOOL( *pxMBFrameCBTransmitterEmpty ) ( void );
BOOL( *pxMBPortCBTimerExpired ) ( void );

BOOL( *pxMBFrameCBReceiveFSMCur ) ( void );
BOOL( *pxMBFrameCBTransmitFSMCur ) ( void );

下面看一下本篇主要学习的eMBInit,Modbus初始化,这里我只保留了RTU模式,其它两种模式都删除了。

/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
    eMBErrorCode    eStatus = MB_ENOERR;

    /* check preconditions */
    if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||
        ( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) )
    {
        eStatus = MB_EINVAL;
    }
    else
    {
        ucMBAddress = ucSlaveAddress;

        switch ( eMode )
        {
#if MB_RTU_ENABLED > 0
        case MB_RTU:
            pvMBFrameStartCur = eMBRTUStart;
            pvMBFrameStopCur = eMBRTUStop;
            peMBFrameSendCur = eMBRTUSend;
            peMBFrameReceiveCur = eMBRTUReceive;
            pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;
            pxMBFrameCBByteReceived = xMBRTUReceiveFSM;
            pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM;
            pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;

            eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );
            break;
#endif
        default:
            eStatus = MB_EINVAL;
        }

        if( eStatus == MB_ENOERR )
        {
            if( !xMBPortEventInit(  ) )
            {
                /* port dependent event module initalization failed. */
                eStatus = MB_EPORTERR;
            }
            else
            {
                eMBCurrentMode = eMode;
                eMBState = STATE_DISABLED;
            }
        }
    }
    return eStatus;
}

先看一下参数
eMBMode eMode, 工作模式,这里选择RTU
UCHAR ucSlaveAddress, 从机地址,UCHAR 类型,即unsigned char类型
UCHAR ucPort, 端口(这个不重要)
ULONG ulBaudRate, 波特率
eMBParity eParity,校验位:奇校验,偶校验,无校验

返回值为错误码,,错误码被协议栈的所有函数使用,这里初始化为无错误

/*! \ingroup modbus
 * \brief Errorcodes used by all function in the protocol stack.
 */
typedef enum
{
    MB_ENOERR,                  /*!< no error. */
    MB_ENOREG,                  /*!< illegal register address. */
    MB_EINVAL,                  /*!< illegal argument. */
    MB_EPORTERR,                /*!< porting layer error. */
    MB_ENORES,                  /*!< insufficient resources. */
    MB_EIO,                     /*!< I/O error. */
    MB_EILLSTATE,               /*!< protocol stack in illegal state. */
    MB_ETIMEDOUT                /*!< timeout error occurred. */
} eMBErrorCode;

先检查传进来的从机地址是否符合要求,
① 不是广播地址0
② 满足在1到147之间

满足要求,则将这个从机地址赋值给我们的静态变量ucMBAddress,从此我们的从机地址就确定了。
在这里插入图片描述

然后将前面提到的函数指针都给赋值(不赋值都为空),这些函数都定义在mbrtc.c文件中
在这里插入图片描述

然后调用函数eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );进行RTU初始化。

/* ----------------------- Start implementation -----------------------------*/
eMBErrorCode
eMBRTUInit( UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    ULONG           usTimerT35_50us;

    ( void )ucSlaveAddress;
    ENTER_CRITICAL_SECTION(  );

    /* Modbus RTU uses 8 Databits. */
    if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE )
    {
        eStatus = MB_EPORTERR;
    }
    else
    {
        /* If baudrate > 19200 then we should use the fixed timer values
         * t35 = 1750us. Otherwise t35 must be 3.5 times the character time.
         */
        if( ulBaudRate > 19200 )
        {
            usTimerT35_50us = 35;       /* 1800us. */
        }
        else
        {
            /* The timer reload value for a character is given by:
             *
             * ChTimeValue = Ticks_per_1s / ( Baudrate / 11 )
             *             = 11 * Ticks_per_1s / Baudrate
             *             = 220000 / Baudrate
             * The reload for t3.5 is 1.5 times this value and similary
             * for t3.5.
             */
            usTimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
        }
        if( xMBPortTimersInit( ( USHORT ) usTimerT35_50us ) != TRUE )
        {
            eStatus = MB_EPORTERR;
        }
    }
    EXIT_CRITICAL_SECTION(  );

    return eStatus;
}

在调用xMBPortSerialInit进行串口初始化,这个函数在portserial.c文件中,这个函数是留给用户自己实现的,就是初始化函数,实际上串口初始化放在外面自己初始化也可以,反正只要初始化了就行。比如使用cubeMX生成的MX_USART2_UART_Init();

下一步是初始化定时器。
因为T3.5字符原则嘛,就是两帧之间间隔3.5个字符的时间长度,字符时间长度当然跟波特率有关系啦。
If baudrate > 19200 时给固定时间间隔 t35 = 1750us.
当If baudrate ≤ 19200us时TimerT35_50us = ( 7UL * 220000UL ) / ( 2UL * ulBaudRate );
这个公式怎么来的呢
令波特率为Baud ,传输一位要的时间为: bitTime = 1000 / Baud ms
传输1个字符,要传11位: charTime = 11 * bitTime = 11 * ( 1000 / Baud ) ms
那么传输 3.5 字符: T3.5 = 3.5 * charTime = 3.5 * 11 * ( 1000 / Baud ) ms
换成整数:T3.5 = ( 7 * 11 * 10^6 ) / ( 2 * Baud ) us
50us的个数:Period = T3.5 / 50 = ( 7 * 220000 ) / ( 2 * Baud )

定时器初始化,我这里使用的是STM32H743,TIM 240MHz,
所以设置Prescaler = 11999;,这样一个时基就是12000/240000000 = 1/20000 = 1/20ms = 1000/20 us = 50us。
Period = usTim1Timerout50us - 1;
所以自动重装载值设为 50us 的个数即可。

BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{

    TIM_MasterConfigTypeDef sMasterConfig = {0};

    htim7.Instance = TIM7;
    htim7.Init.Prescaler = 11999;
    htim7.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim7.Init.Period = usTim1Timerout50us - 1;
    htim7.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if (HAL_TIM_Base_Init(&htim7) != HAL_OK)
    {
        return FALSE;
    }
    sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
    sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
    if (HAL_TIMEx_MasterConfigSynchronization(&htim7, &sMasterConfig) != HAL_OK)
    {
        return FALSE;
    }
    return TRUE;
}

我们继续回到eMBInit函数
eMBRTUInit初始化完成之后,调用xMBPortEventInit初始化,然后把当前模式赋值为RTU,协议栈状态由未初始化变未使能。
在这里插入图片描述

eStatus为本函数内状态,其值为返回的错误码。
eMBState为协议栈状态,其值为:未初始化,未使能,使能。
二者不要搞混。

好了初始化结束。
流程:
eMBInit →
赋值从机地址 →
赋值函数指针 →
eMBRTUInit(→xMBPortSerialInit →xMBPortTimersInit→) →
xMBPortEventInit →
eMBState = STATE_DISABLED;

标签:初始化,eStatus,MB,UCHAR,void,eMBInit,RTU,FreeModbus
From: https://blog.csdn.net/rerrick_rose/article/details/140636867

相关文章

  • FreeModbus学习——eMBPoll轮询
    FreeModbus版本:1.6eMBPoll在mb.c文件中eMBPoll函数是一个核心的Modbus协议栈事件处理函数,负责接收和发送帧,处理不同的事件,并根据需要返回错误码。eMBErrorCodeeMBPoll(void){staticUCHAR*ucMBFrame;//接收到的帧数据staticUCHARucRc......
  • Python ctypes OSError:[WinError 1114]动态链接库(DLL)初始化例程失败
    我试图使用Python中的ctypes库调用C++函数:test.pyfromctypesimport*fromrandomimportrandinttester=cdll.LoadLibrary('./test.dll')print(tester.test(randint(1,100)))test.cpp#include<vector>intcppTest(intnum){std:......
  • 从“typing.Tuple”派生时如何初始化基本“tuple”?
    根据文档,“内置泛型类型..作为类型和基类都是有效的。”它给出了一个源自Dict[str,List[Node]]的示例。但是,当我使用Tuple[int,int]尝试此操作时,构造函数无法正确转发。例如:fromtypingimportTupleclassMyTuple(Tuple[int,int]):......
  • linux内核源码阅读-初始化主程序
     来自:https://in1t.top/2020/03/26/linux%E5%86%85%E6%A0%B8%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB-%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%BB%E7%A8%8B%E5%BA%8F/main.c功能描述之前setup在0x90000~0x901FF保存了一些重要的机器参数,其中包括主内存区的开始地址,内存大小和......
  • 如何使用未初始化的属性正确注释数据类?
    给出以下代码:fromtypingimportSelffromdataclassesimportdataclass,field@dataclassclassMyClass:var:float=field(init=False)def__post_init__(self:Self)->None:self.var=True我的期望是该行self.var=True应该产......
  • 初始化 pytorch RPC 时 getBar1SizeOfGpu 出现运行时错误
    我在系统上使用PyTorch和RPC后端时遇到运行时错误。错误信息如下:谁能告诉我为什么会出现这个问题以及如何解决它?谢谢。Traceback(mostrecentcalllast):File"/work/personal_workspace/torchrpc_test.py",line20,in<module>rpc.init_rpc(name=f"worker_{......
  • Python:定期检测断开故障的USB设备并重新初始化实例
    我有一个USB设备,有时会通过USB端口发送串行数据。问题是设备出现故障,有时会无缘无故地断开连接并再次连接到电脑。问题不大,但在这些情况下我需要重新初始化serial.Serial(port)实例,这有点烦人。该设备没有可以从我那里收到的任何命令,我可以验证它是否已连接。我可以......
  • 01-初始化引导程序
    原理说明Bios会加载第一扇区到内存中,只有512字节,因此该部分的程序无法做太多的事,因此需要扩展程序,有以下两种方式:这里选用方式二,因为实现起来相对简单一点。16位实模式的情况下,寄存器也是16位,详见下图右边的AXBXCXDX8086CPU的寻址范围是1MB确定物理地址的方......
  • vxe-弹窗初始化激活选中Vxe-Table表格中第一行input输入框
    1.实现效果2.Modal弹窗的渲染过程一、Vue组件的生命周期Vue组件从创建到销毁会经历一系列的生命周期钩子,这些钩子为开发者提供了在不同阶段插入自定义逻辑的机会。在Modal弹窗的上下文中,这些生命周期钩子同样适用。beforeCreate:组件实例初始化之后,数据观测和事件配置之前......
  • ubantu初始化配置
    配置网络wxx@k8s-master01:~$more/etc/netplan/00-installer-config.yaml#Thisisthenetworkconfigwrittenby'subiquity'network:ethernets:ens33:dhcp4:nodhcp6:noaddresses:-192.168.223.11/24nameservers:......