Системы ввода

Обработка кнопок, аналоговых осей и ёмкостного тач-ввода.

7. Button - кнопка с дебаунсом

Программный дебаунс 12 мс. Активный уровень определяется по InputMode: Pullup → кнопка замыкает пин на GND (нажатие = низкий уровень). Если платформа не передана, берётся GetPlatform().

class Button
{
    explicit Button(uint8_t pin, InputMode pull = InputMode::Pullup);
    Button(Platform *platform, uint8_t pin, InputMode pull = InputMode::Pullup);
};
Метод Описание
void begin() Настроить пин, прочитать начальное состояние, сбросить флаги
void update() Опрос + дебаунс. Вызывайте каждый цикл
bool wasPressed() true один раз на каждое нажатие; флаг сбрасывается чтением
bool isDown() const Текущее стабильное состояние (нажата ли кнопка сейчас)
uint8_t pin() const Номер пина
InputMode pullMode() const Режим подтяжки

Пример

Button btn(21, InputMode::Pullup);
btn.begin();

while (true)
{
    btn.update();
    if (btn.wasPressed())
        onButton();          // сработает один раз на физическое нажатие
    plat->delayMs(5);
}

Button + wasPressed() - базовый примитив игровых меню. Для удержаний используйте isDown() и собственную логику автоповтора. wasPressed() срабатывает по нажатию (переход в «нажата»), а не по отпусканию.

8. Joystick - аналоговые оси

Аналоговая ось с нормализацией, мёртвой зоной, экспоненциальным сглаживанием и опциональной кнопкой.

Конфигурация

struct AnalogAxisConfig
{
    uint8_t pin = 0xFF;        // пин АЦП; 0xFF = ось не используется
    int16_t minValue = 0;      // сырые границы АЦП (калибровка «краёв»)
    int16_t maxValue = 4095;
    float   deadZone = 0.12f;  // мёртвая зона, доля от 1.0
    bool    inverted = false;  // инверсия оси
};

struct JoystickConfig
{
    AnalogAxisConfig axisX;
    AnalogAxisConfig axisY;
    float     deadZone   = 0.12f;               // радиальная мёртвая зона стика
    uint8_t   buttonPin  = 0xFF;                // 0xFF = без кнопки
    InputMode buttonPull = InputMode::Pullup;
};

AnalogAxis

Одна ось. Используйте, если нужна одна ось или нестандартная комбинация.

Метод Описание
AnalogAxis(Platform *plat = nullptr, const AnalogAxisConfig &c = {}) Конструктор
void begin() Подготовить ось (для пина 0xFF ничего не делает)
float update(float deltaTime) Прочитать АЦП, обработать, сгладить. Возвращает значение -1.0 … 1.0
float value() const Последнее сглаженное значение

Joystick

Две оси и опциональная кнопка.

Метод Описание
Joystick(Platform *plat = nullptr, const JoystickConfig &cfg = {}) Конструктор
void begin() Запуск осей и кнопки
void update(float deltaTime) Опрос. Вызывайте каждый цикл
float x() const / float y() const Положение, -1.0 … 1.0, уже со сглаживанием
bool isPressed() const Кнопка зажата (без кнопки - всегда false)
bool wasPressed() Было нажатие с прошлого вызова

Пример

JoystickConfig jc;
jc.axisX.pin = 1;
jc.axisY.pin = 2;
jc.buttonPin = 3;

Joystick joy(nullptr, jc);
joy.begin();

uint64_t last = plat->nowUs();
while (true)
{
    const uint64_t now = plat->nowUs();
    const float dt = static_cast<float>(now - last) / 1e6f;   // секунды
    last = now;

    joy.update(dt);
    if (joy.wasPressed())
        pauseGame();
    movePlayer(joy.x(), joy.y());
}

9. Touch - ёмкостный тач

Интерфейс ёмкостного тач-контроллера (семейство FT6x36, I²C), до 2 точек. Включается PIPCORE_ENABLE_TOUCH. Доступ - plat->touch().

struct TouchConfig
{
    int8_t   sda = -1;          // пины I²C
    int8_t   scl = -1;
    int8_t   intr = -1;         // пин прерывания INT - обязателен
                                // configure() отвергает intr < 0

    uint8_t  i2cAddr = 0x38;    // адрес контроллера
    uint32_t freqHz = 400000;   // частота I²C
    uint16_t width = 0;         // разрешение панели - для маппинга координат
    uint16_t height = 0;
    uint8_t  rotation = 0;      // поворот системы координат 0..3
};

struct TouchPoint
{
    uint16_t x = 0;             // в координатах экрана (с учётом поворота)
    uint16_t y = 0;
    uint8_t  id = 0;            // идентификатор касания (для мультитач-трекинга)
    TouchState state = TouchState::Released;

    bool active() const;        // state != Released
};
Метод класса Touch Описание
bool configure(const TouchConfig &cfg) Задать параметры. Вызывается до begin()
bool begin() Запуск: I²C-шина, прерывание
void end() Остановка
void update() Опрос контроллера. Вызывайте в цикле
bool ready() const Контроллер запущен
uint8_t count() const Число активных точек
TouchPoint point(uint8_t index) const Точка 0..MaxPoints-1
bool touched() const Есть ли хотя бы одно активное касание
static constexpr uint8_t MaxPoints 2

Пример

Touch *touch = plat->touch();
if (!touch)
    return;                                // модуль выключен в menuconfig

TouchConfig tcfg = {};
tcfg.sda = 4;
tcfg.scl = 5;
tcfg.intr = 6;
tcfg.width = 320;
tcfg.height = 480;

if (!touch->configure(tcfg) || !touch->begin())
{
    log::error("touch init failed");
    return;
}

// в цикле:
touch->update();
for (uint8_t i = 0; i < touch->count(); ++i)
{
    const TouchPoint p = touch->point(i);
    if (p.state == TouchState::Pressed)
        onPress(p.x, p.y);
}
TouchState Значение
Pressed Палец только что коснулся
Held Палец на месте
Moved Палец сместился
Released Точка не активна