From ddd8932ffac52287928503ad5c21eaac8a19615e652eeb8c1aed0d10642c20db Mon Sep 17 00:00:00 2001 From: localhorst Date: Sat, 6 Sep 2025 00:05:39 +0200 Subject: [PATCH] basic button toggle --- main.c | 126 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 96 insertions(+), 30 deletions(-) diff --git a/main.c b/main.c index 56269b9..a1aeab8 100644 --- a/main.c +++ b/main.c @@ -15,22 +15,25 @@ #define PA1_SET_MASK 0x02 ///< LED 1–2 #define PA2_SET_MASK 0x04 ///< LED 3–6 #define PA3_SET_MASK 0x08 ///< LED 7–8 -#define PA6_SET_MASK 0x40 ///< Green LED +#define PA6_SET_MASK 0x40 ///< Switch input (was green LED pin) TODO: Switch to PA0 #define PA7_SET_MASK 0x80 ///< Red LED /** @} */ -/** @brief Main loop delay in ms (system tick). */ -#define MAIN_LOOP_SLEEP 10U +#define MAIN_LOOP_SLEEP 10U // Main loop delay in ms (system tick) +#define BUTTON_PRESS_DURATION 1000U // Button needs to be pressed for at least 1000ms /** @brief Convert milliseconds to system ticks (integer division). */ #define MS_TO_TICKS(ms) ((ms) / MAIN_LOOP_SLEEP) -/** @brief Global flag: enable or disable LED output. */ +/** @brief Global flags */ volatile bool bLedEnabled = true; +volatile bool bBtnPressed = false; // Function forward declarations void blinkLed(void); static inline void leds_off(void); +static inline void leds_on(void); +static void handleSwitch(void); /** * @brief Main entry point. @@ -43,19 +46,45 @@ int main(void) { // --- configure LED pins as outputs --- VPORTA.DIR = (PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK | - PA6_SET_MASK | PA7_SET_MASK); + /*PA6_SET_MASK |*/ // PA6 now input (switch) + PA7_SET_MASK); + + // Configure PA6 as input with pull-up TODO: Switch to PA0 + VPORTA.DIR &= ~PA6_SET_MASK; // Input + PORTA.PIN6CTRL = PORT_PULLUPEN_bm; // Pull-up enabled // --- ensure all LEDs off at startup --- leds_off(); - VPORTA.OUT &= (uint8_t)~(PA6_SET_MASK | PA7_SET_MASK); + VPORTA.OUT &= (uint8_t) ~(PA7_SET_MASK); + + bool bLedEnabledOld = bLedEnabled; while (true) { - // TODO: add status LED handling if needed - // VPORTA.OUT ^= PA6_SET_MASK; // toggle green LED - // VPORTA.OUT ^= PA7_SET_MASK; // toggle red LED + handleSwitch(); // check switch state - blinkLed(); + if (bBtnPressed) + { + leds_on(); + } + else + { + leds_off(); + } + + if (bLedEnabledOld != bLedEnabled) + { + bLedEnabledOld = bLedEnabled; + leds_off(); + _delay_ms(1000); + } + else + { + if ((bLedEnabled) && (!bBtnPressed)) + { + blinkLed(); + } + } _delay_ms(MAIN_LOOP_SLEEP); } @@ -68,32 +97,72 @@ int main(void) */ static inline void leds_off(void) { - VPORTA.OUT &= (uint8_t)~(PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK); + VPORTA.OUT &= (uint8_t) ~(PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK); } /** - * @brief LED blink state machine. + * @brief Switch on all controlled LEDs (PA1, PA2, PA3). * - * Implements the following repeating sequence (non-blocking): + * @note Declared inline for speed (single instruction sequence). + */ +static inline void leds_on(void) +{ + VPORTA.OUT |= (PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK); +} + +/** + * @brief Handle momentary switch input on PA6. TODO: Switch to PA0 * - * Normal cycle: - * - Step 0: all off, wait 250 ms - * - Step 2: LED 1–2 + 7–8 on, wait 50 ms - * - Step 5: all off, wait 100 ms - * - Step 7: LED 1–2 + 7–8 on, wait 50 ms - * - Step 10: all off, wait 250 ms - * - Step 12: LED 3–6 on, wait 50 ms - * - Step 14: all off, wait 100 ms - * - Step 16: LED 3–6 on, wait 50 ms - * - Step 18: all off, wait 250 ms → restart + * A press longer than 2 seconds toggles ::bLedEnabled. + * Uses simple state and counters for debouncing and long-press detection. + */ +static void handleSwitch(void) +{ + static uint16_t pressTicks = 0; ///< press duration counter + static bool prevPressed = false; ///< previous switch state + + bool pressed = !(VPORTA.IN & PA6_SET_MASK); // active-low + + if (pressed) + { + bBtnPressed = true; + if (pressTicks < 0xFFFF) + { + // prevent overflow + pressTicks++; + } + } + else + { + bBtnPressed = false; + pressTicks = 0; + } + + if (prevPressed && pressTicks >= MS_TO_TICKS(BUTTON_PRESS_DURATION)) + { + // long press detected → toggle blinking + bLedEnabled = !bLedEnabled; + } + + prevPressed = pressed; +} + +/** + * @brief LED blink state machine (bike rear light style). + * + * Implements a "rounded" pulsing blink sequence: + * + * Normal cycle (~1 s total): + * - Step 0: all off, wait 100 ms + * - Step 1: LED 1–2 + 7–8 ON, wait 100 ms + * - Step 2: all LEDs ON, wait 200 ms + * - Step 3: only LED 1–2 ON, wait 100 ms + * - Step 4: all off, wait 400 ms → restart * * Special behavior: - * - Every 3rd cycle, after step 18, all LEDs (PA1, PA2, PA3) are switched on for 250 ms. + * - Every 3rd cycle, after step 4, all LEDs are switched on for 300 ms. * * Timing is based on @ref MAIN_LOOP_SLEEP ticks. - * - * @note Must be called periodically every 10 ms from the main loop. - * @note Depends on global flag ::bLedEnabled. If false, does nothing. */ void blinkLed(void) { @@ -107,9 +176,6 @@ void blinkLed(void) static uint8_t state = 0; ///< current step in the sequence static uint8_t cycle = 0; ///< cycle counter (0–2) - if (!bLedEnabled) - return; - counter++; switch (state)