From 33dce24e8a87070417c0905f6639e9952f26c602 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 29 Sep 2020 19:18:52 +0200 Subject: [PATCH] Add gpiote output channel. --- embassy-nrf/src/gpiote.rs | 160 ++++++++++++++++++++++++++++++-------- 1 file changed, 128 insertions(+), 32 deletions(-) diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs index b9a1f6132..5195f926a 100644 --- a/embassy-nrf/src/gpiote.rs +++ b/embassy-nrf/src/gpiote.rs @@ -1,13 +1,17 @@ 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 nrf52840_hal::gpio::{Input, Level, Output, Pin, Port}; use crate::interrupt; +use crate::pac::generic::Reg; +use crate::pac::gpiote::_TASKS_OUT; use crate::pac::GPIOTE; +#[cfg(not(feature = "51"))] +use crate::pac::gpiote::{_TASKS_CLR, _TASKS_SET}; + pub struct Gpiote { inner: GPIOTE, free_channels: Cell, // 0 = used, 1 = free. 8 bits for 8 channelself. @@ -23,6 +27,13 @@ pub enum EventPolarity { Toggle, } +/// Polarity of the `task out` operation. +pub enum TaskOutPolarity { + Set, + Clear, + Toggle, +} + #[derive(defmt::Format)] pub enum NewChannelError { NoFreeChannels, @@ -30,8 +41,6 @@ pub enum NewChannelError { impl Gpiote { pub fn new(gpiote: GPIOTE) -> Self { - let signal: Signal<()> = Signal::new(); - interrupt::unpend(interrupt::GPIOTE); interrupt::enable(interrupt::GPIOTE); @@ -51,24 +60,40 @@ impl Gpiote { } } - pub fn new_input_channel<'a, T>( - &'a self, - pin: &'a GpioPin>, - trigger_mode: EventPolarity, - ) -> Result, NewChannelError> { + fn allocate_channel(&self) -> Result { 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)); + Ok(index as u8) + }) + } - trace!("allocated ch {:u8}", index as u8); + fn free_channel(&self, index: u8) { + interrupt::free(|_| { + self.inner.config[index as usize].write(|w| w.mode().disabled()); + self.inner.intenclr.write(|w| unsafe { w.bits(1 << index) }); - self.inner.config[index].write(|w| { + self.free_channels + .set(self.free_channels.get() | 1 << index); + trace!("freed ch {:u8}", index); + }) + } + + pub fn new_input_channel<'a, T>( + &'a self, + pin: &'a Pin>, + trigger_mode: EventPolarity, + ) -> Result, NewChannelError> { + interrupt::free(|_| { + unsafe { INSTANCE = self }; + let index = self.allocate_channel()?; + trace!("allocated in ch {:u8}", index as u8); + + self.inner.config[index as usize].write(|w| { match trigger_mode { EventPolarity::HiToLo => w.mode().event().polarity().hi_to_lo(), EventPolarity::LoToHi => w.mode().event().polarity().lo_to_hi(), @@ -85,44 +110,115 @@ impl Gpiote { // Enable interrupt self.inner.intenset.write(|w| unsafe { w.bits(1 << index) }); - Ok(Channel { + Ok(InputChannel { gpiote: self, - index: index as u8, + index, + }) + }) + } + + pub fn new_output_channel<'a, T>( + &'a self, + pin: Pin>, + level: Level, + task_out_polarity: TaskOutPolarity, + ) -> Result, NewChannelError> { + interrupt::free(|_| { + unsafe { INSTANCE = self }; + let index = self.allocate_channel()?; + trace!("allocated out ch {:u8}", index); + + self.inner.config[index as usize].write(|w| { + w.mode().task(); + match level { + Level::High => w.outinit().high(), + Level::Low => w.outinit().low(), + }; + match task_out_polarity { + TaskOutPolarity::Set => w.polarity().lo_to_hi(), + TaskOutPolarity::Clear => w.polarity().hi_to_lo(), + TaskOutPolarity::Toggle => w.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(OutputChannel { + gpiote: self, + index, }) }) } } -pub struct Channel<'a> { +pub struct InputChannel<'a> { gpiote: &'a Gpiote, index: u8, } -impl<'a> Drop for Channel<'a> { +impl<'a> Drop for InputChannel<'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); - }) + self.gpiote.free_channel(self.index); } } -impl<'a> Channel<'a> { +impl<'a> InputChannel<'a> { pub async fn wait(&self) -> () { self.gpiote.signals[self.index as usize].wait().await; } } +pub struct OutputChannel<'a> { + gpiote: &'a Gpiote, + index: u8, +} + +impl<'a> Drop for OutputChannel<'a> { + fn drop(&mut self) { + self.gpiote.free_channel(self.index); + } +} + +impl<'a> OutputChannel<'a> { + /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). + pub fn out(&self) { + self.gpiote.inner.tasks_out[self.index as usize].write(|w| unsafe { w.bits(1) }); + } + /// Triggers `task set` (set associated pin high). + #[cfg(not(feature = "51"))] + pub fn set(&self) { + self.gpiote.inner.tasks_set[self.index as usize].write(|w| unsafe { w.bits(1) }); + } + /// Triggers `task clear` (set associated pin low). + #[cfg(not(feature = "51"))] + pub fn clear(&self) { + self.gpiote.inner.tasks_clr[self.index as usize].write(|w| unsafe { w.bits(1) }); + } + + /// Returns reference to task_out endpoint for PPI. + pub fn task_out(&self) -> &Reg { + &self.gpiote.inner.tasks_out[self.index as usize] + } + + /// Returns reference to task_clr endpoint for PPI. + #[cfg(not(feature = "51"))] + pub fn task_clr(&self) -> &Reg { + &self.gpiote.inner.tasks_clr[self.index as usize] + } + + /// Returns reference to task_set endpoint for PPI. + #[cfg(not(feature = "51"))] + pub fn task_set(&self) -> &Reg { + &self.gpiote.inner.tasks_set[self.index as usize] + } +} + #[interrupt] unsafe fn GPIOTE() { let s = &(*INSTANCE);