单片机串口数据处理(2)——uCOSIII+循环队列接收数据

4 篇文章 0 订阅

上一篇文章介绍了串口发送数据的优化方法,使用中断的方式发送数据可以提高系统实时性。这次介绍串口接收数据的方法。新的数据接收方法结合了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

参考资料:

基于uCOSII的MCU串口传输处理

ucos-iii串口用信号量及环形队列中断发送,用内建消息队列中断接收

  • 8
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
UC/OS-III是一个实时操作系统,循环队列是其中一种数据结构,用于解决数据的环形存储和循环访问的问题。 循环队列是一种特殊的队列,它通过使用数组来实现。数组的大小是固定的,当队列满时,如果继续向队列中添加数据,则会发生队列溢出。而循环队列的好处是,即使队列已满,也可以循环利用队列中的空间。 UC/OS-III中的循环队列有两个重要的指针,即读指针和写指针。读指针指向队列中最早的一个元素,而写指针指向队列中最后一个元素的下一位置(即准备写入的位置)。通过读指针和写指针,可以确定队列中的元素和空闲空间。 当向队列中写入数据时,写指针会不断往后移动并写入数据。当写指针到达数组末尾时,如果此时队列还有空闲空间,则写指针会回到数组的起始位置,实现循环存储的效果。 当从队列中读取数据时,读指针也会不断往后移动并读取数据。当读指针到达数组末尾时,如果此时队列中还有数据,则读指针会回到数组的起始位置,实现循环访问的效果。 通过读写指针的循环移动,可以实现数据在队列中的循环存储和循环访问。循环队列在存储数据时效率高,避免了频繁的数据搬移操作,能够更好地满足实时操作系统对存储和访问数据的需求。 总之,UC/OS-III中的循环队列是一种用于解决数据环形存储和循环访问的数据结构,通过读指针和写指针实现数据在队列中的循环存储和循环访问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值