STM32裸机开发(中断,轮询,状态机)与freeRTOS

发布于:2025-07-16 ⋅ 阅读:(20) ⋅ 点赞:(0)
  • 裸机:没有操作系统,程序是单流程的(比如一个大循环里依次执行各个功能,或者用中断嵌套处理事件)。优点是资源占用极少(几乎不占 RAM/Flash)、执行流程直观;但复杂项目里,多个功能(比如同时处理传感器、通信、显示)会互相阻塞,逻辑容易混乱(比如一个耗时的操作会卡住其他功能)。

  • 比如长期做单一类型的裸机项目(比如只写循环 + 中断的控制逻辑),可能会对复杂系统的任务管理、资源调度、多模块协同这些 “上层设计” 接触较少;遇到需要同时处理多个实时性任务时(比如一边高速采数、一边通信、一边控外设),纯裸机逻辑会越写越绕,这时候可能会觉得 “效率低”。

      裸机开发离不开中断,轮询,状态机,假如将状态机放在中断里,1ms触发一次,中断中时间片调度仍有其他任务,功能简单没有大的问题,一旦功能复杂,无论是在中断中即执行又判断,还是中断中只判断,将执行放在了主函数,实时性将变得非常差,耦合性会很强,即使放在主函数中执行进行模块分装,仍然不便于维护。其次,用轮询标志位去实现变量同步,代码量大的情况下逻辑会非常绕。即使可以用 结构体+队列 去完成任务解耦,利用缓冲特性去缓冲一些像阈值判断,解决延迟和滞后问题,但本身这样做已经再向架构思想,轻量级的RTOS靠近了。裸机开发也有架构思想(事件驱动架构,消费者-生产者架......),项目烂尾,其实就是没有架构思想。RTOS内功玩的就是架构。

一.中断中只做 “判断和标记”,主循环中做 “执行” 

初始化系统 → 启动定时器 → 进入主循环(轮询标志位)
                          │
                          ▼
                  等待定时器溢出
                          │
                          ▼
                 触发TIM2中断
                          │
                 执行ISR:
                 1. 清除硬件中断标志
                 2. 设置timerFlag = true
                          │
                 退出ISR,返回主循环
                          │
                 主循环检测到timerFlag = true
                          │
                 1. 清除timerFlag = false
                 2. 执行process_timer_event()
                          │
                 继续主循环(等待下一次事件)

二.中断中即判断也执行

┌───────────────┐
│   系统初始化   │  →  配置定时器、中断使能、GPIO等(仅执行一次)
└───────┬───────┘
        ↓
┌───────────────┐
│   启动定时器   │  →  定时器开始计数,主循环进入空闲/其他任务
└───────┬───────┘
        ↓
┌───────────────┐
│   主循环运行   │  →  执行非中断相关任务(如传感器采样、显示刷新)
└───────┬───────┘
        │
        ▼(定时器溢出时触发)
┌───────────────┐
│  进入TIM2中断  │  →  CPU暂停主循环,转去执行中断服务程序
└───────┬───────┘
        ↓
┌───────────────┐
│ 判断中断来源   │  →  检查是否为定时器溢出(TIM_GetITStatus)
└───────┬───────┘
        ├─ 否 → 退出中断,返回主循环
        │
        ▼ 是
┌───────────────┐
│ 清除硬件标志   │  →  清除定时器中断标志(TIM_ClearITPendingBit)
└───────┬───────┘
        ↓
┌───────────────┐
│ 直接执行任务   │  →  例如:LED_Toggle()(仅耗时极短的操作)
└───────┬───────┘
        ↓
┌───────────────┐
│ 退出中断服务   │  →  CPU返回主循环,继续执行被中断的任务
└───────┬───────┘
        ↓
(回到主循环,等待下一次中断)

简单示例:

一.标志位

#include "stm32f10x.h"
​
// 共享标志位
volatile bool timerFlag = false;
​
// 定时器中断服务程序(仅设置标志位)
void TIM2_IRQHandler(void) {
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  // 清除硬件中断标志
        timerFlag = true;  // 设置软件标志位
    }
}
​
// 主循环(轮询标志位并执行任务)
int main(void) {
    // 系统初始化(省略)...
    
    while (1) {
        if (timerFlag) {      // 检查标志位
            timerFlag = false;  // 清除标志位
            LED_Toggle();     // 翻转LED状态
        }
        
        // 执行其他任务...
    }
}

二.直接中断执行

// 定时器中断服务程序(直接执行任务)
void TIM2_IRQHandler(void) {
    if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  // 清除硬件中断标志
        
        // 直接在中断中执行任务(无需标志位)
        LED_Toggle();  // 翻转LED状态(假设此函数仅操作GPIO,耗时极短)
    }
}
​
// 主循环(无需轮询标志位)
while (1) {
    // 其他任务...(不受定时器事件影响)
}

比较:

特性 中断内直接执行 标志位 + 主循环执行
中断服务程序 (ISR) 完成判断 + 执行(如翻转 LED) 仅设置标志位(轻量化)
主循环 无需处理定时器事件 轮询标志位并执行任务
中断响应时间 短(直接完成任务) 稍长(需等待主循环轮询)
主循环实时性 可能受影响(ISR 执行时主循环暂停) 不受影响(ISR 快速返回)
任务复杂度限制 仅支持极简单任务(如 GPIO 操作) 可处理复杂任务(如数据计算、通信)

总结:

  • 标志位是 “中断轻量化” 的工具:通过分离 “事件检测” 和 “事件处理”,保证中断快速响应,主循环有序执行。

  • 中断内直接执行的适用场景有限:仅适合极简单任务,复杂任务必须避免,否则会破坏系统稳定性。

  • 实际开发中,优先用 “中断设标志 + 主循环处理”,除非能确保任务耗时足够短(通常建议 ISR 执行时间不超过中断周期的 10%)。

三:状态机

中断仅仅作为状态机的触发器,在嵌入式系统中,中断确实可以仅作为状态机的触发器,这种设计模式能有效分离 “事件检测” 和 “事件处理”,提升系统的稳定性和可维护性。

一、核心设计思想

中断的唯一职责:检测特定事件(如定时器溢出、按键按下),并通过标志位通知状态机。 状态机的唯一职责:在主循环中根据标志位执行对应逻辑,完成状态转移。

优势

  • 中断轻量化:中断服务程序(ISR)执行时间极短,避免阻塞其他中断。

  • 逻辑解耦:状态机逻辑与硬件中断分离,便于修改和测试。

  • 可扩展性:可轻松添加新的中断源和状态,无需修改核心逻辑。

  • FreeRTOS作为轻量级 RTOS,本质是在裸机基础上增加了 “任务调度器”,让程序能像 “多线程” 一样运行 —— 把复杂功能拆成多个独立任务,由系统自动分配 CPU 时间。优点是任务间解耦,高优先级任务能及时响应(比如紧急报警);但会占用少量资源(比如几十 KB RAM),且需要理解任务调度、同步等概念。

  • 实时操作系统(RTOS):专门用于实时控制的操作系统,能保证任务在规定时间内响应(比如工业控制中,传感器数据必须在 10ms 内处理),FreeRTOS 就是轻量级 RTOS 的典型。
  • 任务(Task):系统中最小的执行单元,类似 “线程”,每个任务有独立的栈空间和运行状态(就绪、运行、阻塞等)。比如智能手表中,“显示时间”“检测触摸”“计步” 就是不同任务。
  • 优先级(Priority):任务的重要程度标识(FreeRTOS 中数值越大优先级越高)。高优先级任务能打断低优先级任务,比如无人机的 “避障任务” 优先级必须高于 “数据上传任务”,否则可能撞机。
  • 调度器(Scheduler):RTOS 的核心组件,负责决定哪个任务运行(基于优先级或时间片),FreeRTOS 用的是 “抢占式调度”(高优先级任务随时插队)。
  • 上下文切换(Context Switch):调度器切换任务时,保存当前任务的寄存器、栈指针等状态,加载下一个任务的状态,相当于 “暂停 A 任务,继续 B 任务” 的过程,速度越快,系统响应越流畅。
  • 信号量(Semaphore):任务间同步的工具,类似 “通行证”。比如两个任务共用一个传感器,用信号量控制:一个任务拿到信号量才能读取,读完放回,避免冲突。
  • 队列(Queue):任务间传递数据的 “缓冲区”,比如传感器任务采集到数据,通过队列发给处理任务,支持异步通信(发送方不用等接收方立即处理)。
  • FreeRTOS 更适合的场景

  • 多任务并发且有优先级区分
    比如一个设备需要同时处理:传感器数据采集、屏幕显示、蓝牙通信、电机控制,且某些任务必须优先响应(如电机故障检测要比屏幕刷新紧急)。FreeRTOS 的抢占式调度能让高优先级任务及时运行,避免裸机中 “一个任务卡壳导致全局瘫痪” 的问题。
  • 任务间需要灵活通信 / 同步
    当任务间需要传递数据(如传感器任务给处理任务发数据)或协调操作(如两个任务共用一个外设),FreeRTOS 的队列、信号量等机制能简化逻辑,而裸机需要手动设计状态机或全局变量,容易出错。
  • 系统未来可能扩展功能
    如果项目初期简单,但后期可能增加更多模块(如从单一传感器扩展到多传感器 + 联网功能),用 FreeRTOS 能降低后期维护和扩展的难度,任务模块化设计更清晰。

网站公告

今日签到

点亮在社区的每一天
去签到