7.4 KiB
LED Indicator
Неблокирующая C99-библиотека для индикации состояний STARTUP, WORK,
ACTIVITY, WARNING, ERROR и CRITICAL с разными рисунками и частотами.
Один экземпляр обслуживает несколько светодиодов.
Ядро не знает о GPIO, PWM, сдвиговом регистре, RTOS и модели таймера. Порт передаёт логическое состояние канала наружу и читает монотонное время в миллисекундах. В библиотеке нет задержек, динамической памяти, прерываний и изменяемого глобального состояния.
приложение -> LedIndicator_SetMode/Process -> ядро -> write/now_ms -> GPIO/PWM/expander + TIM
Файлы
| Файл | Назначение | Зависимости |
|---|---|---|
led_indicator.h/.c |
режимы, шаблоны и планировщик | C99, stdint.h |
ports/stm32-hal |
GPIO и выбранный аппаратный TIM | STM32 HAL |
tests/test_led_indicator.c |
host-тесты, включая переполнение uint32_t |
libc |
Встроенные режимы
| Режим | Сигнал |
|---|---|
OFF / ON |
постоянно выключен / включён |
STARTUP |
три коротких импульса, затем постоянно включён |
WORK |
100 мс включён, 900 мс выключен |
ACTIVITY |
один цикл: 100 мс включён, 900 мс выключен, затем остаётся выключенным |
WARNING |
250/250 мс |
ERROR |
два импульса и пауза |
CRITICAL |
три импульса и пауза |
Частоты не зашиты в алгоритм. Скопируйте встроенную таблицу и измените
duration_ms до инициализации:
static LedIndicator_Pattern patterns[LED_INDICATOR_MODE_COUNT];
LedIndicator_CopyDefaultPatterns(patterns, LED_INDICATOR_MODE_COUNT);
patterns[LED_INDICATOR_MODE_WORK].duration_ms[0] = 50U;
patterns[LED_INDICATOR_MODE_WORK].duration_ms[1] = 1950U;
config.patterns = patterns;
config.pattern_count = LED_INDICATOR_MODE_COUNT;
Таблица должна существовать всё время работы экземпляра. До восьми шагов
описываются длительностями и битовой маской levels; шаблон может повторяться
или после одного прохода перейти в final_level.
Контракт порта
void write(void *context, uint8_t channel, uint8_t logical_on);
uint32_t now_ms(void *context);
write получает именно логический уровень. Инверсию active-low, управление
PWM или запись общего регистра расширителя выполняет порт. Переполнение
32-битной миллисекундной метки обработано разностью беззнаковых чисел.
Если время уже есть в планировщике приложения, now_ms можно оставить NULL
и вызывать варианты LedIndicator_SetModeAt/LedIndicator_ProcessAt.
Быстрый старт без привязки к HAL
static LedIndicator led;
static LedIndicator_Channel state[2];
LedIndicator_Config cfg;
LedIndicator_Port port = {Board_LedWrite, Board_TimerMs, &board};
LedIndicator_ConfigDefault(&cfg);
LedIndicator_Init(&led, state, 2U, &port, &cfg);
LedIndicator_SetMode(&led, 0U, LED_INDICATOR_MODE_WORK);
LedIndicator_SetMode(&led, 1U, LED_INDICATOR_MODE_ERROR);
for (;;) {
LedIndicator_Process(&led);
}
SetMode идемпотентен: повторный вызов того же режима в каждом проходе цикла
не начинает рисунок заново. Для нового импульса события служит
LedIndicator_RestartMode.
Для индикации приёма вызывайте LedIndicator_TriggerActivity либо
LedIndicator_TriggerActivityAt на каждый принятый пакет. Свечение занимает
10% секундного цикла: 100 мс включён и 900 мс выключен. Новый приём немедленно
перезапускает фазу свечения, даже внутри предыдущего импульса или паузы;
очереди циклов нет. При интервалах между пакетами меньше 100 мс LED может
гореть непрерывно до истечения 100 мс после последнего пакета. Без нового
события ACTIVITY не повторяется. Настраиваемая таблица patterns по-прежнему
позволяет изменить длительности и повторение для других применений.
LedIndicator_TriggerActivityAt(&led, 0U, received_ms); /* Каждый приём запускает цикл канала 0 от миллисекундной метки события. */
LedIndicator_ProcessAt(&led, now_ms); /* Главный цикл обслуживает окончание свечения без задержек и ожидания следующего пакета. */
STM32F103, STM32F4, STM32G431 и STM32G474
Порт ports/stm32-hal принимает конкретный TIM_HandleTypeDef *; библиотека
не использует HAL_GetTick() и не занимает SysTick. Настройте TIM как
free-running, запустите его и передайте частоту счётчика после prescaler.
Скопируйте подходящий led_indicator_stm32_hal_config.*.template.h в каталог
платы под именем led_indicator_stm32_hal_config.h.
static const LedIndicator_Stm32HalOutput outputs[] = {
{STATUS_GPIO_Port, STATUS_Pin, 0U},
{ERROR_GPIO_Port, ERROR_Pin, 1U}
};
static LedIndicator_Stm32HalPort hw;
static LedIndicator_Port port;
HAL_TIM_Base_Start(&htim6); /* CNT = 1 кГц в данном примере. */
LedIndicator_Stm32HalPortInit(&hw, &htim6, 1000U,
outputs, 2U, &port);
LedIndicator_Process() должен вызываться чаще, чем переполняется выбранный
TIM. Для 16-битного CNT на 1 кГц это не реже одного раза за 65 секунд. Сам
таймер и его prescaler/period задаются в CubeMX или board-порте, а не в ядре.
Для К1921ВК028 и C28x используется тот же основной порт: now_ms возвращает
счётчик, увеличиваемый обработчиком выбранного TIMER/CPU Timer, а write
обращается к GPIO SDK. Такое разделение оставляет номер таймера и выводы в
проекте конкретной платы.
Проверка
cmake -S c/led-indicator -B build/led-indicator
cmake --build build/led-indicator
ctest --test-dir build/led-indicator --output-on-failure