为什么只有第一个中断起作用?
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" 睡眠模式。
我正在做一个个人项目,破解万用表并为其添加背光。我正在使用 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" 睡眠模式。