Сохранить текущие изменения NAND и LED перед объединением веток

This commit is contained in:
2026-09-15 15:57:06 +03:00
parent 20dbc2c721
commit 698632ebf3
35 changed files with 1841 additions and 31 deletions

126
c/led-indicator/README.md Normal file
View File

@@ -0,0 +1,126 @@
# 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
```