Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions embassy-nrf/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- bugfix: Do not write to UICR from non-secure code on nrf53
- bugfix: Add delay to uart init anomaly fix
- changed: `BufferedUarte::read_ready` now uses the same definition for 'empty' so following read calls will not block when true is returned
- added: add `gpiote::InputChannel::wait_for_high()` and `wait_for_low()` to wait for specific signal level
- changed: `gpiote::InputChannel::wait()` now takes a mutable reference to `self` to avoid interference from concurrent calls
- changed: `gpiote::InputChannel::wait()` now ensures events are seen as soon as the function is called, even if the future is not polled

## 0.8.0 - 2025-09-30

Expand Down
68 changes: 62 additions & 6 deletions embassy-nrf/src/gpiote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,16 +349,73 @@ impl<'d> InputChannel<'d> {
}

/// Asynchronously wait for an event in this channel.
pub async fn wait(&self) {
let g = self.ch.regs();
let num = self.ch.number();
let waker = self.ch.waker();
///
/// It is possible to call this function and await the returned future later.
/// If an even occurs in the mean time, the future will immediately report ready.
pub fn wait(&mut self) -> impl Future<Output = ()> {
// NOTE: This is `-> impl Future` and not an `async fn` on purpose.
// Otherwise, events will only be detected starting at the first poll of the returned future.
Self::wait_internal(&mut self.ch)
}

/// Asynchronously wait for the pin to become high.
///
/// The channel must be configured with [`InputChannelPolarity::LoToHi`] or [`InputChannelPolarity::Toggle`].
/// If the channel is not configured to detect rising edges, it is unspecified when the returned future completes.
///
/// It is possible to call this function and await the returned future later.
/// If an even occurs in the mean time, the future will immediately report ready.
pub fn wait_for_high(&mut self) -> impl Future<Output = ()> {
// NOTE: This is `-> impl Future` and not an `async fn` on purpose.
// Otherwise, events will only be detected starting at the first poll of the returned future.

// Subscribe to the event before checking the pin level.
let wait = Self::wait_internal(&mut self.ch);
let pin = &self.pin;
async move {
if pin.is_high() {
return;
}
wait.await;
}
}

/// Asynchronously wait for the pin to become low.
///
/// The channel must be configured with [`InputChannelPolarity::HiToLo`] or [`InputChannelPolarity::Toggle`].
/// If the channel is not configured to detect falling edges, it is unspecified when the returned future completes.
///
/// It is possible to call this function and await the returned future later.
/// If an even occurs in the mean time, the future will immediately report ready.
pub fn wait_for_low(&mut self) -> impl Future<Output = ()> {
// NOTE: This is `-> impl Future` and not an `async fn` on purpose.
// Otherwise, events will only be detected starting at the first poll of the returned future.

// Subscribe to the event before checking the pin level.
let wait = Self::wait_internal(&mut self.ch);
let pin = &self.pin;
async move {
if pin.is_low() {
return;
}
wait.await;
}
}

/// Internal implementation for `wait()` and friends.
fn wait_internal(channel: &mut Peri<'_, AnyChannel>) -> impl Future<Output = ()> {
// NOTE: This is `-> impl Future` and not an `async fn` on purpose.
// Otherwise, events will only be detected starting at the first poll of the returned future.

let g = channel.regs();
let num = channel.number();
let waker = channel.waker();

// Enable interrupt
g.events_in(num).write_value(0);
g.intenset(INTNUM).write(|w| w.0 = 1 << num);

poll_fn(|cx| {
poll_fn(move |cx| {
CHANNEL_WAKERS[waker].register(cx.waker());

if g.events_in(num).read() != 0 {
Expand All @@ -367,7 +424,6 @@ impl<'d> InputChannel<'d> {
Poll::Pending
}
})
.await;
}

/// Get the associated input pin.
Expand Down
8 changes: 4 additions & 4 deletions examples/nrf52840/src/bin/gpiote_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default());
info!("Starting!");

let ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo);
let ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::LoToHi);
let ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_24, Pull::Up, InputChannelPolarity::Toggle);
let ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_25, Pull::Up, InputChannelPolarity::Toggle);
let mut ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_11, Pull::Up, InputChannelPolarity::HiToLo);
let mut ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_12, Pull::Up, InputChannelPolarity::LoToHi);
let mut ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_24, Pull::Up, InputChannelPolarity::Toggle);
let mut ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_25, Pull::Up, InputChannelPolarity::Toggle);

let button1 = async {
loop {
Expand Down
8 changes: 4 additions & 4 deletions examples/nrf5340/src/bin/gpiote_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default());
info!("Starting!");

let ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_23, Pull::Up, InputChannelPolarity::HiToLo);
let ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_24, Pull::Up, InputChannelPolarity::LoToHi);
let ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_08, Pull::Up, InputChannelPolarity::Toggle);
let ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_09, Pull::Up, InputChannelPolarity::Toggle);
let mut ch1 = InputChannel::new(p.GPIOTE_CH0, p.P0_23, Pull::Up, InputChannelPolarity::HiToLo);
let mut ch2 = InputChannel::new(p.GPIOTE_CH1, p.P0_24, Pull::Up, InputChannelPolarity::LoToHi);
let mut ch3 = InputChannel::new(p.GPIOTE_CH2, p.P0_08, Pull::Up, InputChannelPolarity::Toggle);
let mut ch4 = InputChannel::new(p.GPIOTE_CH3, p.P0_09, Pull::Up, InputChannelPolarity::Toggle);

let button1 = async {
loop {
Expand Down
8 changes: 4 additions & 4 deletions examples/nrf54l15/src/bin/gpiote_channel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ async fn main(_spawner: Spawner) {
let p = embassy_nrf::init(Default::default());
info!("Starting!");

let ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_13, Pull::Up, InputChannelPolarity::HiToLo);
let ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi);
let ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle);
let ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_04, Pull::Up, InputChannelPolarity::Toggle);
let mut ch1 = InputChannel::new(p.GPIOTE20_CH0, p.P1_13, Pull::Up, InputChannelPolarity::HiToLo);
let mut ch2 = InputChannel::new(p.GPIOTE20_CH1, p.P1_09, Pull::Up, InputChannelPolarity::LoToHi);
let mut ch3 = InputChannel::new(p.GPIOTE20_CH2, p.P1_08, Pull::Up, InputChannelPolarity::Toggle);
let mut ch4 = InputChannel::new(p.GPIOTE30_CH0, p.P0_04, Pull::Up, InputChannelPolarity::Toggle);

let button1 = async {
loop {
Expand Down