diff options
| -rw-r--r-- | embassy-nrf/src/gpiote.rs | 136 | ||||
| -rw-r--r-- | embassy-nrf/src/lib.rs | 2 | ||||
| -rw-r--r-- | examples/src/bin/gpiote.rs | 84 |
3 files changed, 222 insertions, 0 deletions
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs new file mode 100644 index 000000000..e6479b5e8 --- /dev/null +++ b/embassy-nrf/src/gpiote.rs | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | use core::cell::Cell; | ||
| 2 | use core::pin::Pin; | ||
| 3 | use core::ptr; | ||
| 4 | use defmt::trace; | ||
| 5 | use embassy::util::Signal; | ||
| 6 | use nrf52840_hal::gpio::{Floating, Input, Pin as GpioPin, Port}; | ||
| 7 | |||
| 8 | use crate::interrupt; | ||
| 9 | use crate::pac::GPIOTE; | ||
| 10 | |||
| 11 | pub struct Gpiote { | ||
| 12 | inner: GPIOTE, | ||
| 13 | free_channels: Cell<u8>, // 0 = used, 1 = free. 8 bits for 8 channelself. | ||
| 14 | signals: [Signal<()>; 8], | ||
| 15 | } | ||
| 16 | |||
| 17 | static mut INSTANCE: *const Gpiote = ptr::null_mut(); | ||
| 18 | |||
| 19 | pub enum EventPolarity { | ||
| 20 | None, | ||
| 21 | HiToLo, | ||
| 22 | LoToHi, | ||
| 23 | Toggle, | ||
| 24 | } | ||
| 25 | |||
| 26 | #[derive(defmt::Format)] | ||
| 27 | pub enum NewChannelError { | ||
| 28 | NoFreeChannels, | ||
| 29 | } | ||
| 30 | |||
| 31 | impl Gpiote { | ||
| 32 | pub fn new(gpiote: GPIOTE) -> Self { | ||
| 33 | let signal: Signal<()> = Signal::new(); | ||
| 34 | |||
| 35 | interrupt::unpend(interrupt::GPIOTE); | ||
| 36 | interrupt::enable(interrupt::GPIOTE); | ||
| 37 | |||
| 38 | Self { | ||
| 39 | inner: gpiote, | ||
| 40 | free_channels: Cell::new(0xFF), // all 8 channels free | ||
| 41 | signals: [ | ||
| 42 | Signal::new(), | ||
| 43 | Signal::new(), | ||
| 44 | Signal::new(), | ||
| 45 | Signal::new(), | ||
| 46 | Signal::new(), | ||
| 47 | Signal::new(), | ||
| 48 | Signal::new(), | ||
| 49 | Signal::new(), | ||
| 50 | ], | ||
| 51 | } | ||
| 52 | } | ||
| 53 | |||
| 54 | pub fn new_input_channel<T>( | ||
| 55 | &self, | ||
| 56 | pin: GpioPin<Input<T>>, | ||
| 57 | trigger_mode: EventPolarity, | ||
| 58 | ) -> Result<Channel<'_>, NewChannelError> { | ||
| 59 | interrupt::free(|_| { | ||
| 60 | unsafe { INSTANCE = self }; | ||
| 61 | |||
| 62 | let chs = self.free_channels.get(); | ||
| 63 | let index = chs.trailing_zeros() as usize; | ||
| 64 | if index == 8 { | ||
| 65 | return Err(NewChannelError::NoFreeChannels); | ||
| 66 | } | ||
| 67 | self.free_channels.set(chs & !(1 << index)); | ||
| 68 | |||
| 69 | trace!("allocated ch {:u8}", index as u8); | ||
| 70 | |||
| 71 | self.inner.config[index].write(|w| { | ||
| 72 | match trigger_mode { | ||
| 73 | EventPolarity::HiToLo => w.mode().event().polarity().hi_to_lo(), | ||
| 74 | EventPolarity::LoToHi => w.mode().event().polarity().lo_to_hi(), | ||
| 75 | EventPolarity::None => w.mode().event().polarity().none(), | ||
| 76 | EventPolarity::Toggle => w.mode().event().polarity().toggle(), | ||
| 77 | }; | ||
| 78 | w.port().bit(match pin.port() { | ||
| 79 | Port::Port0 => false, | ||
| 80 | Port::Port1 => true, | ||
| 81 | }); | ||
| 82 | unsafe { w.psel().bits(pin.pin()) } | ||
| 83 | }); | ||
| 84 | |||
| 85 | // Enable interrupt | ||
| 86 | self.inner.intenset.write(|w| unsafe { w.bits(1 << index) }); | ||
| 87 | |||
| 88 | Ok(Channel { | ||
| 89 | gpiote: self, | ||
| 90 | index: index as u8, | ||
| 91 | }) | ||
| 92 | }) | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | pub struct Channel<'a> { | ||
| 97 | gpiote: &'a Gpiote, | ||
| 98 | index: u8, | ||
| 99 | } | ||
| 100 | |||
| 101 | impl<'a> Drop for Channel<'a> { | ||
| 102 | fn drop(&mut self) { | ||
| 103 | let g = unsafe { Pin::new_unchecked(self.gpiote) }; | ||
| 104 | |||
| 105 | interrupt::free(|_| { | ||
| 106 | self.gpiote.inner.config[self.index as usize].write(|w| w.mode().disabled()); | ||
| 107 | self.gpiote | ||
| 108 | .inner | ||
| 109 | .intenclr | ||
| 110 | .write(|w| unsafe { w.bits(1 << self.index) }); | ||
| 111 | |||
| 112 | self.gpiote | ||
| 113 | .free_channels | ||
| 114 | .set(self.gpiote.free_channels.get() | 1 << self.index); | ||
| 115 | trace!("freed ch {:u8}", self.index); | ||
| 116 | }) | ||
| 117 | } | ||
| 118 | } | ||
| 119 | |||
| 120 | impl<'a> Channel<'a> { | ||
| 121 | pub async fn wait(&self) -> () { | ||
| 122 | self.gpiote.signals[self.index as usize].wait().await; | ||
| 123 | } | ||
| 124 | } | ||
| 125 | |||
| 126 | #[interrupt] | ||
| 127 | unsafe fn GPIOTE() { | ||
| 128 | let s = &(*INSTANCE); | ||
| 129 | |||
| 130 | for i in 0..8 { | ||
| 131 | if s.inner.events_in[i].read().bits() != 0 { | ||
| 132 | s.inner.events_in[i].write(|w| w); | ||
| 133 | s.signals[i].signal(()); | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
diff --git a/embassy-nrf/src/lib.rs b/embassy-nrf/src/lib.rs index f1ce0cbf9..edc2778b8 100644 --- a/embassy-nrf/src/lib.rs +++ b/embassy-nrf/src/lib.rs | |||
| @@ -37,7 +37,9 @@ pub use nrf52833_pac as pac; | |||
| 37 | #[cfg(feature = "nrf52840")] | 37 | #[cfg(feature = "nrf52840")] |
| 38 | pub use nrf52840_pac as pac; | 38 | pub use nrf52840_pac as pac; |
| 39 | 39 | ||
| 40 | pub mod gpiote; | ||
| 40 | pub mod interrupt; | 41 | pub mod interrupt; |
| 41 | pub mod qspi; | 42 | pub mod qspi; |
| 42 | pub mod uarte; | 43 | pub mod uarte; |
| 44 | |||
| 43 | pub use cortex_m_rt::interrupt; | 45 | 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 000000000..f55b0386d --- /dev/null +++ b/examples/src/bin/gpiote.rs | |||
| @@ -0,0 +1,84 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | #[path = "../example_common.rs"] | ||
| 6 | mod example_common; | ||
| 7 | use example_common::*; | ||
| 8 | |||
| 9 | use core::pin::Pin; | ||
| 10 | use cortex_m_rt::entry; | ||
| 11 | use embassy::io::{AsyncBufRead, AsyncBufReadExt, AsyncWrite, AsyncWriteExt}; | ||
| 12 | use embassy_nrf::gpiote; | ||
| 13 | use futures::pin_mut; | ||
| 14 | use nrf52840_hal::gpio; | ||
| 15 | |||
| 16 | #[static_executor::task] | ||
| 17 | async fn run() { | ||
| 18 | let p = embassy_nrf::pac::Peripherals::take().dewrap(); | ||
| 19 | let port0 = gpio::p0::Parts::new(p.P0); | ||
| 20 | |||
| 21 | let g = gpiote::Gpiote::new(p.GPIOTE); | ||
| 22 | |||
| 23 | info!("Starting!"); | ||
| 24 | |||
| 25 | let pin1 = port0.p0_11.into_pullup_input().degrade(); | ||
| 26 | let button1 = async { | ||
| 27 | let ch = g | ||
| 28 | .new_input_channel(pin1, gpiote::EventPolarity::HiToLo) | ||
| 29 | .dewrap(); | ||
| 30 | |||
| 31 | loop { | ||
| 32 | ch.wait().await; | ||
| 33 | info!("Button 1 pressed") | ||
| 34 | } | ||
| 35 | }; | ||
| 36 | |||
| 37 | let pin2 = port0.p0_12.into_pullup_input().degrade(); | ||
| 38 | let button2 = async { | ||
| 39 | let ch = g | ||
| 40 | .new_input_channel(pin2, gpiote::EventPolarity::LoToHi) | ||
| 41 | .dewrap(); | ||
| 42 | |||
| 43 | loop { | ||
| 44 | ch.wait().await; | ||
| 45 | info!("Button 2 released") | ||
| 46 | } | ||
| 47 | }; | ||
| 48 | |||
| 49 | let pin3 = port0.p0_24.into_pullup_input().degrade(); | ||
| 50 | let button3 = async { | ||
| 51 | let ch = g | ||
| 52 | .new_input_channel(pin3, gpiote::EventPolarity::Toggle) | ||
| 53 | .dewrap(); | ||
| 54 | |||
| 55 | loop { | ||
| 56 | ch.wait().await; | ||
| 57 | info!("Button 3 toggled") | ||
| 58 | } | ||
| 59 | }; | ||
| 60 | |||
| 61 | let pin4 = port0.p0_25.into_pullup_input().degrade(); | ||
| 62 | let button4 = async { | ||
| 63 | let ch = g | ||
| 64 | .new_input_channel(pin4, gpiote::EventPolarity::Toggle) | ||
| 65 | .dewrap(); | ||
| 66 | |||
| 67 | loop { | ||
| 68 | ch.wait().await; | ||
| 69 | info!("Button 4 toggled") | ||
| 70 | } | ||
| 71 | }; | ||
| 72 | |||
| 73 | futures::join!(button1, button2, button3, button4); | ||
| 74 | } | ||
| 75 | |||
| 76 | #[entry] | ||
| 77 | fn main() -> ! { | ||
| 78 | info!("Hello World!"); | ||
| 79 | |||
| 80 | unsafe { | ||
| 81 | run.spawn().dewrap(); | ||
| 82 | static_executor::run(); | ||
| 83 | } | ||
| 84 | } | ||
