diff options
| author | Dario Nieuwenhuis <[email protected]> | 2020-09-29 19:18:52 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2020-09-29 19:18:52 +0200 |
| commit | 33dce24e8a87070417c0905f6639e9952f26c602 (patch) | |
| tree | 6319e7cf4e62b85225e62bed1efe80ac60261050 | |
| parent | c81d626254d4c27e7412d192eb1cb6b6bea2648a (diff) | |
Add gpiote output channel.
| -rw-r--r-- | embassy-nrf/src/gpiote.rs | 160 |
1 files 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 @@ | |||
| 1 | use core::cell::Cell; | 1 | use core::cell::Cell; |
| 2 | use core::pin::Pin; | ||
| 3 | use core::ptr; | 2 | use core::ptr; |
| 4 | use defmt::trace; | 3 | use defmt::trace; |
| 5 | use embassy::util::Signal; | 4 | use embassy::util::Signal; |
| 6 | use nrf52840_hal::gpio::{Floating, Input, Pin as GpioPin, Port}; | 5 | use nrf52840_hal::gpio::{Input, Level, Output, Pin, Port}; |
| 7 | 6 | ||
| 8 | use crate::interrupt; | 7 | use crate::interrupt; |
| 8 | use crate::pac::generic::Reg; | ||
| 9 | use crate::pac::gpiote::_TASKS_OUT; | ||
| 9 | use crate::pac::GPIOTE; | 10 | use crate::pac::GPIOTE; |
| 10 | 11 | ||
| 12 | #[cfg(not(feature = "51"))] | ||
| 13 | use crate::pac::gpiote::{_TASKS_CLR, _TASKS_SET}; | ||
| 14 | |||
| 11 | pub struct Gpiote { | 15 | pub struct Gpiote { |
| 12 | inner: GPIOTE, | 16 | inner: GPIOTE, |
| 13 | free_channels: Cell<u8>, // 0 = used, 1 = free. 8 bits for 8 channelself. | 17 | free_channels: Cell<u8>, // 0 = used, 1 = free. 8 bits for 8 channelself. |
| @@ -23,6 +27,13 @@ pub enum EventPolarity { | |||
| 23 | Toggle, | 27 | Toggle, |
| 24 | } | 28 | } |
| 25 | 29 | ||
| 30 | /// Polarity of the `task out` operation. | ||
| 31 | pub enum TaskOutPolarity { | ||
| 32 | Set, | ||
| 33 | Clear, | ||
| 34 | Toggle, | ||
| 35 | } | ||
| 36 | |||
| 26 | #[derive(defmt::Format)] | 37 | #[derive(defmt::Format)] |
| 27 | pub enum NewChannelError { | 38 | pub enum NewChannelError { |
| 28 | NoFreeChannels, | 39 | NoFreeChannels, |
| @@ -30,8 +41,6 @@ pub enum NewChannelError { | |||
| 30 | 41 | ||
| 31 | impl Gpiote { | 42 | impl Gpiote { |
| 32 | pub fn new(gpiote: GPIOTE) -> Self { | 43 | pub fn new(gpiote: GPIOTE) -> Self { |
| 33 | let signal: Signal<()> = Signal::new(); | ||
| 34 | |||
| 35 | interrupt::unpend(interrupt::GPIOTE); | 44 | interrupt::unpend(interrupt::GPIOTE); |
| 36 | interrupt::enable(interrupt::GPIOTE); | 45 | interrupt::enable(interrupt::GPIOTE); |
| 37 | 46 | ||
| @@ -51,24 +60,40 @@ impl Gpiote { | |||
| 51 | } | 60 | } |
| 52 | } | 61 | } |
| 53 | 62 | ||
| 54 | pub fn new_input_channel<'a, T>( | 63 | fn allocate_channel(&self) -> Result<u8, NewChannelError> { |
| 55 | &'a self, | ||
| 56 | pin: &'a GpioPin<Input<T>>, | ||
| 57 | trigger_mode: EventPolarity, | ||
| 58 | ) -> Result<Channel<'a>, NewChannelError> { | ||
| 59 | interrupt::free(|_| { | 64 | interrupt::free(|_| { |
| 60 | unsafe { INSTANCE = self }; | ||
| 61 | |||
| 62 | let chs = self.free_channels.get(); | 65 | let chs = self.free_channels.get(); |
| 63 | let index = chs.trailing_zeros() as usize; | 66 | let index = chs.trailing_zeros() as usize; |
| 64 | if index == 8 { | 67 | if index == 8 { |
| 65 | return Err(NewChannelError::NoFreeChannels); | 68 | return Err(NewChannelError::NoFreeChannels); |
| 66 | } | 69 | } |
| 67 | self.free_channels.set(chs & !(1 << index)); | 70 | self.free_channels.set(chs & !(1 << index)); |
| 71 | Ok(index as u8) | ||
| 72 | }) | ||
| 73 | } | ||
| 74 | |||
| 75 | fn free_channel(&self, index: u8) { | ||
| 76 | interrupt::free(|_| { | ||
| 77 | self.inner.config[index as usize].write(|w| w.mode().disabled()); | ||
| 78 | self.inner.intenclr.write(|w| unsafe { w.bits(1 << index) }); | ||
| 79 | |||
| 80 | self.free_channels | ||
| 81 | .set(self.free_channels.get() | 1 << index); | ||
| 82 | trace!("freed ch {:u8}", index); | ||
| 83 | }) | ||
| 84 | } | ||
| 68 | 85 | ||
| 69 | trace!("allocated ch {:u8}", index as u8); | 86 | pub fn new_input_channel<'a, T>( |
| 87 | &'a self, | ||
| 88 | pin: &'a Pin<Input<T>>, | ||
| 89 | trigger_mode: EventPolarity, | ||
| 90 | ) -> Result<InputChannel<'a>, NewChannelError> { | ||
| 91 | interrupt::free(|_| { | ||
| 92 | unsafe { INSTANCE = self }; | ||
| 93 | let index = self.allocate_channel()?; | ||
| 94 | trace!("allocated in ch {:u8}", index as u8); | ||
| 70 | 95 | ||
| 71 | self.inner.config[index].write(|w| { | 96 | self.inner.config[index as usize].write(|w| { |
| 72 | match trigger_mode { | 97 | match trigger_mode { |
| 73 | EventPolarity::HiToLo => w.mode().event().polarity().hi_to_lo(), | 98 | EventPolarity::HiToLo => w.mode().event().polarity().hi_to_lo(), |
| 74 | EventPolarity::LoToHi => w.mode().event().polarity().lo_to_hi(), | 99 | EventPolarity::LoToHi => w.mode().event().polarity().lo_to_hi(), |
| @@ -85,44 +110,115 @@ impl Gpiote { | |||
| 85 | // Enable interrupt | 110 | // Enable interrupt |
| 86 | self.inner.intenset.write(|w| unsafe { w.bits(1 << index) }); | 111 | self.inner.intenset.write(|w| unsafe { w.bits(1 << index) }); |
| 87 | 112 | ||
| 88 | Ok(Channel { | 113 | Ok(InputChannel { |
| 114 | gpiote: self, | ||
| 115 | index, | ||
| 116 | }) | ||
| 117 | }) | ||
| 118 | } | ||
| 119 | |||
| 120 | pub fn new_output_channel<'a, T>( | ||
| 121 | &'a self, | ||
| 122 | pin: Pin<Output<T>>, | ||
| 123 | level: Level, | ||
| 124 | task_out_polarity: TaskOutPolarity, | ||
| 125 | ) -> Result<OutputChannel<'a>, NewChannelError> { | ||
| 126 | interrupt::free(|_| { | ||
| 127 | unsafe { INSTANCE = self }; | ||
| 128 | let index = self.allocate_channel()?; | ||
| 129 | trace!("allocated out ch {:u8}", index); | ||
| 130 | |||
| 131 | self.inner.config[index as usize].write(|w| { | ||
| 132 | w.mode().task(); | ||
| 133 | match level { | ||
| 134 | Level::High => w.outinit().high(), | ||
| 135 | Level::Low => w.outinit().low(), | ||
| 136 | }; | ||
| 137 | match task_out_polarity { | ||
| 138 | TaskOutPolarity::Set => w.polarity().lo_to_hi(), | ||
| 139 | TaskOutPolarity::Clear => w.polarity().hi_to_lo(), | ||
| 140 | TaskOutPolarity::Toggle => w.polarity().toggle(), | ||
| 141 | }; | ||
| 142 | w.port().bit(match pin.port() { | ||
| 143 | Port::Port0 => false, | ||
| 144 | Port::Port1 => true, | ||
| 145 | }); | ||
| 146 | unsafe { w.psel().bits(pin.pin()) } | ||
| 147 | }); | ||
| 148 | |||
| 149 | // Enable interrupt | ||
| 150 | self.inner.intenset.write(|w| unsafe { w.bits(1 << index) }); | ||
| 151 | |||
| 152 | Ok(OutputChannel { | ||
| 89 | gpiote: self, | 153 | gpiote: self, |
| 90 | index: index as u8, | 154 | index, |
| 91 | }) | 155 | }) |
| 92 | }) | 156 | }) |
| 93 | } | 157 | } |
| 94 | } | 158 | } |
| 95 | 159 | ||
| 96 | pub struct Channel<'a> { | 160 | pub struct InputChannel<'a> { |
| 97 | gpiote: &'a Gpiote, | 161 | gpiote: &'a Gpiote, |
| 98 | index: u8, | 162 | index: u8, |
| 99 | } | 163 | } |
| 100 | 164 | ||
| 101 | impl<'a> Drop for Channel<'a> { | 165 | impl<'a> Drop for InputChannel<'a> { |
| 102 | fn drop(&mut self) { | 166 | fn drop(&mut self) { |
| 103 | let g = unsafe { Pin::new_unchecked(self.gpiote) }; | 167 | self.gpiote.free_channel(self.index); |
| 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 | } | 168 | } |
| 118 | } | 169 | } |
| 119 | 170 | ||
| 120 | impl<'a> Channel<'a> { | 171 | impl<'a> InputChannel<'a> { |
| 121 | pub async fn wait(&self) -> () { | 172 | pub async fn wait(&self) -> () { |
| 122 | self.gpiote.signals[self.index as usize].wait().await; | 173 | self.gpiote.signals[self.index as usize].wait().await; |
| 123 | } | 174 | } |
| 124 | } | 175 | } |
| 125 | 176 | ||
| 177 | pub struct OutputChannel<'a> { | ||
| 178 | gpiote: &'a Gpiote, | ||
| 179 | index: u8, | ||
| 180 | } | ||
| 181 | |||
| 182 | impl<'a> Drop for OutputChannel<'a> { | ||
| 183 | fn drop(&mut self) { | ||
| 184 | self.gpiote.free_channel(self.index); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | |||
| 188 | impl<'a> OutputChannel<'a> { | ||
| 189 | /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). | ||
| 190 | pub fn out(&self) { | ||
| 191 | self.gpiote.inner.tasks_out[self.index as usize].write(|w| unsafe { w.bits(1) }); | ||
| 192 | } | ||
| 193 | /// Triggers `task set` (set associated pin high). | ||
| 194 | #[cfg(not(feature = "51"))] | ||
| 195 | pub fn set(&self) { | ||
| 196 | self.gpiote.inner.tasks_set[self.index as usize].write(|w| unsafe { w.bits(1) }); | ||
| 197 | } | ||
| 198 | /// Triggers `task clear` (set associated pin low). | ||
| 199 | #[cfg(not(feature = "51"))] | ||
| 200 | pub fn clear(&self) { | ||
| 201 | self.gpiote.inner.tasks_clr[self.index as usize].write(|w| unsafe { w.bits(1) }); | ||
| 202 | } | ||
| 203 | |||
| 204 | /// Returns reference to task_out endpoint for PPI. | ||
| 205 | pub fn task_out(&self) -> &Reg<u32, _TASKS_OUT> { | ||
| 206 | &self.gpiote.inner.tasks_out[self.index as usize] | ||
| 207 | } | ||
| 208 | |||
| 209 | /// Returns reference to task_clr endpoint for PPI. | ||
| 210 | #[cfg(not(feature = "51"))] | ||
| 211 | pub fn task_clr(&self) -> &Reg<u32, _TASKS_CLR> { | ||
| 212 | &self.gpiote.inner.tasks_clr[self.index as usize] | ||
| 213 | } | ||
| 214 | |||
| 215 | /// Returns reference to task_set endpoint for PPI. | ||
| 216 | #[cfg(not(feature = "51"))] | ||
| 217 | pub fn task_set(&self) -> &Reg<u32, _TASKS_SET> { | ||
| 218 | &self.gpiote.inner.tasks_set[self.index as usize] | ||
| 219 | } | ||
| 220 | } | ||
| 221 | |||
| 126 | #[interrupt] | 222 | #[interrupt] |
| 127 | unsafe fn GPIOTE() { | 223 | unsafe fn GPIOTE() { |
| 128 | let s = &(*INSTANCE); | 224 | let s = &(*INSTANCE); |
