use core::future::Future;
use core::marker::PhantomData;
use core::pin::Pin as FuturePin;
use core::sync::atomic::{AtomicU8, AtomicU32, Ordering};
use core::task::{Context, Poll};
use embassy_hal_internal::{Peri, PeripheralType};
use embassy_sync::waitqueue::AtomicWaker;
use fixed::FixedU32;
use fixed::types::extra::U8;
use pio::{Program, SideSet, Wrap};
use crate::dma::{self, Transfer, Word};
use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate};
use crate::interrupt::typelevel::{Binding, Handler, Interrupt};
use crate::relocate::RelocatedProgram;
use crate::{RegExt, pac, peripherals};
mod instr;
#[doc(inline)]
pub use pio as program;
pub struct Wakers([AtomicWaker; 12]);
impl Wakers {
#[inline(always)]
fn fifo_in(&self) -> &[AtomicWaker] {
&self.0[0..4]
}
#[inline(always)]
fn fifo_out(&self) -> &[AtomicWaker] {
&self.0[4..8]
}
#[inline(always)]
fn irq(&self) -> &[AtomicWaker] {
&self.0[8..12]
}
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum FifoJoin {
#[default]
Duplex,
RxOnly,
TxOnly,
#[cfg(feature = "_rp235x")]
RxAsStatus,
#[cfg(feature = "_rp235x")]
RxAsControl,
#[cfg(feature = "_rp235x")]
PioScratch,
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum ShiftDirection {
#[default]
Right = 1,
Left = 0,
}
#[allow(missing_docs)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum Direction {
In = 0,
Out = 1,
}
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum StatusSource {
#[default]
TxFifoLevel = 0,
RxFifoLevel = 1,
#[cfg(feature = "_rp235x")]
Irq = 2,
}
const RXNEMPTY_MASK: u32 = 1 << 0;
const TXNFULL_MASK: u32 = 1 << 4;
const SMIRQ_MASK: u32 = 1 << 8;
pub struct InterruptHandler<PIO: Instance> {
_pio: PhantomData<PIO>,
}
impl<PIO: Instance> Handler<PIO::Interrupt> for InterruptHandler<PIO> {
unsafe fn on_interrupt() {
let ints = PIO::PIO.irqs(0).ints().read().0;
for bit in 0..12 {
if ints & (1 << bit) != 0 {
PIO::wakers().0[bit].wake();
}
}
PIO::PIO.irqs(0).inte().write_clear(|m| m.0 = ints);
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct FifoOutFuture<'a, 'd, PIO: Instance, const SM: usize> {
sm_tx: &'a mut StateMachineTx<'d, PIO, SM>,
value: u32,
}
impl<'a, 'd, PIO: Instance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> {
pub fn new(sm: &'a mut StateMachineTx<'d, PIO, SM>, value: u32) -> Self {
FifoOutFuture { sm_tx: sm, value }
}
}
impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> {
type Output = ();
fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let value = self.value;
if self.get_mut().sm_tx.try_push(value) {
Poll::Ready(())
} else {
PIO::wakers().fifo_out()[SM].register(cx.waker());
PIO::PIO.irqs(0).inte().write_set(|m| {
m.0 = TXNFULL_MASK << SM;
});
Poll::Pending
}
}
}
impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> {
fn drop(&mut self) {
PIO::PIO.irqs(0).inte().write_clear(|m| {
m.0 = TXNFULL_MASK << SM;
});
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct FifoInFuture<'a, 'd, PIO: Instance, const SM: usize> {
sm_rx: &'a mut StateMachineRx<'d, PIO, SM>,
}
impl<'a, 'd, PIO: Instance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> {
pub fn new(sm: &'a mut StateMachineRx<'d, PIO, SM>) -> Self {
FifoInFuture { sm_rx: sm }
}
}
impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> {
type Output = u32;
fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if let Some(v) = self.sm_rx.try_pull() {
Poll::Ready(v)
} else {
PIO::wakers().fifo_in()[SM].register(cx.waker());
PIO::PIO.irqs(0).inte().write_set(|m| {
m.0 = RXNEMPTY_MASK << SM;
});
Poll::Pending
}
}
}
impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> {
fn drop(&mut self) {
PIO::PIO.irqs(0).inte().write_clear(|m| {
m.0 = RXNEMPTY_MASK << SM;
});
}
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct IrqFuture<'a, 'd, PIO: Instance> {
pio: PhantomData<&'a mut Irq<'d, PIO, 0>>,
irq_no: u8,
}
impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> {
type Output = ();
fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
if PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 {
PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no);
return Poll::Ready(());
}
PIO::wakers().irq()[self.irq_no as usize].register(cx.waker());
PIO::PIO.irqs(0).inte().write_set(|m| {
m.0 = SMIRQ_MASK << self.irq_no;
});
Poll::Pending
}
}
impl<'a, 'd, PIO: Instance> Drop for IrqFuture<'a, 'd, PIO> {
fn drop(&mut self) {
PIO::PIO.irqs(0).inte().write_clear(|m| {
m.0 = SMIRQ_MASK << self.irq_no;
});
}
}
pub struct Pin<'l, PIO: Instance> {
pin: Peri<'l, AnyPin>,
pio: PhantomData<PIO>,
}
impl<'l, PIO: Instance> Pin<'l, PIO> {
#[inline]
pub fn set_drive_strength(&mut self, strength: Drive) {
self.pin.pad_ctrl().modify(|w| {
w.set_drive(match strength {
Drive::_2mA => pac::pads::vals::Drive::_2M_A,
Drive::_4mA => pac::pads::vals::Drive::_4M_A,
Drive::_8mA => pac::pads::vals::Drive::_8M_A,
Drive::_12mA => pac::pads::vals::Drive::_12M_A,
});
});
}
#[inline]
pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
self.pin.pad_ctrl().modify(|w| {
w.set_slewfast(slew_rate == SlewRate::Fast);
});
}
#[inline]
pub fn set_pull(&mut self, pull: Pull) {
self.pin.pad_ctrl().modify(|w| {
w.set_pue(pull == Pull::Up);
w.set_pde(pull == Pull::Down);
});
}
#[inline]
pub fn set_schmitt(&mut self, enable: bool) {
self.pin.pad_ctrl().modify(|w| {
w.set_schmitt(enable);
});
}
#[inline]
pub fn set_output_inversion(&mut self, invert: bool) {
self.pin.gpio().ctrl().modify(|w| {
w.set_outover(if invert {
pac::io::vals::Outover::INVERT
} else {
pac::io::vals::Outover::NORMAL
})
});
}
#[inline]
pub fn set_output_enable_inversion(&mut self, invert: bool) {
self.pin.gpio().ctrl().modify(|w| {
w.set_oeover(if invert {
pac::io::vals::Oeover::INVERT
} else {
pac::io::vals::Oeover::NORMAL
})
})
}
pub fn set_input_sync_bypass(&mut self, bypass: bool) {
let mask = 1 << self.pin();
if bypass {
PIO::PIO.input_sync_bypass().write_set(|w| *w = mask);
} else {
PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask);
}
}
pub fn pin(&self) -> u8 {
self.pin._pin()
}
}
pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> {
pio: PhantomData<&'d mut PIO>,
}
impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
pub fn empty(&self) -> bool {
PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0
}
pub fn full(&self) -> bool {
PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0
}
pub fn level(&self) -> u8 {
(PIO::PIO.flevel().read().0 >> (SM * 8 + 4)) as u8 & 0x0f
}
pub fn stalled(&self) -> bool {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().rxstall() & (1 << SM) != 0;
if ret {
fdebug.write(|w| w.set_rxstall(1 << SM));
}
ret
}
pub fn underflowed(&self) -> bool {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().rxunder() & (1 << SM) != 0;
if ret {
fdebug.write(|w| w.set_rxunder(1 << SM));
}
ret
}
pub fn pull(&mut self) -> u32 {
PIO::PIO.rxf(SM).read()
}
pub fn try_pull(&mut self) -> Option<u32> {
if self.empty() {
return None;
}
Some(self.pull())
}
pub fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, 'd, PIO, SM> {
FifoInFuture::new(self)
}
fn dreq() -> crate::pac::dma::vals::TreqSel {
crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8 + 4)
}
pub fn dma_pull<'a, W: Word>(
&'a mut self,
ch: &'a mut dma::Channel<'_>,
data: &'a mut [W],
bswap: bool,
) -> Transfer<'a> {
unsafe { ch.read(PIO::PIO.rxf(SM).as_ptr() as *const W, data, Self::dreq(), bswap) }
}
pub fn dma_pull_discard<'a, W: Word>(&'a mut self, ch: &'a mut dma::Channel<'_>, len: usize) -> Transfer<'a> {
unsafe { ch.read_discard(PIO::PIO.rxf(SM).as_ptr(), len, Self::dreq()) }
}
}
pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> {
pio: PhantomData<&'d mut PIO>,
}
impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
pub fn empty(&self) -> bool {
PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0
}
pub fn full(&self) -> bool {
PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0
}
pub fn level(&self) -> u8 {
(PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f
}
pub fn stalled(&self) -> bool {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().txstall() & (1 << SM) != 0;
if ret {
fdebug.write(|w| w.set_txstall(1 << SM));
}
ret
}
pub fn overflowed(&self) -> bool {
let fdebug = PIO::PIO.fdebug();
let ret = fdebug.read().txover() & (1 << SM) != 0;
if ret {
fdebug.write(|w| w.set_txover(1 << SM));
}
ret
}
pub fn push(&mut self, v: u32) {
PIO::PIO.txf(SM).write_value(v);
}
pub fn try_push(&mut self, v: u32) -> bool {
if self.full() {
return false;
}
self.push(v);
true
}
pub fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, 'd, PIO, SM> {
FifoOutFuture::new(self, value)
}
fn dreq() -> crate::pac::dma::vals::TreqSel {
crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8)
}
pub fn dma_push<'a, W: Word>(
&'a mut self,
ch: &'a mut dma::Channel<'_>,
data: &'a [W],
bswap: bool,
) -> Transfer<'a> {
unsafe { ch.write(data, PIO::PIO.txf(SM).as_ptr() as *mut W, Self::dreq(), bswap) }
}
pub fn dma_push_zeros<'a, W: Word>(&'a mut self, ch: &'a mut dma::Channel<'_>, len: usize) -> Transfer<'a> {
unsafe { ch.write_zeros(len, PIO::PIO.txf(SM).as_ptr() as *mut W, Self::dreq()) }
}
}
pub struct StateMachine<'d, PIO: Instance, const SM: usize> {
rx: StateMachineRx<'d, PIO, SM>,
tx: StateMachineTx<'d, PIO, SM>,
}
impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> {
fn drop(&mut self) {
PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM));
on_pio_drop::<PIO>();
}
}
fn assert_consecutive<PIO: Instance>(pins: &[&Pin<PIO>]) {
for (p1, p2) in pins.iter().zip(pins.iter().skip(1)) {
assert!(p1.pin() + 1 == p2.pin(), "pins must be consecutive");
}
}
#[derive(Clone, Copy, Default, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[non_exhaustive]
pub struct ExecConfig {
pub side_en: bool,
pub side_pindir: bool,
pub jmp_pin: u8,
pub wrap_top: u8,
pub wrap_bottom: u8,
}
#[derive(Clone, Copy, Default, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ShiftConfig {
pub threshold: u8,
pub direction: ShiftDirection,
pub auto_fill: bool,
}
#[derive(Clone, Copy, Default, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct PinConfig {
pub sideset_count: u8,
pub set_count: u8,
pub out_count: u8,
pub in_base: u8,
pub sideset_base: u8,
pub set_base: u8,
pub out_base: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(feature = "_rp235x")]
pub enum StatusN {
This(u8),
Lower(u8),
Higher(u8),
}
#[cfg(feature = "_rp235x")]
impl Default for StatusN {
fn default() -> Self {
Self::This(0)
}
}
#[cfg(feature = "_rp235x")]
impl Into<crate::pac::pio::vals::ExecctrlStatusN> for StatusN {
fn into(self) -> crate::pac::pio::vals::ExecctrlStatusN {
let x = match self {
StatusN::This(n) => n,
StatusN::Lower(n) => n + 0x08,
StatusN::Higher(n) => n + 0x10,
};
crate::pac::pio::vals::ExecctrlStatusN(x)
}
}
#[derive(Clone, Copy, Debug)]
pub struct Config<'d, PIO: Instance> {
pub clock_divider: FixedU32<U8>,
pub out_en_sel: u8,
pub inline_out_en: bool,
pub out_sticky: bool,
pub status_sel: StatusSource,
#[cfg(feature = "rp2040")]
pub status_n: u8,
#[cfg(feature = "_rp235x")]
pub status_n: StatusN,
exec: ExecConfig,
origin: Option<u8>,
pub fifo_join: FifoJoin,
pub shift_in: ShiftConfig,
pub shift_out: ShiftConfig,
pins: PinConfig,
in_count: u8,
_pio: PhantomData<&'d mut PIO>,
}
impl<'d, PIO: Instance> Default for Config<'d, PIO> {
fn default() -> Self {
Self {
clock_divider: 1u8.into(),
out_en_sel: Default::default(),
inline_out_en: Default::default(),
out_sticky: Default::default(),
status_sel: Default::default(),
status_n: Default::default(),
exec: Default::default(),
origin: Default::default(),
fifo_join: Default::default(),
shift_in: Default::default(),
shift_out: Default::default(),
pins: Default::default(),
in_count: Default::default(),
_pio: Default::default(),
}
}
}
impl<'d, PIO: Instance> Config<'d, PIO> {
pub fn get_exec(&self) -> ExecConfig {
self.exec
}
pub unsafe fn set_exec(&mut self, e: ExecConfig) {
self.exec = e;
}
pub fn get_pins(&self) -> PinConfig {
self.pins
}
pub unsafe fn set_pins(&mut self, p: PinConfig) {
self.pins = p;
}
pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) {
assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len());
assert_consecutive(side_set);
self.exec.side_en = prog.side_set.optional();
self.exec.side_pindir = prog.side_set.pindirs();
self.exec.wrap_bottom = prog.wrap.target;
self.exec.wrap_top = prog.wrap.source;
self.pins.sideset_count = prog.side_set.bits();
self.pins.sideset_base = side_set.first().map_or(0, |p| p.pin());
self.origin = Some(prog.origin);
}
pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) {
self.exec.jmp_pin = pin.pin();
}
pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
assert!(pins.len() <= 5);
assert_consecutive(pins);
self.pins.set_base = pins.first().map_or(0, |p| p.pin());
self.pins.set_count = pins.len() as u8;
}
pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
assert_consecutive(pins);
self.pins.out_base = pins.first().map_or(0, |p| p.pin());
self.pins.out_count = pins.len() as u8;
}
pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
assert_consecutive(pins);
self.pins.in_base = pins.first().map_or(0, |p| p.pin());
self.in_count = pins.len() as u8;
}
}
impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
pub fn set_config(&mut self, config: &Config<'d, PIO>) {
assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536");
assert!(config.clock_divider >= 1, "clkdiv must be >= 1");
assert!(config.out_en_sel < 32, "out_en_sel must be < 32");
assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32");
assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32");
let sm = Self::this_sm();
sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8);
sm.execctrl().write(|w| {
w.set_side_en(config.exec.side_en);
w.set_side_pindir(config.exec.side_pindir);
w.set_jmp_pin(config.exec.jmp_pin);
w.set_out_en_sel(config.out_en_sel);
w.set_inline_out_en(config.inline_out_en);
w.set_out_sticky(config.out_sticky);
w.set_wrap_top(config.exec.wrap_top);
w.set_wrap_bottom(config.exec.wrap_bottom);
#[cfg(feature = "_rp235x")]
w.set_status_sel(match config.status_sel {
StatusSource::TxFifoLevel => pac::pio::vals::ExecctrlStatusSel::TXLEVEL,
StatusSource::RxFifoLevel => pac::pio::vals::ExecctrlStatusSel::RXLEVEL,
StatusSource::Irq => pac::pio::vals::ExecctrlStatusSel::IRQ,
});
#[cfg(feature = "rp2040")]
w.set_status_sel(match config.status_sel {
StatusSource::TxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::TXLEVEL,
StatusSource::RxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::RXLEVEL,
});
w.set_status_n(config.status_n.into());
});
sm.shiftctrl().write(|w| {
w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly);
w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly);
w.set_pull_thresh(config.shift_out.threshold);
w.set_push_thresh(config.shift_in.threshold);
w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right);
w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right);
w.set_autopull(config.shift_out.auto_fill);
w.set_autopush(config.shift_in.auto_fill);
#[cfg(feature = "_rp235x")]
{
w.set_fjoin_rx_get(
config.fifo_join == FifoJoin::RxAsControl || config.fifo_join == FifoJoin::PioScratch,
);
w.set_fjoin_rx_put(
config.fifo_join == FifoJoin::RxAsStatus || config.fifo_join == FifoJoin::PioScratch,
);
w.set_in_count(config.in_count);
}
});
#[cfg(feature = "rp2040")]
sm.pinctrl().write(|w| {
w.set_sideset_count(config.pins.sideset_count);
w.set_set_count(config.pins.set_count);
w.set_out_count(config.pins.out_count);
w.set_in_base(config.pins.in_base);
w.set_sideset_base(config.pins.sideset_base);
w.set_set_base(config.pins.set_base);
w.set_out_base(config.pins.out_base);
});
#[cfg(feature = "_rp235x")]
{
let mut low_ok = true;
let mut high_ok = true;
let in_pins = config.pins.in_base..config.pins.in_base + config.in_count;
let side_pins = config.pins.sideset_base..config.pins.sideset_base + config.pins.sideset_count;
let set_pins = config.pins.set_base..config.pins.set_base + config.pins.set_count;
let out_pins = config.pins.out_base..config.pins.out_base + config.pins.out_count;
for pin_range in [in_pins, side_pins, set_pins, out_pins] {
for pin in pin_range {
low_ok &= pin < 32;
high_ok &= pin >= 16;
}
}
if !low_ok && !high_ok {
panic!(
"All pins must either be <32 or >=16, in:{:?}-{:?}, side:{:?}-{:?}, set:{:?}-{:?}, out:{:?}-{:?}",
config.pins.in_base,
config.pins.in_base + config.in_count - 1,
config.pins.sideset_base,
config.pins.sideset_base + config.pins.sideset_count - 1,
config.pins.set_base,
config.pins.set_base + config.pins.set_count - 1,
config.pins.out_base,
config.pins.out_base + config.pins.out_count - 1,
)
}
let shift = if low_ok { 0 } else { 16 };
sm.pinctrl().write(|w| {
w.set_sideset_count(config.pins.sideset_count);
w.set_set_count(config.pins.set_count);
w.set_out_count(config.pins.out_count);
w.set_in_base(config.pins.in_base.checked_sub(shift).unwrap_or_default());
w.set_sideset_base(config.pins.sideset_base.checked_sub(shift).unwrap_or_default());
w.set_set_base(config.pins.set_base.checked_sub(shift).unwrap_or_default());
w.set_out_base(config.pins.out_base.checked_sub(shift).unwrap_or_default());
});
PIO::PIO.gpiobase().write(|w| w.set_gpiobase(shift == 16));
}
if let Some(origin) = config.origin {
unsafe { self.exec_jmp(origin) }
}
}
pub fn rx_fifo_ptr(&self) -> *mut u32 {
PIO::PIO.rxf(SM).as_ptr()
}
pub fn tx_fifo_ptr(&self) -> *mut u32 {
PIO::PIO.txf(SM).as_ptr()
}
pub fn rx_treq(&self) -> crate::pac::dma::vals::TreqSel {
StateMachineRx::<PIO, SM>::dreq()
}
pub fn tx_treq(&self) -> crate::pac::dma::vals::TreqSel {
StateMachineTx::<PIO, SM>::dreq()
}
pub fn get_addr(&self) -> u8 {
let addr = Self::this_sm().addr();
addr.read().addr()
}
pub fn get_tx_threshold(&self) -> u8 {
let shiftctrl = Self::this_sm().shiftctrl();
shiftctrl.read().pull_thresh()
}
pub fn set_tx_threshold(&mut self, threshold: u8) {
assert!(threshold <= 31);
let shiftctrl = Self::this_sm().shiftctrl();
shiftctrl.modify(|w| {
w.set_pull_thresh(threshold);
});
}
pub fn get_rx_threshold(&self) -> u8 {
Self::this_sm().shiftctrl().read().push_thresh()
}
pub fn set_rx_threshold(&mut self, threshold: u8) {
assert!(threshold <= 31);
let shiftctrl = Self::this_sm().shiftctrl();
shiftctrl.modify(|w| {
w.set_push_thresh(threshold);
});
}
pub fn set_thresholds(&mut self, threshold: u8) {
assert!(threshold <= 31);
let shiftctrl = Self::this_sm().shiftctrl();
shiftctrl.modify(|w| {
w.set_push_thresh(threshold);
w.set_pull_thresh(threshold);
});
}
pub fn set_clock_divider(&mut self, clock_divider: FixedU32<U8>) {
let sm = Self::this_sm();
sm.clkdiv().write(|w| w.0 = clock_divider.to_bits() << 8);
}
#[inline(always)]
fn this_sm() -> crate::pac::pio::StateMachine {
PIO::PIO.sm(SM)
}
pub fn restart(&mut self) {
let mask = 1u8 << SM;
PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask));
}
pub fn set_enable(&mut self, enable: bool) {
let mask = 1u8 << SM;
if enable {
PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask));
} else {
PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask));
}
}
pub fn is_enabled(&self) -> bool {
PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0
}
pub fn clkdiv_restart(&mut self) {
let mask = 1u8 << SM;
PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask));
}
fn with_paused(&mut self, f: impl FnOnce(&mut Self)) {
let enabled = self.is_enabled();
self.set_enable(false);
let pincfg = Self::this_sm().pinctrl().read();
let execcfg = Self::this_sm().execctrl().read();
Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true));
f(self);
Self::this_sm().pinctrl().write_value(pincfg);
Self::this_sm().execctrl().write_value(execcfg);
self.set_enable(enabled);
}
#[cfg(feature = "rp2040")]
fn pin_base() -> u8 {
0
}
#[cfg(feature = "_rp235x")]
fn pin_base() -> u8 {
if PIO::PIO.gpiobase().read().gpiobase() { 16 } else { 0 }
}
pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) {
self.with_paused(|sm| {
for pin in pins {
Self::this_sm().pinctrl().write(|w| {
w.set_set_base(pin.pin() - Self::pin_base());
w.set_set_count(1);
});
unsafe { sm.exec_instr(0b111_00000_100_00000 | dir as u16) };
}
});
}
pub fn set_pins(&mut self, level: Level, pins: &[&Pin<'d, PIO>]) {
self.with_paused(|sm| {
for pin in pins {
Self::this_sm().pinctrl().write(|w| {
w.set_set_base(pin.pin() - Self::pin_base());
w.set_set_count(1);
});
unsafe { sm.exec_instr(0b11100_000_000_00000 | level as u16) };
}
});
}
pub fn clear_fifos(&mut self) {
let shiftctrl = Self::this_sm().shiftctrl();
shiftctrl.modify(|w| {
w.set_fjoin_rx(!w.fjoin_rx());
});
shiftctrl.modify(|w| {
w.set_fjoin_rx(!w.fjoin_rx());
});
}
pub unsafe fn exec_instr(&mut self, instr: u16) {
Self::this_sm().instr().write(|w| w.set_instr(instr));
}
pub fn rx(&mut self) -> &mut StateMachineRx<'d, PIO, SM> {
&mut self.rx
}
pub fn tx(&mut self) -> &mut StateMachineTx<'d, PIO, SM> {
&mut self.tx
}
pub fn rx_tx(&mut self) -> (&mut StateMachineRx<'d, PIO, SM>, &mut StateMachineTx<'d, PIO, SM>) {
(&mut self.rx, &mut self.tx)
}
#[cfg(feature = "_rp235x")]
pub fn get_rxf_entry(&self, n: usize) -> u32 {
PIO::PIO.rxf_putget(SM).putget(n).read()
}
#[cfg(feature = "_rp235x")]
pub fn set_rxf_entry(&self, n: usize, val: u32) {
PIO::PIO.rxf_putget(SM).putget(n).write_value(val)
}
}
pub struct Common<'d, PIO: Instance> {
instructions_used: u32,
pio: PhantomData<&'d mut PIO>,
}
impl<'d, PIO: Instance> Drop for Common<'d, PIO> {
fn drop(&mut self) {
on_pio_drop::<PIO>();
}
}
pub struct InstanceMemory<'d, PIO: Instance> {
used_mask: u32,
pio: PhantomData<&'d mut PIO>,
}
pub struct LoadedProgram<'d, PIO: Instance> {
pub used_memory: InstanceMemory<'d, PIO>,
pub origin: u8,
pub wrap: Wrap,
pub side_set: SideSet,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum LoadError {
InsufficientSpace,
AddressInUse(usize),
}
impl<'d, PIO: Instance> Common<'d, PIO> {
pub fn load_program<const SIZE: usize>(&mut self, prog: &Program<SIZE>) -> LoadedProgram<'d, PIO> {
match self.try_load_program(prog) {
Ok(r) => r,
Err(e) => panic!("Failed to load PIO program: {:?}", e),
}
}
pub fn try_load_program<const SIZE: usize>(
&mut self,
prog: &Program<SIZE>,
) -> Result<LoadedProgram<'d, PIO>, LoadError> {
match prog.origin {
Some(origin) => self.try_load_program_at(prog, origin).map_err(LoadError::AddressInUse),
None => {
let mut origin = 0;
while origin < 32 {
match self.try_load_program_at(prog, origin as _) {
Ok(r) => return Ok(r),
Err(a) => origin = a + 1,
}
}
Err(LoadError::InsufficientSpace)
}
}
}
fn try_load_program_at<const SIZE: usize>(
&mut self,
prog: &Program<SIZE>,
origin: u8,
) -> Result<LoadedProgram<'d, PIO>, usize> {
#[cfg(not(feature = "_rp235x"))]
assert!(prog.version == pio::PioVersion::V0);
let prog = RelocatedProgram::new_with_origin(prog, origin);
let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?;
Ok(LoadedProgram {
used_memory,
origin: prog.origin(),
wrap: prog.wrap(),
side_set: prog.side_set(),
})
}
fn try_write_instr<I>(&mut self, start: usize, instrs: I) -> Result<InstanceMemory<'d, PIO>, usize>
where
I: Iterator<Item = u16>,
{
let mut used_mask = 0;
for (i, instr) in instrs.enumerate() {
let addr = (i + start) % 32;
let mask = 1 << addr;
if (self.instructions_used | used_mask) & mask != 0 {
return Err(addr);
}
PIO::PIO.instr_mem(addr).write(|w| {
w.set_instr_mem(instr);
});
used_mask |= mask;
}
self.instructions_used |= used_mask;
Ok(InstanceMemory {
used_mask,
pio: PhantomData,
})
}
pub unsafe fn free_instr(&mut self, instrs: InstanceMemory<PIO>) {
self.instructions_used &= !instrs.used_mask;
}
pub fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) {
PIO::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass);
PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass);
}
pub fn get_input_sync_bypass(&self) -> u32 {
PIO::PIO.input_sync_bypass().read()
}
pub fn make_pio_pin(&mut self, pin: Peri<'d, impl PioPin + 'd>) -> Pin<'d, PIO> {
pin.pad_ctrl().write(|w| w.set_od(false));
pin.pad_ctrl().write(|w| w.set_ie(true));
pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _));
pin.pad_ctrl().write(|w| {
#[cfg(feature = "_rp235x")]
w.set_iso(false);
w.set_schmitt(true);
w.set_slewfast(false);
w.set_ie(true);
w.set_od(false);
w.set_pue(false);
w.set_pde(false);
});
critical_section::with(|_| {
let val = PIO::state().used_pins.load(Ordering::Relaxed);
PIO::state()
.used_pins
.store(val | 1 << pin.pin_bank(), Ordering::Relaxed);
});
Pin {
pin: pin.into(),
pio: PhantomData::default(),
}
}
}
pub struct PioBatch<'a, PIO: Instance> {
clkdiv_restart: u8,
sm_restart: u8,
sm_enable_mask: u8,
sm_enable: u8,
_pio: PhantomData<&'a PIO>,
}
impl<'a, PIO: Instance> PioBatch<'a, PIO> {
pub fn new() -> Self {
Self {
clkdiv_restart: 0,
sm_restart: 0,
sm_enable_mask: 0,
sm_enable: 0,
_pio: PhantomData,
}
}
pub fn restart<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>) {
self.clkdiv_restart |= 1 << SM;
}
pub fn set_enable<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>, enable: bool) {
self.sm_enable_mask |= 1 << SM;
self.sm_enable |= (enable as u8) << SM;
}
pub fn execute(&mut self) {
PIO::PIO.ctrl().modify(|w| {
w.set_clkdiv_restart(self.clkdiv_restart);
w.set_sm_restart(self.sm_restart);
w.set_sm_enable((w.sm_enable() & !self.sm_enable_mask) | self.sm_enable);
});
}
}
pub struct Irq<'d, PIO: Instance, const N: usize> {
pio: PhantomData<&'d mut PIO>,
}
impl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> {
pub fn wait<'a>(&'a mut self) -> IrqFuture<'a, 'd, PIO> {
IrqFuture {
pio: PhantomData,
irq_no: N as u8,
}
}
}
#[derive(Clone)]
pub struct IrqFlags<'d, PIO: Instance> {
pio: PhantomData<&'d mut PIO>,
}
impl<'d, PIO: Instance> IrqFlags<'d, PIO> {
pub fn check(&self, irq_no: u8) -> bool {
assert!(irq_no < 8);
self.check_any(1 << irq_no)
}
pub fn check_any(&self, irqs: u8) -> bool {
PIO::PIO.irq().read().irq() & irqs != 0
}
pub fn check_all(&self, irqs: u8) -> bool {
PIO::PIO.irq().read().irq() & irqs == irqs
}
pub fn clear(&self, irq_no: usize) {
assert!(irq_no < 8);
self.clear_all(1 << irq_no);
}
pub fn clear_all(&self, irqs: u8) {
PIO::PIO.irq().write(|w| w.set_irq(irqs))
}
pub fn set(&self, irq_no: usize) {
assert!(irq_no < 8);
self.set_all(1 << irq_no);
}
pub fn set_all(&self, irqs: u8) {
PIO::PIO.irq_force().write(|w| w.set_irq_force(irqs))
}
}
pub struct Pio<'d, PIO: Instance> {
pub common: Common<'d, PIO>,
pub irq_flags: IrqFlags<'d, PIO>,
pub irq0: Irq<'d, PIO, 0>,
pub irq1: Irq<'d, PIO, 1>,
pub irq2: Irq<'d, PIO, 2>,
pub irq3: Irq<'d, PIO, 3>,
pub sm0: StateMachine<'d, PIO, 0>,
pub sm1: StateMachine<'d, PIO, 1>,
pub sm2: StateMachine<'d, PIO, 2>,
pub sm3: StateMachine<'d, PIO, 3>,
_pio: PhantomData<&'d mut PIO>,
}
impl<'d, PIO: Instance> Pio<'d, PIO> {
pub fn new(_pio: Peri<'d, PIO>, _irq: impl Binding<PIO::Interrupt, InterruptHandler<PIO>>) -> Self {
PIO::state().users.store(5, Ordering::Release);
PIO::state().used_pins.store(0, Ordering::Release);
PIO::Interrupt::unpend();
unsafe { PIO::Interrupt::enable() };
Self {
common: Common {
instructions_used: 0,
pio: PhantomData,
},
irq_flags: IrqFlags { pio: PhantomData },
irq0: Irq { pio: PhantomData },
irq1: Irq { pio: PhantomData },
irq2: Irq { pio: PhantomData },
irq3: Irq { pio: PhantomData },
sm0: StateMachine {
rx: StateMachineRx { pio: PhantomData },
tx: StateMachineTx { pio: PhantomData },
},
sm1: StateMachine {
rx: StateMachineRx { pio: PhantomData },
tx: StateMachineTx { pio: PhantomData },
},
sm2: StateMachine {
rx: StateMachineRx { pio: PhantomData },
tx: StateMachineTx { pio: PhantomData },
},
sm3: StateMachine {
rx: StateMachineRx { pio: PhantomData },
tx: StateMachineTx { pio: PhantomData },
},
_pio: PhantomData,
}
}
}
struct AtomicU64 {
upper_32: AtomicU32,
lower_32: AtomicU32,
}
impl AtomicU64 {
const fn new(val: u64) -> Self {
let upper_32 = (val >> 32) as u32;
let lower_32 = val as u32;
Self {
upper_32: AtomicU32::new(upper_32),
lower_32: AtomicU32::new(lower_32),
}
}
fn load(&self, order: Ordering) -> u64 {
let (upper, lower) = critical_section::with(|_| (self.upper_32.load(order), self.lower_32.load(order)));
let upper = (upper as u64) << 32;
let lower = lower as u64;
upper | lower
}
fn store(&self, val: u64, order: Ordering) {
let upper_32 = (val >> 32) as u32;
let lower_32 = val as u32;
critical_section::with(|_| {
self.upper_32.store(upper_32, order);
self.lower_32.store(lower_32, order);
});
}
}
pub struct State {
users: AtomicU8,
used_pins: AtomicU64,
}
fn on_pio_drop<PIO: Instance>() {
let state = PIO::state();
let users_state = critical_section::with(|_| {
let val = state.users.load(Ordering::Acquire);
state.users.store(val - 1, Ordering::Release);
val
});
if users_state == 1 {
let used_pins = state.used_pins.load(Ordering::Relaxed);
let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _;
for i in 0..crate::gpio::BANK0_PIN_COUNT {
if used_pins & (1 << i) != 0 {
pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null));
}
}
}
}
trait SealedInstance {
const PIO_NO: u8;
const PIO: &'static crate::pac::pio::Pio;
const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel;
#[inline]
fn wakers() -> &'static Wakers {
static WAKERS: Wakers = Wakers([const { AtomicWaker::new() }; 12]);
&WAKERS
}
#[inline]
fn state() -> &'static State {
static STATE: State = State {
users: AtomicU8::new(0),
used_pins: AtomicU64::new(0),
};
&STATE
}
}
#[allow(private_bounds)]
pub trait Instance: SealedInstance + PeripheralType + Sized + Unpin {
type Interrupt: crate::interrupt::typelevel::Interrupt;
}
macro_rules! impl_pio {
($name:ident, $pio:expr, $pac:ident, $funcsel:ident, $irq:ident) => {
impl SealedInstance for peripherals::$name {
const PIO_NO: u8 = $pio;
const PIO: &'static pac::pio::Pio = &pac::$pac;
const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel;
}
impl Instance for peripherals::$name {
type Interrupt = crate::interrupt::typelevel::$irq;
}
};
}
impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0);
impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);
#[cfg(feature = "_rp235x")]
impl_pio!(PIO2, 2, PIO2, PIO2_0, PIO2_IRQ_0);
pub trait PioPin: gpio::Pin {}
macro_rules! impl_pio_pin {
($( $pin:ident, )*) => {
$(
impl PioPin for peripherals::$pin {}
)*
};
}
impl_pio_pin! {
PIN_0,
PIN_1,
PIN_2,
PIN_3,
PIN_4,
PIN_5,
PIN_6,
PIN_7,
PIN_8,
PIN_9,
PIN_10,
PIN_11,
PIN_12,
PIN_13,
PIN_14,
PIN_15,
PIN_16,
PIN_17,
PIN_18,
PIN_19,
PIN_20,
PIN_21,
PIN_22,
PIN_23,
PIN_24,
PIN_25,
PIN_26,
PIN_27,
PIN_28,
PIN_29,
}
#[cfg(feature = "rp235xb")]
impl_pio_pin! {
PIN_30,
PIN_31,
PIN_32,
PIN_33,
PIN_34,
PIN_35,
PIN_36,
PIN_37,
PIN_38,
PIN_39,
PIN_40,
PIN_41,
PIN_42,
PIN_43,
PIN_44,
PIN_45,
PIN_46,
PIN_47,
}