单片机程序开发一般都会用到UART串口通信,通过通信来实现上位机和单片机程序的数据交互。通信中为了实现正常的收发,一般都会有对应的发送和接收缓存来暂存通信数据。这里使用环形缓冲区的方式来设计数据收发的缓存,即缓冲区溢出后,从缓冲区数组的起始索引处重新进行数据的存储,这样可以比较高效地使用缓冲区。
核心思路摘抄如下。规定以下所有方案,在缓冲区满时不可再写入数据,缓冲区空时不能读数据。
常规数组环形缓冲区思路:
设缓冲区大小为N,队头out,队尾in,out、in均是下标表示。
改进版数组环形缓冲区思路:
同样假设缓冲区大小为N,队头out,队尾in,out、in为数组下标,但数据类型为unsigned int。
本文对应的工程代码链接如下。该工程基于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的幂。
全部0条评论
快来发表一下你的评论吧 !