第一讲:RTOS基础概念

各位同学好,我是你们的讲师。今天咱们来聊聊RTOS的基础概念。说实话,我刚开始接触嵌入式开发时,也觉得RTOS挺玄乎的。但干这行十几年了,我越来越觉得,搞懂RTOS是每个嵌入式工程师的必修课。

1.1 实时操作系统定义

实时操作系统,英文叫Real-Time Operating System,简称RTOS。说白了,它就是一个能保证在限定时间内完成特定任务的操作系统。

你可能会问:那和Windows、Linux有啥区别?嗯,区别大了去了。普通操作系统追求的是「平均响应快」,而RTOS追求的是「最坏情况也能按时完成」。我举个例子:你玩电脑游戏,偶尔卡顿一下没关系,但无人机飞控如果卡顿100毫秒,飞机可能就掉下来了。

核心特征:

  • 确定性:系统行为可预测,不会出现意外延迟
  • 及时性:任务必须在截止时间前完成
  • 可靠性:长时间运行不崩溃

我在做农业无人机项目时,遇到过一个问题:喷洒农药时,如果RTOS调度不及时,喷头开关延迟了50ms,就会导致局部喷洒过量。你看,这就是实时性的重要性。

1.2 任务调度机制

任务调度,是RTOS的核心中的核心。它决定了「谁先运行、谁后运行、运行多久」。

常见的调度策略有这么几种:

调度算法 特点 适用场景
优先级抢占式 高优先级任务随时抢占CPU 大多数RTOS默认方案
时间片轮转 同优先级任务轮流运行 多个同等重要任务
单调速率调度 周期越短优先级越高 周期性任务为主
最早截止时间优先 截止时间越近优先级越高 任务截止时间明确

我个人习惯用优先级抢占式调度。为什么?因为它简单、高效、可预测。你想想看,在无人机里,姿态控制任务的优先级肯定要高于日志记录任务,对吧?

避坑指南:我曾经犯过一个错误——把所有任务都设成相同优先级。结果呢?系统在关键时刻卡住了,因为高优先级任务被低优先级任务阻塞了。记住:优先级不是随便设的,要仔细分析任务的实时性要求。

来看一个简单的任务创建代码:

// 在FreeRTOS中创建任务
void vTaskFunction(void *pvParameters) {
    while(1) {
        // 任务代码
        vTaskDelay(pdMS_TO_TICKS(100)); // 延时100ms
    }
}

// 创建任务
xTaskCreate(
    vTaskFunction,      // 任务函数
    "Task1",            // 任务名称
    256,                // 栈大小(字)
    NULL,               // 参数
    2,                  // 优先级(0-最高)
    NULL                // 任务句柄
);

这里要注意:栈大小别设太小。我见过有人设了64字节,结果任务一运行就栈溢出,系统直接崩溃。嗯,这种bug最难查。

1.3 中断管理

中断,是RTOS和外部世界交互的「快车道」。外部事件来了,CPU放下手头工作,先去处理紧急事件。

中断管理有几个关键点:

  • 中断优先级:和任务优先级类似,高优先级中断可以打断低优先级中断
  • 中断嵌套:一个中断处理中,允许更高优先级的中断进来
  • 临界区保护:中断和任务共享数据时,要关中断或使用锁

警告:中断服务函数(ISR)里不要做耗时操作!我曾经在ISR里调用了printf打印日志,结果中断执行了5ms,导致其他中断丢失。记住:ISR要「快进快出」,复杂逻辑放到任务里做。

中断处理的一般流程:

// 中断服务函数示例
void EXTI0_IRQHandler(void) {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    
    // 清除中断标志
    if(EXTI_GetITStatus(EXTI_Line0) != RESET) {
        // 发送信号量通知任务
        xSemaphoreGiveFromISR(xSemaphore, &xHigherPriorityTaskWoken);
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
    
    // 如果有更高优先级任务被唤醒,立即切换
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

你看,ISR里只做了两件事:清除标志、发送信号量。真正的数据处理在任务里完成。这是RTOS编程的黄金法则。

1.4 时钟节拍

时钟节拍,是RTOS的「心跳」。它像一个节拍器,每隔固定时间就「滴答」一下,驱动整个系统运转。

时钟节拍的作用:

  • 任务延时:vTaskDelay(100) 就是等100个节拍
  • 时间片轮转:每个任务运行一个时间片后切换
  • 超时检测:等待信号量、消息队列时可以设置超时

时钟节拍频率怎么选?我一般这样定:

应用场景 推荐节拍频率 说明
无人机飞控 1000Hz(1ms) 姿态控制需要高精度
工业控制 100Hz(10ms) 大多数传感器够用
低功耗设备 10Hz(100ms) 省电优先

个人经验:节拍频率不是越高越好。频率太高,系统频繁切换任务,CPU开销大。我做过一个项目,把节拍设成10kHz,结果CPU 30%的时间都在处理节拍中断,得不偿失。一般来说,1kHz是个不错的起点。

时钟节拍的实现,通常用硬件定时器:

// 配置SysTick定时器(Cortex-M内核)
void vSetupTickInterrupt(void) {
    // 设置重装载值
    // 假设系统时钟72MHz,节拍1ms
    SysTick->LOAD = 72000 - 1;
    
    // 使能中断
    SysTick->VAL = 0;
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
                    SysTick_CTRL_TICKINT_Msk |
                    SysTick_CTRL_ENABLE_Msk;
}

// 节拍中断处理
void SysTick_Handler(void) {
    // 调用RTOS的节拍处理函数
    xPortSysTickHandler();
}

这里有个坑:SysTick的时钟源要选对。有些芯片用内核时钟,有些用外部时钟。选错了,节拍时间就不准了。我刚开始做移植时,就因为这个原因,任务延时总是偏大,查了两天才找到问题。

小结

好了,这一讲的内容就这些。总结一下:

  • RTOS的核心是确定性、及时性、可靠性
  • 任务调度决定了系统响应速度
  • 中断管理要遵循「快进快出」原则
  • 时钟节拍是系统的时间基准

下一讲,我们会深入FreeRTOS的内核结构,看看这些概念是怎么在代码里实现的。到时候我会带大家分析源码,手把手教大家移植。咱们下节课见!

课后思考:如果你设计的无人机需要同时处理姿态控制(1ms周期)、GPS数据接收(10ms周期)、日志记录(100ms周期),你会怎么分配任务优先级?为什么?