diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2023-05-08 09:17:51 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-05-08 09:17:51 +0000 |
| commit | 79c60f4a7d17613269f795df867c3c83ca90cbcc (patch) | |
| tree | 34d841fd742d6b3e7fa4919a9bd60f5bc67e0afb | |
| parent | a9c7263ba02181fc4d54975a63d8a1e92426dff3 (diff) | |
| parent | db9b8eb88f095687ed624171cf79fbacf06c199a (diff) | |
Merge #1434
1434: rp pio IV (the voyage home) r=Dirbaio a=pennae
this should hopefully be the last entry in this series. after this we'll have a reasonably safe interface to pio, both for configuration and at runtime. pio now looks very much like the other peripherals (though not exactly, seeing how state machines can't be constructed from a config but only have it applied to them later). the generated code for `StateMachine::set_config` is still larger than we'd like (almost 300 bytes at Oz), but it's a great step up in safety from the previous interface at approximately the same code space cost.
Co-authored-by: pennae <[email protected]>
| -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 } |
