正确使用引脚变化中断

电器工程 大气压 中断
2022-01-30 01:35:34

我正在尝试使用引脚更改中断来检测按下的按钮。到目前为止,我从未使用过这类中断并且存在一些问题,所以我想确定这是否是正确的用法。

如果我得到了正确的数据表,则必须执行以下操作才能使用引脚更改中断:

  1. 在 PCMSK 寄存器中设置要控制的 PIN
  2. 启用 PIN 寄存器以进行引脚更改中断控制 (PCICR)
  3. 启用中断
  4. 使用对应的中断向量

项目:简单的情绪灯,通过 4 个按钮控制颜色。

设置:

  • Atmega168A-PU
  • 4个迷你按钮开关
  • MOSFET 来控制我的 3 瓦 RGB LED

这是我正在使用的代码,它没有按预期工作:

#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#define BUTTON1 (1<<PC5) 
#define BUTTON2 (1<<PC4) 
#define BUTTON3 (1<<PC3) 
#define BUTTON4 (1<<PC2) 

#define GREEN   (1<<PB1) 
#define BLUE    (1<<PB2) 
#define RED     (1<<PB3) 

void init() {

        // enable LED
        DDRB |= GREEN;
        DDRB |= BLUE;
        DDRB |= RED;

        // button pullups
        PORTC |= BUTTON1;
        PORTC |= BUTTON2;
        PORTC |= BUTTON3;
        PORTC |= BUTTON4;

        // pin change interrupts for buttons
        PCMSK1 |= PCINT13;
        PCMSK1 |= PCINT12;
        PCMSK1 |= PCINT11;
        PCMSK1 |= PCINT10;

        // enable pin change for buttons
        PCICR |= PCIE2;

        sei();

}

ISR(PCINT2_vect) {

                PORTB = BLUE;
}


void ledTest() {

                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;
                _delay_ms(250);
                PORTB ^= RED;


                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;
                _delay_ms(250);
                PORTB ^= BLUE;

                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
                _delay_ms(250);
                PORTB ^= GREEN;
}

int main() {

        init();
        ledTest();

        _delay_ms(500);
        PORTB |= GREEN;

        while(1) {
                _delay_ms(100);
        }
}

注意:按钮应该去抖动。由于我正在逐步尝试此操作,并且不适合打开 LED,因此我在这里忽略了它。

问题:我尝试使用中断的方式是否正确?

我的设置问题:

  • Buttons1-3 完全被忽略。
  • Button4 正在触发 atmega 的重置

我检查的事情:

  • 按钮与重置 PIN 无关
  • 如果按下按钮,则正确连接到 GND
  • 如果未按下按钮,则未连接到 GND
  • 如果我在没有中断的情况下使用它们,按钮会很好地工作,例如:

    if(!(PINC & BUTTON4)) { PORTB ^= 蓝色;}

  • 16MHZ外晶/内晶
  • 路由中的任何错误
  • 我在 atmega 上的 PWR 和 GND 之间使用了一个 100nF 电容器
  • VCC(7), GND(8), GND(22), AVCC(20) 已连接(因为我不需要 AREF,所以没有连接)
2个回答

引脚更改中断通常不是检测按钮操作的好方法。这是因为机械按钮弹跳,你会得到很多无意义的中断,然后你仍然需要做去弹跳。

更好的方法是定期中断,例如每 1 毫秒(1 kHz 速率)。在大多数处理器上,这是一个很长的时间,因此中断所花费的时间部分会很小。只需在每次中断时对按钮状态进行采样。如果您连续 50 毫秒看到新状态,则声明新按钮状态。50 ms 比大多数按钮弹跳时间长,但仍然足够短,以至于人类不会注意到或关心延迟。

请注意,这样您还可以在同一个周期性 1 ms 中断中处理多个按钮。您只需要为每个按钮设置一个计数器。

更多关于去抖动时间:

有时,就像在这种情况下,有人说 50 毫秒的去抖动时间太长了。对于人类按下的普通按钮,情况并非如此。在像秒表这样对时间非常关键的应用程序中,这可能是一个问题,但到目前为止我还没有遇到过。我在 1980 年代初对此进行了测试,许多其他人也进行了测试。

确实,典型的按钮弹跳时间约为 10 毫秒,几乎所有稳定时间为 25 毫秒。去抖动时间的限制因素是人类的感知。50 毫秒比人们在不寻找延迟时开始注意到的延迟要短一些。即使那样,它也需要更长的时间才能让人讨厌。在某些情况下,人类有可能检测到 50 ms 和 0 ms 延迟之间的差异,如果他们专门寻找它,但这与按下按钮并看到发生的事情而不考虑延迟是完全不同的。

因此 50 毫秒是一个很好的去抖时间,因为延迟低于普通应用中的感知限制,远低于干扰限制,并且远高于大多数开关的反弹时间。我发现开关弹跳的时间差不多那么长,所以你不妨推到感知极限,因为没有什么可以松动的。

我已经使用 50 毫秒的去抖时间完成了许多带有固件去抖按钮的产品。客户一次也没有提到甚至注意到延迟。他们都接受按钮工作正常,没有问题。

引脚更改中断是比轮询更好的去抖动方法。中断通常通过一些逻辑,例如 D-Flip Flop 或 D-Latch。虽然这是真的,但用更高级别的编译器实现这个去抖动例程更加困难。一旦发生中断,中断标志不会被清除,并且中断使能被清除,直到发生延迟。一旦发生延迟,就会检查引脚的状态,如果它仍处于触发中断的给定状态,则更改按钮的状态并清除中断标志并设置中断使能。如果不在导致启动的状态,则设置中断使能并且状态保持不变。这为其他任务释放了处理器。周期性中断在程序中浪费时间。