为什么只有第一个中断起作用?

Why does only the first interrupt work?

我正在做一个个人项目,破解万用表并为其添加背光。我正在使用 Attiny13。

我有以下代码:

    /* IR_Switch.c
 *
 * Created: 30/11/2014 23:52:15
 *  Author: keenox
 */

#define F_CPU 128000UL  // 128kHz osc, no prescaling
#define SEC(VAL) ((unsigned int)(VAL) * F_CPU / 256)
#define INV_SEC(VAL) (F_CPU / 256 / (unsigned int)(VAL))

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

#define FORCE_INLINE //__attribute__((always_inline))
#define PWM_ON() do { TCCR0A |= _BV(COM0A1); } while (0)
#define PWM_OFF() do { TCCR0A &= ~_BV(COM0A1); } while (0)
#define COUNTER_ON() do { counter = 0; TIMSK0 = _BV(TOIE0); } while (0)
#define COUNTER_OFF() do { TIMSK0 = 0; } while (0)

#define LED_ON() ( (TCCR0A & _BV(COM0A1)) || (PORTB & _BV(PINB0)) )
#define BUTTON_DOWN() ((~PINB) & _BV(PINB3))
#define BUTTON_UP() (PINB & _BV(PINB3))

#define TIMEOUT 15
char step = 50;
unsigned long counter = 0;

void ledFull(unsigned char _val)
{
    PWM_OFF();
    if (_val)
        PORTB |= _BV(PINB0);
    else
        PORTB &= ~_BV(PINB0);
}

void setLed()
{
    if (OCR0A > 249)
        ledFull(1);
    else if (OCR0A < 6)
        ledFull(0);
    else
        PWM_ON();
}

ISR(TIM0_OVF_vect)
{
    counter++;

    if (BUTTON_UP())
    {
        if (counter >= INV_SEC(4))
        {
            PORTB |= _BV(PINB4);
            if (!LED_ON())
            {
                COUNTER_OFF();
            }
            else if (counter >= SEC(TIMEOUT))
            {
                ledFull(0);
                COUNTER_OFF();
            }
        }
    }
    else if (counter > SEC(3))
    {
        // Change intensity every one sec while button down
        counter -= SEC(1);
        if (OCR0A > 249 || OCR0A < 6)
        step = -step;

        OCR0A += step;
        setLed();
    }
}

ISR(PCINT0_vect)
{
    cli();

    PCMSK = 0x0;

    if (BUTTON_DOWN())
    {
        MCUCR |= _BV(ISC00); // Switch to rising edge
        COUNTER_ON();
    }
    else
    {
        MCUCR &= ~_BV(ISC00); // Switch to falling edge

        if (counter <= INV_SEC(2)) // Normal push
        {
            PORTB &= ~_BV(PINB4);
        }
        else if (counter <= SEC(2))
        {
            if (LED_ON())
            {
                ledFull(0);
                COUNTER_OFF();
            }
            else
            {
                setLed();
            }
        }
    }

    PCMSK = _BV(PCINT3);

    sei();
}

int main(void)
{   
    DDRB  = _BV(PINB4) | _BV(PINB0);             // All inputs, but PB4 output
    PORTB = 0xFF & ~_BV(PINB0);     // All 1, except PINB0
    MCUCR |= _BV(ISC01);            // Falling edge interrupt
    GIMSK = _BV(PCIE);              // Activate only pin change interrupt
    PCMSK = _BV(PCINT3);            // PB3 interrupt mask

    TCCR0A = _BV(WGM01) | _BV(WGM00); // Set OC0A at TOP,  Fast PWM
    TCCR0B = _BV(CS00); // Timer on, No prescaling
    OCR0A = 255; // Max bright

    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    COUNTER_OFF();

    while (1)
    {
        sleep_enable();
#if defined(sleep_bod_disable)
        sleep_bod_disable();
#endif
        sei();
        sleep_cpu();
        sleep_disable();
    }
}

问题是它只在第一次中断(按钮按下)时唤醒,执行它然后什么也没有。 如果我不使用睡眠(只保留 while(1);)程序按预期运行。 你知道可能是什么问题吗?

LE:添加了完整代码。 如果我有:

sei();  
while (1) {}

然后一切正常。我只是想用睡眠来减少消耗。

你的睡眠模式是"Power-down Mode" 如 7.1.3 of the reference manual

中所述

“仅外部复位、看门狗复位、掉电 复位、INT0 上的外部电平中断或引脚变化中断可以唤醒 MCU。这种睡眠模式会停止所有生成的时钟

因此按钮中断已被处理,但是您在按钮处启用的定时器中断永远不会触发,因为返回睡眠模式会禁用定时器。

您想要 "Idle" 睡眠模式。