使用keil5中的CMSIS-Driver層中的USART空閒中斷接收數據

前段時間看帖子發現CMSIS-Driver中的串口驅動可以使用空閒中斷,好吧,或許以前有,是我沒研究深入。今天,我把使用方法分享出來,供大家學習。
新建工程可以參考我的另一篇文章,那個是比較早的,現在版本更新很快,不過基本創建也是差不多的。這裏我只是說明如何使用它的空閒中斷方式去接收數據。
官方的CMSIS-Driver串口驅動中,有幾個事件標誌(如圖)。
在這裏插入圖片描述
對於接收,我們常用的只有這兩個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);)否則你會發現,你的串口要麼接收不了,要麼接收的數據是亂的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章