diff --git a/main.c b/main.c index e32d820..56269b9 100644 --- a/main.c +++ b/main.c @@ -1,42 +1,215 @@ +/** + * @file main.c + * @brief Bike rear light implementation for ATTINY202. + * + */ + +#include +#include #include #include -#define PA1_SET_MASK 0x02 // LED 1 to 2 -#define PA2_SET_MASK 0x04 // LED 3 to 6 -#define PA3_SET_MASK 0x08 // LED 7 to 8 -#define PA6_SET_MASK 0x40 // Green LED -#define PA7_SET_MASK 0x80 // Red LED +/** @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) { - VPORTA.DIR = 0x0; + // --- configure LED pins as outputs --- + VPORTA.DIR = (PA1_SET_MASK | PA2_SET_MASK | PA3_SET_MASK | + PA6_SET_MASK | PA7_SET_MASK); - // Configure PA1 as output (Pin 4 on ATTINY202-SSN) - VPORTA.DIR |= PA1_SET_MASK; + // --- ensure all LEDs off at startup --- + leds_off(); + VPORTA.OUT &= (uint8_t)~(PA6_SET_MASK | PA7_SET_MASK); - // Configure PA2 as output (Pin 5 on ATTINY202-SSN) - VPORTA.DIR |= PA2_SET_MASK; - - // Configure PA3 as output (Pin 7 on ATTINY202-SSN) - VPORTA.DIR |= PA3_SET_MASK; - - // Configure PA6 as output (Pin 2 on ATTINY202-SSN) - VPORTA.DIR |= PA6_SET_MASK; - - // Configure PA7 as output (Pin 3 on ATTINY202-SSN) - VPORTA.DIR |= PA7_SET_MASK; - - VPORTA.OUT ^= !PA1_SET_MASK; // LED 1 to 2 - VPORTA.OUT ^= !PA2_SET_MASK; // LED 3 to 6 - VPORTA.OUT ^= !PA3_SET_MASK; // LED 7 to 8 - - while (1) + while (true) { - VPORTA.OUT ^= PA6_SET_MASK; // toggle green LED - VPORTA.OUT ^= PA7_SET_MASK; // toggle red LED + // TODO: add status LED handling if needed + // VPORTA.OUT ^= PA6_SET_MASK; // toggle green LED + // VPORTA.OUT ^= PA7_SET_MASK; // toggle red LED - VPORTA.OUT ^= PA2_SET_MASK; // toggle inner four LED + blinkLed(); - _delay_ms(500); + _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; } }