Перейти к содержимому

gpio_button — кнопка по прерыванию и светодиод

Короткое нажатие кнопки BOOT/USER переключает светодиод D2 (PG3). Кнопка обрабатывается по прерыванию с подавлением дребезга контактов.

Окно терминала
cmake --preset Release -DH723_RECIPES="gpio_button"
gpio_button: started, press BOOT to toggle LED
gpio_button: press 1 (196 ms), LED on
gpio_button: press 2 (202 ms), LED off
gpio_button: press 3 (181 ms), LED on
gpio_button: press 4 (152 ms), LED off

Подавление дребезга: если прерывание пришло, а уровень на выводе не изменился, в лог попадает gpio_button: bounce, ignored, и светодиод не переключается.

  • Схема. Кнопка замыкает PC13 на 3,3 В, резистор 10 кОм на плате тянет вывод к земле: нажата — 1, отпущена — 0. Та же кнопка подаёт 1 на BOOT0, поэтому если держать её во время сброса, стартует системный загрузчик (DFU), а не прошивка.
  • Долгое нажатие занято. Прошивка (app/app.c) сбрасывает плату, если кнопку держать ~1 с. Рецепт считает только нажатия короче SHORT_PRESS_MS (800 мс).
  • Прерывание по обоим фронтам. PC13 переводится в режим GPIO_MODE_IT_RISING_FALLING — линия EXTI13. Её обработчик EXTI15_10_IRQHandler и HAL_GPIO_EXTI_Callback живут в app/exti.c и отдают прерывание приложению через appGpioExti(pin); что приложение не забрало, уходит в Lua (pio.attachInterrupt), если прошивка собрана с ним. Рецепт переопределяет appGpioExti и забирает PC13 себе.
  • Дребезг. Контакты кнопки при нажатии несколько миллисекунд замыкаются и размыкаются, давая пачку прерываний. Прерывание только будит задачу (vTaskNotifyGiveFromISR): ждать и печатать в нём нельзя, пока оно идёт, стоит вся система. Задача ждёт 20 мс, сбрасывает накопившиеся уведомления и читает вывод. Если уровень не изменился — это был дребезг.
  • Приоритет прерывания 6 — из него можно звать ...FromISR (см. правила в общем README).
  • Светодиод. Обычно им мигает idle-задача (appLedHeartbeat в app/app.c); рецепт выключает мигание и управляет PG3 сам. Состояние хранится в переменной, а не читается с вывода: HAL_GPIO_ReadPin сразу после записи в выход может вернуть старое значение.
  • Внешняя кнопка: любой свободный вывод (docs/board.md), кнопка на GND и подтяжка GPIO_PULLUP — тогда нажата = 0. Меняются BUTTON_PORT, BUTTON_PIN и проверка уровня. Для выводов 0–4 у линий EXTI свои обработчики, для 5–9 — EXTI9_5_IRQHandler, тоже в app/exti.c.
  • appGpioExti переопределяется только в одном модуле, поэтому при нескольких кнопках все выводы обрабатываются в этой одной функции.
  • Двойное нажатие, удержание и т. п. — в buttonTask есть время нажатия (ms), этого достаточно.

Исходники рецепта: recipes/gpio_button