use crate::{
atomic_register_access::{write_bitmask_clear, write_bitmask_set},
dma::{EndlessReadTarget, EndlessWriteTarget, ReadTarget, WriteTarget},
resets::SubsystemReset,
typelevel::Sealed,
};
use pio::{Instruction, InstructionOperands, Program, SideSet, Wrap};
use rp2040_pac::dma::ch::ch_ctrl_trig::TREQ_SEL_A;
use rp2040_pac::{PIO0, PIO1};
const PIO_INSTRUCTION_COUNT: usize = 32;
pub trait PIOExt:
core::ops::Deref<Target = rp2040_pac::pio0::RegisterBlock> + SubsystemReset + Sized + Send + Sealed
{
#[allow(clippy::type_complexity)] fn split(
self,
resets: &mut pac::RESETS,
) -> (
PIO<Self>,
UninitStateMachine<(Self, SM0)>,
UninitStateMachine<(Self, SM1)>,
UninitStateMachine<(Self, SM2)>,
UninitStateMachine<(Self, SM3)>,
) {
self.reset_bring_down(resets);
self.reset_bring_up(resets);
let sm0 = UninitStateMachine {
block: self.deref(),
sm: &self.deref().sm[0],
_phantom: core::marker::PhantomData,
};
let sm1 = UninitStateMachine {
block: self.deref(),
sm: &self.deref().sm[1],
_phantom: core::marker::PhantomData,
};
let sm2 = UninitStateMachine {
block: self.deref(),
sm: &self.deref().sm[2],
_phantom: core::marker::PhantomData,
};
let sm3 = UninitStateMachine {
block: self.deref(),
sm: &self.deref().sm[3],
_phantom: core::marker::PhantomData,
};
(
PIO {
used_instruction_space: 0,
pio: self,
},
sm0,
sm1,
sm2,
sm3,
)
}
fn id() -> usize;
}
impl PIOExt for PIO0 {
fn id() -> usize {
0
}
}
impl PIOExt for PIO1 {
fn id() -> usize {
1
}
}
#[allow(clippy::upper_case_acronyms)]
pub struct PIO<P: PIOExt> {
used_instruction_space: u32, pio: P,
}
impl<P: PIOExt> core::fmt::Debug for PIO<P> {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_struct("PIO")
.field("used_instruction_space", &self.used_instruction_space)
.field("pio", &"PIO { .. }")
.finish()
}
}
unsafe impl<P: PIOExt> Send for PIO<P> {}
impl<P: PIOExt> PIO<P> {
pub fn free(
self,
_sm0: UninitStateMachine<(P, SM0)>,
_sm1: UninitStateMachine<(P, SM1)>,
_sm2: UninitStateMachine<(P, SM2)>,
_sm3: UninitStateMachine<(P, SM3)>,
) -> P {
self.pio
}
pub fn irq0(&self) -> Interrupt<'_, P, 0> {
Interrupt {
block: self.pio.deref(),
_phantom: core::marker::PhantomData,
}
}
pub fn irq1(&self) -> Interrupt<'_, P, 1> {
Interrupt {
block: self.pio.deref(),
_phantom: core::marker::PhantomData,
}
}
pub fn get_irq_raw(&self) -> u8 {
self.pio.irq.read().irq().bits()
}
pub fn clear_irq(&self, flags: u8) {
self.pio.irq.write(|w| unsafe { w.irq().bits(flags) });
}
pub fn force_irq(&self, flags: u8) {
self.pio
.irq_force
.write(|w| unsafe { w.irq_force().bits(flags) });
}
fn instruction_mask(len: usize) -> u32 {
if len < 32 {
(1 << len) - 1
} else {
0xffffffff
}
}
fn find_offset_for_instructions(&self, i: &[u16], origin: Option<u8>) -> Option<u8> {
if i.len() > PIO_INSTRUCTION_COUNT || i.is_empty() {
None
} else {
let mask = Self::instruction_mask(i.len());
if let Some(origin) = origin {
if origin as usize > PIO_INSTRUCTION_COUNT - i.len()
|| self.used_instruction_space & (mask << origin) != 0
{
None
} else {
Some(origin)
}
} else {
for i in (0..=32 - (i.len() as u8)).rev() {
if self.used_instruction_space & (mask << i) == 0 {
return Some(i);
}
}
None
}
}
}
pub fn install(
&mut self,
p: &Program<{ pio::RP2040_MAX_PROGRAM_SIZE }>,
) -> Result<InstalledProgram<P>, InstallError> {
if let Some(offset) = self.find_offset_for_instructions(&p.code, p.origin) {
p.code
.iter()
.cloned()
.map(|instr| {
if instr & 0b1110_0000_0000_0000 == 0 {
let address = (instr & 0b11111) as u8;
let address = address + offset;
assert!(
address < pio::RP2040_MAX_PROGRAM_SIZE as u8,
"Invalid JMP out of the program after offset addition"
);
instr & (!0b11111) | address as u16
} else {
instr
}
})
.enumerate()
.for_each(|(i, instr)| {
self.pio.instr_mem[i + offset as usize]
.write(|w| unsafe { w.instr_mem0().bits(instr) })
});
self.used_instruction_space |= Self::instruction_mask(p.code.len()) << offset;
Ok(InstalledProgram {
offset,
length: p.code.len() as u8,
side_set: p.side_set,
wrap: p.wrap,
_phantom: core::marker::PhantomData,
})
} else {
Err(InstallError::NoSpace)
}
}
pub fn uninstall(&mut self, p: InstalledProgram<P>) {
let instr_mask = Self::instruction_mask(p.length as usize) << p.offset as u32;
self.used_instruction_space &= !instr_mask;
}
}
#[derive(Debug)]
pub struct InstalledProgram<P> {
offset: u8,
length: u8,
side_set: SideSet,
wrap: Wrap,
_phantom: core::marker::PhantomData<P>,
}
impl<P: PIOExt> InstalledProgram<P> {
pub fn set_wrap(self, wrap: Wrap) -> Result<Self, Self> {
if wrap.source < self.length && wrap.target < self.length {
Ok(InstalledProgram { wrap, ..self })
} else {
Err(self)
}
}
pub fn wrap_target(&self) -> u8 {
self.offset + self.wrap.target
}
pub fn offset(&self) -> u8 {
self.offset
}
pub unsafe fn share(&self) -> InstalledProgram<P> {
InstalledProgram {
offset: self.offset,
length: self.length,
side_set: self.side_set,
wrap: self.wrap,
_phantom: core::marker::PhantomData,
}
}
}
pub trait StateMachineIndex: Send + Sealed {
fn id() -> usize;
}
pub struct SM0;
pub struct SM1;
pub struct SM2;
pub struct SM3;
impl StateMachineIndex for SM0 {
fn id() -> usize {
0
}
}
impl Sealed for SM0 {}
impl StateMachineIndex for SM1 {
fn id() -> usize {
1
}
}
impl Sealed for SM1 {}
impl StateMachineIndex for SM2 {
fn id() -> usize {
2
}
}
impl Sealed for SM2 {}
impl StateMachineIndex for SM3 {
fn id() -> usize {
3
}
}
impl Sealed for SM3 {}
pub trait ValidStateMachine: Sealed {
type PIO: PIOExt;
fn id() -> usize;
fn tx_dreq() -> u8;
fn rx_dreq() -> u8;
}
pub type PIO0SM0 = (PIO0, SM0);
pub type PIO0SM1 = (PIO0, SM1);
pub type PIO0SM2 = (PIO0, SM2);
pub type PIO0SM3 = (PIO0, SM3);
pub type PIO1SM0 = (PIO1, SM0);
pub type PIO1SM1 = (PIO1, SM1);
pub type PIO1SM2 = (PIO1, SM2);
pub type PIO1SM3 = (PIO1, SM3);
impl<P: PIOExt, SM: StateMachineIndex> ValidStateMachine for (P, SM) {
type PIO = P;
fn id() -> usize {
SM::id()
}
fn tx_dreq() -> u8 {
((P::id() << 3) | SM::id()) as u8
}
fn rx_dreq() -> u8 {
((P::id() << 3) | SM::id() | 0x4) as u8
}
}
impl<P: PIOExt, SM: StateMachineIndex> Sealed for (P, SM) {}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PinState {
High,
Low,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum PinDir {
Input,
Output,
}
#[derive(Debug)]
pub struct UninitStateMachine<SM: ValidStateMachine> {
block: *const rp2040_pac::pio0::RegisterBlock,
sm: *const rp2040_pac::pio0::SM,
_phantom: core::marker::PhantomData<SM>,
}
unsafe impl<SM: ValidStateMachine + Send> Send for UninitStateMachine<SM> {}
impl<SM: ValidStateMachine> UninitStateMachine<SM> {
fn set_enabled(&mut self, enabled: bool) {
let mask = 1 << SM::id();
if enabled {
self.set_ctrl_bits(mask);
} else {
self.clear_ctrl_bits(mask);
}
}
fn restart(&mut self) {
self.set_ctrl_bits(1 << (SM::id() + 4));
}
fn reset_clock(&mut self) {
self.set_ctrl_bits(1 << (SM::id() + 8));
}
fn set_ctrl_bits(&mut self, bits: u32) {
unsafe {
write_bitmask_set((*self.block).ctrl.as_ptr(), bits);
}
}
fn clear_ctrl_bits(&mut self, bits: u32) {
unsafe {
write_bitmask_clear((*self.block).ctrl.as_ptr(), bits);
}
}
fn set_clock_divisor(&self, int: u16, frac: u8) {
unsafe {
self.sm()
.sm_clkdiv
.write(|w| w.int().bits(int).frac().bits(frac));
}
}
unsafe fn sm(&self) -> &rp2040_pac::pio0::SM {
&*self.sm
}
unsafe fn pio(&self) -> &rp2040_pac::pio0::RegisterBlock {
&*self.block
}
}
pub struct StateMachine<SM: ValidStateMachine, State> {
sm: UninitStateMachine<SM>,
program: InstalledProgram<SM::PIO>,
_phantom: core::marker::PhantomData<State>,
}
pub struct Stopped;
pub struct Running;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PioIRQ {
#[allow(missing_docs)]
Irq0,
#[allow(missing_docs)]
Irq1,
}
impl PioIRQ {
const fn to_index(self) -> usize {
match self {
PioIRQ::Irq0 => 0,
PioIRQ::Irq1 => 1,
}
}
}
impl<SM: ValidStateMachine, State> StateMachine<SM, State> {
pub fn uninit(
mut self,
_rx: Rx<SM>,
_tx: Tx<SM>,
) -> (UninitStateMachine<SM>, InstalledProgram<SM::PIO>) {
self.sm.set_enabled(false);
(self.sm, self.program)
}
pub fn instruction_address(&self) -> u32 {
unsafe { self.sm.sm().sm_addr.read().bits() }
}
#[deprecated(note = "Renamed to exec_instruction")]
pub fn set_instruction(&mut self, instruction: u16) {
let instruction =
Instruction::decode(instruction, self.program.side_set).expect("Invalid instruction");
self.exec_instruction(instruction);
}
pub fn exec_instruction(&mut self, instruction: Instruction) {
let instruction = instruction.encode(self.program.side_set);
unsafe {
self.sm
.sm()
.sm_instr
.write(|w| w.sm0_instr().bits(instruction))
}
}
pub fn stalled(&self) -> bool {
unsafe { self.sm.sm().sm_execctrl.read().exec_stalled().bits() }
}
pub fn drain_tx_fifo(&mut self) {
const OUT: InstructionOperands = InstructionOperands::OUT {
destination: pio::OutDestination::NULL,
bit_count: 32,
};
const PULL: InstructionOperands = InstructionOperands::PULL {
if_empty: false,
block: false,
};
unsafe {
let sm = &self.sm.sm();
let sm_pinctrl = &sm.sm_pinctrl;
let sm_instr = &sm.sm_instr;
let fstat = &self.sm.pio().fstat;
let operands = if sm.sm_shiftctrl.read().autopull().bit_is_set() {
OUT
} else {
PULL
}
.encode();
let mut saved_sideset_count = 0;
sm_pinctrl.modify(|r, w| {
saved_sideset_count = r.sideset_count().bits();
w.sideset_count().bits(0)
});
let mask = 1 << SM::id();
while (fstat.read().txempty().bits() & mask) == 0 {
sm_instr.write(|w| w.sm0_instr().bits(operands))
}
if saved_sideset_count != 0 {
sm_pinctrl.modify(|_, w| w.sideset_count().bits(saved_sideset_count));
}
}
}
pub fn set_clock_divisor(&mut self, divisor: f32) {
let int = divisor as u16;
let frac = ((divisor - int as f32) * 256.0) as u8;
self.sm.set_clock_divisor(int, frac);
}
pub fn clock_divisor_fixed_point(&mut self, int: u16, frac: u8) {
self.sm.set_clock_divisor(int, frac);
}
}
unsafe impl<SM: ValidStateMachine + Send, State> Send for StateMachine<SM, State> {}
impl<SM: ValidStateMachine> StateMachine<SM, Stopped> {
pub fn start(mut self) -> StateMachine<SM, Running> {
self.sm.set_enabled(true);
StateMachine {
sm: self.sm,
program: self.program,
_phantom: core::marker::PhantomData,
}
}
pub fn set_pins(&mut self, pins: impl IntoIterator<Item = (u8, PinState)>) {
let set_high_instr = InstructionOperands::SET {
destination: pio::SetDestination::PINS,
data: 1,
}
.encode();
let set_low_instr = InstructionOperands::SET {
destination: pio::SetDestination::PINS,
data: 0,
}
.encode();
unsafe {
let sm = self.sm.sm();
let sm_pinctrl = &sm.sm_pinctrl;
let sm_execctrl = &sm.sm_execctrl;
let sm_instr = &sm.sm_instr;
let saved_pin_ctrl = sm_pinctrl.read().bits();
let mut saved_execctrl = 0;
sm_execctrl.modify(|r, w| {
saved_execctrl = r.bits();
w.out_sticky().clear_bit()
});
for (pin_num, pin_state) in pins {
sm_pinctrl.write(|w| w.set_base().bits(pin_num).set_count().bits(1));
let instruction = if pin_state == PinState::High {
set_high_instr
} else {
set_low_instr
};
sm_instr.write(|w| w.sm0_instr().bits(instruction))
}
sm_pinctrl.write(|w| w.bits(saved_pin_ctrl));
sm_execctrl.write(|w| w.bits(saved_execctrl));
}
}
pub fn set_pindirs(&mut self, pindirs: impl IntoIterator<Item = (u8, PinDir)>) {
let set_output_instr = InstructionOperands::SET {
destination: pio::SetDestination::PINDIRS,
data: 1,
}
.encode();
let set_input_instr = InstructionOperands::SET {
destination: pio::SetDestination::PINDIRS,
data: 0,
}
.encode();
unsafe {
let sm = self.sm.sm();
let sm_pinctrl = &sm.sm_pinctrl;
let sm_execctrl = &sm.sm_execctrl;
let sm_instr = &sm.sm_instr;
let saved_pin_ctrl = sm_pinctrl.read().bits();
let mut saved_execctrl = 0;
sm_execctrl.modify(|r, w| {
saved_execctrl = r.bits();
w.out_sticky().clear_bit()
});
for (pin_num, pin_dir) in pindirs {
sm_pinctrl.write(|w| w.set_base().bits(pin_num).set_count().bits(1));
let instruction = if pin_dir == PinDir::Output {
set_output_instr
} else {
set_input_instr
};
sm_instr.write(|w| w.sm0_instr().bits(instruction))
}
sm_pinctrl.write(|w| w.bits(saved_pin_ctrl));
sm_execctrl.write(|w| w.bits(saved_execctrl));
}
}
}
impl<P: PIOExt, SM: StateMachineIndex> StateMachine<(P, SM), Stopped> {
pub fn synchronize_with<'sm, SM2: StateMachineIndex>(
&'sm mut self,
_other_sm: &'sm mut StateMachine<(P, SM2), Stopped>,
) -> Synchronize<'sm, (P, SM)> {
let sm_mask = (1 << SM::id()) | (1 << SM2::id());
Synchronize { sm: self, sm_mask }
}
}
impl<P: PIOExt, SM: StateMachineIndex, State> StateMachine<(P, SM), State> {
pub fn with<SM2: StateMachineIndex>(
self,
other_sm: StateMachine<(P, SM2), State>,
) -> StateMachineGroup2<P, SM, SM2, State> {
StateMachineGroup2 {
sm1: self,
sm2: other_sm,
}
}
}
pub struct StateMachineGroup2<
P: PIOExt,
SM1Idx: StateMachineIndex,
SM2Idx: StateMachineIndex,
State,
> {
sm1: StateMachine<(P, SM1Idx), State>,
sm2: StateMachine<(P, SM2Idx), State>,
}
pub struct StateMachineGroup3<
P: PIOExt,
SM1Idx: StateMachineIndex,
SM2Idx: StateMachineIndex,
SM3Idx: StateMachineIndex,
State,
> {
sm1: StateMachine<(P, SM1Idx), State>,
sm2: StateMachine<(P, SM2Idx), State>,
sm3: StateMachine<(P, SM3Idx), State>,
}
pub struct StateMachineGroup4<
P: PIOExt,
SM1Idx: StateMachineIndex,
SM2Idx: StateMachineIndex,
SM3Idx: StateMachineIndex,
SM4Idx: StateMachineIndex,
State,
> {
sm1: StateMachine<(P, SM1Idx), State>,
sm2: StateMachine<(P, SM2Idx), State>,
sm3: StateMachine<(P, SM3Idx), State>,
sm4: StateMachine<(P, SM4Idx), State>,
}
impl<P: PIOExt, SM1Idx: StateMachineIndex, SM2Idx: StateMachineIndex, State>
StateMachineGroup2<P, SM1Idx, SM2Idx, State>
{
#[allow(clippy::type_complexity)]
pub fn free(
self,
) -> (
StateMachine<(P, SM1Idx), State>,
StateMachine<(P, SM2Idx), State>,
) {
(self.sm1, self.sm2)
}
pub fn with<SM3Idx: StateMachineIndex>(
self,
other_sm: StateMachine<(P, SM3Idx), State>,
) -> StateMachineGroup3<P, SM1Idx, SM2Idx, SM3Idx, State> {
StateMachineGroup3 {
sm1: self.sm1,
sm2: self.sm2,
sm3: other_sm,
}
}
fn mask(&self) -> u32 {
(1 << SM1Idx::id()) | (1 << SM2Idx::id())
}
}
impl<
P: PIOExt,
SM1Idx: StateMachineIndex,
SM2Idx: StateMachineIndex,
SM3Idx: StateMachineIndex,
State,
> StateMachineGroup3<P, SM1Idx, SM2Idx, SM3Idx, State>
{
#[allow(clippy::type_complexity)]
pub fn free(
self,
) -> (
StateMachine<(P, SM1Idx), State>,
StateMachine<(P, SM2Idx), State>,
StateMachine<(P, SM3Idx), State>,
) {
(self.sm1, self.sm2, self.sm3)
}
pub fn with<SM4Idx: StateMachineIndex>(
self,
other_sm: StateMachine<(P, SM4Idx), State>,
) -> StateMachineGroup4<P, SM1Idx, SM2Idx, SM3Idx, SM4Idx, State> {
StateMachineGroup4 {
sm1: self.sm1,
sm2: self.sm2,
sm3: self.sm3,
sm4: other_sm,
}
}
fn mask(&self) -> u32 {
(1 << SM1Idx::id()) | (1 << SM2Idx::id()) | (1 << SM3Idx::id())
}
}
impl<
P: PIOExt,
SM1Idx: StateMachineIndex,
SM2Idx: StateMachineIndex,
SM3Idx: StateMachineIndex,
SM4Idx: StateMachineIndex,
State,
> StateMachineGroup4<P, SM1Idx, SM2Idx, SM3Idx, SM4Idx, State>
{
#[allow(clippy::type_complexity)]
pub fn free(
self,
) -> (
StateMachine<(P, SM1Idx), State>,
StateMachine<(P, SM2Idx), State>,
StateMachine<(P, SM3Idx), State>,
StateMachine<(P, SM4Idx), State>,
) {
(self.sm1, self.sm2, self.sm3, self.sm4)
}
fn mask(&self) -> u32 {
(1 << SM1Idx::id()) | (1 << SM2Idx::id()) | (1 << SM3Idx::id()) | (1 << SM4Idx::id())
}
}
impl<P: PIOExt, SM1Idx: StateMachineIndex, SM2Idx: StateMachineIndex>
StateMachineGroup2<P, SM1Idx, SM2Idx, Stopped>
{
pub fn start(mut self) -> StateMachineGroup2<P, SM1Idx, SM2Idx, Running> {
self.sm1.sm.set_ctrl_bits(self.mask());
StateMachineGroup2 {
sm1: StateMachine {
sm: self.sm1.sm,
program: self.sm1.program,
_phantom: core::marker::PhantomData,
},
sm2: StateMachine {
sm: self.sm2.sm,
program: self.sm2.program,
_phantom: core::marker::PhantomData,
},
}
}
pub fn sync(mut self) -> Self {
self.sm1.sm.set_ctrl_bits(self.mask() << 8);
self
}
}
impl<
P: PIOExt,
SM1Idx: StateMachineIndex,
SM2Idx: StateMachineIndex,
SM3Idx: StateMachineIndex,
> StateMachineGroup3<P, SM1Idx, SM2Idx, SM3Idx, Stopped>
{
pub fn start(mut self) -> StateMachineGroup3<P, SM1Idx, SM2Idx, SM3Idx, Running> {
self.sm1.sm.set_ctrl_bits(self.mask());
StateMachineGroup3 {
sm1: StateMachine {
sm: self.sm1.sm,
program: self.sm1.program,
_phantom: core::marker::PhantomData,
},
sm2: StateMachine {
sm: self.sm2.sm,
program: self.sm2.program,
_phantom: core::marker::PhantomData,
},
sm3: StateMachine {
sm: self.sm3.sm,
program: self.sm3.program,
_phantom: core::marker::PhantomData,
},
}
}
pub fn sync(mut self) -> Self {
self.sm1.sm.set_ctrl_bits(self.mask() << 8);
self
}
}
impl<
P: PIOExt,
SM1Idx: StateMachineIndex,
SM2Idx: StateMachineIndex,
SM3Idx: StateMachineIndex,
SM4Idx: StateMachineIndex,
> StateMachineGroup4<P, SM1Idx, SM2Idx, SM3Idx, SM4Idx, Stopped>
{
pub fn start(mut self) -> StateMachineGroup4<P, SM1Idx, SM2Idx, SM3Idx, SM4Idx, Running> {
self.sm1.sm.set_ctrl_bits(self.mask());
StateMachineGroup4 {
sm1: StateMachine {
sm: self.sm1.sm,
program: self.sm1.program,
_phantom: core::marker::PhantomData,
},
sm2: StateMachine {
sm: self.sm2.sm,
program: self.sm2.program,
_phantom: core::marker::PhantomData,
},
sm3: StateMachine {
sm: self.sm3.sm,
program: self.sm3.program,
_phantom: core::marker::PhantomData,
},
sm4: StateMachine {
sm: self.sm4.sm,
program: self.sm4.program,
_phantom: core::marker::PhantomData,
},
}
}
pub fn sync(mut self) -> Self {
self.sm1.sm.set_ctrl_bits(self.mask() << 8);
self
}
}
impl<P: PIOExt, SM1Idx: StateMachineIndex, SM2Idx: StateMachineIndex>
StateMachineGroup2<P, SM1Idx, SM2Idx, Running>
{
pub fn stop(mut self) -> StateMachineGroup2<P, SM1Idx, SM2Idx, Stopped> {
self.sm1.sm.clear_ctrl_bits(self.mask());
StateMachineGroup2 {
sm1: StateMachine {
sm: self.sm1.sm,
program: self.sm1.program,
_phantom: core::marker::PhantomData,
},
sm2: StateMachine {
sm: self.sm2.sm,
program: self.sm2.program,
_phantom: core::marker::PhantomData,
},
}
}
}
impl<
P: PIOExt,
SM1Idx: StateMachineIndex,
SM2Idx: StateMachineIndex,
SM3Idx: StateMachineIndex,
> StateMachineGroup3<P, SM1Idx, SM2Idx, SM3Idx, Running>
{
pub fn stop(mut self) -> StateMachineGroup3<P, SM1Idx, SM2Idx, SM3Idx, Stopped> {
self.sm1.sm.clear_ctrl_bits(self.mask());
StateMachineGroup3 {
sm1: StateMachine {
sm: self.sm1.sm,
program: self.sm1.program,
_phantom: core::marker::PhantomData,
},
sm2: StateMachine {
sm: self.sm2.sm,
program: self.sm2.program,
_phantom: core::marker::PhantomData,
},
sm3: StateMachine {
sm: self.sm3.sm,
program: self.sm3.program,
_phantom: core::marker::PhantomData,
},
}
}
}
impl<
P: PIOExt,
SM1Idx: StateMachineIndex,
SM2Idx: StateMachineIndex,
SM3Idx: StateMachineIndex,
SM4Idx: StateMachineIndex,
> StateMachineGroup4<P, SM1Idx, SM2Idx, SM3Idx, SM4Idx, Running>
{
pub fn stop(mut self) -> StateMachineGroup4<P, SM1Idx, SM2Idx, SM3Idx, SM4Idx, Stopped> {
self.sm1.sm.clear_ctrl_bits(self.mask());
StateMachineGroup4 {
sm1: StateMachine {
sm: self.sm1.sm,
program: self.sm1.program,
_phantom: core::marker::PhantomData,
},
sm2: StateMachine {
sm: self.sm2.sm,
program: self.sm2.program,
_phantom: core::marker::PhantomData,
},
sm3: StateMachine {
sm: self.sm3.sm,
program: self.sm3.program,
_phantom: core::marker::PhantomData,
},
sm4: StateMachine {
sm: self.sm4.sm,
program: self.sm4.program,
_phantom: core::marker::PhantomData,
},
}
}
pub fn sync(mut self) -> Self {
self.sm1.sm.set_ctrl_bits(self.mask() << 8);
self
}
}
pub struct Synchronize<'sm, SM: ValidStateMachine> {
sm: &'sm mut StateMachine<SM, Stopped>,
sm_mask: u32,
}
impl<'sm, P: PIOExt, SM: StateMachineIndex> Synchronize<'sm, (P, SM)> {
pub fn and_with<SM2: StateMachineIndex>(
mut self,
_other_sm: &'sm mut StateMachine<(P, SM2), Stopped>,
) -> Self {
self.sm_mask |= 1 << SM2::id();
self
}
}
impl<'sm, SM: ValidStateMachine> Drop for Synchronize<'sm, SM> {
fn drop(&mut self) {
let sm_mask = self.sm_mask << 8;
self.sm.sm.set_ctrl_bits(sm_mask);
}
}
impl<SM: ValidStateMachine> StateMachine<SM, Running> {
pub fn stop(mut self) -> StateMachine<SM, Stopped> {
self.sm.set_enabled(false);
StateMachine {
sm: self.sm,
program: self.program,
_phantom: core::marker::PhantomData,
}
}
pub fn restart(&mut self) {
self.sm.set_enabled(false);
unsafe {
let sm = self.sm.sm();
let sm_pinctrl = &sm.sm_pinctrl;
let sm_instr = &sm.sm_instr;
let mut saved_sideset_count = 0;
sm_pinctrl.modify(|r, w| {
saved_sideset_count = r.sideset_count().bits();
w.sideset_count().bits(0)
});
let instruction = InstructionOperands::JMP {
condition: pio::JmpCondition::Always,
address: self.program.wrap_target(),
}
.encode();
sm_instr.write(|w| w.sm0_instr().bits(instruction));
if saved_sideset_count != 0 {
sm_pinctrl.modify(|_, w| w.sideset_count().bits(saved_sideset_count));
}
self.sm.restart();
}
self.sm.set_enabled(true);
}
}
pub struct Rx<SM: ValidStateMachine> {
block: *const rp2040_pac::pio0::RegisterBlock,
_phantom: core::marker::PhantomData<SM>,
}
unsafe impl<SM: ValidStateMachine + Send> Send for Rx<SM> {}
impl<SM: ValidStateMachine> Rx<SM> {
unsafe fn block(&self) -> &pac::pio0::RegisterBlock {
&*self.block
}
pub fn fifo_address(&self) -> *const u32 {
unsafe { self.block().rxf[SM::id()].as_ptr() }
}
pub fn dreq_value(&self) -> u8 {
if self.block as usize == 0x5020_0000usize {
TREQ_SEL_A::PIO0_RX0 as u8 + (SM::id() as u8)
} else {
TREQ_SEL_A::PIO1_RX0 as u8 + (SM::id() as u8)
}
}
pub fn read(&mut self) -> Option<u32> {
if self.is_empty() {
return None;
}
Some(unsafe { core::ptr::read_volatile(self.fifo_address()) })
}
pub fn enable_autopush(&mut self, enable: bool) {
unsafe {
self.block().sm[SM::id()]
.sm_shiftctrl
.modify(|_, w| w.autopush().bit(enable))
}
}
pub fn is_empty(&self) -> bool {
unsafe { self.block().fstat.read().rxempty().bits() & (1 << SM::id()) != 0 }
}
pub fn is_full(&self) -> bool {
unsafe { self.block().fstat.read().rxfull().bits() & (1 << SM::id()) != 0 }
}
pub fn enable_rx_not_empty_interrupt(&self, id: PioIRQ) {
unsafe {
write_bitmask_set(
self.block().sm_irq[id.to_index()].irq_inte.as_ptr(),
1 << SM::id(),
);
}
}
pub fn disable_rx_not_empty_interrupt(&self, id: PioIRQ) {
unsafe {
write_bitmask_clear(
self.block().sm_irq[id.to_index()].irq_inte.as_ptr(),
1 << SM::id(),
);
}
}
pub fn force_rx_not_empty_interrupt(&self, id: PioIRQ, state: bool) {
let action = if state {
write_bitmask_set
} else {
write_bitmask_clear
};
unsafe {
action(
self.block().sm_irq[id.to_index()].irq_intf.as_ptr(),
1 << SM::id(),
);
}
}
}
impl<SM: ValidStateMachine> ReadTarget for Rx<SM> {
type ReceivedWord = u32;
fn rx_treq() -> Option<u8> {
Some(SM::rx_dreq())
}
fn rx_address_count(&self) -> (u32, u32) {
(
&unsafe { &*self.block }.rxf[SM::id()] as *const _ as u32,
u32::MAX,
)
}
fn rx_increment(&self) -> bool {
false
}
}
impl<SM: ValidStateMachine> EndlessReadTarget for Rx<SM> {}
pub struct Tx<SM: ValidStateMachine> {
block: *const rp2040_pac::pio0::RegisterBlock,
_phantom: core::marker::PhantomData<SM>,
}
unsafe impl<SM: ValidStateMachine + Send> Send for Tx<SM> {}
impl<SM: ValidStateMachine> Tx<SM> {
unsafe fn block(&self) -> &pac::pio0::RegisterBlock {
&*self.block
}
fn write_generic<T>(&mut self, value: T) -> bool {
if !self.is_full() {
unsafe {
let reg_ptr = self.fifo_address() as *mut T;
reg_ptr.write_volatile(value);
}
true
} else {
false
}
}
pub fn fifo_address(&self) -> *const u32 {
unsafe { self.block().txf[SM::id()].as_ptr() }
}
pub fn dreq_value(&self) -> u8 {
if self.block as usize == 0x5020_0000usize {
TREQ_SEL_A::PIO0_TX0 as u8 + (SM::id() as u8)
} else {
TREQ_SEL_A::PIO1_TX0 as u8 + (SM::id() as u8)
}
}
pub fn write(&mut self, value: u32) -> bool {
self.write_generic(value)
}
pub fn write_u8_replicated(&mut self, value: u8) -> bool {
self.write_generic(value)
}
pub fn write_u16_replicated(&mut self, value: u16) -> bool {
self.write_generic(value)
}
pub fn has_stalled(&self) -> bool {
let mask = 1 << SM::id();
unsafe { self.block().fdebug.read().txstall().bits() & mask == mask }
}
pub fn clear_stalled_flag(&self) {
let mask = 1 << SM::id();
unsafe {
self.block().fdebug.write(|w| w.txstall().bits(mask));
}
}
pub fn is_empty(&self) -> bool {
unsafe { self.block().fstat.read().txempty().bits() & (1 << SM::id()) != 0 }
}
pub fn is_full(&self) -> bool {
unsafe { self.block().fstat.read().txfull().bits() & (1 << SM::id()) != 0 }
}
pub fn enable_tx_not_full_interrupt(&self, id: PioIRQ) {
unsafe {
write_bitmask_set(
self.block().sm_irq[id.to_index()].irq_inte.as_ptr(),
1 << (SM::id() + 4),
);
}
}
pub fn disable_tx_not_full_interrupt(&self, id: PioIRQ) {
unsafe {
write_bitmask_clear(
self.block().sm_irq[id.to_index()].irq_inte.as_ptr(),
1 << (SM::id() + 4),
);
}
}
pub fn force_tx_not_full_interrupt(&self, id: PioIRQ) {
unsafe {
write_bitmask_set(
self.block().sm_irq[id.to_index()].irq_intf.as_ptr(),
1 << (SM::id() + 4),
);
}
}
}
impl<SM: ValidStateMachine> WriteTarget for Tx<SM> {
type TransmittedWord = u32;
fn tx_treq() -> Option<u8> {
Some(SM::tx_dreq())
}
fn tx_address_count(&mut self) -> (u32, u32) {
(
&unsafe { &*self.block }.txf[SM::id()] as *const _ as u32,
u32::MAX,
)
}
fn tx_increment(&self) -> bool {
false
}
}
impl<SM: ValidStateMachine> EndlessWriteTarget for Tx<SM> {}
#[derive(Debug)]
pub struct Interrupt<'a, P: PIOExt, const IRQ: usize> {
block: *const rp2040_pac::pio0::RegisterBlock,
_phantom: core::marker::PhantomData<&'a P>,
}
unsafe impl<'a, P: PIOExt, const IRQ: usize> Send for Interrupt<'a, P, IRQ> {}
impl<'a, P: PIOExt, const IRQ: usize> Interrupt<'a, P, IRQ> {
pub fn enable_sm_interrupt(&self, id: u8) {
assert!(id < 4, "invalid state machine interrupt number");
unsafe {
write_bitmask_set(self.irq().irq_inte.as_ptr(), 1 << (id + 8));
}
}
pub fn disable_sm_interrupt(&self, id: u8) {
assert!(id < 4, "invalid state machine interrupt number");
unsafe {
write_bitmask_clear(self.irq().irq_inte.as_ptr(), 1 << (id + 8));
}
}
pub fn force_sm_interrupt(&self, id: u8, set: bool) {
assert!(id < 4, "invalid state machine interrupt number");
unsafe {
if set {
write_bitmask_set(self.irq().irq_intf.as_ptr(), 1 << (id + 8));
} else {
write_bitmask_clear(self.irq().irq_intf.as_ptr(), 1 << (id + 8));
}
}
}
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn enable_tx_not_full_interrupt(&self, id: u8) {
assert!(id < 4, "invalid state machine interrupt number");
unsafe {
write_bitmask_set(self.irq().irq_inte.as_ptr(), 1 << (id + 4));
}
}
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn disable_tx_not_full_interrupt(&self, id: u8) {
assert!(id < 4, "invalid state machine interrupt number");
unsafe {
write_bitmask_clear(self.irq().irq_inte.as_ptr(), 1 << (id + 4));
}
}
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn force_tx_not_full_interrupt(&self, id: u8) {
assert!(id < 4, "invalid state machine interrupt number");
unsafe {
write_bitmask_set(self.irq().irq_intf.as_ptr(), 1 << (id + 4));
}
}
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn enable_rx_not_empty_interrupt(&self, id: u8) {
assert!(id < 4, "invalid state machine interrupt number");
unsafe {
write_bitmask_set(self.irq().irq_inte.as_ptr(), 1 << id);
}
}
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn disable_rx_not_empty_interrupt(&self, id: u8) {
assert!(id < 4, "invalid state machine interrupt number");
unsafe {
write_bitmask_clear(self.irq().irq_inte.as_ptr(), 1 << id);
}
}
#[deprecated(
since = "0.7.0",
note = "Use the dedicated method on the state machine"
)]
pub fn force_rx_not_empty_interrupt(&self, id: u8) {
assert!(id < 4, "invalid state machine interrupt number");
unsafe {
write_bitmask_set(self.irq().irq_intf.as_ptr(), 1 << id);
}
}
pub fn raw(&self) -> InterruptState {
InterruptState(
unsafe { self.block().intr.read().bits() },
)
}
pub fn state(&self) -> InterruptState {
InterruptState(
unsafe { self.irq().irq_ints.read().bits() },
)
}
unsafe fn block(&self) -> &rp2040_pac::pio0::RegisterBlock {
&*self.block
}
unsafe fn irq(&self) -> &rp2040_pac::pio0::SM_IRQ {
&self.block().sm_irq[IRQ]
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct InterruptState(u32);
macro_rules! raw_interrupt_accessor {
($name:ident, $doc:literal, $idx:expr) => {
#[doc = concat!("Check whether interrupt ", $doc, " has been raised.")]
pub fn $name(self) -> bool {
self.0 & (1 << $idx) != 0
}
};
}
impl InterruptState {
raw_interrupt_accessor!(sm0_rx_not_empty, "SM0_RXNEMPTY", 0);
raw_interrupt_accessor!(sm1_rx_not_empty, "SM1_RXNEMPTY", 1);
raw_interrupt_accessor!(sm2_rx_not_empty, "SM2_RXNEMPTY", 2);
raw_interrupt_accessor!(sm3_rx_not_empty, "SM3_RXNEMPTY", 3);
raw_interrupt_accessor!(sm0_tx_not_full, "SM0_TXNFULL", 4);
raw_interrupt_accessor!(sm1_tx_not_full, "SM1_TXNFULL", 5);
raw_interrupt_accessor!(sm2_tx_not_full, "SM2_TXNFULL", 6);
raw_interrupt_accessor!(sm3_tx_not_full, "SM3_TXNFULL", 7);
raw_interrupt_accessor!(sm0, "SM0", 8);
raw_interrupt_accessor!(sm1, "SM1", 9);
raw_interrupt_accessor!(sm2, "SM2", 10);
raw_interrupt_accessor!(sm3, "SM3", 11);
}
#[derive(Debug, Clone, Copy)]
pub enum MovStatusConfig {
Tx(u8),
Rx(u8),
}
#[derive(Debug, Clone, Copy)]
pub enum ShiftDirection {
Left,
Right,
}
impl ShiftDirection {
fn bit(self) -> bool {
match self {
Self::Left => false,
Self::Right => true,
}
}
}
#[derive(Debug)]
pub struct PIOBuilder<P> {
clock_divisor: (u16, u8),
program: InstalledProgram<P>,
jmp_pin: u8,
out_sticky: bool,
inline_out: Option<u8>,
mov_status: MovStatusConfig,
fifo_join: Buffers,
pull_threshold: u8,
push_threshold: u8,
out_shiftdir: ShiftDirection,
in_shiftdir: ShiftDirection,
autopull: bool,
autopush: bool,
set_count: u8,
out_count: u8,
in_base: u8,
side_set_base: u8,
set_base: u8,
out_base: u8,
}
#[derive(Debug, Clone, Copy)]
pub enum Buffers {
RxTx,
OnlyTx,
OnlyRx,
}
#[derive(Debug)]
pub enum InstallError {
NoSpace,
}
impl<P: PIOExt> PIOBuilder<P> {
pub fn from_program(p: InstalledProgram<P>) -> Self {
PIOBuilder {
clock_divisor: (1, 0),
program: p,
jmp_pin: 0,
out_sticky: false,
inline_out: None,
mov_status: MovStatusConfig::Tx(0),
fifo_join: Buffers::RxTx,
pull_threshold: 0,
push_threshold: 0,
out_shiftdir: ShiftDirection::Left,
in_shiftdir: ShiftDirection::Left,
autopull: false,
autopush: false,
set_count: 5,
out_count: 0,
in_base: 0,
side_set_base: 0,
set_base: 0,
out_base: 0,
}
}
pub fn set_mov_status_config(mut self, mov_status: MovStatusConfig) -> Self {
self.mov_status = mov_status;
self
}
pub fn set_pins(mut self, base: u8, count: u8) -> Self {
assert!(count <= 5);
self.set_base = base;
self.set_count = count;
self
}
pub fn out_pins(mut self, base: u8, count: u8) -> Self {
assert!(count <= 32);
self.out_base = base;
self.out_count = count;
self
}
pub fn in_pin_base(mut self, base: u8) -> Self {
self.in_base = base;
self
}
pub fn jmp_pin(mut self, pin: u8) -> Self {
self.jmp_pin = pin;
self
}
pub fn side_set_pin_base(mut self, base: u8) -> Self {
self.side_set_base = base;
self
}
pub fn buffers(mut self, buffers: Buffers) -> Self {
self.fifo_join = buffers;
self
}
#[deprecated(
since = "0.7.0",
note = "Pulls in floating points. Use the fixed point alternative: clock_divisor_fixed_point"
)]
pub fn clock_divisor(mut self, divisor: f32) -> Self {
self.clock_divisor = (divisor as u16, (divisor * 256.0) as u8);
self
}
pub fn clock_divisor_fixed_point(mut self, int: u16, frac: u8) -> Self {
assert!(int != 0 || frac == 0);
self.clock_divisor = (int, frac);
self
}
pub fn out_sticky(mut self, out_sticky: bool) -> Self {
self.out_sticky = out_sticky;
self
}
pub fn inline_out(mut self, inline_out: Option<u8>) -> Self {
self.inline_out = inline_out;
self
}
pub fn autopush(mut self, autopush: bool) -> Self {
self.autopush = autopush;
self
}
pub fn push_threshold(mut self, threshold: u8) -> Self {
self.push_threshold = threshold;
self
}
pub fn autopull(mut self, autopull: bool) -> Self {
self.autopull = autopull;
self
}
pub fn pull_threshold(mut self, threshold: u8) -> Self {
self.pull_threshold = threshold;
self
}
pub fn in_shift_direction(mut self, direction: ShiftDirection) -> Self {
self.in_shiftdir = direction;
self
}
pub fn out_shift_direction(mut self, direction: ShiftDirection) -> Self {
self.out_shiftdir = direction;
self
}
#[allow(clippy::type_complexity)] pub fn build<SM: StateMachineIndex>(
self,
mut sm: UninitStateMachine<(P, SM)>,
) -> (StateMachine<(P, SM), Stopped>, Rx<(P, SM)>, Tx<(P, SM)>) {
let offset = self.program.offset;
sm.set_enabled(false);
sm.set_clock_divisor(self.clock_divisor.0, self.clock_divisor.1);
unsafe {
sm.sm().sm_execctrl.write(|w| {
w.side_en().bit(self.program.side_set.optional());
w.side_pindir().bit(self.program.side_set.pindirs());
w.jmp_pin().bits(self.jmp_pin);
if let Some(inline_out) = self.inline_out {
w.inline_out_en().bit(true);
w.out_en_sel().bits(inline_out);
} else {
w.inline_out_en().bit(false);
}
w.out_sticky().bit(self.out_sticky);
w.wrap_top().bits(offset + self.program.wrap.source);
w.wrap_bottom().bits(offset + self.program.wrap.target);
let n = match self.mov_status {
MovStatusConfig::Tx(n) => {
w.status_sel().bit(false);
n
}
MovStatusConfig::Rx(n) => {
w.status_sel().bit(true);
n
}
};
w.status_n().bits(n)
});
sm.sm().sm_shiftctrl.write(|w| {
let (fjoin_rx, fjoin_tx) = match self.fifo_join {
Buffers::RxTx => (false, false),
Buffers::OnlyTx => (false, true),
Buffers::OnlyRx => (true, false),
};
w.fjoin_rx().bit(fjoin_rx);
w.fjoin_tx().bit(fjoin_tx);
w.pull_thresh().bits(self.pull_threshold);
w.push_thresh().bits(self.push_threshold);
w.out_shiftdir().bit(self.out_shiftdir.bit());
w.in_shiftdir().bit(self.in_shiftdir.bit());
w.autopull().bit(self.autopull);
w.autopush().bit(self.autopush)
});
sm.sm().sm_pinctrl.write(|w| {
w.sideset_count().bits(self.program.side_set.bits());
w.set_count().bits(self.set_count);
w.out_count().bits(self.out_count);
w.in_base().bits(self.in_base);
w.sideset_base().bits(self.side_set_base);
w.set_base().bits(self.set_base);
w.out_base().bits(self.out_base)
})
}
sm.restart();
sm.reset_clock();
let instr = InstructionOperands::JMP {
condition: pio::JmpCondition::Always,
address: offset,
}
.encode();
unsafe {
sm.sm().sm_instr.write(|w| w.sm0_instr().bits(instr));
}
let rx = Rx {
block: sm.block,
_phantom: core::marker::PhantomData,
};
let tx = Tx {
block: sm.block,
_phantom: core::marker::PhantomData,
};
(
StateMachine {
sm,
program: self.program,
_phantom: core::marker::PhantomData,
},
rx,
tx,
)
}
}