HRTOS应用示例:事件机制与高优先级任务抢占详解

在实时操作系统中,不同任务之间经常需要进行事件通知。例如,一个任务完成某项工作后,需要立即通知另一个任务执行后续操作。

HRTOS 提供了事件机制,可以通过 os_event_write() 发送事件,通过 os_event_wait() 等待事件。

本文以 HRTOS_Event示例为基础,通过低优先级任务、高优先级任务和事件发送任务,演示 HRTOS 中的事件通知、任务就绪以及高优先级任务抢占机制,并说明如何按照 HRTOS 自身的调度规则正确设计应用程序。


一、实验目的

本示例主要验证三个方面:

  1. HRTOS 事件的基本使用方法;

  2. 事件如何使等待任务进入就绪状态;

  3. 高优先级任务进入就绪状态后如何抢占低优先级任务。

系统包含三个任务:

复制代码
low_task
    ↓
低优先级持续运行

high_task
    ↓
等待 EVENT_HIGH

send_task
    ↓
周期发送 EVENT_HIGH

其中:

复制代码
PRIO_LOW  = 3
PRIO_SEND = 4
PRIO_HIGH = 6

因此:

复制代码
TASK_HIGH > TASK_SEND > TASK_LOW

优先级越高的任务,在满足运行条件后越优先获得 CPU。


二、为什么需要事件机制

在裸机程序中,任务之间经常通过全局变量进行通信:

复制代码
if (event_flag)
{
    event_flag = 0;
    // 执行操作
}

这种方式可以实现简单的通知,但随着程序复杂度增加,会出现一些问题。

例如:

复制代码
任务A
 ↓
修改全局变量
 ↓
任务B不断查询

任务 B 必须不断执行查询:

复制代码
if (event_flag)

这实际上是一种轮询方式。

而 RTOS 可以把这种关系变成:

复制代码
任务A
 ↓
发送事件
 ↓
任务B进入就绪状态
 ↓
调度器根据优先级决定运行任务B

这样任务之间的通信关系更加明确,也更符合实时操作系统的设计思想。


三、本示例的整体设计

本示例使用三个任务。

1. 低优先级任务

复制代码
TASK_LOW
PRIO_LOW = 3

持续翻转 LED_LOW

它的主要作用是作为一个低优先级背景任务,用于观察高优先级任务什么时候获得 CPU。


2. 高优先级任务

复制代码
TASK_HIGH
PRIO_HIGH = 6

等待:

复制代码
os_event_wait(EVENT_HIGH, 0)

收到事件后:

复制代码
high_count++
LED_HIGH翻转
os_delay(20)

因此,它平时处于等待状态,不会持续占用 CPU。


3. 事件发送任务

复制代码
TASK_SEND
PRIO_SEND = 4

每隔一定时间:

复制代码
os_event_write(EVENT_HIGH);

向高优先级任务发送事件。

发送完成后延时:

复制代码
os_delay(TICK_SEND);

形成周期性的事件通知。


四、核心运行流程

整个系统可以理解成:

复制代码
                EVENT_HIGH
                    │
                    ▼
              +-----------+
              | send_task |
              +-----------+
                    │
                    │ os_event_write()
                    ▼
              +------------+
              | high_task  |
              | 优先级 6   |
              +------------+
                    │
                    │ 进入就绪状态
                    ▼
              高优先级任务运行
                    │
                    ▼
                 LED_HIGH
                    │
                    ▼
                os_delay()
                    │
                    ▼
              重新等待事件

与此同时:

复制代码
low_task
    │
    ▼
持续运行

high_task 因为事件变为就绪状态后,由于它的优先级高于 low_task,调度器会优先让 high_task 运行。


五、事件初始化

系统入口首先执行:

复制代码
os_event_init(EVENT_HIGH);

这里初始化事件对象。

本示例只使用一个事件:

复制代码
#define EVENT_HIGH 1

因此:

复制代码
EVENT_HIGH
    ↓
用于 send_task → high_task 的事件通知

在实际项目中,可以根据不同功能使用不同的事件编号。


六、高优先级任务为什么需要等待事件

高优先级任务的核心代码:

复制代码
if (os_event_wait(EVENT_HIGH, 0) == 1)
{
    high_count++;

    LED_HIGH = ~LED_HIGH;

    os_delay(20);
}

这里最重要的不是 LED,而是:

复制代码
os_event_wait(EVENT_HIGH, 0)

高优先级任务并不是一直执行实际工作。

没有事件时:

复制代码
high_task
    ↓
等待 EVENT_HIGH

收到事件后:

复制代码
EVENT_HIGH
    ↓
high_task
    ↓
执行任务

这体现了 RTOS 中非常重要的设计思想:

任务没有工作时等待,有工作时立即运行。

而不是让任务一直轮询。


七、事件发送

发送任务执行:

复制代码
os_event_write(EVENT_HIGH);

这条语句就是向系统发送事件。

完整过程可以理解为:

复制代码
send_task
    │
    │ os_event_write(EVENT_HIGH)
    ▼
HRTOS事件系统
    │
    ▼
EVENT_HIGH
    │
    ▼
等待该事件的 high_task
    │
    ▼
high_task进入就绪状态

这就是任务之间最基本的事件通知过程。


八、为什么高优先级任务能够抢占低优先级任务

本示例专门设置:

复制代码
#define PRIO_LOW      3
#define PRIO_HIGH     6

因此:

复制代码
HIGH = 6
LOW  = 3

高优先级任务明显高于低优先级任务。

high_task 正在等待事件时:

复制代码
high_task
    ↓
等待

它不会运行。

此时:

复制代码
low_task
    ↓
获得CPU
    ↓
持续运行

send_task 发送:

复制代码
os_event_write(EVENT_HIGH);

之后:

复制代码
high_task
    ↓
等待状态
    ↓
收到事件
    ↓
就绪状态

由于它的优先级更高:

复制代码
high_task:6
low_task :3

调度器会优先选择 high_task

因此形成:

复制代码
low_task运行
     │
     │ EVENT_HIGH
     ▼
high_task就绪
     │
     ▼
高优先级任务获得CPU
     │
     ▼
high_task执行

这就是本示例需要验证的核心机制。


九、为什么 high_task 执行后还要延时

代码中:

复制代码
os_delay(20);

这一句非常重要。

如果高优先级任务处理完事件以后立即再次执行:

复制代码
while (1)
{
    if (os_event_wait(...))
    {
        ...
    }
}

那么任务可能会再次参与调度。

通过延时:

复制代码
os_delay(20);

可以让当前任务主动进入延时状态。

此时:

复制代码
high_task
    ↓
延时
    ↓
暂时不能运行
    ↓
其他就绪任务获得CPU

这样低优先级任务就能够重新获得运行机会。

因此,本示例形成了一个很清晰的任务切换过程:

复制代码
low_task运行
      ↓
发送事件
      ↓
high_task就绪
      ↓
high_task抢占
      ↓
high_task处理事件
      ↓
high_task延时
      ↓
low_task重新运行

十、为什么 send_task 的优先级设置为 4

三个任务的优先级分别为:

复制代码
high_task = 6
send_task = 4
low_task  = 3

这样的设计可以形成三级优先级关系:

复制代码
        high_task
          优先级6
             ▲
             │
        send_task
          优先级4
             ▲
             │
         low_task
          优先级3

其中 send_task 负责产生事件。

它并不是系统中优先级最高的任务。

真正需要实时响应事件的是:

复制代码
high_task

因此,高优先级任务必须具有最高优先级。


十一、完整代码

复制代码
#include "hrtos.h"

/*==================== 配置 ====================*/
#define TASK_LOW      1
#define TASK_HIGH     2
#define TASK_SEND     3

#define PRIO_LOW      3
#define PRIO_HIGH     6
#define PRIO_SEND     4

#define STACK_LOW     5
#define STACK_HIGH    5
#define STACK_SEND    5

#define EVENT_HIGH    1

#define TICK_SEND     100

/*==================== LED 定义 ====================*/
sbit LED_LOW  = P1^0;   /* 低优先级任务指示灯 */
sbit LED_HIGH = P1^1;   /* 高优先级任务指示灯 */
sbit LED_SEND = P1^2;   /* 发送任务指示灯 */

/*==================== 全局 ====================*/
static u8 high_count = 0;

/*==================== 低优先级任务 ====================*/
static void low_task(void)
{
    LED_LOW = 1;

    while (1)
    {
        /*
         * 低优先级任务持续运行。
         * 当高优先级任务变为就绪状态时,
         * 当前任务会立即被高优先级任务抢占。
         */
        LED_LOW = ~LED_LOW;
    }
}

/*==================== 高优先级任务 ====================*/
static void high_task(void)
{
    LED_HIGH = 1;

    while (1)
    {
        /*
         * 等待事件。
         * 收到事件后,高优先级任务立即参与调度。
         */
        if (os_event_wait(EVENT_HIGH, 0) == 1)
        {
            high_count++;

            /* 高优先级任务运行 → 翻转LED */
            LED_HIGH = ~LED_HIGH;

            /*
             * 延时后再次进入等待,
             * 使低优先级任务重新获得CPU。
             */
            os_delay(20);
        }
    }
}

/*==================== 事件发送任务 ====================*/
static void send_task(void)
{
    LED_SEND = 1;

    while (1)
    {
        /*
         * 发送事件,使高优先级任务变为就绪状态。
         */
        os_event_write(EVENT_HIGH);

        LED_SEND = ~LED_SEND;

        os_delay(TICK_SEND);
    }
}

/*==================== 系统入口 ====================*/
void hrtos_main(void)
{
    os_event_init(EVENT_HIGH);

    if (os_task_create(low_task,
                       TASK_LOW,
                       PRIO_LOW,
                       STACK_LOW) != 1)
    {
        while (1);
    }

    if (os_task_create(high_task,
                       TASK_HIGH,
                       PRIO_HIGH,
                       STACK_HIGH) != 1)
    {
        while (1);
    }

    if (os_task_create(send_task,
                       TASK_SEND,
                       PRIO_SEND,
                       STACK_SEND) != 1)
    {
        while (1);
    }
}

十二、实验现象

程序运行后,可以通过三个 LED 观察任务状态。

LED_LOW

表示低优先级任务正在运行。

LED_SEND

表示事件发送任务正在周期性运行。

LED_HIGH

表示高优先级任务收到事件并执行。

因此可以观察到:

复制代码
LED_SEND
    ↓
周期性翻转
    ↓
发送 EVENT_HIGH
    ↓
LED_HIGH响应

同时:

复制代码
LED_LOW
    ↓
持续运行
    ↓
高优先级任务就绪
    ↓
被高优先级任务抢占
    ↓
高优先级任务延时
    ↓
重新获得运行机会

这可以非常直观地验证 HRTOS 的优先级抢占机制。


十三、与裸机轮询方式的区别

如果使用裸机方式,可能会这样设计:

复制代码
while (1)
{
    if (event_flag)
    {
        event_flag = 0;

        // 执行高优先级操作
    }
}

这种方式本质上是:

复制代码
不断查询
    ↓
有没有事件?
    ↓
没有
    ↓
继续查询

而 HRTOS 的事件机制可以形成:

复制代码
任务等待事件
      ↓
等待
      ↓
事件到达
      ↓
任务进入就绪状态
      ↓
根据优先级参与调度

因此,RTOS 的事件机制实际上把:

"任务之间如何通知"

和:

"通知以后谁应该运行"

结合到了调度系统中。


十四、这个示例对 AI 编写 HRTOS 程序的意义

当前 AI 在生成嵌入式代码时,一个比较常见的问题是:

能够写出语法正确的代码,但不一定理解具体 RTOS 的调度机制。

例如,AI 可能知道:

复制代码
os_event_write()

大概是发送事件,

也知道:

复制代码
os_event_wait()

大概是等待事件。

但如果不了解 HRTOS 的实际实现,就可能错误地假设:

复制代码
发送事件
    ↓
目标任务立即执行

实际上,中间还存在:

复制代码
事件发送
    ↓
等待任务状态发生变化
    ↓
任务进入就绪状态
    ↓
调度器根据优先级选择任务
    ↓
高优先级任务获得CPU

所以,AI 编写 HRTOS 应用程序时,不能简单把其他 RTOS 的经验直接套过来。

应该首先明确:

复制代码
HRTOS API
    ↓
HRTOS任务状态
    ↓
HRTOS调度规则
    ↓
HRTOS事件机制
    ↓
最终应用代码

十五、AI 生成 HRTOS 代码时需要注意什么

对于类似事件通信的应用,可以向 AI 明确提供以下信息:

复制代码
1. 使用 HRTOS 官方事件 API。
2. 使用 os_event_init() 初始化事件。
3. 使用 os_event_write() 发送事件。
4. 使用 os_event_wait() 等待事件。
5. 事件到达后,任务进入就绪状态。
6. 调度器按照任务优先级选择运行任务。
7. 不要根据其他 RTOS 的实现方式猜测 HRTOS 行为。
8. 任务之间的通信和调度过程必须符合 HRTOS 的实际机制。

这样生成的代码才更容易真正运行在 HRTOS 上,而不是仅仅"看起来像 RTOS 代码"。


十六、总结

本示例通过三个任务完整演示了 HRTOS 的事件通信和优先级抢占机制:

复制代码
send_task
    │
    │ os_event_write()
    ▼
EVENT_HIGH
    │
    ▼
high_task
    │
    │ 进入就绪状态
    ▼
高优先级任务抢占
    │
    ▼
处理事件
    │
    ▼
os_delay()
    │
    ▼
low_task重新运行

通过这个简单实验,可以理解 HRTOS 中一个非常重要的基本模型:

事件负责唤醒任务,优先级决定任务获得 CPU 的先后。

这也是从裸机开发进入实时操作系统开发后,需要建立的重要思维方式。

对于 AI 辅助开发同样如此。只有把 HRTOS 自身的 API、任务状态和调度机制描述清楚,AI 才能从"生成类似 RTOS 的代码",逐渐转变为:

真正按照 HRTOS 的设计方式编写应用程序。

相关推荐
大漠飞鹰66662 小时前
【Uart串口-GD32无标题】
stm32·单片机·嵌入式硬件
西峰u2 小时前
Java Socket网络编程|TCP与UDP深度梳理
单片机·嵌入式硬件
至为芯2 小时前
PY32F002B至为芯集成32位ARM内核的低成本MCU单片机
单片机·集成电路·芯片
亮工硬件2 小时前
1-8 简易串口通讯协议(UART + 自定义帧协议)
笔记·stm32·单片机·嵌入式硬件·学习
三佛科技-187366133973 小时前
小家电/玩具MCU选型指南,国产低成本MCU替代之选
单片机·嵌入式硬件
花 满 楼3 小时前
TPWM — PWM 测量模式(TIM PWM Measurement Mode)
单片机·嵌入式硬件·fpga开发
周洲083013 小时前
STM32 最简工程搭建|标准库/HAL库规范化模板,零冗余可直接落地
stm32·单片机·嵌入式硬件
HRTOS16 小时前
HRTOS 4.0 驱动库:06_Storage 存储设备驱动——24C02 EEPROM与I²C驱动开发
c语言·驱动开发·嵌入式硬件·51单片机
周洲083017 小时前
STM32 串口不定长接收|空闲中断 IDLE 方案,无固定帧长限制,项目实战
stm32·单片机·嵌入式硬件