Files
lezyne-rear-light-firmware/main.c

216 lines
5.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file main.c
* @brief Bike rear light implementation for ATTINY202.
*
*/
#include <stdint.h>
#include <stdbool.h>
#include <avr/io.h>
#include <util/delay.h>
/** @defgroup LED_Masks LED bitmasks for PORTA
* @{
*/
#define PA1_SET_MASK 0x02 ///< LED 12
#define PA2_SET_MASK 0x04 ///< LED 36
#define PA3_SET_MASK 0x08 ///< LED 78
#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 12 + 78 on, wait 50 ms
* - Step 5: all off, wait 100 ms
* - Step 7: LED 12 + 78 on, wait 50 ms
* - Step 10: all off, wait 250 ms
* - Step 12: LED 36 on, wait 50 ms
* - Step 14: all off, wait 100 ms
* - Step 16: LED 36 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 (02)
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 12 + 78 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 12 + 78 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 36 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 36 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;
}
}