C 從環形緩衝區提取幀資料

2021-10-25 02:24:37 字數 2942 閱讀 3368

a作為主機(上位機軟體),b作為從機(stm32)硬體和電機連線,a和b之間通過串列埠通訊,使a能間接的控制電機正反轉,速度等。

協議初定:

問題一:怎麼判斷幀資料開始資訊?

通過定義乙個固定的幀頭,作為資料開始訊號。記為0x01。

問題二:如何判斷幀的長度?
幀的長度可以選擇固定長度或可變長度。固定長度邏輯簡單,檢測到幀頭後,往後讀取固定的長度即可。可變長度靈活度高,一幀資料可以攜帶更多的資訊量。那麼可以在幀頭後增加乙個位元組作為長度,記為0***,以便從機判斷幀長。

問題三:資料格式?
資料格式按需編寫,這裡用16bit功能+16bit資料的方式。一組資料共4個位元組。

問題四:如何校驗幀完整性?
可以用兩種方式:

1、增加幀尾作為資料結束標誌,驗證幀尾就知道資料是否完整。但是存在資料=幀尾、通訊異常的情況,這時候可能提取到了錯誤的資料。

2、採用通用校驗方式,工業控制一般使用crc16-modbus。這裡增加兩個位元組作為檢驗碼。這種方式可以保證資料不會出錯。

至此:我們可以將通訊協議制定下來:

幀頭幀長

功能1資料1

功能2資料2

功能…資料…

crc16高位

crc16低位

0x01

len0***

0***

0***

0***

0***

0***

crc_h

crc_l

在理想狀態下,從機在串列埠中斷裡面判斷幀頭,然後迴圈往後讀取len長度的位元組到陣列裡面去,最後驗證一下crc是否一致即可。

但實際使用往往面對更複雜的情形:

這個問題其實就是要解決兩件事情:什麼時候接收資料?什麼時候處理資料?

假設提取資料的函式為:userextractframedata();該函式執行時間為1us。

那麼由簡入繁看看幾種方式:

1、中斷接收中斷處理。(弊端太明顯不再贅述)

2、中斷儲存到buff,中斷外處理buff,處理完後buff清空。

uint8_t recvbuff[50]

;uint8_t recvindex=0;

void

usart1_irqhandler

(void

)//串列埠1中斷服務函式

}void

userextractframedata()

}

當傳輸速率》userextractframedata()處理速度時,必然會導致丟資料。因為recvindex被清0了,這篇文章重點不是這個,這裡不再贅述。

3、使用環形緩衝區。

環形緩衝區原理網上能找到很多,具體實現方式可以看這裡 c環形緩衝區實現

使用環形緩衝區就可以很好的解決這個問題,將接收和處理徹底解耦,接收資料自動填入緩衝區,處理資料自動從緩衝區讀取資料。當瞬間傳輸速率很大時,資料被儲存到緩衝區不會丟失,等到傳輸空閒時就有足夠的時間去處理。

void

usart1_irqhandler

(void

)//串列埠1中斷服務程式

}void

userextractframedata()

}

至此通過crc和緩衝區,我們已經可以保證資料的完整性和正確性了,只需要從緩衝區拿資料出來比對協議就可以了。

那麼接下來再分析一下更加複雜的情形:

問題六:假如主機採用的是一對多的方式,那麼必然有大量的無效資料和類似資料,如何處理?

處理方式有很多,這裡推薦一種位元組流的處理方式。

處理思路:從緩衝區複製乙個位元組,如果是幀頭,繼續複製乙個位元組判斷長度n,然後繼續往後複製乙個位元組,判斷是否是正確的功能碼,再然後繼續往後複製n個位元組,直到判斷crc是否通過。只要遇到錯誤就讀取幀頭不處理即可。

無論主機傳送多少資料,記憶體消耗只是環形緩衝區大小+有效資料大小。未檢測到幀頭前,處理速度》傳輸速率,所以環形緩衝區的大小可以不用設很大。但是有一種極端情況,就是主機傳送的資料含有大量的幀頭,且幀頭後的功能碼檢測也是通過的,那就沒救了,只能增大緩衝區大小。

經過驗證可以高效率提取有效資料。3kb位元組中提取最長32byte的有效資料,時間消耗100ms~220ms左右,主要是傳輸過程占用的時間。記憶體消耗僅100+32byte。

因為是給別人做的專案,這部分**就不貼了~

問題七:假設資料長度較長,傳輸時間》1us,傳輸資料到一半時,進入了處理資料的函式,處理函式由於沒有接收到len長度的資料而判斷為資料不完整錯誤,如何處理?
這個問題很好解決,我們可以再增加乙個緩衝區pbuff,先將資料搬到pbuff,直到接收到了len長度的資料,再去處理pbuff即可。

但這不是最好的解決方法,假如資料大小是1kb,那麼環形緩衝區大小需要》1kb,再來乙個pbuff也需要1kb記憶體,增加了一倍記憶體消耗。

因此在c環形緩衝區實現這裡,我增加兩個介面

uint8_t ringbuffcopybyte(uint8_t *pdata);

uint8_t ringbuffcopydata(uint8_t len, uint8_t *pdata);

處理函式將環形緩衝區資料複製到內部進行處理,處理結束後才會讀掉n個位元組,缺點是程式執行時間有所增大。

c 環形緩衝區

public class circularbuffer icollection,ienumerable,icollection,ienumerable public circularbuffer int capacity,bool allowoverflow public bool allowove...

環形緩衝區

include include include include include define buffsize 1024 1024 define min x,y x y x y pthread mutex t lock pthread mutex initializer struct cycle b...

環形緩衝區

define print buf size 16 1024 static char g pcnetprintbuf 環形緩衝區的首位址 static int g ireadpos 0 環形緩衝區讀的位置 static int g iwritepos 0 環形緩衝區寫的位置 intinit 判斷環形緩...