搜索
bottom↓
回复: 8

请教马老师,有关外部中断中全局中断标志的一个疑问?

[复制链接]

出0入0汤圆

发表于 2007-3-10 05:36:56 | 显示全部楼层 |阅读模式
马老师,您好!



在《中断应用设计要点》这篇文章中,A部分第四条:

4.当MCU响应一个中断时,其硬件系统会自动中断返回地址压入系统堆栈,并将关闭全局中断响应(硬件将中断标志I位清0),清除该中断的中断标志位;执行中断返回指令RETI时,硬件会先允许全局中断响应(硬件将中断标志I位置1),然后从系统堆栈中弹出返回地址到PC程序计数器中,继续执行被中断打断的程序。除此之外,MCU的硬件没有对中断保护做其他处理。



但是在我的程序中,我用INT0外部中断,执行中断返回指令后,中断标志I位还是为0,我必须要加入sei();进行再一次设置才可以进入下一轮中断; 但是在别的中断向量中,比如USART_Rx,不需要加入sei();就可以进行下一轮中断。



请问,这种情况是正确的吗?谢谢。。。

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2007-3-10 12:04:29 | 显示全部楼层
External Interrupts



The External Interrupts are triggered by the INT0, INT1, and INT2 pins. Observe that, if enabled, the interrupts will trigger even if the INT0..2 pins are configured as outputs. This feature provides a way of generating a software interrupt. The external interrupts can be triggered by a falling or rising edge or a low level (INT2 is only an edge triggered interrupt). This is set up as indicated in the specification for the MCU Control Register – MCUCR – and MCU Control and Status Register – MCUCSR. When the external interrupt is enabled and is configured as level triggered (only INT0/INT1), the interrupt will trigger as long as the pin is held low. Note that recognition of falling or rising edge interrupts on INT0 and INT1 requires the presence of an I/O clock, described in “Clock Systems and their Distribution” on page 24. Low level interrupts on INT0/INT1 and the edge interrupt on INT2 are detected asynchronously. This implies that these interrupts can be used for waking the part also from sleep modes other than Idle mode. The I/O clock is halted in all sleep modes except Idle mode.



===========================================================

上面是从手册中对外部中断的描述,注意其中一句:

When the external interrupt is enabled and is configured as level triggered (only INT0/INT1), the interrupt will trigger as long as the pin is held low.



如果你的系统中使用INT0为低电平中断,那么进入中断后就应该通知外部器件将INT0的电平释放抬高了。否则当执行完中断返回后,由于INT0还为低,又是优先级最高的中断,所以马上又进入了INT0中断。因此,在INT0为低的期间,你的程序将老在循环执行INT0的中断过程。



如果你的SEI()是INT0中断返回前的一条指令的话,你实际上在使用中断嵌套过程了。一但你在INT0的中断服务程序中使用了SEI(),那么任何一个新的中断都会打断正在执行的INT0中断服务。



你可以做测试:在INT0中断服务返回前增加一段对INT0引脚电平的读取和判断,如果为低,继续读INT0,等到读到“1”时才执行中断返回。



如果你使用INT0低电平中断,那么上面的分析供参考。



根据我的经验,外部中断的低电平触发方式一般应用于将MCU从休眠状态中唤醒使用,或处理紧急事件(如事件发生,其它工作全部要停止,只能处理该事件的情况)。在正常使用中最好采用边沿或电平改变触发方式。

出0入0汤圆

 楼主| 发表于 2007-3-10 18:48:36 | 显示全部楼层
谢谢马老师的回答。



我没说明我用的是电平改变触发方式,因为我的程序需要判断电平的状态来执行相应的操作。今天我又试验了边沿方式,我得到的结论是:



对于外部中断INT0,若采用电平改变触发方式或边沿触发方式时,一旦INT0中断操作执行后,SREG的标志位I置0,如果需要继续有中断发生时,一定要重新加入sei();才可以。

出0入0汤圆

发表于 2007-3-10 19:54:11 | 显示全部楼层
我简单写了测试程序(cvavr环境)如下:



#include <mega16.h>



unsigned char count;

bit time_125_ok;



// External Interrupt 0 service routine

interrupt [EXT_INT0] void ext_int0_isr(void)

{

    count++;

    if (count >= 125)

    {

        count = 0;

        time_125_ok = 1;

    }

}



void main(void)

{

    PORTA=0x00;

    DDRA=0xFF;



    // External Interrupt(s) initialization

    // INT0:On, INT1:Off, INT2:Off

    // INT0 Mode: Falling Edge

    GICR|=0x40;

    MCUCR=0x02;

    MCUCSR=0x00;

    GIFR=0x40;



    // Global enable interrupts

    #asm("sei")



while (1)

      {

        if (time_125_ok)

        {

            time_125_ok = 0;

            PORTA = ~PORTA;

        }

      }

}



使用M16,在AVR-51的板上运行。

PD2(INT0)输入一个125Hz的方波序列(板上有方波序列发生器),INT0中断服务中的计数器记录中断次数,每125次置标志位为1(正好1秒钟)。

主程序中判断标志,每隔1秒改变PA口的输出,PA口接LED,这样LED每隔1秒亮/暗闪烁。



整个程序仅在初始化后开放中断允许,其它地方没有任何开中断的指令。



程序运行正常,当INT0输入的方波频率为250Hz时,LED闪烁频率也提高一倍。证明你的结论有误。

==============================

完成测试5分钟,回答你的问题需要15分钟。

出0入0汤圆

发表于 2007-3-10 20:15:08 | 显示全部楼层
以下是这段测试程序的汇编代码



+00000000:   940C002B    JMP     0x0000002B       Jump 到初始化程序(cvavr的)

+00000002:   940C005B    JMP     0x0000005B       Jump 到INT0的中断服务程序

+00000004:   940C0000    JMP     0x00000000       Jump

+00000006:   940C0000    JMP     0x00000000       Jump

+00000008:   940C0000    JMP     0x00000000       Jump

+0000000A:   940C0000    JMP     0x00000000       Jump

+0000000C:   940C0000    JMP     0x00000000       Jump

+0000000E:   940C0000    JMP     0x00000000       Jump

+00000010:   940C0000    JMP     0x00000000       Jump

+00000012:   940C0000    JMP     0x00000000       Jump

+00000014:   940C0000    JMP     0x00000000       Jump

+00000016:   940C0000    JMP     0x00000000       Jump

+00000018:   940C0000    JMP     0x00000000       Jump

+0000001A:   940C0000    JMP     0x00000000       Jump

+0000001C:   940C0000    JMP     0x00000000       Jump

+0000001E:   940C0000    JMP     0x00000000       Jump

+00000020:   940C0000    JMP     0x00000000       Jump

+00000022:   940C0000    JMP     0x00000000       Jump

+00000024:   940C0000    JMP     0x00000000       Jump

+00000026:   940C0000    JMP     0x00000000       Jump

+00000028:   940C0000    JMP     0x00000000       Jump

+0000002A:   0000        NOP                      No operation

+0000002B:   94F8        CLI                      Global Interrupt Disable (关中断,cvavr的初始化开始)

+0000002C:   27EE        CLR     R30              Clear Register

+0000002D:   BBEC        OUT     0x1C,R30         Out to I/O location

+0000002E:   E0F1        LDI     R31,0x01         Load immediate

+0000002F:   BFFB        OUT     0x3B,R31         Out to I/O location

+00000030:   BFEB        OUT     0x3B,R30         Out to I/O location

+00000031:   BFE5        OUT     0x35,R30         Out to I/O location

+00000032:   E1F8        LDI     R31,0x18         Load immediate

+00000033:   BDF1        OUT     0x21,R31         Out to I/O location

+00000034:   BDE1        OUT     0x21,R30         Out to I/O location

+00000035:   E08D        LDI     R24,0x0D         Load immediate

+00000036:   E0A2        LDI     R26,0x02         Load immediate

+00000037:   27BB        CLR     R27              Clear Register

+00000038:   93ED        ST      X+,R30           Store indirect and postincrement

+00000039:   958A        DEC     R24              Decrement

+0000003A:   F7E9        BRNE    PC-0x02          Branch if not equal

+0000003B:   E080        LDI     R24,0x00         Load immediate

+0000003C:   E094        LDI     R25,0x04         Load immediate

+0000003D:   E6A0        LDI     R26,0x60         Load immediate

+0000003E:   93ED        ST      X+,R30           Store indirect and postincrement

+0000003F:   9701        SBIW    R24,0x01         Subtract immediate from word

+00000040:   F7E9        BRNE    PC-0x02          Branch if not equal

+00000041:   E5E4        LDI     R30,0x54         Load immediate

+00000042:   E0F0        LDI     R31,0x00         Load immediate

+00000043:   9185        LPM     R24,Z+           Load program memory and postincrement

+00000044:   9195        LPM     R25,Z+           Load program memory and postincrement

+00000045:   9700        SBIW    R24,0x00         Subtract immediate from word

+00000046:   F061        BREQ    PC+0x0D          Branch if equal

+00000047:   91A5        LPM     R26,Z+           Load program memory and postincrement

+00000048:   91B5        LPM     R27,Z+           Load program memory and postincrement

+00000049:   9005        LPM     R0,Z+            Load program memory and postincrement

+0000004A:   9015        LPM     R1,Z+            Load program memory and postincrement

+0000004B:   01BF        MOVW    R22,R30          Copy register pair

+0000004C:   01F0        MOVW    R30,R0           Copy register pair

+0000004D:   9005        LPM     R0,Z+            Load program memory and postincrement

+0000004E:   920D        ST      X+,R0            Store indirect and postincrement

+0000004F:   9701        SBIW    R24,0x01         Subtract immediate from word

+00000050:   F7E1        BRNE    PC-0x03          Branch if not equal

+00000051:   01FB        MOVW    R30,R22          Copy register pair

+00000052:   CFF0        RJMP    PC-0x000F        Relative jump

+00000053:   E5EF        LDI     R30,0x5F         Load immediate

+00000054:   BFED        OUT     0x3D,R30         Out to I/O location

+00000055:   E0E4        LDI     R30,0x04         Load immediate

+00000056:   BFEE        OUT     0x3E,R30         Out to I/O location

+00000057:   E6C0        LDI     R28,0x60         Load immediate

+00000058:   E0D1        LDI     R29,0x01         Load immediate

+00000059:   940C0069    JMP     0x00000069       Jump 到MAIN()函数(用户的初始化)

========================================================================

;INT0中断服务:

+0000005B:   93EA        ST      -Y,R30           Store indirect and predecrement

+0000005C:   B7EF        IN      R30,0x3F         In from I/O location

+0000005D:   93EA        ST      -Y,R30           Store indirect and predecrement

+0000005E:   9443        INC     R4               Increment

+0000005F:   E7ED        LDI     R30,0x7D         Load immediate

+00000060:   164E        CP      R4,R30           Compare

+00000061:   F018        BRCS    PC+0x04          Branch if carry set

+00000062:   2444        CLR     R4               Clear Register

+00000063:   9468        SET                      Set T in SREG

+00000064:   F820        BLD     R2,0             Bit load from T to register

+00000065:   91E9        LD      R30,Y+           Load indirect and postincrement

+00000066:   BFEF        OUT     0x3F,R30         Out to I/O location

+00000067:   91E9        LD      R30,Y+           Load indirect and postincrement

+00000068:   9518        RETI                     Interrupt return

;INT0中断返回

=======================================================================

;用户的MAIN()开始

+00000069:   E0E0        LDI     R30,0x00         Load immediate

+0000006A:   BBEB        OUT     0x1B,R30         Out to I/O location

+0000006B:   EFEF        SER     R30              Set Register

+0000006C:   BBEA        OUT     0x1A,R30         Out to I/O location

+0000006D:   B7EB        IN      R30,0x3B         In from I/O location

+0000006E:   64E0        ORI     R30,0x40         Logical OR with immediate

+0000006F:   BFEB        OUT     0x3B,R30         Out to I/O location

+00000070:   E0E2        LDI     R30,0x02         Load immediate

+00000071:   BFE5        OUT     0x35,R30         Out to I/O location

+00000072:   E0E0        LDI     R30,0x00         Load immediate

+00000073:   BFE4        OUT     0x34,R30         Out to I/O location

+00000074:   E4E0        LDI     R30,0x40         Load immediate

+00000075:   BFEA        OUT     0x3A,R30         Out to I/O location

+00000076:   9478        SEI                      Global Interrupt Enable (开放全局中断)

----------------------------------------------------------------------------

;while()循环开始

+00000077:   FE20        SBRS    R2,0             Skip if bit in register set

+00000078:   C005        RJMP    PC+0x0006        Relative jump

+00000079:   94E8        CLT                      Clear T in SREG

+0000007A:   F820        BLD     R2,0             Bit load from T to register

+0000007B:   B3EB        IN      R30,0x1B         In from I/O location

+0000007C:   95E0        COM     R30              One's complement

+0000007D:   BBEB        OUT     0x1B,R30         Out to I/O location

+0000007E:   CFF8        RJMP    PC-0x0007        Relative jump

+0000007F:   CFFF        RJMP    PC-0x0000        Relative jump

出0入0汤圆

 楼主| 发表于 2007-3-10 22:19:54 | 显示全部楼层
谢谢您快速的回答。



我用的JTAG ICE mkII仿真, GCC.



#include <avr/io.h>

#include <avr/interrupt.h>



#define amount_Tux 10



#define RTS_inactive        PORTD &= ~(1<<PD3)   //level low

#define RTS_active          PORTD |=  (1<<PD3)  //level high

#define CTS_inactive        !(PIND &(1<<2))   //level low disable TXEN

#define CTS_active          PIND &(1<<2)  //level high enable TXEN  



uint8_t send[amount_Tux];

volatile uint8_t i;

volatile uint8_t counter_interrupt;



//initialisation usart

void Init_Usart_Tux(unsigned int baudrate)

{

    UBRR0H = (unsigned char)(baudrate>>8);

    UBRR0L = (unsigned char)baudrate;

       

    UCSR0B = 0x00; //disable while setting baud rate

   

    UCSR0A = (1<<U2X0);



    // Enable receiver

    UCSR0B = (1<<RXEN0);



    // Async mode, 8bits data, 1bits stop

    UCSR0C = (1<<UCSZ00)|(1<<UCSZ01) ;  

}



//init port

void Init_Port_Tux(void)

{

    //PORTD.0 ->    IN          RxD

    //PORTD.1 ->    OUT          TxD

    //PORTD.2 ->    IN          CTS

    //PORTD.3 ->    OUT     RTS  high level means enable RTS

    PORTD = 0b11010011;

    DDRD  = 0b11101010;

}



//init total

void Init_Total_Tux(void)

{

        CLKPR = (1<<CLKPCE);        // set Clock Prescaler Change Enable



         // set prescaler = 8, Inter RC 8Mhz / 8 = 1Mhz

         CLKPR = (1<<CLKPS1) | (1<<CLKPS0);



        Init_Port_Tux();

        Init_Usart_Tux(12); //9600 baudrate



        EICRA = (1<<ISC00); // change of level

        EIMSK = (1<<INT0);  

}



// transmit funtion

void Usart_Tx(unsigned char *datas)  

{                             

      while( i<amount_Tux )              

      {

                   //while(CTS_inactive);   

                   //{                                                                   

                        UDR0=*(datas+i);

                           while (!(UCSR0A & (1<<UDRE0))); //UDRE0=1

                        i++;                               

                   //}                    

          }       

}





/* interruption externe sur CTS le PIND2 */

ISR(INT0_vect)

{

     sei(); // 如果不加这个,中断只发生一次,我用JTAG和软件模拟的结果都是一样的

     switch (counter_interrupt % 2)

       {

                  case 0:

                {

                        counter_interrupt++;

                        UCSR0B |= (1<<TXEN0);//active  TXEN0       

                        Usart_Tx(send);               

                }   break;                  

       

                case 1:

                {

                           counter_interrupt++;

                        UCSR0B &= ~(1<<TXEN0);

                }   break;

        }

}



// programme main

int main (void)

{

        i=0;

        counter_interrupt=0;

        send[0]=0x85;

        send[1]=1;

        send[2]=2;

        send[3]=3;

        send[4]=4;

        send[5]=5;

        send[6]=6;

        send[7]=7;

        send[8]=8;

        send[9]=9;

         

        Init_Total_Tux();

        sei();

                                        

        RTS_active;

                 

        while(1)

        {

                if(i==amount_Tux)

                {

                        RTS_inactive;  

                        break;

                }

        }

                            

        return 0;

}

在我的中断子程序中,如果不加sei();那么中断只执行一次,所以我才会得出上述的结论。



我以前也是觉得执行中断返回指令RETI时,硬件会先允许全局中断响应(硬件将中断标志I位置1),但实际上我的实验效果不是这样的。不明白为什么会这样?

出0入0汤圆

发表于 2007-3-10 23:29:26 | 显示全部楼层
哈哈,我已经说过,不用仿真器。

仿真器是给明白人用的,不明白的人是越用仿真越迷糊。



你如果想证明INT0中断返回后全局中断标志位的情况,请你将程序中所有的那些其它无关的东西(通信部分)统统去掉,就象我的证明程序一样。用48发出一个周期为1秒的连续方波作为162的INT0中断输入,162写一个最简单的仅包括INT0中断服务的程序,每进一次中断仅将I/O脚取反,(该I/O驱动一个LED)。看看LED是否一秒钟亮、一秒钟暗。然后再下结论。

出0入0汤圆

 楼主| 发表于 2007-3-11 04:27:46 | 显示全部楼层
我明白为什么了,再仔细读这句话:

执行中断返回指令RETI时,硬件会先允许全局中断响应(硬件将中断标志I位置1)



再看我的程序,我的中断程序中包含发送数据这个函数,由于counter_interrupt一开始为0,所以它先执行发送数据这个函数,那么下一个中断是在执行发送数据这个函数的过程中响应的,所以永远都不会执行中断返回指令RETI,也就不会允许全局中断响应(硬件将中断标志I位置1)。



所以我的解决办法加入sei();是正确的,强制将中断标志I位置1,就可以响应下一个中断。



现在我的结论是:以下论述绝对正确,但是要好好体会,可以结合我上面的例子。呵呵!



4.当MCU响应一个中断时,其硬件系统会自动中断返回地址压入系统堆栈,并将关闭全局中断响应(硬件将中断标志I位清0),清除该中断的中断标志位;执行中断返回指令RETI时,硬件会先允许全局中断响应(硬件将中断标志I位置1),然后从系统堆栈中弹出返回地址到PC程序计数器中,继续执行被中断打断的程序。除此之外,MCU的硬件没有对中断保护做其他处理。



再次感谢马老师。

出0入0汤圆

发表于 2007-4-14 00:21:42 | 显示全部楼层
建议大家一般情况下使用AVR时,不要采用中断嵌套的结构。使用不的当,会出现一些不能预计的情况,因为中断是随机产生的。



AVR本身速度非常快,不用中断嵌套肯定能够满足大部分应用。当然,这还要求你的中断服务程序要尽量的短,不要在中断中做很多的事情。关于中断的使用及注意点,在我讲义的第7章有比较详细的介绍。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-18 13:11

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表