在实时操作系统中,不同任务之间经常需要进行事件通知。例如,一个任务完成某项工作后,需要立即通知另一个任务执行后续操作。
HRTOS 提供了事件机制,可以通过 os_event_write() 发送事件,通过 os_event_wait() 等待事件。
本文以 HRTOS_Event示例为基础,通过低优先级任务、高优先级任务和事件发送任务,演示 HRTOS 中的事件通知、任务就绪以及高优先级任务抢占机制,并说明如何按照 HRTOS 自身的调度规则正确设计应用程序。
一、实验目的
本示例主要验证三个方面:
-
HRTOS 事件的基本使用方法;
-
事件如何使等待任务进入就绪状态;
-
高优先级任务进入就绪状态后如何抢占低优先级任务。
系统包含三个任务:
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 的设计方式编写应用程序。