#include "interrupts.h" void init_pins(void) { // The reed switch (PD3/INT1) switches to ground and the RTC alarm output // (PD2/INT0) is open-drain, so both need the internal pull-up. Leaving them // floating makes the inputs self-trigger. SET_PIN_IN(DDRD, DDD3); SET_PIN_HIGH(PORTD, PD3); SET_PIN_IN(DDRD, DDD2); SET_PIN_HIGH(PORTD, PD2); } // Both external interrupts are configured low-level triggered (ISCn1:0 = 00). // Edge detection needs the I/O clock, which SLEEP_MODE_PWR_DOWN stops, so a // falling-edge INT0/INT1 can never wake the MCU. Only level detection is // asynchronous. Each handler masks its own interrupt while the source is still // asserted, so the low level does not retrigger in a loop. void set_up_reed_interrupt(void) { EICRA &= ~((1 << ISC11) | (1 << ISC10)); EIMSK |= (1 << INT1); } void set_up_minute_interrupt(void) { EICRA &= ~((1 << ISC01) | (1 << ISC00)); EIMSK |= (1 << INT0); } void reed_interrupt_enable(void) { EIFR = (1 << INTF1); // Drop anything latched while we were masked EIMSK |= (1 << INT1); } void minute_interrupt_enable(void) { EIFR = (1 << INTF0); EIMSK |= (1 << INT0); } void wdt_isr_enable(void) { uint8_t sreg = SREG; cli(); wdt_reset(); // WDRF keeps WDE set, which would block the write below, so it must go // first. The unlock is a single assignment: a read-modify-write does not // open the 4-cycle change window. MCUSR &= ~(1 << WDRF); WDTCSR = (1 << WDCE) | (1 << WDE); // WDP[3:0] = 0b011 -> 0.125 s. Interrupt mode only (WDE clear), so an // expiry wakes us to clear the debounce instead of resetting the part. WDTCSR = (1 << WDIE) | (1 << WDP1) | (1 << WDP0); SREG = sreg; // Restore, never blanket-sei(): these run inside an ISR } void wdt_isr_disable(void) { uint8_t sreg = SREG; cli(); wdt_reset(); MCUSR &= ~(1 << WDRF); WDTCSR = (1 << WDCE) | (1 << WDE); WDTCSR = 0x00; SREG = sreg; }