embassy_nrf/ppi/
mod.rs

1#![macro_use]
2
3//! Programmable Peripheral Interconnect (PPI/DPPI) driver.
4//!
5//! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability
6//! between peripherals through their events and tasks. There are fixed PPI channels and fully
7//! configurable ones. Fixed channels can only connect specific events to specific tasks. For fully
8//! configurable channels, it is possible to choose, via software, the event and the task that it
9//! will triggered by the event.
10//!
11//! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task
12//! to be triggered by the same event, even fixed PPI channels have a configurable fork task.
13//!
14//! The DPPI for nRF53 and nRF91 devices works in a different way. Every channel can support infinitely
15//! many tasks and events, but any single task or event can only be coupled with one channel.
16//!
17
18use core::marker::PhantomData;
19use core::ptr::NonNull;
20
21use embassy_hal_internal::{impl_peripheral, Peri, PeripheralType};
22
23use crate::pac::common::{Reg, RW, W};
24use crate::peripherals;
25
26#[cfg_attr(feature = "_dppi", path = "dppi.rs")]
27#[cfg_attr(feature = "_ppi", path = "ppi.rs")]
28mod _version;
29pub(crate) use _version::*;
30
31/// PPI channel driver.
32pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
33    ch: Peri<'d, C>,
34    #[cfg(feature = "_dppi")]
35    events: [Event<'d>; EVENT_COUNT],
36    #[cfg(feature = "_dppi")]
37    tasks: [Task<'d>; TASK_COUNT],
38}
39
40/// PPI channel group driver.
41pub struct PpiGroup<'d, G: Group> {
42    g: Peri<'d, G>,
43}
44
45impl<'d, G: Group> PpiGroup<'d, G> {
46    /// Create a new PPI group driver.
47    ///
48    /// The group is initialized as containing no channels.
49    pub fn new(g: Peri<'d, G>) -> Self {
50        let r = regs();
51        let n = g.number();
52        r.chg(n).write(|_| ());
53
54        Self { g }
55    }
56
57    /// Add a PPI channel to this group.
58    ///
59    /// If the channel is already in the group, this is a no-op.
60    pub fn add_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
61        &mut self,
62        ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
63    ) {
64        let r = regs();
65        let ng = self.g.number();
66        let nc = ch.ch.number();
67        r.chg(ng).modify(|w| w.set_ch(nc, true));
68    }
69
70    /// Remove a PPI channel from this group.
71    ///
72    /// If the channel is already not in the group, this is a no-op.
73    pub fn remove_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
74        &mut self,
75        ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
76    ) {
77        let r = regs();
78        let ng = self.g.number();
79        let nc = ch.ch.number();
80        r.chg(ng).modify(|w| w.set_ch(nc, false));
81    }
82
83    /// Enable all the channels in this group.
84    pub fn enable_all(&mut self) {
85        let n = self.g.number();
86        regs().tasks_chg(n).en().write_value(1);
87    }
88
89    /// Disable all the channels in this group.
90    pub fn disable_all(&mut self) {
91        let n = self.g.number();
92        regs().tasks_chg(n).dis().write_value(1);
93    }
94
95    /// Get a reference to the "enable all" task.
96    ///
97    /// When triggered, it will enable all the channels in this group.
98    pub fn task_enable_all(&self) -> Task<'d> {
99        let n = self.g.number();
100        Task::from_reg(regs().tasks_chg(n).en())
101    }
102
103    /// Get a reference to the "disable all" task.
104    ///
105    /// When triggered, it will disable all the channels in this group.
106    pub fn task_disable_all(&self) -> Task<'d> {
107        let n = self.g.number();
108        Task::from_reg(regs().tasks_chg(n).dis())
109    }
110}
111impl<G: Group> PpiGroup<'static, G> {
112    /// Persist this group's configuration for the rest of the program's lifetime. This method
113    /// should be preferred over [`core::mem::forget()`] because the `'static` bound prevents
114    /// accidental reuse of the underlying peripheral.
115    pub fn persist(self) {
116        core::mem::forget(self);
117    }
118}
119
120impl<'d, G: Group> Drop for PpiGroup<'d, G> {
121    fn drop(&mut self) {
122        let r = regs();
123        let n = self.g.number();
124        r.chg(n).write(|_| ());
125    }
126}
127
128#[cfg(feature = "_dppi")]
129const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
130
131/// Represents a task that a peripheral can do.
132///
133/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by
134/// a published event.
135#[derive(PartialEq, Eq, Clone, Copy)]
136pub struct Task<'d>(NonNull<u32>, PhantomData<&'d ()>);
137
138impl<'d> Task<'d> {
139    /// Create a new `Task` from a task register pointer
140    ///
141    /// # Safety
142    ///
143    /// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
144    pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
145        Self(ptr, PhantomData)
146    }
147
148    /// Triggers this task.
149    pub fn trigger(&mut self) {
150        unsafe { self.0.as_ptr().write_volatile(1) };
151    }
152
153    pub(crate) fn from_reg(reg: Reg<u32, W>) -> Self {
154        Self(unsafe { NonNull::new_unchecked(reg.as_ptr()) }, PhantomData)
155    }
156
157    /// Address of subscription register for this task.
158    #[cfg(feature = "_dppi")]
159    pub fn subscribe_reg(&self) -> *mut u32 {
160        unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }
161    }
162}
163
164/// # Safety
165///
166/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
167unsafe impl Send for Task<'_> {}
168
169/// Represents an event that a peripheral can publish.
170///
171/// An event can be set to publish on a PPI channel when the event happens.
172#[derive(PartialEq, Eq, Clone, Copy)]
173pub struct Event<'d>(NonNull<u32>, PhantomData<&'d ()>);
174
175impl<'d> Event<'d> {
176    /// Create a new `Event` from an event register pointer
177    ///
178    /// # Safety
179    ///
180    /// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
181    pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
182        Self(ptr, PhantomData)
183    }
184
185    pub(crate) fn from_reg(reg: Reg<u32, RW>) -> Self {
186        Self(unsafe { NonNull::new_unchecked(reg.as_ptr()) }, PhantomData)
187    }
188
189    /// Describes whether this Event is currently in a triggered state.
190    pub fn is_triggered(&self) -> bool {
191        unsafe { self.0.as_ptr().read_volatile() == 1 }
192    }
193
194    /// Clear the current register's triggered state, reverting it to 0.
195    pub fn clear(&mut self) {
196        unsafe { self.0.as_ptr().write_volatile(0) };
197    }
198
199    /// Address of publish register for this event.
200    #[cfg(feature = "_dppi")]
201    pub fn publish_reg(&self) -> *mut u32 {
202        unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }
203    }
204}
205
206/// # Safety
207///
208/// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core.
209unsafe impl Send for Event<'_> {}
210
211// ======================
212//       traits
213
214pub(crate) trait SealedChannel {}
215pub(crate) trait SealedGroup {}
216
217/// Interface for PPI channels.
218#[allow(private_bounds)]
219pub trait Channel: SealedChannel + PeripheralType + Sized + 'static {
220    /// Returns the number of the channel
221    fn number(&self) -> usize;
222}
223
224/// Interface for PPI channels that can be configured.
225pub trait ConfigurableChannel: Channel + Into<AnyConfigurableChannel> {}
226
227/// Interface for PPI channels that cannot be configured.
228pub trait StaticChannel: Channel + Into<AnyStaticChannel> {}
229
230/// Interface for a group of PPI channels.
231#[allow(private_bounds)]
232pub trait Group: SealedGroup + PeripheralType + Into<AnyGroup> + Sized + 'static {
233    /// Returns the number of the group.
234    fn number(&self) -> usize;
235}
236
237// ======================
238//       channels
239
240/// The any channel can represent any static channel at runtime.
241/// This can be used to have fewer generic parameters in some places.
242pub struct AnyStaticChannel {
243    pub(crate) number: u8,
244}
245impl_peripheral!(AnyStaticChannel);
246impl SealedChannel for AnyStaticChannel {}
247impl Channel for AnyStaticChannel {
248    fn number(&self) -> usize {
249        self.number as usize
250    }
251}
252impl StaticChannel for AnyStaticChannel {}
253
254/// The any configurable channel can represent any configurable channel at runtime.
255/// This can be used to have fewer generic parameters in some places.
256pub struct AnyConfigurableChannel {
257    pub(crate) number: u8,
258}
259impl_peripheral!(AnyConfigurableChannel);
260impl SealedChannel for AnyConfigurableChannel {}
261impl Channel for AnyConfigurableChannel {
262    fn number(&self) -> usize {
263        self.number as usize
264    }
265}
266impl ConfigurableChannel for AnyConfigurableChannel {}
267
268#[cfg(not(feature = "_nrf51"))]
269macro_rules! impl_ppi_channel {
270    ($type:ident, $number:expr) => {
271        impl crate::ppi::SealedChannel for peripherals::$type {}
272        impl crate::ppi::Channel for peripherals::$type {
273            fn number(&self) -> usize {
274                $number
275            }
276        }
277    };
278    ($type:ident, $number:expr => static) => {
279        impl_ppi_channel!($type, $number);
280        impl crate::ppi::StaticChannel for peripherals::$type {}
281        impl From<peripherals::$type> for crate::ppi::AnyStaticChannel {
282            fn from(val: peripherals::$type) -> Self {
283                Self {
284                    number: crate::ppi::Channel::number(&val) as u8,
285                }
286            }
287        }
288    };
289    ($type:ident, $number:expr => configurable) => {
290        impl_ppi_channel!($type, $number);
291        impl crate::ppi::ConfigurableChannel for peripherals::$type {}
292        impl From<peripherals::$type> for crate::ppi::AnyConfigurableChannel {
293            fn from(val: peripherals::$type) -> Self {
294                Self {
295                    number: crate::ppi::Channel::number(&val) as u8,
296                }
297            }
298        }
299    };
300}
301
302// ======================
303//       groups
304
305/// A type erased PPI group.
306pub struct AnyGroup {
307    number: u8,
308}
309impl_peripheral!(AnyGroup);
310impl SealedGroup for AnyGroup {}
311impl Group for AnyGroup {
312    fn number(&self) -> usize {
313        self.number as usize
314    }
315}
316
317macro_rules! impl_group {
318    ($type:ident, $number:expr) => {
319        impl SealedGroup for peripherals::$type {}
320        impl Group for peripherals::$type {
321            fn number(&self) -> usize {
322                $number
323            }
324        }
325
326        impl From<peripherals::$type> for crate::ppi::AnyGroup {
327            fn from(val: peripherals::$type) -> Self {
328                Self {
329                    number: crate::ppi::Group::number(&val) as u8,
330                }
331            }
332        }
333    };
334}
335
336impl_group!(PPI_GROUP0, 0);
337impl_group!(PPI_GROUP1, 1);
338impl_group!(PPI_GROUP2, 2);
339impl_group!(PPI_GROUP3, 3);
340#[cfg(not(feature = "_nrf51"))]
341impl_group!(PPI_GROUP4, 4);
342#[cfg(not(feature = "_nrf51"))]
343impl_group!(PPI_GROUP5, 5);