From c9d30ef44e45078988356ebdd3d3dc24e4f20002b8ddd2823f3b7ef147d9d1d8 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 25 Oct 2025 14:15:08 +0200 Subject: [PATCH 1/4] first interrupt wakeup --- README.md | 2 +- main.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7dc0b68..03e253f 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Hardware: FT232 USB-UART adapter connected to UPDI with a 4.7 kΩ resistor. ``` 4. Flash using pymcuprog: ```bash - pymcuprog -t uart -u /dev/ttyUSB0 -d attiny202 write -f build/main.hex + pymcuprog -t uart -u /dev/ttyUSB0 -d attiny202 erase && pymcuprog -t uart -u /dev/ttyUSB0 -d attiny202 write -f build/main.hex ``` ## Hardware Setup (FT232 → ATtiny202 UPDI) ```bash diff --git a/main.c b/main.c index eacd981..44e9302 100644 --- a/main.c +++ b/main.c @@ -8,6 +8,10 @@ #include #include #include +#include +#include +#include +#include // for CCP #define BUTTON_PIN_MASK 0x01 // PA0 TODO: using RESET/UPDI pin #define PA1_SET_MASK 0x02 ///< LED 1–2 @@ -33,17 +37,54 @@ static inline void leds_off(void); static inline void leds_on(void); static void battery_level_indicator(void); static void handleSwitch(void); +int main(void); + +void software_reset(void) +{ + CCP = CCP_IOREG_gc; // unlock protected registers + RSTCTRL.SWRR = 1; // trigger software reset + cli(); // disable interrupts + + // Unlock protected registers + CCP = CCP_IOREG_gc; + + + // Enable WDT with shortest period (~8 cycles) + WDT.CTRLA = WDT_PERIOD_8CLK_gc; + + while (1) + ; // wait for reset +} + +ISR(PORTA_PORT_vect) +{ + // Clear interrupt flags for PA0 + PORTA.INTFLAGS = BUTTON_PIN_MASK; + if (!(VPORTA.IN & BUTTON_PIN_MASK)) // check PA0 low + { + // Turn off all LEDs + leds_off(); + software_reset(); + } +} /** * @brief Main entry point */ int main(void) { + bLedEnabled = true; + bBtnPressed = false; + + cli(); // disable interrupts + + // register8_t backuop_dir = VPORTA.DIR; // Configure LED pins as outputs VPORTA.DIR = (PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK | PA6_SET_MASK | PA7_SET_MASK); // Configure PA0 as input with pull-up VPORTA.DIR &= ~BUTTON_PIN_MASK; // Input + // register8_t backup_ctl = PORTA.PIN0CTRL; PORTA.PIN0CTRL = PORT_PULLUPEN_bm; // Pull-up enabled // Ensure all LEDs off at startup @@ -79,6 +120,21 @@ int main(void) _delay_ms(BUTTON_IGNORE_DURATION_MS); blinkLed(true); // reset blink state machine + leds_off(); + + // TODO: activate the interrupt for PA0 + PORTA.PIN0CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc; + + sei(); // Enable global interrupts + while (1) + { + leds_off(); + _delay_ms(BUTTON_IGNORE_DURATION_MS / 20); + leds_on(); + _delay_ms(BUTTON_IGNORE_DURATION_MS / 20); + } + + // TODO: add a isr that disabled all leds and loops in while(1); } else { From 35c66787de9bcafcfd0e1d1ba149a22bef208a89f93ee51e3813d4c33fb6ffa3 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 25 Oct 2025 14:18:03 +0200 Subject: [PATCH 2/4] cleanup --- main.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/main.c b/main.c index 44e9302..649665b 100644 --- a/main.c +++ b/main.c @@ -43,15 +43,6 @@ void software_reset(void) { CCP = CCP_IOREG_gc; // unlock protected registers RSTCTRL.SWRR = 1; // trigger software reset - cli(); // disable interrupts - - // Unlock protected registers - CCP = CCP_IOREG_gc; - - - // Enable WDT with shortest period (~8 cycles) - WDT.CTRLA = WDT_PERIOD_8CLK_gc; - while (1) ; // wait for reset } @@ -60,6 +51,7 @@ ISR(PORTA_PORT_vect) { // Clear interrupt flags for PA0 PORTA.INTFLAGS = BUTTON_PIN_MASK; + if (!(VPORTA.IN & BUTTON_PIN_MASK)) // check PA0 low { // Turn off all LEDs @@ -73,12 +65,6 @@ ISR(PORTA_PORT_vect) */ int main(void) { - bLedEnabled = true; - bBtnPressed = false; - - cli(); // disable interrupts - - // register8_t backuop_dir = VPORTA.DIR; // Configure LED pins as outputs VPORTA.DIR = (PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK | PA6_SET_MASK | PA7_SET_MASK); From e0781257a659fbbcbbec4c63e1941b5d85641908d801368af32b6136a2d7e731 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 25 Oct 2025 14:30:43 +0200 Subject: [PATCH 3/4] do power down --- main.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/main.c b/main.c index 649665b..f5e1b22 100644 --- a/main.c +++ b/main.c @@ -65,6 +65,9 @@ ISR(PORTA_PORT_vect) */ int main(void) { + // MCU resumes execution here after reset + sleep_disable(); // Disable sleep + // Configure LED pins as outputs VPORTA.DIR = (PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK | PA6_SET_MASK | PA7_SET_MASK); @@ -108,19 +111,17 @@ int main(void) blinkLed(true); // reset blink state machine leds_off(); + _delay_ms(BUTTON_IGNORE_DURATION_MS * 2); + // TODO: activate the interrupt for PA0 PORTA.PIN0CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc; - sei(); // Enable global interrupts - while (1) - { - leds_off(); - _delay_ms(BUTTON_IGNORE_DURATION_MS / 20); - leds_on(); - _delay_ms(BUTTON_IGNORE_DURATION_MS / 20); - } + set_sleep_mode(SLEEP_MODE_STANDBY); // Deepest sleep mode (standby) + sleep_enable(); // Enable sleep - // TODO: add a isr that disabled all leds and loops in while(1); + cli(); // Disable global interrupts + sei(); // Re-enable interrupts + sleep_cpu(); // MCU sleeps here } else { From 7e8165e7a2d6d5fa91251274b1491bf2d4ed97449fb931efaebfaf6c70819df6 Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 25 Oct 2025 14:47:08 +0200 Subject: [PATCH 4/4] cleanup --- main.c | 86 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 44 insertions(+), 42 deletions(-) diff --git a/main.c b/main.c index f5e1b22..b9438c9 100644 --- a/main.c +++ b/main.c @@ -10,10 +10,8 @@ #include #include #include -#include -#include // for CCP -#define BUTTON_PIN_MASK 0x01 // PA0 TODO: using RESET/UPDI pin +#define BUTTON_PIN_MASK 0x01 // PA0 used as RESET/UPDI pin #define PA1_SET_MASK 0x02 ///< LED 1–2 #define PA2_SET_MASK 0x04 ///< LED 3–6 #define PA3_SET_MASK 0x08 ///< LED 7–8 @@ -22,7 +20,9 @@ #define MAIN_LOOP_SLEEP 10U // Main loop delay in ms #define BUTTON_LONG_PRESS_DURATION_MS 1000U // Long press threshold -#define BUTTON_IGNORE_DURATION_MS 1000U // Time button ignored after long press +#define BUTTON_IGNORE_DURATION_MS 2000U // Time button ignored after long press +#define BUTTON_CONFIRMATION_BLINK_LOOPS 10U // Blink animation for confirmation +#define BUTTON_CONFIRMATION_BLINK_DURATION_MS 50U /** Convert milliseconds to system ticks */ #define MS_TO_TICKS(ms) ((ms) / MAIN_LOOP_SLEEP) @@ -37,43 +37,19 @@ static inline void leds_off(void); static inline void leds_on(void); static void battery_level_indicator(void); static void handleSwitch(void); -int main(void); - -void software_reset(void) -{ - CCP = CCP_IOREG_gc; // unlock protected registers - RSTCTRL.SWRR = 1; // trigger software reset - while (1) - ; // wait for reset -} - -ISR(PORTA_PORT_vect) -{ - // Clear interrupt flags for PA0 - PORTA.INTFLAGS = BUTTON_PIN_MASK; - - if (!(VPORTA.IN & BUTTON_PIN_MASK)) // check PA0 low - { - // Turn off all LEDs - leds_off(); - software_reset(); - } -} +static void software_reset(void); +ISR(PORTA_PORT_vect); /** * @brief Main entry point */ int main(void) { - // MCU resumes execution here after reset - sleep_disable(); // Disable sleep - // Configure LED pins as outputs VPORTA.DIR = (PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK | PA6_SET_MASK | PA7_SET_MASK); // Configure PA0 as input with pull-up VPORTA.DIR &= ~BUTTON_PIN_MASK; // Input - // register8_t backup_ctl = PORTA.PIN0CTRL; PORTA.PIN0CTRL = PORT_PULLUPEN_bm; // Pull-up enabled // Ensure all LEDs off at startup @@ -96,24 +72,24 @@ int main(void) leds_off(); } - // Long press detected → show confirmation blink + // Long press detected --> show confirmation blink if (bLedEnabledOld != bLedEnabled) { bLedEnabledOld = bLedEnabled; - leds_off(); - _delay_ms(BUTTON_IGNORE_DURATION_MS / 10); - leds_on(); - _delay_ms(BUTTON_IGNORE_DURATION_MS / 10); - leds_off(); + for (uint8_t i = 0U; i < BUTTON_CONFIRMATION_BLINK_LOOPS; i++) + { + leds_off(); + _delay_ms(BUTTON_CONFIRMATION_BLINK_DURATION_MS); + leds_on(); + _delay_ms(BUTTON_CONFIRMATION_BLINK_DURATION_MS); + leds_off(); + } + + // Give time until button is released _delay_ms(BUTTON_IGNORE_DURATION_MS); - blinkLed(true); // reset blink state machine - leds_off(); - - _delay_ms(BUTTON_IGNORE_DURATION_MS * 2); - - // TODO: activate the interrupt for PA0 + // Activate the interrupt for PA0 PORTA.PIN0CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc; set_sleep_mode(SLEEP_MODE_STANDBY); // Deepest sleep mode (standby) @@ -192,6 +168,32 @@ static void handleSwitch(void) prevPressed = pressed; } +/** + * @brief Perform software reset + */ +static void software_reset(void) +{ + CCP = CCP_IOREG_gc; // unlock protected registers + RSTCTRL.SWRR = 1; // trigger software reset + while (1) + ; // wait for reset +} + +/** + * @brief Interrupt service routine + */ +ISR(PORTA_PORT_vect) +{ + // Clear interrupt flags for PA0 + PORTA.INTFLAGS = BUTTON_PIN_MASK; + + if (!(VPORTA.IN & BUTTON_PIN_MASK)) // check PA0 low + { + // Turn off all LEDs + software_reset(); + } +} + /** * @brief LED blink state machine (bike rear light style) *