您好,欢迎访问三七文档
当前位置:首页 > 电子/通信 > 综合/其它 > 单片机数据接收缓冲流的设计与实现
由一个串口接收数据引发的问题与字节缓冲流系统的设计在一个wifi数据收发项目调试时发现,数据在高速连续发送和接收时,经常出现数据出现了丢失和系统的死机。单片机在接收串口数据时,传统采用中断方法或者查询指定标志位方法接收数据。查询指定标志位:这种方式通常在main函数的大循环中不断的检测标志位或者等待该标志位来判断是否有数据接收。通常有两种方式:1:在大循环中if(标志位成立)表明有数据接收然后进行数据的处理。优点:不会引起整个main函数线程的阻塞;在简单的数据接收项目中可以使用缺点:单片机一般都为单线程,复杂的控制中采用操作系统,例如UC/OS;所以,将所以都函数放在main函数大循环中进行轮番处理。整个循环周期时间不确定,其他任务函数可能发生阻塞,不能够保证数据到来时,正好在执行检测指定标志位,从而出现了数据丢失。2:在大循环中while(标志位);通过while来等待数据的到来。优点:数据不会出现丢失,稳定。缺点:整个main函数主线程出现堵塞,其他函数无法执行,上述所示。显然:以上两种发送在复杂的控制系统中是不能采用的,因此:在没有多任务操作系统时,数据的接收采用中断接收的法式是最佳的。使用中断,可以不用查询和等待的方式接收数据,解决了许多问题。,此时,单片机可以说是多线程执行程序。main函数是一个线程,中断服务子程序是一个线程。中断是前台,main函数是后台。由于是多线程(一般而言),不得不考虑数据的安全性。中断可能随时到来。Main函数会随时被打断,程序计数器寄存器PC指针指向中断函数入口地址,指向中断函数。Main函数在处理数据时被打断,可能会引发数据的丢失。共同访问全局变量时,使用互斥信号量等一些手段保障数据不被修改。设计可能被中断打断的函数时,要注意函数的重入问题,像static等关键字。字节接收缓冲系统设计的核心思想:1:前台(即中断)负责接收数据,并不进行处理,将数据放入消息队列中。2:后台(main函数)负责从消息队列中取出消息,并处理。3:整个接收系统核心为队列,可以当做缓冲区;遵循先进先出原则FIFO采用队列方式接收数据比较简单,并且实现了缓冲,不会出现数据的丢失。消息队列核心算法实现:1:消息队列核心数据结构:typedefstructQueue{unsignedcharfront;//队列头索引unsignedcharrear;//队列尾索引unsignedchar*pArray;//简易的队列指向数组}QueueTypeDef;2:判断队列是否为满伪算法if((rear+1)%数组的元素个数)==front)3:判断队列是否为空伪算法if(rear==front)4:将数据加入队列伪算法if(队列不为满){pArray[rear]=数据;rear=(rear+1)%数组的长度}5:将数据从队列中取出伪算法if(队列不为空){Val=pArray[front];front=(front+1)%数组长度}以上是接收最简单的一个字节的队列;ASCIIC编译通过不依赖于单片机;将其加入中断服务子程序中,把接收的数据加入队列中;以stm32单片机串口中断为例:voidUSART2_IRQHandler(void){if(USART_GetITStatus(USART2,USART_IT_RXNE)==SET){USART_ClearITPendingBit(USART2,USART_IT_RXNE);en_queue(&Queue,(uint8_t)USART_ReceiveData(USART2));//将数据加入消息队列中}if(USART_GetFlagStatus(USART2,USART_FLAG_ORE)==SET){USART_ClearFlag(USART2,USART_FLAG_ORE);USART_ReceiveData(USART2);}}Main函数从消息队列中取出数据unsignedcharval;while(1){if(out_queue(&Queue,&val))//从队列中取出数据{if(i==16)i=0;LCD_print(1,i,val);//显示取出的数据i++;}….…..//其他任务…….}以上算法思路是以接收最简单的一个字节为例:当然可以接收更复杂的数据,数据结构如下typedefstructMessage//消息数据结构{u8clientID;//客户端名u8messgeLength;//消息长度u8message_str[MessageSize];//存放消息的数组}MessageTypeDef;typedefstructQueue//消息队列数据结构{u8front;//队列头u8rear;//队列尾+1MessageTypeDefmessage[MessQueueSize];//消息BOOL(*postMessage)(MessageTypeDefdat);//消息进列BOOL(*getMessage)(MessageTypeDef*datAddr);//消息出列}MessQueue;MessageTypeDef;这个数据结构中构造了接收数据的格式并不是前面最简单的一个字节,根据实际接收数据的需要来构造数据结构,当然在中断函数中要进行数据的处理,也可以放在主函数中处理数据,中断中依然是将字节放入消息队列中。主函数处理完数据后在放入另一个消息队列中,由其他函数处理数据,多级消息队列。以下是带特定格式的消息数据处理:buff[buff_index]=USART_ReceiveData(USART2);//将接收的数据(1个字节)放入缓冲区////if(buff[0]==0x2B)//校验数据头//{//buff_index++;//}//else//{//buff_index=0;//}////if(buff_index==8)//获取数据尾//{//length=(buff[7]-0x30)+1+buff_index;//计算数据尾索引//}////if(buff_index==length)//{//length=200;//buff_index=0;//receive=TRUE;//数据接收完成////DISABLE_WIFI_RX_IRQ();////}附:字节缓冲流系统源码:文件:queue.h#ifndef__QUEUE_H__#define__QUEUE_H__#ifndefbool#defineboolunsignedchar#definetrue1#definefalse0#endif#defineQueueArraySize32//队列长度(字节)typedefstructQueue{unsignedcharfront;//队列头unsignedcharrear;//队列尾+1unsignedchar*pArray;//指向字节数组}QueueTypeDef;externQueueTypeDefQueue;externunsignedcharqueueArray[QueueArraySize];voidqueue_Init(QueueTypeDef*pQ,unsignedchar*array);//初始化boolfull_queue(QueueTypeDef*pQ);//满boolemput_queue(QueueTypeDef*pQ);//空boolen_queue(QueueTypeDef*pQ,unsignedcharval);//入队列boolout_queue(QueueTypeDef*pQ,unsignedchar*dat);//出队列#endif文件:queue.c#includequeue.hQueueTypeDefQueue;unsignedcharqueueArray[QueueArraySize];voidqueue_Init(QueueTypeDef*pQ,unsignedchar*array){Queue.front;Queue.rear;pQ-pArray=array;pQ-front=0;pQ-rear=0;}boolfull_queue(QueueTypeDef*pQ){if((pQ-rear+1)%QueueArraySize==pQ-front)returntrue;elsereturnfalse;}boolemput_queue(QueueTypeDef*pQ){if(pQ-front==pQ-rear)returntrue;elsereturnfalse;}boolen_queue(QueueTypeDef*pQ,unsignedcharval){if(full_queue(pQ)){returnfalse;}else{*((pQ-pArray)+(pQ-rear))=val;//pQ-pArray[pQ-rear]=val;pQ-rear=(pQ-rear+1)%QueueArraySize;returntrue;}}boolout_queue(QueueTypeDef*pQ,unsignedchar*dat){if(emput_queue(pQ)){returnfalse;}else{*dat=pQ-pArray[pQ-front];pQ-front=(pQ-front+1)%QueueArraySize;returntrue;}}附:复杂数据接收缓冲流实现1:文件:queue.h#ifndef__QUEUE_H__#define__QUEUE_H__#ifndefBOOL#defineBOOLunsignedchar#defineTRUE1#defineFALSE0#endif#defineMessageSize10//消息长度(字节)#defineMessQueueSize20//队列长度sizeof(MessageTypeDef)typedefunsignedcharu8;typedefunsignedintu16;typedefstructMessage//消息数据结构{u8clientID;//客户端名u8messgeLength;//消息长度u8message_str[MessageSize];//存放消息的数组}MessageTypeDef;typedefstructQueue//消息队列数据结构{u8front;//队列头u8rear;//队列尾+1MessageTypeDefmessage[MessQueueSize];//消息BOOL(*postMessage)(MessageTypeDefdat);//消息进列BOOL(*getMessage)(MessageTypeDef*datAddr);//消息出列}MessQueue;externMessQueuemess_queue;voidMessageQueueInit(void);//初始化BOOLfull_queue(void);//判断是否为满BOOLemput_queue(void);//判断是否为空BOOLen_queue(MessageTypeDefmessage);//入列BOOLout_queue(MessageTypeDef*message);//出列#endif2:文件:queue.c#includequeue1.hMessQueuemess_queue;//定义消息队列voidMessageQueueInit(void)//初始化{mess_queue.front=0;mess_queue.rear=0;mess_queue.postMessage=en_queue;mess_queue.getMessage=out_queue;}BOOLfull_queue(void)//判断队列是否为满{if((mess_queue.rear+1)%MessQueueSize==mess_queue.front)//rear+1=front{returnTRU
本文标题:单片机数据接收缓冲流的设计与实现
链接地址:https://www.777doc.com/doc-2594475 .html