找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 372|回复: 2
打印 上一主题 下一主题
收起左侧

STC的中断嵌套打断优先级实验

[复制链接]
跳转到指定楼层
楼主
ID:266429 发表于 2026-10-1 23:02 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 慢慢思考 于 2026-10-14 15:32 编辑

前面的帖子讲过,中断中所谓的打断优先级,是指的一件中断发生后,CPU可以打断正在执行的低级别的中断,立即转而执行这个刚发生的中断。
我们的这个实验的目的,一是观察正在执行的低级别的中断被一个高级别的中断打断它的执行,另一个,是证实对于STC来说高标号的中断就是高级别的中断,第三个,是当高级别的中断执行完成后,是否转回去执行先前被打断的中断。
我们的这个实验,在设计时,必须注意的是,那个计划被打断的中断,一定是正在执行中,而不能是可能出现计划被打断的中断在退出间隙发生了另一个高级别的中断。
这个实验中,所采用的中断源为两个,一个是定时器T0,打断级别设定为3;一个是定时器T1,打断级别设定为1。
程序运行过程是这样的:程序开始时,T1的是运行LED电位反转中断程序以驱动LED闪烁,当然每次电位反转都是一次T1的中断函数进出。待闪烁20次后,用while(1)让T1停在中断函数中即不再退出,并让LED保持在亮状态,这样T1就一直在中断的程序中运行。
程序开始时,T0也在运行中也在不断进出它的中断,但在自身中断次数小于200时并不对LED作任何控制,而一旦中断次数达到200次,则控制LED状态为灭,并用while(1)让T0停在其中断函数中不再退出。
所以,程序运行时,我们会看到LED先闪烁20次,然后就处于长亮状态,然后再过一段时间,我们会看到LED就灭了并一直处于灭的状态。
实验中,我们可以把T0和T1换一个位置,看看结果怎样。
程序很简单:
#include  "stc8h.h"

#define  LED  P54

void LED_init(void)
{
P5M0&=~(1<<4);P5M1&=~(1<<4);
}

void TIM0_init(void)   //T0初始化
{
  TMOD&=~(1<<3);//定时器功能
        TMOD&=~(3<<0);//16位自动重装模式
        AUXR&=~(1<<7);//对时钟作12分频作为定时器的工作频率
  TL0=0xB0;
        TH0=0x3C;
        IP|=(1<<1);IPH|=(1<<1);//优先级3
        ET0=1;//允许定时器溢出中断
        TR0=1;//启动定时器
}

void TIM1_init(void)    //T1初始化
{
  TMOD&=~(1<<6);//定时器功能
        TMOD&=~(3<<4);//16位自动重装模式
        AUXR&=~(1<<6);//对时钟作12分频作为定时器的工作频率
  TL1=0xB0;
        TH1=0x3C;
        IP|=(1<<3);IPH&=~(1<<3);//优先级1
        ET1=1;//允许定时器溢出中断
        TR1=1;//启动定时器
}

void main()                          
{
     LED_init();
     LED=0;
     TIM0_init();
     TIM1_init();
     EA  =1;                         //开总中断
             while(1);
}

void tm1() interrupt  3   //这个的优先级是1
{
         unsigned char num;
num++;
         LED=~LED;
if(num==40)          //闪烁20次之后
{
num=0;
               LED=1;
while(1);        
}
}

void tm0() interrupt  1   //这个的优先级是3
{
         unsigned char num;
            num++;
         if(num==200)
         {
                    LED=0;        
            while(1);
          }
}
  另外,我们也可以不采用while(1)来让程序一直停留在中断函数中这一招,而是用延时程序替代而让其只在中断函数中停留一段时间后就退出,然后我们观察一下是个什么情况,这其中就包括高级别的T0退出后是否会让T1再次进出它的中断,也就是观察它是否会再次掌握对LED的控制权。
这部分我就不写了。
补充说明一下,尽管因为while(1)而让程序停留在中断函数中不出来了,但定时器仍然在嘀嘀嗒嗒地反复计时反复产生中断,但CPU是不再响应那些中断包括T0、T1自己产生的中断的,除非又有一个更高级别的中断发生了。不过嘛,对于STC 来说标号3已经是最高级别的中断了。
以上有没有问题?恳请指正。


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶顶 踩踩
回复

使用道具 举报

沙发
ID:891178 发表于 2026-10-7 11:05 | 只看该作者
也是很典型的LED灯闪
回复

使用道具 举报

板凳
ID:155507 发表于 2026-10-15 09:04 | 只看该作者
下面把你文中准确的部分和需要纠正的部分分别说明,尤其是几个关键错误——你最后那段"中断嵌套实验"的程序实际跑不出你描述的现象。

一、你理解正确、值得肯定的部分
1.定时器工作的"三步走"类比(调出定时功能 → 装入初值 → 启动)——这是对的。
2.溢出标志位 TF0 的处理——你说的没错。手册明确写:"CPU 在响应中断后才会自动将其清零"。所以:
- 用中断方式:进中断后 TF0 由硬件自动清零,你不需要再写 TF0=0;
- 用查询方式:TF0 不会被自动清零,必须软件手动清零。
你文中"不是所有中断源都采用这个机制,一定要先看手册"这句话非常专业,是对的。
3.计数初值算法:16 位计数器最大值 65535,计满溢出。要计 N 次数,初值 = 65536 − N。12MHz、12分频 = 1MHz,计 50000 次 = 50ms,初值 15536 = 0x3CB0,所以 TL0=0xB0; TH0=0x3C;。完全正确。
4.STC8H 只有两级中断优先级(打断优先级 + 查询优先级),高编号中断号打断优先级更高——这个判断是对的,STC8 系列确实没有像 Cortex-M/CH32 那样的第三级。

二、必须纠正的几点
1. 关于优先级设定的错误(重要)
你说"高编号的级别高于低编号的级别,对于 STC8 来说"——这句放在中断号(查询顺序)上是对的,但打断优先级不是看编号,而是看 IP/IPH 里设定的值(0~3 级)。

- 打断优先级:由 IP 和 IPH 对应位组合决定,共 4 级(0/1/2/3),3 最高。
- 同打断优先级时:才按中断号(查询顺序)决定谁先被响应,中断号小的先响应。

⚠️ 你文中写"高编号的级别高于低编号"如果用来指导打断优先级,是反的。STC8 的查询顺序是:中断号越小越优先(比如中断号 1 的 T0 排在中断号 3 的 T1 前面)。请务必核对手册"中断系统"章节的查询顺序表。

2. 代码里 num 变量未初始化——这是致命 bug
void tm1() interrupt 3
{
    unsigned char num;   // ← 未初始化,值随机
    num++;               // 每次进中断临时变量,出中断就销毁
    ...
    if(num==40)          // 几乎永远进不去

num 是局部自动变量,每次进中断重新分配、值不确定,num++ 没有任何累积意义。你后面那个"闪烁 20 次""中断 200 次"的判据根本不会成立。应改为全局/静态变量:

static unsigned char num1 = 0;

3. 中断函数里的 while(1); —— 会死锁,实验现象与描述不符
你写"让 T1 停在中断函数里不再退出",逻辑上 CPU 确实卡在这里了。但由此带来的后果你没有讲清楚,而且它直接毁掉了你设计的实验目的:

T1 的 ISR 卡在 while(1),num==40 这个条件本身就进不去(见上一条);
就算进去了,CPU 卡死在 T1 ISR,T0 的中断永远进不来(哪怕 T0 优先级更高)——因为 T0 要打断的是"当前正在执行的中断",而当前执行流已经被你的死循环锁住了,但 T0 中断本身是可以打断 T1 的(优先级 3 > 1)。
这里必须区分清楚:STC8 的中断嵌套,前提是"新中断的打断优先级严格高于当前中断的打断优先级"。 你的 T0=3、T1=1,所以 T0 能打断 T1,方向是对的。但你的代码用 while(1) 停在 ISR 里,会让人误以为是"卡住"而不是"嵌套",实验目的(观察高级打断低级、再返回低级)无法体现。正确做法应该用精确延时让高级 ISR 停留一段时间后退出,才能观察到"返回被中断的低级 ISR"。

4. "中断号小的优先级高"与你代码的设定要分清
你代码里:

T0 中断号 = 1,IP/IPH 设为 3;
T1 中断号 = 3,IP/IPH 设为 1。
这样 T0 打断优先级(3) > T1(1),T0 能打断 T1,这个设定本身没问题。但请记住:这是靠 IP/IPH 值决定的,不是靠中断号。如果两者 IP/IPH 都设成 0,那么虽然 T1 中断号大,T1 也不能打断 T0,反而 T0 因中断号小会先被响应。

三、我来给你一份能真实跑出"嵌套"现象的参考代码
思路:T1 翻转 LED,运行一段时间后用一个软延时循环在 ISR 里停留(模拟"正在处理"),期间允许被高优先级的 T0 打断;T0 每溢出一次就熄灭标志。用 num 做全局计数。

#include "stc8h.h"

#define LED P54

unsigned char t0_cnt = 0;
unsigned char t1_cnt = 0;

void LED_init(void)
{
    P5M0 &= ~(1<<4);
    P5M1 &= ~(1<<4);
}

void TIM0_init(void)          // T0: 打断优先级 3(最高)
{
    TMOD &= ~(1<<3);          // 定时器功能
    TMOD &= ~(3<<0);          // 16位自动重装
    AUXR &= ~(1<<7);          // 12分频
    TL0 = 0xB0;
    TH0 = 0x3C;               // 50ms
    IP   |= (1<<1);
    IPH  |= (1<<1);           // 优先级 3
    ET0 = 1;
    TR0 = 1;
}

void TIM1_init(void)          // T1: 打断优先级 1(较低)
{
    TMOD &= ~(1<<6);          // 定时器功能
    TMOD &= ~(3<<4);          // 16位自动重装
    AUXR &= ~(1<<6);          // 12分频
    TL1 = 0xB0;
    TH1 = 0x3C;
    IP   |= (1<<3);
    IPH  &= ~(1<<3);          // 优先级 1
    ET1 = 1;
    TR1 = 1;
}

void main(void)
{
    unsigned int i;
    LED_init();
    LED = 0;
    TIM0_init();
    TIM1_init();
    EA = 1;
    while(1);
}

/* T0 中断:中断号1,高优先级,能打断 T1 */
void tm0_isr(void) interrupt 1
{
    t0_cnt++;
    if(t0_cnt == 200)
    {
        LED = 0;              // 200次后把灯灭掉,作为“T0曾抢进来说明书”
        t0_cnt = 0;
    }
}

/* T1 中断:中断号3,低优先级。用软延时模拟“正在处理”,期间可被T0打断 */
void tm1_isr(void) interrupt 3
{
    unsigned int i;
    t1_cnt++;
    LED = ~LED;
    if(t1_cnt >= 40)          // 闪20次后改为“长期灭”
    {
        LED = 1;
        for(i = 0; i < 60000; i++);   // 长时间停留,期间T0会反复打断它
    }
}

观察点:

正常时 T1 让 LED 闪烁;
到 20 次后 T1 在 ISR 里"停留",此时若你观察 P54,会看到由于 T0 的 200 次累计(T0 每次打断 T1 并把计数累加),最终 LED 被 T0 置灭——这就证明了 T0(高优先级)打断了 T1(低优先级);
若把 T1 改成优先级 3、T0 改成 1,则 T0 再也无法打断 T1,现象会完全不同。这正是你要做的"换位置"对照实验。

四、小结
你的观点                                        判定
定时器三步走、初值算法、TF0 自动清零机制        ✅ 正确
STC8H 只有两级中断优先级                        ✅ 正确
高编号中断优先级更高                                ⚠️ 仅对查询顺序对,且方向是编号小优先;打断优先级看 IP/IPH 的值,不是编号
num 局部变量统计中断次数                        ❌ 未初始化,逻辑失效,须改全局/静态
用 while(1) 停在 ISR 做嵌套实验                        ❌ 会锁死,无法体现"返回低级中断",实验现象与描述不符

你的"理解原理、不背指令"的学习方法我很赞同,这正是希望用户掌握的。但嵌套实验的代码必须重写,否则会误导读者。

回复

使用道具 举报

无效楼层,该帖已经被删除
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表