Сохранить текущие изменения NAND и LED перед объединением веток
This commit is contained in:
126
c/led-indicator/README.md
Normal file
126
c/led-indicator/README.md
Normal 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
|
||||
```
|
||||
Reference in New Issue
Block a user