引脚变化中断 - 内部中断的外部中断

4
在这个程序中,我正在使用定时器中断循环LED,如果有人按下开关,它应该停止第一个中断并触发第二个中断,根据按下的开关点亮LED。在这里,我有点困惑哪个中断被调用了。我参考了一些关于引脚变化中断的书籍,并编写了一些设置PCMSK2的代码行。输出结果是“最初所有LED都在循环,当按下开关时...LED的循环停止并重新开始(这意味着程序正在读取输入,只是没有触发第二个中断)。它不会停止或暂停,并且不会点亮随后的LED。”请问有人可以帮忙吗?
#include <avr/io.h>
#include <avr/interrupt.h>
#define PINK_MASK \
    ((1<<PINK0)|(1<<PINK1)|(1<<PINK2)|(1<<PINK3)|(1<<PINK4)|(1<<PINK5)|(1<<PINK6)|(1<<PINK7))


volatile unsigned int intrs, i=1;

void enable_ports(void);
void delay(void);

extern void __vector_23 (void) __attribute__ ((interrupt));

extern void __vector_25 (void) __attribute__ ((signal));

void enable_ports()
{
    DDRB = 0xff;   //PORTB as output for leds

    PORTB = 0xff;

    DDRK = 0x00;  //PORTK as input from switches

    PORTK |= PINK_MASK;

    PCMSK2 = PINK_MASK;     //ENABLE PCMSK2, Setting interrupts

    PCICR = 0x04;

    PCIFR = 0x04;

    TCCR0B = 0x03;      //Setting TIMER

    TIMSK0 = 0x01;

    TCNT0 = 0x00;

    intrs = 0;
}
void __vector_23 (void)
{
    intrs++;
    if(intrs > 60)
    {
        intrs = 0;
        PORTB = (0xff<<i);

        i++ ;
        if(i == 10 )
        {
            PORTB = 0xff;
            i = 1 ;
        }
    }
}

void __vector_25 (void)
{
    unsigned char switches;

    switches = ((~PINK) & (PINK_MASK)); //Reading from switches

    if(switches & (1<<PINK0))
        PORTB = (PORTB<<PINK0);

    else if (switches & (1<<PINK1))
        PORTB = (PORTB<<PINK1);

    else if (switches & (1<<PINK2))
        PORTB = (PORTB<<PINK2);

    else if (switches & (1<<PINK3))
        PORTB = (PORTB<<PINK3);

    else if (switches & (1<<PINK4))
        PORTB = (PORTB<<PINK4);

    else if (switches & (1<<PINK5))
        PORTB = (PORTB<<PINK5);

    else if (switches & (1<<PINK6))
        PORTB = (PORTB<<PINK6);

    else if (switches & (1<<PINK7))
        PORTB = (PORTB<<PINK7);
}

int main(void)
{
    enable_ports();
    sei();

    while(1)
    {

    }
}

感谢您的支持。

抱歉,我不使用Atmel µCs。但或许你可以在http://embdev.net/上询问。 - AndreKR
1
你的目标设备是什么? - John Leimon
2个回答

5

AVR架构中的外部中断虽然有些令人困惑,但并非不可能。我发现对我最有帮助的资源是AVR libc中关于中断的页面。我认为你把代码写得太复杂了,与你想要实现的功能不符。让我们从头开始:

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

void main() {
    sei();
    while(1) {};
}

AVR libc实际上使处理中断变得非常容易。在我上面链接的页面中,列出了每个AVR芯片上支持的所有中断向量列表。假设您正在使用Mega32,并且现在想要使用定时器中断使LED闪烁。让我们将其添加到程序中:

uint8_t led_state;

ISR(TIMER0_COMP_vect) {
    led_state = ~led_state;
    PORTB = led_state;
}

void setup_timer_interrupt() {
    TCCR0B = 0x03;
    TIMSK0 = 0x01;
    TCNT0 = 0x00;
}

这将使得PORTB的LED灯在每次定时器中断发生时闪烁。请注意,它应该是通过使用ISR(...)宏来设置的;你正在使用的__vector_...调用已经过时并且有点令人困惑。
最后,如果我理解你的问题正确,你希望使用一组开关来保持LED亮着。实际上,我不会为此使用外部中断,只需要在ISR(TIMER0_COMP_vect)期间使用PINK读取开关的值即可,但如果你愿意,我们可以使用它。我们需要添加以下代码:
uint8_t switch_state;

void setup_switch_interrupt() {
    // I'm assuming this code to enable external interrupts works.
    DDRK = 0x00;
    PORTK = 0xff;
    PCMSK2 = 0xff; // set to all 1s
    PCICR = 0x04;
    PCIFR = 0x04;
}

ISR(INT0_vect) {
    switch_state = PINK;
}

这段代码的作用是什么?我们将开关的状态保存在switch_state中,每次外部中断触发时读取(我猜测你已经设置了它在0->1和1->0转换时触发)。只需根据switch_state的值使LED输出产生变化即可。由于我们迄今为止一直在切换LED,所以我们将在定时器中断中完成此操作。新版本如下:

ISR(TIMER0_COMP_vect) {
    led_state = ~led_state | switch_state;
    PORTB = led_state;
}

应该就可以了!

注:我之前说使用外部中断来读取开关并不是必要的。这是因为你可以在定时器中断期间使用PINK读取开关值。你可以摆脱switch_statesetup_switch_interrupt()ISR(INT0_vect),只需修改定时器中断如下:

ISR(TIMER0_COMP_vect) {
    led_state = ~led_state | PINK;
    PORTB = led_state;
}

那应该使程序变得更简单一些。

0
无论如何,每次执行您的__vector_23时,都会通过递增i循环遍历分配给PORTB的LED。如果我理解您想要做什么,您应该只在按下开关时在__vector_25中递增i

@sneezy,我第一次回答时可能理解有误,请查看我的编辑并阅读更好的说明。 - Mark Elliot
我没有看到__vector_25被调用。 - sneezy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接