Embassy: Implement Button Task

Hello,
I am trying to implement reading the state of a button using embassy and the embassy_nrf crate. The idea is to distinguish if the button was pressed shortly or long. The idea is to start two actions

  • A non blocking gpio state listen action
  • A non blocking timer timeout

When the timer timed out, it is a long press. Otherwise a short. The problem is that I didn't find an example how to reimplement the interrupts by my self, without disturbing the embassy framework.

use embassy_time::Timer;
use embassy_nrf::{
    bind_interrupts,
    gpio::{self, AnyPin},
    peripherals, interrupt,
    Peripheral,
    timer,
};
use embassy_sync::{
    blocking_mutex::{raw::ThreadModeRawMutex},
    signal::Signal,
};

// Enable interrupt executor
#[interrupt]
unsafe fn GPIOTE() {
    // Disable interrupts

    BUTTON1_WAIT_SIGNAL.signal(true);

    // Clear interrupt flag
}

// TODO: allowed to use TIMER0?
#[interrupt]
unsafe fn TIMER1() {
    // Disable interrupts

    BUTTON1_WAIT_SIGNAL.signal(false);

    // Clear interrupt flag
}

pub enum ButtonState {
    ShortPress,
    LongPress,
}

pub (crate) static BUTTON1_STATE: Signal<ThreadModeRawMutex, ButtonState> = Signal::new();
static BUTTON1_WAIT_SIGNAL: Signal<ThreadModeRawMutex, bool> = Signal::new();

#[embassy_executor::task]
pub async fn button1(pin: AnyPin, pull: gpio::Pull, timer: peripherals::TIMER1) -> ! {
    let mut button = gpio::Input::new(pin, pull);
    button_generic(&mut button, timer, &BUTTON1_STATE, &BUTTON1_WAIT_SIGNAL).await
}

pub async fn button_generic<'d, T: timer::Instance>(button: &mut gpio::Input<'_>, timer: impl Peripheral<P = T> + 'd, signal: & Signal<ThreadModeRawMutex, ButtonState>, s: &Signal<ThreadModeRawMutex, bool>) -> ! {
    loop {
        button.wait_for_falling_edge().await;
        Timer::after_millis(10).await; // Debounce
        if button.is_high() {
            continue;
        }

        // TODO: Start timer
        // TODO: Set GPIOTE to trigger on  rising edge

        if s.wait().await {
            // If signal comes from gpiote, the button was released before the timer timed out
            signal.signal(ButtonState::ShortPress);
        } else {
            // If signal comes from timer a timeout occured which means a long press happened
            signal.signal(ButtonState::LongPress);
            
        }
    }
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.