aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-nrf-examples/src/bin/ppi.rs112
-rw-r--r--embassy-nrf/src/gpiote.rs44
-rw-r--r--embassy-nrf/src/ppi.rs97
3 files changed, 231 insertions, 22 deletions
diff --git a/embassy-nrf-examples/src/bin/ppi.rs b/embassy-nrf-examples/src/bin/ppi.rs
new file mode 100644
index 000000000..87854fa5c
--- /dev/null
+++ b/embassy-nrf-examples/src/bin/ppi.rs
@@ -0,0 +1,112 @@
1#![no_std]
2#![no_main]
3#![feature(min_type_alias_impl_trait)]
4#![feature(impl_trait_in_bindings)]
5#![feature(type_alias_impl_trait)]
6#![allow(incomplete_features)]
7
8#[path = "../example_common.rs"]
9mod example_common;
10use core::future::pending;
11
12use example_common::*;
13
14use cortex_m_rt::entry;
15use defmt::panic;
16
17use embassy::executor::{task, Executor};
18use embassy::util::Forever;
19use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull};
20use embassy_nrf::gpiote::{self, InputChannel, InputChannelPolarity};
21use embassy_nrf::ppi::Ppi;
22use embassy_nrf::{interrupt, Peripherals};
23use futures::future;
24use gpiote::{OutputChannel, OutputChannelPolarity};
25
26#[task]
27async fn run() {
28 let p = Peripherals::take().unwrap();
29 let g = gpiote::initialize(p.GPIOTE, interrupt::take!(GPIOTE));
30
31 info!("Starting!");
32
33 let button1 = InputChannel::new(
34 g,
35 p.GPIOTE_CH0,
36 Input::new(p.P0_11, Pull::Up),
37 InputChannelPolarity::HiToLo,
38 );
39 let button2 = InputChannel::new(
40 g,
41 p.GPIOTE_CH1,
42 Input::new(p.P0_12, Pull::Up),
43 InputChannelPolarity::HiToLo,
44 );
45 let button3 = InputChannel::new(
46 g,
47 p.GPIOTE_CH2,
48 Input::new(p.P0_24, Pull::Up),
49 InputChannelPolarity::HiToLo,
50 );
51 let button4 = InputChannel::new(
52 g,
53 p.GPIOTE_CH3,
54 Input::new(p.P0_25, Pull::Up),
55 InputChannelPolarity::HiToLo,
56 );
57
58 let led1 = OutputChannel::new(
59 g,
60 p.GPIOTE_CH4,
61 Output::new(p.P0_13, Level::Low, OutputDrive::Standard),
62 OutputChannelPolarity::Toggle,
63 );
64
65 let led2 = OutputChannel::new(
66 g,
67 p.GPIOTE_CH5,
68 Output::new(p.P0_14, Level::Low, OutputDrive::Standard),
69 OutputChannelPolarity::Toggle,
70 );
71
72 let mut ppi = Ppi::new(p.PPI_CH0);
73 ppi.set_event(button1.event_in());
74 ppi.set_task(led1.task_out());
75 ppi.enable();
76
77 let mut ppi = Ppi::new(p.PPI_CH1);
78 ppi.set_event(button2.event_in());
79 ppi.set_task(led1.task_clr());
80 ppi.enable();
81
82 let mut ppi = Ppi::new(p.PPI_CH2);
83 ppi.set_event(button3.event_in());
84 ppi.set_task(led1.task_set());
85 ppi.enable();
86
87 let mut ppi = Ppi::new(p.PPI_CH3);
88 ppi.set_event(button4.event_in());
89 ppi.set_task(led1.task_out());
90 ppi.set_fork_task(led2.task_out());
91 ppi.enable();
92
93 info!("PPI setup!");
94 info!("Press button 1 to toggle LED 1");
95 info!("Press button 2 to turn on LED 1");
96 info!("Press button 3 to turn off LED 1");
97 info!("Press button 4 to toggle LEDs 1 and 2");
98 // Block forever so the above drivers don't get dropped
99 pending::<()>().await;
100}
101
102static EXECUTOR: Forever<Executor> = Forever::new();
103
104#[entry]
105fn main() -> ! {
106 info!("Hello World!");
107
108 let executor = EXECUTOR.put(Executor::new());
109 executor.run(|spawner| {
110 unwrap!(spawner.spawn(run()));
111 });
112}
diff --git a/embassy-nrf/src/gpiote.rs b/embassy-nrf/src/gpiote.rs
index c321c0fd9..412eef1b5 100644
--- a/embassy-nrf/src/gpiote.rs
+++ b/embassy-nrf/src/gpiote.rs
@@ -7,19 +7,15 @@ use embassy::interrupt::InterruptExt;
7use embassy::traits::gpio::{WaitForHigh, WaitForLow}; 7use embassy::traits::gpio::{WaitForHigh, WaitForLow};
8use embassy::util::AtomicWaker; 8use embassy::util::AtomicWaker;
9use embassy_extras::impl_unborrow; 9use embassy_extras::impl_unborrow;
10use embedded_hal::digital::v2::{InputPin, OutputPin, StatefulOutputPin}; 10use embedded_hal::digital::v2::{InputPin, StatefulOutputPin};
11use futures::future::poll_fn; 11use futures::future::poll_fn;
12 12
13use crate::gpio::sealed::Pin as _; 13use crate::gpio::sealed::Pin as _;
14use crate::gpio::{AnyPin, Input, Output, Pin as GpioPin, Port, Pull}; 14use crate::gpio::{AnyPin, Input, Output, Pin as GpioPin, Port};
15use crate::pac; 15use crate::pac;
16use crate::pac::generic::Reg; 16use crate::ppi::{Event, Task};
17use crate::pac::gpiote::_TASKS_OUT;
18use crate::{interrupt, peripherals}; 17use crate::{interrupt, peripherals};
19 18
20#[cfg(not(feature = "51"))]
21use crate::pac::gpiote::{_TASKS_CLR, _TASKS_SET};
22
23pub const CHANNEL_COUNT: usize = 8; 19pub const CHANNEL_COUNT: usize = 8;
24 20
25#[cfg(any(feature = "52833", feature = "52840"))] 21#[cfg(any(feature = "52833", feature = "52840"))]
@@ -53,7 +49,7 @@ pub struct Initialized {
53 _private: (), 49 _private: (),
54} 50}
55 51
56pub fn initialize(gpiote: peripherals::GPIOTE, irq: interrupt::GPIOTE) -> Initialized { 52pub fn initialize(_gpiote: peripherals::GPIOTE, irq: interrupt::GPIOTE) -> Initialized {
57 #[cfg(any(feature = "52833", feature = "52840"))] 53 #[cfg(any(feature = "52833", feature = "52840"))]
58 let ports = unsafe { &[&*pac::P0::ptr(), &*pac::P1::ptr()] }; 54 let ports = unsafe { &[&*pac::P0::ptr(), &*pac::P1::ptr()] };
59 #[cfg(not(any(feature = "52833", feature = "52840")))] 55 #[cfg(not(any(feature = "52833", feature = "52840")))]
@@ -122,6 +118,7 @@ impl Iterator for BitIter {
122 } 118 }
123} 119}
124 120
121/// GPIOTE channel driver in input mode
125pub struct InputChannel<'d, C: Channel, T: GpioPin> { 122pub struct InputChannel<'d, C: Channel, T: GpioPin> {
126 ch: C, 123 ch: C,
127 pin: Input<'d, T>, 124 pin: Input<'d, T>,
@@ -185,6 +182,12 @@ impl<'d, C: Channel, T: GpioPin> InputChannel<'d, C, T> {
185 }) 182 })
186 .await; 183 .await;
187 } 184 }
185
186 /// Returns the IN event, for use with PPI.
187 pub fn event_in(&self) -> Event {
188 let g = unsafe { &*pac::GPIOTE::ptr() };
189 Event::from_reg(&g.events_in[self.ch.number()])
190 }
188} 191}
189 192
190impl<'d, C: Channel, T: GpioPin> InputPin for InputChannel<'d, C, T> { 193impl<'d, C: Channel, T: GpioPin> InputPin for InputChannel<'d, C, T> {
@@ -199,9 +202,10 @@ impl<'d, C: Channel, T: GpioPin> InputPin for InputChannel<'d, C, T> {
199 } 202 }
200} 203}
201 204
205/// GPIOTE channel driver in output mode
202pub struct OutputChannel<'d, C: Channel, T: GpioPin> { 206pub struct OutputChannel<'d, C: Channel, T: GpioPin> {
203 ch: C, 207 ch: C,
204 pin: Output<'d, T>, 208 _pin: Output<'d, T>,
205} 209}
206 210
207impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> { 211impl<'d, C: Channel, T: GpioPin> Drop for OutputChannel<'d, C, T> {
@@ -242,7 +246,7 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
242 unsafe { w.psel().bits(pin.pin.pin()) } 246 unsafe { w.psel().bits(pin.pin.pin()) }
243 }); 247 });
244 248
245 OutputChannel { ch, pin } 249 OutputChannel { ch, _pin: pin }
246 } 250 }
247 251
248 /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle). 252 /// Triggers `task out` (as configured with task_out_polarity, defaults to Toggle).
@@ -265,28 +269,28 @@ impl<'d, C: Channel, T: GpioPin> OutputChannel<'d, C, T> {
265 g.tasks_clr[self.ch.number()].write(|w| unsafe { w.bits(1) }); 269 g.tasks_clr[self.ch.number()].write(|w| unsafe { w.bits(1) });
266 } 270 }
267 271
268 /// Returns reference to task_out endpoint for PPI. 272 /// Returns the OUT task, for use with PPI.
269 pub fn task_out(&self) -> &Reg<u32, _TASKS_OUT> { 273 pub fn task_out(&self) -> Task {
270 let g = unsafe { &*pac::GPIOTE::ptr() }; 274 let g = unsafe { &*pac::GPIOTE::ptr() };
271 &g.tasks_out[self.ch.number()] 275 Task::from_reg(&g.tasks_out[self.ch.number()])
272 } 276 }
273 277
274 /// Returns reference to task_clr endpoint for PPI. 278 /// Returns the CLR task, for use with PPI.
275 #[cfg(not(feature = "51"))] 279 #[cfg(not(feature = "51"))]
276 pub fn task_clr(&self) -> &Reg<u32, _TASKS_CLR> { 280 pub fn task_clr(&self) -> Task {
277 let g = unsafe { &*pac::GPIOTE::ptr() }; 281 let g = unsafe { &*pac::GPIOTE::ptr() };
278 &g.tasks_clr[self.ch.number()] 282 Task::from_reg(&g.tasks_clr[self.ch.number()])
279 } 283 }
280 284
281 /// Returns reference to task_set endpoint for PPI. 285 /// Returns the SET task, for use with PPI.
282 #[cfg(not(feature = "51"))] 286 #[cfg(not(feature = "51"))]
283 pub fn task_set(&self) -> &Reg<u32, _TASKS_SET> { 287 pub fn task_set(&self) -> Task {
284 let g = unsafe { &*pac::GPIOTE::ptr() }; 288 let g = unsafe { &*pac::GPIOTE::ptr() };
285 &g.tasks_set[self.ch.number()] 289 Task::from_reg(&g.tasks_set[self.ch.number()])
286 } 290 }
287} 291}
288 292
289/// GPIO input driver with support 293/// GPIOTE port input driver
290pub struct PortInput<'d, T: GpioPin> { 294pub struct PortInput<'d, T: GpioPin> {
291 pin: Input<'d, T>, 295 pin: Input<'d, T>,
292} 296}
diff --git a/embassy-nrf/src/ppi.rs b/embassy-nrf/src/ppi.rs
index c06b212c1..0129a2a47 100644
--- a/embassy-nrf/src/ppi.rs
+++ b/embassy-nrf/src/ppi.rs
@@ -9,13 +9,106 @@
9//! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task 9//! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task
10//! to be triggered by the same event, even fixed PPI channels have a configurable fork task. 10//! to be triggered by the same event, even fixed PPI channels have a configurable fork task.
11 11
12use embassy_extras::impl_unborrow; 12use core::marker::PhantomData;
13use core::ptr::NonNull;
14use embassy::util::PeripheralBorrow;
15use embassy_extras::{impl_unborrow, unborrow};
13 16
14use crate::peripherals; 17use crate::{pac, peripherals};
18
19// ======================
20// driver
21
22pub struct Ppi<'d, C: Channel> {
23 ch: C,
24 phantom: PhantomData<&'d mut C>,
25}
26
27impl<'d, C: Channel> Ppi<'d, C> {
28 pub fn new(ch: impl PeripheralBorrow<Target = C> + 'd) -> Self {
29 unborrow!(ch);
30 let mut this = Self {
31 ch,
32 phantom: PhantomData,
33 };
34 #[cfg(not(feature = "51"))]
35 this.clear_fork_task();
36 this
37 }
38
39 /// Enables the channel.
40 pub fn enable(&mut self) {
41 let r = unsafe { &*pac::PPI::ptr() };
42 r.chenset
43 .write(|w| unsafe { w.bits(1 << self.ch.number()) });
44 }
45
46 /// Disables the channel.
47 pub fn disable(&mut self) {
48 let r = unsafe { &*pac::PPI::ptr() };
49 r.chenclr
50 .write(|w| unsafe { w.bits(1 << self.ch.number()) });
51 }
52
53 #[cfg(not(feature = "51"))]
54 /// Sets the fork task that must be triggered when the configured event occurs. The user must
55 /// provide a reference to the task.
56 pub fn set_fork_task(&mut self, task: Task) {
57 let r = unsafe { &*pac::PPI::ptr() };
58 r.fork[self.ch.number()]
59 .tep
60 .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
61 }
62
63 #[cfg(not(feature = "51"))]
64 /// Clear the fork task endpoint. Previously set task will no longer be triggered.
65 pub fn clear_fork_task(&mut self) {
66 let r = unsafe { &*pac::PPI::ptr() };
67 r.fork[self.ch.number()].tep.write(|w| unsafe { w.bits(0) })
68 }
69}
70
71impl<'d, C: Channel> Drop for Ppi<'d, C> {
72 fn drop(&mut self) {
73 self.disable()
74 }
75}
76
77impl<'d, C: ConfigurableChannel> Ppi<'d, C> {
78 /// Sets the task to be triggered when the configured event occurs.
79 pub fn set_task(&mut self, task: Task) {
80 let r = unsafe { &*pac::PPI::ptr() };
81 r.ch[self.ch.number()]
82 .tep
83 .write(|w| unsafe { w.bits(task.0.as_ptr() as u32) })
84 }
85
86 /// Sets the event that will trigger the chosen task(s).
87 pub fn set_event(&mut self, event: Event) {
88 let r = unsafe { &*pac::PPI::ptr() };
89 r.ch[self.ch.number()]
90 .eep
91 .write(|w| unsafe { w.bits(event.0.as_ptr() as u32) })
92 }
93}
15 94
16// ====================== 95// ======================
17// traits 96// traits
18 97
98pub struct Task(pub NonNull<()>);
99impl Task {
100 pub(crate) fn from_reg<T>(reg: &T) -> Self {
101 Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut ()) })
102 }
103}
104
105pub struct Event(pub NonNull<()>);
106impl Event {
107 pub(crate) fn from_reg<T>(reg: &T) -> Self {
108 Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut ()) })
109 }
110}
111
19mod sealed { 112mod sealed {
20 pub trait ConfigurableChannel {} 113 pub trait ConfigurableChannel {}
21 pub trait Channel {} 114 pub trait Channel {}