Compare commits

7 Commits

Author SHA256 Message Date
a9f342b45b Merge branch 'main' into feature/energy-saving 2025-10-25 14:56:37 +02:00
042d38b0f5 Merge pull request 'Disable CPU while Rear Light is not used' (#3) from feature/power-off into main
Reviewed-on: #3
2025-10-25 14:49:38 +02:00
7e8165e7a2 cleanup 2025-10-25 14:47:08 +02:00
e0781257a6 do power down 2025-10-25 14:30:43 +02:00
35c66787de cleanup 2025-10-25 14:18:03 +02:00
c9d30ef44e first interrupt wakeup 2025-10-25 14:15:08 +02:00
74a0780219 try to use timer interrupt 2025-09-13 17:51:19 +02:00
2 changed files with 87 additions and 20 deletions

View File

@ -76,7 +76,7 @@ Hardware: FT232 USB-UART adapter connected to UPDI with a 4.7 kΩ resistor.
``` ```
4. Flash using pymcuprog: 4. Flash using pymcuprog:
```bash ```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) ## Hardware Setup (FT232 → ATtiny202 UPDI)
```bash ```bash

105
main.c
View File

@ -1,26 +1,28 @@
/** /**
* @file main.c * @file main.c
* @brief Bike rear light implementation for ATTINY202. * @brief Bike rear light implementation for ATTINY202 with low-power standby.
*
*/ */
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <avr/io.h> #include <avr/io.h>
#include <util/delay.h> #include <util/delay.h>
#include <avr/sleep.h>
#include <avr/interrupt.h>
#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 12 #define PA1_SET_MASK 0x02 ///< LED 12
#define PA2_SET_MASK 0x04 ///< LED 36 #define PA2_SET_MASK 0x04 ///< LED 36
#define PA3_SET_MASK 0x08 ///< LED 78 #define PA3_SET_MASK 0x08 ///< LED 78
#define PA6_SET_MASK 0x40 ///< Green LED pin #define PA6_SET_MASK 0x40 ///< Green LED pin
#define PA7_SET_MASK 0x80 ///< Red LED #define PA7_SET_MASK 0x80 ///< Red LED
#define MAIN_LOOP_SLEEP 10U // Main loop delay in ms #define MAIN_LOOP_SLEEP 50U // Loop period in ms
#define BUTTON_LONG_PRESS_DURATION_MS 1000U // Long press threshold #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) #define MS_TO_TICKS(ms) ((ms) / MAIN_LOOP_SLEEP)
/** Global flags */ /** Global flags */
@ -33,10 +35,28 @@ static inline void leds_off(void);
static inline void leds_on(void); static inline void leds_on(void);
static void battery_level_indicator(void); static void battery_level_indicator(void);
static void handleSwitch(void); static void handleSwitch(void);
static void software_reset(void);
ISR(PORTA_PORT_vect);
/** /* --- Timer init: Use RTC PIT for periodic wake-up --- */
* @brief Main entry point void init_timer(void)
*/ {
RTC.CLKSEL = RTC_CLKSEL_INT1K_gc; // 1 kHz ULP clock for RTC
while (RTC.STATUS > 0)
{
} // Wait for sync
RTC.PITINTCTRL = RTC_PI_bm; // Enable PIT interrupt
RTC.PITCTRLA = RTC_PERIOD_CYC64_gc // ≈64 ms wake-up (~50 ms)
| RTC_PITEN_bm; // Enable PIT
}
ISR(RTC_PIT_vect)
{
RTC.PITINTFLAGS = RTC_PI_bm; // Clear interrupt flag
}
/* --- MAIN --- */
int main(void) int main(void)
{ {
// Configure LED pins as outputs // Configure LED pins as outputs
@ -52,7 +72,13 @@ int main(void)
bool bLedEnabledOld = bLedEnabled; bool bLedEnabledOld = bLedEnabled;
while (true) cli();
init_timer();
sei();
set_sleep_mode(SLEEP_MODE_STANDBY);
while (1)
{ {
handleSwitch(); // Check switch state handleSwitch(); // Check switch state
@ -66,19 +92,32 @@ int main(void)
leds_off(); leds_off();
} }
// Long press detected show confirmation blink // Long press detected --> show confirmation blink
if (bLedEnabledOld != bLedEnabled) if (bLedEnabledOld != bLedEnabled)
{ {
bLedEnabledOld = bLedEnabled; bLedEnabledOld = bLedEnabled;
leds_off(); for (uint8_t i = 0U; i < BUTTON_CONFIRMATION_BLINK_LOOPS; i++)
_delay_ms(BUTTON_IGNORE_DURATION_MS / 10); {
leds_on(); leds_off();
_delay_ms(BUTTON_IGNORE_DURATION_MS / 10); _delay_ms(BUTTON_CONFIRMATION_BLINK_DURATION_MS);
leds_off(); leds_on();
_delay_ms(BUTTON_CONFIRMATION_BLINK_DURATION_MS);
leds_off();
}
// Give time until button is released
_delay_ms(BUTTON_IGNORE_DURATION_MS); _delay_ms(BUTTON_IGNORE_DURATION_MS);
blinkLed(true); // reset blink state machine // Activate the interrupt for PA0
PORTA.PIN0CTRL = PORT_PULLUPEN_bm | PORT_ISC_FALLING_gc;
set_sleep_mode(SLEEP_MODE_STANDBY); // Deepest sleep mode (standby)
sleep_enable(); // Enable sleep
cli(); // Disable global interrupts
sei(); // Re-enable interrupts
sleep_cpu(); // MCU sleeps here
} }
else else
{ {
@ -88,7 +127,9 @@ int main(void)
} }
} }
_delay_ms(MAIN_LOOP_SLEEP); sleep_enable();
sleep_cpu(); // Sleep until PIT wakes
sleep_disable();
} }
} }
@ -149,6 +190,32 @@ static void handleSwitch(void)
prevPressed = pressed; 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) * @brief LED blink state machine (bike rear light style)
* *
@ -279,7 +346,7 @@ void blinkLed(bool resetCounters)
if (counter >= T250) if (counter >= T250)
{ {
counter = 0; counter = 0;
state = 0; // restart normal sequence state = 0;
} }
break; break;
} }