正确使用引脚更改中断


10

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

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

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

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

设定:

  • Atmega168A-PU
  • 4个迷你按钮开关
  • MOSFETs控制我的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,因此在这里我忽略了它。

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

我的安装出现问题:

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

我检查过的事情:

  • 按钮根本没有连接到重置PIN
  • 如果按下按钮,则按钮已正确连接到GND
  • 如果不按,则按钮未连接到GND
  • 如果我不间断地使用按钮,按钮会很好地工作,例如:

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

  • 16MHZ外部晶体/内部晶体
  • 路由中的任何错误
  • 我在atmega的PWR和GND之间使用100nF电容器
  • VCC(7),GND(8),GND(22),AVCC(20)已连接(由于我不需要AREF,因此未连接)

您需要PCIE1标志(不PCIE2)和PCINT1_vect(不PCINT2)
microtherion

为什么选择PCIE1?我正在使用C寄存器,所以如果我算是A(PCIE0),B(PCIE1),C(PCIE2)?无论如何,我使用PCIE1 nad PCINT1_vect进行了尝试,如果我按下按钮,则没有任何反应。
echox 2013年

1
在这种分配中假设正交性可能有点冒险。在这种情况下,您几乎是正确的,只是ATmega168没有端口A。无论如何,我都遵循数据手册和引脚排列。另一个提示是,您正在使用PCIE2,但是在PCMSK1中设置了位。那不可能是正确的(不幸的是,我不知道为什么您修改后的草图仍然不起作用)。
microtherion 2013年

谢谢,我也了解依赖于自建硬件的调试软件的组合并不是那么容易;-)
echox 2013年

Answers:


14

换针中断通常不是检测按钮动作的好方法。这是因为机械按钮会弹起,并且您会收到许多毫无意义的中断,然后仍然必须反跳。

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

请注意,通过这种方式,您还可以在同一个1 ms的周期性中断中处理多个按钮。您所需要的只是每个按钮一个计数器。

有关反跳时间的更多信息:

有时,在这种情况下,有人说50 ms太长的反跳时间。对于人类按下的普通按钮而言,情况并非如此。在秒表等对时间要求非常严格的应用中,这可能是个问题,但到目前为止,我还没有遇到过。我在1980年代初曾对此进行过测试,其他许多人也对此进行了测试。

的确,典型的按钮弹跳时间约为10 ms,几乎所有的弹跳时间均为25 ms。防抖时间的限制因素是人的感知力​​。50毫秒比人们在不希望看到延迟时开始注意到延迟的时间短一点。即使那样,它仍然要花更长的时间才能使它烦人。在某些情况下,如果人们是专门寻找延迟的,则可能会检测到50 ms和0 ms的延迟之间的差异,但这与按下按钮并看到有什么事情发生而没有考虑延迟是完全不同的。

因此,50 ms是一个良好的反跳时间,因为延迟在普通应用中低于感知极限,远低于烦扰极限,并且远高于大多数开关的反跳时间。我发现开关的确跳动了将近这么长时间,因此您也可以将其推向感知极限,因为没有什么可放松的。

我用50毫秒的反跳时间完成了许多带有防反跳按钮的产品。甚至没有顾客提到延迟。他们都接受了这些按钮,可以正常工作,没有问题。


1
在某些情况下,50ms可能太长(通常,人类感知的极限是10-20ms,这足以消除抖动),但是此处介绍的方法是可行的方法。
Laszlo Valko 2013年

1
@Laszlo:不,一般情况下50毫秒不太长。看到除了我的答案。
Olin Lathrop 2013年

我尝试了50毫秒,这对我来说效果很好:-)我仍然很好奇为什么引脚更改中断不起作用(在弹跳的东西旁边),但是这很有效:-)谢谢。
echox 2013年

1

引脚更改中断比轮询是一种更好的反跳方法。中断通常会经过一些逻辑,例如D触发器或D锁存器。尽管这是事实,但是使用更高级别的编译器来实现此防反跳例程更加困难。一旦发生中断,中断标志不会被清除,并且中断允许将被清除,直到发生延迟为止。一旦发生了延迟,便会检查引脚的状态,如果仍处于触发中断的给定状态,则将更改按钮的状态,并清除中断标志并设置中断使能。如果不在引起启动的状态,则设置中断允许,并且状态保持不变。这样可以释放处理器的其他任务。定期中断会浪费程序时间。


-1

“换针中断通常不是检测按钮动作的好方法。”

错误。PC INT是最好的选择。如果使用轮询来检查按钮的状态,则大多数情况下将不执行任何操作。您浪费了很多宝贵的CPU时间。PC INT允许仅应要求执行操作。

“这是因为机械按钮会弹起,您将收到许多毫无意义的中断,然后无论如何您仍然必须进行弹跳。”

正确弹跳。但是,您切勿在中断例程内部对按钮/开关进行抖动处理(相同原因:浪费CPU时间)。ISR是真正短而高效的代码。只需使用硬件反跳即可。保持软件清洁!

硬件去抖更方便,请参阅此处/ RC去抖+施密特触发器以供参考。我在PC INT上使用了无数次,但从未失败。

所以是的,您可以(并且应该)使用PC INT来获取按钮状态。但是,您还必须使用适当的硬件反跳功能。


2
软件反跳是一种有效的方法,在大多数情况下,很少的额外CPU开销是无关紧要的。说您通常应该在硬件上进行反跳操作充其量是有问题的。说在所有情况下都必须使用硬件去抖动是完全错误的。
奥林·拉斯洛普

在大多数应用中,无论如何,控制器大部分时间始终在空闲状态下运行主循环。同样,执行IO状态检查并可能增加变量所需的CPU时间也很短。在软件中实施简单的反跳操作几乎是“免费”的,硬件要花钱。而且不要嘲笑几分钱,组装也要花钱,而且如果您运行中到高产量的产品,那么这是不容忽视的。的确,ISR时间应该短一些,但是在这种情况下,这几乎不是一个论点。如果PC INT ISR由于弹跳而连续触发50次,则可能更关键。
Rev1.0

@Nelson,“浪费CPU时间”在某些应用程序中很重要,而在许多其他应用程序中则不重要。您应该在CPU时间至关重要的情况下对答案进行限定。
user1139880
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.