版权声明:本文为博主原创文章,转载请附上原文出处链接。
接着之前单机中断原理,再跟大家说说外部中断!
STC8A8K64S4A12开发板上设计了4个用户轻触按键,其中KEY1和KEY2按键可以为外部中断引脚提供触发信号,电路如下。
■ 开发板5个可用外部中断引脚如下表:
INT | 对应IO口 | 功能描述 | 说明 | 备注 |
---|---|---|---|---|
INT0 | P3.2 | 外部中断0 | 非独立GPIO | LCD显示屏模块用。 |
INT1 | P3.3 | 外部中断1 | 非独立GPIO | nRF24L01模块接口J11使用。 |
INT2 | P3.6 | 外部中断2 | 非独立GPIO | W5500模块接口J7使用。 |
INT3 | P3.7 | 外部中断3 | 非独立GPIO | W5500模块接口J7使用。 |
INT4 | P3.0 | 外部中断4 | 非独立GPIO | 板载USB转TTL电路用。 |
☆注:独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。针对非独立GPIO使用时需特别注意。
STC8A8K64S4A12系列MCU不是每一个GPIO口都可以作为外部中断输入使用,只有特定的GPIO口才可以作为特定的外部中断输入使用。STC8A8K64S4A12系列MCU具有5个外部中断引脚,列表如下。
序号 | INT | 对应IO口 | 功能描述 | 备注 |
---|---|---|---|---|
1 | INT0 | P3.2 | 外部中断0 | 中断优先级可设置0/1/2/3。 |
2 | INT1 | P3.3 | 外部中断1 | 中断优先级可设置0/1/2/3。 |
3 | INT2 | P3.6 | 外部中断2 | 只能为最低中断优先级0。 |
4 | INT3 | P3.7 | 外部中断3 | 只能为最低中断优先级0。 |
5 | INT4 | P3.0 | 外部中断4 | 中断优先级可设置0/1/2/3。 |
单片机外部中断引脚电平变化了,就有可能触发中断,进而执行中断服务程序。电平变化有可能是高电平到低电平(即产生下降沿),也可能是低电平到高电平(即产生上升沿)。STC8A8K64S4A12系列外部中断引脚支持的触发方式是不一样的。如下表。
序号 | 触发方式 | INT | 备注 |
---|---|---|---|
1 | 下降沿触发 | INT0、INT1、INT2、INT3、INT4 | |
2 | 上升沿触发 | 无 | 指只可上升沿触发的意思。 |
3 | 上升沿或下降沿触发 | INT0、INT1 |
实现GPIO口电平变化的方式有很多,使用外部按键改变GPIO口电平是一种简单直观的方式。但在实际使用中单片机的外部中断功能是应用于一些模块或芯片的中断引脚上,比如W5500以太网模块、nRF24L01无线模块等。
下面介绍下带有中断引脚(IRQ)的模块或传感器与单片机连接示意图。
■ 需要弄清楚的问题。
■ 延伸出的问题。
■ 回答延伸出的问题。
单片机中断方式,是事件触发的,占用的CPU资源相对很少。换言之打开中断之后,CPU可以做其他事情,只有当符合条件的事件产生时才会进入中断,去执行中断服务程序。举例,针对STC8A8K64S4A12系列单片机,前面介绍的下降沿中断或上升沿中断都是触发中断的条件,在这个条件不满足时单片机是不用理会外部中断引脚的,而一旦条件满足,如果程序中没有其他同时发生的更高优先级的中断的话,CPU会立即执行该外部中断对应的中断服务函数。
单片机查询方式,就是在主函数里面不停循环,查询端口状态,这会占用大量CPU资源。这时模块或传感器IRQ引脚是可以和单片机普通IO口引脚相连的。这种方式的弊端:
☆注:单片机查询方式的缺点就是单片机中断方式的优点。当然,单片机查询方式的优点是可以使用普通IO口甚至不使用IO口。
针对STC8A8K64S4A12系列单片机5个外部中断引脚,软件的配置过程如下:
☆注:实验例程即是按照上述配置步骤操作寄存器相关位实现,后有详述。
首先普及一个常用知识点:在操作单片机寄存器时常说有的寄存器支持位寻址,有的寄存器不支持位寻址?这里的位寻址究竟是什么含义?
位寻址定义:对位地址中的内容进行位操作的寻址方式称为位寻址。由于单片机中只有内部RAM和特殊功能寄存器的部分单元有位地址,因此位寻址只能对有位地址的这两个空间进行寻址操作。
单片机中不是每个寄存器都支持位寻址的,一般如果SFR(特殊功能寄存器)的地址值能被8整除,则该SFR可以进行位寻址。举例P0端口数据寄存器是支持位寻址的。
☆注:单片机中寄存器地址值如不能被8整除,则该寄存器是不支持位寻址的。针对STC8A8K64S4A12系列有些SFR是位于扩展RAM中,这些SFR不可以按照这个总结的规则去判定是否支持位寻址。访问位于扩展RAM中的SFR需要先操作P_SW2的BIT7才可,这个在用到时讲解。
中断允许寄存器IE支持位寻址,该寄存器的B7位是单片机所有中断的总闸,即其他中断位使能后,最后还必须将这个总中断打开才能开启中断,与外部中断有关的还有B0和B2位,含义如下图。
外部中断允许和时钟输出寄存器INTCLKO2不支持位寻址,该寄存器的B4、B5和B6位是外部中断2、外部中断3和外部中断4的中断允许位。因为AUXR2寄存器不支持位寻址,所以操作该寄存器位时,不可以直接“EX2=0;”进行操作INT2中断允许位,参考下图。
定时器/计数器中断控制寄存器TCON支持位寻址,该寄存器的B0位和B2位是选择INT0和INT1中断源类型的(可理解成中断触发方式),寄存器B1位和B3位是INT0和INT1中断请求标志,含义如下图。
☆注:单片机中很多寄存器都像TCON寄存器一样,寄存器关联的单片机外设不止一个,这就需要我们在操作寄存器时一定要按位操作,用不到的位千万不要操作,否则在程序比较复杂、用到的外设比较多时,往往会遇到不可知问题。
☆注:本节的实验源码是在“实验2-2-2: GPIO输入按键检测(多个c文件)”的基础上修改。本节对应的实验源码是:“实验2-4-1:外部中断0(下降沿中断方式)”。
本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。
序号 | 文件名 | 后缀 | 功能描述 |
---|---|---|---|
1 | led | .c | 包含与用户led控制有关的用户自定义函数。 |
2 | exint | .c | 外部中断有关的用户自定义函数。 |
3 | delay | .c | 包含用户自定义延时函数。 |
■ 需要引用的头文件
#include "delay.h"
#include "led.h"
#include "exint.h"
■ 需要包含的头文件路径
本例需要包含的头文件路径如下表:
序号 | 路径 | 描述 |
---|---|---|
1 | …\ Source | led.h、exint.h和delay.h头文件在该路径,所以要包含。 |
2 | …\User | STC8.h头文件在该路径,所以要包含。 |
MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
首先,在exint.c文件中编写外部中断0初始化函数INT0_Init,代码如下。
程序清单:外部中断0初始化函数
/***********************************************************
功能描述:外部中断0初始化
入口参数:无
返回值:无
************************************************************/
void INT0_Init(void)
{
IE0 = 0; //将INT0中断请求标志位清"0"
EX0 = 1; //使能INT0中断允许位
IT0 = 1; //选择INT0为下降沿触发方式
}
然后,编写外部中断服务函数,一旦进入中断会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:外部中断服务程序
入口参数:无
返回值:无
************************************************************/
void INT0_Isr (void) interrupt 0
{
led_toggle(LED_3); //翻转蓝色指示灯D3
}
最后,在主函数中调用INT0初始化函数,开启总中断,在主循环中没有任务,蓝色指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
INT0_Init(); //外部中断0的初始化配置
EA = 1; //允许总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
本实验需要使用P3.2(外部中断0引脚)连接到J26端子KEY1,因此需要按下图所示连接杜邦线。
☆注:本节的实验源码是在“实验2-4-1:外部中断0(下降沿中断方式)”的基础上修改。本节对应的实验源码是:“实验2-4-2:外部中断0(上升沿或下降沿中断方式)”。
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-4-1:外部中断0(下降沿中断方式)”部分。
首先,在exint.c文件中编写外部中断0初始化函数INT0_Init,代码如下。
程序清单:外部中断0初始化函数
/***********************************************************
功能描述:外部中断0初始化
入口参数:无
返回值:无
************************************************************/
void INT0_Init(void)
{
IE0 = 0; //将INT0中断请求标志位清"0"
EX0 = 1; //使能INT0中断允许位
IT0 = 0; //选择INT0为上升沿或下降沿触发方式
}
然后,编写外部中断服务函数,一旦进入中断会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:外部中断服务程序
入口参数:无
返回值:无
************************************************************/
void INT0_Isr (void) interrupt 0
{
led_toggle(LED_3); //翻转蓝色指示灯D3
}
最后,在主函数中调用INT0初始化函数,开启总中断,在主循环中没有任务,蓝色指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
INT0_Init(); //外部中断0的初始化配置
EA = 1; //允许总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
本实验需要使用P3.2(外部中断0引脚)连接到J26端子KEY1,因此需要按下图所示连接杜邦线。
☆注:本节的实验源码是在“实验2-4-1:外部中断0(下降沿中断方式)”的基础上修改。本节对应的实验源码是:“实验2-4-3:外部中断1(下降沿中断方式)”。
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-4-1:外部中断0(下降沿中断方式)”部分。
首先,在exint.c文件中编写外部中断1初始化函数INT1_Init,代码如下。
程序清单:外部中断1初始化函数
/***********************************************************
功能描述:外部中断1初始化
入口参数:无
返回值:无
************************************************************/
void INT1_Init(void)
{
IE1 = 0; //将INT1中断请求标志位清"0"
EX1 = 1; //使能INT1中断允许位
IT1 = 1; //选择INT1为下降沿触发方式
}
然后,编写外部中断服务函数,一旦进入中断会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:外部中断服务程序
入口参数:无
返回值:无
************************************************************/
void INT1_Isr (void) interrupt 2
{
led_toggle(LED_3); //翻转蓝色指示灯D3
}
最后,在主函数中调用INT1初始化函数,开启总中断,在主循环中没有任务,蓝色指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
INT1_Init(); //外部中断1的初始化配置
EA = 1; //允许总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
本实验需要使用P3.3(外部中断1引脚)连接到J26端子KEY1,因此需要按下图所示连接杜邦线。
☆注:本节的实验源码是在“实验2-4-3:外部中断1(下降沿中断方式)”的基础上修改。本节对应的实验源码是:“实验2-4-4:外部中断1(上升沿或下降沿中断方式)”。
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-4-1:外部中断0(下降沿中断方式)”部分。
首先,在exint.c文件中编写外部中断1初始化函数INT1_Init,代码如下。
程序清单:外部中断1初始化函数
/***********************************************************
功能描述:外部中断1初始化
入口参数:无
返回值:无
************************************************************/
void INT1_Init(void)
{
IE1 = 0; //将INT1中断请求标志位清"0"
EX1 = 1; //使能INT1中断允许位
IT1 = 0; //选择INT1为上升沿或下降沿触发方式
}
然后,编写外部中断服务函数,一旦进入中断会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:外部中断服务程序
入口参数:无
返回值:无
************************************************************/
void INT1_Isr (void) interrupt 2
{
led_toggle(LED_3); //翻转蓝色指示灯D3
}
最后,在主函数中调用INT1初始化函数,开启总中断,在主循环中没有任务,蓝色指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
INT1_Init(); //外部中断1的初始化配置
EA = 1; //允许总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
本实验需要使用P3.3(外部中断1引脚)连接到J26端子KEY1,因此需要按下图所示连接杜邦线。
☆注:本节的实验源码是在“实验2-4-1:外部中断0(下降沿中断方式)”的基础上修改。本节对应的实验源码是:“实验2-4-5:外部中断2(下降沿中断方式)”。
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-4-1:外部中断0(下降沿中断方式)”部分。
首先,在exint.c文件中编写外部中断2初始化函数INT2_Init,代码如下。
程序清单:外部中断2初始化函数
int main(void)
/***********************************************************
功能描述:外部中断2初始化
入口参数:无
返回值:无
************************************************************/
void INT2_Init(void)
{
INTCLKO |= 0x10; //使能INT2中断允许位
}
然后,编写外部中断服务函数,一旦进入中断会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:外部中断服务程序
入口参数:无
返回值:无
************************************************************/
void INT2_Isr (void) interrupt 10
{
led_toggle(LED_3); //翻转蓝色指示灯D3
}
最后,在主函数中调用INT2初始化函数,开启总中断,在主循环中没有任务,蓝色指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
INT2_Init(); //外部中断2的初始化配置
EA = 1; //允许总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
本实验需要使用P3.6(外部中断2引脚)连接到按键KEY2,因此需要按下图所示连接短路帽。
==☆注:本节的实验源码是在“实验2-4-1:外部中断0(下降沿中断方式)”的基础上修改。本节对应的实验源码是:“实验2-4-6:外部中断3(下降沿中断方式)”。 ==
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-4-1:外部中断0(下降沿中断方式)”部分。
首先,在exint.c文件中编写外部中断3初始化函数INT3_Init,代码如下。
程序清单:外部中断3初始化函数
/***********************************************************
功能描述:外部中断3初始化
入口参数:无
返回值:无
************************************************************/
void INT3_Init(void)
{
INTCLKO |= 0x20; //使能INT3中断允许位
}
然后,编写外部中断服务函数,一旦进入中断会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:外部中断服务程序
入口参数:无
返回值:无
************************************************************/
void INT3_Isr (void) interrupt 11
{
led_toggle(LED_3); //翻转用户指示灯D3
}
最后,在主函数中调用INT3初始化函数,开启总中断,在主循环中没有任务,蓝色指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
INT3_Init(); //外部中断3的初始化配置
EA = 1; //允许总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
本实验需要使用P3.7(外部中断3引脚)连接到按键KEY1,因此需要按下图所示连接短路帽。
==☆注:本节的实验源码是在“实验2-4-1:外部中断0(下降沿中断方式)”的基础上修改。本节对应的实验源码是:“实验2-4-7:外部中断4(下降沿中断方式)”。 ==
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-4-1:外部中断0(下降沿中断方式)”部分。
首先,在exint.c文件中编写外部中断4初始化函数INT4_Init,代码如下。
程序清单:外部中断4初始化函数
/***********************************************************
功能描述:外部中断4初始化
入口参数:无
返回值:无
************************************************************/
void INT4_Init(void)
{
INTCLKO |= 0x40; //使能INT4中断允许位
}
然后,编写外部中断服务函数,一旦进入中断会执行翻转蓝色指示灯D3的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:外部中断服务程序
入口参数:无
返回值:无
************************************************************/
void INT4_Isr (void) interrupt 16
{
led_toggle(LED_3); //翻转用户指示灯D3
}
最后,在主函数中调用INT4初始化函数,开启总中断,在主循环中没有任务,蓝色指示灯D3变化来自于中断。
代码清单:主函数
int main(void)
{
INT4_Init(); //外部中断4的初始化配置
EA = 1; //允许总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
本实验需要使用P3.0(外部中断4引脚)连接到J26端子KEY1,因此需要按下图所示连接杜邦线。
==☆注:本节的实验源码是在“实验2-4-1:外部中断0(下降沿中断方式)”的基础上修改。本节对应的实验源码是:“实验2-4-8:多个外部中断”。 ==
本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-4-1:外部中断0(下降沿中断方式)”部分。
首先,在exint.c文件中编写外部中断0初始化函数INT0_Init和外部中断1初始化函数INT1_Init,代码如下。
程序清单:外部中断0初始化函数
/***********************************************************
功能描述:外部中断0初始化
入口参数:无
返回值:无
************************************************************/
void INT0_Init(void)
{
IE0 = 0; //将INT0中断请求标志位清"0"
EX0 = 1; //使能INT0中断允许位
IT0 = 1; //选择INT0为下降沿触发方式
/* 将INT0优先级设置为最低优先级 */
IP &= 0xFE; //将PX0位置0
IPH &= 0xFE; //将PX0H位置0
}
程序清单:外部中断1初始化函数
/***********************************************************
功能描述:外部中断1初始化
入口参数:无
返回值:无
************************************************************/
void INT1_Init(void)
{
IE1 = 0; //将INT1中断请求标志位清"0"
EX1 = 1; //使能INT1中断允许位
IT1 = 1; //选择INT1为下降沿触发方式
/* 将INT1优先级设置为最高优先级 */
IP |= 0x04; //将PX1位置1
IPH |= 0x04; //将PX1H位置1
}
然后,编写外部中断服务函数,一旦进入外部中断0会执行翻转4次蓝色指示灯D3的任务,进入外部中断1会执行翻转4次蓝色指示灯D4的任务,代码如下。
程序清单:中断服务函数
/***********************************************************
功能描述:外部中断服务程序
入口参数:无
返回值:无
************************************************************/
void INT0_Isr (void) interrupt 0
{
led_on(LED_3); //点亮用户指示灯D3
delay_ms(200);
led_off(LED_3); //熄灭用户指示灯D3
delay_ms(100);
led_on(LED_3); //点亮用户指示灯D3
delay_ms(200);
led_off(LED_3); //熄灭用户指示灯D3
delay_ms(100);
led_on(LED_3); //点亮用户指示灯D3
delay_ms(200);
led_off(LED_3); //熄灭用户指示灯D3
delay_ms(100);
led_on(LED_3); //点亮用户指示灯D3
delay_ms(200);
led_off(LED_3); //熄灭用户指示灯D3
delay_ms(100);
}
程序清单:中断服务函数
/***********************************************************
功能描述:外部中断服务程序
入口参数:无
返回值:无
************************************************************/
void INT1_Isr (void) interrupt 2
{
led_on(LED_4); //点亮用户指示灯D4
delay_ms(200);
led_off(LED_4); //熄灭用户指示灯D4
delay_ms(100);
led_on(LED_4); //点亮用户指示灯D4
delay_ms(200);
led_off(LED_4); //熄灭用户指示灯D4
delay_ms(100);
led_on(LED_4); //点亮用户指示灯D4
delay_ms(200);
led_off(LED_4); //熄灭用户指示灯D4
delay_ms(100);
led_on(LED_4); //点亮用户指示灯D4
delay_ms(200);
led_off(LED_4); //熄灭用户指示灯D4
delay_ms(100);
}
最后,在主函数中调用INT0和INT1初始化函数,开启总中断。在主循环中没有任务,蓝色指示灯D3和蓝色指示灯D4变化来自于中断。
代码清单:主函数
int main(void)
{
INT0_Init(); //外部中断0的初始化配置
INT1_Init(); //外部中断1的初始化配置
EA = 1; //允许总中断
while (1)
{
; //无任务,说明LED亮灭来自于中断
}
}
本实验需要使用P3.2(外部中断0引脚)连接到J26端子KEY1,P3.3(外部中断1引脚)连接到J26端子KEY2,因此需要按下图所示连接杜邦线。