上一篇文章介绍了串口发送数据的优化方法,使用中断的方式发送数据可以提高系统实时性。这次介绍串口接收数据的方法。新的数据接收方法结合了uCOS-III和循环队列,有较好的实时性。同时,使用STM32的总线空闲中断判断数据包接收完毕,使用状态机检查数据包正误。
配置USART时要使能总线空闲中断,当MCU检测到串口总线上有一个字节的时间没有接收数据时便触发中断。在中断处理函数中必须软件清除中断标志位才能避免反复进入空闲中断,具体方法为先读USART_SR,然后读USART_DR。示例代码如下:
void USARTInit(void)
{
/*
...
省略部分串口配置代码
*/
USART_ITConfig(USART1, USART_IT_RXNE,ENABLE);//开启数据寄存器非空中断
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //开启总线空闲中断
USART_Cmd(USART1,ENABLE);
}
void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET)
{
ch=USART1->SR;
ch=USART1->DR;//清除总线空闲标志
}
}
程序因串口数据寄存器非空进入中断时,读取串口接收的字节并存入循环队列,开始记录数据包字节数。当程序因串口总线空闲进入中断时,发布(OSTaskQPost())数据包首地址及数据包大小到数据处理任务(状态机)的消息队列中,记录当前循环队列写指针(RXqueue.pwrite)的值作为下一个数据包的首地址,清零数据包字节计数变量。
void USART_IRQHandler(void)
{
unsigned char ch;
OS_ERR err;
static unsigned char* p_write=RXqueue.pwrite;
static OS_MSG_SIZE rxcnt=0;
OSIntEnter();
if(USART_GetITStatus(USART1,USART_IT_RXNE)==SET)
{
ch=USART1->DR;
if(RXqueue.FullCheck()==0)
{
*RXqueue.pwrite=ch;
if(RXqueue.pwrite==&RXqueue.arr[QUEUE_SIZE-1])
RXqueue.pwrite=RXqueue.arr;
else
RXqueue.pwrite++;
rxcnt++;
}
}
if(USART_GetITStatus(USART1,USART_IT_IDLE)==SET)
{
ch=USART1->SR;
ch=USART1->DR;
OSTaskQPost(&FiniteStatesTaskTCB,p_write,rxcnt,OS_OPT_POST_FIFO,&err);
rxcnt=0;
p_write=RXqueue.pwrite;
}
if(USART_GetITStatus(USART1,USART_IT_TXE)==SET)
{
if(TXqueue.EmptyCheck()==0)
{
USART1->DR=*TXqueue.pread;
if(TXqueue.pread==&TXqueue.arr[QUEUE_SIZE-1])
TXqueue.pread=TXqueue.arr;
else
TXqueue.pread++;
}
else
USART_ITConfig(USART1,USART_IT_TXE,DISABLE);
}
OSIntExit();
}
数据处理任务任务中,程序阻塞等待获得消息,一则消息被发布后数据处理任务获得消息并开始处理数据。任务阻塞等待消息时,CPU可以处理其他任务,当消息发布后,数据处理任务可以立即响应,有较好的实时性。
void FiniteStatesTask(void *arg)
{
OS_ERR err;
unsigned char *delta;//接收数据包首地址
OS_MSG_SIZE size;//接收数据包大小
CPU_TS ts;
unsigned char flag=0;
while(DEF_ON)
{
delta=(unsigned char*)OSTaskQPend(0,OS_OPT_PEND_BLOCKING,&size,&ts,&err);//任务阻塞等待消息
while(size--)
{
TXqueue.PutData(RXqueue.GetData());//将接收的数据存入发送缓冲队列
flag=receiveFiniteStates(*delta);//状态机处理数据
if(delta==&RXqueue.arr[QUEUE_SIZE-1])
delta=RXqueue.arr;
else
delta++;
}
if(flag==1)
{
flag=0;
USART1_SendChar();//如果数据处理无误,则把接收到的数据传回去
}
}
}
经过试验,波特率115200,数据包发送间隔1ms情况下没有丢包。当我把波特率设为921600时,无论发送间隔多长,数据丢包都很严重,经过硬件仿真发现有时候一包数据都接不全,我初步判断可能是波特率过高造成TTL电平不稳定。
如果有更好的串口数据接收防丢包的方法,欢迎留言。
工程代码下载:
https://download.csdn.net/download/qdchenxr/10958685
参考资料: