/** * @file main.c * @brief Bike rear light implementation for ATTINY202. * */ #include #include #include #include /** @defgroup LED_Masks LED bitmasks for PORTA * @{ */ #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 PA7_SET_MASK 0x80 ///< Red LED /** @} */ /** @brief Main loop delay in ms (system tick). */ #define MAIN_LOOP_SLEEP 10U /** @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. */ volatile bool bLedEnabled = true; // Function forward declarations void blinkLed(void); static inline void leds_off(void); /** * @brief Main entry point. * * Initializes I/O ports and runs the main loop, periodically calling @ref blinkLed(). * * @return never returns */ 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); // --- ensure all LEDs off at startup --- leds_off(); VPORTA.OUT &= (uint8_t)~(PA6_SET_MASK | PA7_SET_MASK); 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 blinkLed(); _delay_ms(MAIN_LOOP_SLEEP); } } /** * @brief Switch off all controlled LEDs (PA1, PA2, PA3). * * @note Declared inline for speed (single instruction sequence). */ static inline void leds_off(void) { VPORTA.OUT &= (uint8_t)~(PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK); } /** * @brief LED blink state machine. * * Implements the following repeating sequence (non-blocking): * * 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 * * Special behavior: * - Every 3rd cycle, after step 18, all LEDs (PA1, PA2, PA3) are switched on for 250 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) { // --- precomputed constants (compile-time) --- const uint8_t T50 = MS_TO_TICKS(50); ///< 50 ms in ticks const uint8_t T100 = MS_TO_TICKS(100); ///< 100 ms in ticks const uint8_t T250 = MS_TO_TICKS(250); ///< 250 ms in ticks // --- persistent state --- static uint16_t counter = 0; ///< counts ticks for current state 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) { case 0: // all LEDs off, wait 250 ms leds_off(); if (counter >= T250) { counter = 0; state = 2; } break; case 2: // LED 1–2 + 7–8 on, wait 50 ms VPORTA.OUT |= (PA1_SET_MASK | PA3_SET_MASK); if (counter >= T50) { counter = 0; state = 5; } break; case 5: // all LEDs off, wait 100 ms leds_off(); if (counter >= T100) { counter = 0; state = 7; } break; case 7: // LED 1–2 + 7–8 on, wait 50 ms VPORTA.OUT |= (PA1_SET_MASK | PA3_SET_MASK); if (counter >= T50) { counter = 0; state = 10; } break; case 10: // all LEDs off, wait 250 ms leds_off(); if (counter >= T250) { counter = 0; state = 12; } break; case 12: // LED 3–6 on, wait 50 ms VPORTA.OUT |= PA2_SET_MASK; if (counter >= T50) { counter = 0; state = 14; } break; case 14: // all LEDs off, wait 100 ms leds_off(); if (counter >= T100) { counter = 0; state = 16; } break; case 16: // LED 3–6 on, wait 50 ms VPORTA.OUT |= PA2_SET_MASK; if (counter >= T50) { counter = 0; state = 18; } break; case 18: // all LEDs off, wait 250 ms leds_off(); if (counter >= T250) { counter = 0; if (++cycle >= 3) { cycle = 0; state = 20; // special all-LEDs-on state } else { state = 0; // restart normal cycle } } break; case 20: // special: all LEDs on for 250 ms VPORTA.OUT |= (PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK); if (counter >= T250) { counter = 0; state = 0; // restart normal sequence } break; } }