STC8A8K64D4单片机的所有gpio都支持I/O 中断,且支持 4 种中断触发模式:下降沿中断、上升沿中断、低电平中断和高电平中断。
I/O中断的中断号如下表所示,P0端口到P7端口的中断号为37~44,都是大于31的,而KEIL中支持的中断号为0~31,因此,I/O中断的中断服务函数如果使用他们的中断号是无法编译的。
宏晶科技给出的解决方案是:借用第13号中断入口地址,详见《STC8A8K64D4系列单片机技术参考手册》中的《附录Q:关于中断号大于 31 在 Keil 中编译出错的处理》。在后面的程序编写小节,我们会说明编写程序时如何解决这个问题。
表3:I/O中断的中断号
中断号
中断向量
中断类型
37
012B H
P0口中断
38
0133 H
P1口中断
39
013B H
P2口中断
40
0143 H
P3口中断
41
014B H
P4口中断
42
0153 H
P5口中断
43
015B H
P6口中断
44
0163 H
P7口中断
从上表中,我们也可以看到,I/O中断是针对IO端口的,只有端口有独立的中断入口地址,不是每个GPIO都有独立的入口地址的,但是每个 GPIO 可独立设置是否开启I/O中断以及中断模式。
编程知识点:电平触发和边沿触发的区别下图描述了边沿触发和电平触发(低电平触发为例,高电平触发类似)的时机。
图8:端口中断模式配置寄存器(PnIM0,PnIM1)
PnIM1.x PnIM0.x的组合决定了端口中各个I/O的中断触发方式,如下表所示。
表8-:端口中断触发模式
PnIM1.x
PnIM0.x
Pn.x 口中断模式
0
0
下降沿中断
0
1
下降沿中断
1
0
低电平中断
1
1
高电平中断
配置示例:配置P0.0为低电平中断P0IM1 |= 0x01; // P0IM1的位0设置为1
P0IM0 &= ~0x01; // P0IM0的位0设置为0
配置中断优先级I/O中断的优先级由端口中断优先级控制寄存器(PINIPL,PINIPH)控制,如下图所示。STC8A8K64D4有P0~P5共6个端口,因此,STC8A8K64D4使用PINIPL和PINIPH中的位0~位5配置中断优先级。
图7-:中断优先级控制寄存器
PxIPH,PxIP:Px(x=0~5)口中断优先级控制位00: Px 口中断优先级为 0 级(最低级)。01: Px 口中断优先级为 1 级(较低级)。10: Px 口中断优先级为 2 级(较高级)。11: Px 口中断优先级为 3 级(最高级)。配置示例:配置P0端口的中断优先级为最高级3PINIPH |= 0x01; // PINIPH的位0设置为1
PINIPL |= 0x01; // PINIPL的位0设置为1
编写中断服务函数由于I/O中断的中断号都是大于31的,在 KEIL中无法直接编译,因此,根据宏晶科技提供的解决方法,我们可以借用0~31中断里面没有使用的中断号,在该中断的入口地址放一条跳转的代码,当该中断产生时,跳转语句执行,跳转到I/O中断服务函数,由此,解决中断号大于31,KEIL中无法直接编译的问题。
在借用中断号时,首先借用第13号中断入口地址,因为第13号是保留中断号,不会占用系统的其他中断,其次,再借用程序中未使用的中断号。
该方法是需要借助汇编代码实现的,具体的做法如下:
新建一个汇编语言文件isr.asm,并将该文件加入到工程的USER组。isr.asm文件中加入如下的代码,这段代码实现的是P1端口借用第13号中断入口地址,P1端口地址是0133H,如果是其他端口,修改这个中断入口地址即可。各个端口的中断入口地址可以在STC8A8K64D4的数据手册中查到(《STC8A8K64D4系列单片机技术参考手册》第958页:中断列表)。代码清单:引用头文件
CSEG AT 0133H ; P1口中断入口地址,如果是其他端口,修改这个中断入口地址即可 JMP P1INT_ISR P1INT_ISR: JMP 006BH ; 借用13号中断的中断入口地址 END 中断服务程序isr.asm文件中借用的是13中断,因此,中断服务程序中的中断号就要使用13,这样,I/O中断产生后,就可以正确地跳转到I/O中断的中断服务函数了。下面是以P1端口I/O中断的中断服务函数示例,借用了13中断,中断产生后,在中断服务函数中读取P1端口中断标志,从而确定是P1端口的哪个I/O触发了中断。
代码清单:中断服务函数示例
/************************************************************************************* 功能描述:P1口中断服务函数,借用第13号中断 参 数:无 返 回 值:无 **************************************************************************************/ void p1_isr (void) interrupt 13 { u8 psw2_st; u8 intf; psw2_st = P_SW2; P_SW2 |= 0x80; //将EAXFR位置1,使能访问XFR(扩展RAM区特殊功能寄存器) intf = P1INTF; //读出P1端口中断标志,用来判断具体是P1口的哪个I/O产生了中断 if(intf) { P1INTF = 0x00; //I/O中断标志必须软件清零 if(intf & 0x01) //P1.0口中断 { //用户功能代码 } if(intf & 0x02) //P1.1口中断 { //用户功能代码 } if(intf & 0x04) //P1.2口中断 { //用户功能代码 } if(intf & 0x08) //P1.3口中断 { //用户功能代码 } if(intf & 0x10) //P1.4口中断 { //用户功能代码 } if(intf & 0x20) //P1.5口中断 { //用户功能代码 } if(intf & 0x40) //P1.6口中断 { //用户功能代码 } if(intf & 0x80) //P1.7口中断 { //用户功能代码 } } P_SW2 = psw2_st; //将EAXFR位置0,关闭访问XFR} 外部中断实验(使用一个端口的I/O中断)注:本节的实验是在“实验2-3-2:触摸按键检测”的基础上修改,本节对应的实验源码是:“实验2-5-1:I/O中断(使用一个端口的I/O中断)”。实验内容配置P3.7为I/O中断(下降沿中断),这里使用P3.7为例,是因为P3.7连接了按键KEY1,方便测试。按下按键KEY1触发P3端口I/O中断,中断服务函数中翻转指示灯D1的状态(由亮变灭或由灭变亮)。P3端口中断号大于31的处理。代码编写新建一个名称为“gpio_int.c”的文件及其头文件“gpio_int.h”并保存到工程的“Source”文件夹,并将“gpio_int.c”加入到Keil工程中的“SOURCE”组。引用头文件因为在“main.c”文件中使用了“gpio_int.c”文件中的函数,所以需要引用下面的头文件“gpio_int.h”。
代码清单:引用头文件
//引用I/O中断头文件 #include "gpio_int.h" 编写跳转的汇编代码本例中,我们只使用了一个端口的I/O中断,因此,我们借用13号中断即可。在STC8A8K64D4的数据手册中(《STC8H系列单片机技术参考手册》第417页:STC8H中断列表)查到P3的I/O中断入口地址是“0143H”,所以,中断跳转代码如下。
代码清单:汇编跳转代码
CSEG AT 0143H ;P3口中断入口地址,如果是其他端口,修改这个中断入口地址即可 JMP P3INT_ISR P3INT_ISR: JMP 006BH ;跳转到13号中断的入口地址 END 编写中断初始化函数中断初始化函数中将P3端口中的P3.7 I/O中断进行初始化,配置为下降沿触发,最低级0级的中断优先级,代码清单如下。
代码清单:中断初始化
/***************************************************************************************** 功能描述:P3端口中的P3.6 I/O中断初始化,下降沿触发,优先级:0 参 数:无 返 回 值:无 ******************************************************************************************/ void p3_int_init(void) { P_SW2 |= 0x80; //将EAXFR位置1,使能访问XFR(扩展RAM区特殊功能寄存器) P3INTF &= ~0x80; //将P3.7 I/O中断标志位清"0" P3INTE |= 0x80; //使能P3.7的I/O中断 P3IM1 &= ~0x80; //下降沿触发方式 P3IM0 &= ~0x80; PINIPH &= ~0x08; //设置P3的中断优先级为0 PINIPL &= ~0x08; P_SW2 &= 0x7F; //将EAXFR位置0,关闭访问XFR } 编写中断服务函数本例中只使用了P3端口的P3.7的I/O中断,因此,中断服务函数里面只需判断P3.7的中断标志即可(P3INTF的位7)。另外,I/O中断的标志需要软件清零,这一点和外部中断不一样,外部中断硬件会自动清零。因此,中断服务函数中需要清零I/O中断标志(P3INTF),代码清单如下。
代码清单:中断服务函数
/***************************************************************************************** 功能描述:P3口中断服务函数,我们使用了P3端口中的P3.7,因此,中断服务函数中判断P3.7的中断标志即可(P3INTF的位7) 参 数:无 返 回 值:无 *****************************************************************************************/ void p3_isr (void) interrupt 13 { u8 psw2_st; u8 intf; psw2_st = P_SW2; P_SW2 |= 0x80; //将EAXFR位置1,以访问在XDATA区域的扩展SFR intf = P3INTF; if(intf) { P3INTF = 0x00; //清零中断标志 if(intf & 0x80) //P3.7中断 { led_toggle(LED_1); //翻转用户指示灯D1 } } P_SW2 = psw2_st; } 主函数主函数中配置P3.7为准双向输入并开启上拉电阻,接着调用P3端口的I/O中断初始化函数p3_int_init ()完成P3.7的I/O中断初始化,之后开启总中断即可。
代码清单:主函数
/***************************************************************************************** 功能描述:主函数 参 数:无 返 回 值:int类型 *****************************************************************************************/ int main(void) { P2M1 &= 0xBF; P2M0 &= 0xBF; /配置P2.6为准双向口(指示灯D1) P3M1 &= 0x7F; P3M0 &= 0x7F; //配置P3.7为准双向口(按键KEY1) p3_int_init(); //初始化P3.7 I/O中断 EA = 1; //允许总中断 while(1) { }}硬件连接本实验需要使用LED指示灯D1和按键KEY1,因此需要用跳线帽短接复用引脚的指示灯(D1)和按键(KEY1)。
图4:跳线帽短接
实验步骤解压“…第3部分:配套例程源码”目录下的压缩文件“实验2-5-2:I/O中断(使用多个端口的I/O中断)”,将解压后得到的文件夹拷贝到合适的目录,如“DSTC8”(这样做的目的是为了防止中文路径或者工程存放的路径过深导致打开工程出现问题)。双击“…multi_gpio_intproject”目录下的工程文件“multi_gpio_int.uvproj”。点击编译按钮编译工程,编译成功后生成的HEX文件“multi_gpio_int.hex”位于工程的“…multi_gpio_intProjectObject”目录下。打开STC-ISP软件下载程序,下载使用内部IRC时钟,IRC频率选择:24MHz。程序运行后,按下按键KEY1触发P3.7的I/O中断,中断服务函数中翻转指示灯D1的状态(由亮变灭或由灭变亮),因此,可以观察到指示灯D1状态改变;按下按键KEY3触发P0.7的I/O中断,中断服务函数中翻转指示灯D2的状态(由亮变灭或由灭变亮),因此,可以观察到指示灯D2状态改变。注意事项:因为按键存在抖动,因此一次按键可能会多次触发中断,即指示灯状态会翻转多次。
相关文章
猜你喜欢