2021-10-18 16:23:39 +02:00
|
|
|
#![macro_use]
|
|
|
|
|
2023-02-01 00:48:33 +01:00
|
|
|
//! Programmable Peripheral Interconnect (PPI/DPPI) driver.
|
2021-10-18 16:23:39 +02:00
|
|
|
//!
|
|
|
|
//! 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.
|
2021-10-18 16:26:27 +02:00
|
|
|
//!
|
2021-10-18 16:23:39 +02:00
|
|
|
//! 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.
|
2021-10-18 16:26:27 +02:00
|
|
|
//!
|
2021-10-18 16:23:39 +02:00
|
|
|
|
2023-06-29 18:09:26 +02:00
|
|
|
use core::marker::PhantomData;
|
2021-10-18 16:23:39 +02:00
|
|
|
use core::ptr::NonNull;
|
2022-06-12 22:15:44 +02:00
|
|
|
|
2023-03-04 05:24:38 +01:00
|
|
|
use embassy_hal_common::{impl_peripheral, into_ref, PeripheralRef};
|
2021-10-18 16:23:39 +02:00
|
|
|
|
2022-07-23 14:00:19 +02:00
|
|
|
use crate::{peripherals, Peripheral};
|
2022-06-12 22:15:44 +02:00
|
|
|
|
2023-03-04 05:24:38 +01:00
|
|
|
#[cfg_attr(feature = "_dppi", path = "dppi.rs")]
|
|
|
|
#[cfg_attr(feature = "_ppi", path = "ppi.rs")]
|
|
|
|
mod _version;
|
|
|
|
pub(crate) use _version::*;
|
2021-10-18 16:23:39 +02:00
|
|
|
|
2023-03-04 05:24:38 +01:00
|
|
|
/// PPI channel driver.
|
2021-10-18 16:23:39 +02:00
|
|
|
pub struct Ppi<'d, C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize> {
|
2022-07-23 14:00:19 +02:00
|
|
|
ch: PeripheralRef<'d, C>,
|
2021-10-19 10:13:08 +02:00
|
|
|
#[cfg(feature = "_dppi")]
|
2023-06-29 18:13:46 +02:00
|
|
|
events: [Event<'d>; EVENT_COUNT],
|
2021-10-19 10:13:08 +02:00
|
|
|
#[cfg(feature = "_dppi")]
|
2023-06-29 18:13:46 +02:00
|
|
|
tasks: [Task<'d>; TASK_COUNT],
|
2021-10-18 16:23:39 +02:00
|
|
|
}
|
|
|
|
|
2023-03-04 05:24:38 +01:00
|
|
|
/// PPI channel group driver.
|
|
|
|
pub struct PpiGroup<'d, G: Group> {
|
|
|
|
g: PeripheralRef<'d, G>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'d, G: Group> PpiGroup<'d, G> {
|
|
|
|
/// Create a new PPI group driver.
|
|
|
|
///
|
|
|
|
/// The group is initialized as containing no channels.
|
|
|
|
pub fn new(g: impl Peripheral<P = G> + 'd) -> Self {
|
|
|
|
into_ref!(g);
|
|
|
|
|
|
|
|
let r = regs();
|
|
|
|
let n = g.number();
|
|
|
|
r.chg[n].write(|w| unsafe { w.bits(0) });
|
|
|
|
|
|
|
|
Self { g }
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add a PPI channel to this group.
|
|
|
|
///
|
|
|
|
/// If the channel is already in the group, this is a no-op.
|
|
|
|
pub fn add_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
|
|
|
|
&mut self,
|
|
|
|
ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
|
|
|
|
) {
|
|
|
|
let r = regs();
|
|
|
|
let ng = self.g.number();
|
|
|
|
let nc = ch.ch.number();
|
|
|
|
r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() | 1 << nc) });
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Remove a PPI channel from this group.
|
|
|
|
///
|
|
|
|
/// If the channel is already not in the group, this is a no-op.
|
|
|
|
pub fn remove_channel<C: Channel, const EVENT_COUNT: usize, const TASK_COUNT: usize>(
|
|
|
|
&mut self,
|
|
|
|
ch: &Ppi<'_, C, EVENT_COUNT, TASK_COUNT>,
|
|
|
|
) {
|
|
|
|
let r = regs();
|
|
|
|
let ng = self.g.number();
|
|
|
|
let nc = ch.ch.number();
|
|
|
|
r.chg[ng].modify(|r, w| unsafe { w.bits(r.bits() & !(1 << nc)) });
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Enable all the channels in this group.
|
|
|
|
pub fn enable_all(&mut self) {
|
|
|
|
let n = self.g.number();
|
|
|
|
regs().tasks_chg[n].en.write(|w| unsafe { w.bits(1) });
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Disable all the channels in this group.
|
|
|
|
pub fn disable_all(&mut self) {
|
|
|
|
let n = self.g.number();
|
|
|
|
regs().tasks_chg[n].dis.write(|w| unsafe { w.bits(1) });
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a reference to the "enable all" task.
|
|
|
|
///
|
|
|
|
/// When triggered, it will enable all the channels in this group.
|
2023-07-05 09:20:56 +02:00
|
|
|
pub fn task_enable_all<'s: 'd>(&'d self) -> Task<'s> {
|
2023-03-04 05:24:38 +01:00
|
|
|
let n = self.g.number();
|
|
|
|
Task::from_reg(®s().tasks_chg[n].en)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a reference to the "disable all" task.
|
|
|
|
///
|
|
|
|
/// When triggered, it will disable all the channels in this group.
|
2023-07-05 09:20:56 +02:00
|
|
|
pub fn task_disable_all<'s: 'd>(&self) -> Task<'s> {
|
2023-03-04 05:24:38 +01:00
|
|
|
let n = self.g.number();
|
|
|
|
Task::from_reg(®s().tasks_chg[n].dis)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'d, G: Group> Drop for PpiGroup<'d, G> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
let r = regs();
|
|
|
|
let n = self.g.number();
|
|
|
|
r.chg[n].write(|w| unsafe { w.bits(0) });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-24 01:52:15 +02:00
|
|
|
#[cfg(feature = "_dppi")]
|
2021-10-19 10:13:08 +02:00
|
|
|
const REGISTER_DPPI_CONFIG_OFFSET: usize = 0x80 / core::mem::size_of::<u32>();
|
|
|
|
|
2021-10-18 16:23:39 +02:00
|
|
|
/// Represents a task that a peripheral can do.
|
|
|
|
///
|
2022-08-24 01:54:27 +02:00
|
|
|
/// When a task is subscribed to a PPI channel, it will run when the channel is triggered by
|
|
|
|
/// a published event.
|
2021-10-18 16:23:39 +02:00
|
|
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
2023-06-29 18:09:26 +02:00
|
|
|
pub struct Task<'d>(NonNull<u32>, PhantomData<&'d ()>);
|
2022-08-24 01:54:27 +02:00
|
|
|
|
2023-06-29 18:09:26 +02:00
|
|
|
impl<'d> Task<'_> {
|
2022-08-24 01:54:27 +02:00
|
|
|
/// Create a new `Task` from a task register pointer
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// `ptr` must be a pointer to a valid `TASKS_*` register from an nRF peripheral.
|
|
|
|
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
2023-06-29 18:09:26 +02:00
|
|
|
Self(ptr, PhantomData)
|
2022-08-24 01:54:27 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 17:37:51 +02:00
|
|
|
/// Triggers this task.
|
2023-06-29 17:44:46 +02:00
|
|
|
pub fn trigger(&mut self) {
|
|
|
|
unsafe { self.0.as_ptr().write_volatile(1) };
|
2023-06-29 17:11:36 +02:00
|
|
|
}
|
|
|
|
|
2021-10-18 16:23:39 +02:00
|
|
|
pub(crate) fn from_reg<T>(reg: &T) -> Self {
|
2023-06-29 18:09:26 +02:00
|
|
|
Self(
|
|
|
|
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
|
|
|
|
PhantomData,
|
|
|
|
)
|
2021-10-18 16:23:39 +02:00
|
|
|
}
|
2021-10-19 10:13:08 +02:00
|
|
|
|
2022-08-24 01:52:15 +02:00
|
|
|
/// Address of subscription register for this task.
|
|
|
|
#[cfg(feature = "_dppi")]
|
2021-10-19 10:13:08 +02:00
|
|
|
pub fn subscribe_reg(&self) -> *mut u32 {
|
|
|
|
unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }
|
|
|
|
}
|
2021-10-18 16:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// # 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.
|
2023-06-29 18:09:26 +02:00
|
|
|
unsafe impl Send for Task<'_> {}
|
2021-10-18 16:23:39 +02:00
|
|
|
|
|
|
|
/// Represents an event that a peripheral can publish.
|
|
|
|
///
|
2022-08-24 01:54:27 +02:00
|
|
|
/// An event can be set to publish on a PPI channel when the event happens.
|
2021-10-18 16:23:39 +02:00
|
|
|
#[derive(PartialEq, Eq, Clone, Copy)]
|
2023-06-29 18:09:26 +02:00
|
|
|
pub struct Event<'d>(NonNull<u32>, PhantomData<&'d ()>);
|
2022-08-24 01:54:27 +02:00
|
|
|
|
2023-06-29 18:09:26 +02:00
|
|
|
impl<'d> Event<'_> {
|
2022-08-24 01:54:27 +02:00
|
|
|
/// Create a new `Event` from an event register pointer
|
|
|
|
///
|
|
|
|
/// # Safety
|
|
|
|
///
|
|
|
|
/// `ptr` must be a pointer to a valid `EVENTS_*` register from an nRF peripheral.
|
|
|
|
pub unsafe fn new_unchecked(ptr: NonNull<u32>) -> Self {
|
2023-06-29 18:09:26 +02:00
|
|
|
Self(ptr, PhantomData)
|
2022-08-24 01:54:27 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 18:09:26 +02:00
|
|
|
pub(crate) fn from_reg<T>(reg: &'d T) -> Self {
|
|
|
|
Self(
|
|
|
|
unsafe { NonNull::new_unchecked(reg as *const _ as *mut _) },
|
|
|
|
PhantomData,
|
|
|
|
)
|
2021-10-18 16:23:39 +02:00
|
|
|
}
|
2021-10-19 10:13:08 +02:00
|
|
|
|
2023-06-29 17:37:51 +02:00
|
|
|
/// Describes whether this Event is currently in a triggered state.
|
2023-06-29 17:44:46 +02:00
|
|
|
pub fn is_triggered(&self) -> bool {
|
|
|
|
unsafe { self.0.as_ptr().read_volatile() == 1 }
|
2023-06-29 17:11:36 +02:00
|
|
|
}
|
|
|
|
|
2023-06-29 17:37:51 +02:00
|
|
|
/// Clear the current register's triggered state, reverting it to 0.
|
2023-06-29 17:44:46 +02:00
|
|
|
pub fn clear(&mut self) {
|
|
|
|
unsafe { self.0.as_ptr().write_volatile(0) };
|
2023-06-29 17:11:36 +02:00
|
|
|
}
|
|
|
|
|
2022-08-22 10:36:33 +02:00
|
|
|
/// Address of publish register for this event.
|
2022-08-24 01:52:15 +02:00
|
|
|
#[cfg(feature = "_dppi")]
|
2021-10-19 10:13:08 +02:00
|
|
|
pub fn publish_reg(&self) -> *mut u32 {
|
|
|
|
unsafe { self.0.as_ptr().add(REGISTER_DPPI_CONFIG_OFFSET) }
|
|
|
|
}
|
2021-10-18 16:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// # 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.
|
2023-06-29 18:09:26 +02:00
|
|
|
unsafe impl Send for Event<'_> {}
|
2021-10-18 16:23:39 +02:00
|
|
|
|
|
|
|
// ======================
|
|
|
|
// traits
|
|
|
|
|
|
|
|
pub(crate) mod sealed {
|
|
|
|
pub trait Channel {}
|
|
|
|
pub trait Group {}
|
|
|
|
}
|
|
|
|
|
2022-08-22 10:36:33 +02:00
|
|
|
/// Interface for PPI channels.
|
2023-03-04 05:24:38 +01:00
|
|
|
pub trait Channel: sealed::Channel + Peripheral<P = Self> + Sized + 'static {
|
2021-10-18 16:23:39 +02:00
|
|
|
/// Returns the number of the channel
|
|
|
|
fn number(&self) -> usize;
|
2021-10-26 09:45:29 +02:00
|
|
|
}
|
|
|
|
|
2022-08-22 10:36:33 +02:00
|
|
|
/// Interface for PPI channels that can be configured.
|
2022-07-23 14:27:45 +02:00
|
|
|
pub trait ConfigurableChannel: Channel + Into<AnyConfigurableChannel> {
|
2022-08-22 10:36:33 +02:00
|
|
|
/// Convert into a type erased configurable channel.
|
2021-10-26 09:45:29 +02:00
|
|
|
fn degrade(self) -> AnyConfigurableChannel;
|
2021-10-18 16:23:39 +02:00
|
|
|
}
|
|
|
|
|
2022-08-22 10:36:33 +02:00
|
|
|
/// Interface for PPI channels that cannot be configured.
|
2022-07-23 14:27:45 +02:00
|
|
|
pub trait StaticChannel: Channel + Into<AnyStaticChannel> {
|
2022-08-22 10:36:33 +02:00
|
|
|
/// Convert into a type erased static channel.
|
2021-10-26 09:45:29 +02:00
|
|
|
fn degrade(self) -> AnyStaticChannel;
|
|
|
|
}
|
2021-10-18 16:23:39 +02:00
|
|
|
|
2022-08-22 10:36:33 +02:00
|
|
|
/// Interface for a group of PPI channels.
|
2023-03-04 05:24:38 +01:00
|
|
|
pub trait Group: sealed::Group + Peripheral<P = Self> + Into<AnyGroup> + Sized + 'static {
|
2022-08-22 10:36:33 +02:00
|
|
|
/// Returns the number of the group.
|
2021-10-18 16:23:39 +02:00
|
|
|
fn number(&self) -> usize;
|
2022-08-22 10:36:33 +02:00
|
|
|
/// Convert into a type erased group.
|
2021-10-26 09:45:29 +02:00
|
|
|
fn degrade(self) -> AnyGroup {
|
|
|
|
AnyGroup {
|
|
|
|
number: self.number() as u8,
|
|
|
|
}
|
|
|
|
}
|
2021-10-18 16:23:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ======================
|
|
|
|
// channels
|
|
|
|
|
2021-10-26 09:45:29 +02:00
|
|
|
/// The any channel can represent any static channel at runtime.
|
|
|
|
/// This can be used to have fewer generic parameters in some places.
|
|
|
|
pub struct AnyStaticChannel {
|
|
|
|
pub(crate) number: u8,
|
|
|
|
}
|
2022-07-23 14:00:19 +02:00
|
|
|
impl_peripheral!(AnyStaticChannel);
|
2021-10-26 09:45:29 +02:00
|
|
|
impl sealed::Channel for AnyStaticChannel {}
|
|
|
|
impl Channel for AnyStaticChannel {
|
|
|
|
fn number(&self) -> usize {
|
|
|
|
self.number as usize
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl StaticChannel for AnyStaticChannel {
|
|
|
|
fn degrade(self) -> AnyStaticChannel {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The any configurable channel can represent any configurable channel at runtime.
|
|
|
|
/// This can be used to have fewer generic parameters in some places.
|
|
|
|
pub struct AnyConfigurableChannel {
|
|
|
|
pub(crate) number: u8,
|
2021-10-18 16:23:39 +02:00
|
|
|
}
|
2022-07-23 14:00:19 +02:00
|
|
|
impl_peripheral!(AnyConfigurableChannel);
|
2021-10-26 09:45:29 +02:00
|
|
|
impl sealed::Channel for AnyConfigurableChannel {}
|
|
|
|
impl Channel for AnyConfigurableChannel {
|
2021-10-18 16:23:39 +02:00
|
|
|
fn number(&self) -> usize {
|
|
|
|
self.number as usize
|
|
|
|
}
|
2021-10-26 09:45:29 +02:00
|
|
|
}
|
|
|
|
impl ConfigurableChannel for AnyConfigurableChannel {
|
|
|
|
fn degrade(self) -> AnyConfigurableChannel {
|
|
|
|
self
|
2021-10-18 16:23:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_ppi_channel {
|
2021-10-26 16:10:34 +02:00
|
|
|
($type:ident, $number:expr) => {
|
2021-10-19 10:13:08 +02:00
|
|
|
impl crate::ppi::sealed::Channel for peripherals::$type {}
|
|
|
|
impl crate::ppi::Channel for peripherals::$type {
|
2021-10-18 16:23:39 +02:00
|
|
|
fn number(&self) -> usize {
|
|
|
|
$number
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2021-10-26 09:45:29 +02:00
|
|
|
($type:ident, $number:expr => static) => {
|
2021-10-26 16:10:34 +02:00
|
|
|
impl_ppi_channel!($type, $number);
|
2021-10-26 09:45:29 +02:00
|
|
|
impl crate::ppi::StaticChannel for peripherals::$type {
|
|
|
|
fn degrade(self) -> crate::ppi::AnyStaticChannel {
|
|
|
|
use crate::ppi::Channel;
|
|
|
|
crate::ppi::AnyStaticChannel {
|
|
|
|
number: self.number() as u8,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-23 14:27:45 +02:00
|
|
|
|
|
|
|
impl From<peripherals::$type> for crate::ppi::AnyStaticChannel {
|
|
|
|
fn from(val: peripherals::$type) -> Self {
|
|
|
|
crate::ppi::StaticChannel::degrade(val)
|
|
|
|
}
|
|
|
|
}
|
2021-10-18 16:23:39 +02:00
|
|
|
};
|
2021-10-26 09:45:29 +02:00
|
|
|
($type:ident, $number:expr => configurable) => {
|
2021-10-26 16:10:34 +02:00
|
|
|
impl_ppi_channel!($type, $number);
|
2021-10-26 09:45:29 +02:00
|
|
|
impl crate::ppi::ConfigurableChannel for peripherals::$type {
|
|
|
|
fn degrade(self) -> crate::ppi::AnyConfigurableChannel {
|
|
|
|
use crate::ppi::Channel;
|
|
|
|
crate::ppi::AnyConfigurableChannel {
|
|
|
|
number: self.number() as u8,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-07-23 14:27:45 +02:00
|
|
|
|
|
|
|
impl From<peripherals::$type> for crate::ppi::AnyConfigurableChannel {
|
|
|
|
fn from(val: peripherals::$type) -> Self {
|
|
|
|
crate::ppi::ConfigurableChannel::degrade(val)
|
|
|
|
}
|
|
|
|
}
|
2021-10-18 16:23:39 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// ======================
|
|
|
|
// groups
|
|
|
|
|
2022-08-22 10:36:33 +02:00
|
|
|
/// A type erased PPI group.
|
2021-10-18 16:23:39 +02:00
|
|
|
pub struct AnyGroup {
|
|
|
|
number: u8,
|
|
|
|
}
|
2022-07-23 14:00:19 +02:00
|
|
|
impl_peripheral!(AnyGroup);
|
2021-10-18 16:23:39 +02:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
2023-03-04 05:24:38 +01:00
|
|
|
|
|
|
|
impl From<peripherals::$type> for crate::ppi::AnyGroup {
|
|
|
|
fn from(val: peripherals::$type) -> Self {
|
|
|
|
crate::ppi::Group::degrade(val)
|
|
|
|
}
|
|
|
|
}
|
2021-10-18 16:23:39 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|