学习目标:
- 理解掌握,通过实例验证学习掌握情况
学习内容:
- 外部中断程序
- 如何设置闲时模式和掉电模式
- 唤醒机制
- 完整代码示例
正文:
一、外部中断程序
在51单片机(如STC89C52RC)中,外部中断是一种重要的功能,用于响应外部事件(如按键按下、传感器触发等)。以下是关于51单片机外部中断的详细说明:
外部中断概述
51单片机通常支持 2个外部中断:
- INT0:对应P3.2引脚
- INT1:对应P3.3引脚
每个外部中断可以配置为 电平触发 或 边沿触发,并通过中断服务函数(ISR)处理事件。
寄存器配置
51单片机的外部中断相关寄存器如下:
1. TCON寄存器(定时器/计数器控制寄存器)
位 | 名称 | 功能描述 |
---|---|---|
BIT7 | TF1 | 定时器1溢出标志 |
BIT6 | TR1 | 定时器1运行控制位 |
BIT5 | TF0 | 定时器0溢出标志 |
BIT4 | TR0 | 定时器0运行控制位 |
BIT3 | IE1 | 外部中断1(INT1)触发标志 |
BIT2 | IT1 | 外部中断1触发方式控制(0=电平,1=边沿) |
BIT1 | IE0 | 外部中断0(INT0)触发标志 |
BIT0 | IT0 | 外部中断0触发方式控制(0=电平,1=边沿) |
- IT0/IT1:
0
:低电平触发1
:下降沿触发
2. IE寄存器(中断使能寄存器)
位 | 名称 | 功能描述 |
---|---|---|
BIT7 | EA | 总中断使能(1=使能,0=禁止) |
BIT6 | - | 保留 |
BIT5 | ET2 | 定时器2中断使能 |
BIT4 | ES | 串口中断使能 |
BIT3 | ET1 | 定时器1中断使能 |
BIT2 | EX1 | 外部中断1(INT1)使能 |
BIT1 | ET0 | 定时器0中断使能 |
BIT0 | EX0 | 外部中断0(INT0)使能 |
- EX0/EX1:
1
:使能外部中断0
:禁止外部中断
外部中断配置步骤
- 设置触发方式:
- 配置
TCON
寄存器的IT0
和IT1
位,选择电平触发或边沿触发。
- 配置
- 使能中断:
- 配置
IE
寄存器的EX0
和EX1
位,使能外部中断。 - 配置
IE
寄存器的EA
位,开启总中断。
- 配置
- 编写中断服务函数:
- 使用
interrupt
关键字定义中断服务函数。
- 使用
代码示例
1. 外部中断初始化
void Init_External_Interrupt() {
IT0 = 1; // 设置INT0为下降沿触发
IT1 = 1; // 设置INT1为下降沿触发
EX0 = 1; // 使能INT0中断
EX1 = 1; // 使能INT1中断
EA = 1; // 开启总中断
}
2. 中断服务函数
// INT0中断服务函数
void INT0_ISR() interrupt 0 {
// 处理INT0事件
}
// INT1中断服务函数
void INT1_ISR() interrupt 2 {
// 处理INT1事件
}
触发方式详解
1. 电平触发
- 配置:
IT0 = 0
或IT1 = 0
- 特点:
- 当引脚为低电平时,持续触发中断。
- 需要在中断服务函数中清除外部信号,否则会重复触发。
2. 边沿触发
- 配置:
IT0 = 1
或IT1 = 1
- 特点:
- 当引脚电平从高到低变化时,触发一次中断。
- 适用于按键等短时信号。
注意事项
-
消抖处理:
- 按键等机械开关会产生抖动,需在硬件或软件中消抖。
- 硬件消抖:在按键两端并联0.1μF电容。
- 软件消抖:在中断服务函数中加入延时检测。
-
中断优先级:
- 51单片机的中断优先级固定为:
INT0 > T0 > INT1 > T1 > UART
。 - 若需修改优先级,可使用IP寄存器。
- 51单片机的中断优先级固定为:
-
中断标志位:
- 电平触发时,需在中断服务函数中清除外部信号,避免重复触发。
- 边沿触发时,硬件会自动清除标志位。
示例:按键控制LED
功能描述
- 按下按键1(INT0)时,LED状态翻转。
- 按下按键2(INT1)时,LED熄灭。
代码实现
/*********************************************************************************************/
#include <REG51.h> //51头文件
#include <intrins.h>
sbit LED = P1 ^ 0; //定义LED灯
//注:外部中断固定与P3.2和P3.3复用,无需定义这两个接口。
/*********************************************************************************************
函数名:外部中断INT初始化函数
调 用:INT_init();
参 数:无
返回值:无
结 果:启动外部中断INT1、INT0中断,设置中断方式
备 注:
/**********************************************************************************************/
void INT_init (void){
EA = 1; //中断总开关
EX1 = 1; //允许外部中断1中断
EX0 = 1; //允许外部中断0中断
IT1 = 1; //1:下沿触发 0:低电平触发
IT0 = 1; //1:下沿触发 0:低电平触发
}
/**********************************************************************************************/
/*********************************************************************************************
函数名:外部中断INT1中断处理程序
调 用:[外部引脚INT1中断处理]
参 数:无
返回值:无
结 果:用户处理外部中断信号
备 注:
/**********************************************************************************************/
void INT_1 (void) interrupt 2 using 2{ //切换寄存器组到2
LED = 1; //关灯
}
/**********************************************************************************************/
/*********************************************************************************************
函数名:外部中断INT0中断处理程序
调 用:[外部引脚INT0中断处理]
参 数:无
返回值:无
结 果:用户处理外部中断信号
备 注:
/**********************************************************************************************/
void INT_0 (void) interrupt 0 using 2{ //切换寄存器组到2
LED = 0; //开灯
}
/**********************************************************************************************/
/*********************************************************************************************/
void main(void){
INT_init(); //外部中断初始化
while(1){
PCON |= 0x01; // 设置IDL位进入空闲模式 (PCON |= 0x02;进入掉电模式)
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); // 唤醒后继续执行此处
_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
}
}
二、空闲模式 vs 掉电模式
特性 | 空闲模式(Idle Mode) | 掉电模式(Power Down Mode) |
---|---|---|
功耗 | 较低(CPU停止,外设和时钟仍运行) | 极低(CPU和外设均停止,时钟关闭) |
唤醒方式 | 任何中断(外部中断、定时器中断、串口中断等) | 仅外部中断(INT0/INT1)或复位信号(RST) |
唤醒后执行位置 | 从进入空闲模式的下一条指令继续执行 | 从进入掉电模式的下一条指令继续执行 |
时钟状态 | 系统时钟继续运行 | 系统时钟停止 |
外设状态 | 外设保持运行状态 | 外设停止运行 |
唤醒时间 | 较短(仅需恢复CPU运行) | 较长(需重新启动时钟和外设) |
典型应用场景 | 短时低功耗,需快速唤醒 | 长时间低功耗,仅需外部事件唤醒 |
寄存器配置 | `PCON | = 0x01;`(设置IDL位) |
唤醒后外设恢复 | 无需恢复,外设保持运行 | 需重新初始化外设(如定时器、串口等) |
电流消耗 | 约1-5mA(取决于外设运行状态) | 约0.1μA-1μA(极低功耗) |
代码示例对比
空闲模式代码
void Enter_Idle_Mode() {
PCON |= 0x01; // 设置IDL位进入空闲模式
_nop_(); // 唤醒后继续执行此处
_nop_();
}
掉电模式代码
void Enter_PowerDown_Mode() {
PCON |= 0x02; // 设置PD位进入掉电模式
_nop_(); // 唤醒后继续执行此处
_nop_();
}
三、唤醒机制对比
空闲模式唤醒
- 唤醒源:
- 任何中断(如外部中断、定时器中断、串口中断等)。
- 唤醒流程:
- 中断触发后,CPU立即恢复运行,从进入空闲模式的下一条指令继续执行。
掉电模式唤醒
- 唤醒源:
- 仅外部中断(INT0/INT1)或复位信号(RST)。
- 唤醒流程:
- 中断触发后,系统时钟重新启动,CPU从进入掉电模式的下一条指令继续执行。
功耗对比
模式 | 典型电流消耗(3.3V供电) |
---|---|
空闲模式 | 1-5mA |
掉电模式 | 0.1μA-1μA |
四、完整代码示例(以51单片机为例)
- 空闲模式适合需要快速唤醒且外设需保持运行的场景,功耗相对较高。
#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit LED = P1^0; // LED指示灯(低电平点亮)
sbit KEY_IDLE = P3^2; // 空闲模式按键(INT0)
sbit KEY_WAKEUP = P3^3; // 唤醒按键(INT1)
bit idle_flag = 0; // 空闲模式标志位
bit wakeup_flag = 0; // 唤醒标志位
/* 延时函数 */
void delay_ms(uint ms) {
uint i, j;
for(i=0; i<ms; i++)
for(j=0; j<114; j++);
}
/* 进入空闲模式 */
void enter_idle_mode() {
LED = 1; // 熄灭LED
PCON |= 0x01; // 设置IDL位进入空闲模式
_nop_(); // 等待唤醒后继续执行
_nop_();
LED = 0; // 唤醒后点亮LED
}
/* 中断初始化 */
void init_interrupt() {
IT0 = 1; // 设置INT0下降沿触发
IT1 = 1; // 设置INT1下降沿触发
EX0 = 1; // 使能INT0中断(空闲模式按键)
EX1 = 1; // 使能INT1中断(唤醒按键)
EA = 1; // 开启总中断
}
/* 主函数 */
void main() {
LED = 0; // 初始化点亮LED
init_interrupt();
while(1) {
// 检测模式标志位
if(idle_flag) {
idle_flag = 0;
enter_idle_mode();
}
// 检测唤醒按键
if (KEY_WAKEUP == 0) {
delay_ms(10); // 消抖
if (KEY_WAKEUP == 0) {
wakeup_flag = 1; // 设置唤醒标志
}
while (KEY_WAKEUP == 0); // 等待按键释放
}
if (wakeup_flag) {
wakeup_flag = 0;
// 唤醒后的处理逻辑
LED = 0; // 点亮LED
// 可以在此添加其他唤醒后的操作
}
}
}
/* INT0中断服务函数(空闲模式) */
void int0_isr() interrupt 0 {
delay_ms(20); // 消抖处理
if(KEY_IDLE == 0) { // 确认按键按下
idle_flag = 1; // 设置空闲模式标志
}
}
/* INT1中断服务函数(唤醒按键) */
void int1_isr() interrupt 2 {
delay_ms(20); // 消抖处理
if(KEY_WAKEUP == 0) { // 确认按键按下
wakeup_flag = 1; // 设置唤醒标志
}
}
- 掉电模式适合长时间低功耗场景,功耗极低,但唤醒源有限且唤醒时间较长。
#include <reg52.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
sbit LED = P1^0; // LED指示灯(低电平点亮)
sbit KEY_PDOWN = P3^2; // 掉电模式按键(INT0)
sbit KEY_WAKEUP = P3^3; // 唤醒按键(INT1)
bit pd_flag = 0; // 掉电模式标志位
bit wakeup_flag = 0; // 唤醒标志位
/* 延时函数 */
void delay_ms(uint ms) {
uint i, j;
for(i=0; i<ms; i++)
for(j=0; j<12; j++);
}
/* 进入掉电模式 */
void enter_power_down() {
LED = 1; // 熄灭LED
PCON |= 0x02; // 设置PD位进入掉电模式
_nop_(); // 唤醒后从下一指令继续执行
_nop_();
LED = 0; // 唤醒后点亮LED
}
/* 中断初始化 */
void init_interrupt() {
IT0 = 1; // 设置INT0下降沿触发
IT1 = 1; // 设置INT1下降沿触发
EX0 = 1; // 使能INT0中断(掉电模式按键)
EX1 = 1; // 使能INT1中断(唤醒按键)
EA = 1; // 开启总中断
}
/* 主函数 */
void main() {
LED = 0; // 初始化点亮LED
init_interrupt();
while(1) {
// 检测模式标志位
if(pd_flag) {
pd_flag = 0;
enter_power_down();
}
// 检测唤醒按键
if (KEY_WAKEUP == 0) {
delay_ms(10); // 消抖
if (KEY_WAKEUP == 0) {
wakeup_flag = 1; // 设置唤醒标志
}
while (KEY_WAKEUP == 0); // 等待按键释放
}
if (wakeup_flag) {
wakeup_flag = 0;
// 唤醒后的处理逻辑
LED = 0; // 点亮LED
// 可以在此添加其他唤醒后的操作
}
}
}
/* INT0中断服务函数(掉电模式) */
void int0_isr() interrupt 0 {
delay_ms(20); // 消抖处理
if(KEY_PDOWN == 0) { // 确认按键按下
pd_flag = 1; // 设置掉电模式标志
}
}
/* INT1中断服务函数(唤醒按键) */
void int1_isr() interrupt 2 {
delay_ms(20); // 消抖处理
if(KEY_WAKEUP == 0) { // 确认按键按下
wakeup_flag = 1; // 设置唤醒标志
}
}
@外部中断代码参考来源于http://www.doyoung.net/DY/program/INT/index.html