I am working on a personal project, hacking a multimeter and adding backlight to it. I am using an Attiny13.
I have the following code:
/* 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();
}
}
The problem is it wakes up only on first interrupt (button push), executes it and then nothing. If I don't use sleep (leave only while(1);) the program runs as expected. Do you know what could be the problem?
LE: Added full code. If I have:
sei();
while (1) {}
Then everything works OK. I just want to use sleep to reduce consumption.
Your sleep mode is "Power-down Mode" as described in 7.1.3 of the reference manual
"Only an External Reset, a Watchdog Reset, a Brown-out Reset, an external level interrupt on INT0, or a pin change interrupt can wake up the MCU. This sleep mode halts all generated clocks"
So the push button interrupt is handled, however the timer interrupt you enable at push button never fires because returning to sleep mode disables the timer.
You want the "Idle" sleep mode.