1. SafeRTOS内核启动与任务创建
任务目标
在STM32平台上完成SafeRTOS内核初始化,创建两个不同优先级的LED闪烁任务,通过调度器启动验证内核能够正常切换任务运行。
环境依赖
- 硬件:STM32F103C8T6最小系统板(或兼容STM32系列)
- 工具链:arm-none-eabi-gcc + CMake,或Keil MDK-ARM
- SafeRTOS内核源码(v10.x,需包含portable/IAR/ARM_CM3或对应移植层)
- STM32标准外设库或HAL库(仅需配置系统时钟与GPIO)
实现要点
SafeRTOS是FreeRTOS的安全认证版本,API与FreeRTOS高度兼容。本案例核心流程为:配置系统时钟→创建两个任务(不同优先级)→调用vTaskStartScheduler()启动调度器。两个任务分别控制板载LED以不同频率翻转,通过观察LED闪烁节奏确认调度正常。
关键完整代码
/* main.c - SafeRTOS内核启动与双任务LED闪烁 */
#include "FreeRTOS.h"
#include "task.h"
#include "stm32f1xx_hal.h"
/* 任务函数声明 */
void vTaskLED1(void *pvParameters);
void vTaskLED2(void *pvParameters);
/* 板载LED引脚定义(以PC13为例) */
#define LED1_PIN GPIO_PIN_13
#define LED1_PORT GPIOC
#define LED2_PIN GPIO_PIN_0
#define LED2_PORT GPIOB
/* 系统时钟配置(HSE 8MHz → PLL → 72MHz) */
static void SystemClock_Config(void) {
RCC_OscInitTypeDef osc = {0};
RCC_ClkInitTypeDef clk = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
osc.OscillatorType = RCC_OSCILLATORTYPE_HSE;
osc.HSEState = RCC_HSE_ON;
osc.PLL.PLLState = RCC_PLL_ON;
osc.PLL.PLLSource = RCC_PLLSOURCE_HSE;
osc.PLL.PLLM = 8;
osc.PLL.PLLN = 72;
osc.PLL.PLLP = RCC_PLLP_DIV2;
osc.PLL.PLLQ = 4;
HAL_RCC_OscConfig(&osc);
clk.ClockType = RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_SYSCLK |
RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
clk.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
clk.AHBCLKDivider = RCC_SYSCLK_DIV1;
clk.APB1CLKDivider = RCC_HCLK_DIV2;
clk.APB2CLKDivider = RCC_HCLK_DIV1;
HAL_RCC_ClockConfig(&clk, FLASH_LATENCY_2);
}
/* GPIO初始化 */
static void GPIO_Init(void) {
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef gpio = {0};
gpio.Mode = GPIO_MODE_OUTPUT_PP;
gpio.Pull = GPIO_NOPULL;
gpio.Speed = GPIO_SPEED_FREQ_LOW;
gpio.Pin = LED1_PIN;
HAL_GPIO_Init(LED1_PORT, &gpio);
gpio.Pin = LED2_PIN;
HAL_GPIO_Init(LED2_PORT, &gpio);
}
/* 任务1:LED1以500ms周期翻转(优先级1) */
void vTaskLED1(void *pvParameters) {
(void)pvParameters;
for (;;) {
HAL_GPIO_TogglePin(LED1_PORT, LED1_PIN);
vTaskDelay(pdMS_TO_TICKS(250)); /* 250ms延时 → 500ms翻转周期 */
}
}
/* 任务2:LED2以200ms周期翻转(优先级2,更高) */
void vTaskLED2(void *pvParameters) {
(void)pvParameters;
for (;;) {
HAL_GPIO_TogglePin(LED2_PORT, LED2_PIN);
vTaskDelay(pdMS_TO_TICKS(100)); /* 100ms延时 → 200ms翻转周期 */
}
}
int main(void) {
HAL_Init();
SystemClock_Config();
GPIO_Init();
/* 创建两个任务,栈大小512字(2KB),优先级分别为1和2 */
xTaskCreate(vTaskLED1, "LED1", 512, NULL, 1, NULL);
xTaskCreate(vTaskLED2, "LED2", 512, NULL, 2, NULL);
/* 启动调度器(不会返回) */
vTaskStartScheduler();
/* 正常不会执行到这里 */
for (;;) { }
}
/* FreeRTOS堆内存定义(在FreeRTOSConfig.h中配置总堆大小) */
/* 在FreeRTOSConfig.h中需定义:
#define configTOTAL_HEAP_SIZE ((size_t)(8 * 1024))
#define configMINIMAL_STACK_SIZE 128
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
*/
FreeRTOSConfig.h 关键配置
/* FreeRTOSConfig.h - 与本案例相关的必要配置 */
#define configUSE_PREEMPTION 1
#define configUSE_IDLE_HOOK 0
#define configUSE_TICK_HOOK 0
#define configCPU_CLOCK_HZ ((unsigned long)72000000)
#define configTICK_RATE_HZ ((TickType_t)1000)
#define configMINIMAL_STACK_SIZE ((unsigned short)128)
#define configTOTAL_HEAP_SIZE ((size_t)(8 * 1024))
#define configMAX_TASK_NAME_LEN (16)
#define configUSE_16_BIT_TICKS 0
#define configIDLE_SHOULD_YIELD 1
#define configUSE_MUTEXES 0
#define configUSE_RECURSIVE_MUTEXES 0
#define configUSE_COUNTING_SEMAPHORES 0
#define configUSE_TRACE_FACILITY 0
编译与运行
- 将上述
main.c与FreeRTOSConfig.h加入工程,并链接SafeRTOS内核源码(包含tasks.c、queue.c、list.c、port.c及对应ARM CM3移植文件)。 - 使用CMake或Keil编译生成HEX文件,通过ST-Link烧录至STM32F103C8T6。
- 若使用其他STM32型号,仅需调整
LED1_PIN/LED1_PORT宏定义及系统时钟配置部分。
验证方法与判定标准
- 现象观察:上电后,PC13所接LED1以约500ms周期(亮250ms/灭250ms)闪烁;PB0所接LED2以约200ms周期(亮100ms/灭100ms)闪烁。
- 调度验证:由于LED2任务优先级(2)高于LED1任务优先级(1),且两个任务均调用
vTaskDelay主动让出CPU,因此两个LED应独立按各自频率闪烁,互不阻塞。若两个LED闪烁频率与上述一致,说明内核初始化成功、任务创建正确、调度器正常运行。 - 异常排查:若LED不亮或闪烁频率异常,检查系统时钟是否配置为72MHz(影响
vTaskDelay时间基准)、configTOTAL_HEAP_SIZE是否足够(两个任务各需2KB栈+内核开销,8KB堆通常足够)、以及GPIO引脚是否与硬件连接一致。