1. 实验目的
在实时操作系统中,中断并不是只发生在普通应用任务运行期间。
对于一个完整的实时系统来说,中断可能在系统运行的任意时刻到来,包括:
-
普通任务运行期间;
-
任务切换过程中;
-
系统调度过程中;
-
内核执行关键操作期间。
因此,一个实时操作系统除了需要验证普通任务之间的调度是否正确,还需要进一步验证:
当 CPU 正处于 HRTOS 内核执行过程中时,如果硬件中断到来,系统能否正确响应中断,并在中断处理完成后恢复原有执行流程。
本实验正是针对这一问题设计的。
通过 Timer1 周期性产生中断,同时创建普通任务、中断任务以及嵌套中断任务,对 HRTOS 的中断打断内核运行能力进行测试。
2. 测试系统结构
本实验包含三个层次的运行对象:
┌─────────────────────────────┐
│ HRTOS 内核运行 │
│ │
│ 调度 / 任务切换 / 内核处理 │
└──────────────┬──────────────┘
│
│ 硬件中断到来
▼
┌─────────────────────────────┐
│ T1 中断处理 │
│ 优先级 9 │
└──────────────┬──────────────┘
│
│ 更高优先级中断
▼
┌─────────────────────────────┐
│ T1 嵌套中断层 │
│ 优先级 10 │
└─────────────────────────────┘
与此同时,系统还运行两个普通任务:
TASK_A Priority 4
TASK_B Priority 4
因此整个实验同时包含:
-
普通任务运行;
-
同优先级任务调度;
-
硬件 Timer1 中断;
-
中断打断内核;
-
中断优先级;
-
中断嵌套;
-
中断退出后的系统恢复。
这使得它并不是一个简单的“LED 中断实验”,而是一个针对 HRTOS 内核与中断协同工作能力 的测试程序。
3. 普通任务
实验首先创建两个普通任务:
#define TASK_A 1
#define TASK_B 2
#define PRIO_TASK 4
两个任务具有相同的优先级。
任务 A:
static void task_a(void)
{
LED_A = 1;
while (1)
{
count_a++;
LED_A = ~LED_A;
os_delay(TICK_TASK);
}
}
任务 B:
static void task_b(void)
{
LED_B = 1;
while (1)
{
count_b++;
LED_B = ~LED_B;
os_delay(TICK_TASK);
}
}
两个任务分别完成:
任务运行
↓
计数器增加
↓
LED 翻转
↓
延时 20
↓
重新进入调度
它们的作用并不是本实验的重点,而是提供一个持续运行的 HRTOS 应用环境。
这样 Timer1 中断发生时,系统并不是停留在一个简单的裸机 while(1) 中,而是在真实的任务调度环境中运行。
4. Timer1 初始化
Timer1 配置代码如下:
void Timer1_Init(void)
{
TMOD &= 0x0F;
TMOD |= 0x10; // T1模式1
TH1 = 0xFC;
TL1 = 0x18; // 12MHz 1ms
TF1 = 0;
ET1 = 1;
PT1 = 1; // T1高优先级
TR1 = 1;
}
这里使用 Timer1 模式 1。
在 12MHz 时钟条件下,通过:
TH1 = 0xFC;
TL1 = 0x18;
设置定时器初值,使 Timer1 周期性产生中断。
同时:
ET1 = 1;
打开 Timer1 中断。
而:
PT1 = 1;
将 Timer1 设置为较高的硬件中断优先级。
最后:
TR1 = 1;
启动 Timer1。
5. T1 中断处理
T1 中断处理函数:
static void t1_isr(void)
{
count_t1++;
TH1 = 0xFC;
TL1 = 0x18;
/*
* T1进入中断
*/
LED_T1 = ~LED_T1;
/*
* 中断处理完成
*/
os_interrupt_exit();
}
每次 Timer1 产生中断后,首先:
count_t1++;
增加中断计数。
然后重新装载定时器:
TH1 = 0xFC;
TL1 = 0x18;
之后翻转:
LED_T1 = ~LED_T1;
通过 LED 可以直观看到 Timer1 中断是否持续进入。
最后调用:
os_interrupt_exit();
通知 HRTOS:
当前中断处理已经完成,可以退出当前中断处理流程。
因此,中断处理的基本过程可以表示为:
Timer1 到期
↓
产生硬件中断
↓
进入 HRTOS 中断处理
↓
保存当前运行环境
↓
执行 t1_isr()
↓
更新中断计数
↓
重新装载 Timer1
↓
处理中断
↓
os_interrupt_exit()
↓
恢复原有运行环境
6. 中断优先级设计
本实验非常关键的一部分,是 HRTOS 对中断运行层级的设计。
代码中定义:
#define PRIO_TASK 4
#define PRIO_T1 9
#define PRIO_T1_NEST 10
可以看到:
Priority 10 T1 嵌套中断
↑
Priority 9 T1 普通中断
↑
Priority 4 普通任务
因此 HRTOS 的运行层级可以理解为:
普通任务
↓
普通中断
↓
嵌套中断
这里体现的是一种非常明确的实时优先级层次。
普通任务处于较低优先级。
Timer1 中断处于更高的中断优先级。
而嵌套中断又处于更高一级。
7. 什么叫“中断打断内核”?
这是本实验最核心的概念。
普通情况下,我们可能只考虑:
任务 A
↓
Timer1 中断
↓
返回任务 A
这种情况实际上只说明:
中断可以打断应用任务。
但对于操作系统来说,这还不够。
HRTOS 的 CPU 并不总是在运行应用任务。
例如系统可能正在执行:
任务切换
↓
保存任务上下文
↓
选择下一个任务
↓
恢复任务上下文
这些操作属于内核运行过程。
此时如果 Timer1 中断到来,就会形成:
HRTOS 内核
↓
正在执行内核代码
↓
Timer1 中断到来
↓
中断打断内核
↓
执行中断处理
↓
中断退出
↓
恢复被打断的内核运行
↓
继续完成原来的内核操作
这才是本实验真正想验证的内容。
8. 为什么这个测试很重要?
因为内核代码与普通应用代码存在一个重要区别:
内核代码往往直接操作任务上下文、调度状态以及 CPU 寄存器环境。
如果中断在不合适的时刻进入,而系统没有正确保存和恢复现场,就可能产生非常严重的问题。
例如:
内核正在切换任务
↓
中断突然进入
↓
修改 CPU 上下文
↓
中断返回
↓
原来的上下文恢复错误
↓
任务异常
严重情况下可能表现为:
-
程序跑飞;
-
任务无法继续运行;
-
LED 停止变化;
-
系统进入异常状态;
-
栈数据破坏;
-
长时间运行后出现随机错误。
因此,中断打断内核之后还能否正确恢复,是判断实时内核可靠性的重要测试之一。
9. HRTOS 中断嵌套测试
系统入口中创建了两个中断运行层:
os_task_create((unsigned int)t1_isr, TASK_T1, PRIO_T1, STACK_T1);
以及:
os_task_create((unsigned int)t1_isr, TASK_T1, PRIO_T1_NEST, STACK_T1);
对应:
T1 普通中断任务
Priority 9
T1 嵌套中断任务
Priority 10
这意味着 HRTOS 不只是测试普通中断,还进一步建立了更高优先级的嵌套中断运行层。
因此测试覆盖的范围进一步扩大:
普通任务
↓
中断
↓
更高优先级中断
如果在这种情况下系统仍然能够持续运行,就可以进一步验证中断上下文管理和嵌套处理能力。
10. 中断退出为什么需要专门的 API?
本实验使用:
os_interrupt_exit();
而不是简单地从中断函数返回。
这是因为对于 HRTOS 来说,中断不仅仅是普通 C 函数调用。
进入中断以后,系统可能涉及:
-
CPU 上下文;
-
当前任务状态;
-
中断层级;
-
调度状态;
-
中断嵌套状态;
-
中断返回后的任务恢复。
因此,中断处理完成以后,需要通过 HRTOS 提供的中断退出机制完成对应的系统处理。
本实验中:
os_interrupt_exit();
就是整个中断处理流程的重要组成部分。
因此不能简单地把它理解成一个普通的函数调用。
11. 实验观察方法
本实验使用三个 LED:
sbit LED_A = P1^0;
sbit LED_B = P1^1;
sbit LED_T1 = P1^2;
分别对应:
P1.0 → 任务 A
P1.1 → 任务 B
P1.2 → Timer1 中断
同时还有三个计数变量:
static u16 count_a = 0;
static u16 count_b = 0;
static u16 xdata count_t1 = 0;
它们可以分别用于观察:
count_a → 任务 A 是否正常运行
count_b → 任务 B 是否正常运行
count_t1 → Timer1 中断是否持续进入
因此,如果系统长时间运行后:
-
A LED 正常变化;
-
B LED 正常变化;
-
T1 LED 持续变化;
-
三个计数器持续增长;
-
系统没有跑飞;
那么就说明在测试条件下,中断与 HRTOS 任务运行之间能够持续协同工作。
12. 与普通裸机中断实验的区别
传统 8051 裸机中断测试通常是:
while (1)
{
// 主循环
}
然后:
Timer1 中断
↓
执行 ISR
↓
返回主循环
这种实验主要验证:
8051 硬件中断能否正常工作。
而 HRTOS 的测试环境则复杂得多:
HRTOS 内核
│
┌────────┴────────┐
│ │
任务 A 任务 B
│ │
└────────┬────────┘
│
Timer1
│
▼
中断处理
│
更高优先级嵌套
│
▼
中断退出
│
▼
恢复原有执行流程
所以这个实验的目标已经从:
“中断能不能工作”
提升到了:
“中断在 HRTOS 运行过程中能否正确打断系统,并保持内核运行环境的一致性。”
这两者的测试难度并不是一个级别。
13. 完整测试代码
#include "hrtos.h"
/*==================== 配置 ====================*/
#define TASK_A 1
#define TASK_B 2
#define TASK_T1 0
#define PRIO_TASK 4
#define PRIO_T1 9
#define PRIO_T1_NEST 10
#define STACK_TASK_A 5
#define STACK_TASK_B 5
#define STACK_T1 0
#define TICK_TASK 20
/*==================== LED 定义 ====================*/
sbit LED_A = P1^0; /* 任务A指示灯 */
sbit LED_B = P1^1; /* 任务B指示灯 */
sbit LED_T1 = P1^2; /* T1中断指示灯 */
/*==================== 全局 ====================*/
static u16 count_a = 0;
static u16 count_b = 0;
static u16 xdata count_t1 = 0;
/*==================== 普通任务A ====================*/
static void task_a(void)
{
LED_A = 1;
while (1)
{
count_a++;
LED_A = ~LED_A;
os_delay(TICK_TASK);
}
}
/*==================== 普通任务B ====================*/
static void task_b(void)
{
LED_B = 1;
while (1)
{
count_b++;
LED_B = ~LED_B;
os_delay(TICK_TASK);
}
}
void Timer1_Init(void)
{
TMOD &= 0x0F;
TMOD |= 0x10; // T1模式1
TH1 = 0xFC;
TL1 = 0x18; // 12MHz 1ms
TF1 = 0;
ET1 = 1;
PT1 = 1; // T1高优先级
TR1 = 1;
}
/*==================== T1中断处理 ====================*/
static void t1_isr(void)
{
count_t1++;
TH1 = 0xFC;
TL1 = 0x18; // 12MHz 1ms
/*
* T1进入中断
*/
LED_T1 = ~LED_T1;
/*
* 中断处理完成
*/
os_interrupt_exit();
}
/*==================== 系统入口 ====================*/
void hrtos_main(void)
{
Timer1_Init();
/*
* 两个同优先级普通任务
*/
if (os_task_create(task_a, TASK_A, PRIO_TASK, STACK_TASK_A) != 1)
{
while (1);
}
if (os_task_create(task_b, TASK_B, PRIO_TASK, STACK_TASK_B) != 1)
{
while (1);
}
/*
* T1普通中断任务
* 优先级9
*/
os_task_create((unsigned int)t1_isr, TASK_T1, PRIO_T1, STACK_T1);
/*
* T1嵌套中断任务
* 优先级10
*/
os_task_create((unsigned int)t1_isr, TASK_T1, PRIO_T1_NEST, STACK_T1);
}
14. 这个实验真正验证了什么?
这个程序的价值并不在于 Timer1 本身。
Timer1 只是一个中断触发源。
真正需要验证的是下面这一条链路:
HRTOS 正常运行
↓
CPU 执行内核代码
↓
硬件中断产生
↓
中断打断当前内核执行
↓
进入中断处理
↓
中断处理完成
↓
os_interrupt_exit()
↓
恢复原来的运行环境
↓
HRTOS 继续执行
进一步加入中断嵌套后,则变成:
HRTOS 内核
↓
T1 中断
↓
更高优先级中断
↓
嵌套中断退出
↓
T1 中断继续/退出
↓
恢复 HRTOS
因此,这个实验实际上是在对 HRTOS 最底层的内核—中断协同机制进行压力验证。
15. AI 编写 HRTOS 中断程序需要注意什么?
这个实验同样非常适合用于建立 HRTOS 的 AI 编程知识体系。
对于 AI 来说,看到:
os_interrupt_exit();
不能简单按照其他 RTOS 的中断 API 进行推测。
HRTOS 的中断模型具有自己的任务优先级和中断层级设计:
普通任务
Priority 4
普通中断
Priority 9
嵌套中断
Priority 10
因此,AI 在生成 HRTOS 中断程序时,需要首先依据 HRTOS 自身的 API 和任务模型进行设计,而不是直接套用其他 RTOS 的中断处理模板。
尤其需要注意:
中断处理程序、普通任务和内核调度代码并不是同一种运行上下文。
如果不了解这一点,很容易生成虽然“语法正确”,但不符合 HRTOS 实际运行机制的程序。
16. 总结
HRTOS_InterruptNesting 并不是一个简单的 Timer1 中断例程。
它的核心测试目标是:
验证 HRTOS 内核运行过程中受到硬件中断打断后,系统能否正确处理中断,并恢复原有的内核运行流程。
实验同时引入普通任务、Timer1 中断以及更高优先级的嵌套中断,形成:
普通任务
↓
HRTOS 内核
↓
Timer1 中断
↓
嵌套中断
↓
中断退出
↓
恢复 HRTOS
对于一个 8051 实时操作系统来说,这类测试比单纯的 LED、任务创建测试更加接近内核底层可靠性验证。
也正因为如此,中断打断内核的测试,是 HRTOS 从“能够运行”进一步走向“能够长期可靠运行”的重要测试环节。
网硕互联帮助中心




评论前必须登录!
注册