原创 基于STM8S单片机的FREEMODBUS总线协议栈的移植

2013-11-26 15:07 4506 25 25 分类: MCU/ 嵌入式 文集: 电子应用

     FreeMODBUS一个奥地利人写的Modbus协议。它是一个针对嵌入式应用的一个免费(自由)的通用MODBUS协议的移植。Modbus是一个工业制造环境中应用的一个通用协议.。

    FreeMODBUS最新版本V1.5。下载地址:http://www.freemodbus.org/index.php?idx=5

   下面进行的移植基于STM8S单片机的官方固件库

   1、物理层接口文件修改(具体应修改接口文件portserial.c及porttimer.c)

    portserial.c文件中:
    void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )函数设置串口状态。当xRxEnable为真时,应使能串口接收及接收中断。在RS485通讯系统中,还要注意将RS485接口芯片设为接收使能状态;当xTxEnable为真时,应使能串口发送及发送中断。在RS485通讯系统中,还要注意将RS485接口芯片设为发送使能状态。void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) //控制串口的收发中断
{
    if(TRUE==xRxEnable)
    {
        UART1_ITConfig(UART1_IT_RXNE, ENABLE);
    }
    else
    {
        UART1_ITConfig(UART1_IT_RXNE, DISABLE);    
    }

    if(TRUE==xTxEnable)
    {
        UART1_ITConfig(UART1_IT_TXE,ENABLE);
    }
    else
    {
       UART1_ITConfig(UART1_IT_TXE,  DISABLE);
    }
}

BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )函数初始化串行通讯端口。参数ucPORT可以忽略;参数ulBaudRate是通讯端口的波特率,应根据此数值设置所使用硬件端口的波特率;参数ucDataBits为通讯时所使用的数据位宽,注意,若使用RTU模式,则有ucDataBits=8,若使用ASCII模式,则有ucDataBits=7,应根据此参数设置所使用硬件端口的数据位宽;eParity为校验方式,eParity=MB_PAR_NONE为无校验,此时硬件端口应设置为无校验方式及两个停止位,eParity=MB_PAR_ODD为奇校验,此时硬件端口应设置为奇校验方式及一个停止位,eParity= MB_PAR_EVEN为偶校验,此时硬件端口应设置为偶校验方式及一个停止位。函数返回值务必为TRUE。
BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
      UART1_WordLength_TypeDef databit;
      UART1_Parity_TypeDef parity;
     UART1_DeInit();
        if(8==ucDataBits)
        {
            databit = UART1_WORDLENGTH_8D;
        }
        else
        {
            databit = UART1_WORDLENGTH_9D;        
        }
        if(MB_PAR_NONE == eParity)
        {
            parity = UART1_PARITY_NO;
        }
        else if(MB_PAR_ODD == eParity)
        {
                parity = UART1_PARITY_ODD;        
        }
        else
        {
                parity = UART1_PARITY_EVEN;        
        }
        UART1_Init((uint32_t)ulBaudRate,databit, UART1_STOPBITS_1, parity,
         UART1_SYNCMODE_CLOCK_DISABLE, UART1_MODE_TXRX_ENABLE);
    /* ENABLE the USARTx */
    UART1_ITConfig(UART1_IT_TXE, ENABLE);       
    return TRUE;
}

    BOOL xMBPortSerialPutByte( CHAR ucByte )通讯端口发送一字节数据函数。在此函数中编写发送一字节数据的函数。注意,由于使用的是中断发送,故只需将数据放到发送寄存器即可。函数返回值务必为TRUE。
BOOLxMBPortSerialPutByte( CHAR ucByte )
{
    UART1_SendData8(ucByte);
    return TRUE;
}

BOOL xMBPortSerialGetByte( CHAR * pucByte )通讯端口接收一字节数据函数。在此函数中编写接收的函数。由于使用的是中断接收,只需将接收寄存器的值放到* pucByte即可。函数返回值务必为TRUE。
BOOL xMBPortSerialGetByte( CHAR * pucByte )
{
    *pucByte = UART1_ReceiveData8();
    return TRUE;
}

void prvvUARTRxISR( void )和void prvvUARTTxReadyISR( void )无需修改。

portserial.c文件修改
BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )初始化超时定时器播函数。根据所使用的硬件初始化超时定时器,使之能产生中断时间为usTim1Timerout50us*50us的中断,本例移植采用单片机的TIM4定时器。函数返回值务必为TRUE。
BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )
{
  /* TIM4 configuration:
   - TIM4CLK is set to 16 MHz, the TIM4 Prescaler is equal to 128 so the TIM1 counter
   clock used is 16 MHz / 128 = 125 000 Hz
  - With 125 000 Hz we can generate time base:
      max time base is 2.048 ms if TIM4_PERIOD = 255 --> (255 + 1) / 125000 = 2.048 ms
      min time base is 0.016 ms if TIM4_PERIOD = 1   --> (  1 + 1) / 125000 = 0.016 ms
                                                           --> 16us
  - In this example we need to generate a time base equal to 50us
   so TIM4_PERIOD = (0.00005 * 125000 - 1) = 5 */
    /* Time base configuration */
  TIM4_TimeBaseInit(TIM4_PRESCALER_128, 5);
  /* Clear TIM4 update flag */
  TIM4_ClearFlag(TIM4_FLAG_UPDATE);
  /* Enable update interrupt */
  TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);  
  /* enable interrupts */
 // enableInterrupts();
  /* Enable TIM4 */
  TIM4_Cmd(ENABLE);
    return TRUE;;
}

void vMBPortTimersEnable(  )使能超时定时器函数。需在此函数中清除中断标志位、清零定时器计数值,并重新使能定时器中断。
void vMBPortTimersEnable(  ) //打开时钟
{
    /* Clear TIM4 update flag */
  TIM4_ClearFlag(TIM4_FLAG_UPDATE);
     /* Enable update interrupt */
  TIM4_ITConfig(TIM4_IT_UPDATE, ENABLE);
    TIM4_SetCounter(0x00);
     /* Enable TIM4 */
  TIM4_Cmd(ENABLE);
}

void vMBPortTimersDisable(  )关闭超时定时器函数。在此函数中清零定时器计数值,并关闭定时器中断。
void vMBPortTimersDisable(  ) //关闭时钟
{
    TIM4_Cmd(DISABLE);
    TIM4_SetCounter(0x00);
    TIM4_ITConfig(TIM4_IT_UPDATE, DISABLE);
  TIM4_ClearFlag(TIM4_FLAG_UPDATE);
}

void prvvTIMERExpiredISR( void )函数不需修改。

2、 应用层回函数的修改
    在应用层,用户需要定义所需要使用的寄存器,并修改对应的回函数。

eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )输入寄存器回函数。* pucRegBuffer为协议中所需的数据,usAddress为输入寄存器地址,usNRegs为访问寄存器的个数,eMode为访问类型(MB_REG_READ为读保持寄存器,MB_REG_WRITE为写保持寄存器)。用户应根据要访问的寄存器地址usAddress将相应输入寄存器的值按顺序添加到pucRegBuffer中,或将协议中的数据根据要访问的寄存器地址usAddress放到相应保持寄存器中。
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 )保持寄存器回函数。* pucRegBuffer为要协议中的数据,usAddress为输入寄存器地址,usNRegs为访问寄存器的个数,eMode为访问类型(MB_REG_READ为读保持寄存器,MB_REG_WRITE为写保持寄存器)。用户应根据要访问的寄存器地址usAddress将相应输入寄存器的值按顺序添加到pucRegBuffer中,或将协议中的数据根据要访问的寄存器地址usAddress放到相应保持寄存器中。
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;
    u16 *PRT=(u16*)pucRegBuffer;
    if( ( usAddress >= REG_HOLDING_START ) && ( usAddress + usNRegs <= REG_HOLDING_START + REG_HOLDING_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - usRegHoldingStart );
        switch ( eMode )
        {
         case MB_REG_READ:
            while( usNRegs > 0 )
            {
              ////  *PRT++ = __REV16(usRegHoldingBuf[iRegIndex++]); //数据序转 REV16.W

                       *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 );
              *pucRegBuffer++ = ( unsigned char )( usRegHoldingBuf[iRegIndex] & 0xFF );
                      iRegIndex++;
              usNRegs--;
            }
            break;

        case MB_REG_WRITE:
            while( usNRegs > 0 )
            {
              ////  usRegHoldingBuf[iRegIndex++] = __REV16(*PRT++); //数据序转 REV16.W
                    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 )读/写开关寄存器 函数。* pucRegBuffer为要添加到协议中的数据,usAddress为地址,usNCoils为要访问的个数,eMode为访问类型(MB_REG_READ为读状态,MB_REG_WRITE为写)。用户应根据要访问的地址usAddress将相应的值按顺序添加到pucRegBuffer中,或将协议中的数据根据要访问的地址usAddress放到相应地址中。
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
    ( void )pucRegBuffer;
    ( void )usAddress;
    ( void )usNCoils;
    ( void )eMode;
    return MB_ENOREG;
}

eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )读开关寄存器函数。
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    ( void )pucRegBuffer;
    ( void )usAddress;
    ( void )usNDiscrete;
    return MB_ENOREG;
}
 

 

 

 

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
25
关闭 站长推荐上一条 /3 下一条