From f6131011244467b9b96715235fe3cbded914e95c Mon Sep 17 00:00:00 2001 From: Andrey Date: Sat, 17 Oct 2026 14:43:12 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=B7=D0=B0=D0=BF?= =?UTF-8?q?=D1=83=D1=81=D0=BA=D0=B0=D1=82=D1=8C=20=D0=B8=D0=BC=D0=BF=D1=83?= =?UTF-8?q?=D0=BB=D1=8C=D1=81=20=D1=81=D0=B2=D0=B5=D1=82=D0=BE=D0=B4=D0=B8?= =?UTF-8?q?=D0=BE=D0=B4=D0=B0=20=D0=BF=D1=80=D0=B8=20=D0=BA=D0=B0=D0=B6?= =?UTF-8?q?=D0=B4=D0=BE=D0=BC=20=D0=BF=D1=80=D0=B8=D1=91=D0=BC=D0=B5=20?= =?UTF-8?q?=D0=BF=D0=B0=D0=BA=D0=B5=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- c/led-indicator/README.md | 16 +++++++++++++++- c/led-indicator/led_indicator.c | 12 +++++++++++- c/led-indicator/led_indicator.h | 6 ++++++ c/led-indicator/tests/test_led_indicator.c | 22 +++++++++++++++++++++- 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/c/led-indicator/README.md b/c/led-indicator/README.md index 7b36a85..7cbcf69 100644 --- a/c/led-indicator/README.md +++ b/c/led-indicator/README.md @@ -28,7 +28,7 @@ | `OFF` / `ON` | постоянно выключен / включён | | `STARTUP` | три коротких импульса, затем постоянно включён | | `WORK` | 100 мс включён, 900 мс выключен | -| `ACTIVITY` | 50/50 мс | +| `ACTIVITY` | один цикл: 100 мс включён, 900 мс выключен, затем остаётся выключенным | | `WARNING` | 250/250 мс | | `ERROR` | два импульса и пауза | | `CRITICAL` | три импульса и пауза | @@ -86,6 +86,20 @@ for (;;) { не начинает рисунок заново. Для нового импульса события служит `LedIndicator_RestartMode`. +Для индикации приёма вызывайте `LedIndicator_TriggerActivity` либо +`LedIndicator_TriggerActivityAt` на каждый принятый пакет. Свечение занимает +10% секундного цикла: 100 мс включён и 900 мс выключен. Новый приём немедленно +перезапускает фазу свечения, даже внутри предыдущего импульса или паузы; +очереди циклов нет. При интервалах между пакетами меньше 100 мс LED может +гореть непрерывно до истечения 100 мс после последнего пакета. Без нового +события `ACTIVITY` не повторяется. Настраиваемая таблица `patterns` по-прежнему +позволяет изменить длительности и повторение для других применений. + +```c +LedIndicator_TriggerActivityAt(&led, 0U, received_ms); /* Каждый приём запускает цикл канала 0 от миллисекундной метки события. */ +LedIndicator_ProcessAt(&led, now_ms); /* Главный цикл обслуживает окончание свечения без задержек и ожидания следующего пакета. */ +``` + ## STM32F103, STM32F4, STM32G431 и STM32G474 Порт `ports/stm32-hal` принимает конкретный `TIM_HandleTypeDef *`; библиотека diff --git a/c/led-indicator/led_indicator.c b/c/led-indicator/led_indicator.c index 13e998d..c3aaa5f 100644 --- a/c/led-indicator/led_indicator.c +++ b/c/led-indicator/led_indicator.c @@ -18,7 +18,7 @@ static const LedIndicator_Pattern g_default_patterns[LED_INDICATOR_MODE_COUNT] = {{0U}, LEVELS_1, 1U, 1U, 1U}, {{100U, 100U, 100U, 100U, 100U, 100U}, LEVELS_101010, 6U, 0U, 1U}, {{100U, 900U}, LEVELS_10, 2U, 1U, 0U}, - {{50U, 50U}, LEVELS_10, 2U, 1U, 0U}, + {{100U, 900U}, LEVELS_10, 2U, 0U, 0U}, /* Один цикл приёма длится 1000 мс: 100 мс свечения составляют 10%, затем 900 мс паузы и выключение. */ {{250U, 250U}, LEVELS_10, 2U, 1U, 0U}, {{100U, 100U, 100U, 700U}, LEVELS_1010, 4U, 1U, 0U}, {{100U, 100U, 100U, 100U, 100U, 700U}, LEVELS_101010, 6U, 1U, 0U} @@ -220,6 +220,16 @@ uint8_t LedIndicator_RestartMode(LedIndicator *instance, size_t channel, instance->port.now_ms(instance->port.context)); } +uint8_t LedIndicator_TriggerActivityAt(LedIndicator *instance, size_t channel, uint32_t now_ms) /* Каждый пакет начинает ACTIVITY заново, даже если предыдущая вспышка ещё продолжается. */ +{ /* Использует общую проверку канала и принудительный сброс фазы в миллисекундной временной базе. */ + return LedIndicator_RestartModeAt(instance, channel, LED_INDICATOR_MODE_ACTIVITY, now_ms); /* Штатный однократный шаблон завершает цикл выключенным уровнем без свободного повторения. */ +} /* Завершает запуск одного цикла приёма без ожидания окончания свечения либо паузы. */ + +uint8_t LedIndicator_TriggerActivity(LedIndicator *instance, size_t channel) /* Предоставляет тот же перезапуск по событию с использованием часов аппаратного порта. */ +{ /* Сохраняет поведение варианта At для приложения без собственного передаваемого времени. */ + return LedIndicator_RestartMode(instance, channel, LED_INDICATOR_MODE_ACTIVITY); /* Часы порта считываются штатным API один раз на зарегистрированный приём. */ +} /* Завершает неблокирующий запуск цикла приёма с аппаратным источником времени. */ + void LedIndicator_ProcessAt(LedIndicator *instance, uint32_t now_ms) { size_t i; diff --git a/c/led-indicator/led_indicator.h b/c/led-indicator/led_indicator.h index a910599..626fe79 100644 --- a/c/led-indicator/led_indicator.h +++ b/c/led-indicator/led_indicator.h @@ -114,6 +114,12 @@ void LedIndicator_Process(LedIndicator *instance); /** Обновляет выходы с явно переданной меткой времени. */ void LedIndicator_ProcessAt(LedIndicator *instance, uint32_t now_ms); +/** Начинает один цикл ACTIVITY на каждый приём; новый вызов немедленно перезапускает свечение, не создавая очередь. */ +uint8_t LedIndicator_TriggerActivityAt(LedIndicator *instance, size_t channel, uint32_t now_ms); /* Стандартный цикл: 100 мс включён, 900 мс выключен; время задаётся в миллисекундах. */ + +/** Использует часы аппаратного порта для такого же перезапуска цикла при каждом приёме. */ +uint8_t LedIndicator_TriggerActivity(LedIndicator *instance, size_t channel); /* Вызывать из контекста Process; передачу событий из ISR обеспечивает приложение. */ + LedIndicator_Mode LedIndicator_GetMode(const LedIndicator *instance, size_t channel); #ifdef __cplusplus diff --git a/c/led-indicator/tests/test_led_indicator.c b/c/led-indicator/tests/test_led_indicator.c index b72cc9d..8db9fc7 100644 --- a/c/led-indicator/tests/test_led_indicator.c +++ b/c/led-indicator/tests/test_led_indicator.c @@ -85,6 +85,26 @@ int main(void) CHECK(LedIndicator_SetMode(&indicator, 3U, LED_INDICATOR_MODE_ON) == 0U); CHECK(LedIndicator_GetMode(&indicator, 0U) == LED_INDICATOR_MODE_WORK); - puts("led indicator tests passed"); + CHECK(patterns[LED_INDICATOR_MODE_ACTIVITY].duration_ms[0] == 100U && patterns[LED_INDICATOR_MODE_ACTIVITY].duration_ms[1] == 900U && patterns[LED_INDICATOR_MODE_ACTIVITY].repeat == 0U); /* Фиксирует согласованные 10% свечения и один цикл на приём в канонической таблице. */ + CHECK(LedIndicator_TriggerActivityAt(&indicator, 0U, 2500U) != 0U && fake.outputs[0] == 1U); /* Приём сразу включает канал независимо от фазы прежнего WORK. */ + LedIndicator_ProcessAt(&indicator, 2599U); CHECK(fake.outputs[0] == 1U); /* До истечения 100 мс светодиод продолжает гореть. */ + LedIndicator_ProcessAt(&indicator, 2600U); CHECK(fake.outputs[0] == 0U); /* Ровно через 100 мс начинается оставшаяся пауза 900 мс. */ + LedIndicator_ProcessAt(&indicator, 3499U); CHECK(fake.outputs[0] == 0U); /* Конец первого периода не порождает дополнительную вспышку. */ + LedIndicator_ProcessAt(&indicator, 3500U); CHECK(fake.outputs[0] == 0U); /* Однократный ACTIVITY завершается выключенным уровнем на границе 1000 мс. */ + LedIndicator_ProcessAt(&indicator, 4500U); CHECK(fake.outputs[0] == 0U); /* Без новых пакетов свободное периодическое мигание отсутствует. */ + CHECK(LedIndicator_TriggerActivityAt(&indicator, 0U, 5000U) != 0U); /* Начинает новый приём после долгого отсутствия обмена. */ + CHECK(LedIndicator_TriggerActivityAt(&indicator, 0U, 5050U) != 0U); /* Второй пакет приходит внутри текущей фазы свечения. */ + LedIndicator_ProcessAt(&indicator, 5100U); CHECK(fake.outputs[0] == 1U); /* Старый срок выключения больше не действует: отсчёт перезапущен новым пакетом. */ + LedIndicator_ProcessAt(&indicator, 5149U); CHECK(fake.outputs[0] == 1U); /* Новый пакет получает собственные полные 100 мс свечения. */ + LedIndicator_ProcessAt(&indicator, 5150U); CHECK(fake.outputs[0] == 0U); /* Новая фаза паузы начинается ровно от последнего приёма. */ + CHECK(LedIndicator_TriggerActivityAt(&indicator, 0U, 5400U) != 0U && fake.outputs[0] == 1U); /* Приём во время паузы сразу включает LED вместо ожидания границы старого периода. */ + LedIndicator_ProcessAt(&indicator, 5500U); CHECK(fake.outputs[0] == 0U); /* Повторный запуск в паузе не создаёт очередь пропущенных циклов. */ + fake.now = 7000U; CHECK(LedIndicator_TriggerActivity(&indicator, 1U) != 0U && fake.outputs[1] == 1U); /* Проверяет вариант событийного API с часами аппаратного порта и другим каналом. */ + fake.now = 7100U; LedIndicator_Process(&indicator); CHECK(fake.outputs[1] == 0U && fake.outputs[0] == 0U); /* Каналы не перезапускают друг другу фазы и не создают фоновую активность. */ + CHECK(LedIndicator_TriggerActivityAt(&indicator, 0U, UINT32_MAX - 49U) != 0U); /* Начинает импульс непосредственно перед переполнением 32-битного времени. */ + LedIndicator_ProcessAt(&indicator, 49U); CHECK(fake.outputs[0] == 1U); /* Через переполнение прошло 99 мс, поэтому свечение ещё активно. */ + LedIndicator_ProcessAt(&indicator, 50U); CHECK(fake.outputs[0] == 0U); /* Через переполнение прошло ровно 100 мс, и LED гаснет без зависания. */ + CHECK(LedIndicator_TriggerActivityAt(NULL, 0U, 0U) == 0U && LedIndicator_TriggerActivityAt(&indicator, 3U, 0U) == 0U); /* Ошибочный экземпляр и канал не вызывают запись чужого GPIO. */ + puts("led indicator tests passed: receive 100/900 ms, immediate retrigger, no repeat, timer wrap"); /* Показывает прохождение новых событийных сценариев вместе с прежней диагностикой. */ return 0; }