2. 多任务并发执行
任务目标
在RTOS中创建两个独立任务:一个控制LED以500ms周期闪烁,另一个控制蜂鸣器以200ms周期鸣响。通过串口打印任务运行状态,观察RTOS调度器如何在不同优先级下并发调度两个任务。
硬件依赖
本案例基于STM32F407开发板,使用标准库。LED接PB0(推挽输出),蜂鸣器接PB1(推挽输出),串口1(PA9/PA10)用于打印调试信息。若使用其他平台,仅需修改GPIO初始化部分。
完整代码
/* FreeRTOS + STM32F407 多任务并发示例 */
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f4xx.h"
#include "stdio.h"
/* 任务句柄 */
TaskHandle_t xLEDTaskHandle = NULL;
TaskHandle_t xBuzzerTaskHandle = NULL;
/* 串口发送字符 */
void USART1_SendChar(char c) {
while (!(USART1->SR & USART_SR_TXE));
USART1->DR = c;
}
/* 串口打印字符串 */
void USART1_Print(const char* str) {
while (*str) USART1_SendChar(*str++);
}
/* GPIO初始化 */
void GPIO_Init(void) {
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_InitTypeDef gpio;
gpio.GPIO_Mode = GPIO_Mode_OUT;
gpio.GPIO_OType = GPIO_OType_PP;
gpio.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
gpio.GPIO_PuPd = GPIO_PuPd_NOPULL;
gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpio);
gpio.GPIO_Mode = GPIO_Mode_AF;
gpio.GPIO_Pin = GPIO_Pin_9;
gpio.GPIO_AF = GPIO_AF_USART1;
GPIO_Init(GPIOA, &gpio);
gpio.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &gpio);
USART_InitTypeDef usart;
usart.USART_BaudRate = 115200;
usart.USART_WordLength = USART_WordLength_8b;
usart.USART_StopBits = USART_StopBits_1;
usart.USART_Parity = USART_Parity_No;
usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart.USART_Mode = USART_Mode_TX;
USART_Init(USART1, &usart);
USART_Cmd(USART1, ENABLE);
}
/* LED任务:500ms翻转一次 */
void vLEDTask(void* pvParameters) {
GPIO_SetBits(GPIOB, GPIO_Pin_0);
for (;;) {
GPIO_ToggleBits(GPIOB, GPIO_Pin_0);
USART1_Print("LED Task Running\r\n");
vTaskDelay(pdMS_TO_TICKS(500));
}
}
/* 蜂鸣器任务:200ms翻转一次 */
void vBuzzerTask(void* pvParameters) {
GPIO_SetBits(GPIOB, GPIO_Pin_1);
for (;;) {
GPIO_ToggleBits(GPIOB, GPIO_Pin_1);
USART1_Print("Buzzer Task Running\r\n");
vTaskDelay(pdMS_TO_TICKS(200));
}
}
int main(void) {
GPIO_Init();
xTaskCreate(vLEDTask, "LED", 128, NULL, 1, &xLEDTaskHandle);
xTaskCreate(vBuzzerTask, "Buzzer", 128, NULL, 2, &xBuzzerTaskHandle);
vTaskStartScheduler();
for (;;);
}
构建与运行
- 创建STM32F407裸机工程,加入FreeRTOS源码(heap_4.c、list.c、queue.c、tasks.c、port.c等)。
- 将上述代码保存为main.c,替换工程中的主文件。
- 编译并烧录到开发板。
- 打开串口助手,波特率115200,观察输出。
验证方法
通过条件:
- LED以约500ms周期闪烁(人眼可观察)。
- 蜂鸣器以约200ms周期鸣响(声音节奏明显快于LED)。
- 串口交替打印"LED Task Running"和"Buzzer Task Running",且Buzzer打印频率约为LED的2.5倍。
- 两个任务互不阻塞,同时运行——证明RTOS时间片调度生效。
调度行为说明
蜂鸣器任务优先级为2,高于LED任务的优先级1。当蜂鸣器任务调用vTaskDelay进入阻塞态时,LED任务获得运行机会;LED任务阻塞后,系统进入空闲态。由于两个任务都主动让出CPU,因此即使优先级不同,两者都能按各自周期执行,实现并发效果。若将蜂鸣器任务改为死循环且不延时,LED任务将因优先级低而永远无法运行——这是观察RTOS优先级抢占的附加实验。
图中深色块表示任务处于运行态,浅色块表示任务因vTaskDelay进入阻塞态。蜂鸣器任务周期短,因此运行频率更高;LED任务在蜂鸣器阻塞期间获得CPU执行权,两者交替占用处理器,宏观上表现为“同时运行”。