1. 创建第一个RTOS任务
任务目标
在STM32裸机工程基础上移植FreeRTOS,创建第一个任务实现LED以500ms周期闪烁。通过本案例掌握xTaskCreate创建任务、vTaskDelay延时以及调度器启动的核心流程。
硬件与工程准备
- 硬件:STM32F103C8T6最小系统板,板载LED接PC13(低电平点亮)
- 工程基础:已有可运行的裸机工程(含GPIO初始化),使用Keil MDK或STM32CubeIDE
- FreeRTOS源码:从FreeRTOS官网获取V10.4.6+,将
Source目录下*.c文件加入工程,include目录加入头文件路径 - 配置文件:在工程中新建
FreeRTOSConfig.h,按本案例末尾配置内容填写
核心代码实现
以下代码完整实现任务创建、LED闪烁逻辑与调度器启动。将代码放入main.c中,替换原有main函数。
/* FreeRTOS头文件 */
#include "FreeRTOS.h"
#include "task.h"
/* STM32标准库头文件(根据实际工程调整) */
#include "stm32f10x.h"
/* LED引脚定义:PC13 */
#define LED_GPIO_PORT GPIOC
#define LED_GPIO_PIN GPIO_Pin_13
#define LED_GPIO_CLK RCC_APB2Periph_GPIOC
/* 任务函数声明 */
void vLEDTask(void *pvParameters);
/* 硬件初始化:配置PC13为推挽输出 */
static void HardwareInit(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(LED_GPIO_CLK, ENABLE);
GPIO_InitStructure.GPIO_Pin = LED_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(LED_GPIO_PORT, &GPIO_InitStructure);
GPIO_SetBits(LED_GPIO_PORT, LED_GPIO_PIN); /* 初始熄灭 */
}
int main(void)
{
HardwareInit();
/* 创建LED任务:优先级1,栈大小128字(512字节) */
xTaskCreate(vLEDTask, "LED", 128, NULL, 1, NULL);
/* 启动调度器(不会返回) */
vTaskStartScheduler();
/* 正常情况下不会执行到这里 */
while (1) {}
}
/* LED闪烁任务:每500ms翻转一次电平 */
void vLEDTask(void *pvParameters)
{
(void)pvParameters; /* 未使用参数 */
for (;;)
{
GPIO_WriteBit(LED_GPIO_PORT, LED_GPIO_PIN,
(BitAction)(1 - GPIO_ReadOutputDataBit(LED_GPIO_PORT, LED_GPIO_PIN)));
vTaskDelay(pdMS_TO_TICKS(500)); /* 延时500ms */
}
}
FreeRTOSConfig.h 必要配置
#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H
/* 基础配置 */
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ (SystemCoreClock)
#define configTICK_RATE_HZ (1000)
#define configMAX_PRIORITIES (5)
#define configMINIMAL_STACK_SIZE (128)
#define configTOTAL_HEAP_SIZE (8 * 1024)
#define configMAX_TASK_NAME_LEN (16)
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
/* 钩子函数与内存 */
#define configCHECK_FOR_STACK_OVERFLOW 0
#define configUSE_MALLOC_FAILED_HOOK 0
/* 可选API(本案例未使用,可关闭) */
#define configUSE_TIMERS 0
#define configUSE_COUNTING_SEMAPHORES 0
#define configUSE_MUTEXES 0
#define configUSE_RECURSIVE_MUTEXES 0
#define configUSE_QUEUE_SETS 0
#define configUSE_TASK_NOTIFICATIONS 0
/* 中断优先级(STM32使用) */
#define configPRIO_BITS 4
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 5
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 15
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5
/* 必须包含的宏 */
#define INCLUDE_vTaskDelay 1
#define INCLUDE_xTaskGetCurrentTaskHandle 0
#define INCLUDE_vTaskDelete 0
#endif /* FREERTOS_CONFIG_H */
编译与运行
- 将上述代码加入工程,确认FreeRTOS源码已编译(
tasks.c、queue.c、list.c、heap_4.c必须包含) - 在
stm32f10x_it.c中实现SysTick_Handler,调用xPortSysTickHandler();若使用PendSV和SVC中断,确认启动文件已正确映射(标准库工程通常已处理) - 编译下载至开发板,观察LED是否以约1Hz频率闪烁(亮500ms,灭500ms)
验证方法
- 现象验证:LED持续闪烁,周期约1秒(亮灭各500ms),说明任务创建成功且调度器正常运行
- 调试验证:在
vLEDTask中设置断点,全速运行后应能命中断点,证明任务被调度执行 - 异常排查:若LED不闪烁,检查
configTOTAL_HEAP_SIZE是否足够(本案例8KB充足)、SysTick_Handler是否已正确调用FreeRTOS心跳处理函数
关键说明
xTaskCreate参数依次为:任务函数、任务名、栈大小(单位字)、参数、优先级、任务句柄。本案例未使用任务删除功能,因此vTaskDelete未调用vTaskDelay使任务进入阻塞态,让出CPU给低优先级任务或空闲任务,这是RTOS任务调度的基本机制- 若需删除任务,可在任务内调用
vTaskDelete(NULL),但需在FreeRTOSConfig.h中定义INCLUDE_vTaskDelete为1