zoukankan      html  css  js  c++  java
  • 基于RT-Thread移植FreeModbus

    平台:正点原子STM32F407探索者开发板 + FreeModbus V1.6 + RT-Thread

    源码链接:https://www.embedded-solutions.at/en/freemodbus-downloads/

    源码列表如图所示,需要的文件包括modbus文件夹和demo文件夹内的port相关文件,port文件位置如下图

    在BASE文件夹内有一个port文件夹(内含需要的port相关文件)和一个demo文件,demo文件写好了FreeModbus的启动使用。

    将这些文件载入到工程中,并包含相关的头文件

    由于第一次移植,没敢乱动,直接全部移植进来只开启了需要的功能。mb开头的文件为协议栈程序,暂时不必改动;port开头文件是需要用户进行相关配置的文件。其中 portevent.c 不用动,portserial.c 和 porttimer.c 内部实现串口功能和定时器功能,包括初始化配置以及启动和关闭。

    第一步:配置portserial.c

      源码文件中portserial.c包含下面几个函数,大体功能如注释所述。

     1 void
     2 vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )//可以根据输入参数配置RS485收发器的收发模式
     3 {
     4     /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     5      * transmitter empty interrupts.
     6      */
    7 } 8 9 BOOL 10 xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) 11 {
        //初始化串口 --> 查找串口设备,根据参数配置串口,打开串口设备,设置串口接收回调函数等
    12 return FALSE;//添加代码后需要修改为 TRUE 13 } 14 15 BOOL 16 xMBPortSerialPutByte( CHAR ucByte ) 17 { 18 /* Put a byte in the UARTs transmit buffer. This function is called 19 * by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been 20 * called. */
        //发送一个字节数据,只需要调用发送接口即可
    21 return TRUE; 22 } 23 24 BOOL 25 xMBPortSerialGetByte( CHAR * pucByte ) 26 { 27 /* Return the byte in the UARTs receive buffer. This function is called 28 * by the protocol stack after pxMBFrameCBByteReceived( ) has been called. 29 */
        //接收一个字节数据
    30 return TRUE; 31 } 32 33 /* Create an interrupt handler for the transmit buffer empty interrupt 34 * (or an equivalent) for your target processor. This function should then 35 * call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that 36 * a new character can be sent. The protocol stack will then call 37 * xMBPortSerialPutByte( ) to send the character. 38 */ 39 static void prvvUARTTxReadyISR( void ) 40 {
        //协议栈的发送中断函数,可以在串口发送中断里面调用它
    41 pxMBFrameCBTransmitterEmpty( ); 42 } 43 44 /* Create an interrupt handler for the receive interrupt for your target 45 * processor. This function should then call pxMBFrameCBByteReceived( ). The 46 * protocol stack will then call xMBPortSerialGetByte( ) to retrieve the 47 * character. 48 */ 49 static void prvvUARTRxISR( void ) 50 {
        //协议栈的接收中断函数,可以在串口接收中断里面调用它;放在回调函数即可
    51 pxMBFrameCBByteReceived( ); 52 }

    第二步:配置porttimer.c

      源码文件与串口的形式类似,具体如下

     1 BOOL
     2 xMBPortTimersInit( USHORT usTim1Timerout50us )//定时器设备初始化,查找设备,打开设备,配置设备,根据参数设置超时时间
     3 {
     4     return FALSE;//添加代码后需要改为 TRUE
     5 }
     6 
     7 
     8 inline void
     9 vMBPortTimersEnable(  )//此处理解为定时器的启动,调用该接口即开始定时
    10 {
    11     /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
    12 }
    13 
    14 inline void
    15 vMBPortTimersDisable(  )//定时器的关闭,我个人使用的单次定时,所以就没做处理。如果是循环的需要关闭定时器
    16 {
    17     /* Disable any pending timers. */
    18 }
    19 
    20 static void prvvTIMERExpiredISR( void )//超时函数,可以放到定时器超时回调函数中调用
    21 {
    22     ( void )pxMBPortCBTimerExpired(  );
    23 }

    第三步:配置demo.c文件

      该文件下有有一个main函数,其它函数为不同功能的回调函数。官方下载的源码中main函数和eMBRegInputCB已经实现,可以参考使用。

     1 int
     2 main( void )
     3 {
     4     const UCHAR     ucSlaveID[] = { 0xAA, 0xBB, 0xCC };
     5     eMBErrorCode    eStatus;
     6 
     7     eStatus = eMBInit( MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN );
     8 
     9     eStatus = eMBSetSlaveID( 0x34, TRUE, ucSlaveID, 3 );
    10     sei(  );
    11 
    12     /* Enable the Modbus Protocol Stack. */
    13     eStatus = eMBEnable(  );
    14 
    15     for( ;; )
    16     {
    17         ( void )eMBPoll(  );
    18 
    19         /* Here we simply count the number of poll cycles. */
    20         usRegInputBuf[0]++;
    21     }
    22 }

      main函数内容实现的是协议栈的初始化、启动和循环更新,重要的三个函数为eMBInit、eMBEnable、eMBPoll。官方源码中作为一个功能循环使用,在RT-Thread中我把其作为一个独立任务,在任务函数里进行协议栈初始化和启动,状态更新作为线程循环。

     1 eMBErrorCode
     2 eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
     3 {
     4     eMBErrorCode    eStatus = MB_ENOERR;
     5     int             iRegIndex;
     6 
     7     if( ( usAddress >= REG_INPUT_START )
     8         && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
     9     {
    10         iRegIndex = ( int )( usAddress - usRegInputStart );
    11         while( usNRegs > 0 )
    12         {
    13             *pucRegBuffer++ =
    14                 ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
    15             *pucRegBuffer++ =
    16                 ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
    17             iRegIndex++;
    18             usNRegs--;
    19         }
    20     }
    21     else
    22     {
    23         eStatus = MB_ENOREG;
    24     }
    25 
    26     return eStatus;
    27 }
    28 
    29 eMBErrorCode
    30 eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
    31                  eMBRegisterMode eMode )
    32 {
    33     return MB_ENOREG;
    34 }
    35 
    36 
    37 eMBErrorCode
    38 eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
    39                eMBRegisterMode eMode )
    40 {
    41     return MB_ENOREG;
    42 }
    43 
    44 eMBErrorCode
    45 eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
    46 {
    47     return MB_ENOREG;
    48 }

      这些函数是协议栈对应功能的回调函数,并且给了一个示例,它们由function文件下的函数调用,只需在需要的功能里作出对应实现即可。这些函数的返回值是特定的形式,参考示例函数进行操作即可,至于相应地址数据的处理根据实际需求。

    最后:

      新手上路,还有很多地方理解不到位,暂时记录这么多,继续学习!

  • 相关阅读:
    由于空间,注定的结果——第五届山东省ACM编程比赛总结
    GPS 偏移校正(WGS-84) 至(GCJ-02) java版本号以实现
    IM设计与实现的系统模块的聊天记录
    Android数据存储——SQLite数据库(模板)
    JAVA多线程和并发基础面试问答
    好博客
    浅谈Java中的equals和==
    JVM的内存区域划分
    try,catch,finally
    Java 如何有效地避免OOM:善于利用软引用和弱引用
  • 原文地址:https://www.cnblogs.com/niu-li/p/11580608.html
Copyright © 2011-2022 走看看