使用keil5中的CMSIS-Driver层中的USART空闲中断接收数据

前段时间看帖子发现CMSIS-Driver中的串口驱动可以使用空闲中断,好吧,或许以前有,是我没研究深入。今天,我把使用方法分享出来,供大家学习。
新建工程可以参考我的另一篇文章,那个是比较早的,现在版本更新很快,不过基本创建也是差不多的。这里我只是说明如何使用它的空闲中断方式去接收数据。
官方的CMSIS-Driver串口驱动中,有几个事件标志(如图)。
使用keil5中的CMSIS-Driver层中的USART空闲中断接收数据_第1张图片
对于接收,我们常用的只有这两个ARM_USART_EVENT_RECEIVE_COMPLETE|ARM_USART_EVENT_SEND_COMPLETE接收完成和发送完成事件。
在这里插入图片描述
这里的接收事件,是在调用int32_t(* Receive)(void *data, uint32_t num)函数时,串口接收到num个数据后,产生的接收完成事件。这个功能,在我们接收不定长的数据时,是很不好的用的。我上一篇文章对于接收不定长的数据,是每次只接收一个字节,然后用队列方式或者自己判断帧空闲方式去实现不定长数据接收。这样做就很麻烦。现在它的驱动里有另一个事件ARM_USART_EVENT_RX_TIMEOUT,这个事件是在开启DMA后,对于调用Receive函数,产生的接收超时事件。也就是接收到了一帧数据,只是这一帧数据没有达到我要接收的字节个数。我们把Receive函数中的num值设大一点,相当于给DMA接收设一个大一点的缓存,然后利用它的接收超时事件,来接收一帧数据。看它的源代码就是开启了IDLE中断,中断发生时,产生该接收超时事件。看下使用例子代码

void myUSART2_callback(uint32_t event)
{
	if(event & ARM_USART_EVENT_RECEIVE_COMPLETE)//表示buf接收满
	{
		osThreadFlagsSet(tid_usart2_Thread, EVENT_FLAGS_USART2_RX_COMPLETE);
	}
	else if(event & ARM_USART_EVENT_RX_TIMEOUT) //接收超时事件,表示接收到一帧数据
	{
		osThreadFlagsSet(tid_usart2_Thread, EVENT_FLAGS_USART2_RX_TIMEOUT);
	}
	else if(event & ARM_USART_EVENT_SEND_COMPLETE)
	{
		osThreadFlagsSet(tid_usart2_Thread, EVENT_FLAGS_USART2_TX_COMPLETE);
	}
}

上片代码是串口的回调函数,其中我在项目使用的CMSIS-RTOS2,在相应的事件中设置了相关的事件标志,以供线程使用。
我这里只关注了接收完成、接收超时和发送完成事件,还可以关注接收溢出,发送错误等其它事件。

void myThread_USART2(void *argument) //RS485
{
	uint32_t	flags;
	osStatus_t status;
	static ARM_DRIVER_USART * USARTdrv = &Driver_USART2;
	uint8_t rx_buf[50];
	MSG_QUEUE_t rs485_msg;
	MSG_QUEUE_t lora_msg;
	
	
	
	RS485_CtrlInit();
	RS485_SetRx();
	
	USARTdrv->Initialize(myUSART2_callback);
	/*Power up the USART peripheral */
	USARTdrv->PowerControl(ARM_POWER_FULL);
	/*Configure the USART to 9600 Bits/sec */
	USARTdrv->Control(ARM_USART_MODE_ASYNCHRONOUS |
										ARM_USART_DATA_BITS_8 |
										ARM_USART_PARITY_NONE |
										ARM_USART_STOP_BITS_1 |
										ARM_USART_FLOW_CONTROL_NONE, 9600);
	 
	/* Enable Receiver and Transmitter lines */
	USARTdrv->Control (ARM_USART_CONTROL_TX, 1);
	USARTdrv->Control (ARM_USART_CONTROL_RX, 1);	
	
	while(1)
	{
		USARTdrv->Receive(rx_buf,sizeof(rx_buf));
		
		flags = osThreadFlagsWait(EVENT_FLAGS_USART2_RX_COMPLETE|
															EVENT_FLAGS_USART2_RX_TIMEOUT|
															EVENT_FLAGS_RS485_TX,
															osFlagsWaitAny,
															osWaitForever);
		
		switch(flags)
		{
			case EVENT_FLAGS_USART2_RX_COMPLETE:	//buf存满
				break;
			case EVENT_FLAGS_USART2_RX_TIMEOUT: //接收到一帧数据
				USARTdrv->Control (ARM_USART_ABORT_RECEIVE, 1); //在这里要终止接收,防止下一帧数据过来,影响数据接收
				osThreadFlagsSet(tid_usart3_Thread,EVENT_FLAGS_LORA_TX); //设置LORA发送事件
				lora_msg.Len = USARTdrv->GetRxCount();
				memcpy(lora_msg.Buf,rx_buf,lora_msg.Len);
				status = osMessageQueuePut(mid_lora_msg,&lora_msg,0,0);
				if(status == osOK)
				{
					memset(&lora_msg,0,sizeof(lora_msg));
				}
				printf("%s",rx_buf);
		
				break;
			case EVENT_FLAGS_RS485_TX:
				status = osMessageQueueGet(mid_rs485_msg,&rs485_msg,0,osWaitForever);
				if(status == osOK)
				{
					RS485_SetTx();
					USARTdrv->Send(rs485_msg.Buf,rs485_msg.Len);
					osThreadFlagsWait(EVENT_FLAGS_USART2_TX_COMPLETE,osFlagsWaitAny,osWaitForever);
					RS485_SetRx();						
				}
				break;
		}
	}
}

这个是该串口的任务线程,该线程通过从串口2接收到一帧数据后,通过RS485串口发送出去。可以看到使用接收超时事件后,就可以接收不定长的数据帧,用起来贼爽。对于不带RTOS的使用方法,其实没什么区别。重点是不要漏掉2个操作,开始接收(USARTdrv->Receive(rx_buf,sizeof(rx_buf));)和终止接收(USARTdrv->Control (ARM_USART_ABORT_RECEIVE, 1);)否则你会发现,你的串口要么接收不了,要么接收的数据是乱的。

你可能感兴趣的:(STM32)