最近在写can通讯的代码,发现在CAN通讯数据量比较大的时候,由于命令不能及时的处理导致部分CAN命令的丢失。后来查询资料发现可以采用队列的方式对CAN命令的传输做一个缓冲处理,这样可以在通讯量大的时候也能保证命令不丢失。以下就是我在学习队列过程中的方法和如何运用到CAN上。
队列的介绍
队列就像一个单车道的隧道,来的车辆都要一个一个的通过,先来的车辆优先通过隧道,后来的车辆也就
后通过。整个过程用专业的词就是先入先出(FIFO)的原理。
就像上图所示的一样,队列头相当于隧道的出口,队列尾相当于隧道的入口。来的车辆从队尾进入队头出去。
队列的实现(C语言)
下面就直接附上代码进行分析
先是入队函数
#define MAX_DATA_NUM 80 //队列最大的深度
int massage_data[80]={0}; //缓冲区
int fornt=0; //出队的个数
int rear=0; //记录入队的个数
int mark=0; //缓冲区填满次数
/* 入队函数 */
int data_push(int *massage,int data)
{
massage[rear] = data;
rear ++;
if(rear >= MAX_DATA_NUM){
rear -= MAX_DATA_NUM;
mark++;
}
}
在上述的代码中,rear就相当于隧道入口处的监控,记录着进入隧道的车辆数量。而massage就相当于是用来记录车辆车牌号的一张表格,表格的顺序按照rear来,而车牌号就是data。if后面的语句主要是用来防止队列的丢失。MAX_DATA_NUM 值的设定需要根据实际的使用环境来设定,该值设定的过小,若数据读取的速度过慢,很容易导致数据缓冲区溢出,从而导致数据丢失。若该值设定的过大,则会浪费内存空间。
出队函数
int data_pop(int *massage)
{
if((fornt == rear)&&(mark == 0)){
printf("\r\n队列元素已经全部出栈\r\n");
return 0;
}
printf("%2d ",massage[fornt]);
fornt ++;
if(fornt >= MAX_DATA_NUM){
fornt -= MAX_DATA_NUM;
mark--;
}
return 1;
}
出队函数就相当于是隧道出口的监控,当一辆车从出口驶出,则front加一,用来记录出隧道的车辆数量,同时去表格上查找对应编号的车辆信息。并输出通过入口时对应的车牌号。
主函数
int main()
{
int i,j=1;
for(i=0;i<40;i++){
data_push(massage_data,i);
}
for(i=0;i<80;i++){
j = data_pop(massage_data);
if(j == 0)
break;
}
return 0;
}
队列在CAN中的实现代码
以下的代码是在stm32F205中实现的:
#define MAX_MASSAGE 80
volatile uint32_t msg_rptr = 0, msg_wptr = 0;
MASSAGE_TYPE MsgBuff[MAX_MASSAGE];
/*
* 函数功能:消息传输计数
*/
int Massage_Count(void)
{
int Count = msg_wptr;
Count += MAX_MASSAGE;
Count -= msg_rptr;
if(Count >= MAX_MASSAGE)
Count -= MAX_MASSAGE;
return Count;
}
/*
* CAN消息数据出列
*/
MASSAGE_TYPE *Massage_Pop(void)
{
if(Massage_Count() > 0) {
int rptr = msg_rptr;
msg_rptr ++;
if(msg_rptr >= MAX_MASSAGE) {
msg_rptr -= MAX_MASSAGE;
}
return &MsgBuff[rptr];
}
return NULL;
}
总结
队列的使用不仅仅是在CAN通讯上,在生活中,队列的运用无处不还在,像医院的排队叫号,食堂排队打饭等等。
在一些网络通信中,还有对单纯队列改进后的链式队列。
经过以上的简单学习,并运用到实际的项目中,感觉学习数据结构和算法不能单纯的仅仅是学习,而是应该考
虑在实际的项目中应该如何去使用它,并且思考在哪些方面可以使用,以便于以后遇到的时候可以及时的想起来。