1. 创建第一个RTOS任务

创建第一个RTOS任务 学习卡片

任务定义

本案例使用RTIC(Real-Time Interrupt-driven Concurrency)框架——一个基于Rust的嵌入式RTOS——在STM32F103开发板上创建并启动一个周期任务,驱动板载LED以1Hz频率闪烁。通过该最小可运行工程,理解RTOS中“任务”作为独立调度单元的基本概念:任务由静态声明定义、由调度器按优先级自动管理、任务体在无限循环中执行周期操作。

硬件与依赖准备

目标硬件:STM32F103C8T6蓝色药丸板(板载PC13 LED)。需要以下依赖:

# Cargo.toml
[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "0.2"
rtic = "1.1.4"
embedded-hal = "0.2.7"
nb = "1"
cortex-m-semihosting = "0.5"   # 仅调试输出用

[dependencies.stm32f1xx-hal]
version = "0.10.0"
features = ["rt", "stm32f103"]

代码实现

// src/main.rs
#![no_main]
#![no_std]

use panic_halt as _;
use rtic::app;

#[app(device = stm32f1xx_hal::pac, peripherals = true)]
const APP: () = {
    // 任务资源:LED引脚句柄
    struct Resources {
        led: stm32f1xx_hal::gpio::gpioc::PC13<stm32f1xx_hal::gpio::Output<stm32f1xx_hal::gpio::PushPull>>,
    }

    // 初始化:配置时钟、GPIO、启动周期任务
    #[init]
    fn init(ctx: init::Context) -> init::LateResources {
        // 时钟配置:使用外部8MHz晶振,系统时钟72MHz
        let mut flash = ctx.device.FLASH.constrain();
        let mut rcc = ctx.device.RCC.constrain();
        let clocks = rcc
            .cfgr
            .use_hse(8.MHz())
            .sysclk(72.MHz())
            .pclk1(36.MHz())
            .freeze(&mut flash.acr);

        // GPIO配置:PC13推挽输出
        let mut gpioc = ctx.device.GPIOC.split(&mut rcc.apb2);
        let led = gpioc.pc13.into_push_pull_output(&mut gpioc.crh);

        // 启动周期任务:每500ms触发一次(1Hz闪烁)
        blink::spawn_after(500.millis()).unwrap();

        init::LateResources { led }
    }

    // 周期任务:翻转LED状态
    #[task(resources = [led])]
    fn blink(ctx: blink::Context) {
        ctx.resources.led.toggle();
        // 重新调度自身,形成周期
        blink::spawn_after(500.millis()).unwrap();
    }

    // 空闲任务(RTIC默认提供,此处可省略)
    #[idle]
    fn idle(_: idle::Context) -> ! {
        loop {
            cortex_m::asm::wfi(); // 等待中断,降低功耗
        }
    }
};

关键机制说明

RTIC框架中,任务通过#[task]属性声明,每个任务拥有独立的优先级(默认优先级为1)。init函数完成硬件初始化后,通过blink::spawn_after()将任务首次挂入调度队列。任务体执行完毕后,再次调用spawn_after()实现自我周期重调度——这是RTOS任务常用的“自触发”模式。调度器基于优先级抢占,当前单任务场景下,任务在定时器中断触发时被唤醒执行,执行完毕后CPU进入idle任务等待下一次事件。

SVG:任务调度时序与状态

RTIC任务调度时序(1Hz LED闪烁) 0ms 500ms 1000ms blink#1 blink#2 blink#3 idle(WFI等待) idle(WFI等待) spawn_after(500ms) spawn_after(500ms) LED: ON OFF ON 任务执行 空闲

构建与烧录

# 添加编译目标
rustup target add thumbv7m-none-eabi

# 构建(release模式)
cargo build --release

# 烧录(使用OpenOCD + ST-Link)
openocd -f interface/stlink.cfg -f target/stm32f1x.cfg -c "program target/thumbv7m-none-eabi/release/rtos_task1 verify reset exit"

验证方法与判定标准

运行验证:将编译固件烧录至STM32F103后,观察板载LED(PC13)是否以约1Hz频率闪烁(每500ms翻转一次)。

判定标准:

  1. LED持续稳定闪烁,周期误差在视觉可接受范围内(约1秒一个完整亮灭周期);
  2. 若使用调试器连接,在blink任务入口处设置断点,每次触发间隔应为500ms±1ms(由定时器精度保证);
  3. 系统不进入HardFault,空闲时CPU处于WFI低功耗状态(可通过电流表测量,空闲电流应明显低于运行电流)。

预期输出:LED以固定周期闪烁,逻辑分析仪测量PC13引脚波形为方波,高电平持续500ms,低电平持续500ms,频率1Hz。