#![cfg_attr(docsrs, procmacros::doc_replace)]
use core::marker::PhantomData;
use crate::{
gpio::{
Level,
Pull,
interconnect::{InputSignal, OutputSignal},
},
peripherals::GPIO_SD,
private,
};
#[non_exhaustive]
pub struct Channels<'d> {
_gpio_sd: GPIO_SD<'d>,
pub channel0_task: TaskChannel<0>,
pub channel0_event: EventChannel<0>,
pub channel1_task: TaskChannel<1>,
pub channel1_event: EventChannel<1>,
pub channel2_task: TaskChannel<2>,
pub channel2_event: EventChannel<2>,
pub channel3_task: TaskChannel<3>,
pub channel3_event: EventChannel<3>,
pub channel4_task: TaskChannel<4>,
pub channel4_event: EventChannel<4>,
pub channel5_task: TaskChannel<5>,
pub channel5_event: EventChannel<5>,
pub channel6_task: TaskChannel<6>,
pub channel6_event: EventChannel<6>,
pub channel7_task: TaskChannel<7>,
pub channel7_event: EventChannel<7>,
}
impl<'d> Channels<'d> {
pub fn new(peripheral: GPIO_SD<'d>) -> Self {
Self {
_gpio_sd: peripheral,
channel0_task: TaskChannel {},
channel0_event: EventChannel {},
channel1_task: TaskChannel {},
channel1_event: EventChannel {},
channel2_task: TaskChannel {},
channel2_event: EventChannel {},
channel3_task: TaskChannel {},
channel3_event: EventChannel {},
channel4_task: TaskChannel {},
channel4_event: EventChannel {},
channel5_task: TaskChannel {},
channel5_event: EventChannel {},
channel6_task: TaskChannel {},
channel6_event: EventChannel {},
channel7_task: TaskChannel {},
channel7_event: EventChannel {},
}
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct InputConfig {
pub pull: Pull,
}
impl Default for InputConfig {
fn default() -> Self {
Self { pull: Pull::None }
}
}
pub struct EventChannel<const C: u8> {}
impl<const C: u8> EventChannel<C> {
pub fn rising_edge<'d>(
self,
pin: impl Into<InputSignal<'d>>,
pin_config: InputConfig,
) -> Event<'d> {
self.into_event(pin, pin_config, EventKind::Rising)
}
pub fn falling_edge<'d>(
self,
pin: impl Into<InputSignal<'d>>,
pin_config: InputConfig,
) -> Event<'d> {
self.into_event(pin, pin_config, EventKind::Falling)
}
pub fn any_edge<'d>(
self,
pin: impl Into<InputSignal<'d>>,
pin_config: InputConfig,
) -> Event<'d> {
self.into_event(pin, pin_config, EventKind::Any)
}
fn into_event<'d>(
self,
pin: impl Into<InputSignal<'d>>,
pin_config: InputConfig,
kind: EventKind,
) -> Event<'d> {
let pin = pin.into();
if let Some(number) = pin.gpio_number() {
pin.apply_input_config(&crate::gpio::InputConfig::default().with_pull(pin_config.pull));
pin.set_input_enable(true);
enable_event_channel(C, number);
}
Event {
id: kind.id() + C,
_pin: PhantomData,
}
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum EventKind {
Rising,
Falling,
Any,
}
impl EventKind {
fn id(&self) -> u8 {
match self {
EventKind::Rising => 1,
EventKind::Falling => 9,
EventKind::Any => 17,
}
}
}
pub struct Event<'d> {
_pin: PhantomData<&'d mut ()>,
id: u8,
}
impl private::Sealed for Event<'_> {}
impl crate::etm::EtmEvent for Event<'_> {
fn id(&self) -> u8 {
self.id
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct OutputConfig {
pub open_drain: bool,
pub pull: Pull,
pub initial_state: Level,
}
impl Default for OutputConfig {
fn default() -> Self {
Self {
open_drain: false,
pull: Pull::None,
initial_state: Level::Low,
}
}
}
pub struct TaskChannel<const C: u8> {}
impl<const C: u8> TaskChannel<C> {
pub fn set<'d>(self, pin: impl Into<OutputSignal<'d>>, pin_config: OutputConfig) -> Task<'d> {
self.into_task(pin, pin_config, TaskKind::Set)
}
pub fn clear<'d>(self, pin: impl Into<OutputSignal<'d>>, pin_config: OutputConfig) -> Task<'d> {
self.into_task(pin, pin_config, TaskKind::Clear)
}
pub fn toggle<'d>(
self,
pin: impl Into<OutputSignal<'d>>,
pin_config: OutputConfig,
) -> Task<'d> {
self.into_task(pin, pin_config, TaskKind::Toggle)
}
fn into_task<'d>(
self,
pin: impl Into<OutputSignal<'d>>,
pin_config: OutputConfig,
kind: TaskKind,
) -> Task<'d> {
let pin = pin.into();
if let Some(number) = pin.gpio_number() {
let config = if pin_config.open_drain {
super::OutputConfig::default()
.with_drive_mode(super::DriveMode::OpenDrain)
.with_pull(pin_config.pull)
} else {
super::OutputConfig::default()
};
pin.set_output_high(pin_config.initial_state.into());
pin.apply_output_config(&config);
pin.set_output_enable(true);
enable_task_channel(C, number);
}
Task {
id: kind.id() + C,
_pin: PhantomData,
}
}
}
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
enum TaskKind {
Set,
Clear,
Toggle,
}
impl TaskKind {
fn id(&self) -> u8 {
match self {
TaskKind::Set => 1,
TaskKind::Clear => 9,
TaskKind::Toggle => 17,
}
}
}
pub struct Task<'d> {
_pin: PhantomData<&'d mut ()>,
id: u8,
}
impl private::Sealed for Task<'_> {}
impl crate::etm::EtmTask for Task<'_> {
fn id(&self) -> u8 {
self.id
}
}
fn enable_task_channel(channel: u8, pin: u8) {
let gpio_sd = GPIO_SD::regs();
let ptr = unsafe { gpio_sd.etm_task_p0_cfg().as_ptr().add(pin as usize / 4) };
let shift = 8 * (pin as usize % 4);
unsafe {
ptr.write_volatile(
ptr.read_volatile() & !(0xf << shift)
| (1 << shift)
| ((channel as u32) << (shift + 1)),
);
}
}
fn enable_event_channel(channel: u8, pin: u8) {
let gpio_sd = GPIO_SD::regs();
gpio_sd
.etm_event_ch_cfg(channel as usize)
.modify(|_, w| w.event_en().clear_bit());
gpio_sd
.etm_event_ch_cfg(channel as usize)
.modify(|_, w| unsafe { w.event_sel().bits(pin) });
gpio_sd
.etm_event_ch_cfg(channel as usize)
.modify(|_, w| w.event_en().set_bit());
}