127 lines
5.9 KiB
Markdown
127 lines
5.9 KiB
Markdown
# 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
|
||
```
|