diff options
| -rw-r--r-- | embassy-rp/Cargo.toml | 8 | ||||
| -rw-r--r-- | embassy-rp/src/gpio.rs | 42 | ||||
| -rw-r--r-- | embassy-rp/src/lib.rs | 9 | ||||
| -rw-r--r-- | embassy-rp/src/pio.rs | 1250 | ||||
| -rw-r--r-- | embassy-rp/src/pio_instr_util.rs | 90 | ||||
| -rw-r--r-- | examples/rp/Cargo.toml | 7 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_async.rs | 105 | ||||
| -rw-r--r-- | examples/rp/src/bin/pio_dma.rs | 67 |
8 files changed, 1576 insertions, 2 deletions
diff --git a/embassy-rp/Cargo.toml b/embassy-rp/Cargo.toml index 284d458c6..751875710 100644 --- a/embassy-rp/Cargo.toml +++ b/embassy-rp/Cargo.toml | |||
| @@ -26,6 +26,7 @@ time-driver = [] | |||
| 26 | rom-func-cache = [] | 26 | rom-func-cache = [] |
| 27 | intrinsics = [] | 27 | intrinsics = [] |
| 28 | rom-v2-intrinsics = [] | 28 | rom-v2-intrinsics = [] |
| 29 | pio = ["dep:pio", "dep:pio-proc"] | ||
| 29 | 30 | ||
| 30 | # Enable nightly-only features | 31 | # Enable nightly-only features |
| 31 | nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] | 32 | nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] |
| @@ -64,3 +65,10 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["un | |||
| 64 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} | 65 | embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} |
| 65 | embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} | 66 | embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} |
| 66 | embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} | 67 | embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} |
| 68 | |||
| 69 | paste = "1.0" | ||
| 70 | pio-proc = {version= "0.2", optional = true} | ||
| 71 | pio = {version= "0.2", optional = true} | ||
| 72 | |||
| 73 | [patch.crates-io] | ||
| 74 | pio = {git = "https://github.com/rp-rs/pio-rs.git"} \ No newline at end of file | ||
diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 930de2068..315cd2aca 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs | |||
| @@ -48,6 +48,21 @@ pub enum Pull { | |||
| 48 | Down, | 48 | Down, |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | /// Drive strength of an output | ||
| 52 | #[derive(Debug, Eq, PartialEq)] | ||
| 53 | pub enum Drive { | ||
| 54 | _2mA, | ||
| 55 | _4mA, | ||
| 56 | _8mA, | ||
| 57 | _12mA, | ||
| 58 | } | ||
| 59 | /// Slew rate of an output | ||
| 60 | #[derive(Debug, Eq, PartialEq)] | ||
| 61 | pub enum SlewRate { | ||
| 62 | Fast, | ||
| 63 | Slow, | ||
| 64 | } | ||
| 65 | |||
| 51 | /// A GPIO bank with up to 32 pins. | 66 | /// A GPIO bank with up to 32 pins. |
| 52 | #[derive(Debug, Eq, PartialEq)] | 67 | #[derive(Debug, Eq, PartialEq)] |
| 53 | pub enum Bank { | 68 | pub enum Bank { |
| @@ -459,7 +474,7 @@ impl<'d, T: Pin> Flex<'d, T> { | |||
| 459 | #[inline] | 474 | #[inline] |
| 460 | pub fn set_pull(&mut self, pull: Pull) { | 475 | pub fn set_pull(&mut self, pull: Pull) { |
| 461 | unsafe { | 476 | unsafe { |
| 462 | self.pin.pad_ctrl().write(|w| { | 477 | self.pin.pad_ctrl().modify(|w| { |
| 463 | w.set_ie(true); | 478 | w.set_ie(true); |
| 464 | match pull { | 479 | match pull { |
| 465 | Pull::Up => w.set_pue(true), | 480 | Pull::Up => w.set_pue(true), |
| @@ -470,6 +485,31 @@ impl<'d, T: Pin> Flex<'d, T> { | |||
| 470 | } | 485 | } |
| 471 | } | 486 | } |
| 472 | 487 | ||
| 488 | /// Set the pin's drive strength. | ||
| 489 | #[inline] | ||
| 490 | pub fn set_drive_strength(&mut self, strength: Drive) { | ||
| 491 | unsafe { | ||
| 492 | self.pin.pad_ctrl().modify(|w| { | ||
| 493 | w.set_drive(match strength { | ||
| 494 | Drive::_2mA => pac::pads::vals::Drive::_2MA, | ||
| 495 | Drive::_4mA => pac::pads::vals::Drive::_4MA, | ||
| 496 | Drive::_8mA => pac::pads::vals::Drive::_8MA, | ||
| 497 | Drive::_12mA => pac::pads::vals::Drive::_12MA, | ||
| 498 | }); | ||
| 499 | }); | ||
| 500 | } | ||
| 501 | } | ||
| 502 | |||
| 503 | // Set the pin's slew rate. | ||
| 504 | #[inline] | ||
| 505 | pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { | ||
| 506 | unsafe { | ||
| 507 | self.pin.pad_ctrl().modify(|w| { | ||
| 508 | w.set_slewfast(slew_rate == SlewRate::Fast); | ||
| 509 | }); | ||
| 510 | } | ||
| 511 | } | ||
| 512 | |||
| 473 | /// Put the pin into input mode. | 513 | /// Put the pin into input mode. |
| 474 | /// | 514 | /// |
| 475 | /// The pull setting is left unchanged. | 515 | /// The pull setting is left unchanged. |
diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d21b5f7b0..67eb29d5e 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs | |||
| @@ -12,6 +12,12 @@ pub mod dma; | |||
| 12 | pub mod gpio; | 12 | pub mod gpio; |
| 13 | pub mod i2c; | 13 | pub mod i2c; |
| 14 | pub mod interrupt; | 14 | pub mod interrupt; |
| 15 | |||
| 16 | #[cfg(feature = "pio")] | ||
| 17 | pub mod pio; | ||
| 18 | #[cfg(feature = "pio")] | ||
| 19 | pub mod pio_instr_util; | ||
| 20 | |||
| 15 | pub mod rom_data; | 21 | pub mod rom_data; |
| 16 | pub mod rtc; | 22 | pub mod rtc; |
| 17 | pub mod spi; | 23 | pub mod spi; |
| @@ -102,6 +108,9 @@ embassy_hal_common::peripherals! { | |||
| 102 | FLASH, | 108 | FLASH, |
| 103 | 109 | ||
| 104 | ADC, | 110 | ADC, |
| 111 | |||
| 112 | PIO0, | ||
| 113 | PIO1, | ||
| 105 | } | 114 | } |
| 106 | 115 | ||
| 107 | #[link_section = ".boot2"] | 116 | #[link_section = ".boot2"] |
diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs new file mode 100644 index 000000000..f049a16e7 --- /dev/null +++ b/embassy-rp/src/pio.rs | |||
| @@ -0,0 +1,1250 @@ | |||
| 1 | use core::future::Future; | ||
| 2 | use core::marker::PhantomData; | ||
| 3 | use core::pin::Pin as FuturePin; | ||
| 4 | use core::sync::atomic::{compiler_fence, Ordering}; | ||
| 5 | use core::task::{Context, Poll}; | ||
| 6 | |||
| 7 | use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; | ||
| 8 | use embassy_hal_common::PeripheralRef; | ||
| 9 | use embassy_sync::waitqueue::AtomicWaker; | ||
| 10 | |||
| 11 | use crate::dma::{self, Channel, Transfer}; | ||
| 12 | use crate::gpio::sealed::Pin as SealedPin; | ||
| 13 | use crate::gpio::{Drive, Pin, Pull, SlewRate}; | ||
| 14 | use crate::pac::dma::vals::{DataSize, TreqSel}; | ||
| 15 | use crate::{interrupt, pac, peripherals}; | ||
| 16 | |||
| 17 | const PIOS: [&pac::pio::Pio; 2] = [&pac::PIO0, &pac::PIO1]; | ||
| 18 | const NEW_AW: AtomicWaker = AtomicWaker::new(); | ||
| 19 | const PIO_WAKERS_INIT: [AtomicWaker; 4] = [NEW_AW; 4]; | ||
| 20 | static FIFO_OUT_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; | ||
| 21 | static FIFO_IN_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; | ||
| 22 | static IRQ_WAKERS: [[AtomicWaker; 4]; 2] = [PIO_WAKERS_INIT; 2]; | ||
| 23 | |||
| 24 | pub enum FifoJoin { | ||
| 25 | /// Both TX and RX fifo is enabled | ||
| 26 | Duplex, | ||
| 27 | /// Rx fifo twice as deep. TX fifo disabled | ||
| 28 | RxOnly, | ||
| 29 | /// Tx fifo twice as deep. RX fifo disabled | ||
| 30 | TxOnly, | ||
| 31 | } | ||
| 32 | |||
| 33 | #[derive(PartialEq)] | ||
| 34 | pub enum ShiftDirection { | ||
| 35 | Right = 1, | ||
| 36 | Left = 0, | ||
| 37 | } | ||
| 38 | |||
| 39 | const RXNEMPTY_MASK: u32 = 1 << 0; | ||
| 40 | const TXNFULL_MASK: u32 = 1 << 4; | ||
| 41 | const SMIRQ_MASK: u32 = 1 << 8; | ||
| 42 | |||
| 43 | #[interrupt] | ||
| 44 | unsafe fn PIO0_IRQ_1() { | ||
| 45 | use crate::pac; | ||
| 46 | let ints = pac::PIO0.irqs(1).ints().read().0; | ||
| 47 | let inte = pac::PIO0.irqs(1).inte(); | ||
| 48 | for i in 0..4 { | ||
| 49 | // Check RXNEMPTY | ||
| 50 | if ints & (RXNEMPTY_MASK << i) != 0 { | ||
| 51 | inte.modify(|m| { | ||
| 52 | m.0 &= !(RXNEMPTY_MASK << i); | ||
| 53 | }); | ||
| 54 | FIFO_IN_WAKERS[0][i].wake(); | ||
| 55 | } | ||
| 56 | // Check IRQ flgs | ||
| 57 | if ints & (SMIRQ_MASK << i) != 0 { | ||
| 58 | inte.modify(|m| { | ||
| 59 | m.0 &= !(SMIRQ_MASK << i); | ||
| 60 | }); | ||
| 61 | IRQ_WAKERS[0][i].wake(); | ||
| 62 | } | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | #[interrupt] | ||
| 67 | unsafe fn PIO1_IRQ_1() { | ||
| 68 | use crate::pac; | ||
| 69 | let ints = pac::PIO1.irqs(1).ints().read().0; | ||
| 70 | let inte = pac::PIO1.irqs(1).inte(); | ||
| 71 | for i in 0..4 { | ||
| 72 | // Check all RXNEMPTY | ||
| 73 | if ints & (RXNEMPTY_MASK << i) != 0 { | ||
| 74 | inte.modify(|m| { | ||
| 75 | m.0 &= !(RXNEMPTY_MASK << i); | ||
| 76 | }); | ||
| 77 | FIFO_IN_WAKERS[1][i].wake(); | ||
| 78 | } | ||
| 79 | // Check IRQ flgs | ||
| 80 | if ints & (SMIRQ_MASK << i) != 0 { | ||
| 81 | inte.modify(|m| { | ||
| 82 | m.0 &= !(SMIRQ_MASK << i); | ||
| 83 | }); | ||
| 84 | IRQ_WAKERS[1][i].wake(); | ||
| 85 | } | ||
| 86 | } | ||
| 87 | } | ||
| 88 | |||
| 89 | #[interrupt] | ||
| 90 | unsafe fn PIO0_IRQ_0() { | ||
| 91 | use crate::pac; | ||
| 92 | let ints = pac::PIO0.irqs(0).ints().read().0; | ||
| 93 | let inte = pac::PIO0.irqs(0).inte(); | ||
| 94 | //debug!("!{:04x}",ints); | ||
| 95 | // Check all TXNFULL | ||
| 96 | for i in 0..4 { | ||
| 97 | if ints & (TXNFULL_MASK << i) != 0 { | ||
| 98 | inte.modify(|m| { | ||
| 99 | m.0 &= !(TXNFULL_MASK << i); | ||
| 100 | }); | ||
| 101 | FIFO_OUT_WAKERS[0][i].wake(); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | #[interrupt] | ||
| 107 | unsafe fn PIO1_IRQ_0() { | ||
| 108 | use crate::pac; | ||
| 109 | let ints = pac::PIO1.irqs(0).ints().read().0; | ||
| 110 | let inte = pac::PIO1.irqs(0).inte(); | ||
| 111 | // Check all TXNFULL | ||
| 112 | for i in 0..4 { | ||
| 113 | if ints & (TXNFULL_MASK << i) != 0 { | ||
| 114 | inte.modify(|m| { | ||
| 115 | m.0 &= !(TXNFULL_MASK << i); | ||
| 116 | }); | ||
| 117 | FIFO_OUT_WAKERS[1][i].wake(); | ||
| 118 | } | ||
| 119 | } | ||
| 120 | } | ||
| 121 | |||
| 122 | /// Future that waits for TX-FIFO to become writable | ||
| 123 | pub struct FifoOutFuture<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> { | ||
| 124 | sm: &'a mut SM, | ||
| 125 | pio: PhantomData<PIO>, | ||
| 126 | value: u32, | ||
| 127 | } | ||
| 128 | |||
| 129 | impl<'a, PIO: PioInstance, SM: PioStateMachine + Unpin> FifoOutFuture<'a, PIO, SM> { | ||
| 130 | pub fn new(sm: &'a mut SM, value: u32) -> Self { | ||
| 131 | unsafe { | ||
| 132 | critical_section::with(|_| { | ||
| 133 | let irq = PIO::IrqOut::steal(); | ||
| 134 | irq.set_priority(interrupt::Priority::P3); | ||
| 135 | |||
| 136 | irq.enable(); | ||
| 137 | }); | ||
| 138 | } | ||
| 139 | FifoOutFuture { | ||
| 140 | sm, | ||
| 141 | pio: PhantomData::default(), | ||
| 142 | value, | ||
| 143 | } | ||
| 144 | } | ||
| 145 | } | ||
| 146 | |||
| 147 | impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture<'d, PIO, SM> { | ||
| 148 | type Output = (); | ||
| 149 | fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 150 | //debug!("Poll {},{}", PIO::PIO_NO, SM); | ||
| 151 | let value = self.value; | ||
| 152 | if self.get_mut().sm.try_push_tx(value) { | ||
| 153 | Poll::Ready(()) | ||
| 154 | } else { | ||
| 155 | FIFO_OUT_WAKERS[PIO::PIO_NO as usize][SM::Sm::SM_NO as usize].register(cx.waker()); | ||
| 156 | unsafe { | ||
| 157 | let irq = PIO::IrqOut::steal(); | ||
| 158 | irq.disable(); | ||
| 159 | critical_section::with(|_| { | ||
| 160 | PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { | ||
| 161 | m.0 |= TXNFULL_MASK << SM::Sm::SM_NO; | ||
| 162 | }); | ||
| 163 | }); | ||
| 164 | irq.enable(); | ||
| 165 | } | ||
| 166 | // debug!("Pending"); | ||
| 167 | Poll::Pending | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<'d, PIO, SM> { | ||
| 173 | fn drop(&mut self) { | ||
| 174 | unsafe { | ||
| 175 | critical_section::with(|_| { | ||
| 176 | PIOS[PIO::PIO_NO as usize].irqs(0).inte().modify(|m| { | ||
| 177 | m.0 &= !(TXNFULL_MASK << SM::Sm::SM_NO); | ||
| 178 | }); | ||
| 179 | }); | ||
| 180 | } | ||
| 181 | } | ||
| 182 | } | ||
| 183 | |||
| 184 | /// Future that waits for RX-FIFO to become readable | ||
| 185 | pub struct FifoInFuture<'a, PIO: PioInstance, SM: PioStateMachine> { | ||
| 186 | sm: &'a mut SM, | ||
| 187 | pio: PhantomData<PIO>, | ||
| 188 | } | ||
| 189 | |||
| 190 | impl<'a, PIO: PioInstance, SM: PioStateMachine> FifoInFuture<'a, PIO, SM> { | ||
| 191 | pub fn new(sm: &'a mut SM) -> Self { | ||
| 192 | unsafe { | ||
| 193 | critical_section::with(|_| { | ||
| 194 | let irq = PIO::IrqIn::steal(); | ||
| 195 | irq.set_priority(interrupt::Priority::P3); | ||
| 196 | |||
| 197 | irq.enable(); | ||
| 198 | }); | ||
| 199 | } | ||
| 200 | FifoInFuture { | ||
| 201 | sm, | ||
| 202 | pio: PhantomData::default(), | ||
| 203 | } | ||
| 204 | } | ||
| 205 | } | ||
| 206 | |||
| 207 | impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, SM> { | ||
| 208 | type Output = u32; | ||
| 209 | fn poll(mut self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 210 | //debug!("Poll {},{}", PIO::PIO_NO, SM); | ||
| 211 | if let Some(v) = self.sm.try_pull_rx() { | ||
| 212 | Poll::Ready(v) | ||
| 213 | } else { | ||
| 214 | FIFO_IN_WAKERS[PIO::PIO_NO as usize][SM::Sm::SM_NO as usize].register(cx.waker()); | ||
| 215 | unsafe { | ||
| 216 | let irq = PIO::IrqIn::steal(); | ||
| 217 | irq.disable(); | ||
| 218 | critical_section::with(|_| { | ||
| 219 | PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { | ||
| 220 | m.0 |= RXNEMPTY_MASK << SM::Sm::SM_NO; | ||
| 221 | }); | ||
| 222 | }); | ||
| 223 | irq.enable(); | ||
| 224 | } | ||
| 225 | //debug!("Pending"); | ||
| 226 | Poll::Pending | ||
| 227 | } | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, SM> { | ||
| 232 | fn drop(&mut self) { | ||
| 233 | unsafe { | ||
| 234 | critical_section::with(|_| { | ||
| 235 | PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { | ||
| 236 | m.0 &= !(RXNEMPTY_MASK << SM::Sm::SM_NO); | ||
| 237 | }); | ||
| 238 | }); | ||
| 239 | } | ||
| 240 | } | ||
| 241 | } | ||
| 242 | |||
| 243 | /// Future that waits for IRQ | ||
| 244 | pub struct IrqFuture<PIO: PioInstance> { | ||
| 245 | pio: PhantomData<PIO>, | ||
| 246 | irq_no: u8, | ||
| 247 | } | ||
| 248 | |||
| 249 | impl<'a, PIO: PioInstance> IrqFuture<PIO> { | ||
| 250 | pub fn new(irq_no: u8) -> Self { | ||
| 251 | unsafe { | ||
| 252 | critical_section::with(|_| { | ||
| 253 | let irq = PIO::IrqSm::steal(); | ||
| 254 | irq.set_priority(interrupt::Priority::P3); | ||
| 255 | |||
| 256 | irq.enable(); | ||
| 257 | }); | ||
| 258 | } | ||
| 259 | IrqFuture { | ||
| 260 | pio: PhantomData::default(), | ||
| 261 | irq_no, | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | |||
| 266 | impl<'d, PIO: PioInstance> Future for IrqFuture<PIO> { | ||
| 267 | type Output = (); | ||
| 268 | fn poll(self: FuturePin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { | ||
| 269 | //debug!("Poll {},{}", PIO::PIO_NO, SM); | ||
| 270 | |||
| 271 | // Check if IRQ flag is already set | ||
| 272 | if critical_section::with(|_| unsafe { | ||
| 273 | let irq_flags = PIOS[PIO::PIO_NO as usize].irq(); | ||
| 274 | if irq_flags.read().0 & (1 << self.irq_no) != 0 { | ||
| 275 | irq_flags.write(|m| { | ||
| 276 | m.0 = 1 << self.irq_no; | ||
| 277 | }); | ||
| 278 | true | ||
| 279 | } else { | ||
| 280 | false | ||
| 281 | } | ||
| 282 | }) { | ||
| 283 | return Poll::Ready(()); | ||
| 284 | } | ||
| 285 | |||
| 286 | IRQ_WAKERS[PIO::PIO_NO as usize][self.irq_no as usize].register(cx.waker()); | ||
| 287 | unsafe { | ||
| 288 | let irq = PIO::IrqSm::steal(); | ||
| 289 | irq.disable(); | ||
| 290 | critical_section::with(|_| { | ||
| 291 | PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { | ||
| 292 | m.0 |= SMIRQ_MASK << self.irq_no; | ||
| 293 | }); | ||
| 294 | }); | ||
| 295 | irq.enable(); | ||
| 296 | } | ||
| 297 | Poll::Pending | ||
| 298 | } | ||
| 299 | } | ||
| 300 | |||
| 301 | impl<'d, PIO: PioInstance> Drop for IrqFuture<PIO> { | ||
| 302 | fn drop(&mut self) { | ||
| 303 | unsafe { | ||
| 304 | critical_section::with(|_| { | ||
| 305 | PIOS[PIO::PIO_NO as usize].irqs(1).inte().modify(|m| { | ||
| 306 | m.0 &= !(SMIRQ_MASK << self.irq_no); | ||
| 307 | }); | ||
| 308 | }); | ||
| 309 | } | ||
| 310 | } | ||
| 311 | } | ||
| 312 | |||
| 313 | pub struct PioPin<PIO: PioInstance> { | ||
| 314 | pin_bank: u8, | ||
| 315 | pio: PhantomData<PIO>, | ||
| 316 | } | ||
| 317 | |||
| 318 | impl<PIO: PioInstance> PioPin<PIO> { | ||
| 319 | /// Set the pin's drive strength. | ||
| 320 | #[inline] | ||
| 321 | pub fn set_drive_strength(&mut self, strength: Drive) { | ||
| 322 | unsafe { | ||
| 323 | self.pad_ctrl().modify(|w| { | ||
| 324 | w.set_drive(match strength { | ||
| 325 | Drive::_2mA => pac::pads::vals::Drive::_2MA, | ||
| 326 | Drive::_4mA => pac::pads::vals::Drive::_4MA, | ||
| 327 | Drive::_8mA => pac::pads::vals::Drive::_8MA, | ||
| 328 | Drive::_12mA => pac::pads::vals::Drive::_12MA, | ||
| 329 | }); | ||
| 330 | }); | ||
| 331 | } | ||
| 332 | } | ||
| 333 | |||
| 334 | // Set the pin's slew rate. | ||
| 335 | #[inline] | ||
| 336 | pub fn set_slew_rate(&mut self, slew_rate: SlewRate) { | ||
| 337 | unsafe { | ||
| 338 | self.pad_ctrl().modify(|w| { | ||
| 339 | w.set_slewfast(slew_rate == SlewRate::Fast); | ||
| 340 | }); | ||
| 341 | } | ||
| 342 | } | ||
| 343 | |||
| 344 | /// Set the pin's pull. | ||
| 345 | #[inline] | ||
| 346 | pub fn set_pull(&mut self, pull: Pull) { | ||
| 347 | unsafe { | ||
| 348 | self.pad_ctrl().modify(|w| match pull { | ||
| 349 | Pull::Up => w.set_pue(true), | ||
| 350 | Pull::Down => w.set_pde(true), | ||
| 351 | Pull::None => {} | ||
| 352 | }); | ||
| 353 | } | ||
| 354 | } | ||
| 355 | |||
| 356 | /// Set the pin's pull. | ||
| 357 | #[inline] | ||
| 358 | pub fn set_schmitt(&mut self, enable: bool) { | ||
| 359 | unsafe { | ||
| 360 | self.pad_ctrl().modify(|w| { | ||
| 361 | w.set_schmitt(enable); | ||
| 362 | }); | ||
| 363 | } | ||
| 364 | } | ||
| 365 | |||
| 366 | pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) { | ||
| 367 | let mask = 1 << self.pin(); | ||
| 368 | unsafe { | ||
| 369 | PIOS[PIO::PIO_NO as usize] | ||
| 370 | .input_sync_bypass() | ||
| 371 | .modify(|w| *w = if bypass { *w & !mask } else { *w | mask }); | ||
| 372 | } | ||
| 373 | } | ||
| 374 | |||
| 375 | pub fn pin(&self) -> u8 { | ||
| 376 | self._pin() | ||
| 377 | } | ||
| 378 | } | ||
| 379 | |||
| 380 | impl<PIO: PioInstance> SealedPin for PioPin<PIO> { | ||
| 381 | fn pin_bank(&self) -> u8 { | ||
| 382 | self.pin_bank | ||
| 383 | } | ||
| 384 | } | ||
| 385 | |||
| 386 | pub struct PioStateMachineInstance<PIO: PioInstance, SM: SmInstance> { | ||
| 387 | pio: PhantomData<PIO>, | ||
| 388 | sm: PhantomData<SM>, | ||
| 389 | } | ||
| 390 | |||
| 391 | impl<PIO: PioInstance, SM: SmInstance> PioStateMachine for PioStateMachineInstance<PIO, SM> { | ||
| 392 | type Pio = PIO; | ||
| 393 | type Sm = SM; | ||
| 394 | } | ||
| 395 | |||
| 396 | pub trait PioStateMachine: Sized + Unpin { | ||
| 397 | type Pio: PioInstance; | ||
| 398 | type Sm: SmInstance; | ||
| 399 | fn pio_no(&self) -> u8 { | ||
| 400 | let _ = self; | ||
| 401 | Self::Pio::PIO_NO | ||
| 402 | } | ||
| 403 | |||
| 404 | fn sm_no(&self) -> u8 { | ||
| 405 | Self::Sm::SM_NO | ||
| 406 | } | ||
| 407 | |||
| 408 | fn restart(&mut self) { | ||
| 409 | let _ = self; | ||
| 410 | unsafe { | ||
| 411 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 412 | .ctrl() | ||
| 413 | .modify(|w| w.set_sm_restart(1u8 << Self::Sm::SM_NO)); | ||
| 414 | } | ||
| 415 | } | ||
| 416 | fn set_enable(&mut self, enable: bool) { | ||
| 417 | let _ = self; | ||
| 418 | let mask = 1u8 << Self::Sm::SM_NO; | ||
| 419 | unsafe { | ||
| 420 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 421 | .ctrl() | ||
| 422 | .modify(|w| w.set_sm_enable((w.sm_enable() & !mask) | (if enable { mask } else { 0 }))); | ||
| 423 | } | ||
| 424 | } | ||
| 425 | |||
| 426 | fn is_enabled(&self) -> bool { | ||
| 427 | let _ = self; | ||
| 428 | unsafe { PIOS[Self::Pio::PIO_NO as usize].ctrl().read().sm_enable() & (1u8 << Self::Sm::SM_NO) != 0 } | ||
| 429 | } | ||
| 430 | |||
| 431 | fn is_tx_empty(&self) -> bool { | ||
| 432 | let _ = self; | ||
| 433 | unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().txempty() & (1u8 << Self::Sm::SM_NO) != 0 } | ||
| 434 | } | ||
| 435 | fn is_tx_full(&self) -> bool { | ||
| 436 | let _ = self; | ||
| 437 | unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().txfull() & (1u8 << Self::Sm::SM_NO) != 0 } | ||
| 438 | } | ||
| 439 | |||
| 440 | fn is_rx_empty(&self) -> bool { | ||
| 441 | let _ = self; | ||
| 442 | unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().rxempty() & (1u8 << Self::Sm::SM_NO) != 0 } | ||
| 443 | } | ||
| 444 | fn is_rx_full(&self) -> bool { | ||
| 445 | let _ = self; | ||
| 446 | unsafe { PIOS[Self::Pio::PIO_NO as usize].fstat().read().rxfull() & (1u8 << Self::Sm::SM_NO) != 0 } | ||
| 447 | } | ||
| 448 | |||
| 449 | fn tx_level(&self) -> u8 { | ||
| 450 | unsafe { | ||
| 451 | let flevel = PIOS[Self::Pio::PIO_NO as usize].flevel().read().0; | ||
| 452 | (flevel >> (Self::Sm::SM_NO * 8)) as u8 & 0x0f | ||
| 453 | } | ||
| 454 | } | ||
| 455 | |||
| 456 | fn rx_level(&self) -> u8 { | ||
| 457 | unsafe { | ||
| 458 | let flevel = PIOS[Self::Pio::PIO_NO as usize].flevel().read().0; | ||
| 459 | (flevel >> (Self::Sm::SM_NO * 8 + 4)) as u8 & 0x0f | ||
| 460 | } | ||
| 461 | } | ||
| 462 | |||
| 463 | fn push_tx(&mut self, v: u32) { | ||
| 464 | unsafe { | ||
| 465 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 466 | .txf(Self::Sm::SM_NO as usize) | ||
| 467 | .write_value(v); | ||
| 468 | } | ||
| 469 | } | ||
| 470 | |||
| 471 | fn try_push_tx(&mut self, v: u32) -> bool { | ||
| 472 | if self.is_tx_full() { | ||
| 473 | return false; | ||
| 474 | } | ||
| 475 | self.push_tx(v); | ||
| 476 | true | ||
| 477 | } | ||
| 478 | |||
| 479 | fn pull_rx(&mut self) -> u32 { | ||
| 480 | unsafe { PIOS[Self::Pio::PIO_NO as usize].rxf(Self::Sm::SM_NO as usize).read() } | ||
| 481 | } | ||
| 482 | |||
| 483 | fn try_pull_rx(&mut self) -> Option<u32> { | ||
| 484 | if self.is_rx_empty() { | ||
| 485 | return None; | ||
| 486 | } | ||
| 487 | Some(self.pull_rx()) | ||
| 488 | } | ||
| 489 | |||
| 490 | fn set_clkdiv(&mut self, div_x_256: u32) { | ||
| 491 | unsafe { | ||
| 492 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 493 | .sm(Self::Sm::SM_NO as usize) | ||
| 494 | .clkdiv() | ||
| 495 | .write(|w| w.0 = div_x_256 << 8); | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 499 | fn get_clkdiv(&self) -> u32 { | ||
| 500 | unsafe { | ||
| 501 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 502 | .sm(Self::Sm::SM_NO as usize) | ||
| 503 | .clkdiv() | ||
| 504 | .read() | ||
| 505 | .0 | ||
| 506 | >> 8 | ||
| 507 | } | ||
| 508 | } | ||
| 509 | |||
| 510 | fn clkdiv_restart(&mut self) { | ||
| 511 | let _ = self; | ||
| 512 | unsafe { | ||
| 513 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 514 | .ctrl() | ||
| 515 | .modify(|w| w.set_clkdiv_restart(1u8 << Self::Sm::SM_NO)); | ||
| 516 | } | ||
| 517 | } | ||
| 518 | |||
| 519 | fn set_side_enable(&self, enable: bool) { | ||
| 520 | unsafe { | ||
| 521 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 522 | .sm(Self::Sm::SM_NO as usize) | ||
| 523 | .execctrl() | ||
| 524 | .modify(|w| w.set_side_en(enable)); | ||
| 525 | } | ||
| 526 | } | ||
| 527 | |||
| 528 | fn is_side_enabled(&self) -> bool { | ||
| 529 | unsafe { | ||
| 530 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 531 | .sm(Self::Sm::SM_NO as usize) | ||
| 532 | .execctrl() | ||
| 533 | .read() | ||
| 534 | .side_en() | ||
| 535 | } | ||
| 536 | } | ||
| 537 | |||
| 538 | fn set_side_pindir(&mut self, pindir: bool) { | ||
| 539 | unsafe { | ||
| 540 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 541 | .sm(Self::Sm::SM_NO as usize) | ||
| 542 | .execctrl() | ||
| 543 | .modify(|w| w.set_side_pindir(pindir)); | ||
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 547 | fn is_side_pindir(&self) -> bool { | ||
| 548 | unsafe { | ||
| 549 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 550 | .sm(Self::Sm::SM_NO as usize) | ||
| 551 | .execctrl() | ||
| 552 | .read() | ||
| 553 | .side_pindir() | ||
| 554 | } | ||
| 555 | } | ||
| 556 | |||
| 557 | fn set_jmp_pin(&mut self, pin: u8) { | ||
| 558 | unsafe { | ||
| 559 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 560 | .sm(Self::Sm::SM_NO as usize) | ||
| 561 | .execctrl() | ||
| 562 | .modify(|w| w.set_jmp_pin(pin)); | ||
| 563 | } | ||
| 564 | } | ||
| 565 | |||
| 566 | fn get_jmp_pin(&mut self) -> u8 { | ||
| 567 | unsafe { | ||
| 568 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 569 | .sm(Self::Sm::SM_NO as usize) | ||
| 570 | .execctrl() | ||
| 571 | .read() | ||
| 572 | .jmp_pin() | ||
| 573 | } | ||
| 574 | } | ||
| 575 | |||
| 576 | fn set_wrap(&self, source: u8, target: u8) { | ||
| 577 | unsafe { | ||
| 578 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 579 | .sm(Self::Sm::SM_NO as usize) | ||
| 580 | .execctrl() | ||
| 581 | .modify(|w| { | ||
| 582 | w.set_wrap_top(source); | ||
| 583 | w.set_wrap_bottom(target) | ||
| 584 | }); | ||
| 585 | } | ||
| 586 | } | ||
| 587 | |||
| 588 | /// Get wrapping addresses. Returns (source, target). | ||
| 589 | fn get_wrap(&self) -> (u8, u8) { | ||
| 590 | unsafe { | ||
| 591 | let r = PIOS[Self::Pio::PIO_NO as usize] | ||
| 592 | .sm(Self::Sm::SM_NO as usize) | ||
| 593 | .execctrl() | ||
| 594 | .read(); | ||
| 595 | (r.wrap_top(), r.wrap_bottom()) | ||
| 596 | } | ||
| 597 | } | ||
| 598 | |||
| 599 | fn set_fifo_join(&mut self, join: FifoJoin) { | ||
| 600 | let (rx, tx) = match join { | ||
| 601 | FifoJoin::Duplex => (false, false), | ||
| 602 | FifoJoin::RxOnly => (true, false), | ||
| 603 | FifoJoin::TxOnly => (false, true), | ||
| 604 | }; | ||
| 605 | unsafe { | ||
| 606 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 607 | .sm(Self::Sm::SM_NO as usize) | ||
| 608 | .shiftctrl() | ||
| 609 | .modify(|w| { | ||
| 610 | w.set_fjoin_rx(rx); | ||
| 611 | w.set_fjoin_tx(tx) | ||
| 612 | }); | ||
| 613 | } | ||
| 614 | } | ||
| 615 | fn get_fifo_join(&self) -> FifoJoin { | ||
| 616 | unsafe { | ||
| 617 | let r = PIOS[Self::Pio::PIO_NO as usize] | ||
| 618 | .sm(Self::Sm::SM_NO as usize) | ||
| 619 | .shiftctrl() | ||
| 620 | .read(); | ||
| 621 | // Ignores the invalid state when both bits are set | ||
| 622 | if r.fjoin_rx() { | ||
| 623 | FifoJoin::RxOnly | ||
| 624 | } else if r.fjoin_tx() { | ||
| 625 | FifoJoin::TxOnly | ||
| 626 | } else { | ||
| 627 | FifoJoin::Duplex | ||
| 628 | } | ||
| 629 | } | ||
| 630 | } | ||
| 631 | |||
| 632 | fn clear_fifos(&mut self) { | ||
| 633 | // Toggle FJOIN_RX to flush FIFOs | ||
| 634 | unsafe { | ||
| 635 | let shiftctrl = PIOS[Self::Pio::PIO_NO as usize] | ||
| 636 | .sm(Self::Sm::SM_NO as usize) | ||
| 637 | .shiftctrl(); | ||
| 638 | shiftctrl.modify(|w| { | ||
| 639 | w.set_fjoin_rx(!w.fjoin_rx()); | ||
| 640 | }); | ||
| 641 | shiftctrl.modify(|w| { | ||
| 642 | w.set_fjoin_rx(!w.fjoin_rx()); | ||
| 643 | }); | ||
| 644 | } | ||
| 645 | } | ||
| 646 | |||
| 647 | fn set_pull_threshold(&mut self, threshold: u8) { | ||
| 648 | unsafe { | ||
| 649 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 650 | .sm(Self::Sm::SM_NO as usize) | ||
| 651 | .shiftctrl() | ||
| 652 | .modify(|w| w.set_pull_thresh(threshold)); | ||
| 653 | } | ||
| 654 | } | ||
| 655 | |||
| 656 | fn get_pull_threshold(&self) -> u8 { | ||
| 657 | unsafe { | ||
| 658 | let r = PIOS[Self::Pio::PIO_NO as usize] | ||
| 659 | .sm(Self::Sm::SM_NO as usize) | ||
| 660 | .shiftctrl() | ||
| 661 | .read(); | ||
| 662 | r.pull_thresh() | ||
| 663 | } | ||
| 664 | } | ||
| 665 | fn set_push_threshold(&mut self, threshold: u8) { | ||
| 666 | unsafe { | ||
| 667 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 668 | .sm(Self::Sm::SM_NO as usize) | ||
| 669 | .shiftctrl() | ||
| 670 | .modify(|w| w.set_push_thresh(threshold)); | ||
| 671 | } | ||
| 672 | } | ||
| 673 | |||
| 674 | fn get_push_threshold(&self) -> u8 { | ||
| 675 | unsafe { | ||
| 676 | let r = PIOS[Self::Pio::PIO_NO as usize] | ||
| 677 | .sm(Self::Sm::SM_NO as usize) | ||
| 678 | .shiftctrl() | ||
| 679 | .read(); | ||
| 680 | r.push_thresh() | ||
| 681 | } | ||
| 682 | } | ||
| 683 | |||
| 684 | fn set_out_shift_dir(&mut self, dir: ShiftDirection) { | ||
| 685 | unsafe { | ||
| 686 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 687 | .sm(Self::Sm::SM_NO as usize) | ||
| 688 | .shiftctrl() | ||
| 689 | .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); | ||
| 690 | } | ||
| 691 | } | ||
| 692 | fn get_out_shiftdir(&self) -> ShiftDirection { | ||
| 693 | unsafe { | ||
| 694 | if PIOS[Self::Pio::PIO_NO as usize] | ||
| 695 | .sm(Self::Sm::SM_NO as usize) | ||
| 696 | .shiftctrl() | ||
| 697 | .read() | ||
| 698 | .out_shiftdir() | ||
| 699 | { | ||
| 700 | ShiftDirection::Right | ||
| 701 | } else { | ||
| 702 | ShiftDirection::Left | ||
| 703 | } | ||
| 704 | } | ||
| 705 | } | ||
| 706 | |||
| 707 | fn set_in_shift_dir(&mut self, dir: ShiftDirection) { | ||
| 708 | unsafe { | ||
| 709 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 710 | .sm(Self::Sm::SM_NO as usize) | ||
| 711 | .shiftctrl() | ||
| 712 | .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); | ||
| 713 | } | ||
| 714 | } | ||
| 715 | fn get_in_shiftdir(&self) -> ShiftDirection { | ||
| 716 | unsafe { | ||
| 717 | if PIOS[Self::Pio::PIO_NO as usize] | ||
| 718 | .sm(Self::Sm::SM_NO as usize) | ||
| 719 | .shiftctrl() | ||
| 720 | .read() | ||
| 721 | .in_shiftdir() | ||
| 722 | { | ||
| 723 | ShiftDirection::Right | ||
| 724 | } else { | ||
| 725 | ShiftDirection::Left | ||
| 726 | } | ||
| 727 | } | ||
| 728 | } | ||
| 729 | |||
| 730 | fn set_autopull(&mut self, auto: bool) { | ||
| 731 | unsafe { | ||
| 732 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 733 | .sm(Self::Sm::SM_NO as usize) | ||
| 734 | .shiftctrl() | ||
| 735 | .modify(|w| w.set_autopull(auto)); | ||
| 736 | } | ||
| 737 | } | ||
| 738 | |||
| 739 | fn is_autopull(&self) -> bool { | ||
| 740 | unsafe { | ||
| 741 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 742 | .sm(Self::Sm::SM_NO as usize) | ||
| 743 | .shiftctrl() | ||
| 744 | .read() | ||
| 745 | .autopull() | ||
| 746 | } | ||
| 747 | } | ||
| 748 | |||
| 749 | fn set_autopush(&mut self, auto: bool) { | ||
| 750 | unsafe { | ||
| 751 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 752 | .sm(Self::Sm::SM_NO as usize) | ||
| 753 | .shiftctrl() | ||
| 754 | .modify(|w| w.set_autopush(auto)); | ||
| 755 | } | ||
| 756 | } | ||
| 757 | |||
| 758 | fn is_autopush(&self) -> bool { | ||
| 759 | unsafe { | ||
| 760 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 761 | .sm(Self::Sm::SM_NO as usize) | ||
| 762 | .shiftctrl() | ||
| 763 | .read() | ||
| 764 | .autopush() | ||
| 765 | } | ||
| 766 | } | ||
| 767 | |||
| 768 | fn get_addr(&self) -> u8 { | ||
| 769 | unsafe { | ||
| 770 | let r = PIOS[Self::Pio::PIO_NO as usize] | ||
| 771 | .sm(Self::Sm::SM_NO as usize) | ||
| 772 | .addr() | ||
| 773 | .read(); | ||
| 774 | r.addr() | ||
| 775 | } | ||
| 776 | } | ||
| 777 | fn set_sideset_count(&mut self, count: u8) { | ||
| 778 | unsafe { | ||
| 779 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 780 | .sm(Self::Sm::SM_NO as usize) | ||
| 781 | .pinctrl() | ||
| 782 | .modify(|w| w.set_sideset_count(count)); | ||
| 783 | } | ||
| 784 | } | ||
| 785 | |||
| 786 | fn get_sideset_count(&self) -> u8 { | ||
| 787 | unsafe { | ||
| 788 | let r = PIOS[Self::Pio::PIO_NO as usize] | ||
| 789 | .sm(Self::Sm::SM_NO as usize) | ||
| 790 | .pinctrl() | ||
| 791 | .read(); | ||
| 792 | r.sideset_count() | ||
| 793 | } | ||
| 794 | } | ||
| 795 | |||
| 796 | fn make_pio_pin(&self, pin: impl Pin) -> PioPin<Self::Pio> { | ||
| 797 | unsafe { | ||
| 798 | pin.io().ctrl().write(|w| { | ||
| 799 | w.set_funcsel( | ||
| 800 | if Self::Pio::PIO_NO == 1 { | ||
| 801 | pac::io::vals::Gpio0ctrlFuncsel::PIO1_0 | ||
| 802 | } else { | ||
| 803 | // PIO == 0 | ||
| 804 | pac::io::vals::Gpio0ctrlFuncsel::PIO0_0 | ||
| 805 | } | ||
| 806 | .0, | ||
| 807 | ); | ||
| 808 | }); | ||
| 809 | } | ||
| 810 | PioPin { | ||
| 811 | pin_bank: pin.pin_bank(), | ||
| 812 | pio: PhantomData::default(), | ||
| 813 | } | ||
| 814 | } | ||
| 815 | |||
| 816 | fn set_sideset_base_pin(&mut self, base_pin: &PioPin<Self::Pio>) { | ||
| 817 | unsafe { | ||
| 818 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 819 | .sm(Self::Sm::SM_NO as usize) | ||
| 820 | .pinctrl() | ||
| 821 | .modify(|w| w.set_sideset_base(base_pin.pin())); | ||
| 822 | } | ||
| 823 | } | ||
| 824 | |||
| 825 | fn get_sideset_base(&self) -> u8 { | ||
| 826 | unsafe { | ||
| 827 | let r = PIOS[Self::Pio::PIO_NO as usize] | ||
| 828 | .sm(Self::Sm::SM_NO as usize) | ||
| 829 | .pinctrl() | ||
| 830 | .read(); | ||
| 831 | r.sideset_base() | ||
| 832 | } | ||
| 833 | } | ||
| 834 | |||
| 835 | /// Set the range of out pins affected by a set instruction. | ||
| 836 | fn set_set_range(&mut self, base: u8, count: u8) { | ||
| 837 | assert!(base + count < 32); | ||
| 838 | unsafe { | ||
| 839 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 840 | .sm(Self::Sm::SM_NO as usize) | ||
| 841 | .pinctrl() | ||
| 842 | .modify(|w| { | ||
| 843 | w.set_set_base(base); | ||
| 844 | w.set_set_count(count) | ||
| 845 | }); | ||
| 846 | } | ||
| 847 | } | ||
| 848 | |||
| 849 | /// Get the range of out pins affected by a set instruction. Returns (base, count). | ||
| 850 | fn get_set_range(&self) -> (u8, u8) { | ||
| 851 | unsafe { | ||
| 852 | let r = PIOS[Self::Pio::PIO_NO as usize] | ||
| 853 | .sm(Self::Sm::SM_NO as usize) | ||
| 854 | .pinctrl() | ||
| 855 | .read(); | ||
| 856 | (r.set_base(), r.set_count()) | ||
| 857 | } | ||
| 858 | } | ||
| 859 | |||
| 860 | fn set_in_base_pin(&mut self, base: &PioPin<Self::Pio>) { | ||
| 861 | unsafe { | ||
| 862 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 863 | .sm(Self::Sm::SM_NO as usize) | ||
| 864 | .pinctrl() | ||
| 865 | .modify(|w| w.set_in_base(base.pin())); | ||
| 866 | } | ||
| 867 | } | ||
| 868 | |||
| 869 | fn get_in_base(&self) -> u8 { | ||
| 870 | unsafe { | ||
| 871 | let r = PIOS[Self::Pio::PIO_NO as usize] | ||
| 872 | .sm(Self::Sm::SM_NO as usize) | ||
| 873 | .pinctrl() | ||
| 874 | .read(); | ||
| 875 | r.in_base() | ||
| 876 | } | ||
| 877 | } | ||
| 878 | |||
| 879 | fn set_out_range(&mut self, base: u8, count: u8) { | ||
| 880 | assert!(base + count < 32); | ||
| 881 | unsafe { | ||
| 882 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 883 | .sm(Self::Sm::SM_NO as usize) | ||
| 884 | .pinctrl() | ||
| 885 | .modify(|w| { | ||
| 886 | w.set_out_base(base); | ||
| 887 | w.set_out_count(count) | ||
| 888 | }); | ||
| 889 | } | ||
| 890 | } | ||
| 891 | |||
| 892 | /// Get the range of out pins affected by a set instruction. Returns (base, count). | ||
| 893 | fn get_out_range(&self) -> (u8, u8) { | ||
| 894 | unsafe { | ||
| 895 | let r = PIOS[Self::Pio::PIO_NO as usize] | ||
| 896 | .sm(Self::Sm::SM_NO as usize) | ||
| 897 | .pinctrl() | ||
| 898 | .read(); | ||
| 899 | (r.out_base(), r.out_count()) | ||
| 900 | } | ||
| 901 | } | ||
| 902 | |||
| 903 | fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&PioPin<Self::Pio>]) { | ||
| 904 | let count = pins.len(); | ||
| 905 | assert!(count >= 1); | ||
| 906 | let start = pins[0].pin() as usize; | ||
| 907 | assert!(start + pins.len() <= 32); | ||
| 908 | for i in 0..count { | ||
| 909 | assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); | ||
| 910 | } | ||
| 911 | self.set_out_range(start as u8, count as u8); | ||
| 912 | } | ||
| 913 | |||
| 914 | fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&PioPin<Self::Pio>]) { | ||
| 915 | let count = pins.len(); | ||
| 916 | assert!(count >= 1); | ||
| 917 | let start = pins[0].pin() as usize; | ||
| 918 | assert!(start + pins.len() <= 32); | ||
| 919 | for i in 0..count { | ||
| 920 | assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); | ||
| 921 | } | ||
| 922 | self.set_set_range(start as u8, count as u8); | ||
| 923 | } | ||
| 924 | |||
| 925 | fn get_current_instr() -> u32 { | ||
| 926 | unsafe { | ||
| 927 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 928 | .sm(Self::Sm::SM_NO as usize) | ||
| 929 | .instr() | ||
| 930 | .read() | ||
| 931 | .0 | ||
| 932 | } | ||
| 933 | } | ||
| 934 | |||
| 935 | fn exec_instr(&mut self, instr: u16) { | ||
| 936 | unsafe { | ||
| 937 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 938 | .sm(Self::Sm::SM_NO as usize) | ||
| 939 | .instr() | ||
| 940 | .write(|w| w.set_instr(instr)); | ||
| 941 | } | ||
| 942 | } | ||
| 943 | |||
| 944 | fn write_instr(&mut self, start: usize, instrs: &[u16]) { | ||
| 945 | let _ = self; | ||
| 946 | write_instr( | ||
| 947 | Self::Pio::PIO_NO, | ||
| 948 | start, | ||
| 949 | instrs, | ||
| 950 | MEM_USED_BY_STATEMACHINE | Self::Sm::SM_NO as u32, | ||
| 951 | ); | ||
| 952 | } | ||
| 953 | |||
| 954 | fn is_irq_set(&self, irq_no: u8) -> bool { | ||
| 955 | assert!(irq_no < 8); | ||
| 956 | unsafe { | ||
| 957 | let irq_flags = PIOS[Self::Pio::PIO_NO as usize].irq(); | ||
| 958 | irq_flags.read().0 & (1 << irq_no) != 0 | ||
| 959 | } | ||
| 960 | } | ||
| 961 | |||
| 962 | fn clear_irq(&mut self, irq_no: usize) { | ||
| 963 | assert!(irq_no < 8); | ||
| 964 | unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(1 << irq_no)) } | ||
| 965 | } | ||
| 966 | |||
| 967 | fn wait_push<'a>(&'a mut self, value: u32) -> FifoOutFuture<'a, Self::Pio, Self> { | ||
| 968 | FifoOutFuture::new(self, value) | ||
| 969 | } | ||
| 970 | |||
| 971 | fn wait_pull<'a>(&'a mut self) -> FifoInFuture<'a, Self::Pio, Self> { | ||
| 972 | FifoInFuture::new(self) | ||
| 973 | } | ||
| 974 | |||
| 975 | fn wait_irq(&self, irq_no: u8) -> IrqFuture<Self::Pio> { | ||
| 976 | IrqFuture::new(irq_no) | ||
| 977 | } | ||
| 978 | |||
| 979 | fn has_tx_stalled(&self) -> bool { | ||
| 980 | unsafe { | ||
| 981 | let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); | ||
| 982 | let ret = fdebug.read().txstall() & (1 << Self::Sm::SM_NO) != 0; | ||
| 983 | fdebug.write(|w| w.set_txstall(1 << Self::Sm::SM_NO)); | ||
| 984 | ret | ||
| 985 | } | ||
| 986 | } | ||
| 987 | |||
| 988 | fn has_tx_overflowed(&self) -> bool { | ||
| 989 | unsafe { | ||
| 990 | let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); | ||
| 991 | let ret = fdebug.read().txover() & (1 << Self::Sm::SM_NO) != 0; | ||
| 992 | fdebug.write(|w| w.set_txover(1 << Self::Sm::SM_NO)); | ||
| 993 | ret | ||
| 994 | } | ||
| 995 | } | ||
| 996 | |||
| 997 | fn has_rx_stalled(&self) -> bool { | ||
| 998 | unsafe { | ||
| 999 | let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); | ||
| 1000 | let ret = fdebug.read().rxstall() & (1 << Self::Sm::SM_NO) != 0; | ||
| 1001 | fdebug.write(|w| w.set_rxstall(1 << Self::Sm::SM_NO)); | ||
| 1002 | ret | ||
| 1003 | } | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | fn has_rx_underflowed(&self) -> bool { | ||
| 1007 | unsafe { | ||
| 1008 | let fdebug = PIOS[Self::Pio::PIO_NO as usize].fdebug(); | ||
| 1009 | let ret = fdebug.read().rxunder() & (1 << Self::Sm::SM_NO) != 0; | ||
| 1010 | fdebug.write(|w| w.set_rxunder(1 << Self::Sm::SM_NO)); | ||
| 1011 | ret | ||
| 1012 | } | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | fn dma_push<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [u32]) -> Transfer<'a, C> { | ||
| 1016 | unsafe { | ||
| 1017 | dma::init(); | ||
| 1018 | let pio_no = Self::Pio::PIO_NO; | ||
| 1019 | let sm_no = Self::Sm::SM_NO; | ||
| 1020 | let p = ch.regs(); | ||
| 1021 | p.read_addr().write_value(data.as_ptr() as u32); | ||
| 1022 | p.write_addr() | ||
| 1023 | .write_value(PIOS[pio_no as usize].txf(sm_no as usize).ptr() as u32); | ||
| 1024 | p.trans_count().write_value(data.len() as u32); | ||
| 1025 | p.ctrl_trig().write(|w| { | ||
| 1026 | // Set TX DREQ for this statemachine | ||
| 1027 | w.set_treq_sel(TreqSel(pio_no * 8 + sm_no)); | ||
| 1028 | w.set_data_size(DataSize::SIZE_WORD); | ||
| 1029 | w.set_chain_to(ch.number()); | ||
| 1030 | w.set_incr_read(true); | ||
| 1031 | w.set_incr_write(false); | ||
| 1032 | w.set_en(true); | ||
| 1033 | }); | ||
| 1034 | compiler_fence(Ordering::SeqCst); | ||
| 1035 | } | ||
| 1036 | Transfer::new(ch) | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | fn dma_pull<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [u32]) -> Transfer<'a, C> { | ||
| 1040 | unsafe { | ||
| 1041 | dma::init(); | ||
| 1042 | let pio_no = Self::Pio::PIO_NO; | ||
| 1043 | let sm_no = Self::Sm::SM_NO; | ||
| 1044 | let p = ch.regs(); | ||
| 1045 | p.write_addr().write_value(data.as_ptr() as u32); | ||
| 1046 | p.read_addr() | ||
| 1047 | .write_value(PIOS[pio_no as usize].rxf(sm_no as usize).ptr() as u32); | ||
| 1048 | p.trans_count().write_value(data.len() as u32); | ||
| 1049 | p.ctrl_trig().write(|w| { | ||
| 1050 | // Set TX DREQ for this statemachine | ||
| 1051 | w.set_treq_sel(TreqSel(pio_no * 8 + sm_no + 4)); | ||
| 1052 | w.set_data_size(DataSize::SIZE_WORD); | ||
| 1053 | w.set_chain_to(ch.number()); | ||
| 1054 | w.set_incr_read(false); | ||
| 1055 | w.set_incr_write(true); | ||
| 1056 | w.set_en(true); | ||
| 1057 | }); | ||
| 1058 | compiler_fence(Ordering::SeqCst); | ||
| 1059 | } | ||
| 1060 | Transfer::new(ch) | ||
| 1061 | } | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | /* | ||
| 1065 | This is a bit array containing 4 bits for every word in the PIO instruction memory. | ||
| 1066 | */ | ||
| 1067 | // Bit 3-2 | ||
| 1068 | //const MEM_USE_MASK: u32 = 0b1100; | ||
| 1069 | const MEM_NOT_USED: u32 = 0b0000; | ||
| 1070 | const MEM_USED_BY_STATEMACHINE: u32 = 0b0100; | ||
| 1071 | const MEM_USED_BY_COMMON: u32 = 0b1000; | ||
| 1072 | |||
| 1073 | // Bit 1-0 is the number of the state machine | ||
| 1074 | //const MEM_STATE_MASK: u32 = 0b0011; | ||
| 1075 | |||
| 1076 | // Should use mutex if running on multiple cores | ||
| 1077 | static mut INSTR_MEM_STATUS: &'static mut [[u32; 4]; 2] = &mut [[0; 4]; 2]; | ||
| 1078 | |||
| 1079 | fn instr_mem_get_status(pio_no: u8, addr: u8) -> u32 { | ||
| 1080 | ((unsafe { INSTR_MEM_STATUS[pio_no as usize][(addr >> 3) as usize] }) >> ((addr & 0x07) * 4)) & 0xf | ||
| 1081 | } | ||
| 1082 | |||
| 1083 | fn instr_mem_set_status(pio_no: u8, addr: u8, status: u32) { | ||
| 1084 | let w = unsafe { &mut INSTR_MEM_STATUS[pio_no as usize][(addr >> 3) as usize] }; | ||
| 1085 | let shift = (addr & 0x07) * 4; | ||
| 1086 | *w = (*w & !(0xf << shift)) | (status << shift); | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | fn instr_mem_is_free(pio_no: u8, addr: u8) -> bool { | ||
| 1090 | instr_mem_get_status(pio_no, addr) == MEM_NOT_USED | ||
| 1091 | } | ||
| 1092 | |||
| 1093 | pub struct PioCommonInstance<PIO: PioInstance> { | ||
| 1094 | pio: PhantomData<PIO>, | ||
| 1095 | } | ||
| 1096 | |||
| 1097 | impl<PIO: PioInstance> PioCommon for PioCommonInstance<PIO> { | ||
| 1098 | type Pio = PIO; | ||
| 1099 | } | ||
| 1100 | |||
| 1101 | fn write_instr(pio_no: u8, start: usize, instrs: &[u16], mem_user: u32) { | ||
| 1102 | for (i, instr) in instrs.iter().enumerate() { | ||
| 1103 | let addr = (i + start) as u8; | ||
| 1104 | assert!( | ||
| 1105 | instr_mem_is_free(pio_no, addr), | ||
| 1106 | "Trying to write already used PIO instruction memory at {}", | ||
| 1107 | addr | ||
| 1108 | ); | ||
| 1109 | unsafe { | ||
| 1110 | PIOS[pio_no as usize].instr_mem(addr as usize).write(|w| { | ||
| 1111 | w.set_instr_mem(*instr); | ||
| 1112 | }); | ||
| 1113 | instr_mem_set_status(pio_no, addr, mem_user); | ||
| 1114 | } | ||
| 1115 | } | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | pub trait PioCommon: Sized { | ||
| 1119 | type Pio: PioInstance; | ||
| 1120 | |||
| 1121 | fn write_instr(&mut self, start: usize, instrs: &[u16]) { | ||
| 1122 | let _ = self; | ||
| 1123 | write_instr(Self::Pio::PIO_NO, start, instrs, MEM_USED_BY_COMMON); | ||
| 1124 | } | ||
| 1125 | |||
| 1126 | fn clear_irq(&mut self, irq_no: usize) { | ||
| 1127 | assert!(irq_no < 8); | ||
| 1128 | unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(1 << irq_no)) } | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | fn clear_irqs(&mut self, mask: u8) { | ||
| 1132 | unsafe { PIOS[Self::Pio::PIO_NO as usize].irq().write(|w| w.set_irq(mask)) } | ||
| 1133 | } | ||
| 1134 | |||
| 1135 | fn force_irq(&mut self, irq_no: usize) { | ||
| 1136 | assert!(irq_no < 8); | ||
| 1137 | unsafe { | ||
| 1138 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 1139 | .irq_force() | ||
| 1140 | .write(|w| w.set_irq_force(1 << irq_no)) | ||
| 1141 | } | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { | ||
| 1145 | unsafe { | ||
| 1146 | PIOS[Self::Pio::PIO_NO as usize] | ||
| 1147 | .input_sync_bypass() | ||
| 1148 | .modify(|w| *w = (*w & !mask) | (bypass & mask)); | ||
| 1149 | } | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | fn get_input_sync_bypass(&self) -> u32 { | ||
| 1153 | unsafe { PIOS[Self::Pio::PIO_NO as usize].input_sync_bypass().read() } | ||
| 1154 | } | ||
| 1155 | } | ||
| 1156 | |||
| 1157 | // Identifies a specific state machine inside a PIO device | ||
| 1158 | pub struct SmInstanceBase<const SM_NO: u8> {} | ||
| 1159 | |||
| 1160 | pub trait SmInstance: Unpin { | ||
| 1161 | const SM_NO: u8; | ||
| 1162 | } | ||
| 1163 | |||
| 1164 | impl<const SM_NO: u8> SmInstance for SmInstanceBase<SM_NO> { | ||
| 1165 | const SM_NO: u8 = SM_NO; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | pub trait PioPeripherial: Sized { | ||
| 1169 | type Pio: PioInstance; | ||
| 1170 | fn pio(&self) -> u8 { | ||
| 1171 | let _ = self; | ||
| 1172 | Self::Pio::PIO_NO | ||
| 1173 | } | ||
| 1174 | |||
| 1175 | fn split( | ||
| 1176 | self, | ||
| 1177 | ) -> ( | ||
| 1178 | PioCommonInstance<Self::Pio>, | ||
| 1179 | PioStateMachineInstance<Self::Pio, SmInstanceBase<0>>, | ||
| 1180 | PioStateMachineInstance<Self::Pio, SmInstanceBase<1>>, | ||
| 1181 | PioStateMachineInstance<Self::Pio, SmInstanceBase<2>>, | ||
| 1182 | PioStateMachineInstance<Self::Pio, SmInstanceBase<3>>, | ||
| 1183 | ) { | ||
| 1184 | let _ = self; | ||
| 1185 | ( | ||
| 1186 | PioCommonInstance { | ||
| 1187 | pio: PhantomData::default(), | ||
| 1188 | }, | ||
| 1189 | PioStateMachineInstance { | ||
| 1190 | sm: PhantomData::default(), | ||
| 1191 | pio: PhantomData::default(), | ||
| 1192 | }, | ||
| 1193 | PioStateMachineInstance { | ||
| 1194 | sm: PhantomData::default(), | ||
| 1195 | pio: PhantomData::default(), | ||
| 1196 | }, | ||
| 1197 | PioStateMachineInstance { | ||
| 1198 | sm: PhantomData::default(), | ||
| 1199 | pio: PhantomData::default(), | ||
| 1200 | }, | ||
| 1201 | PioStateMachineInstance { | ||
| 1202 | sm: PhantomData::default(), | ||
| 1203 | pio: PhantomData::default(), | ||
| 1204 | }, | ||
| 1205 | ) | ||
| 1206 | } | ||
| 1207 | } | ||
| 1208 | |||
| 1209 | // Identifies a specific PIO device | ||
| 1210 | pub struct PioInstanceBase<const PIO_NO: u8> {} | ||
| 1211 | |||
| 1212 | pub trait PioInstance: Unpin { | ||
| 1213 | const PIO_NO: u8; | ||
| 1214 | type IrqOut: Interrupt; | ||
| 1215 | type IrqIn: Interrupt; | ||
| 1216 | type IrqSm: Interrupt; | ||
| 1217 | } | ||
| 1218 | |||
| 1219 | impl PioInstance for PioInstanceBase<0> { | ||
| 1220 | const PIO_NO: u8 = 0; | ||
| 1221 | type IrqOut = interrupt::PIO0_IRQ_0; | ||
| 1222 | type IrqIn = interrupt::PIO0_IRQ_1; | ||
| 1223 | type IrqSm = interrupt::PIO0_IRQ_1; | ||
| 1224 | } | ||
| 1225 | |||
| 1226 | impl PioInstance for PioInstanceBase<1> { | ||
| 1227 | const PIO_NO: u8 = 1; | ||
| 1228 | type IrqOut = interrupt::PIO1_IRQ_0; | ||
| 1229 | type IrqIn = interrupt::PIO1_IRQ_1; | ||
| 1230 | type IrqSm = interrupt::PIO1_IRQ_1; | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | pub type Pio0 = PioInstanceBase<0>; | ||
| 1234 | pub type Pio1 = PioInstanceBase<1>; | ||
| 1235 | |||
| 1236 | pub type Sm0 = SmInstanceBase<0>; | ||
| 1237 | pub type Sm1 = SmInstanceBase<1>; | ||
| 1238 | pub type Sm2 = SmInstanceBase<2>; | ||
| 1239 | pub type Sm3 = SmInstanceBase<3>; | ||
| 1240 | |||
| 1241 | macro_rules! impl_pio_sm { | ||
| 1242 | ($name:ident, $pio:expr) => { | ||
| 1243 | impl PioPeripherial for peripherals::$name { | ||
| 1244 | type Pio = PioInstanceBase<$pio>; | ||
| 1245 | } | ||
| 1246 | }; | ||
| 1247 | } | ||
| 1248 | |||
| 1249 | impl_pio_sm!(PIO0, 0); | ||
| 1250 | impl_pio_sm!(PIO1, 1); | ||
diff --git a/embassy-rp/src/pio_instr_util.rs b/embassy-rp/src/pio_instr_util.rs new file mode 100644 index 000000000..ae26ff1dc --- /dev/null +++ b/embassy-rp/src/pio_instr_util.rs | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination}; | ||
| 2 | |||
| 3 | use crate::pio::PioStateMachine; | ||
| 4 | |||
| 5 | pub fn set_x<SM: PioStateMachine>(sm: &mut SM, value: u32) { | ||
| 6 | const OUT: u16 = InstructionOperands::OUT { | ||
| 7 | destination: OutDestination::X, | ||
| 8 | bit_count: 32, | ||
| 9 | } | ||
| 10 | .encode(); | ||
| 11 | sm.push_tx(value); | ||
| 12 | sm.exec_instr(OUT); | ||
| 13 | } | ||
| 14 | |||
| 15 | pub fn get_x<SM: PioStateMachine>(sm: &mut SM) -> u32 { | ||
| 16 | const IN: u16 = InstructionOperands::IN { | ||
| 17 | source: InSource::X, | ||
| 18 | bit_count: 32, | ||
| 19 | } | ||
| 20 | .encode(); | ||
| 21 | sm.exec_instr(IN); | ||
| 22 | sm.pull_rx() | ||
| 23 | } | ||
| 24 | |||
| 25 | pub fn set_y<SM: PioStateMachine>(sm: &mut SM, value: u32) { | ||
| 26 | const OUT: u16 = InstructionOperands::OUT { | ||
| 27 | destination: OutDestination::Y, | ||
| 28 | bit_count: 32, | ||
| 29 | } | ||
| 30 | .encode(); | ||
| 31 | sm.push_tx(value); | ||
| 32 | sm.exec_instr(OUT); | ||
| 33 | } | ||
| 34 | |||
| 35 | pub fn get_y<SM: PioStateMachine>(sm: &mut SM) -> u32 { | ||
| 36 | const IN: u16 = InstructionOperands::IN { | ||
| 37 | source: InSource::Y, | ||
| 38 | bit_count: 32, | ||
| 39 | } | ||
| 40 | .encode(); | ||
| 41 | sm.exec_instr(IN); | ||
| 42 | |||
| 43 | sm.pull_rx() | ||
| 44 | } | ||
| 45 | |||
| 46 | pub fn set_pindir<SM: PioStateMachine>(sm: &mut SM, data: u8) { | ||
| 47 | let set: u16 = InstructionOperands::SET { | ||
| 48 | destination: SetDestination::PINDIRS, | ||
| 49 | data, | ||
| 50 | } | ||
| 51 | .encode(); | ||
| 52 | sm.exec_instr(set); | ||
| 53 | } | ||
| 54 | |||
| 55 | pub fn set_pin<SM: PioStateMachine>(sm: &mut SM, data: u8) { | ||
| 56 | let set: u16 = InstructionOperands::SET { | ||
| 57 | destination: SetDestination::PINS, | ||
| 58 | data, | ||
| 59 | } | ||
| 60 | .encode(); | ||
| 61 | sm.exec_instr(set); | ||
| 62 | } | ||
| 63 | |||
| 64 | pub fn set_out_pin<SM: PioStateMachine>(sm: &mut SM, data: u32) { | ||
| 65 | const OUT: u16 = InstructionOperands::OUT { | ||
| 66 | destination: OutDestination::PINS, | ||
| 67 | bit_count: 32, | ||
| 68 | } | ||
| 69 | .encode(); | ||
| 70 | sm.push_tx(data); | ||
| 71 | sm.exec_instr(OUT); | ||
| 72 | } | ||
| 73 | pub fn set_out_pindir<SM: PioStateMachine>(sm: &mut SM, data: u32) { | ||
| 74 | const OUT: u16 = InstructionOperands::OUT { | ||
| 75 | destination: OutDestination::PINDIRS, | ||
| 76 | bit_count: 32, | ||
| 77 | } | ||
| 78 | .encode(); | ||
| 79 | sm.push_tx(data); | ||
| 80 | sm.exec_instr(OUT); | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn exec_jmp<SM: PioStateMachine>(sm: &mut SM, to_addr: u8) { | ||
| 84 | let jmp: u16 = InstructionOperands::JMP { | ||
| 85 | address: to_addr, | ||
| 86 | condition: JmpCondition::Always, | ||
| 87 | } | ||
| 88 | .encode(); | ||
| 89 | sm.exec_instr(jmp); | ||
| 90 | } | ||
diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 60a8ba94d..b07c471af 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml | |||
| @@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0" | |||
| 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } | 9 | embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } |
| 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } | 10 | embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } |
| 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } | 11 | embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } |
| 12 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } | 12 | embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio"] } |
| 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } | 13 | embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } |
| 14 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } | 14 | embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } |
| 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | 15 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } |
| @@ -34,6 +34,11 @@ embedded-io = { version = "0.4.0", features = ["async", "defmt"] } | |||
| 34 | embedded-storage = { version = "0.3" } | 34 | embedded-storage = { version = "0.3" } |
| 35 | static_cell = "1.0.0" | 35 | static_cell = "1.0.0" |
| 36 | log = "0.4" | 36 | log = "0.4" |
| 37 | pio-proc = "0.2" | ||
| 38 | pio = "0.2" | ||
| 37 | 39 | ||
| 38 | [profile.release] | 40 | [profile.release] |
| 39 | debug = true | 41 | debug = true |
| 42 | |||
| 43 | [patch.crates-io] | ||
| 44 | pio = {git = "https://github.com/rp-rs/pio-rs.git"} \ No newline at end of file | ||
diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs new file mode 100644 index 000000000..e9211db3b --- /dev/null +++ b/examples/rp/src/bin/pio_async.rs | |||
| @@ -0,0 +1,105 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_rp::gpio::{AnyPin, Pin}; | ||
| 7 | use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; | ||
| 8 | use embassy_rp::pio_instr_util; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::task] | ||
| 12 | async fn pio_task_sm0(mut sm: PioStateMachineInstance<Pio0, Sm0>, pin: AnyPin) { | ||
| 13 | // Setup sm0 | ||
| 14 | |||
| 15 | // Send data serially to pin | ||
| 16 | let prg = pio_proc::pio_asm!( | ||
| 17 | ".origin 16", | ||
| 18 | "set pindirs, 1", | ||
| 19 | ".wrap_target", | ||
| 20 | "out pins,1 [19]", | ||
| 21 | ".wrap", | ||
| 22 | ); | ||
| 23 | |||
| 24 | let origin = prg.program.origin.unwrap_or(0); | ||
| 25 | let out_pin = sm.make_pio_pin(pin); | ||
| 26 | let pio_pins = [&out_pin]; | ||
| 27 | sm.set_out_pins(&pio_pins); | ||
| 28 | sm.write_instr(origin as usize, &prg.program.code); | ||
| 29 | pio_instr_util::exec_jmp(&mut sm, origin); | ||
| 30 | sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); | ||
| 31 | sm.set_set_range(0, 1); | ||
| 32 | sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); | ||
| 33 | sm.set_autopull(true); | ||
| 34 | sm.set_out_shift_dir(ShiftDirection::Left); | ||
| 35 | |||
| 36 | sm.set_enable(true); | ||
| 37 | |||
| 38 | let mut v = 0x0f0caffa; | ||
| 39 | loop { | ||
| 40 | sm.wait_push(v).await; | ||
| 41 | v ^= 0xffff; | ||
| 42 | info!("Pushed {:032b} to FIFO", v); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | #[embassy_executor::task] | ||
| 47 | async fn pio_task_sm1(mut sm: PioStateMachineInstance<Pio0, Sm1>) { | ||
| 48 | // Setupm sm1 | ||
| 49 | |||
| 50 | // Read 0b10101 repeatedly until ISR is full | ||
| 51 | let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); | ||
| 52 | |||
| 53 | let origin = prg.program.origin.unwrap_or(0); | ||
| 54 | sm.write_instr(origin as usize, &prg.program.code); | ||
| 55 | pio_instr_util::exec_jmp(&mut sm, origin); | ||
| 56 | sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); | ||
| 57 | sm.set_set_range(0, 0); | ||
| 58 | sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); | ||
| 59 | sm.set_autopush(true); | ||
| 60 | sm.set_in_shift_dir(ShiftDirection::Right); | ||
| 61 | sm.set_enable(true); | ||
| 62 | loop { | ||
| 63 | let rx = sm.wait_pull().await; | ||
| 64 | info!("Pulled {:032b} from FIFO", rx); | ||
| 65 | } | ||
| 66 | } | ||
| 67 | |||
| 68 | #[embassy_executor::task] | ||
| 69 | async fn pio_task_sm2(mut sm: PioStateMachineInstance<Pio0, Sm2>) { | ||
| 70 | // Setup sm2 | ||
| 71 | |||
| 72 | // Repeatedly trigger IRQ 3 | ||
| 73 | let prg = pio_proc::pio_asm!( | ||
| 74 | ".origin 0", | ||
| 75 | ".wrap_target", | ||
| 76 | "set x,10", | ||
| 77 | "delay:", | ||
| 78 | "jmp x-- delay [15]", | ||
| 79 | "irq 3 [15]", | ||
| 80 | ".wrap", | ||
| 81 | ); | ||
| 82 | let origin = prg.program.origin.unwrap_or(0); | ||
| 83 | |||
| 84 | sm.write_instr(origin as usize, &prg.program.code); | ||
| 85 | sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); | ||
| 86 | pio_instr_util::exec_jmp(&mut sm, origin); | ||
| 87 | sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); | ||
| 88 | sm.set_enable(true); | ||
| 89 | loop { | ||
| 90 | sm.wait_irq(3).await; | ||
| 91 | info!("IRQ trigged"); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | #[embassy_executor::main] | ||
| 96 | async fn main(spawner: Spawner) { | ||
| 97 | let p = embassy_rp::init(Default::default()); | ||
| 98 | let pio = p.PIO0; | ||
| 99 | |||
| 100 | let (_, sm0, sm1, sm2, ..) = pio.split(); | ||
| 101 | |||
| 102 | spawner.spawn(pio_task_sm0(sm0, p.PIN_0.degrade())).unwrap(); | ||
| 103 | spawner.spawn(pio_task_sm1(sm1)).unwrap(); | ||
| 104 | spawner.spawn(pio_task_sm2(sm2)).unwrap(); | ||
| 105 | } | ||
diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs new file mode 100644 index 000000000..bdcdf200c --- /dev/null +++ b/examples/rp/src/bin/pio_dma.rs | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | use defmt::info; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_futures::join::join; | ||
| 7 | use embassy_rp::pio::{PioPeripherial, PioStateMachine, ShiftDirection}; | ||
| 8 | use embassy_rp::{pio_instr_util, Peripheral}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | fn swap_nibbles(v: u32) -> u32 { | ||
| 12 | let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4; | ||
| 13 | let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8; | ||
| 14 | (v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16 | ||
| 15 | } | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_rp::init(Default::default()); | ||
| 20 | let pio = p.PIO0; | ||
| 21 | let (_, mut sm, ..) = pio.split(); | ||
| 22 | |||
| 23 | let prg = pio_proc::pio_asm!( | ||
| 24 | ".origin 0", | ||
| 25 | "set pindirs,1", | ||
| 26 | ".wrap_target", | ||
| 27 | "set y,7", | ||
| 28 | "loop:", | ||
| 29 | "out x,4", | ||
| 30 | "in x,4", | ||
| 31 | "jmp y--, loop", | ||
| 32 | ".wrap", | ||
| 33 | ); | ||
| 34 | |||
| 35 | let origin = prg.program.origin.unwrap_or(0); | ||
| 36 | sm.write_instr(origin as usize, &prg.program.code); | ||
| 37 | pio_instr_util::exec_jmp(&mut sm, origin); | ||
| 38 | sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); | ||
| 39 | sm.set_wrap(prg.program.wrap.source + origin, prg.program.wrap.target + origin); | ||
| 40 | sm.set_autopull(true); | ||
| 41 | sm.set_autopush(true); | ||
| 42 | sm.set_pull_threshold(32); | ||
| 43 | sm.set_push_threshold(32); | ||
| 44 | sm.set_out_shift_dir(ShiftDirection::Right); | ||
| 45 | sm.set_in_shift_dir(ShiftDirection::Left); | ||
| 46 | |||
| 47 | sm.set_enable(true); | ||
| 48 | |||
| 49 | let mut dma_out_ref = p.DMA_CH0.into_ref(); | ||
| 50 | let mut dma_in_ref = p.DMA_CH1.into_ref(); | ||
| 51 | let mut dout = [0x12345678u32; 29]; | ||
| 52 | for i in 1..dout.len() { | ||
| 53 | dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7; | ||
| 54 | } | ||
| 55 | let mut din = [0u32; 29]; | ||
| 56 | loop { | ||
| 57 | join( | ||
| 58 | sm.dma_push(dma_out_ref.reborrow(), &dout), | ||
| 59 | sm.dma_pull(dma_in_ref.reborrow(), &mut din), | ||
| 60 | ) | ||
| 61 | .await; | ||
| 62 | for i in 0..din.len() { | ||
| 63 | assert_eq!(din[i], swap_nibbles(dout[i])); | ||
| 64 | } | ||
| 65 | info!("Swapped {} words", dout.len()); | ||
| 66 | } | ||
| 67 | } | ||
