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的幂。
-
单片机
+关注
关注
6044文章
44634浏览量
639657 -
缓冲区
+关注
关注
0文章
33浏览量
9185 -
程序
+关注
关注
117文章
3799浏览量
81533 -
uart
+关注
关注
22文章
1245浏览量
101913 -
串口通信
+关注
关注
34文章
1628浏览量
55814
发布评论请先 登录
相关推荐
STM32进阶之串口环形缓冲区实现
MCU进阶之串口环形缓冲区实现
STM32串口环形缓冲区的实现
环形缓冲区的设计分享!
请问串口的DMA接收缓冲区是不是环形缓冲区
rtt的环形缓冲区读完就丢弃了?
环形缓冲区读写操作的分析与实现
缓冲区是啥意思 STM32串口数据接收之环形缓冲区
STM32串口数据接收 --环形缓冲区

评论