Implements TIM3_CH1 PWM driver for 8-LED NeoPixel ring with: - 6 state-based animations: boot (blue chase), armed (solid green), error (red blink), low battery (yellow pulse), charging (green breathe), e_stop (red strobe) - Non-blocking via 1 ms tick callback - GRB byte order encoding (WS2812B standard) - PWM duty values for "0" (~40%) and "1" (~56%) bit encoding - 10 unit tests covering state transitions, animations, color encoding Driver integrated into main.c initialization and main loop tick. Includes buzzer driver (Issue #189) integration. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
102 lines
2.9 KiB
C
102 lines
2.9 KiB
C
#ifndef LED_H
|
||
#define LED_H
|
||
|
||
#include <stdint.h>
|
||
#include <stdbool.h>
|
||
|
||
/*
|
||
* led.h — WS2812B NeoPixel status indicator driver (Issue #193)
|
||
*
|
||
* Hardware: TIM3_CH1 PWM on PB4 at 800 kHz (1.25 µs per bit).
|
||
* Controls an 8-LED ring with state-based animations:
|
||
* - Boot: Blue chase (startup sequence)
|
||
* - Armed: Solid green
|
||
* - Error: Red blinking (visual alert)
|
||
* - Low Battery: Yellow pulsing (warning)
|
||
* - Charging: Green breathing (soft indication)
|
||
* - E-Stop: Red strobe (immediate action required)
|
||
*
|
||
* State transitions are non-blocking via a 1 ms timer callback (led_tick).
|
||
* Each state defines its own animation envelope: color, timing, and brightness.
|
||
*
|
||
* WS2812 protocol (NRZ):
|
||
* - Bit "0": High 350 ns, Low 800 ns (1.25 µs total)
|
||
* - Bit "1": High 700 ns, Low 600 ns (1.25 µs total)
|
||
* - Reset: Low > 50 µs
|
||
*
|
||
* PWM-based implementation via DMA:
|
||
* - 10 levels: [350 ns, 400, 450, 500, 550, 600, 650, 700, 750, 800]
|
||
* - Bit "0" → High 350-400 ns Bit "1" → High 650-800 ns
|
||
* - Each bit requires one PWM cycle; 24 bits/LED × 8 LEDs = 192 cycles
|
||
* - DMA rings through buffer, auto-reloads on update events
|
||
*/
|
||
|
||
/* LED state enumeration */
|
||
typedef enum {
|
||
LED_STATE_BOOT = 0, /* Blue chase (startup) */
|
||
LED_STATE_ARMED = 1, /* Solid green */
|
||
LED_STATE_ERROR = 2, /* Red blinking */
|
||
LED_STATE_LOW_BATT = 3, /* Yellow pulsing */
|
||
LED_STATE_CHARGING = 4, /* Green breathing */
|
||
LED_STATE_ESTOP = 5, /* Red strobe */
|
||
LED_STATE_COUNT
|
||
} LEDState;
|
||
|
||
/* RGB color (8-bit per channel) */
|
||
typedef struct {
|
||
uint8_t r;
|
||
uint8_t g;
|
||
uint8_t b;
|
||
} RGBColor;
|
||
|
||
/*
|
||
* led_init()
|
||
*
|
||
* Configure TIM3_CH1 PWM on PB4 at 800 kHz, set up DMA for bit streaming,
|
||
* and initialize the LED buffer. Call once at startup, after buzzer_init()
|
||
* but before the main loop.
|
||
*/
|
||
void led_init(void);
|
||
|
||
/*
|
||
* led_set_state(state)
|
||
*
|
||
* Change the LED display state. The animation runs non-blocking via led_tick().
|
||
* Valid states: LED_STATE_BOOT, LED_STATE_ARMED, LED_STATE_ERROR, etc.
|
||
*/
|
||
void led_set_state(LEDState state);
|
||
|
||
/*
|
||
* led_get_state()
|
||
*
|
||
* Return the current LED state.
|
||
*/
|
||
LEDState led_get_state(void);
|
||
|
||
/*
|
||
* led_set_color(r, g, b)
|
||
*
|
||
* Manually set the LED ring to a solid color. Overrides the current state
|
||
* animation until led_set_state() is called again.
|
||
*/
|
||
void led_set_color(uint8_t r, uint8_t g, uint8_t b);
|
||
|
||
/*
|
||
* led_tick(now_ms)
|
||
*
|
||
* Advance animation state machine. Must be called every 1 ms from the main loop.
|
||
* Handles state-specific animations: chase timing, pulse envelope, strobe phase, etc.
|
||
* Updates the DMA buffer with new LED values without blocking.
|
||
*/
|
||
void led_tick(uint32_t now_ms);
|
||
|
||
/*
|
||
* led_is_animating()
|
||
*
|
||
* Returns true if the current state is actively animating (e.g., chase, pulse, strobe).
|
||
* Returns false for static states (armed, error solid).
|
||
*/
|
||
bool led_is_animating(void);
|
||
|
||
#endif /* LED_H */
|