前言
环形队列的理论知识网上有很多文章,这里我就仅通过代码分享一下使用经验,在我的驱动中,队列内容是一包一包的待处理数据包,而不是一个个的字节。
一、构建环形队列结构体
队列中头部位置和尾部位置的指示是必须的,另外为了使用方便,加入数据包计数cnt,写入一包数据时cnt++,读取一包数据时cnt–,需要判断队列空或满时,就看cnt是否为0或最大值。
*注意:因为在驱动中,队列中放置的是一包一包的待处理数据包,所以又加入了每个数据包的有效长度lenth,读取到数据包后,还返回此包的数据长度。
首先需要定义队列大小:队列中最多存放多少数据包、单包的最大长度
//队列中数据包个数最大值:64包
#define Queue_Packet_MaxNumber 64
//队列中单包最大长度
#define Queue_Packet_MaxLenth 256
//定义队列结构体
typedef struct{
int front; //队列当前头位置
int rear; //队列当前尾位置
int counter; //队列数据包计数
uint8_t data[Queue_Packet_MaxNumber][Queue_Packet_MaxLenth]; //存放数据包内容
uint16_t lenth[Queue_Packet_MaxNumber]; //数据包对应的长度
}xQueue_Typedef;
二、队列初始化
1.定义一个队列
//串口接收队列
xQueue_Typedef Usart_Receive_Queue;
//初始化一个队列
void xQueue_Init(xQueue_Typedef *Queue)
{
Queue->front=0;
Queue->rear=0;
Queue->counter=0;
memset(&Queue->data[0][0],0,Queue_Packet_MaxLenth*Queue_Packet_MaxNumber);
}
2.队列初始化,调用上面的函数即可,会把传入的队列清空
xQueue_Init(&Usart_Receive_Queue);
三、读写数据
1、队列满判断
读取队列内容前,要先判断队列是否为空,如果为空,读取失败。
队列写入数据前,要先判断队列是否已满,如果已满,写入失败。
//队列满判断
//返回:1:队列已满、0:队列未满
uint8_t xQueue_FullJudge(xQueue_Typedef *Queue)
{
//队列中数据包个数已为最大值,队列已满
if(Queue->counter==Queue_Packet_MaxNumber)
{
return 1;
}else
{
return 0;
}
}
2、队列空判断
//队列空判断
//返回:1:队列已空、0:队列未空
uint8_t xQueue_EmptyJudge(xQueue_Typedef *Queue)
{
if(Queue->counter==0)
{
return 1;
}else
{
return 0;
}
}
3、队列写入数据
//存入一包数据
//返回0:队列已满,写入失败、返回1:写入成功
//函数内部不判断写入的长度是否会超过单包最大值,调用函数前应该自行判断
uint8_t xQueue_WriteData(xQueue_Typedef *Queue,uint8_t *Data,uint16_t Lenth)
{
//判断是否为满
if(xQueue_FullJudge(Queue))
{
return 0;
}else
{
//队列当前数据量++
Queue->counter++;
//写入一包数据
memcpy(&Queue->data[Queue->rear][0],&Data[0],Lenth);
//记录此包数据的长度
Queue->lenth[Queue->rear]=Lenth;
//包尾位置++
Queue->rear=(Queue->rear+1)%Queue_Packet_MaxNumber;
return 1;
}
}
4、队列读取数据
//读出一包数据
//返回0:队列为空,读取失败、返回非0:读取的数据包长度
uint16_t xQueue_ReadData(xQueue_Typedef *Queue,uint8_t *Data)
{
uint16_t Lenth;
//判断是否为空
if(xQueue_EmptyJudge(Queue))
{
return 0;
}else
{
//当前数据量--
Queue->counter--;
//此包长度
Lenth=Queue->lenth[Queue->front];
//读出一包数据
memcpy(&Data[0],&Queue->data[Queue->front][0],Lenth);
//包头位置++
Queue->front=(Queue->front+1)%Queue_Packet_MaxNumber;
return Lenth;
}
}
四、实际使用
后续有时间了更新一篇STM32串口使用总结:CubeMX配置、接收中断逐字节处理、空闲中断整包处理、空闲中断+DMA接收、串口阻塞发送与DMA发送等等
//定义队列
xQueue_Typedef Usart_Receive_Queue;
int main()
{
/* code begin*/
//数据队列初始化
xQueue_Init(&Usart_Receive_Queue);
/* code end*/
return 0;
}
//串口1中断服务程序
void USART1_IRQHandler(void)
{
HAL_UART_IRQHandler(&UART1_Handler);
//空闲中断到
if(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_IDLE) != RESET)
{
//清中断
__HAL_UART_CLEAR_IDLEFLAG(&UART1_Handler);
//DMA停止
HAL_UART_DMAStop(&UART1_Handler);
//获取数据包长度
Receive_Frame_Len_Usart=RECEIVE_MAX_LENTH-__HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
//写入队列,Receive_Buff_Usart是接收的缓冲区,写入队列时没有判断长度
xQueue_WriteData(&Usart_Receive_Queue,Receive_Buff_Usart,Receive_Frame_Len_Usart);
//重新指定DMA接收的缓冲区
HAL_UART_Receive_DMA(&UART1_Handler,Receive_Buff_Usart,RECEIVE_MAX_LENTH);
}
}
//处理数据
int UnpackModule_Periodic_Handle()
{
uint8_t Data[Queue_Packet_MaxLenth];
uint16_t Lenth;
/*code begin*/
//读取队列中的数据
Lenth=xQueue_ReadData(&Usart_Receive_Queue,Data);
//长度非0,内容有效
if(Lenth)
{
//判断包头包尾
if(Data[0] != 0x55 || Data[Lenth-] != 0xAA)
{
return 0;
}
switch(Data[2])
{
default:break;
}
}
/* code end*/
return 0;
}