226 lines
5.8 KiB
C
226 lines
5.8 KiB
C
/*******************************************************************************
|
|
* @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 <zephyr/kernel.h>
|
|
#include <zephyr/drivers/gpio.h>
|
|
#include <zephyr/devicetree.h>
|
|
#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
|
|
}
|