247 lines
7.4 KiB
C
247 lines
7.4 KiB
C
#include "led_indicator.h"
|
|
|
|
#include <limits.h>
|
|
|
|
#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;
|
|
}
|