前段時間看帖子發現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);)否則你會發現,你的串口要麼接收不了,要麼接收的數據是亂的。