#![macro_use] //! HAL interface for the PPI and DPPI peripheral. //! //! The (Distributed) Programmable Peripheral Interconnect interface allows for an autonomous interoperability //! between peripherals through their events and tasks. There are fixed PPI channels and fully //! configurable ones. Fixed channels can only connect specific events to specific tasks. For fully //! configurable channels, it is possible to choose, via software, the event and the task that it //! will triggered by the event. //! //! On nRF52 devices, there is also a fork task endpoint, where the user can configure one more task //! to be triggered by the same event, even fixed PPI channels have a configurable fork task. //! //! The DPPI for nRF53 and nRF91 devices works in a different way. Every channel can support infinitely //! many tasks and events, but any single task or event can only be coupled with one channel. //! use crate::{pac, peripherals}; use core::marker::PhantomData; use core::ptr::NonNull; use embassy::util::Unborrow; use embassy_hal_common::{unborrow, unsafe_impl_unborrow}; #[cfg(feature = "_dppi")] mod dppi; #[cfg(feature = "_ppi")] mod ppi; pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> { ch: C, events: [Event; EVENT_COUNT], tasks: [Task; TASK_COUNT], phantom: PhantomData<&'d mut C>, } impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, C, EVENT_COUNT, TASK_COUNT> { pub fn degrade(self) -> Ppi<'d, AnyChannel, EVENT_COUNT, TASK_COUNT> { Ppi { ch: AnyChannel { number: self.ch.number() as u8, #[cfg(feature = "_ppi")] has_configurable_task: self.ch.is_task_configurable(), }, events: self.events, tasks: self.tasks, phantom: PhantomData, } } /// Enables the channel. pub fn enable(&mut self) { let r = unsafe { &*pac::PPI::ptr() }; r.chenset .write(|w| unsafe { w.bits(1 << self.ch.number()) }); } /// Disables the channel. pub fn disable(&mut self) { let r = unsafe { &*pac::PPI::ptr() }; r.chenclr .write(|w| unsafe { w.bits(1 << self.ch.number()) }); } /// Enables all tasks and events fn enable_all(&self) { for (index, task) in self.tasks.iter().enumerate() { Self::enable_task(task, &self.ch, index); } for (index, event) in self.events.iter().enumerate() { Self::enable_event(event, &self.ch, index); } } /// Disable all tasks and events fn disable_all(&self) { for (index, task) in self.tasks.iter().enumerate() { Self::disable_task(task, &self.ch, index); } for (index, event) in self.events.iter().enumerate() { Self::disable_event(event, &self.ch, index); } } } impl<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Drop for Ppi<'d, C, EVENT_COUNT, TASK_COUNT> { fn drop(&mut self) { self.disable(); self.disable_all(); } } impl<'d, C: StaticToOneChannel> Ppi<'d, C, 0, 1> { pub fn new_static_to_one(ch: impl Unborrow + 'd, task: Task) -> Self { unborrow!(ch); let s = Self { ch, events: [], tasks: [task], phantom: PhantomData, }; s.enable_all(); s } } impl<'d, C: OneToOneChannel> Ppi<'d, C, 1, 1> { pub fn new_one_to_one(ch: impl Unborrow + 'd, event: Event, task: Task) -> Self { unborrow!(ch); let s = Self { ch, events: [event], tasks: [task], phantom: PhantomData, }; s.enable_all(); s } } impl<'d, C: OneToTwoChannel> Ppi<'d, C, 1, 2> { pub fn new_one_to_two( ch: impl Unborrow + 'd, event: Event, task1: Task, task2: Task, ) -> Self { unborrow!(ch); let s = Self { ch, events: [event], tasks: [task1, task2], phantom: PhantomData, }; s.enable_all(); s } } impl<'d, C: ManyToManyChannel, const EVENT_COUNT: usize, const TASK_COUNT: usize> Ppi<'d, C, EVENT_COUNT, TASK_COUNT> { pub fn new_many_to_many( ch: impl Unborrow + 'd, events: [Event; EVENT_COUNT], tasks: [Task; TASK_COUNT], ) -> Self { unborrow!(ch); let s = Self { ch, events, tasks, phantom: PhantomData, }; s.enable_all(); s } } /// Represents a task that a peripheral can do. /// When a task is subscribed to a PPI channel it will run when the channel is triggered by /// a published event. /// /// The pointer is to a task register #[derive(PartialEq, Eq, Clone, Copy)] pub struct Task(pub NonNull); impl Task { pub(crate) fn from_reg(reg: &T) -> Self { Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) } } /// # Safety /// /// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core. unsafe impl Send for Task {} /// Represents an event that a peripheral can publish. /// An event can be set to publish on a PPI channel when the event happens. /// /// The pointer is to an event register #[derive(PartialEq, Eq, Clone, Copy)] pub struct Event(pub NonNull); impl Event { pub(crate) fn from_reg(reg: &T) -> Self { Self(unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) }) } } /// # Safety /// /// NonNull is not send, but this event is only allowed to point at registers and those exist in any context on the same core. unsafe impl Send for Event {} // ====================== // traits pub(crate) mod sealed { pub trait Channel {} pub trait Group {} } pub trait Channel: sealed::Channel + Unborrow + Sized { /// Returns the number of the channel fn number(&self) -> usize; #[cfg(feature = "_ppi")] fn is_task_configurable(&self) -> bool; } pub trait StaticToOneChannel: Channel {} pub trait OneToOneChannel: StaticToOneChannel {} pub trait OneToTwoChannel: OneToOneChannel {} pub trait ManyToManyChannel: OneToTwoChannel {} pub trait Group: sealed::Group + Sized { fn number(&self) -> usize; } // ====================== // channels pub struct AnyChannel { number: u8, #[cfg(feature = "_ppi")] has_configurable_task: bool, } unsafe_impl_unborrow!(AnyChannel); impl sealed::Channel for AnyChannel {} impl Channel for AnyChannel { fn number(&self) -> usize { self.number as usize } #[cfg(feature = "_ppi")] fn is_task_configurable(&self) -> bool { self.has_configurable_task } } macro_rules! impl_ppi_channel { ($type:ident, $number:expr, $has_configurable_task:expr) => { impl crate::interconnect::sealed::Channel for peripherals::$type {} impl crate::interconnect::Channel for peripherals::$type { fn number(&self) -> usize { $number } #[cfg(feature = "_ppi")] fn is_task_configurable(&self) -> bool { $has_configurable_task } } }; ($type:ident, $number:expr, $has_configurable_task:expr, 0, 0) => { impl_ppi_channel!($type, $number, $has_configurable_task); }; ($type:ident, $number:expr, $has_configurable_task:expr, 0, 1) => { impl_ppi_channel!($type, $number, $has_configurable_task, 0, 0); impl crate::interconnect::StaticToOneChannel for peripherals::$type {} }; ($type:ident, $number:expr, $has_configurable_task:expr, 1, 1) => { impl_ppi_channel!($type, $number, $has_configurable_task, 0, 1); impl crate::interconnect::OneToOneChannel for peripherals::$type {} }; ($type:ident, $number:expr, $has_configurable_task:expr, 1, 2) => { impl_ppi_channel!($type, $number, $has_configurable_task, 1, 1); impl crate::interconnect::OneToTwoChannel for peripherals::$type {} }; ($type:ident, $number:expr, $has_configurable_task:expr, many, many) => { impl_ppi_channel!($type, $number, $has_configurable_task, 1, 2); impl crate::interconnect::ManyToManyChannel for peripherals::$type {} }; } // ====================== // groups pub struct AnyGroup { number: u8, } unsafe_impl_unborrow!(AnyGroup); impl sealed::Group for AnyGroup {} impl Group for AnyGroup { fn number(&self) -> usize { self.number as usize } } macro_rules! impl_group { ($type:ident, $number:expr) => { impl sealed::Group for peripherals::$type {} impl Group for peripherals::$type { fn number(&self) -> usize { $number } } }; } impl_group!(PPI_GROUP0, 0); impl_group!(PPI_GROUP1, 1); impl_group!(PPI_GROUP2, 2); impl_group!(PPI_GROUP3, 3); #[cfg(not(feature = "nrf51"))] impl_group!(PPI_GROUP4, 4); #[cfg(not(feature = "nrf51"))] impl_group!(PPI_GROUP5, 5);