kywy/
button_async.rs

1// SPDX-FileCopyrightText: 2025 KOINSLOT Inc.
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5//! src/buttons.rs
6//! Button event system using a shared Channel for press/release detection.
7
8use embassy_executor::Spawner;
9use embassy_rp::Peri;
10use embassy_rp::gpio::{Input, Level, Pull};
11use embassy_rp::peripherals::*;
12use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
13use embassy_sync::channel::Channel;
14
15#[derive(Clone, Copy, Debug)]
16pub enum ButtonId {
17    Left,
18    Right,
19    DUp,
20    DDown,
21    DLeft,
22    DRight,
23    DCenter,
24}
25
26#[derive(Clone, Copy, Debug, Eq, PartialEq)]
27pub enum ButtonState {
28    Pressed,
29    Released,
30}
31
32#[derive(Clone, Copy, Debug)]
33pub struct ButtonEvent {
34    pub id: ButtonId,
35    pub state: ButtonState,
36}
37
38const BUTTON_CHANNEL_CAPACITY: usize = 16;
39
40static BUTTON_CHANNEL: Channel<ThreadModeRawMutex, ButtonEvent, BUTTON_CHANNEL_CAPACITY> =
41    Channel::new();
42
43pub fn init(
44    spawner: &Spawner,
45    pin_left: Peri<'static, PIN_12>,
46    pin_right: Peri<'static, PIN_2>,
47    pin_dup: Peri<'static, PIN_9>,
48    pin_ddown: Peri<'static, PIN_3>,
49    pin_dleft: Peri<'static, PIN_6>,
50    pin_dright: Peri<'static, PIN_7>,
51    pin_dcenter: Peri<'static, PIN_8>,
52) -> &'static Channel<ThreadModeRawMutex, ButtonEvent, BUTTON_CHANNEL_CAPACITY> {
53    spawn_button(spawner, pin_left, ButtonId::Left);
54    spawn_button(spawner, pin_right, ButtonId::Right);
55    spawn_button(spawner, pin_dup, ButtonId::DUp);
56    spawn_button(spawner, pin_ddown, ButtonId::DDown);
57    spawn_button(spawner, pin_dleft, ButtonId::DLeft);
58    spawn_button(spawner, pin_dright, ButtonId::DRight);
59    spawn_button(spawner, pin_dcenter, ButtonId::DCenter);
60
61    &BUTTON_CHANNEL
62}
63
64fn spawn_button<P: embassy_rp::gpio::Pin + 'static>(
65    spawner: &Spawner,
66    pin: Peri<'static, P>,
67    id: ButtonId,
68) {
69    let mut input = Input::new(pin, Pull::Up);
70    input.set_schmitt(true);
71    spawner.spawn(button_task(input, id)).unwrap();
72}
73
74#[embassy_executor::task(pool_size = 7)] // spawns 7 tasks, one for each button
75async fn button_task(mut pin: Input<'static>, id: ButtonId) {
76    loop {
77        pin.wait_for_any_edge().await;
78        let level = pin.get_level();
79
80        let state = if level == Level::Low {
81            ButtonState::Pressed
82        } else {
83            ButtonState::Released
84        };
85
86        let _ = BUTTON_CHANNEL.try_send(ButtonEvent { id, state });
87    }
88}