FreeModbus串口移植注意事项

FreeModbus串口移植因为要考虑到实时性,这部分还是很重要的。为了保证实时,使用中断还是很有必要的。下面以RTU的工作模式为例。如有不对,欢迎指正。

1. 串口接口描述

BOOL            xMBPortSerialInit( UCHAR ucPort, ULONG ulBaudRate,
                                   UCHAR ucDataBits, eMBParity eParity );//串口初始化
void            vMBPortClose( void );//关闭串口
void            xMBPortSerialClose( void );//这个一般不需要实现
void            vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable );//是能串口的TX,RX	
BOOL            xMBPortSerialGetByte( CHAR * pucByte );//从缓冲区获取一个字节
BOOL            xMBPortSerialPutByte( CHAR ucByte );//设置一个字节到缓冲区中

2. 串口接收方法xMBRTUReceiveFSM()

串口接收移植是最重要的,如果移植不太好,会大大的影响系统稳定性。为了保证实时性xMBRTUReceiveFSM()最好在串口中断服务程序中处理。同时xMBPortSerialGetByte()直接读取串口的数据寄存器,最好不要干其它事情,这样的话就连续不断的把串口数据接收到RTU中。

xMBRTUReceiveFSM( void )
{
    BOOL            xTaskNeedSwitch = FALSE;
    UCHAR           ucByte;

    assert( eSndState == STATE_TX_IDLE );

    /* Always read the character. */
    //每来一次中断,读取一次数据,这里会从串口接收数据寄存器中取出数据.
    ( void )xMBPortSerialGetByte( ( CHAR * ) & ucByte );

    switch ( eRcvState )
    {
    case STATE_RX_INIT:
        vMBPortTimersEnable(  );
        break;
    case STATE_RX_ERROR:
        vMBPortTimersEnable(  );
        break;
    case STATE_RX_IDLE://空闲时为这个状态
        usRcvBufferPos = 0;
        ucRTUBuf[usRcvBufferPos++] = ucByte;
        eRcvState = STATE_RX_RCV;

        /* Enable t3.5 timers. */
        vMBPortTimersEnable(  );
        break;
    case STATE_RX_RCV:
        if( usRcvBufferPos < MB_SER_PDU_SIZE_MAX )
        {
            ucRTUBuf[usRcvBufferPos++] = ucByte;
        }
        else
        {
            eRcvState = STATE_RX_ERROR;
        }
        vMBPortTimersEnable(  ); //重新设置下定时器,即重新计时,准备接收下一个字节。
        break;
    }
    return xTaskNeedSwitch;
}

2.1 串口xMBPortSerialGetByte()移植示例

直接读取串口数据寄存器,最好不要有其它操作。

BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
    *pucByte = USARTX->DR;
    return TRUE;
}

2.2 xMBRTUReceiveFSM移植示例

直接在串口中断服务程序中处理,最好不要有其它操作。

void UsartHandleISR()
{
	if (is_receive)//判断是哪种中断
		pxMBFrameCBByteReceived()
}

3. 串口发送方法xMBRTUTransmitFSM()

由于是发送数据,主动权掌握在发送端。虽然理论上只要把数据发送出去就可以了,但是接收端也同样会检测帧间隔。如果帧间隔控制不好,会导致接收端丢帧。目前有2种方案,一种软件轮询方案,我在本地验证还是很好用的,另外一个中断的,等用stm32验证下,到时在更新下博文。

BOOL
xMBRTUTransmitFSM( void )
{
    BOOL            xNeedPoll = FALSE;

    assert( eRcvState == STATE_RX_IDLE );

    switch ( eSndState )
    {
        /* We should not get a transmitter event if the transmitter is in
         * idle state.  */
    case STATE_TX_IDLE:
        /* enable receiver/disable transmitter. */
        vMBPortSerialEnable( TRUE, FALSE );
        break;

    case STATE_TX_XMIT: //启动发送之前,发送状态机eSndState = STATE_TX_XMIT
        /* check if we are finished. */
        if( usSndBufferCount != 0 )
        {
            xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
            pucSndBufferCur++;  /* next byte in sendbuffer. */
            usSndBufferCount--;
        }
        else
        {   //到这里数据已经发送完成,设置状态机为EV_FRAME_SENT 
            xNeedPoll = xMBPortEventPost( EV_FRAME_SENT );
            /* Disable transmitter. This prevents another transmit buffer
             * empty interrupt. */
            vMBPortSerialEnable( TRUE, FALSE );
            eSndState = STATE_TX_IDLE;
        }
        break;
    }

    return xNeedPoll;
}

单字节发送xMBPortSerialPutByte(),一般直接写发送寄存器。

BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
    USARTX->DR = ucByte;
    return TRUE;
}

3.1 软件循环发送xMBRTUTransmitFSM()

mb.c中的eMBPoll( void )一般都是在一个while循环中,而该函数会源源不断的读取消息队列,如果消息队列为空,则会执行发送流程。一般走到send流程,消息队列中是没有消息处理的。如下添加一个xMBPortSerialPoll()函数,去循环处理发送操作,此接口会暴露给portevent.c。

BOOL
xMBPortSerialPoll(  )
{
    if( bTxEnabled )
    {
        while( bTxEnabled )
        {
            ( void )pxMBFrameCBTransmitterEmpty(  );
            /* Call the modbus stack to let him fill the buffer. */
            //这里最好加个超时机制
        }
   }
}
//mb.c
BOOL
xMBPortEventGet( eMBEventType * eEvent )
{
    BOOL            xEventHappened = FALSE;

    if( xEventInQueue ) //没
    {
        *eEvent = eQueuedEvent;
        xEventInQueue = FALSE;
        xEventHappened = TRUE;
    }
    else
    {        
        ( void )xMBPortSerialPoll(  );  //这里取执行发送过程    
    }
    return xEventHappened;
}

如下面即为eMBPoll()触发写操作的过程

eMBErrorCode
eMBPoll( void )
{
.....
    if( xMBPortEventGet( &eEvent ) == TRUE )//在这里执行发送流程
    {
        switch ( eEvent )
        {
        case EV_READY:
            break;

        case EV_FRAME_RECEIVED:
        	break;
        }
     }
}

3.2 发送函数添加到串口中断服务中

放到串口中断服务程序中执行,实时性还是很有保证,不过就怕产生中断时,上一次中断服务程序还没有执行完毕,一些中断标记位还没来得及置位。

void UsartHandleISR()
{
	if (is_send) {//判断是哪种中断
		pxMBFrameCBTransmitterEmpty()
		clear_int_bit();//示例,千万不要忘了清除相关中断标志位
    }
}

虽然想使用中断服务程序去处理发送流程,但总需要有人去发数据,激活中断服务吧。为此需要在eMBRTUSend()发送第一个字节,修改如下。

eMBErrorCode
eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          usCRC16;

    ENTER_CRITICAL_SECTION(  );

    /* Check if the receiver is still in idle state. If not we where to
     * slow with processing the received frame and the master sent another
     * frame on the network. We have to abort sending the frame.
     */
    if( eRcvState == STATE_RX_IDLE )
    {
        /* First byte before the Modbus-PDU is the slave address. */
        pucSndBufferCur = ( UCHAR * ) pucFrame - 1;
        usSndBufferCount = 1;

        /* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */
        pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;
        usSndBufferCount += usLength;

        /* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */
        usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );
        ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );
        ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );

        /* Activate the transmitter. */
        eSndState = STATE_TX_XMIT;
        vMBPortSerialEnable( FALSE, TRUE );
        //前面的数据和串口都已经准备就绪,启动发送第一个字节,触发发送中断,启动发送发动机。
        xMBRTUTransmitFSM();
    }
    else
    {
        eStatus = MB_EIO;
    }
    EXIT_CRITICAL_SECTION(  );
    return eStatus;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: Freemodbus HAL移植是将Freemodbus层与底层硬件之间的接口适配到不同的硬件平台或操作系统上的一个过程。 在进行Freemodbus HAL移植前,我们首先需要理解Freemodbus的原理和结构。Freemodbus是一款开源的Modbus通信协议栈,它主要由Modbus协议的核心功能、硬件抽象层(HAL)和物理层(PHY)组成。其中,HAL层主要负责与底层硬件的交互,包括GPIO控制、串口通信、定时器等。 进行Freemodbus HAL移植的具体步骤如下: 1. 确定目标硬件平台或操作系统。 2. 查阅目标平台的文档或参考资料,了解其硬件架构、IO口、串口、定时器等相关信息。 3. 根据目标平台的特点,对Freemodbus HAL层进行修改和适配。主要包括GPIO控制、串口通信和定时器等功能的实现。 4. 在目标平台上移植和编译Freemodbus源代码,并与应用程序进行集成。 5. 进行功能测试和兼容性测试,确保移植后的Freemodbus可以正常运行和与其他设备进行Modbus通信。 在进行Freemodbus HAL移植时,需要注意以下几点: 1. 理解目标平台的硬件特性和限制,合理利用硬件资源。 2. 根据目标平台的不同,可能需要修改一些底层驱动程序或库函数的实现,以适配不同的硬件接口和操作系统调用。 3. 注意调试和测试过程,定位并修复可能出现的问题和Bug。 4. 充分了解Freemodbus的应用场景和功能需求,确保移植后的系统可以满足实际应用需求。 总之,Freemodbus HAL移植是将Freemodbus与不同的硬件平台或操作系统进行适配的过程,需要掌握目标平台的相关知识和技术,并进行充分的测试和调试工作,以确保移植后的系统能够正常工作并实现预期的功能。 ### 回答2: freemodbus hal 移植指的是将freemodbus(自由的 Modbus 通信库)的硬件抽象层(HAL)移植到其他硬件平台上。 首先,进行freemodbus hal 移植前,我们需要了解目标硬件平台和所需要的功能。根据硬件平台的不同,移植的步骤可能有所差异,但下面是一般的移植步骤: 1. 硬件初始化:根据硬件平台的要求,初始化与Modbus通信相关的硬件资源,如串口、GPIO等。 2. HAL 接口实现:根据freemodbus HAL的接口定义,实现与目标硬件平台相关的 HAL 接口函数。这些接口函数包括初始化串口、发送和接收数据等。 3. 中断处理:如果硬件平台支持中断,我们需要实现相应的中断处理函数,并在适当的时候调用freemodbus HAL提供的中断相关接口。 4. 调试与测试:进行移植后,需要对移植freemodbus HAL进行调试和测试。可以使用Modbus调试工具,如Modbus Slave,来验证Modbus通信的正确性。 总结来说,freemodbus hal 移植的关键在于实现与目标硬件平台相关的 HAL 接口函数,并根据硬件平台的特性进行适配。移植完成后,可以使用freemodbus库的功能来实现Modbus通信,并对其进行调试和测试,确保功能正常。 需要注意的是,由于具体硬件平台和要求的不同,移植的详细步骤可能会有所变化。因此,在进行具体的freemodbus hal 移植前,应该仔细阅读相关的文档和参考资料,并根据硬件平台的要求进行适当的调整和修改。 ### 回答3: Freemodbus HAL(硬件抽象层)移植是将Freemodbus协议栈适配到特定硬件平台或操作系统的过程。在进行移植之前,首先需要了解目标平台的硬件特性和操作系统支持情况。 移植的第一步是针对目标平台和操作系统编写HAL层驱动程序。这些驱动程序负责与硬件进行通信,并提供必要的接口供协议栈使用。HAL层驱动程序通常包括串口或以太网接口驱动,用于与Modbus设备进行通信。 其次,需要根据目标平台的字节序,调整协议栈的字节序设置。Modbus协议使用大端字节序,因此移植时需要设置正确的字节序,以确保数据的正确传输和解析。 接下来,需要根据目标平台的内存和处理能力,进行协议栈的调整。可能需要进行内存优化或适配以满足硬件资源限制。例如,可以选择禁用一些不必要的功能或减小数据包缓冲区的大小。 最后,在移植过程中需要对协议栈进行验证和测试,以确保其在目标平台上的正常运行。可以使用一些模拟器或硬件测试设备来进行测试,并检查通信的准确性和稳定性。如果发现问题,则需要进行调试和修复。 总的来说,Freemodbus HAL移植需要根据目标平台的硬件和操作系统特性进行适配和调整。这样才能确保协议栈在新平台上能够正常运行,并与Modbus设备进行通信。移植过程需要耐心和仔细的调试和测试,以确保移植成功。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值