...
主程序正在执行,这时候定时器溢出、串口收到一个字节、或者按键被按下——中断请求来了。
CPU 现在面临一个选择:手头这条指令还没执行完,是继续执行完,还是立刻跳过去处理中断?
8051 的中断响应不是"立即"的,它有三个明确的条件:
所以中断响应的延迟不是固定的。最快的情况,中断请求来临时刚好处在最后一条指令的最后一个周期,下一个周期就能响应;最慢的可能要等好几个机器周期。
这个细节解释了一个常见的困惑:"为什么我算好的定时时间总是有一点点偏差?"——偏差的一部分就来自这里。
跳去处理中断之前,CPU 得先把"手头的活"记下来,不然回来就接不上了。8051 自动做两件事:
但注意,这远远不够。ACC、B、DPTR、以及你正在用的通用寄存器 R0~R7,这些 CPU 都不会帮你保存。它们得你自己在中断服务程序里用 PUSH 保存、RETI 前用 POP 恢复。
TIMER0_ISR:
PUSH ACC ; 保存现场
PUSH PSW
PUSH DPL
PUSH DPH
; ... 中断处理代码 ...
MOV TH0, #0xDC ; 重装初值
MOV TL0, #0x00
POP DPH ; 恢复现场
POP DPL
POP PSW
POP ACC
RETI ; 注意是 RETI,不是 RET
中断服务程序结尾必须用 RETI,不能用 RET。RETI 除了弹出返回地址,还会清除中断优先级触发器——用 RET 的话,这个中断的响应标志不会被清掉,会导致这个中断之后再也不响应了。
8051 有两级优先级。规则很简单:
| 情况 | 结果 |
|---|---|
| 高优先级请求到达,低优先级正在执行 | 可以打断(嵌套) |
| 低优先级请求到达,高优先级正在执行 | 等它做完 |
| 同级请求到达,同级正在执行 | 等它做完(同级不能嵌套) |
| 同时来两个不同级 | 高优先级先响应 |
同级不能嵌套这条经常被忽略。比如串口和定时器1都被设成低优先级,定时器1的中断服务程序执行到一半,串口请求来了——CPU 不会切过去,必须等定时器1的 ISR 完全结束。
如果你希望串口能打断定时器,就得把串口设成高优先级。
如果同时来了两个同级中断,按固定的查询顺序响应,也就是所谓的"中断优先级排队"顺序:
外部中断0 → 定时器0 → 外部中断1 → 定时器1 → 串口
高 低
这个顺序是硬件定死的,改不了。只能通过设置 IP 寄存器把它们分到不同优先级来调整。
RETI指令取指、译码、执行的具体过程,可以对着 8051 指令查表动画 一步一步走一遍,比看文字直观。
B. 直接写 HTML:...