/******************************************************************************* * @file led_control.c * @brief LED direct control driver * * k_timer based 2-color LED (green/orange) pattern control * Simple on/off states use immediate GPIO, complex patterns use state machine ******************************************************************************/ #include #include #include #include "led_control.h" #include "debug_print.h" /* Devicetree LED specs */ #define LED_BLE_NODE DT_NODELABEL(led_ble) #define FUNCTION_LED_NODE DT_NODELABEL(function_led) static const struct gpio_dt_spec led_ble = GPIO_DT_SPEC_GET(LED_BLE_NODE, gpios); static const struct gpio_dt_spec function_led = GPIO_DT_SPEC_GET(FUNCTION_LED_NODE, gpios); /* Color constants */ #define COLOR_NONE 0 #define COLOR_GREEN 1 #define COLOR_ORANGE 2 /* Error pattern constants (State 7) */ #define ERROR_BLINK_ON_MS 166 #define ERROR_BLINK_OFF_MS 166 #define ERROR_BLINK_COUNT 3 #define ERROR_PAUSE_MS 1000 /* Pattern table */ typedef struct { uint32_t on_ms; uint32_t off_ms; uint8_t color; bool repeat; } led_pattern_t; static const led_pattern_t m_patterns[LED_STATE_COUNT] = { [LED_STATE_OFF] = { 0, 0, COLOR_NONE, false }, [LED_STATE_POWER_ON] = { 2000, 0, COLOR_GREEN, false }, [LED_STATE_POWER_OFF] = { 2000, 0, COLOR_GREEN, false }, [LED_STATE_ADVERTISING] = { 500, 500, COLOR_GREEN, true }, [LED_STATE_DETACH_WARNING] = { 1000, 3000, COLOR_GREEN, true }, [LED_STATE_ALIGN_SEARCHING] = { 1000, 1000, COLOR_ORANGE, true }, [LED_STATE_ALIGN_COMPLETE] = { 0, 0, COLOR_GREEN, false }, [LED_STATE_ERROR] = { 0, 0, COLOR_ORANGE, true }, }; /* Module variables */ static struct k_timer m_led_timer; static led_state_t m_current_state = LED_STATE_OFF; static bool m_phase_on; /* Error pattern state machine */ static uint8_t m_error_blink_cnt; static uint8_t m_error_phase; /* GPIO helpers */ static inline void led_ble_on(void) { gpio_pin_set_dt(&led_ble, 1); } static inline void led_ble_off(void) { gpio_pin_set_dt(&led_ble, 0); } static inline void function_led_on(void) { gpio_pin_set_dt(&function_led, 1); } static inline void function_led_off(void) { gpio_pin_set_dt(&function_led, 0); } static void led_all_off(void) { led_ble_off(); function_led_off(); } static void led_color_on(uint8_t color) { led_all_off(); if (color == COLOR_GREEN) led_ble_on(); else if (color == COLOR_ORANGE) function_led_on(); } /* Timer helper */ static void timer_start_ms(uint32_t ms) { if (ms == 0) return; k_timer_start(&m_led_timer, K_MSEC(ms), K_NO_WAIT); } /* Error pattern state machine */ static void error_pattern_start(void) { m_error_blink_cnt = 0; m_error_phase = 0; led_color_on(COLOR_ORANGE); timer_start_ms(ERROR_BLINK_ON_MS); } static void error_pattern_tick(void) { switch (m_error_phase) { case 0: // ON period done -> OFF led_all_off(); m_error_phase = 1; timer_start_ms(ERROR_BLINK_OFF_MS); break; case 1: // OFF period done m_error_blink_cnt++; if (m_error_blink_cnt < ERROR_BLINK_COUNT) { m_error_phase = 0; led_color_on(COLOR_ORANGE); timer_start_ms(ERROR_BLINK_ON_MS); } else { m_error_phase = 2; timer_start_ms(ERROR_PAUSE_MS); } break; case 2: // Pause done -> restart m_error_blink_cnt = 0; m_error_phase = 0; led_color_on(COLOR_ORANGE); timer_start_ms(ERROR_BLINK_ON_MS); break; default: break; } } /* Timer callback */ static void led_timer_handler(struct k_timer *timer) { ARG_UNUSED(timer); if (m_current_state == LED_STATE_ERROR) { error_pattern_tick(); return; } const led_pattern_t *p = &m_patterns[m_current_state]; if (m_phase_on) { // ON -> OFF transition led_all_off(); m_phase_on = false; if (p->off_ms > 0) { timer_start_ms(p->off_ms); } else if (!p->repeat) { if (m_current_state == LED_STATE_POWER_OFF) { led_all_off(); m_current_state = LED_STATE_OFF; } // POWER_ON: stays lit, no timer } } else { // OFF -> ON transition if (p->repeat) { led_color_on(p->color); m_phase_on = true; timer_start_ms(p->on_ms); } } } /* Public functions */ void led_init(void) { gpio_pin_configure_dt(&led_ble, GPIO_OUTPUT_INACTIVE); gpio_pin_configure_dt(&function_led, GPIO_OUTPUT_INACTIVE); led_all_off(); k_timer_init(&m_led_timer, led_timer_handler, NULL); m_current_state = LED_STATE_OFF; } void led_set_state(led_state_t state) { if (state >= LED_STATE_COUNT) return; k_timer_stop(&m_led_timer); led_all_off(); m_current_state = state; m_phase_on = false; const led_pattern_t *p = &m_patterns[state]; switch (state) { case LED_STATE_OFF: break; case LED_STATE_ERROR: error_pattern_start(); break; default: led_color_on(p->color); m_phase_on = true; if (p->on_ms > 0) { timer_start_ms(p->on_ms); } break; } } led_state_t led_get_state(void) { return m_current_state; } void led_ble_solid(void) { k_timer_stop(&m_led_timer); led_all_off(); led_ble_on(); m_current_state = LED_STATE_OFF; // no pattern running }