FreeRTOS--消息队列
在任何RTOS中,都具有一个重要的通信机制----消息队列。
队列是任务间通信的主要形式。它们可用于在任务之间、中断和任务之间发送消息。在大多数情况下,它们被用作线程安全的FIFO(先进先出)缓冲区,新数据被发送到队列的后面,不过数据也可以发送到前面。
消息队列的概念及其作用(以下基础内容转载自安富莱电子)
消息队列就是通过RTOS内核提供的服务,任务或中断服务子程序可以将一个消息(注意,FreeRTOS消息队列传递的是实际数据,并不是数据地址,RTX,uCOS-II和uCOS-III是传递的地址)放入到队列。同样,一个或者多个任务可以通过RTOS内核服务从队列中得到消息。通常,先进入消息队列的消息先传给任务,也就是说,任务先得到的是最先进入到消息队列的消息,即先进先出的原则(FIFO),FreeRTOS的消息队列支持FIFO和LIFO两种数据存取方式。 也许有不理解的初学者会问采用消息队列多麻烦,搞个全局数组不是更简单,其实不然。在裸机编程时,使用全局数组的确比较方便,但是在加上RTOS后就是另一种情况了。相比消息队列,使用全局数组主要有如下四个问题:
♦使用消息队列可以让RTOS内核有效地管理任务,而全局数组是无法做到的,任务的超时等机制需要用户自己去实现。
♦使用了全局数组就要防止多任务的访问冲突,而使用消息队列则处理好了这个问题,用户无需担心。
♦使用消息队列可以有效地解决中断服务程序与任务之间消息传递的问题。
♦FIFO机制更有利于数据的处理。
这里我将创建两个线程,monitor Task和 control Task 。通过monitor Task发送消息,control Task接受消息,根据消息类型打印不同的信息。
1.基于STM32Cube配置FreeRTOS,我这里是用的STM32F103RCT6,首先Enable FreeRTOS,配置RCC外部高速晶振8M HZ
2.配置打印串口USART1,HAL时钟基准源TIM1,RTOS将systick作为时钟源。
3.配置时钟树
4.配置FreeRTOS
5.生成代码配置
生成的main主函数,System,GPIO,USART,FreeRTOS初始化,一旦启动线程调度,Task开始执行,main中的while不会进入。
1 int main(void) 2 { 3 /* USER CODE BEGIN 1 */ 4 5 /* USER CODE END 1 */ 6 7 /* MCU Configuration----------------------------------------------------------*/ 8 9 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ 10 HAL_Init(); 11 12 /* USER CODE BEGIN Init */ 13 14 /* USER CODE END Init */ 15 16 /* Configure the system clock */ 17 SystemClock_Config(); 18 19 /* USER CODE BEGIN SysInit */ 20 21 /* USER CODE END SysInit */ 22 23 /* Initialize all configured peripherals */ 24 MX_GPIO_Init(); 25 MX_USART1_UART_Init(); 26 27 28 /* Initialize interrupts */ 29 MX_NVIC_Init(); 30 /* USER CODE BEGIN 2 */ 31 printf("Test Message Queue!\n"); 32 /* USER CODE END 2 */ 33 34 /* Call init function for freertos objects (in freertos.c) */ 35 MX_FREERTOS_Init(); 36 37 /* Start scheduler */ 38 osKernelStart(); 39 40 /* We should never get here as control is now taken by the scheduler */ 41 42 /* Infinite loop */ 43 /* USER CODE BEGIN WHILE */ 44 while (1) 45 { 46 47 /* USER CODE END WHILE */ 48 49 /* USER CODE BEGIN 3 */ 50 51 } 52 /* USER CODE END 3 */ 53 54 }
定义消息结构:
1 /* USER CODE BEGIN Variables */ 2 typedef enum _Msg_Type_ 3 { 4 MSG_TYPE_LED, 5 MSG_TYPE_LED_TOGGLE, 6 MSG_TYPE_BUZZER, 7 MSG_TYPE_MAX 8 }MsgType; 9 10 typedef struct _Msg 11 { 12 MsgType msgType; 13 bool bswitch; 14 }Msg; 15 16 17 /* USER CODE END Variables */
定义Debug_Printf用来打印串口信息
void Debug_Printf(char *format, ...) { char buf_str[128]; va_list v_args; va_start(v_args, format); (void)vsnprintf((char *)&buf_str[0], (size_t ) sizeof(buf_str), (char const *) format, v_args); va_end(v_args); /* 互斥信号量 */ osMutexWait(Mutex_printfHandle,osWaitForever); printf("[%ldms]%s",xTaskGetTickCount(),buf_str); osMutexRelease(Mutex_printfHandle); }
FreeRTOS初始化
/* Init FreeRTOS */ void MX_FREERTOS_Init(void) { /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Create the mutex(es) */ /* definition and creation of Mutex_printf */ osMutexDef(Mutex_printf); Mutex_printfHandle = osMutexCreate(osMutex(Mutex_printf)); /* USER CODE BEGIN RTOS_MUTEX */ /* add mutexes, ... */ /* USER CODE END RTOS_MUTEX */ /* Create the semaphores(s) */ /* USER CODE BEGIN RTOS_SEMAPHORES */ /* add semaphores, ... */ /* USER CODE END RTOS_SEMAPHORES */ /* USER CODE BEGIN RTOS_TIMERS */ /* start timers, add new ones, ... */ /* USER CODE END RTOS_TIMERS */ /* Create the thread(s) */ /* definition and creation of vTask1 */ osThreadDef(vTask1, monitor_task, osPriorityNormal, 0, 256); vTask1Handle = osThreadCreate(osThread(vTask1), NULL); /* definition and creation of vTaks2 */ osThreadDef(vTaks2, control_task, osPriorityNormal, 0, 256); vTaks2Handle = osThreadCreate(osThread(vTaks2), NULL); /* USER CODE BEGIN RTOS_THREADS */ /* add threads, ... */ /* USER CODE END RTOS_THREADS */ /* Create the queue(s) */ /* definition and creation of msgQueue */ osMessageQDef(msgQueue, 16, Msg); //定义消息队列的深度及负载大小 msgQueueHandle = osMessageCreate(osMessageQ(msgQueue), NULL); /* USER CODE BEGIN RTOS_QUEUES */ /* add queues, ... */ /* USER CODE END RTOS_QUEUES */ } /* monitor_task function */ void monitor_task(void const * argument) { /* USER CODE BEGIN monitor_task */ int cnt = 0; Msg msg; Debug_Printf("Enter monitor task!\n"); /* Infinite loop */ for(;;) { cnt++; if(cnt == 1 ) { msg.msgType = MSG_TYPE_LED; msg.bswitch = true; xQueueSend(msgQueueHandle,&msg,10); } else if(cnt == 5) { msg.msgType = MSG_TYPE_LED; msg.bswitch = false; xQueueSend(msgQueueHandle,&msg,10); } else if(cnt == 10) { msg.msgType = MSG_TYPE_LED_TOGGLE; msg.bswitch = true; xQueueSend(msgQueueHandle,&msg,10); } osDelay(1000); } /* USER CODE END monitor_task */ } /* control_task function */ void control_task(void const * argument) { /* USER CODE BEGIN control_task */ Msg tempMsg ; Debug_Printf("Enter control task!\n"); /* Infinite loop */ for(;;) { xQueueReceive(msgQueueHandle,(void*)&tempMsg,osWaitForever); switch(tempMsg.msgType) { case MSG_TYPE_LED: if(tempMsg.bswitch) { Debug_Printf("LED is Open!\n"); } else { Debug_Printf("LED is Closed!\n"); } break; case MSG_TYPE_LED_TOGGLE: if(tempMsg.bswitch) { Debug_Printf("LED is Toggling!\n"); } else { Debug_Printf("LED is off!\n"); } break; default: break; } osDelay(10); } /* USER CODE END control_task */ }
测试结果:通过系统时间戳可以看出,首先monitor Task 发送消息类型MSG_TYPE_LED,
消息负载为Open,过了4秒发送MSG_TYPE_LED,消息负载为Close;再过5秒发送MSG_TYPE_LED_TOGGLE