这里我的把接受正确数据直接打印出来,也可以通过设置标识位,然后在主函数里面轮询再操作。
【资料图】
以上的接收形式,是中断一次就接收一个字符,这在UCOS等实时内核系统中频繁的中断,非常消耗CPU资源,在有些时候我们需要接收大量数据时且波特率很高的情况下,长时间中断会带来一些额外的问题。所以以DMA形式配合串口的IDLE(空闲中断)来接受数据将会大大的提高CPU的利用率,减少系统资源的消耗。首先还是先看代码。
#defineDMA_USART1_RECEIVE_LEN18voidUSART1_IRQHandler(void){u32temp=0;uint16_ti=0;if(USART_GetITStatus(USART1,USART_IT_IDLE)!=RESET){USART1->SR;USART1->DR;//这里我们通过先读SR(状态寄存器)和DR(数据寄存器)来清USART_IT_IDLE标志DMA_Cmd(DMA1_Channel5,DISABLE);temp=DMA_USART1_RECEIVE_LEN-DMA_GetCurrDataCounter(DMA1_Channel5);//接收的字符串长度=设置的接收长度-剩余DMA缓存大小for(i=0;i
之前的串口中断是一个一个字符的接收,现在改为串口空闲中断
,就是一帧数据过来才中断进入一次。而且接收的数据时候是DMA来搬运到我们指定的缓冲区(也就是程序中的USART1_RECEIVE_DMABuffer数组),是不占用CPU时间资源的。
最后在讲下DMA的发送:
#defineDMA_USART1_SEND_LEN64voidDMA_SEND_EN(void){DMA_Cmd(DMA1_Channel4,DISABLE);DMA_SetCurrDataCounter(DMA1_Channel4,DMA_USART1_SEND_LEN);DMA_Cmd(DMA1_Channel4,ENABLE);}
这里需要注意下DMA_Cmd(DMA1_Channel4,DISABLE)函数需要在设置传输大小之前调用一下,否则不会重新启动DMA发送。
有了以上的接收方式,对一般的串口数据处理是没有问题的了。下面再讲一下,在ucosiii中我使用信号量+消息队列+储存管理的形式来处理我们的串口数据。先来说一下这种方式对比其他方式的一些优缺点。
一般对串口的处理形式是"生产者"和"消费者"的模式,即本次接收的数据要马上处理,否则当数据大量涌进的时候,就来不及"消费"掉生产者(串口接收中断)的数据,那么就会丢失本次的数据处理。所以使用队列就能够很方便的解决这个问题。
在下面的程序中,对数据的处理是先接受,在处理,如果在处理的过程中,有串口中断接受数据,那么就把它依次放在队列中,队列的特征是先进先出,在串口中就是先处理先接受的数据,所以根据生产和消费的速度,定义不同大小的消息队列缓冲区就可以了。缺点就是太占用系统资源,一般51单片机是没可能了。下面是从我做的项目中截取过来的程序:
OS_MSG_SIZEUsart1_Rx_cnt;//字节大小计数值unsignedcharUsart1_data;//每次中断接收的数据unsignedchar*Usart1_Rx_Ptr;//储存管理分配内存的首地址的指针unsignedchar*Usart1_Rx_Ptr1;//储存首地址的指针voidUSART1_IRQHandler(){OS_ERRerr;OSIntEnter();if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE)!=RESET)//中断产生{USART_ClearFlag(USART1,USART_FLAG_RXNE);//清除中断标志Usart1_data=USART_ReceiveData(USART1);//接收串口1数据到buff缓冲区if(Usart1_data=="+")//接收到数据头标识{//OSSemPend((OS_SEM*)&SEM_IAR_UART,//这里请求信号量是为了保证分配的存储区,但一般来说不允许//(OS_TICK)0,//在终端服务函数中调用信号量请求但因为//(OS_OPT)OS_OPT_PEND_NON_BLOCKING,//我OPT参数设置为非阻塞,所以可以这么写//(CPU_TS*)0,//(OS_ERR*)&err);//if(err==OS_ERR_PEND_WOULD_BLOCK)//检测到当前信号量不可用//{//printf("error");//}Usart1_Rx_Ptr=(unsignedchar*)OSMemGet((OS_MEM*)&UART1_MemPool,&err);//分配存储区Usart1_Rx_Ptr1=Usart1_Rx_Ptr;//储存存储区的首地址}if(Usart1_data==0x0a)//接收到尾标志{*Usart1_Rx_Ptr++=Usart1_data;Usart1_Rx_cnt++;//字节大小增加OSTaskQPost((OS_TCB*)&Task1_TaskTCB,(void*)Usart1_Rx_Ptr1,//发送存储区首地址到消息队列(OS_MSG_SIZE)Usart1_Rx_cnt,(OS_OPT)OS_OPT_POST_FIFO,//先进先出,也可设置为后进先出,再有地方很有用(OS_ERR*)&err);Usart1_Rx_Ptr=NULL;//将指针指向为空,防止修改Usart1_Rx_cnt=0;//字节大小计数清零}else{*Usart1_Rx_Ptr=Usart1_data;//储存接收到的数据Usart1_Rx_Ptr++;Usart1_Rx_cnt++;}}OSIntExit();}
上面被注释掉的代码为我是为了防止当分区中没有空闲的存储块时加入信号量,打印出报警信息。当然我们也可以将存储块直接设置大一点,但是还是无法避免当没有可有存储块时会程序会崩溃现象。希望懂的朋友能告知下~。
下面是串口数据处理任务,这里删去了其他代码,只把他打印出来了而已。
voidtask1_task(void*p_arg){OS_ERRerr;OS_MSG_SIZEUsart1_Data_size;u8*p;while(1){p=(u8*)OSTaskQPend((OS_TICK)0,//请求消息队列,获得储存区首地址(OS_OPT)OS_OPT_PEND_BLOCKING,(OS_MSG_SIZE*)&Usart1_Data_size,(CPU_TS*)0,(OS_ERR*)&err);printf("%s\r\n",p);//打印数据delay_ms(100);OSMemPut((OS_MEM*)&UART1_MemPool,//释放储存区(void*)p,(OS_ERR*)&err);OSSemPost((OS_SEM*)&SEM_IAR_UART,//释放信号量(OS_OPT)OS_OPT_POST_NO_SCHED,(OS_ERR*)&err);OSTimeDlyHMSM(0,0,1,500,OS_OPT_TIME_PERIODIC,&err);}}
猜你喜欢:
谈谈嵌入式软件的兼容性!
实用 | 分享几个非常实用的开源项目