1use core::future::Future;
3use core::marker::PhantomData;
4use core::pin::Pin as FuturePin;
5use core::sync::atomic::{AtomicU8, AtomicU32, Ordering};
6use core::task::{Context, Poll};
7
8use embassy_hal_internal::{Peri, PeripheralType};
9use embassy_sync::waitqueue::AtomicWaker;
10use fixed::FixedU32;
11use fixed::types::extra::U8;
12use pio::{Program, SideSet, Wrap};
13
14use crate::dma::{self, Transfer, Word};
15use crate::gpio::{self, AnyPin, Drive, Level, Pull, SealedPin, SlewRate};
16use crate::interrupt::typelevel::{Binding, Handler, Interrupt};
17use crate::relocate::RelocatedProgram;
18use crate::{RegExt, pac, peripherals};
19
20mod instr;
21
22#[doc(inline)]
23pub use pio as program;
24
25pub struct Wakers([AtomicWaker; 12]);
27
28impl Wakers {
29 #[inline(always)]
30 fn fifo_in(&self) -> &[AtomicWaker] {
31 &self.0[0..4]
32 }
33 #[inline(always)]
34 fn fifo_out(&self) -> &[AtomicWaker] {
35 &self.0[4..8]
36 }
37 #[inline(always)]
38 fn irq(&self) -> &[AtomicWaker] {
39 &self.0[8..12]
40 }
41}
42
43#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
45#[cfg_attr(feature = "defmt", derive(defmt::Format))]
46#[repr(u8)]
47pub enum FifoJoin {
48 #[default]
50 Duplex,
51 RxOnly,
53 TxOnly,
55 #[cfg(feature = "_rp235x")]
58 RxAsStatus,
59 #[cfg(feature = "_rp235x")]
62 RxAsControl,
63 #[cfg(feature = "_rp235x")]
66 PioScratch,
67}
68
69#[allow(missing_docs)]
71#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
72#[cfg_attr(feature = "defmt", derive(defmt::Format))]
73#[repr(u8)]
74pub enum ShiftDirection {
75 #[default]
76 Right = 1,
77 Left = 0,
78}
79
80#[allow(missing_docs)]
82#[derive(Clone, Copy, PartialEq, Eq, Debug)]
83#[cfg_attr(feature = "defmt", derive(defmt::Format))]
84#[repr(u8)]
85pub enum Direction {
86 In = 0,
87 Out = 1,
88}
89
90#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
92#[cfg_attr(feature = "defmt", derive(defmt::Format))]
93#[repr(u8)]
94pub enum StatusSource {
95 #[default]
96 TxFifoLevel = 0,
98 RxFifoLevel = 1,
100 #[cfg(feature = "_rp235x")]
102 Irq = 2,
103}
104
105const RXNEMPTY_MASK: u32 = 1 << 0;
106const TXNFULL_MASK: u32 = 1 << 4;
107const SMIRQ_MASK: u32 = 1 << 8;
108
109pub struct InterruptHandler<PIO: Instance> {
111 _pio: PhantomData<PIO>,
112}
113
114impl<PIO: Instance> Handler<PIO::Interrupt> for InterruptHandler<PIO> {
115 unsafe fn on_interrupt() {
116 let ints = PIO::PIO.irqs(0).ints().read().0;
117 for bit in 0..12 {
118 if ints & (1 << bit) != 0 {
119 PIO::wakers().0[bit].wake();
120 }
121 }
122 PIO::PIO.irqs(0).inte().write_clear(|m| m.0 = ints);
123 }
124}
125
126#[must_use = "futures do nothing unless you `.await` or poll them"]
128pub struct FifoOutFuture<'a, 'd, PIO: Instance, const SM: usize> {
129 sm_tx: &'a mut StateMachineTx<'d, PIO, SM>,
130 value: u32,
131}
132
133impl<'a, 'd, PIO: Instance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> {
134 pub fn new(sm: &'a mut StateMachineTx<'d, PIO, SM>, value: u32) -> Self {
136 FifoOutFuture { sm_tx: sm, value }
137 }
138}
139
140impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> {
141 type Output = ();
142 fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
143 let value = self.value;
145 if self.get_mut().sm_tx.try_push(value) {
146 Poll::Ready(())
147 } else {
148 PIO::wakers().fifo_out()[SM].register(cx.waker());
149 PIO::PIO.irqs(0).inte().write_set(|m| {
150 m.0 = TXNFULL_MASK << SM;
151 });
152 Poll::Pending
154 }
155 }
156}
157
158impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> {
159 fn drop(&mut self) {
160 PIO::PIO.irqs(0).inte().write_clear(|m| {
161 m.0 = TXNFULL_MASK << SM;
162 });
163 }
164}
165
166#[must_use = "futures do nothing unless you `.await` or poll them"]
168pub struct FifoInFuture<'a, 'd, PIO: Instance, const SM: usize> {
169 sm_rx: &'a mut StateMachineRx<'d, PIO, SM>,
170}
171
172impl<'a, 'd, PIO: Instance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> {
173 pub fn new(sm: &'a mut StateMachineRx<'d, PIO, SM>) -> Self {
175 FifoInFuture { sm_rx: sm }
176 }
177}
178
179impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> {
180 type Output = u32;
181 fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
182 if let Some(v) = self.sm_rx.try_pull() {
184 Poll::Ready(v)
185 } else {
186 PIO::wakers().fifo_in()[SM].register(cx.waker());
187 PIO::PIO.irqs(0).inte().write_set(|m| {
188 m.0 = RXNEMPTY_MASK << SM;
189 });
190 Poll::Pending
192 }
193 }
194}
195
196impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> {
197 fn drop(&mut self) {
198 PIO::PIO.irqs(0).inte().write_clear(|m| {
199 m.0 = RXNEMPTY_MASK << SM;
200 });
201 }
202}
203
204#[must_use = "futures do nothing unless you `.await` or poll them"]
206pub struct IrqFuture<'a, 'd, PIO: Instance> {
207 pio: PhantomData<&'a mut Irq<'d, PIO, 0>>,
208 irq_no: u8,
209}
210
211impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> {
212 type Output = ();
213 fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
214 if PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 {
218 PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no);
219 return Poll::Ready(());
220 }
221
222 PIO::wakers().irq()[self.irq_no as usize].register(cx.waker());
223 PIO::PIO.irqs(0).inte().write_set(|m| {
224 m.0 = SMIRQ_MASK << self.irq_no;
225 });
226 Poll::Pending
227 }
228}
229
230impl<'a, 'd, PIO: Instance> Drop for IrqFuture<'a, 'd, PIO> {
231 fn drop(&mut self) {
232 PIO::PIO.irqs(0).inte().write_clear(|m| {
233 m.0 = SMIRQ_MASK << self.irq_no;
234 });
235 }
236}
237
238pub struct Pin<'l, PIO: Instance> {
240 pin: Peri<'l, AnyPin>,
241 pio: PhantomData<PIO>,
242}
243
244impl<'l, PIO: Instance> Pin<'l, PIO> {
245 #[inline]
247 pub fn set_drive_strength(&mut self, strength: Drive) {
248 self.pin.pad_ctrl().modify(|w| {
249 w.set_drive(match strength {
250 Drive::_2mA => pac::pads::vals::Drive::_2M_A,
251 Drive::_4mA => pac::pads::vals::Drive::_4M_A,
252 Drive::_8mA => pac::pads::vals::Drive::_8M_A,
253 Drive::_12mA => pac::pads::vals::Drive::_12M_A,
254 });
255 });
256 }
257
258 #[inline]
260 pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
261 self.pin.pad_ctrl().modify(|w| {
262 w.set_slewfast(slew_rate == SlewRate::Fast);
263 });
264 }
265
266 #[inline]
268 pub fn set_pull(&mut self, pull: Pull) {
269 self.pin.pad_ctrl().modify(|w| {
270 w.set_pue(pull == Pull::Up);
271 w.set_pde(pull == Pull::Down);
272 });
273 }
274
275 #[inline]
277 pub fn set_schmitt(&mut self, enable: bool) {
278 self.pin.pad_ctrl().modify(|w| {
279 w.set_schmitt(enable);
280 });
281 }
282
283 #[inline]
285 pub fn set_output_inversion(&mut self, invert: bool) {
286 self.pin.gpio().ctrl().modify(|w| {
287 w.set_outover(if invert {
288 pac::io::vals::Outover::INVERT
289 } else {
290 pac::io::vals::Outover::NORMAL
291 })
292 });
293 }
294
295 #[inline]
297 pub fn set_output_enable_inversion(&mut self, invert: bool) {
298 self.pin.gpio().ctrl().modify(|w| {
299 w.set_oeover(if invert {
300 pac::io::vals::Oeover::INVERT
301 } else {
302 pac::io::vals::Oeover::NORMAL
303 })
304 })
305 }
306
307 pub fn set_input_sync_bypass(&mut self, bypass: bool) {
309 let mask = 1 << self.pin();
310 if bypass {
311 PIO::PIO.input_sync_bypass().write_set(|w| *w = mask);
312 } else {
313 PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask);
314 }
315 }
316
317 pub fn pin(&self) -> u8 {
319 self.pin._pin()
320 }
321}
322
323pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> {
325 pio: PhantomData<&'d mut PIO>,
326}
327
328impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> {
329 pub fn empty(&self) -> bool {
331 PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0
332 }
333
334 pub fn full(&self) -> bool {
336 PIO::PIO.fstat().read().rxfull() & (1u8 << SM) != 0
337 }
338
339 pub fn level(&self) -> u8 {
341 (PIO::PIO.flevel().read().0 >> (SM * 8 + 4)) as u8 & 0x0f
342 }
343
344 pub fn stalled(&self) -> bool {
346 let fdebug = PIO::PIO.fdebug();
347 let ret = fdebug.read().rxstall() & (1 << SM) != 0;
348 if ret {
349 fdebug.write(|w| w.set_rxstall(1 << SM));
350 }
351 ret
352 }
353
354 pub fn underflowed(&self) -> bool {
356 let fdebug = PIO::PIO.fdebug();
357 let ret = fdebug.read().rxunder() & (1 << SM) != 0;
358 if ret {
359 fdebug.write(|w| w.set_rxunder(1 << SM));
360 }
361 ret
362 }
363
364 pub fn pull(&mut self) -> u32 {
370 PIO::PIO.rxf(SM).read()
371 }
372
373 pub fn try_pull(&mut self) -> Option<u32> {
375 if self.empty() {
376 return None;
377 }
378 Some(self.pull())
379 }
380
381 pub fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, 'd, PIO, SM> {
383 FifoInFuture::new(self)
384 }
385
386 fn dreq() -> crate::pac::dma::vals::TreqSel {
387 crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8 + 4)
388 }
389
390 pub fn dma_pull<'a, W: Word>(
392 &'a mut self,
393 ch: &'a mut dma::Channel<'_>,
394 data: &'a mut [W],
395 bswap: bool,
396 ) -> Transfer<'a> {
397 unsafe { ch.read(PIO::PIO.rxf(SM).as_ptr() as *const W, data, Self::dreq(), bswap) }
398 }
399
400 pub fn dma_pull_discard<'a, W: Word>(&'a mut self, ch: &'a mut dma::Channel<'_>, len: usize) -> Transfer<'a> {
402 unsafe { ch.read_discard(PIO::PIO.rxf(SM).as_ptr(), len, Self::dreq()) }
403 }
404}
405
406pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> {
408 pio: PhantomData<&'d mut PIO>,
409}
410
411impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> {
412 pub fn empty(&self) -> bool {
414 PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0
415 }
416
417 pub fn full(&self) -> bool {
419 PIO::PIO.fstat().read().txfull() & (1u8 << SM) != 0
420 }
421
422 pub fn level(&self) -> u8 {
424 (PIO::PIO.flevel().read().0 >> (SM * 8)) as u8 & 0x0f
425 }
426
427 pub fn stalled(&self) -> bool {
429 let fdebug = PIO::PIO.fdebug();
430 let ret = fdebug.read().txstall() & (1 << SM) != 0;
431 if ret {
432 fdebug.write(|w| w.set_txstall(1 << SM));
433 }
434 ret
435 }
436
437 pub fn overflowed(&self) -> bool {
439 let fdebug = PIO::PIO.fdebug();
440 let ret = fdebug.read().txover() & (1 << SM) != 0;
441 if ret {
442 fdebug.write(|w| w.set_txover(1 << SM));
443 }
444 ret
445 }
446
447 pub fn push(&mut self, v: u32) {
449 PIO::PIO.txf(SM).write_value(v);
450 }
451
452 pub fn try_push(&mut self, v: u32) -> bool {
454 if self.full() {
455 return false;
456 }
457 self.push(v);
458 true
459 }
460
461 pub fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, 'd, PIO, SM> {
463 FifoOutFuture::new(self, value)
464 }
465
466 fn dreq() -> crate::pac::dma::vals::TreqSel {
467 crate::pac::dma::vals::TreqSel::from(PIO::PIO_NO * 8 + SM as u8)
468 }
469
470 pub fn dma_push<'a, W: Word>(
472 &'a mut self,
473 ch: &'a mut dma::Channel<'_>,
474 data: &'a [W],
475 bswap: bool,
476 ) -> Transfer<'a> {
477 unsafe { ch.write(data, PIO::PIO.txf(SM).as_ptr() as *mut W, Self::dreq(), bswap) }
478 }
479
480 pub fn dma_push_zeros<'a, W: Word>(&'a mut self, ch: &'a mut dma::Channel<'_>, len: usize) -> Transfer<'a> {
482 unsafe { ch.write_zeros(len, PIO::PIO.txf(SM).as_ptr() as *mut W, Self::dreq()) }
483 }
484}
485
486pub struct StateMachine<'d, PIO: Instance, const SM: usize> {
488 rx: StateMachineRx<'d, PIO, SM>,
489 tx: StateMachineTx<'d, PIO, SM>,
490}
491
492impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> {
493 fn drop(&mut self) {
494 PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM));
495 on_pio_drop::<PIO>();
496 }
497}
498
499fn assert_consecutive<PIO: Instance>(pins: &[&Pin<PIO>]) {
500 for (p1, p2) in pins.iter().zip(pins.iter().skip(1)) {
501 assert!(p1.pin() + 1 == p2.pin(), "pins must be consecutive");
503 }
504}
505
506#[derive(Clone, Copy, Default, Debug)]
508#[cfg_attr(feature = "defmt", derive(defmt::Format))]
509#[non_exhaustive]
510pub struct ExecConfig {
511 pub side_en: bool,
513 pub side_pindir: bool,
515 pub jmp_pin: u8,
517 pub wrap_top: u8,
519 pub wrap_bottom: u8,
521}
522
523#[derive(Clone, Copy, Default, Debug)]
525#[cfg_attr(feature = "defmt", derive(defmt::Format))]
526pub struct ShiftConfig {
527 pub threshold: u8,
529 pub direction: ShiftDirection,
531 pub auto_fill: bool,
534}
535
536#[derive(Clone, Copy, Default, Debug)]
538#[cfg_attr(feature = "defmt", derive(defmt::Format))]
539pub struct PinConfig {
540 pub sideset_count: u8,
542 pub set_count: u8,
544 pub out_count: u8,
546 pub in_base: u8,
548 pub sideset_base: u8,
550 pub set_base: u8,
552 pub out_base: u8,
554}
555
556#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
558#[cfg_attr(feature = "defmt", derive(defmt::Format))]
559#[cfg(feature = "_rp235x")]
560pub enum StatusN {
561 This(u8),
563 Lower(u8),
565 Higher(u8),
567}
568
569#[cfg(feature = "_rp235x")]
570impl Default for StatusN {
571 fn default() -> Self {
572 Self::This(0)
573 }
574}
575
576#[cfg(feature = "_rp235x")]
577impl Into<crate::pac::pio::vals::ExecctrlStatusN> for StatusN {
578 fn into(self) -> crate::pac::pio::vals::ExecctrlStatusN {
579 let x = match self {
580 StatusN::This(n) => n,
581 StatusN::Lower(n) => n + 0x08,
582 StatusN::Higher(n) => n + 0x10,
583 };
584
585 crate::pac::pio::vals::ExecctrlStatusN(x)
586 }
587}
588
589#[derive(Clone, Copy, Debug)]
591pub struct Config<'d, PIO: Instance> {
592 pub clock_divider: FixedU32<U8>,
594 pub out_en_sel: u8,
596 pub inline_out_en: bool,
598 pub out_sticky: bool,
600 pub status_sel: StatusSource,
602 #[cfg(feature = "rp2040")]
604 pub status_n: u8,
605 #[cfg(feature = "_rp235x")]
607 pub status_n: StatusN,
609 exec: ExecConfig,
610 origin: Option<u8>,
611 pub fifo_join: FifoJoin,
613 pub shift_in: ShiftConfig,
615 pub shift_out: ShiftConfig,
617 pins: PinConfig,
619 in_count: u8,
620 _pio: PhantomData<&'d mut PIO>,
621}
622
623impl<'d, PIO: Instance> Default for Config<'d, PIO> {
624 fn default() -> Self {
625 Self {
626 clock_divider: 1u8.into(),
627 out_en_sel: Default::default(),
628 inline_out_en: Default::default(),
629 out_sticky: Default::default(),
630 status_sel: Default::default(),
631 status_n: Default::default(),
632 exec: Default::default(),
633 origin: Default::default(),
634 fifo_join: Default::default(),
635 shift_in: Default::default(),
636 shift_out: Default::default(),
637 pins: Default::default(),
638 in_count: Default::default(),
639 _pio: Default::default(),
640 }
641 }
642}
643
644impl<'d, PIO: Instance> Config<'d, PIO> {
645 pub fn get_exec(&self) -> ExecConfig {
647 self.exec
648 }
649
650 pub unsafe fn set_exec(&mut self, e: ExecConfig) {
652 self.exec = e;
653 }
654
655 pub fn get_pins(&self) -> PinConfig {
657 self.pins
658 }
659
660 pub unsafe fn set_pins(&mut self, p: PinConfig) {
662 self.pins = p;
663 }
664
665 pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) {
672 assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len());
673 assert_consecutive(side_set);
674 self.exec.side_en = prog.side_set.optional();
675 self.exec.side_pindir = prog.side_set.pindirs();
676 self.exec.wrap_bottom = prog.wrap.target;
677 self.exec.wrap_top = prog.wrap.source;
678 self.pins.sideset_count = prog.side_set.bits();
679 self.pins.sideset_base = side_set.first().map_or(0, |p| p.pin());
680 self.origin = Some(prog.origin);
681 }
682
683 pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) {
685 self.exec.jmp_pin = pin.pin();
686 }
687
688 pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
692 assert!(pins.len() <= 5);
693 assert_consecutive(pins);
694 self.pins.set_base = pins.first().map_or(0, |p| p.pin());
695 self.pins.set_count = pins.len() as u8;
696 }
697
698 pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
702 assert_consecutive(pins);
703 self.pins.out_base = pins.first().map_or(0, |p| p.pin());
704 self.pins.out_count = pins.len() as u8;
705 }
706
707 pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) {
711 assert_consecutive(pins);
712 self.pins.in_base = pins.first().map_or(0, |p| p.pin());
713 self.in_count = pins.len() as u8;
714 }
715}
716
717impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
718 pub fn set_config(&mut self, config: &Config<'d, PIO>) {
720 assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536");
722 assert!(config.clock_divider >= 1, "clkdiv must be >= 1");
723 assert!(config.out_en_sel < 32, "out_en_sel must be < 32");
724 assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32");
727 assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32");
728 let sm = Self::this_sm();
729 sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8);
730 sm.execctrl().write(|w| {
731 w.set_side_en(config.exec.side_en);
732 w.set_side_pindir(config.exec.side_pindir);
733 w.set_jmp_pin(config.exec.jmp_pin);
734 w.set_out_en_sel(config.out_en_sel);
735 w.set_inline_out_en(config.inline_out_en);
736 w.set_out_sticky(config.out_sticky);
737 w.set_wrap_top(config.exec.wrap_top);
738 w.set_wrap_bottom(config.exec.wrap_bottom);
739 #[cfg(feature = "_rp235x")]
740 w.set_status_sel(match config.status_sel {
741 StatusSource::TxFifoLevel => pac::pio::vals::ExecctrlStatusSel::TXLEVEL,
742 StatusSource::RxFifoLevel => pac::pio::vals::ExecctrlStatusSel::RXLEVEL,
743 StatusSource::Irq => pac::pio::vals::ExecctrlStatusSel::IRQ,
744 });
745 #[cfg(feature = "rp2040")]
746 w.set_status_sel(match config.status_sel {
747 StatusSource::TxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::TXLEVEL,
748 StatusSource::RxFifoLevel => pac::pio::vals::SmExecctrlStatusSel::RXLEVEL,
749 });
750 w.set_status_n(config.status_n.into());
751 });
752 sm.shiftctrl().write(|w| {
753 w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly);
754 w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly);
755 w.set_pull_thresh(config.shift_out.threshold);
756 w.set_push_thresh(config.shift_in.threshold);
757 w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right);
758 w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right);
759 w.set_autopull(config.shift_out.auto_fill);
760 w.set_autopush(config.shift_in.auto_fill);
761
762 #[cfg(feature = "_rp235x")]
763 {
764 w.set_fjoin_rx_get(
765 config.fifo_join == FifoJoin::RxAsControl || config.fifo_join == FifoJoin::PioScratch,
766 );
767 w.set_fjoin_rx_put(
768 config.fifo_join == FifoJoin::RxAsStatus || config.fifo_join == FifoJoin::PioScratch,
769 );
770 w.set_in_count(config.in_count);
771 }
772 });
773
774 #[cfg(feature = "rp2040")]
775 sm.pinctrl().write(|w| {
776 w.set_sideset_count(config.pins.sideset_count);
777 w.set_set_count(config.pins.set_count);
778 w.set_out_count(config.pins.out_count);
779 w.set_in_base(config.pins.in_base);
780 w.set_sideset_base(config.pins.sideset_base);
781 w.set_set_base(config.pins.set_base);
782 w.set_out_base(config.pins.out_base);
783 });
784
785 #[cfg(feature = "_rp235x")]
786 {
787 let mut low_ok = true;
788 let mut high_ok = true;
789
790 let in_pins = config.pins.in_base..config.pins.in_base + config.in_count;
791 let side_pins = config.pins.sideset_base..config.pins.sideset_base + config.pins.sideset_count;
792 let set_pins = config.pins.set_base..config.pins.set_base + config.pins.set_count;
793 let out_pins = config.pins.out_base..config.pins.out_base + config.pins.out_count;
794
795 for pin_range in [in_pins, side_pins, set_pins, out_pins] {
796 for pin in pin_range {
797 low_ok &= pin < 32;
798 high_ok &= pin >= 16;
799 }
800 }
801
802 if !low_ok && !high_ok {
803 panic!(
804 "All pins must either be <32 or >=16, in:{:?}-{:?}, side:{:?}-{:?}, set:{:?}-{:?}, out:{:?}-{:?}",
805 config.pins.in_base,
806 config.pins.in_base + config.in_count - 1,
807 config.pins.sideset_base,
808 config.pins.sideset_base + config.pins.sideset_count - 1,
809 config.pins.set_base,
810 config.pins.set_base + config.pins.set_count - 1,
811 config.pins.out_base,
812 config.pins.out_base + config.pins.out_count - 1,
813 )
814 }
815 let shift = if low_ok { 0 } else { 16 };
816
817 sm.pinctrl().write(|w| {
818 w.set_sideset_count(config.pins.sideset_count);
819 w.set_set_count(config.pins.set_count);
820 w.set_out_count(config.pins.out_count);
821 w.set_in_base(config.pins.in_base.checked_sub(shift).unwrap_or_default());
822 w.set_sideset_base(config.pins.sideset_base.checked_sub(shift).unwrap_or_default());
823 w.set_set_base(config.pins.set_base.checked_sub(shift).unwrap_or_default());
824 w.set_out_base(config.pins.out_base.checked_sub(shift).unwrap_or_default());
825 });
826
827 PIO::PIO.gpiobase().write(|w| w.set_gpiobase(shift == 16));
828 }
829
830 if let Some(origin) = config.origin {
831 unsafe { self.exec_jmp(origin) }
832 }
833 }
834
835 pub fn rx_fifo_ptr(&self) -> *mut u32 {
837 PIO::PIO.rxf(SM).as_ptr()
838 }
839
840 pub fn tx_fifo_ptr(&self) -> *mut u32 {
842 PIO::PIO.txf(SM).as_ptr()
843 }
844
845 pub fn rx_treq(&self) -> crate::pac::dma::vals::TreqSel {
847 StateMachineRx::<PIO, SM>::dreq()
848 }
849
850 pub fn tx_treq(&self) -> crate::pac::dma::vals::TreqSel {
852 StateMachineTx::<PIO, SM>::dreq()
853 }
854
855 pub fn get_addr(&self) -> u8 {
857 let addr = Self::this_sm().addr();
858 addr.read().addr()
859 }
860
861 pub fn get_tx_threshold(&self) -> u8 {
863 let shiftctrl = Self::this_sm().shiftctrl();
864 shiftctrl.read().pull_thresh()
865 }
866
867 pub fn set_tx_threshold(&mut self, threshold: u8) {
869 assert!(threshold <= 31);
870 let shiftctrl = Self::this_sm().shiftctrl();
871 shiftctrl.modify(|w| {
872 w.set_pull_thresh(threshold);
873 });
874 }
875
876 pub fn get_rx_threshold(&self) -> u8 {
878 Self::this_sm().shiftctrl().read().push_thresh()
879 }
880
881 pub fn set_rx_threshold(&mut self, threshold: u8) {
883 assert!(threshold <= 31);
884 let shiftctrl = Self::this_sm().shiftctrl();
885 shiftctrl.modify(|w| {
886 w.set_push_thresh(threshold);
887 });
888 }
889
890 pub fn set_thresholds(&mut self, threshold: u8) {
892 assert!(threshold <= 31);
893 let shiftctrl = Self::this_sm().shiftctrl();
894 shiftctrl.modify(|w| {
895 w.set_push_thresh(threshold);
896 w.set_pull_thresh(threshold);
897 });
898 }
899
900 pub fn set_clock_divider(&mut self, clock_divider: FixedU32<U8>) {
902 let sm = Self::this_sm();
903 sm.clkdiv().write(|w| w.0 = clock_divider.to_bits() << 8);
904 }
905
906 #[inline(always)]
907 fn this_sm() -> crate::pac::pio::StateMachine {
908 PIO::PIO.sm(SM)
909 }
910
911 pub fn restart(&mut self) {
913 let mask = 1u8 << SM;
914 PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask));
915 }
916
917 pub fn set_enable(&mut self, enable: bool) {
919 let mask = 1u8 << SM;
920 if enable {
921 PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask));
922 } else {
923 PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask));
924 }
925 }
926
927 pub fn is_enabled(&self) -> bool {
929 PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0
930 }
931
932 pub fn clkdiv_restart(&mut self) {
934 let mask = 1u8 << SM;
935 PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask));
936 }
937
938 fn with_paused(&mut self, f: impl FnOnce(&mut Self)) {
939 let enabled = self.is_enabled();
940 self.set_enable(false);
941 let pincfg = Self::this_sm().pinctrl().read();
942 let execcfg = Self::this_sm().execctrl().read();
943 Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true));
944 f(self);
945 Self::this_sm().pinctrl().write_value(pincfg);
946 Self::this_sm().execctrl().write_value(execcfg);
947 self.set_enable(enabled);
948 }
949
950 #[cfg(feature = "rp2040")]
951 fn pin_base() -> u8 {
952 0
953 }
954
955 #[cfg(feature = "_rp235x")]
956 fn pin_base() -> u8 {
957 if PIO::PIO.gpiobase().read().gpiobase() { 16 } else { 0 }
958 }
959
960 pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) {
963 self.with_paused(|sm| {
964 for pin in pins {
965 Self::this_sm().pinctrl().write(|w| {
966 w.set_set_base(pin.pin() - Self::pin_base());
967 w.set_set_count(1);
968 });
969 unsafe { sm.exec_instr(0b111_00000_100_00000 | dir as u16) };
971 }
972 });
973 }
974
975 pub fn set_pins(&mut self, level: Level, pins: &[&Pin<'d, PIO>]) {
978 self.with_paused(|sm| {
979 for pin in pins {
980 Self::this_sm().pinctrl().write(|w| {
981 w.set_set_base(pin.pin() - Self::pin_base());
982 w.set_set_count(1);
983 });
984 unsafe { sm.exec_instr(0b11100_000_000_00000 | level as u16) };
986 }
987 });
988 }
989
990 pub fn clear_fifos(&mut self) {
992 let shiftctrl = Self::this_sm().shiftctrl();
994 shiftctrl.modify(|w| {
995 w.set_fjoin_rx(!w.fjoin_rx());
996 });
997 shiftctrl.modify(|w| {
998 w.set_fjoin_rx(!w.fjoin_rx());
999 });
1000 }
1001
1002 pub unsafe fn exec_instr(&mut self, instr: u16) {
1007 Self::this_sm().instr().write(|w| w.set_instr(instr));
1008 }
1009
1010 pub fn rx(&mut self) -> &mut StateMachineRx<'d, PIO, SM> {
1012 &mut self.rx
1013 }
1014
1015 pub fn tx(&mut self) -> &mut StateMachineTx<'d, PIO, SM> {
1017 &mut self.tx
1018 }
1019
1020 pub fn rx_tx(&mut self) -> (&mut StateMachineRx<'d, PIO, SM>, &mut StateMachineTx<'d, PIO, SM>) {
1022 (&mut self.rx, &mut self.tx)
1023 }
1024
1025 #[cfg(feature = "_rp235x")]
1028 pub fn get_rxf_entry(&self, n: usize) -> u32 {
1029 PIO::PIO.rxf_putget(SM).putget(n).read()
1030 }
1031
1032 #[cfg(feature = "_rp235x")]
1035 pub fn set_rxf_entry(&self, n: usize, val: u32) {
1036 PIO::PIO.rxf_putget(SM).putget(n).write_value(val)
1037 }
1038}
1039
1040pub struct Common<'d, PIO: Instance> {
1042 instructions_used: u32,
1043 pio: PhantomData<&'d mut PIO>,
1044}
1045
1046impl<'d, PIO: Instance> Drop for Common<'d, PIO> {
1047 fn drop(&mut self) {
1048 on_pio_drop::<PIO>();
1049 }
1050}
1051
1052pub struct InstanceMemory<'d, PIO: Instance> {
1054 used_mask: u32,
1055 pio: PhantomData<&'d mut PIO>,
1056}
1057
1058pub struct LoadedProgram<'d, PIO: Instance> {
1060 pub used_memory: InstanceMemory<'d, PIO>,
1062 pub origin: u8,
1064 pub wrap: Wrap,
1066 pub side_set: SideSet,
1068}
1069
1070#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1072#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1073pub enum LoadError {
1074 InsufficientSpace,
1076 AddressInUse(usize),
1079}
1080
1081impl<'d, PIO: Instance> Common<'d, PIO> {
1082 pub fn load_program<const SIZE: usize>(&mut self, prog: &Program<SIZE>) -> LoadedProgram<'d, PIO> {
1087 match self.try_load_program(prog) {
1088 Ok(r) => r,
1089 Err(e) => panic!("Failed to load PIO program: {:?}", e),
1090 }
1091 }
1092
1093 pub fn try_load_program<const SIZE: usize>(
1098 &mut self,
1099 prog: &Program<SIZE>,
1100 ) -> Result<LoadedProgram<'d, PIO>, LoadError> {
1101 match prog.origin {
1102 Some(origin) => self.try_load_program_at(prog, origin).map_err(LoadError::AddressInUse),
1103 None => {
1104 let mut origin = 0;
1108 while origin < 32 {
1109 match self.try_load_program_at(prog, origin as _) {
1110 Ok(r) => return Ok(r),
1111 Err(a) => origin = a + 1,
1112 }
1113 }
1114 Err(LoadError::InsufficientSpace)
1115 }
1116 }
1117 }
1118
1119 fn try_load_program_at<const SIZE: usize>(
1120 &mut self,
1121 prog: &Program<SIZE>,
1122 origin: u8,
1123 ) -> Result<LoadedProgram<'d, PIO>, usize> {
1124 #[cfg(not(feature = "_rp235x"))]
1125 assert!(prog.version == pio::PioVersion::V0);
1126
1127 let prog = RelocatedProgram::new_with_origin(prog, origin);
1128 let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?;
1129 Ok(LoadedProgram {
1130 used_memory,
1131 origin: prog.origin(),
1132 wrap: prog.wrap(),
1133 side_set: prog.side_set(),
1134 })
1135 }
1136
1137 fn try_write_instr<I>(&mut self, start: usize, instrs: I) -> Result<InstanceMemory<'d, PIO>, usize>
1138 where
1139 I: Iterator<Item = u16>,
1140 {
1141 let mut used_mask = 0;
1142 for (i, instr) in instrs.enumerate() {
1143 let addr = (i + start) % 32;
1145 let mask = 1 << addr;
1146 if (self.instructions_used | used_mask) & mask != 0 {
1147 return Err(addr);
1148 }
1149 PIO::PIO.instr_mem(addr).write(|w| {
1150 w.set_instr_mem(instr);
1151 });
1152 used_mask |= mask;
1153 }
1154 self.instructions_used |= used_mask;
1155 Ok(InstanceMemory {
1156 used_mask,
1157 pio: PhantomData,
1158 })
1159 }
1160
1161 pub unsafe fn free_instr(&mut self, instrs: InstanceMemory<PIO>) {
1164 self.instructions_used &= !instrs.used_mask;
1165 }
1166
1167 pub fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) {
1169 PIO::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass);
1173 PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass);
1174 }
1175
1176 pub fn get_input_sync_bypass(&self) -> u32 {
1178 PIO::PIO.input_sync_bypass().read()
1179 }
1180
1181 pub fn make_pio_pin(&mut self, pin: Peri<'d, impl PioPin + 'd>) -> Pin<'d, PIO> {
1186 pin.pad_ctrl().write(|w| w.set_od(false));
1188 pin.pad_ctrl().write(|w| w.set_ie(true));
1190
1191 pin.gpio().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL as _));
1192 pin.pad_ctrl().write(|w| {
1193 #[cfg(feature = "_rp235x")]
1194 w.set_iso(false);
1195 w.set_schmitt(true);
1196 w.set_slewfast(false);
1197 w.set_ie(true);
1200 w.set_od(false);
1201 w.set_pue(false);
1202 w.set_pde(false);
1203 });
1204 critical_section::with(|_| {
1206 let val = PIO::state().used_pins.load(Ordering::Relaxed);
1207 PIO::state()
1208 .used_pins
1209 .store(val | 1 << pin.pin_bank(), Ordering::Relaxed);
1210 });
1211
1212 Pin {
1213 pin: pin.into(),
1214 pio: PhantomData::default(),
1215 }
1216 }
1217}
1218
1219pub struct PioBatch<'a, PIO: Instance> {
1221 clkdiv_restart: u8,
1222 sm_restart: u8,
1223 sm_enable_mask: u8,
1224 sm_enable: u8,
1225 _pio: PhantomData<&'a PIO>,
1226}
1227
1228impl<'a, PIO: Instance> PioBatch<'a, PIO> {
1229 pub fn new() -> Self {
1231 Self {
1232 clkdiv_restart: 0,
1233 sm_restart: 0,
1234 sm_enable_mask: 0,
1235 sm_enable: 0,
1236 _pio: PhantomData,
1237 }
1238 }
1239
1240 pub fn restart<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>) {
1242 self.clkdiv_restart |= 1 << SM;
1243 }
1244
1245 pub fn set_enable<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>, enable: bool) {
1247 self.sm_enable_mask |= 1 << SM;
1248 self.sm_enable |= (enable as u8) << SM;
1249 }
1250
1251 pub fn execute(&mut self) {
1253 PIO::PIO.ctrl().modify(|w| {
1254 w.set_clkdiv_restart(self.clkdiv_restart);
1255 w.set_sm_restart(self.sm_restart);
1256 w.set_sm_enable((w.sm_enable() & !self.sm_enable_mask) | self.sm_enable);
1257 });
1258 }
1259}
1260
1261pub struct Irq<'d, PIO: Instance, const N: usize> {
1263 pio: PhantomData<&'d mut PIO>,
1264}
1265
1266impl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> {
1267 pub fn wait<'a>(&'a mut self) -> IrqFuture<'a, 'd, PIO> {
1269 IrqFuture {
1270 pio: PhantomData,
1271 irq_no: N as u8,
1272 }
1273 }
1274}
1275
1276#[derive(Clone)]
1278pub struct IrqFlags<'d, PIO: Instance> {
1279 pio: PhantomData<&'d mut PIO>,
1280}
1281
1282impl<'d, PIO: Instance> IrqFlags<'d, PIO> {
1283 pub fn check(&self, irq_no: u8) -> bool {
1285 assert!(irq_no < 8);
1286 self.check_any(1 << irq_no)
1287 }
1288
1289 pub fn check_any(&self, irqs: u8) -> bool {
1291 PIO::PIO.irq().read().irq() & irqs != 0
1292 }
1293
1294 pub fn check_all(&self, irqs: u8) -> bool {
1296 PIO::PIO.irq().read().irq() & irqs == irqs
1297 }
1298
1299 pub fn clear(&self, irq_no: usize) {
1301 assert!(irq_no < 8);
1302 self.clear_all(1 << irq_no);
1303 }
1304
1305 pub fn clear_all(&self, irqs: u8) {
1307 PIO::PIO.irq().write(|w| w.set_irq(irqs))
1308 }
1309
1310 pub fn set(&self, irq_no: usize) {
1312 assert!(irq_no < 8);
1313 self.set_all(1 << irq_no);
1314 }
1315
1316 pub fn set_all(&self, irqs: u8) {
1318 PIO::PIO.irq_force().write(|w| w.set_irq_force(irqs))
1319 }
1320}
1321
1322pub struct Pio<'d, PIO: Instance> {
1324 pub common: Common<'d, PIO>,
1326 pub irq_flags: IrqFlags<'d, PIO>,
1328 pub irq0: Irq<'d, PIO, 0>,
1330 pub irq1: Irq<'d, PIO, 1>,
1332 pub irq2: Irq<'d, PIO, 2>,
1334 pub irq3: Irq<'d, PIO, 3>,
1336 pub sm0: StateMachine<'d, PIO, 0>,
1338 pub sm1: StateMachine<'d, PIO, 1>,
1340 pub sm2: StateMachine<'d, PIO, 2>,
1342 pub sm3: StateMachine<'d, PIO, 3>,
1344 _pio: PhantomData<&'d mut PIO>,
1345}
1346
1347impl<'d, PIO: Instance> Pio<'d, PIO> {
1348 pub fn new(_pio: Peri<'d, PIO>, _irq: impl Binding<PIO::Interrupt, InterruptHandler<PIO>>) -> Self {
1350 PIO::state().users.store(5, Ordering::Release);
1351 PIO::state().used_pins.store(0, Ordering::Release);
1352 PIO::Interrupt::unpend();
1353
1354 unsafe { PIO::Interrupt::enable() };
1355 Self {
1356 common: Common {
1357 instructions_used: 0,
1358 pio: PhantomData,
1359 },
1360 irq_flags: IrqFlags { pio: PhantomData },
1361 irq0: Irq { pio: PhantomData },
1362 irq1: Irq { pio: PhantomData },
1363 irq2: Irq { pio: PhantomData },
1364 irq3: Irq { pio: PhantomData },
1365 sm0: StateMachine {
1366 rx: StateMachineRx { pio: PhantomData },
1367 tx: StateMachineTx { pio: PhantomData },
1368 },
1369 sm1: StateMachine {
1370 rx: StateMachineRx { pio: PhantomData },
1371 tx: StateMachineTx { pio: PhantomData },
1372 },
1373 sm2: StateMachine {
1374 rx: StateMachineRx { pio: PhantomData },
1375 tx: StateMachineTx { pio: PhantomData },
1376 },
1377 sm3: StateMachine {
1378 rx: StateMachineRx { pio: PhantomData },
1379 tx: StateMachineTx { pio: PhantomData },
1380 },
1381 _pio: PhantomData,
1382 }
1383 }
1384}
1385
1386struct AtomicU64 {
1387 upper_32: AtomicU32,
1388 lower_32: AtomicU32,
1389}
1390
1391impl AtomicU64 {
1392 const fn new(val: u64) -> Self {
1393 let upper_32 = (val >> 32) as u32;
1394 let lower_32 = val as u32;
1395
1396 Self {
1397 upper_32: AtomicU32::new(upper_32),
1398 lower_32: AtomicU32::new(lower_32),
1399 }
1400 }
1401
1402 fn load(&self, order: Ordering) -> u64 {
1403 let (upper, lower) = critical_section::with(|_| (self.upper_32.load(order), self.lower_32.load(order)));
1404
1405 let upper = (upper as u64) << 32;
1406 let lower = lower as u64;
1407
1408 upper | lower
1409 }
1410
1411 fn store(&self, val: u64, order: Ordering) {
1412 let upper_32 = (val >> 32) as u32;
1413 let lower_32 = val as u32;
1414
1415 critical_section::with(|_| {
1416 self.upper_32.store(upper_32, order);
1417 self.lower_32.store(lower_32, order);
1418 });
1419 }
1420}
1421
1422pub struct State {
1430 users: AtomicU8,
1431 used_pins: AtomicU64,
1432}
1433
1434fn on_pio_drop<PIO: Instance>() {
1435 let state = PIO::state();
1436 let users_state = critical_section::with(|_| {
1437 let val = state.users.load(Ordering::Acquire);
1438 state.users.store(val - 1, Ordering::Release);
1439 val
1440 });
1441 if users_state == 1 {
1442 let used_pins = state.used_pins.load(Ordering::Relaxed);
1443 let null = pac::io::vals::Gpio0ctrlFuncsel::NULL as _;
1444 for i in 0..crate::gpio::BANK0_PIN_COUNT {
1445 if used_pins & (1 << i) != 0 {
1446 pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null));
1447 }
1448 }
1449 }
1450}
1451
1452trait SealedInstance {
1453 const PIO_NO: u8;
1454 const PIO: &'static crate::pac::pio::Pio;
1455 const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel;
1456
1457 #[inline]
1458 fn wakers() -> &'static Wakers {
1459 static WAKERS: Wakers = Wakers([const { AtomicWaker::new() }; 12]);
1460 &WAKERS
1461 }
1462
1463 #[inline]
1464 fn state() -> &'static State {
1465 static STATE: State = State {
1466 users: AtomicU8::new(0),
1467 used_pins: AtomicU64::new(0),
1468 };
1469
1470 &STATE
1471 }
1472}
1473
1474#[allow(private_bounds)]
1476pub trait Instance: SealedInstance + PeripheralType + Sized + Unpin {
1477 type Interrupt: crate::interrupt::typelevel::Interrupt;
1479}
1480
1481macro_rules! impl_pio {
1482 ($name:ident, $pio:expr, $pac:ident, $funcsel:ident, $irq:ident) => {
1483 impl SealedInstance for peripherals::$name {
1484 const PIO_NO: u8 = $pio;
1485 const PIO: &'static pac::pio::Pio = &pac::$pac;
1486 const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel;
1487 }
1488 impl Instance for peripherals::$name {
1489 type Interrupt = crate::interrupt::typelevel::$irq;
1490 }
1491 };
1492}
1493
1494impl_pio!(PIO0, 0, PIO0, PIO0_0, PIO0_IRQ_0);
1495impl_pio!(PIO1, 1, PIO1, PIO1_0, PIO1_IRQ_0);
1496#[cfg(feature = "_rp235x")]
1497impl_pio!(PIO2, 2, PIO2, PIO2_0, PIO2_IRQ_0);
1498
1499pub trait PioPin: gpio::Pin {}
1501
1502macro_rules! impl_pio_pin {
1503 ($( $pin:ident, )*) => {
1504 $(
1505 impl PioPin for peripherals::$pin {}
1506 )*
1507 };
1508}
1509
1510impl_pio_pin! {
1511 PIN_0,
1512 PIN_1,
1513 PIN_2,
1514 PIN_3,
1515 PIN_4,
1516 PIN_5,
1517 PIN_6,
1518 PIN_7,
1519 PIN_8,
1520 PIN_9,
1521 PIN_10,
1522 PIN_11,
1523 PIN_12,
1524 PIN_13,
1525 PIN_14,
1526 PIN_15,
1527 PIN_16,
1528 PIN_17,
1529 PIN_18,
1530 PIN_19,
1531 PIN_20,
1532 PIN_21,
1533 PIN_22,
1534 PIN_23,
1535 PIN_24,
1536 PIN_25,
1537 PIN_26,
1538 PIN_27,
1539 PIN_28,
1540 PIN_29,
1541}
1542
1543#[cfg(feature = "rp235xb")]
1544impl_pio_pin! {
1545 PIN_30,
1546 PIN_31,
1547 PIN_32,
1548 PIN_33,
1549 PIN_34,
1550 PIN_35,
1551 PIN_36,
1552 PIN_37,
1553 PIN_38,
1554 PIN_39,
1555 PIN_40,
1556 PIN_41,
1557 PIN_42,
1558 PIN_43,
1559 PIN_44,
1560 PIN_45,
1561 PIN_46,
1562 PIN_47,
1563}