diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs new file mode 100644 index 00000000..e6479b5e --- /dev/null +++ b/embassy-nrf/src/gpiote.rs @@ -0,0 +1,136 @@ +use core::cell::Cell; +use core::pin::Pin; +use core::ptr; +use defmt::trace; +use embassy::util::Signal; +use nrf52840_hal::gpio::{Floating, Input, Pin as GpioPin, Port}; + +use crate::interrupt; +use crate::pac::GPIOTE; + +pub struct Gpiote { + inner: GPIOTE, + free_channels: Cell, // 0 = used, 1 = free. 8 bits for 8 channelself. + signals: [Signal<()>; 8], +} + +static mut INSTANCE: *const Gpiote = ptr::null_mut(); + +pub enum EventPolarity { + None, + HiToLo, + LoToHi, + Toggle, +} + +#[derive(defmt::Format)] +pub enum NewChannelError { + NoFreeChannels, +} + +impl Gpiote { + pub fn new(gpiote: GPIOTE) -> Self { + let signal: Signal<()> = Signal::new(); + + interrupt::unpend(interrupt::GPIOTE); + interrupt::enable(interrupt::GPIOTE); + + Self { + inner: gpiote, + free_channels: Cell::new(0xFF), // all 8 channels free + signals: [ + Signal::new(), + Signal::new(), + Signal::new(), + Signal::new(), + Signal::new(), + Signal::new(), + Signal::new(), + Signal::new(), + ], + } + } + + pub fn new_input_channel( + &self, + pin: GpioPin>, + trigger_mode: EventPolarity, + ) -> Result, NewChannelError> { + interrupt::free(|_| { + unsafe { INSTANCE = self }; + + let chs = self.free_channels.get(); + let index = chs.trailing_zeros() as usize; + if index == 8 { + return Err(NewChannelError::NoFreeChannels); + } + self.free_channels.set(chs & !(1 << index)); + + trace!("allocated ch {:u8}", index as u8); + + self.inner.config[index].write(|w| { + match trigger_mode { + EventPolarity::HiToLo => w.mode().event().polarity().hi_to_lo(), + EventPolarity::LoToHi => w.mode().event().polarity().lo_to_hi(), + EventPolarity::None => w.mode().event().polarity().none(), + EventPolarity::Toggle => w.mode().event().polarity().toggle(), + }; + w.port().bit(match pin.port() { + Port::Port0 => false, + Port::Port1 => true, + }); + unsafe { w.psel().bits(pin.pin()) } + }); + + // Enable interrupt + self.inner.intenset.write(|w| unsafe { w.bits(1 << index) }); + + Ok(Channel { + gpiote: self, + index: index as u8, + }) + }) + } +} + +pub struct Channel<'a> { + gpiote: &'a Gpiote, + index: u8, +} + +impl<'a> Drop for Channel<'a> { + fn drop(&mut self) { + let g = unsafe { Pin::new_unchecked(self.gpiote) }; + + interrupt::free(|_| { + self.gpiote.inner.config[self.index as usize].write(|w| w.mode().disabled()); + self.gpiote + .inner + .intenclr + .write(|w| unsafe { w.bits(1 << self.index) }); + + self.gpiote + .free_channels + .set(self.gpiote.free_channels.get() | 1 << self.index); + trace!("freed ch {:u8}", self.index); + }) + } +} + +impl<'a> Channel<'a> { + pub async fn wait(&self) -> () { + self.gpiote.signals[self.index as usize].wait().await; + } +} + +#[interrupt] +unsafe fn GPIOTE() { + let s = &(*INSTANCE); + + for i in 0..8 { + if s.inner.events_in[i].read().bits() != 0 { + s.inner.events_in[i].write(|w| w); + s.signals[i].signal(()); + } + } +} diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index f1ce0cbf..edc2778b 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs @@ -37,7 +37,9 @@ pub use nrf52833_pac as pac; #[cfg(feature = "nrf52840")] pub use nrf52840_pac as pac; +pub mod gpiote; pub mod interrupt; pub mod qspi; pub mod uarte; + pub use cortex_m_rt::interrupt; diff --git a/examples/src/bin/gpiote.rs b/examples/src/bin/gpiote.rs new file mode 100644 index 00000000..f55b0386 --- /dev/null +++ b/examples/src/bin/gpiote.rs @@ -0,0 +1,84 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +#[path = "../example_common.rs"] +mod example_common; +use example_common::*; + +use core::pin::Pin; +use cortex_m_rt::entry; +use embassy::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt}; +use embassy_nrf::gpiote; +use futures::pin_mut; +use nrf52840_hal::gpio; + +#[static_executor::task] +async fn run() { + let p = embassy_nrf::pac::Peripherals::take().dewrap(); + let port0 = gpio::p0::Parts::new(p.P0); + + let g = gpiote::Gpiote::new(p.GPIOTE); + + info!("Starting!"); + + let pin1 = port0.p0_11.into_pullup_input().degrade(); + let button1 = async { + let ch = g + .new_input_channel(pin1, gpiote::EventPolarity::HiToLo) + .dewrap(); + + loop { + ch.wait().await; + info!("Button 1 pressed") + } + }; + + let pin2 = port0.p0_12.into_pullup_input().degrade(); + let button2 = async { + let ch = g + .new_input_channel(pin2, gpiote::EventPolarity::LoToHi) + .dewrap(); + + loop { + ch.wait().await; + info!("Button 2 released") + } + }; + + let pin3 = port0.p0_24.into_pullup_input().degrade(); + let button3 = async { + let ch = g + .new_input_channel(pin3, gpiote::EventPolarity::Toggle) + .dewrap(); + + loop { + ch.wait().await; + info!("Button 3 toggled") + } + }; + + let pin4 = port0.p0_25.into_pullup_input().degrade(); + let button4 = async { + let ch = g + .new_input_channel(pin4, gpiote::EventPolarity::Toggle) + .dewrap(); + + loop { + ch.wait().await; + info!("Button 4 toggled") + } + }; + + futures::join!(button1, button2, button3, button4); +} + +#[entry] +fn main() -> ! { + info!("Hello World!"); + + unsafe { + run.spawn().dewrap(); + static_executor::run(); + } +}