[嵌入式開發模塊]通用接收狀態機模塊

版權聲明:本文爲博主(http://blog.csdn.net/lin_strong)原創文章,轉載請標明出處並事先取得博主同意 https://blog.csdn.net/lin_strong/article/details/80499831

前言

在軟件開發的過程中,只要涉及到通信,就會涉及到數據接收機的編寫,通信協議雖然多種多樣,但是數據包的形式確是很相似的(暫時沒看到特別複雜,此模塊解決不了的),爲此可以把其中通用的部分抽象出來,然後就成了這個模塊。

模塊相關概念和邏輯

接收機狀態

接收機有兩個基本狀態:

  • 狀態A:preRx 等待幀頭中,這個時候接收機會不斷判斷是否收到了特殊序列、幀頭和強幀尾。如果收到了幀頭,則(默認)會把幀頭填入緩衝區的最前面,然後進入狀態B。
  • 狀態B:Rxing 等待幀尾中,收到的數據會按序填入接收緩衝區,同時不斷查找幀尾、強幀頭以及強特殊序列,不管找到了哪一個都會觸發flush。如果,找到的是強幀頭,則仍然在狀態B,否則恢復狀態A。

不管在哪一個狀態下,如果接受緩衝區滿了,都會觸發flush並回到狀態A。

標誌字符序列

接收機定義了以下標誌序列類型:

類型 模塊中標記 描述
幀頭 header 只在狀態A起作用;找到時會導致之前接收區內的數據被flush,然後默認會填入接收區的最前面,可以通過選項來改變這個行爲,然後接收機進入狀態B。
強幀頭 strong-header 在狀態B也會起作用,其他行爲與普通幀頭一樣
幀尾 ender 只在狀態B起作用;找到時會導致當前接收區內的數據被flush,默認會處於回調時給用戶的buffer的最後面,可以通過選項來改變這個行爲,然後接收機回到狀態A。
強幀頭 strong-header 在狀態A也會起作用,其他行爲與普通幀尾一樣
特殊序列 unique 只在狀態A起作用;找到時會導致之前接收區內的數據被flush,然後自己被填入接收區內後再次觸發flush,然後接收機回到狀態A。
強特殊序列 strong-unique 在狀態B也會起作用,其他行爲與特殊序列一樣

對應模塊中的結構體爲:

typedef struct rx_flag{
   INT8U const *pBuf;
   INT8U len;
   INT8U option;
} RX_FLAG,* pRX_FLAG;

一般的流程中,初始化接收機前,用戶需要準備好接收機使用的所有標誌序列,標誌好每個序列的類型。模塊提供宏函數以抽象這個過程:

// to set the flag's option
//    pbuf     pointer to the flag buffer
//    bufSize  size of flag 
//    opt      see  FLAG_OPTION_XXXXX
#define RX_FLAG_INIT(pFlag,pbuf,bufSize,opt)     \
         (pFlag)->pBuf =(pbuf);(pFlag)->len =(bufSize);(pFlag)->option = (opt);

flush

每當接收機根據標誌字符序列找到一個完整或不完整的數據包時,接收機會進行回調以通知用戶處理這個數據包,這個行爲被稱爲flush,這個回調函數叫做onFlushed。

此函數的原型如下

void (* RXMAC_FLUSH_EVENT)(pRX_MAC sender,pRB_BYTE pBuf,INT16U len,RX_STATE state,pRX_FLAG pHorU,pRX_FLAG pEnder);

整個數據包爲pBuf指向的長度爲len的區域。

數據包的狀態可以通過state參數得知,RX_STATE 的類型定義如下

typedef struct rx_state{
  unsigned int headerFound: 1;     // 1: have get header
  unsigned int enderFound : 1;     // 1: have get ender
  unsigned int isFull     : 1;     // 1: the buffer is full
  unsigned int UniqueFound: 1;     // 1: this is unique flag.
} RX_STATE;

通過判斷每個標誌位是否置1,可以得知當前數據包的狀態。
如果headerFound == 1,說明數據包是有幀頭的,可以通過pHorU獲得幀頭
如果enderFound == 1,說明數據包是有幀尾的,可以通過pEnder獲得幀尾
如果isFull == 1,說明此數據包是因爲接收區滿了放不下了而產生的回調,在一些需要根據某個字段來判斷數據包真正大小的協議裏,可以通過調整接收緩衝區的大小來恰當的產生flush。
如果UniqueFound == 1,說明此數據包是標誌序列,這時緩衝區內的內容等於pHorU指向的那個標誌序列

接收機結構體

由於嵌入式系統中一般不使用動態分配內存,所以要求用戶自己分配用到的內存。
模塊要求用戶爲每個接收機準備分配一個接收機結構體(以及對應的緩衝區和標誌序列),在對接收機進行操作時將指向結構體的指針作爲第一個參數傳入,以模擬面向對象的操作。

結構體的定義如下:

struct rx_mac{
   RING_QUEUE FlagQueue;   // 用於判斷標誌串的環形緩衝區對象

   pRX_FLAG Flags;         // 標誌數組
   INT8U FlagsCnt;         // 標誌數組的個數
   RX_STATE state;         // 接收的狀態(內部使用)
   pRX_FLAG pHorU;         // 內部使用

   pRB_BYTE pRxBuf;        // 存放數據的緩衝區
   INT16U RxBufSize;       // 緩衝區的長度

   pRB_BYTE pCur;          // 指向緩衝區內下一個填充字符的位置

   RXMAC_FILTER onFeeded;  // 當被喂字符時觸發,返回指向緩衝區中剛剛喂進來的字符的指針以及是緩衝區內的第幾個字符

   RXMAC_FLAG_EVENT onGetHeader;  // 獲得頭標誌位時觸發。
   RXMAC_FLUSH_EVENT onFlushed;    // 回調函數
};

一般情況下,用戶不應直接對內部的字段進行操作,而應該使用提供的函數。

緩衝區分配

用戶在初始化時需爲接收機準備一塊緩衝區,緩衝區會按照如下進行分配:

RxBuffer的大小由RxBufSize決定,其會在初始化時由BufLen - maxLenOfFlags計算出來。當RxBuffer中的數據量達到RxBufSize時會因爲緩衝區滿而觸發flush。

RxBufSize不是固定的,用戶可以通過RxMac_SetRxSize來重設RxBufSize的值以控制數據包的大小。還可以通過RxMac_ResetRxSize來重設其爲最大值。

過濾器

接收機每次被feed時,都會觸發onFeeded事件(可以在配置中取消這個事件以節省點代碼)。原型如下:

typedef void (* RXMAC_FILTER)(pRX_MAC sender,pRB_BYTE pCurChar,INT16U bytesCnt);

pCurChar :指向接收區中剛收到的字符
bytesCnt :這個字符是接收區中的第幾個字符

此時,接收機已經將其放入接收區但是還沒進一步進行判斷,用戶可以修改字符/配置接收機以改變接收機的行爲,比如將收到的字母全變爲小寫。或者根據收到的數據來決定還要收多少數據。

onGetHeader事件

當找到任意幀頭時會觸發。

接收機運行邏輯

接收機的運作邏輯如下:

邊際條件

標誌序列儘量不要有重合,如果同時可能匹配兩個標誌序列,最終行爲是未定義的,不保證正確執行,最終結果可能與標誌序列的位置以及類型有關係。

同一個標誌串可以同時是多種類型,比如同時是幀頭與幀尾,但要小心類型間不要有衝突,否則不保證正確執行。

對於標誌序列匹配到一半發生了接收緩衝區滿,從而導致發生標誌序列匹配時,接收緩衝區中只有半個標誌序列的情況:
如果標誌序列是(強)特殊序列或(強)幀頭的情況,可以保證功能正常運行。
如果標誌序列是強幀尾的情況,緩衝區內會只剩下後半段的幀尾,但可以根據pEnder參數來得知真正的幀尾。
幀尾不會發生這種邊際條件。

相關代碼

此模塊引用了我自己寫的環形緩衝區模塊:
https://blog.csdn.net/lin_strong/article/details/73604561

接收機代碼

頭文件

/*
*********************************************************************************************************
*
*
*                                  Universal Receive State Machine
*                                          通用接收狀態機
*
* File : RxMac.h
*
* By   : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date: 2018/05/29
* version: 1.0
* History: 2018/05/29     the prototype
* NOTE(s):  1. the receive process has two basic state
*              A. preRx: when haven't found any header, the RxMac will search for the unique
*                        flag, header and strong-ender. Only when a header is found will come
*                        to next step.
*              B. Rxing: the RxMac will put the successive bytes into the buffer, and search
*                        for the strong-unique, strong-header, ender. 
*           2. the module is drived by the RxMac_FeedData(), user should get the the char 
*              from data stream and pass the data one by one to the RxMac through RxMac_FeedData()
*           3. each time RxMac find a frame(complete or incomplete),it will call the onFlushed
*              to notify the results; user can judge the frame through the state parameter; 
*               state.headerFound == 1:   find any header, the header is passed by pHorU
*               state.enderFound  == 1:   find any ender,  the ender  is passed by pEnder
*               state.isFull      == 1:   the buffer is full, maybe you should check headerFound
*                                         to see whether a header has been found.
*               state.uniqueFound == 1:   find any unique flag. In this case, other parameters will
*                                         always be 0 & the datas in the buffer will be the flag.
*           4. To use this module, for each receive machine:
*              A. allocate the space for buffer, RxMac & FLAGS
*                   RX_MAC _Mac;
*                   RX_FLAG flags[2];
*                   INT8U buf[300];
*              B. set the flags according to the protocol, define the callback funcitons
*                  according to your need.
*                   static void onGetHeader(pRX_MAC sender,pRX_FLAG pFlag){ ...... };
*                   static void onFlushed(pRX_MAC sender,pRB_BYTE pBuf,INT16U len,
*                      RX_STATE state,pRX_FLAG pHorU,pRX_FLAG pEnder){ ...... };
*                   const INT8U HeaderFlag[] = "Header";
*                   const INT8U EnderFlag[] = "\r\n";
*                   RX_FLAG_INIT(flags,HeaderFlag,StrSize(HeaderFlag),FLAG_OPTION_HEADER);
*                   RX_FLAG_INIT(&flags[1],EnderFlag,StrSize(EnderFlag),FLAG_OPTION_ENDER |
*                                 FLAG_OPTION_NOTFILL_ENDER);
*              C. init the receive machine:
*                   RxMac_Init(&_Mac,flags,2,6,buf,300,NULL, onGetHeader, onFlushed );
*              D. feed the receive machine:
*                   while(1){
*                     c = GetNextChar();
*                     RxMac_FeedData(&_Mac,c);
*                   }
*********************************************************************************************************
*/

#ifndef RX_MAC_H
#define RX_MAC_H


/*
*********************************************************************************************************
*                                       INCLUDES
*********************************************************************************************************
*/
#include "RingQueue.h"
#include <os_cpu.h>

//typedef unsigned char INT8U;
//typedef unsigned short INT16U;

/*
*********************************************************************************************************
*                                       ADDRESSING MODE 尋址模式
*********************************************************************************************************
*/
// the addressing mode for buffer
#define RXMAC_BUF_ADDRESSING_MODE   RQ_ADDRESSING_MODE

typedef INT8U     RB_BYTE;
typedef RB_BYTE * RXMAC_BUF_ADDRESSING_MODE pRB_BYTE;
/*
*********************************************************************************************************
*                                       CONFIGURATION  配置
*********************************************************************************************************
*/

#define RXMAC_ARGUMENT_CHECK_EN   TRUE
#define RXMAC_NOTFILL_EN          TRUE

#define RXMAC_ONFEEDED_EN         TRUE    // TRUE: enable the onFeeded function.

#define RXMAC_SINGLETON_EN        FALSE   // TRUE: enable singleton pattern,so argument pRxMac of interfaces
                                          // is useless, and user don't need to allocate space for RX_MAC,
                                          // but you still need to allocate buffer and call init();

/*
*********************************************************************************************************
*                                       CONST
*********************************************************************************************************
*/

#define RXMAC_ERR_NONE           0
#define RXMAC_ERR_ARGUMENT       1
#define RXMAC_ERR_POINTERNULL    2
#define RXMAC_ERR_UNKNOWN        3
#define RXMAC_ERR_INIT           4
/*
*********************************************************************************************************
*                                 TYPE DEFINITION
*********************************************************************************************************
*/

// struct of RX_FLAG.option 
/*typedef struct FLAG_OPTION{
  unsigned int isHeader : 1;  // 1: the flag is the head of the frame
  unsigned int strong_H : 1;  // 1: strong-header, RxMac will 
  unsigned int notfill_H: 1;  // 0: fill the flag into the buffer when found as header
  unsigned int isEnder  : 1;  // 1: the flag is the end of the frame
  unsigned int strong_E : 1;  // 
  unsigned int notfill_E: 1;  // 0: fill the flag into the buffer when found as ender
  unsigned int isUnique : 1;  // 1: the flag is a unique flag which is treated as single frame.
  unsigned int strong_U : 1;  // 0: when receiving a frame, RxMac will not 
}; //*/

// 接收標誌位
typedef struct rx_flag{
   INT8U const *pBuf;
   INT8U len;
   INT8U option;
} RX_FLAG,* pRX_FLAG;

// normal header, RxMac will only check it in Step A
#define FLAG_OPTION_HEADER               0x01
// strong header, RxMac will always check it.
#define FLAG_OPTION_STRONG_HEADER        0x03
// the header will not be filled into buffer when found.(only valid when is header)
#define FLAG_OPTION_NOTFILL_HEADER       0x04
// normal ender, RxMac will only check it in Step B
#define FLAG_OPTION_ENDER                0x08
// strong header, RxMac will always check it.
#define FLAG_OPTION_STRONG_ENDER         0x18
// the ender will not be filled into buffer when found.(only valid when is ender)
#define FLAG_OPTION_NOTFILL_ENDER        0x20
// normal unique, RxMac will only check it in Step A
#define FLAG_OPTION_UNIQUE               0x40
// strong unique, RxMac will always check it.
#define FLAG_OPTION_STRONG_UNIQUE        0xC0

typedef struct rx_state{
  unsigned int headerFound: 1;     // 1: have get header
  unsigned int enderFound : 1;     // 1: have get ender
  unsigned int isFull     : 1;     // 1: the buffer is full
  unsigned int uniqueFound: 1;     // 1: this is unique flag.
} RX_STATE;

typedef struct rx_mac RX_MAC, *pRX_MAC;
typedef void (* RXMAC_FLUSH_EVENT)(pRX_MAC sender,pRB_BYTE pBuf,INT16U len,RX_STATE state,pRX_FLAG pHorU,pRX_FLAG pEnder);
typedef void (* RXMAC_FLAG_EVENT)(pRX_MAC sender,pRX_FLAG pFlag);
typedef void (* RXMAC_FILTER)(pRX_MAC sender,pRB_BYTE pCurChar,INT16U bytesCnt);

struct rx_mac{
   RING_QUEUE FlagQueue;   // 用於判斷標誌串的環形緩衝區對象

   pRX_FLAG Flags;         // 標誌數組
   INT8U FlagsCnt;         // 標誌數組的個數
   RX_STATE state;         // 接收的狀態(內部使用)
   pRX_FLAG pHorU;         // 內部使用

   pRB_BYTE pRxBuf;        // 存放數據的緩衝區
   INT16U RxBufSize;       // 緩衝區的長度

   pRB_BYTE pCur;          // 指向緩衝區內下一個填充字符的位置

   RXMAC_FILTER onFeeded;  // 當被喂字符時觸發,返回指向緩衝區中剛剛喂進來的字符的指針以及是緩衝區內的第幾個字符

   RXMAC_FLAG_EVENT onGetHeader;  // 獲得頭標誌位時觸發。
   RXMAC_FLUSH_EVENT onFlushed;    // 回調函數
};


/*
*********************************************************************************************************
*                                  FUNCTION DECLARATION
*********************************************************************************************************
*/
// to set the flag's option
//    pbuf     pointer to the flag buffer
//    bufSize  size of flag 
//    opt      see  FLAG_OPTION_XXXXX
#define RX_FLAG_INIT(pFlag,pbuf,bufSize,opt)     \
         (pFlag)->pBuf =(pbuf);(pFlag)->len =(bufSize);(pFlag)->option = (opt);

// to init the RxMac
INT8U RxMac_Init(pRX_MAC pRxMac,            // 需要用戶自己申請個UNI_RX_MACHINE對象的空間
                    RX_FLAG Flags[],INT8U FlagsCnt,INT8U maxLenOfFlags,  // 提供標誌字符串的數組
                    pRB_BYTE pBuf,INT16U BufLen, // 用戶需要提供緩衝區 (緩存區大小起碼應該要能
                                                 // 放的下最長的Flag+最長的幀,最後部分會分配給RQ)
                    RXMAC_FILTER onFeeded,           // 在每次被Feed時觸發
                    RXMAC_FLAG_EVENT onGetHeader,    // 獲得頭標誌位時觸發。
                    RXMAC_FLUSH_EVENT onFlushed      // 收到一幀數據時的回調函數
                    );
// 向接收機內喂字節
void RxMac_FeedData(pRX_MAC pRxMac,INT8U c);
// 重置接收區長度爲最長那個長度
INT8U RxMac_ResetRxSize(pRX_MAC pRxMac);
// 設置最大接收到多少個字節
INT8U RxMac_SetRxSize(pRX_MAC pRxMac, INT16U size);
// 重置接收機的狀態
INT8U RxMac_ResetState(pRX_MAC pRxMac);
// 強制接收機flush
INT8U RxMac_Flush(pRX_MAC pRxMac);
// 設置onFeeded
INT8U RxMac_SetOnFeeded(pRX_MAC pRxMac,RXMAC_FILTER onFeeded);
// 設置onGetHeader
INT8U RxMac_SetOnGetHeader(pRX_MAC pRxMac,RXMAC_FLAG_EVENT onGetHeader);
// 設置onFlushed
INT8U RxMac_SetOnFlushed(pRX_MAC pRxMac,RXMAC_FLUSH_EVENT onFlushed);

#endif // of RX_MAC_H

源文件:

/*
*********************************************************************************************************
*
*
*                                  Universal Receive State Machine
*                                          通用接收狀態機
*
* File : RxMac.c
*
* By   : Lin Shijun(https://blog.csdn.net/lin_strong)
* Date: 2018/05/29
* version: 1.0
* History: 
* NOTE(s): 
*
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                       INCLUDES
*********************************************************************************************************
*/
#include "RxMac.h"

#include <string.h>
#include <stddef.h>
/*
*********************************************************************************************************
*                                       CONSTANT
*********************************************************************************************************
*/
// normal header, RxMac will only check it in Step A
#define FLAG_OPTBIT_HEADER               0x01
// strong header, RxMac will always check it.
#define FLAG_OPTBIT_STRONG_HEADER        0x02
// the header will not be filled into buffer when found.(only valid when is header)
#define FLAG_OPTBIT_NOTFILL_HEADER       0x04
// normal ender, RxMac will only check it in Step B
#define FLAG_OPTBIT_ENDER                0x08
// strong header, RxMac will always check it.
#define FLAG_OPTBIT_STRONG_ENDER         0x10
// the ender will not be filled into buffer when found.(only valid when is ender)
#define FLAG_OPTBIT_NOTFILL_ENDER        0x20
// normal unique, RxMac will only check it in Step A
#define FLAG_OPTBIT_UNIQUE               0x40
// strong unique, RxMac will always check it.
#define FLAG_OPTBIT_STRONG_UNIQUE        0x80

#define STATEMASK_STEPA   (FLAG_OPTBIT_HEADER | FLAG_OPTBIT_UNIQUE | FLAG_OPTBIT_STRONG_ENDER)
#define STATEMASK_STEPB   (FLAG_OPTBIT_STRONG_UNIQUE | FLAG_OPTBIT_ENDER | FLAG_OPTBIT_STRONG_HEADER)
#define FLAGMASK_USUHSH   (FLAG_OPTBIT_HEADER | FLAG_OPTBIT_STRONG_HEADER | FLAG_OPTION_UNIQUE | FLAG_OPTBIT_STRONG_UNIQUE)

/*
*********************************************************************************************************
*                                   LOCAL  FUNCITON  DECLARATION
*********************************************************************************************************
*/
#if(RXMAC_SINGLETON_EN == FALSE)
  #define _pRxMac       pRxMac
#else
  static RX_MAC _RxMac;
  #define _pRxMac       (&_RxMac)
#endif

#define _FlagQueue   (_pRxMac->FlagQueue)
#define _Flags       (_pRxMac->Flags)
#define _FlagsCnt    (_pRxMac->FlagsCnt)
#define _state       (_pRxMac->state)
#define _stateByte   (*(INT8U *)(&_state))
#define _pHorU       (_pRxMac->pHorU)
#define _pRxBuf      (_pRxMac->pRxBuf)
#define _RxBufSize   (_pRxMac->RxBufSize)
#define _pCur        (_pRxMac->pCur)
#define _onFeeded    (_pRxMac->onFeeded)
#define _onGetHeader (_pRxMac->onGetHeader)
#define _onFlushed   (_pRxMac->onFlushed)
#define _isRxBufFull()  ((_pCur - _pRxBuf) >= _RxBufSize)


// 因爲不能保證用戶把數據放在非分頁區,只好自己實現一個,實際使用中如果確定在非分頁區可以把下面的宏替換爲庫函數memcpy
static pRB_BYTE _memcpy_internal(pRB_BYTE dest, pRB_BYTE src, size_t n);
#define _memcpy(dest,src,n)   _memcpy_internal(dest,src,n)
// 沖刷緩衝區
static void _flush(pRX_MAC pRxMac,pRX_FLAG ender);
// 往接收機緩衝區內放數據
static void _BufIn(pRX_MAC pRxMac,pRB_BYTE pBuf,INT16U len);

/*
*********************************************************************************************************
*                                        RxMac_Init()
*
* Description : To initialize a RxMac.    初始化接收機
*
* Arguments   : pRxMac    pointer to the RxMac struct;    指向接收機結構體的指針
*               Flags     pointer to the flags(an array); 指向標誌串(數組)的指針
*               FlagsCnt  the count of the flags;         有多少個標誌串;
*               maxLenOfFlags  the max length of flags;   標誌字符串最長的長度 
*               pBuf      pointer to the buffer provided to the RxMac;提供給接收機使用的緩存
*               BufLen    the size of the buffer.         緩存的大小
*               onFeeded   the callback func that will be called when feeded.
*                          每次被feed時會調用的回調函數,如改變對應數據值會影響標誌位的判斷
*               onGetHeader the callback func that will be called when find a header.
*                          當發現幀頭時會調用的回調函數
*               onFlushed  the callback func that will be called when flushed.
*                          當Flush時會調用的回調函數
*
* Return      : RXMAC_ERR_NONE        if success
*               RXMAC_ERR_ARGUMENT    if the length of longest Flags bigger than Buflen,or one of them is 0
*               RXMAC_ERR_POINTERNULL if empty pointer 
*
* Note(s)     : size of buffer should bigger than  the longest flag plus the longest frame 
*               that may be received(so at least 2 * maxLenOfFlags).
*               the buffer is allocate as follow:
*                 <----------------------  BufLen  ---------------------->
*                |                 RxBuffer             |                 |   
*                 <------------- RxBufSize ------------> <-maxLenOfFlags->
* 
*               void onGetHeader(pRX_MAC sender,pRX_FLAG pFlag):
*                sender   the pointer to the RxMac which call this function
*                pFlag    the header matched
*
*               void onFeeded(pRX_MAC sender,pRB_BYTE pCurChar,INT16U bytesCnt):
*                sender    the pointer to the RxMac which call this function
*                pCurChar  point to the char in the buffer just received.
*                bytesCnt  the number of bytes in the buffer including the char just feeded.
*
*               void onFlushed(pRX_MAC sender,pRB_BYTE pBuf,INT16U len,RX_STATE state,pRX_FLAG pHorU,
*                  pRX_FLAG pEnder);
*                sender    the pointer to the RxMac which call this function
*                pBuf      the pointer to the frame.
*                len       the length of frame.
*                state     the state of frame.
*                pHorU     point to the header flag if state.headerFound == 1, or unique flag if 
*                          state.uniqueFound == 1.
*                pEnder    point to the ender flag if state.enderFound == 1.
*********************************************************************************************************
*/
INT8U RxMac_Init
  (pRX_MAC pRxMac,RX_FLAG Flags[],INT8U FlagsCnt,INT8U maxLenOfFlags,pRB_BYTE pBuf,INT16U BufLen,
    RXMAC_FILTER onFeeded,RXMAC_FLAG_EVENT onGetHeader,RXMAC_FLUSH_EVENT onFlushed){
    //INT8U maxLen = 0;
    INT8U i;
#if(RXMAC_ARGUMENT_CHECK_EN)
    if( 
#if(!RXMAC_SINGLETON_EN)
      _pRxMac == NULL || 
#endif
      Flags == NULL || pBuf == NULL)
      return RXMAC_ERR_POINTERNULL;
#endif
    // find out the max length of flags.
    //for(i = 0; i < FlagsCnt; i++){
    //  if(Flags[i].len > maxLen)
    //    maxLen = Flags[i].len;
    //}
#if(RXMAC_ARGUMENT_CHECK_EN)
    if(maxLenOfFlags == 0 || (maxLenOfFlags * 2) > BufLen || BufLen == 0 || FlagsCnt == 0 ||
      maxLenOfFlags == 0)
      return RXMAC_ERR_ARGUMENT;
#endif
    BufLen -= maxLenOfFlags;
    // 把buffer的最後一段分配給環形緩衝區
    RingQueueInit(&_FlagQueue,pBuf + BufLen,maxLenOfFlags,&i);
    _Flags = Flags;
    _FlagsCnt = FlagsCnt;
    _stateByte = 0;
    _pHorU = NULL;
    _pRxBuf = pBuf;
    _RxBufSize = BufLen;
    _pCur = pBuf;
    _onFeeded = onFeeded;
    _onGetHeader = onGetHeader;
    _onFlushed = onFlushed;
    return RXMAC_ERR_NONE;
}
/*
*********************************************************************************************************
*                                        RxMac_FeedData()
*
* Description : To feed RxMac the next char.    用於給接收機下一個字符
*
* Arguments   : pRxMac    pointer to the RxMac struct;    指向接收機結構體的指針
*               c         the char to feed;               下一個字符
*
* Return      : 
*
* Note(s)     : 
*********************************************************************************************************
*/
void RxMac_FeedData(pRX_MAC pRxMac,INT8U c){
  INT8U i,mask;
  pRX_FLAG pFlag = NULL;
#if(RXMAC_ONFEEDED_EN)
  pRB_BYTE pCurChar = _pCur;
#endif
#if(RXMAC_ARGUMENT_CHECK_EN && !RXMAC_SINGLETON_EN)
  if(_pRxMac == NULL)
    return;
#endif
  *_pCur++ = c;    // 填入緩衝區
#if(RXMAC_ONFEEDED_EN)
  if(_onFeeded != NULL)
    _onFeeded(_pRxMac,pCurChar,_pCur - _pRxBuf);
  RingQueueIn(&_FlagQueue,*pCurChar,RQ_OPTION_WHEN_FULL_DISCARD_FIRST,&i);
#else
  RingQueueIn(&_FlagQueue,c,RQ_OPTION_WHEN_FULL_DISCARD_FIRST,&i);
#endif
  // _state.headerFound == 1  說明在等待幀尾,否則在等待幀頭
  mask = (_state.headerFound)?STATEMASK_STEPB:STATEMASK_STEPA;
  // 尋找匹配的標誌串
  for(i = 0; i < _FlagsCnt; i++){
    if((_Flags[i].option & mask) && 
      (RingQueueMatch(&_FlagQueue,(pRQTYPE)_Flags[i].pBuf,_Flags[i].len) >= 0)){
        RingQueueClear(&_FlagQueue);
        pFlag = &_Flags[i];
        break;
    }
  }
  // 如果沒有發現標誌串,檢查下有沒滿了,滿了就Flush
  if(pFlag == NULL){
    if(_isRxBufFull()){
      _state.isFull = 1;
      _flush(_pRxMac,NULL);
    }
    return;
  }
  // 這4種標誌串要_flush掉前面的東西
  if(pFlag->option & FLAGMASK_USUHSH){
    _pCur -= pFlag->len;
    _flush(_pRxMac,NULL);
    _pHorU = pFlag;
  }
  // 如果是幀頭的處理
  if(pFlag->option & (FLAG_OPTION_STRONG_HEADER | FLAG_OPTION_HEADER)){
#if(RXMAC_NOTFILL_EN == TRUE)
    if(!(pFlag->option & FLAG_OPTION_NOTFILL_HEADER))
#endif
      _BufIn(_pRxMac,(pRQTYPE)pFlag->pBuf,pFlag->len);
    _state.headerFound = 1;
    if(_onGetHeader != NULL)
      _onGetHeader(_pRxMac,pFlag);
    return;
  }
  // 如果是Unique的處理
  if(pFlag->option & (FLAG_OPTION_STRONG_UNIQUE | FLAG_OPTION_UNIQUE)){
    _state.uniqueFound = 1;
    _BufIn(_pRxMac,(pRQTYPE)pFlag->pBuf,pFlag->len);
    _flush(_pRxMac,NULL);
  }else{   // 能到這裏說明是幀尾
    _state.enderFound = 1;
#if(RXMAC_NOTFILL_EN == TRUE)
    if(pFlag->option & FLAG_OPTION_NOTFILL_ENDER)
      _pCur -= pFlag->len;
#endif
    _flush(_pRxMac,pFlag);
  }
  return;
}
/*
*********************************************************************************************************
*                                        RxMac_ResetRxSize()
*
* Description : reset the size of RxBuf to the max size.   重置接收緩衝區
*
* Arguments   : pRxMac    pointer to the RxMac struct;    指向接收機結構體的指針
*
* Return      : RXMAC_ERR_NONE            if Success;
*               RXMAC_ERR_POINTERNULL     if pRxMac == NULL
*               RXMAC_ERR_INIT            if RxMac hasn't inited or any error in initialization
* Note(s)     : 
*********************************************************************************************************
*/
INT8U RxMac_ResetRxSize(pRX_MAC pRxMac){
  int size;
#if(RXMAC_ARGUMENT_CHECK_EN && !RXMAC_SINGLETON_EN)
  if(_pRxMac == NULL)
    return RXMAC_ERR_POINTERNULL;
#endif
  size = _FlagQueue.RingBuf - _pRxBuf;
#if(RXMAC_ARGUMENT_CHECK_EN)
  if(size < 0)
    return RXMAC_ERR_INIT;
#endif
  _RxBufSize = (INT16U)size;
  return RXMAC_ERR_NONE;
}
/*
*********************************************************************************************************
*                                        RxMac_SetRxSize()
*
* Description : set the size of RxBuf to the max size.   重置接收緩衝區
*
* Arguments   : pRxMac    pointer to the RxMac struct;    指向接收機結構體的指針
*               size      the size to set;                
*
* Return      : RXMAC_ERR_NONE            if Success;
*               RXMAC_ERR_POINTERNULL     if pRxMac == NULL
*               RXMAC_ERR_ARGUMENT        if size is wrong.
* Note(s)     : the size shouldn't be bigger than the initial value, and should bigger than
*               the current number of chars in the RxBuf.
*               
*********************************************************************************************************
*/
INT8U RxMac_SetRxSize(pRX_MAC pRxMac, INT16U size){
#if(RXMAC_ARGUMENT_CHECK_EN)
 #if (!RXMAC_SINGLETON_EN)
  if(_pRxMac == NULL)
    return RXMAC_ERR_POINTERNULL;
 #endif
  if(_FlagQueue.RingBuf - _pRxBuf < size || size <= _pCur - _pRxBuf)
    return RXMAC_ERR_ARGUMENT;
#endif
  _RxBufSize = size;
  return RXMAC_ERR_NONE;
}
/*
*********************************************************************************************************
*                                        RxMac_ResetState()
*
* Description : reset the state of receive machine.   重置接收機的狀態
*
* Arguments   : pRxMac    pointer to the RxMac struct;    指向接收機結構體的指針
*
* Return      : RXMAC_ERR_NONE            if Success;
*               RXMAC_ERR_POINTERNULL     if pRxMac == NULL
* Note(s)     : it will not trigger call-back of onFlush.
*********************************************************************************************************
*/
INT8U RxMac_ResetState(pRX_MAC pRxMac){
#if(RXMAC_ARGUMENT_CHECK_EN  && !RXMAC_SINGLETON_EN)
  if(_pRxMac == NULL)
    return RXMAC_ERR_POINTERNULL;
#endif
  // 復位接收機
  RingQueueClear(&_FlagQueue);
  _pCur = _pRxBuf;
  _stateByte = 0;
  _pHorU = NULL;
  return RXMAC_ERR_NONE;
}
/*
*********************************************************************************************************
*                                        RxMac_Flush()
*
* Description : force receive machine to flush.
*
* Arguments   : pRxMac    pointer to the RxMac struct;    指向接收機結構體的指針
*
* Return      : RXMAC_ERR_NONE            if Success;
*               RXMAC_ERR_POINTERNULL     if pRxMac == NULL
*
* Note(s)     : it will force receive machine to flush, if there is any data in the RxBuffer,
*
*********************************************************************************************************
*/
INT8U RxMac_Flush(pRX_MAC pRxMac){
#if(RXMAC_ARGUMENT_CHECK_EN  && !RXMAC_SINGLETON_EN)
  if(_pRxMac == NULL)
    return RXMAC_ERR_POINTERNULL;
#endif
  _flush(_pRxMac,NULL);
  return RXMAC_ERR_NONE;
}
/*
*********************************************************************************************************
*                                        RxMac_SetOnFeeded()
*
* Description : set the onFeeded callback function.
*
* Arguments   : pRxMac    pointer to the RxMac struct;    指向接收機結構體的指針
*               onFeeded  the callback function to set;   要設置的回調函數
*
* Return      : RXMAC_ERR_NONE            if Success;
*               RXMAC_ERR_POINTERNULL     if pRxMac == NULL
*
* Note(s)     :
*
*********************************************************************************************************
*/
INT8U RxMac_SetOnFeeded(pRX_MAC pRxMac,RXMAC_FILTER onFeeded){
#if(RXMAC_ARGUMENT_CHECK_EN  && !RXMAC_SINGLETON_EN)
  if(_pRxMac == NULL)
    return RXMAC_ERR_POINTERNULL;
#endif
  _onFeeded = onFeeded;
  return RXMAC_ERR_NONE;
}
/*
*********************************************************************************************************
*                                        RxMac_SetOnGetHeader()
*
* Description : set the onGetHeader callback function.
*
* Arguments   : pRxMac       pointer to the RxMac struct;    指向接收機結構體的指針
*               onGetHeader  the callback function to set;   要設置的回調函數
*
* Return      : RXMAC_ERR_NONE            if Success;
*               RXMAC_ERR_POINTERNULL     if pRxMac == NULL
*
* Note(s)     :
*
*********************************************************************************************************
*/
INT8U RxMac_SetOnGetHeader(pRX_MAC pRxMac,RXMAC_FLAG_EVENT onGetHeader){
#if(RXMAC_ARGUMENT_CHECK_EN  && !RXMAC_SINGLETON_EN)
  if(_pRxMac == NULL)
    return RXMAC_ERR_POINTERNULL;
#endif
  _onGetHeader = onGetHeader;
  return RXMAC_ERR_NONE;
}
/*
*********************************************************************************************************
*                                        RxMac_SetOnFlushed()
*
* Description : set the onFlushed callback function.
*
* Arguments   : pRxMac       pointer to the RxMac struct;    指向接收機結構體的指針
*               onFlushed    the callback function to set;   要設置的回調函數
*
* Return      : RXMAC_ERR_NONE            if Success;
*               RXMAC_ERR_POINTERNULL     if pRxMac == NULL
*
* Note(s)     :
*
*********************************************************************************************************
*/
INT8U RxMac_SetOnFlushed(pRX_MAC pRxMac,RXMAC_FLUSH_EVENT onFlushed){
#if(RXMAC_ARGUMENT_CHECK_EN  && !RXMAC_SINGLETON_EN)
  if(_pRxMac == NULL)
    return RXMAC_ERR_POINTERNULL;
#endif
  _onFlushed = onFlushed;
  return RXMAC_ERR_NONE;
}
/*
*********************************************************************************************************
*                                   LOCAL  FUNCITON 
*********************************************************************************************************
*/
static pRB_BYTE _memcpy_internal(pRB_BYTE dest, pRB_BYTE src, size_t n){
  pRB_BYTE p = dest;
  while(n-- > 0)
    *p++ = *src++;
  return dest;
}

static void _BufIn(pRX_MAC pRxMac,pRB_BYTE pBuf,INT16U len){
  _memcpy(_pCur,pBuf,len);
  _pCur += len;
}

static void _flush(pRX_MAC pRxMac,pRX_FLAG ender){
  // 觸發回調
  if(_pCur-_pRxBuf > 0 && _onFlushed != NULL)
    _onFlushed(_pRxMac,_pRxBuf,_pCur-_pRxBuf,_state,_pHorU,ender);
  // 復位接收機
  _pCur = _pRxBuf;
  _stateByte = 0;
  _pHorU = NULL;
}

測試/示例代碼

/*
*********************************************************************************************************
*                                                uC/OS-II
*                                          The Real-Time Kernel
*                                               Framework
*
* By  : Lin Shijun
* Note: This is a framework for uCos-ii project with only S12CPU, none float, banked memory model.
*       You can use this framework with same modification as the start point of your project.
*       I've removed the os_probe module,since I thought it useless in most case.
*       This framework is adapted from the official release.
*********************************************************************************************************
*/

#include "includes.h"
#include "SCI_def.h"
#include "RxMac.h"
/*
*********************************************************************************************************
*                                      STACK SPACE DECLARATION
*********************************************************************************************************
*/
static  OS_STK  AppTaskStartStk[APP_TASK_START_STK_SIZE];

/*
*********************************************************************************************************
*                                      TASK FUNCTION DECLARATION
*********************************************************************************************************
*/

static  void    AppTaskStart(void *p_arg);

/*
*********************************************************************************************************
*                                           CALLBACK FUNCITON
*********************************************************************************************************
*/
void onGetData(pRX_MAC sender,pRB_BYTE pCurChar,INT16U bytesCnt){
  // 因爲發現幀頭後才掛載事件,所以下一次回掉正好是說明字符數的那個字符,否則還得根據bytesCnt來判斷當前位置
  RxMac_SetOnFeeded(sender,NULL);
  if(*pCurChar > '0' && *pCurChar <= '9' ){
    // bytesCnt是當前收到了多少個字符,所以接收區大小爲當前字符數加上還要接收的
    RxMac_SetRxSize(sender,*pCurChar - '0' + bytesCnt);
  }
}
void onFlushed(pRX_MAC sender,pRB_BYTE pBuf,INT16U len,RX_STATE state,pRX_FLAG pHorU,pRX_FLAG pEnder){
  SCI_PutCharsB(SCI0,"\nFlushed:",9,0);
  if(state.headerFound)
    SCI_PutCharsB(SCI0,"headerFound,",12,0);
  if(state.enderFound)
    SCI_PutCharsB(SCI0,"enderFound,",11,0);
  if(state.isFull)
    SCI_PutCharsB(SCI0,"full,",5,0);
  if(state.uniqueFound)
    SCI_PutCharsB(SCI0,"unique,",7,0);
  SCI_PutCharsB(SCI0,"\nDatas:",7,0);
  SCI_PutCharsB(SCI0,pBuf,len,0);
  SCI_PutCharsB(SCI0,"\n",1,0);
  RxMac_ResetRxSize(sender);
}
void onGetHeader(pRX_MAC sender,pRX_FLAG pFlag){
  SCI_PutCharsB(SCI0,"\nFoundHeader:",13,0);
  SCI_PutCharsB(SCI0,pFlag->pBuf,pFlag->len,0);
  SCI_PutCharsB(SCI0,"\n",1,0);
}
void onGetHeader2(pRX_MAC sender,pRX_FLAG pFlag){
  SCI_PutCharsB(SCI0,"\nFoundHeader:",13,0);
  SCI_PutCharsB(SCI0,pFlag->pBuf,pFlag->len,0);
  SCI_PutCharsB(SCI0,"\n",1,0);
  RxMac_SetOnFeeded(sender,onGetData);
}
/*
*********************************************************************************************************
*                                           FLAGS
*********************************************************************************************************
*/
RX_MAC  _rxmac;
#define BUF_SIZE  20
INT8U  buffer[BUF_SIZE];
RX_FLAG flags[4];

// 協議示例1:
// 幀頭    :HEADER 或者 START
// 強幀尾  :END
// 強特殊串:12345  
// 
static void protocol1_init(){
  RX_FLAG_INIT(&flags[0],"HEADER",6,FLAG_OPTION_HEADER);
  RX_FLAG_INIT(&flags[1],"START",5,FLAG_OPTION_HEADER);
  RX_FLAG_INIT(&flags[2],"END",3,FLAG_OPTION_STRONG_ENDER);
  RX_FLAG_INIT(&flags[3],"12345",5,FLAG_OPTION_STRONG_UNIQUE);
  RxMac_Init(&_rxmac,flags,4,6,buffer,BUF_SIZE,NULL,onGetHeader,onFlushed);
}
// 協議示例2:
// 幀頭    : START
// 幀頭後的第1個字符表示後面還要接收多少個字符 1-9,'4'表示4個,如果不是數字或爲'0',則等待幀尾
// 幀尾    : END
// 特殊串:  NOW
// 
static void protocol2_init(){
  RX_FLAG_INIT(&flags[0],"START",5,FLAG_OPTION_HEADER);
  RX_FLAG_INIT(&flags[1],"END",3,FLAG_OPTION_ENDER);
  RX_FLAG_INIT(&flags[2],"NOW",3,FLAG_OPTION_UNIQUE);
  RxMac_Init(&_rxmac,flags,3,5,buffer,BUF_SIZE,NULL,onGetHeader2,onFlushed);
}

/*
*********************************************************************************************************
*                                           MAIN FUNCTION
*********************************************************************************************************
*/


void main(void) {
    INT8U  err;    
    BSP_IntDisAll();                                                    /* Disable ALL interrupts to the interrupt controller       */
    OSInit();                                                           /* Initialize uC/OS-II                                      */

    err = OSTaskCreate(AppTaskStart,
                          NULL,
                          (OS_STK *)&AppTaskStartStk[APP_TASK_START_STK_SIZE - 1],
                          APP_TASK_START_PRIO);                     
    OSStart();
}

static  void  AppTaskStart (void *p_arg)
{
  INT8U c,err;
  (void)p_arg;                      /* Prevent compiler warning  */
  BSP_Init(); 
  SCI_Init(SCI0);
  SCI_EnableTrans(SCI0);
  SCI_EnableRecv(SCI0);
  SCI_EnableRxInt(SCI0);
  SCI_BufferInit();
  // 選擇想要實驗的協議來初始化
  protocol1_init();
  //protocol2_init();
  while (DEF_TRUE) 
  {
    // 獲取下一個字符
    c = SCI_GetCharB(SCI0,0,&err);
    // 回顯
    SCI_PutCharB(SCI0,c,0);
    // 餵給接收機
    RxMac_FeedData(&_rxmac,c);
  }
}

示例協議1測試結果

示例協議2測試結果

可以看到,第二個協議中我們通過改變緩衝區大小成功控制了數據包的長度。

雖然這裏的示例都是基於ASCII的,但這只是爲了觀察起來方便,普通的基於二進制的協議也是可以使用這個模塊的。

後記

此模塊已經通過單元測試,但難保不會有一些未發現的bug,如果使用中發現任何問題或者有任何建議意見請立刻聯繫我,謝謝。

更新歷史

2018/05/29 V1.0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章