使用 atmega16 和外部中断的七段项目

Seven segment project using atmega16 and external interrupts

我一直在 proteus 仿真的 AVR 微控制器 atmega16 上做一个简单的项目。

项目是: 当我按下按钮时,它会将七段增加到数字 9 之后,如果我们再次按下按钮,它会溢出并且 return 再次变为 0.
当我在 while 循环代码中更改 (flag=1;) 位置时,它会给我不同的输出,我的意思是当我按下按钮时,它在按下后没有响应。
可能需要再次按下按钮才能增加七段 proteus simulation

唯一正常工作的代码是在退出前设置 flag=1;(第二个 if 和 else 条件)

所以我的问题是当我更改代码中的 flag=1; 语句位置时实际发生了什么。

#include<avr/io.h>
#include<avr/interrupt.h>
char flag=1;
char num=0;
void INT2_Init(void){
    DDRB&=~(1<<2);
    SREG|=(1<<7);
    GICR|=(1<<INT2);
    MCUCSR|=(1<<ISC2);
}
ISR(INT2_vect){
    flag=0;
    GIFR|=(1<<INTF2);
}

int main(void){
    DDRC=0x0f;
    PORTC=0;
    DDRB&=~(1<<2);
    INT2_Init();
    while(1){
        if(flag==0){
            if(num>8){
                num=0;
                PORTC=(PORTC&0xf0)|num;
            }
            else{
                num++;
                PORTC=(PORTC&0xf0)|num;
            }
        }
        flag=1;
    }

}

我不太清楚你在问什么。您只是难以理解代码吗?我的评论有帮助吗?

#include<avr/io.h>
#include<avr/interrupt.h>
char flag=1;
char num=0;

void INT2_Init(void){
    DDRB&=~(1<<2);
    SREG|=(1<<7);
    GICR|=(1<<INT2);
    MCUCSR|=(1<<ISC2);
}

// sets flag to 0 on external interrupt (button press)
ISR(INT2_vect){
    flag=0;
    GIFR|=(1<<INTF2);
}

int main(void){
    DDRC=0x0f;
    PORTC=0;
    DDRB&=~(1<<2);
    INT2_Init();

    while(1){
        // if flag was set to 0 in external interrupt -> increment (or overflow) number on segment display
        if(flag==0){
            if(num>8){
                num=0;
                PORTC=(PORTC&0xf0)|num;
            }
            else{
                num++;
                PORTC=(PORTC&0xf0)|num;
            }
        }
        // reset the flag to 1 to prevent num from being incremented continuously
        flag=1;
    }
}

我建议将 flag=1; 移到 if 中以明确意图,即每次按下按钮时语句只执行一次。

    while(1) {
        // if flag was set to 0 in external interrupt -> increment (or overflow) number on segment display
        if(flag==0){
          // reset the flag to 1 to prevent num from being incremented continuously
          flag=1;

            if(num>8){
                num=0;
                PORTC=(PORTC&0xf0)|num;
            }
            else{
                num++;
                PORTC=(PORTC&0xf0)|num;
            }
        }
    }

还建议使用自描述变量名。例如 buttonWasPressed 而不是 flag。这使得这样的代码更容易阅读。

如果主代码中的某个变量发生变化而中断,则必须对其进行标记volatile。否则,编译器可能会决定缓存变量值并在循环中忽略它的读取。

volatile char flag=1;

其次,注意标志变量何时何地发生变化。例如:

    while(1){
        // at this point flag is 0, comparison failed
        if(flag==0) {
          ...
        }
// Interrupt happens here, changing flag to 0
// Then flag is immediately reset, thus flag change is missed in the loop
        flag=1; 
    }

改为考虑使用这样的模式:

    while (1) {
         cli(); // disabling interrupts ensuring no interrupt happens inside 
         if (flag==0) {
           ...
         }
         flag = 1;
         sei(); // re-enable interrupts
    }

或者,在这种情况下,它可以更简单

    while (1) {
         if (flag==0) {
             flag = 1; // Reset flag only if it set, right after comparison
             ...
         }
    }