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

编译与运行

  1. 将上述main.c与FreeRTOSConfig.h加入工程,并链接SafeRTOS内核源码(包含tasks.c、queue.c、list.c、port.c及对应ARM CM3移植文件)。
  2. 使用CMake或Keil编译生成HEX文件,通过ST-Link烧录至STM32F103C8T6。
  3. 若使用其他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引脚是否与硬件连接一致。