#include "led_indicator.h" #include #define LEVELS_1 0x01U #define LEVELS_10 0x01U #define LEVELS_1010 0x05U #define LEVELS_101010 0x15U static const LedIndicator_Pattern g_default_patterns[LED_INDICATOR_MODE_COUNT] = { {{0U}, 0U, 1U, 1U, 0U}, {{0U}, LEVELS_1, 1U, 1U, 1U}, {{100U, 100U, 100U, 100U, 100U, 100U}, LEVELS_101010, 6U, 0U, 1U}, {{100U, 900U}, LEVELS_10, 2U, 1U, 0U}, {{50U, 50U}, LEVELS_10, 2U, 1U, 0U}, {{250U, 250U}, LEVELS_10, 2U, 1U, 0U}, {{100U, 100U, 100U, 700U}, LEVELS_1010, 4U, 1U, 0U}, {{100U, 100U, 100U, 100U, 100U, 700U}, LEVELS_101010, 6U, 1U, 0U} }; static uint8_t pattern_valid(const LedIndicator_Pattern *pattern) { size_t i; uint32_t total = 0U; if ((pattern == NULL) || (pattern->step_count == 0U) || (pattern->step_count > LED_INDICATOR_MAX_STEPS)) { return 0U; } if (pattern->step_count == 1U) { return 1U; } for (i = 0U; i < pattern->step_count; ++i) { if ((pattern->duration_ms[i] == 0U) || (UINT32_MAX - total < pattern->duration_ms[i])) { return 0U; } total += pattern->duration_ms[i]; } return (uint8_t)(total != 0U); } static uint32_t pattern_period(const LedIndicator_Pattern *pattern) { size_t i; uint32_t total = 0U; for (i = 0U; i < pattern->step_count; ++i) { total += pattern->duration_ms[i]; } return total; } static uint8_t pattern_level(const LedIndicator_Pattern *pattern, uint32_t elapsed) { uint32_t position = elapsed; uint32_t period; size_t i; if (pattern->step_count == 1U) { return (uint8_t)(pattern->levels & 1U); } period = pattern_period(pattern); if (pattern->repeat != 0U) { position %= period; } else if (position >= period) { return (uint8_t)(pattern->final_level != 0U); } for (i = 0U; i < pattern->step_count; ++i) { if (position < pattern->duration_ms[i]) { return (uint8_t)((pattern->levels >> i) & 1U); } position -= pattern->duration_ms[i]; } return (uint8_t)(pattern->final_level != 0U); } static void write_if_changed(LedIndicator *instance, size_t channel, uint8_t level, uint8_t force) { LedIndicator_Channel *state = &instance->channels[channel]; level = (uint8_t)(level != 0U); if ((force != 0U) || (state->output_level != level)) { state->output_level = level; instance->port.write(instance->port.context, (uint8_t)channel, level); } } void LedIndicator_ConfigDefault(LedIndicator_Config *config) { if (config != NULL) { config->patterns = g_default_patterns; config->pattern_count = LED_INDICATOR_MODE_COUNT; } } size_t LedIndicator_CopyDefaultPatterns(LedIndicator_Pattern *patterns, size_t capacity) { size_t i; size_t count = (size_t)LED_INDICATOR_MODE_COUNT; if (patterns == NULL) { return 0U; } if (capacity < count) { count = capacity; } for (i = 0U; i < count; ++i) { patterns[i] = g_default_patterns[i]; } return count; } uint8_t LedIndicator_ValidateConfig(const LedIndicator_Config *config) { size_t i; if ((config == NULL) || (config->patterns == NULL) || (config->pattern_count == 0U) || (config->pattern_count > (size_t)LED_INDICATOR_MODE_COUNT)) { return 0U; } for (i = 0U; i < config->pattern_count; ++i) { if (pattern_valid(&config->patterns[i]) == 0U) { return 0U; } } return 1U; } uint8_t LedIndicator_Init(LedIndicator *instance, LedIndicator_Channel *channels, size_t channel_count, const LedIndicator_Port *port, const LedIndicator_Config *config) { size_t i; if ((instance == NULL) || (channels == NULL) || (channel_count == 0U) || (channel_count > 256U) || (port == NULL) || (port->write == NULL) || (LedIndicator_ValidateConfig(config) == 0U)) { return 0U; } instance->port = *port; instance->patterns = config->patterns; instance->pattern_count = config->pattern_count; instance->channels = channels; instance->channel_count = channel_count; instance->initialized = 1U; for (i = 0U; i < channel_count; ++i) { channels[i].mode = LED_INDICATOR_MODE_OFF; channels[i].started_ms = 0U; channels[i].output_level = 0U; channels[i].initialized = 1U; write_if_changed(instance, i, 0U, 1U); } return 1U; } uint8_t LedIndicator_SetModeAt(LedIndicator *instance, size_t channel, LedIndicator_Mode mode, uint32_t now_ms) { if ((instance == NULL) || (instance->initialized == 0U) || (channel >= instance->channel_count) || ((size_t)mode >= instance->pattern_count)) { return 0U; } if (instance->channels[channel].mode == mode) { return 1U; } return LedIndicator_RestartModeAt(instance, channel, mode, now_ms); } uint8_t LedIndicator_RestartModeAt(LedIndicator *instance, size_t channel, LedIndicator_Mode mode, uint32_t now_ms) { LedIndicator_Channel *state; const LedIndicator_Pattern *pattern; if ((instance == NULL) || (instance->initialized == 0U) || (channel >= instance->channel_count) || ((size_t)mode >= instance->pattern_count)) { return 0U; } state = &instance->channels[channel]; state->mode = mode; state->started_ms = now_ms; pattern = &instance->patterns[(size_t)mode]; write_if_changed(instance, channel, pattern_level(pattern, 0U), 0U); return 1U; } uint8_t LedIndicator_SetMode(LedIndicator *instance, size_t channel, LedIndicator_Mode mode) { if ((instance == NULL) || (instance->port.now_ms == NULL)) { return 0U; } return LedIndicator_SetModeAt(instance, channel, mode, instance->port.now_ms(instance->port.context)); } uint8_t LedIndicator_RestartMode(LedIndicator *instance, size_t channel, LedIndicator_Mode mode) { if ((instance == NULL) || (instance->port.now_ms == NULL)) { return 0U; } return LedIndicator_RestartModeAt(instance, channel, mode, instance->port.now_ms(instance->port.context)); } void LedIndicator_ProcessAt(LedIndicator *instance, uint32_t now_ms) { size_t i; if ((instance == NULL) || (instance->initialized == 0U)) { return; } for (i = 0U; i < instance->channel_count; ++i) { const LedIndicator_Channel *state = &instance->channels[i]; const LedIndicator_Pattern *pattern = &instance->patterns[(size_t)state->mode]; const uint32_t elapsed = now_ms - state->started_ms; write_if_changed(instance, i, pattern_level(pattern, elapsed), 0U); } } void LedIndicator_Process(LedIndicator *instance) { if ((instance != NULL) && (instance->port.now_ms != NULL)) { LedIndicator_ProcessAt(instance, instance->port.now_ms(instance->port.context)); } } LedIndicator_Mode LedIndicator_GetMode(const LedIndicator *instance, size_t channel) { if ((instance == NULL) || (instance->initialized == 0U) || (channel >= instance->channel_count)) { return LED_INDICATOR_MODE_OFF; } return instance->channels[channel].mode; }