3. 任务间消息队列通信
任务说明
本实验基于STM32F407开发板与FreeRTOS(通过freertos-rust crate使用),创建两个不同优先级的任务:高优先级任务每200ms翻转LED1,低优先级任务每500ms翻转LED2。通过逻辑分析仪或示波器观察两个GPIO的翻转时序,验证高优先级任务能够抢占低优先级任务——即当高优先级任务就绪时,低优先级任务会被立即暂停执行。
硬件连接
- LED1(高优先级任务控制)→ PA5(开发板载LED)
- LED2(低优先级任务控制)→ PB0(外接LED,串联220Ω电阻到GND)
Cargo依赖配置
[dependencies]
cortex-m = "0.7"
cortex-m-rt = "0.7"
panic-halt = "0.2"
embedded-hal = "0.2"
nb = "1"
freertos-rust = "0.1.3"
stm32f4xx-hal = "0.9"
[[bin]]
name = "priority_demo"
path = "src/main.rs"
[profile.release]
opt-level = "s"
实现代码
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use panic_halt as _;
use stm32f4xx_hal::{
prelude::*,
pac,
gpio::{Output, PushPull, PA5, PB0},
};
use freertos_rust::*;
// 高优先级任务句柄(用于验证抢占)
static HIGH_TASK: Mutex<Option<TaskHandle>> = Mutex::new(None);
// 高优先级任务:翻转PA5,优先级为2
fn high_priority_task(_: ()) {
let dp = unsafe { pac::Peripherals::steal() };
let gpioc = dp.GPIOA.split();
let mut led = gpioc.pa5.into_push_pull_output();
loop {
led.toggle();
// 延时200ms
Timer::delay_ms(200);
}
}
// 低优先级任务:翻转PB0,优先级为1
fn low_priority_task(_: ()) {
let dp = unsafe { pac::Peripherals::steal() };
let gpiob = dp.GPIOB.split();
let mut led = gpiob.pb0.into_push_pull_output();
loop {
led.toggle();
// 延时500ms
Timer::delay_ms(500);
}
}
#[entry]
fn main() -> ! {
// 初始化时钟(168MHz)
let dp = pac::Peripherals::take().unwrap();
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.sysclk(168.mhz()).freeze();
// 启动FreeRTOS调度器
unsafe { freertos_rust::start_kernel() };
// 创建低优先级任务(优先级1)
let low_task = Task::new()
.name("low")
.priority(1)
.stack_size(128)
.start(low_priority_task, ())
.unwrap();
// 创建高优先级任务(优先级2)
let high_task = Task::new()
.name("high")
.priority(2)
.stack_size(128)
.start(high_priority_task, ())
.unwrap();
// 保存高优先级任务句柄(供验证使用)
*HIGH_TASK.lock().unwrap() = Some(high_task);
// 调度器启动后不会返回
loop {
cortex_m::asm::wfi();
}
}
抢占行为说明
FreeRTOS采用基于优先级的抢占式调度。当高优先级任务(优先级2)进入就绪态时,内核会立即暂停当前运行的低优先级任务(优先级1),切换到高优先级任务执行。在本例中:
- 低优先级任务每500ms翻转一次PB0,但在其延时期间,高优先级任务每200ms就会就绪并抢占CPU。
- 高优先级任务执行完翻转和延时后,若低优先级任务仍在延时中,则高优先级任务继续运行(因为其优先级更高,即使低优先级任务就绪也会先运行高优先级任务)。
- 实际效果:高优先级任务以200ms周期稳定翻转PA5;低优先级任务只有在高优先级任务延时期间且自身延时结束时才能获得CPU执行翻转PB0。
验证方法
- 将逻辑分析仪通道1接PA5,通道2接PB0,公共地接GND。
- 编译并烧录:
cargo build --release,使用cargo flash --release --chip STM32F407VGTx烧录。 - 观察波形:PA5应呈现周期约200ms的方波(高电平50ms,低电平50ms,因翻转后延时200ms,实际周期为400ms——翻转后延时200ms再翻转,故周期为400ms)。
- 观察PB0波形:其翻转周期应约为500ms×2=1000ms,但翻转时刻会受高优先级任务抢占影响而出现抖动。
- 判定标准:PA5波形稳定且周期准确;PB0波形周期约为1000ms,且其边沿与PA5边沿无固定相位关系(说明被抢占导致执行延迟)。若将高优先级任务优先级改为0(低于低优先级),则PB0波形会变得稳定,PA5波形出现抖动,以此对比验证抢占机制。
预期观测结果(基于代码逻辑推导):
PA5翻转周期 = 200ms延时 × 2 = 400ms(每次翻转后延时200ms)
PB0翻转周期 = 500ms延时 × 2 = 1000ms
由于高优先级任务每400ms完成一次完整周期,而低优先级任务需要1000ms,因此高优先级任务会多次抢占低优先级任务的延时窗口,导致PB0的实际翻转间隔略大于1000ms且不均匀。
PA5翻转周期 = 200ms延时 × 2 = 400ms(每次翻转后延时200ms)
PB0翻转周期 = 500ms延时 × 2 = 1000ms
由于高优先级任务每400ms完成一次完整周期,而低优先级任务需要1000ms,因此高优先级任务会多次抢占低优先级任务的延时窗口,导致PB0的实际翻转间隔略大于1000ms且不均匀。
验证判定标准
| 观测项 | 通过条件 |
|---|---|
| PA5波形 | 周期稳定在400ms±5ms,占空比50% |
| PB0波形 | 平均周期约1000ms,但边沿位置相对PA5无固定相位,存在±50ms以上抖动 |
| 抢占验证 | 将高优先级任务优先级改为0后重新烧录,PB0波形变为稳定1000ms周期,PA5波形出现抖动 |
说明:若使用开发板板载LED(PA5)观察,由于人眼视觉暂留,无法直接看到400ms翻转效果,建议使用逻辑分析仪或示波器。若仅有LED,可将高优先级任务延时改为50ms、低优先级改为200ms,通过观察两个LED的亮度差异(高优先级LED更亮)来间接判断抢占行为。