263 lines
9.6 KiB
C
263 lines
9.6 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},
|
||
{{100U, 900U}, LEVELS_10, 2U, 0U, 0U}, /* Один цикл приёма длится 1000 мс: 100 мс свечения составляют 10%, затем 900 мс паузы и выключение. */
|
||
{{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));
|
||
}
|
||
|
||
uint8_t LedIndicator_TriggerActivityAt(LedIndicator *instance, size_t channel, uint32_t now_ms) /* Каждый пакет начинает ACTIVITY заново, даже если предыдущая вспышка ещё продолжается. */
|
||
{ /* Использует общую проверку канала и принудительный сброс фазы в миллисекундной временной базе. */
|
||
return LedIndicator_RestartModeAt(instance, channel, LED_INDICATOR_MODE_ACTIVITY, now_ms); /* Штатный однократный шаблон завершает цикл выключенным уровнем без свободного повторения. */
|
||
} /* Завершает запуск одного цикла приёма без ожидания окончания свечения либо паузы. */
|
||
|
||
uint8_t LedIndicator_TriggerActivity(LedIndicator *instance, size_t channel) /* Предоставляет тот же перезапуск по событию с использованием часов аппаратного порта. */
|
||
{ /* Сохраняет поведение варианта At для приложения без собственного передаваемого времени. */
|
||
return LedIndicator_RestartMode(instance, channel, LED_INDICATOR_MODE_ACTIVITY); /* Часы порта считываются штатным API один раз на зарегистрированный приём. */
|
||
} /* Завершает неблокирующий запуск цикла приёма с аппаратным источником времени. */
|
||
|
||
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;
|
||
}
|