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

时间:2015-10-11 22:54:52

标签: c microcontroller avr atmel isr

我正在开展个人项目,攻击万用表并为其添加背光。我正在使用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();
    }
}

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

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

sei();  
while (1) {}

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

1 个答案:

答案 0 :(得分:2)

您的睡眠模式为&#34;掉电模式&#34; 如7.1.3 of the reference manual

中所述

&#34;只有外部复位,看门狗复位,掉电 复位,INT0上的外部电平中断或引脚更改中断可唤醒MCU。此睡眠模式会暂停所有生成的时钟&#34;

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

你想要&#34;空闲&#34;睡眠模式。