diff options
| -rw-r--r-- | embassy-rp/src/pio.rs | 810 | ||||
| -rw-r--r-- | embassy-rp/src/pio_instr_util.rs | 20 | ||||
| -rw-r--r-- | embassy-rp/src/relocate.rs | 10 | ||||
| -rw-r--r-- | examples/rp/Cargo.toml | 2 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_async.rs | 62 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_dma.rs | 32 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_hd44780.rs | 66 | ||||
| -rw-r--r-- | examples/rp/src/bin/ws2812-pio.rs | 58 |
8 files changed, 564 insertions, 496 deletions
diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 2cf4761a5..1e41bed30 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs | |||
| @@ -6,15 +6,21 @@ use core::task::{Context, Poll}; | |||
| 6 | 6 | ||
| 7 | use atomic_polyfill::{AtomicU32, AtomicU8}; | 7 | use atomic_polyfill::{AtomicU32, AtomicU8}; |
| 8 | use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; | 8 | use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; |
| 9 | use embassy_embedded_hal::SetConfig; | ||
| 9 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; | 10 | use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; |
| 10 | use embassy_sync::waitqueue::AtomicWaker; | 11 | use embassy_sync::waitqueue::AtomicWaker; |
| 12 | use fixed::types::extra::U8; | ||
| 13 | use fixed::FixedU32; | ||
| 11 | use pac::io::vals::Gpio0ctrlFuncsel; | 14 | use pac::io::vals::Gpio0ctrlFuncsel; |
| 15 | use pac::pio::vals::SmExecctrlStatusSel; | ||
| 16 | use pio::{SideSet, Wrap}; | ||
| 12 | 17 | ||
| 13 | use crate::dma::{Channel, Transfer, Word}; | 18 | use crate::dma::{Channel, Transfer, Word}; |
| 14 | use crate::gpio::sealed::Pin as SealedPin; | 19 | use crate::gpio::sealed::Pin as SealedPin; |
| 15 | use crate::gpio::{self, AnyPin, Drive, Pull, SlewRate}; | 20 | use crate::gpio::{self, AnyPin, Drive, Level, Pull, SlewRate}; |
| 16 | use crate::pac::dma::vals::TreqSel; | 21 | use crate::pac::dma::vals::TreqSel; |
| 17 | use crate::{interrupt, pac, peripherals, RegExt}; | 22 | use crate::relocate::RelocatedProgram; |
| 23 | use crate::{interrupt, pac, peripherals, pio_instr_util, RegExt}; | ||
| 18 | 24 | ||
| 19 | struct Wakers([AtomicWaker; 12]); | 25 | struct Wakers([AtomicWaker; 12]); |
| 20 | 26 | ||
| @@ -37,8 +43,12 @@ const NEW_AW: AtomicWaker = AtomicWaker::new(); | |||
| 37 | const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]); | 43 | const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]); |
| 38 | static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2]; | 44 | static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2]; |
| 39 | 45 | ||
| 46 | #[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] | ||
| 47 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 48 | #[repr(u8)] | ||
| 40 | pub enum FifoJoin { | 49 | pub enum FifoJoin { |
| 41 | /// Both TX and RX fifo is enabled | 50 | /// Both TX and RX fifo is enabled |
| 51 | #[default] | ||
| 42 | Duplex, | 52 | Duplex, |
| 43 | /// Rx fifo twice as deep. TX fifo disabled | 53 | /// Rx fifo twice as deep. TX fifo disabled |
| 44 | RxOnly, | 54 | RxOnly, |
| @@ -46,12 +56,32 @@ pub enum FifoJoin { | |||
| 46 | TxOnly, | 56 | TxOnly, |
| 47 | } | 57 | } |
| 48 | 58 | ||
| 49 | #[derive(PartialEq)] | 59 | #[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] |
| 60 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 61 | #[repr(u8)] | ||
| 50 | pub enum ShiftDirection { | 62 | pub enum ShiftDirection { |
| 63 | #[default] | ||
| 51 | Right = 1, | 64 | Right = 1, |
| 52 | Left = 0, | 65 | Left = 0, |
| 53 | } | 66 | } |
| 54 | 67 | ||
| 68 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] | ||
| 69 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 70 | #[repr(u8)] | ||
| 71 | pub enum Direction { | ||
| 72 | In = 0, | ||
| 73 | Out = 1, | ||
| 74 | } | ||
| 75 | |||
| 76 | #[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] | ||
| 77 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] | ||
| 78 | #[repr(u8)] | ||
| 79 | pub enum StatusSource { | ||
| 80 | #[default] | ||
| 81 | TxFifoLevel = 0, | ||
| 82 | RxFifoLevel = 1, | ||
| 83 | } | ||
| 84 | |||
| 55 | const RXNEMPTY_MASK: u32 = 1 << 0; | 85 | const RXNEMPTY_MASK: u32 = 1 << 0; |
| 56 | const TXNFULL_MASK: u32 = 1 << 4; | 86 | const TXNFULL_MASK: u32 = 1 << 4; |
| 57 | const SMIRQ_MASK: u32 = 1 << 8; | 87 | const SMIRQ_MASK: u32 = 1 << 8; |
| @@ -96,18 +126,18 @@ pub(crate) unsafe fn init() { | |||
| 96 | 126 | ||
| 97 | /// Future that waits for TX-FIFO to become writable | 127 | /// Future that waits for TX-FIFO to become writable |
| 98 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 128 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 99 | pub struct FifoOutFuture<'a, 'd, PIO: PioInstance, const SM: usize> { | 129 | pub struct FifoOutFuture<'a, 'd, PIO: Instance, const SM: usize> { |
| 100 | sm_tx: &'a mut PioStateMachineTx<'d, PIO, SM>, | 130 | sm_tx: &'a mut StateMachineTx<'d, PIO, SM>, |
| 101 | value: u32, | 131 | value: u32, |
| 102 | } | 132 | } |
| 103 | 133 | ||
| 104 | impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> { | 134 | impl<'a, 'd, PIO: Instance, const SM: usize> FifoOutFuture<'a, 'd, PIO, SM> { |
| 105 | pub fn new(sm: &'a mut PioStateMachineTx<'d, PIO, SM>, value: u32) -> Self { | 135 | pub fn new(sm: &'a mut StateMachineTx<'d, PIO, SM>, value: u32) -> Self { |
| 106 | FifoOutFuture { sm_tx: sm, value } | 136 | FifoOutFuture { sm_tx: sm, value } |
| 107 | } | 137 | } |
| 108 | } | 138 | } |
| 109 | 139 | ||
| 110 | impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> { | 140 | impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoOutFuture<'a, 'd, PIO, SM> { |
| 111 | type Output = (); | 141 | type Output = (); |
| 112 | fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | 142 | fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 113 | //debug!("Poll {},{}", PIO::PIO_NO, SM); | 143 | //debug!("Poll {},{}", PIO::PIO_NO, SM); |
| @@ -127,7 +157,7 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoOutFuture<'a, 'd, | |||
| 127 | } | 157 | } |
| 128 | } | 158 | } |
| 129 | 159 | ||
| 130 | impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> { | 160 | impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoOutFuture<'a, 'd, PIO, SM> { |
| 131 | fn drop(&mut self) { | 161 | fn drop(&mut self) { |
| 132 | unsafe { | 162 | unsafe { |
| 133 | PIO::PIO.irqs(0).inte().write_clear(|m| { | 163 | PIO::PIO.irqs(0).inte().write_clear(|m| { |
| @@ -139,17 +169,17 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoOutFuture<'a, 'd, P | |||
| 139 | 169 | ||
| 140 | /// Future that waits for RX-FIFO to become readable | 170 | /// Future that waits for RX-FIFO to become readable |
| 141 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 171 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 142 | pub struct FifoInFuture<'a, 'd, PIO: PioInstance, const SM: usize> { | 172 | pub struct FifoInFuture<'a, 'd, PIO: Instance, const SM: usize> { |
| 143 | sm_rx: &'a mut PioStateMachineRx<'d, PIO, SM>, | 173 | sm_rx: &'a mut StateMachineRx<'d, PIO, SM>, |
| 144 | } | 174 | } |
| 145 | 175 | ||
| 146 | impl<'a, 'd, PIO: PioInstance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> { | 176 | impl<'a, 'd, PIO: Instance, const SM: usize> FifoInFuture<'a, 'd, PIO, SM> { |
| 147 | pub fn new(sm: &'a mut PioStateMachineRx<'d, PIO, SM>) -> Self { | 177 | pub fn new(sm: &'a mut StateMachineRx<'d, PIO, SM>) -> Self { |
| 148 | FifoInFuture { sm_rx: sm } | 178 | FifoInFuture { sm_rx: sm } |
| 149 | } | 179 | } |
| 150 | } | 180 | } |
| 151 | 181 | ||
| 152 | impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> { | 182 | impl<'a, 'd, PIO: Instance, const SM: usize> Future for FifoInFuture<'a, 'd, PIO, SM> { |
| 153 | type Output = u32; | 183 | type Output = u32; |
| 154 | fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | 184 | fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 155 | //debug!("Poll {},{}", PIO::PIO_NO, SM); | 185 | //debug!("Poll {},{}", PIO::PIO_NO, SM); |
| @@ -168,7 +198,7 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Future for FifoInFuture<'a, 'd, | |||
| 168 | } | 198 | } |
| 169 | } | 199 | } |
| 170 | 200 | ||
| 171 | impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> { | 201 | impl<'a, 'd, PIO: Instance, const SM: usize> Drop for FifoInFuture<'a, 'd, PIO, SM> { |
| 172 | fn drop(&mut self) { | 202 | fn drop(&mut self) { |
| 173 | unsafe { | 203 | unsafe { |
| 174 | PIO::PIO.irqs(0).inte().write_clear(|m| { | 204 | PIO::PIO.irqs(0).inte().write_clear(|m| { |
| @@ -180,28 +210,21 @@ impl<'a, 'd, PIO: PioInstance, const SM: usize> Drop for FifoInFuture<'a, 'd, PI | |||
| 180 | 210 | ||
| 181 | /// Future that waits for IRQ | 211 | /// Future that waits for IRQ |
| 182 | #[must_use = "futures do nothing unless you `.await` or poll them"] | 212 | #[must_use = "futures do nothing unless you `.await` or poll them"] |
| 183 | pub struct IrqFuture<'a, 'd, PIO: PioInstance> { | 213 | pub struct IrqFuture<'a, 'd, PIO: Instance> { |
| 184 | pio: PhantomData<&'a PioIrq<'d, PIO, 0>>, | 214 | pio: PhantomData<&'a mut Irq<'d, PIO, 0>>, |
| 185 | irq_no: u8, | 215 | irq_no: u8, |
| 186 | } | 216 | } |
| 187 | 217 | ||
| 188 | impl<'a, 'd, PIO: PioInstance> Future for IrqFuture<'a, 'd, PIO> { | 218 | impl<'a, 'd, PIO: Instance> Future for IrqFuture<'a, 'd, PIO> { |
| 189 | type Output = (); | 219 | type Output = (); |
| 190 | fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | 220 | fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { |
| 191 | //debug!("Poll {},{}", PIO::PIO_NO, SM); | 221 | //debug!("Poll {},{}", PIO::PIO_NO, SM); |
| 192 | 222 | ||
| 193 | // Check if IRQ flag is already set | 223 | // Check if IRQ flag is already set |
| 194 | if critical_section::with(|_| unsafe { | 224 | if unsafe { PIO::PIO.irq().read().0 & (1 << self.irq_no) != 0 } { |
| 195 | let irq_flags = PIO::PIO.irq(); | 225 | unsafe { |
| 196 | if irq_flags.read().0 & (1 << self.irq_no) != 0 { | 226 | PIO::PIO.irq().write(|m| m.0 = 1 << self.irq_no); |
| 197 | irq_flags.write(|m| { | ||
| 198 | m.0 = 1 << self.irq_no; | ||
| 199 | }); | ||
| 200 | true | ||
| 201 | } else { | ||
| 202 | false | ||
| 203 | } | 227 | } |
| 204 | }) { | ||
| 205 | return Poll::Ready(()); | 228 | return Poll::Ready(()); |
| 206 | } | 229 | } |
| 207 | 230 | ||
| @@ -215,7 +238,7 @@ impl<'a, 'd, PIO: PioInstance> Future for IrqFuture<'a, 'd, PIO> { | |||
| 215 | } | 238 | } |
| 216 | } | 239 | } |
| 217 | 240 | ||
| 218 | impl<'a, 'd, PIO: PioInstance> Drop for IrqFuture<'a, 'd, PIO> { | 241 | impl<'a, 'd, PIO: Instance> Drop for IrqFuture<'a, 'd, PIO> { |
| 219 | fn drop(&mut self) { | 242 | fn drop(&mut self) { |
| 220 | unsafe { | 243 | unsafe { |
| 221 | PIO::PIO.irqs(0).inte().write_clear(|m| { | 244 | PIO::PIO.irqs(0).inte().write_clear(|m| { |
| @@ -225,12 +248,12 @@ impl<'a, 'd, PIO: PioInstance> Drop for IrqFuture<'a, 'd, PIO> { | |||
| 225 | } | 248 | } |
| 226 | } | 249 | } |
| 227 | 250 | ||
| 228 | pub struct Pin<'l, PIO: PioInstance> { | 251 | pub struct Pin<'l, PIO: Instance> { |
| 229 | pin: PeripheralRef<'l, AnyPin>, | 252 | pin: PeripheralRef<'l, AnyPin>, |
| 230 | pio: PhantomData<PIO>, | 253 | pio: PhantomData<PIO>, |
| 231 | } | 254 | } |
| 232 | 255 | ||
| 233 | impl<'l, PIO: PioInstance> Pin<'l, PIO> { | 256 | impl<'l, PIO: Instance> Pin<'l, PIO> { |
| 234 | /// Set the pin's drive strength. | 257 | /// Set the pin's drive strength. |
| 235 | #[inline] | 258 | #[inline] |
| 236 | pub fn set_drive_strength(&mut self, strength: Drive) { | 259 | pub fn set_drive_strength(&mut self, strength: Drive) { |
| @@ -293,11 +316,11 @@ impl<'l, PIO: PioInstance> Pin<'l, PIO> { | |||
| 293 | } | 316 | } |
| 294 | } | 317 | } |
| 295 | 318 | ||
| 296 | pub struct PioStateMachineRx<'d, PIO: PioInstance, const SM: usize> { | 319 | pub struct StateMachineRx<'d, PIO: Instance, const SM: usize> { |
| 297 | pio: PhantomData<&'d PIO>, | 320 | pio: PhantomData<&'d mut PIO>, |
| 298 | } | 321 | } |
| 299 | 322 | ||
| 300 | impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> { | 323 | impl<'d, PIO: Instance, const SM: usize> StateMachineRx<'d, PIO, SM> { |
| 301 | pub fn empty(&self) -> bool { | 324 | pub fn empty(&self) -> bool { |
| 302 | unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 } | 325 | unsafe { PIO::PIO.fstat().read().rxempty() & (1u8 << SM) != 0 } |
| 303 | } | 326 | } |
| @@ -314,7 +337,9 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> { | |||
| 314 | unsafe { | 337 | unsafe { |
| 315 | let fdebug = PIO::PIO.fdebug(); | 338 | let fdebug = PIO::PIO.fdebug(); |
| 316 | let ret = fdebug.read().rxstall() & (1 << SM) != 0; | 339 | let ret = fdebug.read().rxstall() & (1 << SM) != 0; |
| 317 | fdebug.write(|w| w.set_rxstall(1 << SM)); | 340 | if ret { |
| 341 | fdebug.write(|w| w.set_rxstall(1 << SM)); | ||
| 342 | } | ||
| 318 | ret | 343 | ret |
| 319 | } | 344 | } |
| 320 | } | 345 | } |
| @@ -323,7 +348,9 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> { | |||
| 323 | unsafe { | 348 | unsafe { |
| 324 | let fdebug = PIO::PIO.fdebug(); | 349 | let fdebug = PIO::PIO.fdebug(); |
| 325 | let ret = fdebug.read().rxunder() & (1 << SM) != 0; | 350 | let ret = fdebug.read().rxunder() & (1 << SM) != 0; |
| 326 | fdebug.write(|w| w.set_rxunder(1 << SM)); | 351 | if ret { |
| 352 | fdebug.write(|w| w.set_rxunder(1 << SM)); | ||
| 353 | } | ||
| 327 | ret | 354 | ret |
| 328 | } | 355 | } |
| 329 | } | 356 | } |
| @@ -370,11 +397,11 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineRx<'d, PIO, SM> { | |||
| 370 | } | 397 | } |
| 371 | } | 398 | } |
| 372 | 399 | ||
| 373 | pub struct PioStateMachineTx<'d, PIO: PioInstance, const SM: usize> { | 400 | pub struct StateMachineTx<'d, PIO: Instance, const SM: usize> { |
| 374 | pio: PhantomData<&'d PIO>, | 401 | pio: PhantomData<&'d mut PIO>, |
| 375 | } | 402 | } |
| 376 | 403 | ||
| 377 | impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'d, PIO, SM> { | 404 | impl<'d, PIO: Instance, const SM: usize> StateMachineTx<'d, PIO, SM> { |
| 378 | pub fn empty(&self) -> bool { | 405 | pub fn empty(&self) -> bool { |
| 379 | unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 } | 406 | unsafe { PIO::PIO.fstat().read().txempty() & (1u8 << SM) != 0 } |
| 380 | } | 407 | } |
| @@ -390,7 +417,9 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'d, PIO, SM> { | |||
| 390 | unsafe { | 417 | unsafe { |
| 391 | let fdebug = PIO::PIO.fdebug(); | 418 | let fdebug = PIO::PIO.fdebug(); |
| 392 | let ret = fdebug.read().txstall() & (1 << SM) != 0; | 419 | let ret = fdebug.read().txstall() & (1 << SM) != 0; |
| 393 | fdebug.write(|w| w.set_txstall(1 << SM)); | 420 | if ret { |
| 421 | fdebug.write(|w| w.set_txstall(1 << SM)); | ||
| 422 | } | ||
| 394 | ret | 423 | ret |
| 395 | } | 424 | } |
| 396 | } | 425 | } |
| @@ -399,7 +428,9 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'d, PIO, SM> { | |||
| 399 | unsafe { | 428 | unsafe { |
| 400 | let fdebug = PIO::PIO.fdebug(); | 429 | let fdebug = PIO::PIO.fdebug(); |
| 401 | let ret = fdebug.read().txover() & (1 << SM) != 0; | 430 | let ret = fdebug.read().txover() & (1 << SM) != 0; |
| 402 | fdebug.write(|w| w.set_txover(1 << SM)); | 431 | if ret { |
| 432 | fdebug.write(|w| w.set_txover(1 << SM)); | ||
| 433 | } | ||
| 403 | ret | 434 | ret |
| 404 | } | 435 | } |
| 405 | } | 436 | } |
| @@ -445,12 +476,12 @@ impl<'d, PIO: PioInstance, const SM: usize> PioStateMachineTx<'d, PIO, SM> { | |||
| 445 | } | 476 | } |
| 446 | } | 477 | } |
| 447 | 478 | ||
| 448 | pub struct PioStateMachine<'d, PIO: PioInstance, const SM: usize> { | 479 | pub struct StateMachine<'d, PIO: Instance, const SM: usize> { |
| 449 | rx: PioStateMachineRx<'d, PIO, SM>, | 480 | rx: StateMachineRx<'d, PIO, SM>, |
| 450 | tx: PioStateMachineTx<'d, PIO, SM>, | 481 | tx: StateMachineTx<'d, PIO, SM>, |
| 451 | } | 482 | } |
| 452 | 483 | ||
| 453 | impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM> { | 484 | impl<'d, PIO: Instance, const SM: usize> Drop for StateMachine<'d, PIO, SM> { |
| 454 | fn drop(&mut self) { | 485 | fn drop(&mut self) { |
| 455 | unsafe { | 486 | unsafe { |
| 456 | PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM)); | 487 | PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(1 << SM)); |
| @@ -459,377 +490,394 @@ impl<'d, PIO: PioInstance, const SM: usize> Drop for PioStateMachine<'d, PIO, SM | |||
| 459 | } | 490 | } |
| 460 | } | 491 | } |
| 461 | 492 | ||
| 462 | impl<'d, PIO: PioInstance + 'd, const SM: usize> PioStateMachine<'d, PIO, SM> { | 493 | fn assert_consecutive<'d, PIO: Instance>(pins: &[&Pin<'d, PIO>]) { |
| 463 | #[inline(always)] | 494 | for (p1, p2) in pins.iter().zip(pins.iter().skip(1)) { |
| 464 | fn this_sm() -> crate::pac::pio::StateMachine { | 495 | // purposely does not allow wrap-around because we can't claim pins 30 and 31. |
| 465 | PIO::PIO.sm(SM) | 496 | assert!(p1.pin() + 1 == p2.pin(), "pins must be consecutive"); |
| 466 | } | 497 | } |
| 498 | } | ||
| 467 | 499 | ||
| 468 | pub fn restart(&mut self) { | 500 | #[derive(Clone, Copy, Default, Debug)] |
| 469 | let mask = 1u8 << SM; | 501 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 470 | unsafe { | 502 | #[non_exhaustive] |
| 471 | PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask)); | 503 | pub struct ExecConfig { |
| 472 | } | 504 | pub side_en: bool, |
| 473 | } | 505 | pub side_pindir: bool, |
| 474 | pub fn set_enable(&mut self, enable: bool) { | 506 | pub jmp_pin: u8, |
| 475 | let mask = 1u8 << SM; | 507 | pub wrap_top: u8, |
| 476 | unsafe { | 508 | pub wrap_bottom: u8, |
| 477 | if enable { | 509 | } |
| 478 | PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); | ||
| 479 | } else { | ||
| 480 | PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask)); | ||
| 481 | } | ||
| 482 | } | ||
| 483 | } | ||
| 484 | 510 | ||
| 485 | pub fn is_enabled(&self) -> bool { | 511 | #[derive(Clone, Copy, Default, Debug)] |
| 486 | unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } | 512 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 487 | } | 513 | pub struct ShiftConfig { |
| 514 | pub threshold: u8, | ||
| 515 | pub direction: ShiftDirection, | ||
| 516 | pub auto_fill: bool, | ||
| 517 | } | ||
| 488 | 518 | ||
| 489 | pub fn set_clkdiv(&mut self, div_x_256: u32) { | 519 | #[derive(Clone, Copy, Default, Debug)] |
| 490 | unsafe { | 520 | #[cfg_attr(feature = "defmt", derive(defmt::Format))] |
| 491 | Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8); | 521 | pub struct PinConfig { |
| 492 | } | 522 | pub sideset_count: u8, |
| 493 | } | 523 | pub set_count: u8, |
| 524 | pub out_count: u8, | ||
| 525 | pub in_base: u8, | ||
| 526 | pub sideset_base: u8, | ||
| 527 | pub set_base: u8, | ||
| 528 | pub out_base: u8, | ||
| 529 | } | ||
| 494 | 530 | ||
| 495 | pub fn get_clkdiv(&self) -> u32 { | 531 | #[derive(Clone, Copy, Debug)] |
| 496 | unsafe { Self::this_sm().clkdiv().read().0 >> 8 } | 532 | pub struct Config<'d, PIO: Instance> { |
| 497 | } | 533 | // CLKDIV |
| 534 | pub clock_divider: FixedU32<U8>, | ||
| 535 | // EXECCTRL | ||
| 536 | pub out_en_sel: u8, | ||
| 537 | pub inline_out_en: bool, | ||
| 538 | pub out_sticky: bool, | ||
| 539 | pub status_sel: StatusSource, | ||
| 540 | pub status_n: u8, | ||
| 541 | exec: ExecConfig, | ||
| 542 | origin: Option<u8>, | ||
| 543 | // SHIFTCTRL | ||
| 544 | pub fifo_join: FifoJoin, | ||
| 545 | pub shift_in: ShiftConfig, | ||
| 546 | pub shift_out: ShiftConfig, | ||
| 547 | // PINCTRL | ||
| 548 | pins: PinConfig, | ||
| 549 | in_count: u8, | ||
| 550 | _pio: PhantomData<&'d mut PIO>, | ||
| 551 | } | ||
| 498 | 552 | ||
| 499 | pub fn clkdiv_restart(&mut self) { | 553 | impl<'d, PIO: Instance> Default for Config<'d, PIO> { |
| 500 | let mask = 1u8 << SM; | 554 | fn default() -> Self { |
| 501 | unsafe { | 555 | Self { |
| 502 | PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask)); | 556 | clock_divider: 1u8.into(), |
| 557 | out_en_sel: Default::default(), | ||
| 558 | inline_out_en: Default::default(), | ||
| 559 | out_sticky: Default::default(), | ||
| 560 | status_sel: Default::default(), | ||
| 561 | status_n: Default::default(), | ||
| 562 | exec: Default::default(), | ||
| 563 | origin: Default::default(), | ||
| 564 | fifo_join: Default::default(), | ||
| 565 | shift_in: Default::default(), | ||
| 566 | shift_out: Default::default(), | ||
| 567 | pins: Default::default(), | ||
| 568 | in_count: Default::default(), | ||
| 569 | _pio: Default::default(), | ||
| 503 | } | 570 | } |
| 504 | } | 571 | } |
| 572 | } | ||
| 505 | 573 | ||
| 506 | pub fn set_side_enable(&self, enable: bool) { | 574 | impl<'d, PIO: Instance> Config<'d, PIO> { |
| 507 | unsafe { | 575 | pub fn get_exec(&self) -> ExecConfig { |
| 508 | Self::this_sm().execctrl().modify(|w| w.set_side_en(enable)); | 576 | self.exec |
| 509 | } | ||
| 510 | } | 577 | } |
| 511 | 578 | pub unsafe fn set_exec(&mut self, e: ExecConfig) { | |
| 512 | pub fn is_side_enabled(&self) -> bool { | 579 | self.exec = e; |
| 513 | unsafe { Self::this_sm().execctrl().read().side_en() } | ||
| 514 | } | 580 | } |
| 515 | 581 | ||
| 516 | pub fn set_side_pindir(&mut self, pindir: bool) { | 582 | pub fn get_pins(&self) -> PinConfig { |
| 517 | unsafe { | 583 | self.pins |
| 518 | Self::this_sm().execctrl().modify(|w| w.set_side_pindir(pindir)); | ||
| 519 | } | ||
| 520 | } | 584 | } |
| 521 | 585 | pub unsafe fn set_pins(&mut self, p: PinConfig) { | |
| 522 | pub fn is_side_pindir(&self) -> bool { | 586 | self.pins = p; |
| 523 | unsafe { Self::this_sm().execctrl().read().side_pindir() } | ||
| 524 | } | 587 | } |
| 525 | 588 | ||
| 526 | pub fn set_jmp_pin(&mut self, pin: u8) { | 589 | /// Configures this state machine to use the given program, including jumping to the origin |
| 527 | unsafe { | 590 | /// of the program. The state machine is not started. |
| 528 | Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); | 591 | /// |
| 529 | } | 592 | /// `side_set` sets the range of pins affected by side-sets. The range must be consecutive. |
| 593 | /// Side-set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be | ||
| 594 | /// effective. | ||
| 595 | pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) { | ||
| 596 | assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len()); | ||
| 597 | assert_consecutive(side_set); | ||
| 598 | self.exec.side_en = prog.side_set.optional(); | ||
| 599 | self.exec.side_pindir = prog.side_set.pindirs(); | ||
| 600 | self.exec.wrap_bottom = prog.wrap.target; | ||
| 601 | self.exec.wrap_top = prog.wrap.source; | ||
| 602 | self.pins.sideset_count = prog.side_set.bits(); | ||
| 603 | self.pins.sideset_base = side_set.first().map_or(0, |p| p.pin()); | ||
| 604 | self.origin = Some(prog.origin); | ||
| 530 | } | 605 | } |
| 531 | 606 | ||
| 532 | pub fn get_jmp_pin(&mut self) -> u8 { | 607 | pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) { |
| 533 | unsafe { Self::this_sm().execctrl().read().jmp_pin() } | 608 | self.exec.jmp_pin = pin.pin(); |
| 534 | } | 609 | } |
| 535 | 610 | ||
| 536 | pub fn set_wrap(&self, source: u8, target: u8) { | 611 | /// Sets the range of pins affected by SET instructions. The range must be consecutive. |
| 537 | unsafe { | 612 | /// Set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be |
| 538 | Self::this_sm().execctrl().modify(|w| { | 613 | /// effective. |
| 539 | w.set_wrap_top(source); | 614 | pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) { |
| 540 | w.set_wrap_bottom(target) | 615 | assert!(pins.len() <= 5); |
| 541 | }); | 616 | assert_consecutive(pins); |
| 542 | } | 617 | self.pins.set_base = pins.first().map_or(0, |p| p.pin()); |
| 618 | self.pins.set_count = pins.len() as u8; | ||
| 543 | } | 619 | } |
| 544 | 620 | ||
| 545 | /// Get wrapping addresses. Returns (source, target). | 621 | /// Sets the range of pins affected by OUT instructions. The range must be consecutive. |
| 546 | pub fn get_wrap(&self) -> (u8, u8) { | 622 | /// Out pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be |
| 547 | unsafe { | 623 | /// effective. |
| 548 | let r = Self::this_sm().execctrl().read(); | 624 | pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) { |
| 549 | (r.wrap_top(), r.wrap_bottom()) | 625 | assert_consecutive(pins); |
| 550 | } | 626 | self.pins.out_base = pins.first().map_or(0, |p| p.pin()); |
| 627 | self.pins.out_count = pins.len() as u8; | ||
| 551 | } | 628 | } |
| 552 | 629 | ||
| 553 | pub fn set_fifo_join(&mut self, join: FifoJoin) { | 630 | /// Sets the range of pins used by IN instructions. The range must be consecutive. |
| 554 | let (rx, tx) = match join { | 631 | /// In pins must configured as inputs using [`StateMachine::set_pin_dirs`] to be |
| 555 | FifoJoin::Duplex => (false, false), | 632 | /// effective. |
| 556 | FifoJoin::RxOnly => (true, false), | 633 | pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) { |
| 557 | FifoJoin::TxOnly => (false, true), | 634 | assert_consecutive(pins); |
| 558 | }; | 635 | self.pins.in_base = pins.first().map_or(0, |p| p.pin()); |
| 559 | unsafe { | 636 | self.in_count = pins.len() as u8; |
| 560 | Self::this_sm().shiftctrl().modify(|w| { | ||
| 561 | w.set_fjoin_rx(rx); | ||
| 562 | w.set_fjoin_tx(tx) | ||
| 563 | }); | ||
| 564 | } | ||
| 565 | } | ||
| 566 | pub fn get_fifo_join(&self) -> FifoJoin { | ||
| 567 | unsafe { | ||
| 568 | let r = Self::this_sm().shiftctrl().read(); | ||
| 569 | // Ignores the invalid state when both bits are set | ||
| 570 | if r.fjoin_rx() { | ||
| 571 | FifoJoin::RxOnly | ||
| 572 | } else if r.fjoin_tx() { | ||
| 573 | FifoJoin::TxOnly | ||
| 574 | } else { | ||
| 575 | FifoJoin::Duplex | ||
| 576 | } | ||
| 577 | } | ||
| 578 | } | 637 | } |
| 638 | } | ||
| 579 | 639 | ||
| 580 | pub fn clear_fifos(&mut self) { | 640 | impl<'d, PIO: Instance, const SM: usize> SetConfig for StateMachine<'d, PIO, SM> { |
| 581 | // Toggle FJOIN_RX to flush FIFOs | 641 | type Config = Config<'d, PIO>; |
| 642 | |||
| 643 | fn set_config(&mut self, config: &Self::Config) { | ||
| 644 | // sm expects 0 for 65536, truncation makes that happen | ||
| 645 | assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536"); | ||
| 646 | assert!(config.clock_divider >= 1, "clkdiv must be >= 1"); | ||
| 647 | assert!(config.out_en_sel < 32, "out_en_sel must be < 32"); | ||
| 648 | assert!(config.status_n < 32, "status_n must be < 32"); | ||
| 649 | // sm expects 0 for 32, truncation makes that happen | ||
| 650 | assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32"); | ||
| 651 | assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32"); | ||
| 652 | let sm = Self::this_sm(); | ||
| 582 | unsafe { | 653 | unsafe { |
| 583 | let shiftctrl = Self::this_sm().shiftctrl(); | 654 | sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8); |
| 584 | shiftctrl.modify(|w| { | 655 | sm.execctrl().write(|w| { |
| 585 | w.set_fjoin_rx(!w.fjoin_rx()); | 656 | w.set_side_en(config.exec.side_en); |
| 657 | w.set_side_pindir(config.exec.side_pindir); | ||
| 658 | w.set_jmp_pin(config.exec.jmp_pin); | ||
| 659 | w.set_out_en_sel(config.out_en_sel); | ||
| 660 | w.set_inline_out_en(config.inline_out_en); | ||
| 661 | w.set_out_sticky(config.out_sticky); | ||
| 662 | w.set_wrap_top(config.exec.wrap_top); | ||
| 663 | w.set_wrap_bottom(config.exec.wrap_bottom); | ||
| 664 | w.set_status_sel(match config.status_sel { | ||
| 665 | StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL, | ||
| 666 | StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL, | ||
| 667 | }); | ||
| 668 | w.set_status_n(config.status_n); | ||
| 586 | }); | 669 | }); |
| 587 | shiftctrl.modify(|w| { | 670 | sm.shiftctrl().write(|w| { |
| 588 | w.set_fjoin_rx(!w.fjoin_rx()); | 671 | w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly); |
| 672 | w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly); | ||
| 673 | w.set_pull_thresh(config.shift_out.threshold); | ||
| 674 | w.set_push_thresh(config.shift_in.threshold); | ||
| 675 | w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right); | ||
| 676 | w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right); | ||
| 677 | w.set_autopull(config.shift_out.auto_fill); | ||
| 678 | w.set_autopush(config.shift_in.auto_fill); | ||
| 589 | }); | 679 | }); |
| 680 | sm.pinctrl().write(|w| { | ||
| 681 | w.set_sideset_count(config.pins.sideset_count); | ||
| 682 | w.set_set_count(config.pins.set_count); | ||
| 683 | w.set_out_count(config.pins.out_count); | ||
| 684 | w.set_in_base(config.pins.in_base); | ||
| 685 | w.set_sideset_base(config.pins.sideset_base); | ||
| 686 | w.set_set_base(config.pins.set_base); | ||
| 687 | w.set_out_base(config.pins.out_base); | ||
| 688 | }); | ||
| 689 | if let Some(origin) = config.origin { | ||
| 690 | pio_instr_util::exec_jmp(self, origin); | ||
| 691 | } | ||
| 590 | } | 692 | } |
| 591 | } | 693 | } |
| 694 | } | ||
| 592 | 695 | ||
| 593 | pub fn set_pull_threshold(&mut self, threshold: u8) { | 696 | impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { |
| 594 | unsafe { | 697 | #[inline(always)] |
| 595 | Self::this_sm().shiftctrl().modify(|w| w.set_pull_thresh(threshold)); | 698 | fn this_sm() -> crate::pac::pio::StateMachine { |
| 596 | } | 699 | PIO::PIO.sm(SM) |
| 597 | } | ||
| 598 | |||
| 599 | pub fn get_pull_threshold(&self) -> u8 { | ||
| 600 | unsafe { Self::this_sm().shiftctrl().read().pull_thresh() } | ||
| 601 | } | ||
| 602 | pub fn set_push_threshold(&mut self, threshold: u8) { | ||
| 603 | unsafe { | ||
| 604 | Self::this_sm().shiftctrl().modify(|w| w.set_push_thresh(threshold)); | ||
| 605 | } | ||
| 606 | } | ||
| 607 | |||
| 608 | pub fn get_push_threshold(&self) -> u8 { | ||
| 609 | unsafe { Self::this_sm().shiftctrl().read().push_thresh() } | ||
| 610 | } | 700 | } |
| 611 | 701 | ||
| 612 | pub fn set_out_shift_dir(&mut self, dir: ShiftDirection) { | 702 | pub fn restart(&mut self) { |
| 703 | let mask = 1u8 << SM; | ||
| 613 | unsafe { | 704 | unsafe { |
| 614 | Self::this_sm() | 705 | PIO::PIO.ctrl().write_set(|w| w.set_sm_restart(mask)); |
| 615 | .shiftctrl() | ||
| 616 | .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); | ||
| 617 | } | 706 | } |
| 618 | } | 707 | } |
| 619 | pub fn get_out_shiftdir(&self) -> ShiftDirection { | 708 | pub fn set_enable(&mut self, enable: bool) { |
| 709 | let mask = 1u8 << SM; | ||
| 620 | unsafe { | 710 | unsafe { |
| 621 | if Self::this_sm().shiftctrl().read().out_shiftdir() { | 711 | if enable { |
| 622 | ShiftDirection::Right | 712 | PIO::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); |
| 623 | } else { | 713 | } else { |
| 624 | ShiftDirection::Left | 714 | PIO::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask)); |
| 625 | } | 715 | } |
| 626 | } | 716 | } |
| 627 | } | 717 | } |
| 628 | 718 | ||
| 629 | pub fn set_in_shift_dir(&mut self, dir: ShiftDirection) { | 719 | pub fn is_enabled(&self) -> bool { |
| 630 | unsafe { | 720 | unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } |
| 631 | Self::this_sm() | ||
| 632 | .shiftctrl() | ||
| 633 | .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); | ||
| 634 | } | ||
| 635 | } | ||
| 636 | pub fn get_in_shiftdir(&self) -> ShiftDirection { | ||
| 637 | unsafe { | ||
| 638 | if Self::this_sm().shiftctrl().read().in_shiftdir() { | ||
| 639 | ShiftDirection::Right | ||
| 640 | } else { | ||
| 641 | ShiftDirection::Left | ||
| 642 | } | ||
| 643 | } | ||
| 644 | } | 721 | } |
| 645 | 722 | ||
| 646 | pub fn set_autopull(&mut self, auto: bool) { | 723 | pub fn clkdiv_restart(&mut self) { |
| 724 | let mask = 1u8 << SM; | ||
| 647 | unsafe { | 725 | unsafe { |
| 648 | Self::this_sm().shiftctrl().modify(|w| w.set_autopull(auto)); | 726 | PIO::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask)); |
| 649 | } | 727 | } |
| 650 | } | 728 | } |
| 651 | 729 | ||
| 652 | pub fn is_autopull(&self) -> bool { | 730 | fn with_paused(&mut self, f: impl FnOnce(&mut Self)) { |
| 653 | unsafe { Self::this_sm().shiftctrl().read().autopull() } | 731 | let enabled = self.is_enabled(); |
| 654 | } | 732 | self.set_enable(false); |
| 655 | 733 | let pincfg = unsafe { Self::this_sm().pinctrl().read() }; | |
| 656 | pub fn set_autopush(&mut self, auto: bool) { | 734 | let execcfg = unsafe { Self::this_sm().execctrl().read() }; |
| 657 | unsafe { | 735 | unsafe { |
| 658 | Self::this_sm().shiftctrl().modify(|w| w.set_autopush(auto)); | 736 | Self::this_sm().execctrl().write_clear(|w| w.set_out_sticky(true)); |
| 659 | } | 737 | } |
| 660 | } | 738 | f(self); |
| 661 | |||
| 662 | pub fn is_autopush(&self) -> bool { | ||
| 663 | unsafe { Self::this_sm().shiftctrl().read().autopush() } | ||
| 664 | } | ||
| 665 | |||
| 666 | pub fn get_addr(&self) -> u8 { | ||
| 667 | unsafe { Self::this_sm().addr().read().addr() } | ||
| 668 | } | ||
| 669 | pub fn set_sideset_count(&mut self, count: u8) { | ||
| 670 | unsafe { | 739 | unsafe { |
| 671 | Self::this_sm().pinctrl().modify(|w| w.set_sideset_count(count)); | 740 | Self::this_sm().pinctrl().write_value(pincfg); |
| 741 | Self::this_sm().execctrl().write_value(execcfg); | ||
| 672 | } | 742 | } |
| 743 | self.set_enable(enabled); | ||
| 673 | } | 744 | } |
| 674 | 745 | ||
| 675 | pub fn get_sideset_count(&self) -> u8 { | 746 | /// Sets pin directions. This pauses the current state machine to run `SET` commands |
| 676 | unsafe { Self::this_sm().pinctrl().read().sideset_count() } | 747 | /// and temporarily unsets the `OUT_STICKY` bit. |
| 677 | } | 748 | pub fn set_pin_dirs(&mut self, dir: Direction, pins: &[&Pin<'d, PIO>]) { |
| 678 | 749 | self.with_paused(|sm| { | |
| 679 | pub fn set_sideset_base_pin(&mut self, base_pin: &Pin<PIO>) { | 750 | for pin in pins { |
| 680 | unsafe { | 751 | unsafe { |
| 681 | Self::this_sm().pinctrl().modify(|w| w.set_sideset_base(base_pin.pin())); | 752 | Self::this_sm().pinctrl().write(|w| { |
| 682 | } | 753 | w.set_set_base(pin.pin()); |
| 754 | w.set_set_count(1); | ||
| 755 | }); | ||
| 756 | // SET PINDIRS, (dir) | ||
| 757 | sm.exec_instr(0b111_00000_100_00000 | dir as u16); | ||
| 758 | } | ||
| 759 | } | ||
| 760 | }); | ||
| 683 | } | 761 | } |
| 684 | 762 | ||
| 685 | pub fn get_sideset_base(&self) -> u8 { | 763 | /// Sets pin output values. This pauses the current state machine to run |
| 686 | unsafe { | 764 | /// `SET` commands and temporarily unsets the `OUT_STICKY` bit. |
| 687 | let r = Self::this_sm().pinctrl().read(); | 765 | pub fn set_pins(&mut self, level: Level, pins: &[&Pin<'d, PIO>]) { |
| 688 | r.sideset_base() | 766 | self.with_paused(|sm| { |
| 689 | } | 767 | for pin in pins { |
| 768 | unsafe { | ||
| 769 | Self::this_sm().pinctrl().write(|w| { | ||
| 770 | w.set_set_base(pin.pin()); | ||
| 771 | w.set_set_count(1); | ||
| 772 | }); | ||
| 773 | // SET PINS, (dir) | ||
| 774 | sm.exec_instr(0b111_00000_000_00000 | level as u16); | ||
| 775 | } | ||
| 776 | } | ||
| 777 | }); | ||
| 690 | } | 778 | } |
| 691 | 779 | ||
| 692 | /// Set the range of out pins affected by a set instruction. | 780 | pub fn clear_fifos(&mut self) { |
| 693 | pub fn set_set_range(&mut self, base: u8, count: u8) { | 781 | // Toggle FJOIN_RX to flush FIFOs |
| 694 | assert!(base + count < 32); | ||
| 695 | unsafe { | 782 | unsafe { |
| 696 | Self::this_sm().pinctrl().modify(|w| { | 783 | let shiftctrl = Self::this_sm().shiftctrl(); |
| 697 | w.set_set_base(base); | 784 | shiftctrl.modify(|w| { |
| 698 | w.set_set_count(count) | 785 | w.set_fjoin_rx(!w.fjoin_rx()); |
| 699 | }); | 786 | }); |
| 700 | } | 787 | shiftctrl.modify(|w| { |
| 701 | } | 788 | w.set_fjoin_rx(!w.fjoin_rx()); |
| 702 | |||
| 703 | /// Get the range of out pins affected by a set instruction. Returns (base, count). | ||
| 704 | pub fn get_set_range(&self) -> (u8, u8) { | ||
| 705 | unsafe { | ||
| 706 | let r = Self::this_sm().pinctrl().read(); | ||
| 707 | (r.set_base(), r.set_count()) | ||
| 708 | } | ||
| 709 | } | ||
| 710 | |||
| 711 | pub fn set_in_base_pin(&mut self, base: &Pin<PIO>) { | ||
| 712 | unsafe { | ||
| 713 | Self::this_sm().pinctrl().modify(|w| w.set_in_base(base.pin())); | ||
| 714 | } | ||
| 715 | } | ||
| 716 | |||
| 717 | pub fn get_in_base(&self) -> u8 { | ||
| 718 | unsafe { | ||
| 719 | let r = Self::this_sm().pinctrl().read(); | ||
| 720 | r.in_base() | ||
| 721 | } | ||
| 722 | } | ||
| 723 | |||
| 724 | pub fn set_out_range(&mut self, base: u8, count: u8) { | ||
| 725 | assert!(base + count < 32); | ||
| 726 | unsafe { | ||
| 727 | Self::this_sm().pinctrl().modify(|w| { | ||
| 728 | w.set_out_base(base); | ||
| 729 | w.set_out_count(count) | ||
| 730 | }); | 789 | }); |
| 731 | } | 790 | } |
| 732 | } | 791 | } |
| 733 | 792 | ||
| 734 | /// Get the range of out pins affected by a set instruction. Returns (base, count). | 793 | pub unsafe fn exec_instr(&mut self, instr: u16) { |
| 735 | pub fn get_out_range(&self) -> (u8, u8) { | 794 | Self::this_sm().instr().write(|w| w.set_instr(instr)); |
| 736 | unsafe { | ||
| 737 | let r = Self::this_sm().pinctrl().read(); | ||
| 738 | (r.out_base(), r.out_count()) | ||
| 739 | } | ||
| 740 | } | ||
| 741 | |||
| 742 | pub fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin<PIO>]) { | ||
| 743 | let count = pins.len(); | ||
| 744 | assert!(count >= 1); | ||
| 745 | let start = pins[0].pin() as usize; | ||
| 746 | assert!(start + pins.len() <= 32); | ||
| 747 | for i in 0..count { | ||
| 748 | assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); | ||
| 749 | } | ||
| 750 | self.set_out_range(start as u8, count as u8); | ||
| 751 | } | ||
| 752 | |||
| 753 | pub fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin<PIO>]) { | ||
| 754 | let count = pins.len(); | ||
| 755 | assert!(count >= 1); | ||
| 756 | let start = pins[0].pin() as usize; | ||
| 757 | assert!(start + pins.len() <= 32); | ||
| 758 | for i in 0..count { | ||
| 759 | assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); | ||
| 760 | } | ||
| 761 | self.set_set_range(start as u8, count as u8); | ||
| 762 | } | ||
| 763 | |||
| 764 | pub fn get_current_instr() -> u32 { | ||
| 765 | unsafe { Self::this_sm().instr().read().0 } | ||
| 766 | } | ||
| 767 | |||
| 768 | pub fn exec_instr(&mut self, instr: u16) { | ||
| 769 | unsafe { | ||
| 770 | Self::this_sm().instr().write(|w| w.set_instr(instr)); | ||
| 771 | } | ||
| 772 | } | 795 | } |
| 773 | 796 | ||
| 774 | pub fn rx(&mut self) -> &mut PioStateMachineRx<'d, PIO, SM> { | 797 | pub fn rx(&mut self) -> &mut StateMachineRx<'d, PIO, SM> { |
| 775 | &mut self.rx | 798 | &mut self.rx |
| 776 | } | 799 | } |
| 777 | pub fn tx(&mut self) -> &mut PioStateMachineTx<'d, PIO, SM> { | 800 | pub fn tx(&mut self) -> &mut StateMachineTx<'d, PIO, SM> { |
| 778 | &mut self.tx | 801 | &mut self.tx |
| 779 | } | 802 | } |
| 780 | pub fn rx_tx(&mut self) -> (&mut PioStateMachineRx<'d, PIO, SM>, &mut PioStateMachineTx<'d, PIO, SM>) { | 803 | pub fn rx_tx(&mut self) -> (&mut StateMachineRx<'d, PIO, SM>, &mut StateMachineTx<'d, PIO, SM>) { |
| 781 | (&mut self.rx, &mut self.tx) | 804 | (&mut self.rx, &mut self.tx) |
| 782 | } | 805 | } |
| 783 | } | 806 | } |
| 784 | 807 | ||
| 785 | pub struct PioCommon<'d, PIO: PioInstance> { | 808 | pub struct Common<'d, PIO: Instance> { |
| 786 | instructions_used: u32, | 809 | instructions_used: u32, |
| 787 | pio: PhantomData<&'d PIO>, | 810 | pio: PhantomData<&'d mut PIO>, |
| 788 | } | 811 | } |
| 789 | 812 | ||
| 790 | impl<'d, PIO: PioInstance> Drop for PioCommon<'d, PIO> { | 813 | impl<'d, PIO: Instance> Drop for Common<'d, PIO> { |
| 791 | fn drop(&mut self) { | 814 | fn drop(&mut self) { |
| 792 | on_pio_drop::<PIO>(); | 815 | on_pio_drop::<PIO>(); |
| 793 | } | 816 | } |
| 794 | } | 817 | } |
| 795 | 818 | ||
| 796 | pub struct PioInstanceMemory<'d, PIO: PioInstance> { | 819 | pub struct InstanceMemory<'d, PIO: Instance> { |
| 797 | used_mask: u32, | 820 | used_mask: u32, |
| 798 | pio: PhantomData<&'d PIO>, | 821 | pio: PhantomData<&'d mut PIO>, |
| 799 | } | 822 | } |
| 800 | 823 | ||
| 801 | impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { | 824 | pub struct LoadedProgram<'d, PIO: Instance> { |
| 802 | pub fn write_instr<I>(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, PIO> | 825 | pub used_memory: InstanceMemory<'d, PIO>, |
| 826 | origin: u8, | ||
| 827 | wrap: Wrap, | ||
| 828 | side_set: SideSet, | ||
| 829 | } | ||
| 830 | |||
| 831 | impl<'d, PIO: Instance> Common<'d, PIO> { | ||
| 832 | pub fn load_program<const SIZE: usize>(&mut self, prog: &RelocatedProgram<SIZE>) -> LoadedProgram<'d, PIO> { | ||
| 833 | match self.try_load_program(prog) { | ||
| 834 | Ok(r) => r, | ||
| 835 | Err(at) => panic!("Trying to write already used PIO instruction memory at {}", at), | ||
| 836 | } | ||
| 837 | } | ||
| 838 | |||
| 839 | pub fn try_load_program<const SIZE: usize>( | ||
| 840 | &mut self, | ||
| 841 | prog: &RelocatedProgram<SIZE>, | ||
| 842 | ) -> Result<LoadedProgram<'d, PIO>, usize> { | ||
| 843 | let used_memory = self.try_write_instr(prog.origin() as _, prog.code())?; | ||
| 844 | Ok(LoadedProgram { | ||
| 845 | used_memory, | ||
| 846 | origin: prog.origin(), | ||
| 847 | wrap: prog.wrap(), | ||
| 848 | side_set: prog.side_set(), | ||
| 849 | }) | ||
| 850 | } | ||
| 851 | |||
| 852 | pub fn try_write_instr<I>(&mut self, start: usize, instrs: I) -> Result<InstanceMemory<'d, PIO>, usize> | ||
| 803 | where | 853 | where |
| 804 | I: Iterator<Item = u16>, | 854 | I: Iterator<Item = u16>, |
| 805 | { | 855 | { |
| 806 | let mut used_mask = 0; | 856 | let mut used_mask = 0; |
| 807 | for (i, instr) in instrs.enumerate() { | 857 | for (i, instr) in instrs.enumerate() { |
| 808 | let addr = (i + start) as u8; | 858 | // wrapping around the end of program memory is valid, let's make use of that. |
| 809 | let mask = 1 << (addr as usize); | 859 | let addr = (i + start) % 32; |
| 810 | assert!( | 860 | let mask = 1 << addr; |
| 811 | self.instructions_used & mask == 0, | 861 | if (self.instructions_used | used_mask) & mask != 0 { |
| 812 | "Trying to write already used PIO instruction memory at {}", | 862 | return Err(addr); |
| 813 | addr | 863 | } |
| 814 | ); | ||
| 815 | unsafe { | 864 | unsafe { |
| 816 | PIO::PIO.instr_mem(addr as usize).write(|w| { | 865 | PIO::PIO.instr_mem(addr).write(|w| { |
| 817 | w.set_instr_mem(instr); | 866 | w.set_instr_mem(instr); |
| 818 | }); | 867 | }); |
| 819 | } | 868 | } |
| 820 | used_mask |= mask; | 869 | used_mask |= mask; |
| 821 | } | 870 | } |
| 822 | self.instructions_used |= used_mask; | 871 | self.instructions_used |= used_mask; |
| 823 | PioInstanceMemory { | 872 | Ok(InstanceMemory { |
| 824 | used_mask, | 873 | used_mask, |
| 825 | pio: PhantomData, | 874 | pio: PhantomData, |
| 826 | } | 875 | }) |
| 827 | } | 876 | } |
| 828 | 877 | ||
| 829 | /// Free instruction memory previously allocated with [`PioCommon::write_instr`]. | 878 | /// Free instruction memory. This is always possible but unsafe if any |
| 830 | /// This is always possible but unsafe if any state machine is still using this | 879 | /// state machine is still using this bit of memory. |
| 831 | /// bit of memory. | 880 | pub unsafe fn free_instr(&mut self, instrs: InstanceMemory<PIO>) { |
| 832 | pub unsafe fn free_instr(&mut self, instrs: PioInstanceMemory<PIO>) { | ||
| 833 | self.instructions_used &= !instrs.used_mask; | 881 | self.instructions_used &= !instrs.used_mask; |
| 834 | } | 882 | } |
| 835 | 883 | ||
| @@ -848,8 +896,8 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { | |||
| 848 | } | 896 | } |
| 849 | 897 | ||
| 850 | /// Register a pin for PIO usage. Pins will be released from the PIO block | 898 | /// Register a pin for PIO usage. Pins will be released from the PIO block |
| 851 | /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`PioCommon`] *and* | 899 | /// (i.e., have their `FUNCSEL` reset to `NULL`) when the [`Common`] *and* |
| 852 | /// all [`PioStateMachine`]s for this block have been dropped. **Other members | 900 | /// all [`StateMachine`]s for this block have been dropped. **Other members |
| 853 | /// of [`Pio`] do not keep pin registrations alive.** | 901 | /// of [`Pio`] do not keep pin registrations alive.** |
| 854 | pub fn make_pio_pin(&mut self, pin: impl Peripheral<P = impl PioPin + 'd> + 'd) -> Pin<'d, PIO> { | 902 | pub fn make_pio_pin(&mut self, pin: impl Peripheral<P = impl PioPin + 'd> + 'd) -> Pin<'d, PIO> { |
| 855 | into_ref!(pin); | 903 | into_ref!(pin); |
| @@ -863,13 +911,54 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { | |||
| 863 | pio: PhantomData::default(), | 911 | pio: PhantomData::default(), |
| 864 | } | 912 | } |
| 865 | } | 913 | } |
| 914 | |||
| 915 | pub fn apply_sm_batch(&mut self, f: impl FnOnce(&mut PioBatch<'d, PIO>)) { | ||
| 916 | let mut batch = PioBatch { | ||
| 917 | clkdiv_restart: 0, | ||
| 918 | sm_restart: 0, | ||
| 919 | sm_enable_mask: 0, | ||
| 920 | sm_enable: 0, | ||
| 921 | _pio: PhantomData, | ||
| 922 | }; | ||
| 923 | f(&mut batch); | ||
| 924 | unsafe { | ||
| 925 | PIO::PIO.ctrl().modify(|w| { | ||
| 926 | w.set_clkdiv_restart(batch.clkdiv_restart); | ||
| 927 | w.set_sm_restart(batch.sm_restart); | ||
| 928 | w.set_sm_enable((w.sm_enable() & !batch.sm_enable_mask) | batch.sm_enable); | ||
| 929 | }); | ||
| 930 | } | ||
| 931 | } | ||
| 932 | } | ||
| 933 | |||
| 934 | pub struct PioBatch<'a, PIO: Instance> { | ||
| 935 | clkdiv_restart: u8, | ||
| 936 | sm_restart: u8, | ||
| 937 | sm_enable_mask: u8, | ||
| 938 | sm_enable: u8, | ||
| 939 | _pio: PhantomData<&'a PIO>, | ||
| 940 | } | ||
| 941 | |||
| 942 | impl<'a, PIO: Instance> PioBatch<'a, PIO> { | ||
| 943 | pub fn restart_clockdiv<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>) { | ||
| 944 | self.clkdiv_restart |= 1 << SM; | ||
| 945 | } | ||
| 946 | |||
| 947 | pub fn restart<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>) { | ||
| 948 | self.clkdiv_restart |= 1 << SM; | ||
| 949 | } | ||
| 950 | |||
| 951 | pub fn set_enable<const SM: usize>(&mut self, _sm: &mut StateMachine<'a, PIO, SM>, enable: bool) { | ||
| 952 | self.sm_enable_mask |= 1 << SM; | ||
| 953 | self.sm_enable |= (enable as u8) << SM; | ||
| 954 | } | ||
| 866 | } | 955 | } |
| 867 | 956 | ||
| 868 | pub struct PioIrq<'d, PIO: PioInstance, const N: usize> { | 957 | pub struct Irq<'d, PIO: Instance, const N: usize> { |
| 869 | pio: PhantomData<&'d PIO>, | 958 | pio: PhantomData<&'d mut PIO>, |
| 870 | } | 959 | } |
| 871 | 960 | ||
| 872 | impl<'d, PIO: PioInstance, const N: usize> PioIrq<'d, PIO, N> { | 961 | impl<'d, PIO: Instance, const N: usize> Irq<'d, PIO, N> { |
| 873 | pub fn wait<'a>(&'a mut self) -> IrqFuture<'a, 'd, PIO> { | 962 | pub fn wait<'a>(&'a mut self) -> IrqFuture<'a, 'd, PIO> { |
| 874 | IrqFuture { | 963 | IrqFuture { |
| 875 | pio: PhantomData, | 964 | pio: PhantomData, |
| @@ -879,11 +968,11 @@ impl<'d, PIO: PioInstance, const N: usize> PioIrq<'d, PIO, N> { | |||
| 879 | } | 968 | } |
| 880 | 969 | ||
| 881 | #[derive(Clone)] | 970 | #[derive(Clone)] |
| 882 | pub struct PioIrqFlags<'d, PIO: PioInstance> { | 971 | pub struct IrqFlags<'d, PIO: Instance> { |
| 883 | pio: PhantomData<&'d PIO>, | 972 | pio: PhantomData<&'d mut PIO>, |
| 884 | } | 973 | } |
| 885 | 974 | ||
| 886 | impl<'d, PIO: PioInstance> PioIrqFlags<'d, PIO> { | 975 | impl<'d, PIO: Instance> IrqFlags<'d, PIO> { |
| 887 | pub fn check(&self, irq_no: u8) -> bool { | 976 | pub fn check(&self, irq_no: u8) -> bool { |
| 888 | assert!(irq_no < 8); | 977 | assert!(irq_no < 8); |
| 889 | self.check_any(1 << irq_no) | 978 | self.check_any(1 << irq_no) |
| @@ -916,49 +1005,51 @@ impl<'d, PIO: PioInstance> PioIrqFlags<'d, PIO> { | |||
| 916 | } | 1005 | } |
| 917 | } | 1006 | } |
| 918 | 1007 | ||
| 919 | pub struct Pio<'d, PIO: PioInstance> { | 1008 | pub struct Pio<'d, PIO: Instance> { |
| 920 | pub common: PioCommon<'d, PIO>, | 1009 | pub common: Common<'d, PIO>, |
| 921 | pub irq_flags: PioIrqFlags<'d, PIO>, | 1010 | pub irq_flags: IrqFlags<'d, PIO>, |
| 922 | pub irq0: PioIrq<'d, PIO, 0>, | 1011 | pub irq0: Irq<'d, PIO, 0>, |
| 923 | pub irq1: PioIrq<'d, PIO, 1>, | 1012 | pub irq1: Irq<'d, PIO, 1>, |
| 924 | pub irq2: PioIrq<'d, PIO, 2>, | 1013 | pub irq2: Irq<'d, PIO, 2>, |
| 925 | pub irq3: PioIrq<'d, PIO, 3>, | 1014 | pub irq3: Irq<'d, PIO, 3>, |
| 926 | pub sm0: PioStateMachine<'d, PIO, 0>, | 1015 | pub sm0: StateMachine<'d, PIO, 0>, |
| 927 | pub sm1: PioStateMachine<'d, PIO, 1>, | 1016 | pub sm1: StateMachine<'d, PIO, 1>, |
| 928 | pub sm2: PioStateMachine<'d, PIO, 2>, | 1017 | pub sm2: StateMachine<'d, PIO, 2>, |
| 929 | pub sm3: PioStateMachine<'d, PIO, 3>, | 1018 | pub sm3: StateMachine<'d, PIO, 3>, |
| 1019 | _pio: PhantomData<&'d mut PIO>, | ||
| 930 | } | 1020 | } |
| 931 | 1021 | ||
| 932 | impl<'d, PIO: PioInstance> Pio<'d, PIO> { | 1022 | impl<'d, PIO: Instance> Pio<'d, PIO> { |
| 933 | pub fn new(_pio: impl Peripheral<P = PIO> + 'd) -> Self { | 1023 | pub fn new(_pio: impl Peripheral<P = PIO> + 'd) -> Self { |
| 934 | PIO::state().users.store(5, Ordering::Release); | 1024 | PIO::state().users.store(5, Ordering::Release); |
| 935 | PIO::state().used_pins.store(0, Ordering::Release); | 1025 | PIO::state().used_pins.store(0, Ordering::Release); |
| 936 | Self { | 1026 | Self { |
| 937 | common: PioCommon { | 1027 | common: Common { |
| 938 | instructions_used: 0, | 1028 | instructions_used: 0, |
| 939 | pio: PhantomData, | 1029 | pio: PhantomData, |
| 940 | }, | 1030 | }, |
| 941 | irq_flags: PioIrqFlags { pio: PhantomData }, | 1031 | irq_flags: IrqFlags { pio: PhantomData }, |
| 942 | irq0: PioIrq { pio: PhantomData }, | 1032 | irq0: Irq { pio: PhantomData }, |
| 943 | irq1: PioIrq { pio: PhantomData }, | 1033 | irq1: Irq { pio: PhantomData }, |
| 944 | irq2: PioIrq { pio: PhantomData }, | 1034 | irq2: Irq { pio: PhantomData }, |
| 945 | irq3: PioIrq { pio: PhantomData }, | 1035 | irq3: Irq { pio: PhantomData }, |
| 946 | sm0: PioStateMachine { | 1036 | sm0: StateMachine { |
| 947 | rx: PioStateMachineRx { pio: PhantomData }, | 1037 | rx: StateMachineRx { pio: PhantomData }, |
| 948 | tx: PioStateMachineTx { pio: PhantomData }, | 1038 | tx: StateMachineTx { pio: PhantomData }, |
| 949 | }, | 1039 | }, |
| 950 | sm1: PioStateMachine { | 1040 | sm1: StateMachine { |
| 951 | rx: PioStateMachineRx { pio: PhantomData }, | 1041 | rx: StateMachineRx { pio: PhantomData }, |
| 952 | tx: PioStateMachineTx { pio: PhantomData }, | 1042 | tx: StateMachineTx { pio: PhantomData }, |
| 953 | }, | 1043 | }, |
| 954 | sm2: PioStateMachine { | 1044 | sm2: StateMachine { |
| 955 | rx: PioStateMachineRx { pio: PhantomData }, | 1045 | rx: StateMachineRx { pio: PhantomData }, |
| 956 | tx: PioStateMachineTx { pio: PhantomData }, | 1046 | tx: StateMachineTx { pio: PhantomData }, |
| 957 | }, | 1047 | }, |
| 958 | sm3: PioStateMachine { | 1048 | sm3: StateMachine { |
| 959 | rx: PioStateMachineRx { pio: PhantomData }, | 1049 | rx: StateMachineRx { pio: PhantomData }, |
| 960 | tx: PioStateMachineTx { pio: PhantomData }, | 1050 | tx: StateMachineTx { pio: PhantomData }, |
| 961 | }, | 1051 | }, |
| 1052 | _pio: PhantomData, | ||
| 962 | } | 1053 | } |
| 963 | } | 1054 | } |
| 964 | } | 1055 | } |
| @@ -974,12 +1065,13 @@ pub struct State { | |||
| 974 | used_pins: AtomicU32, | 1065 | used_pins: AtomicU32, |
| 975 | } | 1066 | } |
| 976 | 1067 | ||
| 977 | fn on_pio_drop<PIO: PioInstance>() { | 1068 | fn on_pio_drop<PIO: Instance>() { |
| 978 | let state = PIO::state(); | 1069 | let state = PIO::state(); |
| 979 | if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { | 1070 | if state.users.fetch_sub(1, Ordering::AcqRel) == 1 { |
| 980 | let used_pins = state.used_pins.load(Ordering::Relaxed); | 1071 | let used_pins = state.used_pins.load(Ordering::Relaxed); |
| 981 | let null = Gpio0ctrlFuncsel::NULL.0; | 1072 | let null = Gpio0ctrlFuncsel::NULL.0; |
| 982 | for i in 0..32 { | 1073 | // we only have 30 pins. don't test the other two since gpio() asserts. |
| 1074 | for i in 0..30 { | ||
| 983 | if used_pins & (1 << i) != 0 { | 1075 | if used_pins & (1 << i) != 0 { |
| 984 | unsafe { | 1076 | unsafe { |
| 985 | pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null)); | 1077 | pac::IO_BANK0.gpio(i).ctrl().write(|w| w.set_funcsel(null)); |
| @@ -994,7 +1086,7 @@ mod sealed { | |||
| 994 | 1086 | ||
| 995 | pub trait PioPin {} | 1087 | pub trait PioPin {} |
| 996 | 1088 | ||
| 997 | pub trait PioInstance { | 1089 | pub trait Instance { |
| 998 | const PIO_NO: u8; | 1090 | const PIO_NO: u8; |
| 999 | const PIO: &'static crate::pac::pio::Pio; | 1091 | const PIO: &'static crate::pac::pio::Pio; |
| 1000 | const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; | 1092 | const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; |
| @@ -1011,16 +1103,16 @@ mod sealed { | |||
| 1011 | } | 1103 | } |
| 1012 | } | 1104 | } |
| 1013 | 1105 | ||
| 1014 | pub trait PioInstance: sealed::PioInstance + Sized + Unpin {} | 1106 | pub trait Instance: sealed::Instance + Sized + Unpin {} |
| 1015 | 1107 | ||
| 1016 | macro_rules! impl_pio { | 1108 | macro_rules! impl_pio { |
| 1017 | ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { | 1109 | ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { |
| 1018 | impl sealed::PioInstance for peripherals::$name { | 1110 | impl sealed::Instance for peripherals::$name { |
| 1019 | const PIO_NO: u8 = $pio; | 1111 | const PIO_NO: u8 = $pio; |
| 1020 | const PIO: &'static pac::pio::Pio = &pac::$pac; | 1112 | const PIO: &'static pac::pio::Pio = &pac::$pac; |
| 1021 | const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel; | 1113 | const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel; |
| 1022 | } | 1114 | } |
| 1023 | impl PioInstance for peripherals::$name {} | 1115 | impl Instance for peripherals::$name {} |
| 1024 | }; | 1116 | }; |
| 1025 | } | 1117 | } |
| 1026 | 1118 | ||
diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs index 81abdb666..25393b476 100644 --- a/embassy-rp/src/pio_instr_util.rs +++ b/embassy-rp/src/pio_instr_util.rs | |||
| @@ -1,8 +1,8 @@ | |||
| 1 | use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination}; | 1 | use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination}; |
| 2 | 2 | ||
| 3 | use crate::pio::{PioInstance, PioStateMachine}; | 3 | use crate::pio::{Instance, StateMachine}; |
| 4 | 4 | ||
| 5 | pub fn set_x<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, value: u32) { | 5 | pub unsafe fn set_x<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, value: u32) { |
| 6 | const OUT: u16 = InstructionOperands::OUT { | 6 | const OUT: u16 = InstructionOperands::OUT { |
| 7 | destination: OutDestination::X, | 7 | destination: OutDestination::X, |
| 8 | bit_count: 32, | 8 | bit_count: 32, |
| @@ -12,7 +12,7 @@ pub fn set_x<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM | |||
| 12 | sm.exec_instr(OUT); | 12 | sm.exec_instr(OUT); |
| 13 | } | 13 | } |
| 14 | 14 | ||
| 15 | pub fn get_x<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>) -> u32 { | 15 | pub unsafe fn get_x<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>) -> u32 { |
| 16 | const IN: u16 = InstructionOperands::IN { | 16 | const IN: u16 = InstructionOperands::IN { |
| 17 | source: InSource::X, | 17 | source: InSource::X, |
| 18 | bit_count: 32, | 18 | bit_count: 32, |
| @@ -22,7 +22,7 @@ pub fn get_x<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM | |||
| 22 | sm.rx().pull() | 22 | sm.rx().pull() |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | pub fn set_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, value: u32) { | 25 | pub unsafe fn set_y<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, value: u32) { |
| 26 | const OUT: u16 = InstructionOperands::OUT { | 26 | const OUT: u16 = InstructionOperands::OUT { |
| 27 | destination: OutDestination::Y, | 27 | destination: OutDestination::Y, |
| 28 | bit_count: 32, | 28 | bit_count: 32, |
| @@ -32,7 +32,7 @@ pub fn set_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM | |||
| 32 | sm.exec_instr(OUT); | 32 | sm.exec_instr(OUT); |
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | pub fn get_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>) -> u32 { | 35 | pub unsafe fn get_y<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>) -> u32 { |
| 36 | const IN: u16 = InstructionOperands::IN { | 36 | const IN: u16 = InstructionOperands::IN { |
| 37 | source: InSource::Y, | 37 | source: InSource::Y, |
| 38 | bit_count: 32, | 38 | bit_count: 32, |
| @@ -43,7 +43,7 @@ pub fn get_y<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM | |||
| 43 | sm.rx().pull() | 43 | sm.rx().pull() |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | pub fn set_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u8) { | 46 | pub unsafe fn set_pindir<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, data: u8) { |
| 47 | let set: u16 = InstructionOperands::SET { | 47 | let set: u16 = InstructionOperands::SET { |
| 48 | destination: SetDestination::PINDIRS, | 48 | destination: SetDestination::PINDIRS, |
| 49 | data, | 49 | data, |
| @@ -52,7 +52,7 @@ pub fn set_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PI | |||
| 52 | sm.exec_instr(set); | 52 | sm.exec_instr(set); |
| 53 | } | 53 | } |
| 54 | 54 | ||
| 55 | pub fn set_pin<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u8) { | 55 | pub unsafe fn set_pin<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, data: u8) { |
| 56 | let set: u16 = InstructionOperands::SET { | 56 | let set: u16 = InstructionOperands::SET { |
| 57 | destination: SetDestination::PINS, | 57 | destination: SetDestination::PINS, |
| 58 | data, | 58 | data, |
| @@ -61,7 +61,7 @@ pub fn set_pin<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, | |||
| 61 | sm.exec_instr(set); | 61 | sm.exec_instr(set); |
| 62 | } | 62 | } |
| 63 | 63 | ||
| 64 | pub fn set_out_pin<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u32) { | 64 | pub unsafe fn set_out_pin<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, data: u32) { |
| 65 | const OUT: u16 = InstructionOperands::OUT { | 65 | const OUT: u16 = InstructionOperands::OUT { |
| 66 | destination: OutDestination::PINS, | 66 | destination: OutDestination::PINS, |
| 67 | bit_count: 32, | 67 | bit_count: 32, |
| @@ -70,7 +70,7 @@ pub fn set_out_pin<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<P | |||
| 70 | sm.tx().push(data); | 70 | sm.tx().push(data); |
| 71 | sm.exec_instr(OUT); | 71 | sm.exec_instr(OUT); |
| 72 | } | 72 | } |
| 73 | pub fn set_out_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, data: u32) { | 73 | pub unsafe fn set_out_pindir<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, data: u32) { |
| 74 | const OUT: u16 = InstructionOperands::OUT { | 74 | const OUT: u16 = InstructionOperands::OUT { |
| 75 | destination: OutDestination::PINDIRS, | 75 | destination: OutDestination::PINDIRS, |
| 76 | bit_count: 32, | 76 | bit_count: 32, |
| @@ -80,7 +80,7 @@ pub fn set_out_pindir<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachin | |||
| 80 | sm.exec_instr(OUT); | 80 | sm.exec_instr(OUT); |
| 81 | } | 81 | } |
| 82 | 82 | ||
| 83 | pub fn exec_jmp<PIO: PioInstance, const SM: usize>(sm: &mut PioStateMachine<PIO, SM>, to_addr: u8) { | 83 | pub unsafe fn exec_jmp<PIO: Instance, const SM: usize>(sm: &mut StateMachine<PIO, SM>, to_addr: u8) { |
| 84 | let jmp: u16 = InstructionOperands::JMP { | 84 | let jmp: u16 = InstructionOperands::JMP { |
| 85 | address: to_addr, | 85 | address: to_addr, |
| 86 | condition: JmpCondition::Always, | 86 | condition: JmpCondition::Always, |
diff --git a/embassy-rp/src/relocate.rs b/embassy-rp/src/relocate.rs index f36170e03..9cb279ccd 100644 --- a/embassy-rp/src/relocate.rs +++ b/embassy-rp/src/relocate.rs | |||
| @@ -26,11 +26,7 @@ where | |||
| 26 | Some(if instr & 0b1110_0000_0000_0000 == 0 { | 26 | Some(if instr & 0b1110_0000_0000_0000 == 0 { |
| 27 | // this is a JMP instruction -> add offset to address | 27 | // this is a JMP instruction -> add offset to address |
| 28 | let address = (instr & 0b1_1111) as u8; | 28 | let address = (instr & 0b1_1111) as u8; |
| 29 | let address = address + self.offset; | 29 | let address = address.wrapping_add(self.offset) % 32; |
| 30 | assert!( | ||
| 31 | address < pio::RP2040_MAX_PROGRAM_SIZE as u8, | ||
| 32 | "Invalid JMP out of the program after offset addition" | ||
| 33 | ); | ||
| 34 | instr & (!0b11111) | address as u16 | 30 | instr & (!0b11111) | address as u16 |
| 35 | } else { | 31 | } else { |
| 36 | instr | 32 | instr |
| @@ -62,8 +58,8 @@ impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> { | |||
| 62 | let wrap = self.program.wrap; | 58 | let wrap = self.program.wrap; |
| 63 | let origin = self.origin; | 59 | let origin = self.origin; |
| 64 | Wrap { | 60 | Wrap { |
| 65 | source: wrap.source + origin, | 61 | source: wrap.source.wrapping_add(origin) % 32, |
| 66 | target: wrap.target + origin, | 62 | target: wrap.target.wrapping_add(origin) % 32, |
| 67 | } | 63 | } |
| 68 | } | 64 | } |
| 69 | 65 | ||
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 57f6f5c67..d2829df99 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml | |||
| @@ -22,6 +22,8 @@ lorawan = { version = "0.7.3", default-features = false, features = ["default-cr | |||
| 22 | 22 | ||
| 23 | defmt = "0.3" | 23 | defmt = "0.3" |
| 24 | defmt-rtt = "0.4" | 24 | defmt-rtt = "0.4" |
| 25 | fixed = "1.23.1" | ||
| 26 | fixed-macro = "1.2" | ||
| 25 | 27 | ||
| 26 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } | 28 | #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } |
| 27 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } | 29 | cortex-m = { version = "0.7.6", features = ["inline-asm"] } |
diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 4e0ab5e3c..12484e882 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs | |||
| @@ -2,14 +2,16 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | use defmt::info; | 4 | use defmt::info; |
| 5 | use embassy_embedded_hal::SetConfig; | ||
| 5 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 6 | use embassy_rp::peripherals::PIO0; | 7 | use embassy_rp::peripherals::PIO0; |
| 7 | use embassy_rp::pio::{Pio, PioCommon, PioIrq, PioPin, PioStateMachine, ShiftDirection}; | 8 | use embassy_rp::pio::{Common, Config, Irq, Pio, PioPin, ShiftDirection, StateMachine}; |
| 8 | use embassy_rp::pio_instr_util; | ||
| 9 | use embassy_rp::relocate::RelocatedProgram; | 9 | use embassy_rp::relocate::RelocatedProgram; |
| 10 | use fixed::traits::ToFixed; | ||
| 11 | use fixed_macro::types::U56F8; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | 12 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 13 | ||
| 12 | fn setup_pio_task_sm0(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0, 0>, pin: impl PioPin) { | 14 | fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { |
| 13 | // Setup sm0 | 15 | // Setup sm0 |
| 14 | 16 | ||
| 15 | // Send data serially to pin | 17 | // Send data serially to pin |
| @@ -22,22 +24,18 @@ fn setup_pio_task_sm0(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0, | |||
| 22 | ); | 24 | ); |
| 23 | 25 | ||
| 24 | let relocated = RelocatedProgram::new(&prg.program); | 26 | let relocated = RelocatedProgram::new(&prg.program); |
| 27 | let mut cfg = Config::default(); | ||
| 28 | cfg.use_program(&pio.load_program(&relocated), &[]); | ||
| 25 | let out_pin = pio.make_pio_pin(pin); | 29 | let out_pin = pio.make_pio_pin(pin); |
| 26 | let pio_pins = [&out_pin]; | 30 | cfg.set_out_pins(&[&out_pin]); |
| 27 | sm.set_out_pins(&pio_pins); | 31 | cfg.set_set_pins(&[&out_pin]); |
| 28 | pio.write_instr(relocated.origin() as usize, relocated.code()); | 32 | cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed(); |
| 29 | pio_instr_util::exec_jmp(sm, relocated.origin()); | 33 | cfg.shift_out.auto_fill = true; |
| 30 | sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); | 34 | sm.set_config(&cfg); |
| 31 | sm.set_set_range(0, 1); | ||
| 32 | let pio::Wrap { source, target } = relocated.wrap(); | ||
| 33 | sm.set_wrap(source, target); | ||
| 34 | |||
| 35 | sm.set_autopull(true); | ||
| 36 | sm.set_out_shift_dir(ShiftDirection::Left); | ||
| 37 | } | 35 | } |
| 38 | 36 | ||
| 39 | #[embassy_executor::task] | 37 | #[embassy_executor::task] |
| 40 | async fn pio_task_sm0(mut sm: PioStateMachine<'static, PIO0, 0>) { | 38 | async fn pio_task_sm0(mut sm: StateMachine<'static, PIO0, 0>) { |
| 41 | sm.set_enable(true); | 39 | sm.set_enable(true); |
| 42 | 40 | ||
| 43 | let mut v = 0x0f0caffa; | 41 | let mut v = 0x0f0caffa; |
| @@ -48,26 +46,23 @@ async fn pio_task_sm0(mut sm: PioStateMachine<'static, PIO0, 0>) { | |||
| 48 | } | 46 | } |
| 49 | } | 47 | } |
| 50 | 48 | ||
| 51 | fn setup_pio_task_sm1(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0, 1>) { | 49 | fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 1>) { |
| 52 | // Setupm sm1 | 50 | // Setupm sm1 |
| 53 | 51 | ||
| 54 | // Read 0b10101 repeatedly until ISR is full | 52 | // Read 0b10101 repeatedly until ISR is full |
| 55 | let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); | 53 | let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); |
| 56 | 54 | ||
| 57 | let relocated = RelocatedProgram::new(&prg.program); | 55 | let relocated = RelocatedProgram::new(&prg.program); |
| 58 | pio.write_instr(relocated.origin() as usize, relocated.code()); | 56 | let mut cfg = Config::default(); |
| 59 | pio_instr_util::exec_jmp(sm, relocated.origin()); | 57 | cfg.use_program(&pio.load_program(&relocated), &[]); |
| 60 | sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); | 58 | cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); |
| 61 | sm.set_set_range(0, 0); | 59 | cfg.shift_in.auto_fill = true; |
| 62 | let pio::Wrap { source, target } = relocated.wrap(); | 60 | cfg.shift_in.direction = ShiftDirection::Right; |
| 63 | sm.set_wrap(source, target); | 61 | sm.set_config(&cfg); |
| 64 | |||
| 65 | sm.set_autopush(true); | ||
| 66 | sm.set_in_shift_dir(ShiftDirection::Right); | ||
| 67 | } | 62 | } |
| 68 | 63 | ||
| 69 | #[embassy_executor::task] | 64 | #[embassy_executor::task] |
| 70 | async fn pio_task_sm1(mut sm: PioStateMachine<'static, PIO0, 1>) { | 65 | async fn pio_task_sm1(mut sm: StateMachine<'static, PIO0, 1>) { |
| 71 | sm.set_enable(true); | 66 | sm.set_enable(true); |
| 72 | loop { | 67 | loop { |
| 73 | let rx = sm.rx().wait_pull().await; | 68 | let rx = sm.rx().wait_pull().await; |
| @@ -75,7 +70,7 @@ async fn pio_task_sm1(mut sm: PioStateMachine<'static, PIO0, 1>) { | |||
| 75 | } | 70 | } |
| 76 | } | 71 | } |
| 77 | 72 | ||
| 78 | fn setup_pio_task_sm2(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0, 2>) { | 73 | fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 2>) { |
| 79 | // Setup sm2 | 74 | // Setup sm2 |
| 80 | 75 | ||
| 81 | // Repeatedly trigger IRQ 3 | 76 | // Repeatedly trigger IRQ 3 |
| @@ -89,17 +84,14 @@ fn setup_pio_task_sm2(pio: &mut PioCommon<PIO0>, sm: &mut PioStateMachine<PIO0, | |||
| 89 | ".wrap", | 84 | ".wrap", |
| 90 | ); | 85 | ); |
| 91 | let relocated = RelocatedProgram::new(&prg.program); | 86 | let relocated = RelocatedProgram::new(&prg.program); |
| 92 | pio.write_instr(relocated.origin() as usize, relocated.code()); | 87 | let mut cfg = Config::default(); |
| 93 | 88 | cfg.use_program(&pio.load_program(&relocated), &[]); | |
| 94 | let pio::Wrap { source, target } = relocated.wrap(); | 89 | cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); |
| 95 | sm.set_wrap(source, target); | 90 | sm.set_config(&cfg); |
| 96 | |||
| 97 | pio_instr_util::exec_jmp(sm, relocated.origin()); | ||
| 98 | sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); | ||
| 99 | } | 91 | } |
| 100 | 92 | ||
| 101 | #[embassy_executor::task] | 93 | #[embassy_executor::task] |
| 102 | async fn pio_task_sm2(mut irq: PioIrq<'static, PIO0, 3>, mut sm: PioStateMachine<'static, PIO0, 2>) { | 94 | async fn pio_task_sm2(mut irq: Irq<'static, PIO0, 3>, mut sm: StateMachine<'static, PIO0, 2>) { |
| 103 | sm.set_enable(true); | 95 | sm.set_enable(true); |
| 104 | loop { | 96 | loop { |
| 105 | irq.wait().await; | 97 | irq.wait().await; |
diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index c664482e5..7f85288bf 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs | |||
| @@ -2,11 +2,14 @@ | |||
| 2 | #![no_main] | 2 | #![no_main] |
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | use defmt::info; | 4 | use defmt::info; |
| 5 | use embassy_embedded_hal::SetConfig; | ||
| 5 | use embassy_executor::Spawner; | 6 | use embassy_executor::Spawner; |
| 6 | use embassy_futures::join::join; | 7 | use embassy_futures::join::join; |
| 7 | use embassy_rp::pio::{Pio, ShiftDirection}; | 8 | use embassy_rp::pio::{Config, Pio, ShiftConfig, ShiftDirection}; |
| 8 | use embassy_rp::relocate::RelocatedProgram; | 9 | use embassy_rp::relocate::RelocatedProgram; |
| 9 | use embassy_rp::{pio_instr_util, Peripheral}; | 10 | use embassy_rp::Peripheral; |
| 11 | use fixed::traits::ToFixed; | ||
| 12 | use fixed_macro::types::U56F8; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 11 | 14 | ||
| 12 | fn swap_nibbles(v: u32) -> u32 { | 15 | fn swap_nibbles(v: u32) -> u32 { |
| @@ -38,18 +41,21 @@ async fn main(_spawner: Spawner) { | |||
| 38 | ); | 41 | ); |
| 39 | 42 | ||
| 40 | let relocated = RelocatedProgram::new(&prg.program); | 43 | let relocated = RelocatedProgram::new(&prg.program); |
| 41 | common.write_instr(relocated.origin() as usize, relocated.code()); | 44 | let mut cfg = Config::default(); |
| 42 | pio_instr_util::exec_jmp(&mut sm, relocated.origin()); | 45 | cfg.use_program(&common.load_program(&relocated), &[]); |
| 43 | sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); | 46 | cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); |
| 44 | let pio::Wrap { source, target } = relocated.wrap(); | 47 | cfg.shift_in = ShiftConfig { |
| 45 | sm.set_wrap(source, target); | 48 | auto_fill: true, |
| 46 | sm.set_autopull(true); | 49 | threshold: 32, |
| 47 | sm.set_autopush(true); | 50 | direction: ShiftDirection::Left, |
| 48 | sm.set_pull_threshold(32); | 51 | }; |
| 49 | sm.set_push_threshold(32); | 52 | cfg.shift_out = ShiftConfig { |
| 50 | sm.set_out_shift_dir(ShiftDirection::Right); | 53 | auto_fill: true, |
| 51 | sm.set_in_shift_dir(ShiftDirection::Left); | 54 | threshold: 32, |
| 55 | direction: ShiftDirection::Right, | ||
| 56 | }; | ||
| 52 | 57 | ||
| 58 | sm.set_config(&cfg); | ||
| 53 | sm.set_enable(true); | 59 | sm.set_enable(true); |
| 54 | 60 | ||
| 55 | let mut dma_out_ref = p.DMA_CH0.into_ref(); | 61 | let mut dma_out_ref = p.DMA_CH0.into_ref(); |
diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index f76d334e7..088fd5649 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs | |||
| @@ -4,11 +4,12 @@ | |||
| 4 | 4 | ||
| 5 | use core::fmt::Write; | 5 | use core::fmt::Write; |
| 6 | 6 | ||
| 7 | use embassy_embedded_hal::SetConfig; | ||
| 7 | use embassy_executor::Spawner; | 8 | use embassy_executor::Spawner; |
| 8 | use embassy_rp::dma::{AnyChannel, Channel}; | 9 | use embassy_rp::dma::{AnyChannel, Channel}; |
| 9 | use embassy_rp::peripherals::PIO0; | 10 | use embassy_rp::peripherals::PIO0; |
| 10 | use embassy_rp::pio::{FifoJoin, Pio, PioPin, PioStateMachine, ShiftDirection}; | 11 | use embassy_rp::pio::{Config, Direction, FifoJoin, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; |
| 11 | use embassy_rp::pwm::{Config, Pwm}; | 12 | use embassy_rp::pwm::{self, Pwm}; |
| 12 | use embassy_rp::relocate::RelocatedProgram; | 13 | use embassy_rp::relocate::RelocatedProgram; |
| 13 | use embassy_rp::{into_ref, Peripheral, PeripheralRef}; | 14 | use embassy_rp::{into_ref, Peripheral, PeripheralRef}; |
| 14 | use embassy_time::{Duration, Instant, Timer}; | 15 | use embassy_time::{Duration, Instant, Timer}; |
| @@ -29,7 +30,7 @@ async fn main(_spawner: Spawner) { | |||
| 29 | let p = embassy_rp::init(Default::default()); | 30 | let p = embassy_rp::init(Default::default()); |
| 30 | 31 | ||
| 31 | let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { | 32 | let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { |
| 32 | let mut c = Config::default(); | 33 | let mut c = pwm::Config::default(); |
| 33 | c.divider = 125.into(); | 34 | c.divider = 125.into(); |
| 34 | c.top = 100; | 35 | c.top = 100; |
| 35 | c.compare_b = 50; | 36 | c.compare_b = 50; |
| @@ -64,7 +65,7 @@ async fn main(_spawner: Spawner) { | |||
| 64 | 65 | ||
| 65 | pub struct HD44780<'l> { | 66 | pub struct HD44780<'l> { |
| 66 | dma: PeripheralRef<'l, AnyChannel>, | 67 | dma: PeripheralRef<'l, AnyChannel>, |
| 67 | sm: PioStateMachine<'l, PIO0, 0>, | 68 | sm: StateMachine<'l, PIO0, 0>, |
| 68 | 69 | ||
| 69 | buf: [u8; 40], | 70 | buf: [u8; 40], |
| 70 | } | 71 | } |
| @@ -83,7 +84,6 @@ impl<'l> HD44780<'l> { | |||
| 83 | ) -> HD44780<'l> { | 84 | ) -> HD44780<'l> { |
| 84 | into_ref!(dma); | 85 | into_ref!(dma); |
| 85 | 86 | ||
| 86 | let db7pin = db7.pin(); | ||
| 87 | let Pio { | 87 | let Pio { |
| 88 | mut common, | 88 | mut common, |
| 89 | mut irq0, | 89 | mut irq0, |
| @@ -95,6 +95,7 @@ impl<'l> HD44780<'l> { | |||
| 95 | let prg = pio_proc::pio_asm!( | 95 | let prg = pio_proc::pio_asm!( |
| 96 | r#" | 96 | r#" |
| 97 | .side_set 1 opt | 97 | .side_set 1 opt |
| 98 | .origin 20 | ||
| 98 | 99 | ||
| 99 | loop: | 100 | loop: |
| 100 | out x, 24 | 101 | out x, 24 |
| @@ -115,27 +116,20 @@ impl<'l> HD44780<'l> { | |||
| 115 | let db6 = common.make_pio_pin(db6); | 116 | let db6 = common.make_pio_pin(db6); |
| 116 | let db7 = common.make_pio_pin(db7); | 117 | let db7 = common.make_pio_pin(db7); |
| 117 | 118 | ||
| 118 | sm0.set_set_pins(&[&rs, &rw]); | 119 | sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); |
| 119 | embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11); | ||
| 120 | sm0.set_set_pins(&[&e]); | ||
| 121 | embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b1); | ||
| 122 | sm0.set_set_pins(&[&db4, &db5, &db6, &db7]); | ||
| 123 | embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11111); | ||
| 124 | 120 | ||
| 125 | let relocated = RelocatedProgram::new(&prg.program); | 121 | let relocated = RelocatedProgram::new(&prg.program); |
| 126 | common.write_instr(relocated.origin() as usize, relocated.code()); | 122 | let mut cfg = Config::default(); |
| 127 | embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); | 123 | cfg.use_program(&common.load_program(&relocated), &[&e]); |
| 128 | sm0.set_clkdiv(125 * 256); | 124 | cfg.clock_divider = 125u8.into(); |
| 129 | let pio::Wrap { source, target } = relocated.wrap(); | 125 | cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); |
| 130 | sm0.set_wrap(source, target); | 126 | cfg.shift_out = ShiftConfig { |
| 131 | sm0.set_side_enable(true); | 127 | auto_fill: true, |
| 132 | sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); | 128 | direction: ShiftDirection::Left, |
| 133 | sm0.set_sideset_base_pin(&e); | 129 | threshold: 32, |
| 134 | sm0.set_sideset_count(2); | 130 | }; |
| 135 | sm0.set_out_shift_dir(ShiftDirection::Left); | 131 | cfg.fifo_join = FifoJoin::TxOnly; |
| 136 | sm0.set_fifo_join(FifoJoin::TxOnly); | 132 | sm0.set_config(&cfg); |
| 137 | sm0.set_autopull(true); | ||
| 138 | sm0.set_pull_threshold(32); | ||
| 139 | 133 | ||
| 140 | sm0.set_enable(true); | 134 | sm0.set_enable(true); |
| 141 | // init to 8 bit thrice | 135 | // init to 8 bit thrice |
| @@ -155,7 +149,7 @@ impl<'l> HD44780<'l> { | |||
| 155 | // many side sets are only there to free up a delay bit! | 149 | // many side sets are only there to free up a delay bit! |
| 156 | let prg = pio_proc::pio_asm!( | 150 | let prg = pio_proc::pio_asm!( |
| 157 | r#" | 151 | r#" |
| 158 | .origin 7 | 152 | .origin 27 |
| 159 | .side_set 1 | 153 | .side_set 1 |
| 160 | 154 | ||
| 161 | .wrap_target | 155 | .wrap_target |
| @@ -199,19 +193,15 @@ impl<'l> HD44780<'l> { | |||
| 199 | ); | 193 | ); |
| 200 | 194 | ||
| 201 | let relocated = RelocatedProgram::new(&prg.program); | 195 | let relocated = RelocatedProgram::new(&prg.program); |
| 202 | common.write_instr(relocated.origin() as usize, relocated.code()); | 196 | let mut cfg = Config::default(); |
| 203 | embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); | 197 | cfg.use_program(&common.load_program(&relocated), &[&e]); |
| 204 | let pio::Wrap { source, target } = relocated.wrap(); | 198 | cfg.clock_divider = 8u8.into(); // ~64ns/insn |
| 205 | sm0.set_clkdiv(8 * 256); // ~64ns/insn | 199 | cfg.set_jmp_pin(&db7); |
| 206 | sm0.set_side_enable(false); | 200 | cfg.set_set_pins(&[&rs, &rw]); |
| 207 | sm0.set_jmp_pin(db7pin); | 201 | cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); |
| 208 | sm0.set_wrap(source, target); | 202 | cfg.shift_out.direction = ShiftDirection::Left; |
| 209 | sm0.set_set_pins(&[&rs, &rw]); | 203 | cfg.fifo_join = FifoJoin::TxOnly; |
| 210 | sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); | 204 | sm0.set_config(&cfg); |
| 211 | sm0.set_sideset_base_pin(&e); | ||
| 212 | sm0.set_sideset_count(1); | ||
| 213 | sm0.set_out_shift_dir(ShiftDirection::Left); | ||
| 214 | sm0.set_fifo_join(FifoJoin::TxOnly); | ||
| 215 | 205 | ||
| 216 | sm0.set_enable(true); | 206 | sm0.set_enable(true); |
| 217 | 207 | ||
diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index c9c701a70..d7c4742d8 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs | |||
| @@ -3,19 +3,20 @@ | |||
| 3 | #![feature(type_alias_impl_trait)] | 3 | #![feature(type_alias_impl_trait)] |
| 4 | 4 | ||
| 5 | use defmt::*; | 5 | use defmt::*; |
| 6 | use embassy_embedded_hal::SetConfig; | ||
| 6 | use embassy_executor::Spawner; | 7 | use embassy_executor::Spawner; |
| 7 | use embassy_rp::pio::{FifoJoin, Pio, PioCommon, PioInstance, PioPin, PioStateMachine, ShiftDirection}; | 8 | use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; |
| 8 | use embassy_rp::pio_instr_util; | ||
| 9 | use embassy_rp::relocate::RelocatedProgram; | 9 | use embassy_rp::relocate::RelocatedProgram; |
| 10 | use embassy_time::{Duration, Timer}; | 10 | use embassy_time::{Duration, Timer}; |
| 11 | use fixed_macro::fixed; | ||
| 11 | use smart_leds::RGB8; | 12 | use smart_leds::RGB8; |
| 12 | use {defmt_rtt as _, panic_probe as _}; | 13 | use {defmt_rtt as _, panic_probe as _}; |
| 13 | pub struct Ws2812<'d, P: PioInstance, const S: usize> { | 14 | pub struct Ws2812<'d, P: Instance, const S: usize> { |
| 14 | sm: PioStateMachine<'d, P, S>, | 15 | sm: StateMachine<'d, P, S>, |
| 15 | } | 16 | } |
| 16 | 17 | ||
| 17 | impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> { | 18 | impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { |
| 18 | pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachine<'d, P, S>, pin: impl PioPin) -> Self { | 19 | pub fn new(mut pio: Common<'d, P>, mut sm: StateMachine<'d, P, S>, pin: impl PioPin) -> Self { |
| 19 | // Setup sm0 | 20 | // Setup sm0 |
| 20 | 21 | ||
| 21 | // prepare the PIO program | 22 | // prepare the PIO program |
| @@ -44,41 +45,30 @@ impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> { | |||
| 44 | a.bind(&mut wrap_source); | 45 | a.bind(&mut wrap_source); |
| 45 | 46 | ||
| 46 | let prg = a.assemble_with_wrap(wrap_source, wrap_target); | 47 | let prg = a.assemble_with_wrap(wrap_source, wrap_target); |
| 47 | 48 | let mut cfg = Config::default(); | |
| 48 | let relocated = RelocatedProgram::new(&prg); | ||
| 49 | pio.write_instr(relocated.origin() as usize, relocated.code()); | ||
| 50 | pio_instr_util::exec_jmp(&mut sm, relocated.origin()); | ||
| 51 | 49 | ||
| 52 | // Pin config | 50 | // Pin config |
| 53 | let out_pin = pio.make_pio_pin(pin); | 51 | let out_pin = pio.make_pio_pin(pin); |
| 54 | sm.set_set_pins(&[&out_pin]); | ||
| 55 | sm.set_sideset_base_pin(&out_pin); | ||
| 56 | sm.set_sideset_count(1); | ||
| 57 | 52 | ||
| 58 | // Clock config | 53 | let relocated = RelocatedProgram::new(&prg); |
| 59 | // TODO CLOCK_FREQ should come from embassy_rp | 54 | cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); |
| 60 | const CLOCK_FREQ: u32 = 125_000_000; | ||
| 61 | const WS2812_FREQ: u32 = 800_000; | ||
| 62 | |||
| 63 | let bit_freq = WS2812_FREQ * CYCLES_PER_BIT; | ||
| 64 | let mut int = CLOCK_FREQ / bit_freq; | ||
| 65 | let rem = CLOCK_FREQ - (int * bit_freq); | ||
| 66 | let frac = (rem * 256) / bit_freq; | ||
| 67 | // 65536.0 is represented as 0 in the pio's clock divider | ||
| 68 | if int == 65536 { | ||
| 69 | int = 0; | ||
| 70 | } | ||
| 71 | 55 | ||
| 72 | sm.set_clkdiv((int << 8) | frac); | 56 | // Clock config, measured in kHz to avoid overflows |
| 73 | let pio::Wrap { source, target } = relocated.wrap(); | 57 | // TODO CLOCK_FREQ should come from embassy_rp |
| 74 | sm.set_wrap(source, target); | 58 | let clock_freq = fixed!(125_000: U24F8); |
| 59 | let ws2812_freq = fixed!(800: U24F8); | ||
| 60 | let bit_freq = ws2812_freq * CYCLES_PER_BIT; | ||
| 61 | cfg.clock_divider = clock_freq / bit_freq; | ||
| 75 | 62 | ||
| 76 | // FIFO config | 63 | // FIFO config |
| 77 | sm.set_autopull(true); | 64 | cfg.fifo_join = FifoJoin::TxOnly; |
| 78 | sm.set_fifo_join(FifoJoin::TxOnly); | 65 | cfg.shift_out = ShiftConfig { |
| 79 | sm.set_pull_threshold(24); | 66 | auto_fill: true, |
| 80 | sm.set_out_shift_dir(ShiftDirection::Left); | 67 | threshold: 24, |
| 81 | 68 | direction: ShiftDirection::Left, | |
| 69 | }; | ||
| 70 | |||
| 71 | sm.set_config(&cfg); | ||
| 82 | sm.set_enable(true); | 72 | sm.set_enable(true); |
| 83 | 73 | ||
| 84 | Self { sm } | 74 | Self { sm } |
