# LED Indicator Неблокирующая C99-библиотека для индикации состояний `STARTUP`, `WORK`, `ACTIVITY`, `WARNING`, `ERROR` и `CRITICAL` с разными рисунками и частотами. Один экземпляр обслуживает несколько светодиодов. Ядро не знает о GPIO, PWM, сдвиговом регистре, RTOS и модели таймера. Порт передаёт логическое состояние канала наружу и читает монотонное время в миллисекундах. В библиотеке нет задержек, динамической памяти, прерываний и изменяемого глобального состояния. ```text приложение -> 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` | 50/50 мс | | `WARNING` | 250/250 мс | | `ERROR` | два импульса и пауза | | `CRITICAL` | три импульса и пауза | Частоты не зашиты в алгоритм. Скопируйте встроенную таблицу и измените `duration_ms` до инициализации: ```c 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`. ## Контракт порта ```c 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 ```c 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`. ## 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`. ```c 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. Такое разделение оставляет номер таймера и выводы в проекте конкретной платы. ## Проверка ```sh cmake -S c/led-indicator -B build/led-indicator cmake --build build/led-indicator ctest --test-dir build/led-indicator --output-on-failure ```