230 likes | 414 Views
第 6 章 中断系统. §6.1 中断的一般概念. 中断向量,是中断源提供的处理程序的入口,其入口处,是由系统统一划拨存储空间,给各中断源存放中断处理程序的入口,即中断向量表。 中断响应过程:申请 - 响应( 终止当前程序 - 保护断点 - 转到处理程序。 ) - 中断处理( 保护现场 - 处理 - 清该标志、恢复现场 - 返回。软件 )。. CPU 对突发事件响应方式 中断源及中断申请 开中断与关中断 中断优先级及中断嵌套 中断向量与中断处理. 硬件实现. 一个应用系统,面对处理器众多的中,是根据应用来选取允许中断的事件与不允许中断的事件。. 软件完成.
E N D
§6.1 中断的一般概念 中断向量,是中断源提供的处理程序的入口,其入口处,是由系统统一划拨存储空间,给各中断源存放中断处理程序的入口,即中断向量表。 中断响应过程:申请-响应(终止当前程序-保护断点-转到处理程序。)-中断处理(保护现场-处理-清该标志、恢复现场-返回。软件)。 • CPU对突发事件响应方式 • 中断源及中断申请 • 开中断与关中断 • 中断优先级及中断嵌套 • 中断向量与中断处理 硬件实现 一个应用系统,面对处理器众多的中,是根据应用来选取允许中断的事件与不允许中断的事件。 软件完成
中断类型与中断源 • 异常中断-RESET,向量地址为FFF7H。优先级最高。 • 软中断-执行指令BREAK,向量地址为FFF5H。优先级最低。 • 事件中断-外部与片内I/O事件。快速中断FIQ与中断IRQ。FIQ优先级高于IRQ 。
事件中断 如果同时有两个以上的IRQ中断时,按此优先级响应。但较“高级别”IRQ中断不能中断“较低级别”IRQ中断。即在IRQ中断中不允许中断嵌套。 中断服务程序以此取名。
中断控制寄存器 • P_INT_Ctrl(7010H) 读/写 读,中断标志字,用于判别产生中断的源。写,选择允许中断的源。 • P_INT_Clear (7011H) 写 清中断标志。选择的源与中断标志字对应。写“1”清该中断标志。 • P_INT_Ctrl_New(702DH) 读/写 激活与屏蔽中断。读,了解哪些中断源被屏蔽。写,对应0的中断源被屏蔽。对应1的中断源被激活。
中断控制字 每一位对应一个事件,如果通过[P_INT_Ctrl]向某位写1,则允许该事件向CPU申请中断。 如果通过[P_INT_Ctrl]读,是读中断标志,置1的位,说明该事件已向CPU申请了中断。 [P_INT_Ctrl_New]端口的每一位,也对应表中的事件。 如果通过[P_INT_Clear]向某位写1,则清除该事件的中断标志。
中断控制指令 注意:只有在P_INT_Ctrl中设置了中断源允许位为“1”, 相应开/关中断指令才有意义。
[P_IOB_ATTRI]=r1 //IOB口设置为同相高电平输出口 • [P_IOB_DIR]=r1 • [P_IOB_DATA]=r1; • R1=0x000C • [P_INT_Ctrl ]=R1 • [P_INT_CTRL_New ]=R1 • R1=0 • [TIME2]=R1 • [TIME4]=R1 • INT IRQ • Loop: • NOP • NOP • JMP Loop • //============================ • .TEXT • .PUBLIC _IRQ5 • _IRQ5:PUSH R1,R5 TO [SP]R1 = 0x0008;TEST R1,[P_INT_Ctrl]; //中断识别JNZ L_IRQ5_4Hz; [例6.1]IRQ5有2个中断源,2Hz和4Hz。TIME2对2Hz中断计数, TIME4对4Hz中断计数。2Hz中断控制A口0~3LED以2S速率闪烁, 4Hz中断控制B口0~3LED以1S速率闪烁。 L_IRQ5_4Hz:R1=0x0008[P_INT_Clear] = R1; //清中断R2=[TIME4] //计数器+1R2+=1[TIME4]=R2CMP R2,2 //是否0.5S? JBE LED4Hz_OFF //否, LED灭 R1=0xFFF0 //是, LED亮 [P_IOB_DATA]=R1 CMP R2,4 //是否1S? JBE LED4Hz_RET //否,中断返回 R2=0 //是, 清TIME4 [TIME4]=R2 JMP LED4Hz_RET LED4Hz_OFF: R1=0xFFFF [P_IOA_DATA]=R1 LED4Hz_RET POP R1,R5 FROM [SP];RETI; L_IRQ5_2Hz:R1=0x0004[P_INT_Clear] = R1; //清中断R2=[TIME2] //计数器+1R2+=1[TIME2]=R2CMP R2,2 //是否1S? JBE LED2Hz_OFF //否, LED灭 R1=0xFFF0 //是, LED亮 [P_IOA_DATA]=R1 CMP R2,4 //是否2S? JBE LED2Hz_RET //否,中断返回 R2=0 //是, 清TIME2 [TIME2]=R2 JMP LED2Hz_RET LED2Hz_OFF: R1=0xFFFF [P_IOA_DATA]=R1 LED2Hz_RET POP R1,R5 FROM [SP];RETI; • .DEFINE P_IOA_DATA 0x7000 • .DEFINE P_IOA_DIR 0x7002 • .DEFINE P_IOA_ATTRI 0x7003 • .DEFINE P_IOB_DATA 0x7005 • .DEFINE P_IOB_DIR 0x7007 • .DEFINE P_IOB_ATTRI 0x7008 • .DEFINE P_INT_Ctrl 0x7010 • .DEFINE P_INT_CLEAR 0x7011 • .DEFINE P_INT_CTRL_New 0x702D • .RAM • .VAR TIME2 //2Hz计数器 • .VAR TIME4 //4Hz计数器 • .CODE • .PUBLIC _main //主程序 • _main: • INT off • R1=0xffff //r1的值为0xffff • [P_IOA_ATTRI]=r1 //IOA口设置为同相高电平输出 • [P_IOA_DIR]=r1 • [P_IOA_DATA]=r1; 否,就是2Hz中断。 Y
[例6.2]键唤醒,A口键盘输入,B口LED输出。 这个循环起什么作用? 低位LED会亮吗?程序如何修改? 交换后,高位LED会亮吗?程序如何修改? R1= 0xFFFE //低位为0 R4=0xFFFF R4=R4 LSL 4 //SR置全1 R2=0xFFFF L_Loopin: R2 - =1 JNZ L_Loopin R1=R1 ROL 1 //循环左移 [P_IOB_Data]=R1 //LED显示 CMP R1,0xFF7F //是否最后一个LED JNE L_Loopin R1=0xFFFE JMP L_Loopin .include Hardwore.inc //硬件资源接口 .define P_IOA_RL 0x7004 // P_IOA_Latch .CODE .PUBLIC _main _main: r1=0 //IOA为带下拉电阻输入。 [P_IOA_Dir]=r1 [P_IOA_Attr]=r1 [P_IOA_Data]=r1 r1=0xFFFF //IOB低电平输出 [P_IOB_Dir]=r1 [P_IOB_Attrib] = r1 r1=0 [P_IOA_Data]=r1 INT OFF Keydown: R1= [P_IOA_Data] CMP R1,0x0001 JNE Keydown Keyup: R1= [P_IOA_Data] CMP R1,0x0000 JNE Keyup R1=0xFFFF [P_IOA_Data]=R1 //LED灭? R1=0x0080 [P_INT_Ctrl]=R1 //开IRQ3_KEY中断 R1=[P_IOA_RL] //激活键唤醒 INT IRQ R1=0x0007 [P_SystemClock]=R1 //CPU入睡 // _IRQ3中断服务程序 .TEXT .PUBLIC _IRQ3 _IRQ3: PUSH R1, R4 TO [SP] R1=0x0080 TEST R1, [P_INT_Ctrl] JZ L_NOTKeyArouse R1=0x0080 [P_INT_Clear]=R1 L_NOTKeyArouse: POP R1, R4 FROM [SP] RETI .END [P_IOB_Data]=R1 //LED显示 R1=R1 ROL 1 //循环左移 CMP R1,0xFEFF
用C语言编写中断服务程序 函数库SPCE061.LIB提供了相关的底层函数,中断服务程序可以直接用这些函数来描述。
SPCE061.LIB中与中断相关的函数 函数原形: void Set_INT_Ctrl(unsigned int); void Set_INT_Mask(unsigned int); unsigned int Get_INT_Ctrl(void); unsigned int Get_INT_Mask(void); void INT_Clear(unsigned int); void FIQ_ON(void ) void FIQ_OFF(void); void IRQ_ON(void); void IRQ_OFF(void); void INT_FIQ(void); void INT_IRQ(void); void INT_FIQ_IRQ(void); void INT_OFF(void); FIQ_ON(); FIQ ON FIQ_OFF(); FIQ OFF IRQ_ON(); IRQ ON IRQ_OFF(); IRQ OFF INT_FIQ(); INT FIQ INT_IRQ(); INT IRQ INT_FIQ_IRQ(); INT FIQ_IRQ INT_OFF(); INT OFF
[例6.4]A口的LED按1S速率闪烁 #include “SPCE061.H” unsigned int g_uioutput = 0; void IRQ5(void)_attribute_((ISR)) void IRQ5(void) { if (*P_INT_Ctrl & 0x0004) { //IRQ5_2Hz *P_IOA_Data = g_uioutput; g_uioutput ^=0xFFFF; //取反 *P_INT_Clear = 0x0004; // C_IRQ5_2Hz } else { // IRQ5_4Hz *P_INT_Clear = 0x0008; //C_IRQ5_4Hz } } ISR-interrupt service routine #include “SPCE061.H” main() { asm(“INT OFF”); *P_IOA_Dir = 0x00FF; *P_IOA_Attib = 0x00FF; *P_IOA_DATA = 0x0000; //A口为输出,低电平。 *P_INT_Ctrl = C_IRQ5_2Hz; //0004H P_INT_Ctrl asm(“INT IRQ”); while(1) //死循环。 *P_Watchdog_Clear = C_WDTCLR } 如果不喜欢使用SPCE061.H,也不想包含SPCE061.lib,你也可以象下面这样写程序,其实,这里只是将SPCE061.lib中我们用到的部分代码摘出来了。 volatile unsigned int *P_IOA_Data= (unsigned int*)(0x7000); // Data vector for IOA volatile unsigned int *P_IOA_Dir = (unsigned int*)(0x7002); // Direction vector for IOA volatile unsigned int *P_IOA_Attrib = (unsigned int*)(0x7003); // Attribute vector for IOA volatile unsigned int *P_IOB_Data = (unsigned int*)(0x7005); // Data vector for IOB volatile unsigned int *P_IOB_Dir = (unsigned int*)(0x7007); // Direction vector for IOB volatile unsigned int *P_IOB_Attrib = (unsigned int*)(0x7008); // Attribute vector for IOB
[例6.5]不同中断入口的中断源程序举例。LED,A口为1S、B口0.5S闪烁。[例6.5]不同中断入口的中断源程序举例。LED,A口为1S、B口0.5S闪烁。 #include “SPCE061.H” unsigned int g_uiIOA_LED = 0xFF, g_uiIOB_LED = 0xFF; unsigned int g_uiClockCnt = 0; void IRQ2(void)_attribute_((ISR)); void IRQ2(void) //TimerB 中断 { *P_IOB_Data = g_uiIOB_LED ; g_uiIOB_LED ^= 0xFFFF; //取反 *P_INT_Clear = 0x0400; // 清该中断标志 } void IRQ4(void)_attribute_((ISR)); void IRQ4(void) { IF *P_INT_Ctrl & C_IRQ4_1KHz // 如果是IRQ4_1KHz中断 { IF g_uiClockCnt <1024 //且计数器<1024 g_uiClockCnt++ ; // 计数器加1 ELSE { *P_IOA_Data = g_uiIOA_LED ;//显示 g_uiIOA_LED ^= 0xFFFF; //取反,下一个显示状态 g_uiClockCnt = 0 //计数器清0 *P_INT_Clear = C_IRQ4_1KHz; // 清该中断标志 } } IF *P_INT_Ctrl & C_IRQ4_2KHz //如果是IRQ4_2KHz { *P_INT_Clear = C_IRQ4_2KHz; // 清IRQ4_2KHz中断标志 } IF *P_INT_Ctrl & C_IRQ4_4KHz //如果是IRQ4_4KHz { *P_INT_Clear = C_IRQ4_4KHz; // 清IRQ4_4KHz中断标志 } } #include “SPCE061.H” #define TIMER_DATA_FOR_4KHz (65535-2048) //计数器初值 main() { asm(“INT OFF”); *P_IOA_Dir = 0xFFFF; *P_IOA_Attib = 0xFFFF; *P_IOA_Data = 0x00FF; //A口为输出,低8位输出低电平。 *P_IOB_Dir = 0xFFFF; *P_IOB_Attib = 0xFFFF; *P_IOB_Data = 0x00FF; //B口为输出,低8位输出低电平。 *P_Timer_Data = TIMER_DATA_FOR_4KHz; //0.5S,2048次分频 *P_Timer_Ctrl = C_SourceA_4096Hz //0004H (P_Timer_Ctrl) *P_INT_Ctrl = C_IRQ4_1KHz| C_IRQ2_TMB ; //允许中断 asm(“INT IRQ”); while(1) //死循环。 *P_Watchdog_Clear = C_WDTCLR }
LED闪亮 1KHz中断控制 2KHz中断控制 4KHz中断控制 1KHz中断,1mS中断。 2KHz中断,0.5mS中断。 4KHz中断,0.25mS中断。
IRQ4中断程序 寄存器组压栈保护 N Y 1KHz中断? 否则,4KHz中断 TIME1+1 2KHz中断? TIME4+1 Y >1S? >0.25S? TIME2+1 Y Y IOA0,1 IOA4,7 LED亮 LED灭 LED亮 LED灭 >0.5S? Y IOA2,3 >2S? >0.5S? LED灭 LED亮 Y Y TIME1清0 TIME4清0 >1S? Y TIME2清0 清1KHz中断标志 清4KHz中断标志 清2KHz中断标志 寄存器组出栈恢复 中断返回
.DEFINE P_IOA_DATA 0x7000 • .DEFINE P_IOA_DIR 0x7002 • .DEFINE P_IOA_ATTRI 0x7003 • .DEFINE P_INT_Ctrl 0x7010 • .DEFINE P_INT_CLEAR 0x7011 • .DEFINE P_WATCHDOG_CLEAR 0x7012 • .RAM • .VAR TIME1 //1KHz计数器 • .VAR TIME2 //2KHz计数器 • .VAR TIME4 //4KHz计数器 • .CODE • .PUBLIC _main //主程序 • _main: • INT off • R1=0xffff //r1的值为0xffff • [P_IOA_ATTRI]=r1 //IOA口设置为同相低电平输出 • [P_IOA_DIR]=r1 ; • R1=0 • [P_IOA_DATA]=r1; • R1=0x0070 • [P_INT_Ctrl ]=R1 • R1=0 • [TIME1]=R1 • [TIME2]=R1 • [TIME4]=R1 • INT on • Loop: • R1=1 • [P_WATCHDOG_CLEAR]=R1 • JMP Loop • //============================ • .TEXT • .PUBLIC _IRQ4 • _IRQ4:PUSH R1,R5 TO [SP]R1 = 0x0010; //中断识别TEST R1,[P_INT_Ctrl]; //是1KHz?JNZ L_IRQ4_1KHz; • R1 = 0x0020;TEST R1,[P_INT_Ctrl]; //是2KHz?JNZ L_IRQ4_2KHz; L_IRQ4_1KHz:R2=[TIME1] //计数器+1R2+=1[TIME1]=R2R1=[P_IOA_DATA] CMP R2,0x0400 //是否1S? JBE LED1KHz_OFF //小于等于, LED灭 R1|=0x0003 //否则,0~1LED [P_IOA_DATA]=R1 CMP R2,0800 //是否2S? JBE LED1KHz_RET //小于等于, 0~1LED继续亮,中断返回 R2=0 //是, 清TIME1 ,在下一次中断灭 [TIME1]=R2 JMP LED2Hz_RET LED1KHz_OFF: R1&=0xFFFC [P_IOA_DATA]=R1 LED1KHz_RET: R1=0x0010[P_INT_Clear] = R1; //清中断POP R1,R5 FROM [SP];RETI; L_IRQ4_4KHz:R2=[TIME4] //TIME4计数器+1R2+=1[TIME4]=R2R1=[P_IOA_DATA] //读A引脚状态 CMP R2,0x0400 //是否≤0.25S? JBE LED4KHz_OFF //是, LED灭 R1|=0x00F0 //否,4~7 LED亮 [P_IOA_DATA]=R1 CMP R2,0x0800 //是否≤0.5S? JBE LED4KHz_RET //是, 4~7 LED继续亮中断返回 R2=0 //否,清TIME4,在下一次中断灭 [TIME4]=R2 JMP LED4KHz_RET LED4Hz_OFF: R1&=0xFF0F // 4~7 LED灭 [P_IOA_DATA]=R1 LED4Hz_RET: R1=0x0040[P_INT_Clear] = R1; //清中断POP R1,R5 FROM [SP];RETI; L_IRQ4_2KHz:R2=[TIME2] //计数器+1R2+=1[TIME2]=R2R1=[P_IOA_DATA] CMP R2,0x0400 //是否≤0.5S? JBE LED2KHz_OFF //小于等于, LED灭 R1|=0x000C //否则,2~3 LED亮 [P_IOA_DATA]=R1 CMP R2,0800 //是否小于等于1S? JBE LED2KHz_RET //小于等于, 2~3 LED继续亮,中断返回 R2=0 //否则,清TIME2 ,在下一次中断灭 [TIME2]=R2 JMP LED2KHz_RET LED2KHz_OFF: R1&=0xFFF3 [P_IOA_DATA]=R1 LED2KHz_RET: R1=0x0010[P_INT_Clear] = R1; //清中断POP R1,R5 FROM [SP];RETI; 程序设计
本章小结 • 掌握中断系统的一般概念 • CPU对I/O设备管理的方式 • 中断优先级及中断嵌套 • 开中断与关中断 • 中断响应过程 • 掌握µ’nSPTM中断系统的基本特点 • 中断源、中断事件、中断优先级、中断向量、中断响应 • µ’nSPTM系统的中断程序设计 作业: P106 3、6、
IRQ6中断程序 [例6.3] 寄存器组压栈保护 TMB2中断? 计数器+1 计数器+1 >1S? >0.5S? Y Y LED灭 LED亮 LED亮 LED灭 >2S? >1S? Y Y 计数器清0 计数器清0 清Timer2中断标志 清Timer4中断标志 寄存器组出栈恢复 中断返回
[例6.3]程序 .TEXT .PUBLIC _FIQ _FIQ: PUSH R1,R5 TO [SP] R1 = 0x0800; //中断识别 TEST R1,[P_INT_Ctrl]; //是FIQ_TMB?JNZ L_FIQ_TMB; R1 = 0x0200; //否, TEST R1,[P_INT_Ctrl]; //是FIQ_TMA ?JNZ L_FIQ_TMA; L_FIQ_PWM: //否,是FIQ_PWM R1=0x8000 [P_INT_Clear]=R1 POP R1,R5 FROM [SP] RETI .include hardware.inc .DEFINE timea_clk 0x020d ; .DEFINE timeb_clk 0x0004 ; .RAM .VAR TA_Flag .VAR TB_Flag .PUBLIC _main //主程序 _main: INT off R1=0xffff [P_IOA_ATTRI]=r1 //IOA口设置为同相低电平输出 [P_IOA_DIR]=r1 R1=0 [P_IOA_DATA]=r1; R1=0xffff [P_IOB_ATTRI]=r1 //IOB口设置为同相高电平输出 [P_IOB_DIR]=r1 R1=0 [P_IOB_DATA]=r1; L_FIQ_TMA: R1=[TA_Flag] R1^=0xFFFF [P_IOA_Data]=R1 [TA_Flag]=R1 R1=0x0200 [P_INT_Clear]=R1 POP R1,R5 FROM [SP] RETI L_FIQ_TMB: R1=0x0800 [P_INT_Clear]=R1 POP R1,R5 FROM [SP] RETI R1=0xFF9F [P_TimerA_Data]=R1 [P_TimerB_Data]=R1 R1= timea_clk [P_TimerA_Ctrl]=R1 R2=0000000000000100B R1=0010000000000000B R1 | =R2 [P_INT_Ctrl]=R1 INT IRQ,FIQ L_LOOP: NOP NOP NOP JMP L_LOOP
主程序 设置中断 开中断 初始化A、B口 关中断 系统进入睡眠状态 S1键按下吗? N Y 有键按下吗? N 松开S1键吗? N Y Y LED灯灭 循环点亮LED
IOA0 IOA1 IOA2 R×8 IOA3 IOA4 IOA5 IOA6 IOA7