1.1 環形緩沖區的實現思路
單片機程序開發一般都會用到UART串口通信,通過通信來實現上位機和單片機程序的數據交互。通信中為了實現正常的收發,一般都會有對應的發送和接收緩存來暫存通信數據。這里使用環形緩沖區的方式來設計數據收發的緩存,即緩沖區溢出后,從緩沖區數組的起始索引處重新進行數據的存儲,這樣可以比較高效地使用緩沖區。
核心思路摘抄如下。規定以下所有方案,在緩沖區滿時不可再寫入數據,緩沖區空時不能讀數據。
常規數組環形緩沖區思路:
設緩沖區大小為N,隊頭out,隊尾in,out、in均是下標表示。
- 初始時,in = out = 0
- 隊頭隊尾的更新用取模操作,out = (out + 1) % N,in = (in + 1) % N
- out == in表示緩沖區空,(in + 1) % N == out表示緩沖區滿
- 入隊que[in] = value; in = (in + 1) % N;
- 出隊ret = que[out]; out = (out + 1) % N;
- 數據長度 len = (in - out + N) % N
改進版數組環形緩沖區思路:
同樣假設緩沖區大小為N,隊頭out,隊尾in,out、in為數組下標,但數據類型為unsigned int。
- 初始時,in = out = 0
- 上調緩沖區大小N為2的冪,假設為M
- 隊頭隊尾更新不再取模,直接++out,++in
- out == in表示緩沖區空,(in - out) == M表示緩沖區滿
- 入隊que[in & (M - 1)] = value; ++in;
- 出隊ret = que[out & (M - 1)] ; ++out;
- in - out表示數據長度
1.2 環形緩沖區的代碼實現
本文對應的工程代碼鏈接如下。該工程基于eclipse IDE開發,編譯器使用arm-none-eabi-gcc,使用的硬件是STM32F429I-DISCO開發板。
https://download.csdn.net/download/goodrenze/85163032
根據以上的環形緩沖區設計思路,先定義緩存對應的結構體類型如下。
typedef struct _UartBuf_t
{
#if UART_RECORD_LOST_NUM
uint32_t TxLostNum;
#endif
uint8_t* TxBuf;
#if UART_BUF_SIZE_IS_2POW
uint16_t TxIn;
uint16_t TxOut;
uint16_t TxSize;
#else
int16_t TxIn;
int16_t TxOut;
int16_t TxSize;
#endif
#if UART_RECORD_LOST_NUM
uint32_t RxLostNum;
#endif
uint8_t* RxBuf;
#if UART_BUF_SIZE_IS_2POW
uint16_t RxIn;
uint16_t RxOut;
uint16_t RxSize;
#else
int16_t RxIn;
int16_t RxOut;
int16_t RxSize;
#endif
}UartBuf_t;
以上結構體中,UART_RECORD_LOST_NUM宏定義用于設置是否記錄丟失的數據個數,UART_BUF_SIZE_IS_2POW宏定義用于設置收發緩存的長度是否是2的冪,如果緩存長度是2的冪,則緩存索引和長度使用無符號數,否則使用有符號數。TxBuf和RxBuf指針用于指向對應的發送和接收的緩存數組。
本例程的UART串口發送和接收都是用串口中斷來實現的,串口中斷處理函數的代碼實現如下,只需要在對應的串口中斷入口函數中調用該函數進行串口數據的收發處理即可。
// 以下環形緩沖區的設計思路參考以下鏈接:
// https://www.cnblogs.com/zengzy/p/5139582.html
void UartIrqService(USART_TypeDef* UartX, UartBuf_t* Buf)
{
uint32_t SR = UartX->SR;
uint32_t CR1 = UartX->CR1;
uint32_t CR3 = UartX->CR3;
UNUSED(CR3);
while(SR & USART_SR_RXNE)
{
#if UART_RECORD_LOST_NUM
if(SR & USART_SR_ORE)
{
Buf->RxLostNum++;
}
#endif
#if UART_BUF_SIZE_IS_2POW
if ((Buf->RxIn - Buf->RxOut) != Buf->RxSize)
{
Buf->RxBuf[Buf->RxIn & (Buf->RxSize - 1)] = (uint8_t)(UartX->DR & (uint8_t)0x00FF);
Buf->RxIn++;
}
#else
if (((Buf->RxIn + 1) % Buf->RxSize) != Buf->RxOut)
{
Buf->RxBuf[Buf->RxIn++] = (uint8_t)(UartX->DR & (uint8_t)0x00FF);
Buf->RxIn %= Buf->RxSize;
}
#endif
else
{
Buf->RxBuf[Buf->RxIn] = (uint8_t)(UartX->DR & (uint8_t)0x00FF);
#if UART_RECORD_LOST_NUM
Buf->RxLostNum++;
#endif
}
SR = UartX->SR;
}
if((SR & USART_SR_TXE) && (CR1 & USART_CR1_TXEIE))
{
if(Buf->TxIn != Buf->TxOut)
{
#if UART_BUF_SIZE_IS_2POW
UartX->DR = (uint8_t)(Buf->TxBuf[Buf->TxOut & (Buf->TxSize - 1)] & (uint8_t)0x00FF);
Buf->TxOut++;
#else
UartX->DR = (uint8_t)(Buf->TxBuf[Buf->TxOut++] & (uint8_t)0x00FF);
Buf->TxOut %= Buf->TxSize;
#endif
}
else
{
CLEAR_BIT(UartX->CR1, USART_CR1_TXEIE);
}
}
}
以上代碼就是上面提到的環形緩沖區思路的具體實現。當緩沖區的數據長度是2的冪的時候,可以省去求余的運算,可以提高代碼的執行速度。所以如果要求代碼的執行時間盡量短,可以考慮將緩沖區的長度設置成2的冪。
-
單片機
+關注
關注
6063文章
44915瀏覽量
646912 -
緩沖區
+關注
關注
0文章
36瀏覽量
9316 -
程序
+關注
關注
117文章
3823瀏覽量
82403 -
uart
+關注
關注
22文章
1267瀏覽量
103245 -
串口通信
+關注
關注
34文章
1635瀏覽量
56537
發布評論請先 登錄
STM32進階之串口環形緩沖區實現
MCU進階之串口環形緩沖區實現
STM32串口環形緩沖區的實現
環形緩沖區的設計分享!
請問串口的DMA接收緩沖區是不是環形緩沖區
rtt的環形緩沖區讀完就丟棄了?
環形緩沖區讀寫操作的分析與實現
緩沖區是啥意思 STM32串口數據接收之環形緩沖區
STM32串口數據接收 --環形緩沖區

評論