前言
平台:GD32F303+FreeRTOS
实现思路:
使用队列作为数据容器,将串口接收中断里的数据传递到任务中,替代使用全局变量的方式,保证数据的完整性。
实现过程
- 串口的初始化,标准流程,应该没什么好说的了吧。
/* USART0 init function */ void MX_USART0_UART_Init(void) { /* USART interrupt configuration */ nvic_irq_enable(USART0_IRQn, 0, 0); /* enable GPIO clock */ rcu_periph_clock_enable(RCU_GPIOA); /* enable USART clock */ rcu_periph_clock_enable(RCU_USART0); /* connect port to USARTx_Tx */ gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9); /* connect port to USARTx_Rx */ gpio_init(GPIOA, GPIO_MODE_IN_FLOATING, GPIO_OSPEED_50MHZ, GPIO_PIN_10); /* USART configure */ usart_deinit(USART0); usart_baudrate_set(USART0, 115200U); usart_receive_config(USART0, USART_RECEIVE_ENABLE); usart_transmit_config(USART0, USART_TRANSMIT_ENABLE); usart_enable(USART0); /* enable USART0 receive interrupt */ usart_interrupt_enable(USART0, USART_INT_RBNE); }
- 主函数,初始化队列和接收任务。这里任务堆栈使用最小堆栈,任务优先级可自定义。
//uart0 task #define UART0_STK_SIZE configMINIMAL_STACK_SIZE//任务堆栈 #define UART0_TASK_PRIO ( tskIDLE_PRIORITY + 3 )//任务优先级 TaskHandle_t uart0_task_t;//任务句柄 void uart0_task(void * pvParameters);//任务函数 #define UART0_RX_QUEUE_SIZE (0XFF)//接收队列长度 QueueHandle_t uart0_rx_queue = NULL;//接收队列句柄 int main(void) { MX_USART0_UART_Init(); taskENTER_CRITICAL(); //创建任务 xTaskCreate(uart0_task, "uart0_task", UART0_STK_SIZE, NULL, UART0_TASK_PRIO, &uart0_task_t); //初始化接收队列,队列长度根据需求定义 uart0_rx_queue = xQueueCreate(UART0_RX_QUEUE_SIZE, sizeof(char));//单个长度为char taskEXIT_CRITICAL(); /* start scheduler */ vTaskStartScheduler(); while(1) { } }
-
接收任务函数,这里判断0xOD、0x0A是以回车换行结束的数据为接收依据,portMAX_DELAY表示任务将阻塞等待直到收到数据。
/* 接收任务 */ void uart0_task(void * pvParameters) { static uint8_t rx_byte;//接收字节 static uint16_t rx_flag;//接收标志 static uint8_t rx_data[UART0_RX_QUEUE_SIZE];//接收缓存 while(1) { if (xQueueReceive(uart0_rx_queue, &rx_byte, portMAX_DELAY) == pdPASS)//等待接收队列 { /*接收到了0x0d*/ if((rx_byte==0x0d)&&((rx_flag&0X3FFF)>0)) { rx_flag|=0x4000; rx_data[rx_flag&0X3FFF] = rx_byte; rx_flag++; } /*接收到了0x0a*/ else if((rx_flag&0x4000)&&((rx_flag&0X3FFF)>0)) { if(rx_byte==0x0a) { rx_flag|=0x8000; } rx_data[rx_flag&0X3FFF] = rx_byte; rx_flag++; usart_interrupt_flag_clear(USART0, USART_INT_FLAG_RBNE); printf("rx: %d,%s", rx_flag&0x3fff, rx_data); memset(rx_data, 0, sizeof(rx_data)); rx_flag = 0; } else { rx_data[rx_flag&0X3FFF] = rx_byte; rx_flag++; if(rx_flag > UART0_RX_QUEUE_SIZE-1) rx_flag = 0; } } } }
- 串口中断函数,将接收到的数据发送到队列,注意中断里要使用xQueueSendFromISR(),如果出现队列满,可以适当增加队列长度。
/*! \brief this function handles USART RBNE interrupt request and TBE interrupt request \param[in] none \param[out] none \retval none */ void USART0_IRQHandler(void) { uint8_t res; BaseType_t xHigherPriorityTaskWoken = pdTRUE; BaseType_t err; if(RESET != usart_interrupt_flag_get(USART0, USART_INT_FLAG_RBNE))//接收缓存非空标志 { /* receive data */ res = usart_data_receive(USART0); if(xQueueIsQueueFullFromISR(uart0_rx_queue) == pdFALSE) { err = xQueueSendFromISR(uart0_rx_queue, &res, &xHigherPriorityTaskWoken);;//发送到接收数据 if(err != pdPASS) debug_log("uart0 queue send error", NULL, -1, -1); } else { debug_log("uart0 queue full", NULL, -1, -1); xQueueReset(uart0_rx_queue); } portYIELD_FROM_ISR(xHigherPriorityTaskWoken);//如果需要的话进行一次任务切换 } }
小结
当然实现串口接收的方式还有很多种,这里仅提供一种较直接简单的思路,后续整理好继续分享给大家。