From 3c31236c1050f98c870ec03b98bc746e6e2ab1b5 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 07:40:12 +0200 Subject: [PATCH 01/19] rp: remove leftovers from #1414 forgot to remove these when they were no longer necessary or useful. oops. --- embassy-rp/src/uart/mod.rs | 8 +++----- tests/rp/src/bin/uart_dma.rs | 4 ---- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index ebe32cc66..4084c158a 100644 --- a/embassy-rp/src/uart/mod.rs +++ b/embassy-rp/src/uart/mod.rs @@ -874,7 +874,6 @@ mod sealed { pub trait Instance { const TX_DREQ: u8; const RX_DREQ: u8; - const ID: usize; type Interrupt: crate::interrupt::Interrupt; @@ -909,11 +908,10 @@ impl_mode!(Async); pub trait Instance: sealed::Instance {} macro_rules! impl_instance { - ($inst:ident, $irq:ident, $id:expr, $tx_dreq:expr, $rx_dreq:expr) => { + ($inst:ident, $irq:ident, $tx_dreq:expr, $rx_dreq:expr) => { impl sealed::Instance for peripherals::$inst { const TX_DREQ: u8 = $tx_dreq; const RX_DREQ: u8 = $rx_dreq; - const ID: usize = $id; type Interrupt = crate::interrupt::$irq; @@ -939,8 +937,8 @@ macro_rules! impl_instance { }; } -impl_instance!(UART0, UART0_IRQ, 0, 20, 21); -impl_instance!(UART1, UART1_IRQ, 1, 22, 23); +impl_instance!(UART0, UART0_IRQ, 20, 21); +impl_instance!(UART1, UART1_IRQ, 22, 23); pub trait TxPin: sealed::TxPin + crate::gpio::Pin {} pub trait RxPin: sealed::RxPin + crate::gpio::Pin {} diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index 92aa205c9..52f42e582 100644 --- a/tests/rp/src/bin/uart_dma.rs +++ b/tests/rp/src/bin/uart_dma.rs @@ -53,10 +53,6 @@ async fn main(_spawner: Spawner) { let (mut tx, mut rx, mut uart) = (p.PIN_0, p.PIN_1, p.UART0); let mut irq = interrupt::take!(UART0_IRQ); - // TODO - // nuclear error reporting. just abort the entire transfer and invalidate the - // dma buffer, buffered buffer, fifo etc. - // We can't send too many bytes, they have to fit in the FIFO. // This is because we aren't sending+receiving at the same time. { From c6424fdc112776b5ceeef4a01c56b1479c2901c5 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 08:29:32 +0200 Subject: [PATCH 02/19] gp/gpio: fix InputFuture edge waits InputFuture did not use and check edge interrupts correctly. InterruptTrigger should've checked for not 1,2,3,4 but 1,2,4,8 since the inte fields are bitmasks, and not clearing INTR would have repeatedly triggered edge interrupts early. --- embassy-rp/src/gpio.rs | 126 ++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 83 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 98e182868..7a86418aa 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -136,18 +136,6 @@ pub enum InterruptTrigger { AnyEdge, } -impl InterruptTrigger { - fn from_u32(value: u32) -> Option { - match value { - 1 => Some(InterruptTrigger::LevelLow), - 2 => Some(InterruptTrigger::LevelHigh), - 3 => Some(InterruptTrigger::EdgeLow), - 4 => Some(InterruptTrigger::EdgeHigh), - _ => None, - } - } -} - #[interrupt] unsafe fn IO_IRQ_BANK0() { let cpu = SIO.cpuid().read() as usize; @@ -166,26 +154,16 @@ unsafe fn IO_IRQ_BANK0() { let pin_group = (pin % 8) as usize; let event = (intsx.read().0 >> pin_group * 4) & 0xf as u32; - if let Some(trigger) = InterruptTrigger::from_u32(event) { + // no more than one event can be awaited per pin at any given time, so + // we can just clear all interrupt enables for that pin without having + // to check which event was signalled. + if event != 0 { critical_section::with(|_| { - proc_intx.inte(pin / 8).modify(|w| match trigger { - InterruptTrigger::AnyEdge => { - w.set_edge_high(pin_group, false); - w.set_edge_low(pin_group, false); - } - InterruptTrigger::LevelHigh => { - trace!("IO_IRQ_BANK0 pin {} LevelHigh triggered", pin); - w.set_level_high(pin_group, false); - } - InterruptTrigger::LevelLow => { - w.set_level_low(pin_group, false); - } - InterruptTrigger::EdgeHigh => { - w.set_edge_high(pin_group, false); - } - InterruptTrigger::EdgeLow => { - w.set_edge_low(pin_group, false); - } + proc_intx.inte(pin / 8).modify(|w| { + w.set_edge_high(pin_group, false); + w.set_edge_low(pin_group, false); + w.set_level_high(pin_group, false); + w.set_level_low(pin_group, false); }); }); INTERRUPT_WAKERS[pin as usize].wake(); @@ -207,10 +185,23 @@ impl<'d, T: Pin> InputFuture<'d, T> { irq.disable(); irq.set_priority(interrupt::Priority::P3); + let pin_group = (pin.pin() % 8) as usize; + // first, clear the INTR register bits. without this INTR will still + // contain reports of previous edges, causing the IRQ to fire early + // on stale state. clearing these means that we can only detect edges + // that occur *after* the clear happened, but since both this and the + // alternative are fundamentally racy it's probably fine. + // (the alternative being checking the current level and waiting for + // its inverse, but that requires reading the current level and thus + // missing anything that happened before the level was read.) + pac::IO_BANK0.intr(pin.pin() as usize / 8).write(|w| { + w.set_edge_high(pin_group, true); + w.set_edge_low(pin_group, true); + }); + // Each INTR register is divided into 8 groups, one group for each // pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW, // and EGDE_HIGH. - let pin_group = (pin.pin() % 8) as usize; critical_section::with(|_| { pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { InterruptTrigger::LevelHigh => { @@ -227,7 +218,8 @@ impl<'d, T: Pin> InputFuture<'d, T> { w.set_edge_low(pin_group, true); } InterruptTrigger::AnyEdge => { - // noop + w.set_edge_high(pin_group, true); + w.set_edge_low(pin_group, true); } }); }); @@ -257,47 +249,21 @@ impl<'d, T: Pin> Future for InputFuture<'d, T> { // LEVEL_HIGH, EDGE_LOW, and EDGE_HIGH for each pin. let pin_group = (self.pin.pin() % 8) as usize; - // This should check the the level of the interrupt trigger level of - // the pin and if it has been disabled that means it was done by the - // interrupt service routine, so we then know that the event/trigger - // happened and Poll::Ready will be returned. - trace!("{:?} for pin {}", self.level, self.pin.pin()); - match self.level { - InterruptTrigger::AnyEdge => { - if !inte.edge_high(pin_group) && !inte.edge_low(pin_group) { - #[rustfmt::skip] - trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); - return Poll::Ready(()); - } - } - InterruptTrigger::LevelHigh => { - if !inte.level_high(pin_group) { - #[rustfmt::skip] - trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); - return Poll::Ready(()); - } - } - InterruptTrigger::LevelLow => { - if !inte.level_low(pin_group) { - #[rustfmt::skip] - trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); - return Poll::Ready(()); - } - } - InterruptTrigger::EdgeHigh => { - if !inte.edge_high(pin_group) { - #[rustfmt::skip] - trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); - return Poll::Ready(()); - } - } - InterruptTrigger::EdgeLow => { - if !inte.edge_low(pin_group) { - #[rustfmt::skip] - trace!("{:?} for pin {} was cleared, return Poll::Ready", self.level, self.pin.pin()); - return Poll::Ready(()); - } - } + // since the interrupt handler clears all INTE flags we'll check that + // all have been cleared and unconditionally return Ready(()) if so. + // we don't need further handshaking since only a single event wait + // is possible for any given pin at any given time. + if !inte.edge_high(pin_group) + && !inte.edge_low(pin_group) + && !inte.level_high(pin_group) + && !inte.level_low(pin_group) + { + trace!( + "{:?} for pin {} was cleared, return Poll::Ready", + self.level, + self.pin.pin() + ); + return Poll::Ready(()); } trace!("InputFuture::poll return Poll::Pending"); Poll::Pending @@ -644,23 +610,17 @@ impl<'d, T: Pin> Flex<'d, T> { #[inline] pub async fn wait_for_rising_edge(&mut self) { - self.wait_for_low().await; - self.wait_for_high().await; + InputFuture::new(&mut self.pin, InterruptTrigger::EdgeHigh).await; } #[inline] pub async fn wait_for_falling_edge(&mut self) { - self.wait_for_high().await; - self.wait_for_low().await; + InputFuture::new(&mut self.pin, InterruptTrigger::EdgeLow).await; } #[inline] pub async fn wait_for_any_edge(&mut self) { - if self.is_high() { - self.wait_for_low().await; - } else { - self.wait_for_high().await; - } + InputFuture::new(&mut self.pin, InterruptTrigger::AnyEdge).await; } } From 8fc92fdf6285190a1ba5ddf356958d49ed4225a3 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 08:34:36 +0200 Subject: [PATCH 03/19] rp/gpio: drop critical_section use we don't need critical sections if we just use atomic access aliases. --- embassy-rp/src/gpio.rs | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 7a86418aa..66b9af04b 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -9,7 +9,7 @@ use embassy_sync::waitqueue::AtomicWaker; use crate::pac::common::{Reg, RW}; use crate::pac::SIO; -use crate::{interrupt, pac, peripherals, Peripheral}; +use crate::{interrupt, pac, peripherals, Peripheral, RegExt}; const PIN_COUNT: usize = 30; const NEW_AW: AtomicWaker = AtomicWaker::new(); @@ -158,13 +158,11 @@ unsafe fn IO_IRQ_BANK0() { // we can just clear all interrupt enables for that pin without having // to check which event was signalled. if event != 0 { - critical_section::with(|_| { - proc_intx.inte(pin / 8).modify(|w| { - w.set_edge_high(pin_group, false); - w.set_edge_low(pin_group, false); - w.set_level_high(pin_group, false); - w.set_level_low(pin_group, false); - }); + proc_intx.inte(pin / 8).write_clear(|w| { + w.set_edge_high(pin_group, true); + w.set_edge_low(pin_group, true); + w.set_level_high(pin_group, true); + w.set_level_low(pin_group, true); }); INTERRUPT_WAKERS[pin as usize].wake(); } @@ -202,8 +200,9 @@ impl<'d, T: Pin> InputFuture<'d, T> { // Each INTR register is divided into 8 groups, one group for each // pin, and each group consists of LEVEL_LOW, LEVEL_HIGH, EDGE_LOW, // and EGDE_HIGH. - critical_section::with(|_| { - pin.int_proc().inte((pin.pin() / 8) as usize).modify(|w| match level { + pin.int_proc() + .inte((pin.pin() / 8) as usize) + .write_set(|w| match level { InterruptTrigger::LevelHigh => { trace!("InputFuture::new enable LevelHigh for pin {}", pin.pin()); w.set_level_high(pin_group, true); @@ -222,7 +221,6 @@ impl<'d, T: Pin> InputFuture<'d, T> { w.set_edge_low(pin_group, true); } }); - }); irq.enable(); } From 54e695b1b2105c1b2484584d100564afc0679596 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 10:50:06 +0200 Subject: [PATCH 04/19] rp/pio: fix dma fixing the dma word size to 32 makes it impossible to implement any peripheral that takes its data in smaller chunks, eg uart, spi, i2c, ws2812, the list goes on. compiler barriers were also not set correctly; we need a SeqCst barrier before starting a transfer as well to avoid reordering of accesses into a buffer after dma has started. --- embassy-rp/src/pio.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 7faec10b5..cab57f765 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -8,10 +8,10 @@ use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; use embassy_hal_common::PeripheralRef; use embassy_sync::waitqueue::AtomicWaker; -use crate::dma::{Channel, Transfer}; +use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{Drive, Pin, Pull, SlewRate}; -use crate::pac::dma::vals::{DataSize, TreqSel}; +use crate::pac::dma::vals::TreqSel; use crate::pio::sealed::{PioInstance as _, SmInstance as _}; use crate::{interrupt, pac, peripherals, RegExt}; @@ -820,7 +820,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } - fn dma_push<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [u32]) -> Transfer<'a, C> { + fn dma_push<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> { unsafe { let pio_no = Self::Pio::PIO_NO; let sm_no = Self::Sm::SM_NO; @@ -829,10 +829,11 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { p.write_addr() .write_value(Self::Pio::PIO.txf(sm_no as usize).ptr() as u32); p.trans_count().write_value(data.len() as u32); + compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { // Set TX DREQ for this statemachine w.set_treq_sel(TreqSel(pio_no * 8 + sm_no)); - w.set_data_size(DataSize::SIZE_WORD); + w.set_data_size(W::size()); w.set_chain_to(ch.number()); w.set_incr_read(true); w.set_incr_write(false); @@ -843,7 +844,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { Transfer::new(ch) } - fn dma_pull<'a, C: Channel>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [u32]) -> Transfer<'a, C> { + fn dma_pull<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [W]) -> Transfer<'a, C> { unsafe { let pio_no = Self::Pio::PIO_NO; let sm_no = Self::Sm::SM_NO; @@ -852,10 +853,11 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { p.read_addr() .write_value(Self::Pio::PIO.rxf(sm_no as usize).ptr() as u32); p.trans_count().write_value(data.len() as u32); + compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { - // Set TX DREQ for this statemachine + // Set RX DREQ for this statemachine w.set_treq_sel(TreqSel(pio_no * 8 + sm_no + 4)); - w.set_data_size(DataSize::SIZE_WORD); + w.set_data_size(W::size()); w.set_chain_to(ch.number()); w.set_incr_read(false); w.set_incr_write(true); From 8e22d574478d7480bc0473ed0ad9dac7d02602a8 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 10:44:00 +0200 Subject: [PATCH 05/19] rp/pio: add hd44780 example add an hd44780 example for pio. hd44780 with busy polling is a pretty complicated protocol if the busy polling is to be done by the peripheral, and this example exercises many pio features that we don't have good examples for yet. --- examples/rp/src/bin/pio_hd44780.rs | 244 +++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 examples/rp/src/bin/pio_hd44780.rs diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs new file mode 100644 index 000000000..6bcd0652b --- /dev/null +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -0,0 +1,244 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use core::fmt::Write; + +use embassy_executor::Spawner; +use embassy_rp::dma::{AnyChannel, Channel}; +use embassy_rp::gpio::Pin; +use embassy_rp::peripherals::PIO0; +use embassy_rp::pio::{ + FifoJoin, PioCommon, PioInstanceBase, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, + SmInstanceBase, +}; +use embassy_rp::pwm::{Config, Pwm}; +use embassy_rp::relocate::RelocatedProgram; +use embassy_rp::{into_ref, Peripheral, PeripheralRef}; +use embassy_time::{Duration, Instant, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + // this test assumes a 2x16 HD44780 display attached as follow: + // rs = PIN0 + // rw = PIN1 + // e = PIN2 + // db4 = PIN3 + // db5 = PIN4 + // db6 = PIN5 + // db7 = PIN6 + // additionally a pwm signal for a bias voltage charge pump is provided on pin 15, + // allowing direct connection of the display to the RP2040 without level shifters. + let p = embassy_rp::init(Default::default()); + + let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { + let mut c = Config::default(); + c.divider = 125.into(); + c.top = 100; + c.compare_b = 50; + c + }); + + let mut hd = HD44780::new( + p.PIO0, p.DMA_CH3, p.PIN_0, p.PIN_1, p.PIN_2, p.PIN_3, p.PIN_4, p.PIN_5, p.PIN_6, + ) + .await; + + loop { + struct Buf([u8; N], usize); + impl Write for Buf { + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + for b in s.as_bytes() { + if self.1 >= N { + return Err(core::fmt::Error); + } + self.0[self.1] = *b; + self.1 += 1; + } + Ok(()) + } + } + let mut buf = Buf([0; 16], 0); + write!(buf, "up {}s", Instant::now().as_micros() as f32 / 1e6).unwrap(); + hd.add_line(&buf.0[0..buf.1]).await; + Timer::after(Duration::from_secs(1)).await; + } +} + +pub struct HD44780<'l> { + dma: PeripheralRef<'l, AnyChannel>, + sm: PioStateMachineInstance, SmInstanceBase<0>>, + + buf: [u8; 40], +} + +impl<'l> HD44780<'l> { + pub async fn new( + pio: PIO0, + dma: impl Peripheral

+ 'l, + rs: impl Pin, + rw: impl Pin, + e: impl Pin, + db4: impl Pin, + db5: impl Pin, + db6: impl Pin, + db7: impl Pin, + ) -> HD44780<'l> { + into_ref!(dma); + + let db7pin = db7.pin(); + let (mut common, mut sm0, ..) = pio.split(); + + // takes command words ( <0:4>) + let prg = pio_proc::pio_asm!( + r#" + .side_set 1 opt + + loop: + out x, 24 + delay: + jmp x--, delay + out pins, 4 side 1 + out null, 4 side 0 + jmp !osre, loop + irq 0 + "#, + ); + + let rs = common.make_pio_pin(rs); + let rw = common.make_pio_pin(rw); + let e = common.make_pio_pin(e); + let db4 = common.make_pio_pin(db4); + let db5 = common.make_pio_pin(db5); + let db6 = common.make_pio_pin(db6); + let db7 = common.make_pio_pin(db7); + + sm0.set_set_pins(&[&rs, &rw]); + embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11); + sm0.set_set_pins(&[&e]); + embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b1); + sm0.set_set_pins(&[&db4, &db5, &db6, &db7]); + embassy_rp::pio_instr_util::set_pindir(&mut sm0, 0b11111); + + let relocated = RelocatedProgram::new(&prg.program); + common.write_instr(relocated.origin() as usize, relocated.code()); + embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); + sm0.set_clkdiv(125 * 256); + let pio::Wrap { source, target } = relocated.wrap(); + sm0.set_wrap(source, target); + sm0.set_side_enable(true); + sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); + sm0.set_sideset_base_pin(&e); + sm0.set_sideset_count(2); + sm0.set_out_shift_dir(ShiftDirection::Left); + sm0.set_fifo_join(FifoJoin::TxOnly); + sm0.set_autopull(true); + sm0.set_pull_threshold(32); + + sm0.set_enable(true); + // init to 8 bit thrice + sm0.push_tx((50000 << 8) | 0x30); + sm0.push_tx((5000 << 8) | 0x30); + sm0.push_tx((200 << 8) | 0x30); + // init 4 bit + sm0.push_tx((200 << 8) | 0x20); + // set font and lines + sm0.push_tx((50 << 8) | 0x20); + sm0.push_tx(0b1100_0000); + + sm0.wait_irq(0).await; + sm0.set_enable(false); + + // takes command sequences ( , data...) + // many side sets are only there to free up a delay bit! + let prg = pio_proc::pio_asm!( + r#" + .origin 7 + .side_set 1 + + .wrap_target + pull side 0 + out x 1 side 0 ; !rs + out y 7 side 0 ; #data - 1 + + ; rs/rw to e: >= 60ns + ; e high time: >= 500ns + ; e low time: >= 500ns + ; read data valid after e falling: ~5ns + ; write data hold after e falling: ~10ns + + loop: + pull side 0 + jmp !x data side 0 + command: + set pins 0b00 side 0 + jmp shift side 0 + data: + set pins 0b01 side 0 + shift: + out pins 4 side 1 [9] + nop side 0 [9] + out pins 4 side 1 [9] + mov osr null side 0 [7] + out pindirs 4 side 0 + set pins 0b10 side 0 + busy: + nop side 1 [9] + jmp pin more side 0 [9] + mov osr ~osr side 1 [9] + nop side 0 [4] + out pindirs 4 side 0 + jmp y-- loop side 0 + .wrap + more: + nop side 1 [9] + jmp busy side 0 [9] + "# + ); + + let relocated = RelocatedProgram::new(&prg.program); + common.write_instr(relocated.origin() as usize, relocated.code()); + embassy_rp::pio_instr_util::exec_jmp(&mut sm0, relocated.origin()); + let pio::Wrap { source, target } = relocated.wrap(); + sm0.set_clkdiv(8 * 256); // ~64ns/insn + sm0.set_side_enable(false); + sm0.set_jmp_pin(db7pin); + sm0.set_wrap(source, target); + sm0.set_set_pins(&[&rs, &rw]); + sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); + sm0.set_sideset_base_pin(&e); + sm0.set_sideset_count(1); + sm0.set_out_shift_dir(ShiftDirection::Left); + sm0.set_fifo_join(FifoJoin::TxOnly); + + sm0.set_enable(true); + + // display on and cursor on and blinking, reset display + sm0.dma_push(dma.reborrow(), &[0x81u8, 0x0f, 1]).await; + + Self { + dma: dma.map_into(), + sm: sm0, + buf: [0x20; 40], + } + } + + pub async fn add_line(&mut self, s: &[u8]) { + // move cursor to 0:0, prepare 16 characters + self.buf[..3].copy_from_slice(&[0x80, 0x80, 15]); + // move line 2 up + self.buf.copy_within(22..38, 3); + // move cursor to 1:0, prepare 16 characters + self.buf[19..22].copy_from_slice(&[0x80, 0xc0, 15]); + // file line 2 with spaces + self.buf[22..38].fill(0x20); + // copy input line + let len = s.len().min(16); + self.buf[22..22 + len].copy_from_slice(&s[0..len]); + // set cursor to 1:15 + self.buf[38..].copy_from_slice(&[0x80, 0xcf]); + + self.sm.dma_push(self.dma.reborrow(), &self.buf).await; + } +} From 47ae9b79815af97e7e5d8b2f3c94af2326d7fd79 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 21:55:33 +0200 Subject: [PATCH 06/19] rp/pio: add funcsel values to PioInstance makes code setting funcsels easier to read and should make it easier to hook up more pio blocks, should they ever appear --- embassy-rp/src/pio.rs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index cab57f765..ecf7c9227 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -959,17 +959,7 @@ pub trait PioCommon: sealed::PioCommon + Sized { fn make_pio_pin(&self, pin: impl Pin) -> PioPin { unsafe { - pin.io().ctrl().write(|w| { - w.set_funcsel( - if Self::Pio::PIO_NO == 1 { - pac::io::vals::Gpio0ctrlFuncsel::PIO1_0 - } else { - // PIO == 0 - pac::io::vals::Gpio0ctrlFuncsel::PIO0_0 - } - .0, - ); - }); + pin.io().ctrl().write(|w| w.set_funcsel(Self::Pio::FUNCSEL.0)); } PioPin { pin_bank: pin.pin_bank(), @@ -1031,6 +1021,7 @@ mod sealed { pub trait PioInstance { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; + const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; } pub trait PioCommon { @@ -1059,12 +1050,14 @@ pub trait PioInstance: sealed::PioInstance + Unpin {} impl sealed::PioInstance for PioInstanceBase<0> { const PIO_NO: u8 = 0; const PIO: &'static pac::pio::Pio = &pac::PIO0; + const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::PIO0_0; } impl PioInstance for PioInstanceBase<0> {} impl sealed::PioInstance for PioInstanceBase<1> { const PIO_NO: u8 = 1; const PIO: &'static pac::pio::Pio = &pac::PIO1; + const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::PIO1_0; } impl PioInstance for PioInstanceBase<1> {} From 0d224a00e1023082f03610b74c37933506561c26 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 22:19:13 +0200 Subject: [PATCH 07/19] rp/pio: avoid sm(SM_NO) indexing accessing the current state machine is an extremely common operation that shouldn't have its specifics repeated myriad times. --- embassy-rp/src/pio.rs | 159 ++++++++++++------------------------------ 1 file changed, 43 insertions(+), 116 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index ecf7c9227..b20ebbc77 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -410,15 +410,12 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_clkdiv(&mut self, div_x_256: u32) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .clkdiv() - .write(|w| w.0 = div_x_256 << 8); + Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8); } } fn get_clkdiv(&self) -> u32 { - unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).clkdiv().read().0 >> 8 } + unsafe { Self::this_sm().clkdiv().read().0 >> 8 } } fn clkdiv_restart(&mut self) { @@ -431,52 +428,37 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_side_enable(&self, enable: bool) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .modify(|w| w.set_side_en(enable)); + Self::this_sm().execctrl().modify(|w| w.set_side_en(enable)); } } fn is_side_enabled(&self) -> bool { - unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().read().side_en() } + unsafe { Self::this_sm().execctrl().read().side_en() } } fn set_side_pindir(&mut self, pindir: bool) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .modify(|w| w.set_side_pindir(pindir)); + Self::this_sm().execctrl().modify(|w| w.set_side_pindir(pindir)); } } fn is_side_pindir(&self) -> bool { - unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .read() - .side_pindir() - } + unsafe { Self::this_sm().execctrl().read().side_pindir() } } fn set_jmp_pin(&mut self, pin: u8) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .execctrl() - .modify(|w| w.set_jmp_pin(pin)); + Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); } } fn get_jmp_pin(&mut self) -> u8 { - unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().read().jmp_pin() } + unsafe { Self::this_sm().execctrl().read().jmp_pin() } } fn set_wrap(&self, source: u8, target: u8) { unsafe { - Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().modify(|w| { + Self::this_sm().execctrl().modify(|w| { w.set_wrap_top(source); w.set_wrap_bottom(target) }); @@ -486,7 +468,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { /// Get wrapping addresses. Returns (source, target). fn get_wrap(&self) -> (u8, u8) { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).execctrl().read(); + let r = Self::this_sm().execctrl().read(); (r.wrap_top(), r.wrap_bottom()) } } @@ -498,7 +480,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { FifoJoin::TxOnly => (false, true), }; unsafe { - Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().modify(|w| { + Self::this_sm().shiftctrl().modify(|w| { w.set_fjoin_rx(rx); w.set_fjoin_tx(tx) }); @@ -506,7 +488,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn get_fifo_join(&self) -> FifoJoin { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().read(); + let r = Self::this_sm().shiftctrl().read(); // Ignores the invalid state when both bits are set if r.fjoin_rx() { FifoJoin::RxOnly @@ -521,7 +503,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn clear_fifos(&mut self) { // Toggle FJOIN_RX to flush FIFOs unsafe { - let shiftctrl = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl(); + let shiftctrl = Self::this_sm().shiftctrl(); shiftctrl.modify(|w| { w.set_fjoin_rx(!w.fjoin_rx()); }); @@ -533,51 +515,33 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_pull_threshold(&mut self, threshold: u8) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .modify(|w| w.set_pull_thresh(threshold)); + Self::this_sm().shiftctrl().modify(|w| w.set_pull_thresh(threshold)); } } fn get_pull_threshold(&self) -> u8 { - unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().read(); - r.pull_thresh() - } + unsafe { Self::this_sm().shiftctrl().read().pull_thresh() } } fn set_push_threshold(&mut self, threshold: u8) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .modify(|w| w.set_push_thresh(threshold)); + Self::this_sm().shiftctrl().modify(|w| w.set_push_thresh(threshold)); } } fn get_push_threshold(&self) -> u8 { - unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).shiftctrl().read(); - r.push_thresh() - } + unsafe { Self::this_sm().shiftctrl().read().push_thresh() } } fn set_out_shift_dir(&mut self, dir: ShiftDirection) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) + Self::this_sm() .shiftctrl() .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); } } fn get_out_shiftdir(&self) -> ShiftDirection { unsafe { - if Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read() - .out_shiftdir() - { + if Self::this_sm().shiftctrl().read().out_shiftdir() { ShiftDirection::Right } else { ShiftDirection::Left @@ -587,20 +551,14 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_in_shift_dir(&mut self, dir: ShiftDirection) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) + Self::this_sm() .shiftctrl() .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); } } fn get_in_shiftdir(&self) -> ShiftDirection { unsafe { - if Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read() - .in_shiftdir() - { + if Self::this_sm().shiftctrl().read().in_shiftdir() { ShiftDirection::Right } else { ShiftDirection::Left @@ -610,76 +568,46 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_autopull(&mut self, auto: bool) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .modify(|w| w.set_autopull(auto)); + Self::this_sm().shiftctrl().modify(|w| w.set_autopull(auto)); } } fn is_autopull(&self) -> bool { - unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read() - .autopull() - } + unsafe { Self::this_sm().shiftctrl().read().autopull() } } fn set_autopush(&mut self, auto: bool) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .modify(|w| w.set_autopush(auto)); + Self::this_sm().shiftctrl().modify(|w| w.set_autopush(auto)); } } fn is_autopush(&self) -> bool { - unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .shiftctrl() - .read() - .autopush() - } + unsafe { Self::this_sm().shiftctrl().read().autopush() } } fn get_addr(&self) -> u8 { - unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).addr().read(); - r.addr() - } + unsafe { Self::this_sm().addr().read().addr() } } fn set_sideset_count(&mut self, count: u8) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .modify(|w| w.set_sideset_count(count)); + Self::this_sm().pinctrl().modify(|w| w.set_sideset_count(count)); } } fn get_sideset_count(&self) -> u8 { - unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); - r.sideset_count() - } + unsafe { Self::this_sm().pinctrl().read().sideset_count() } } fn set_sideset_base_pin(&mut self, base_pin: &PioPin) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .modify(|w| w.set_sideset_base(base_pin.pin())); + Self::this_sm().pinctrl().modify(|w| w.set_sideset_base(base_pin.pin())); } } fn get_sideset_base(&self) -> u8 { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); + let r = Self::this_sm().pinctrl().read(); r.sideset_base() } } @@ -688,7 +616,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_set_range(&mut self, base: u8, count: u8) { assert!(base + count < 32); unsafe { - Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().modify(|w| { + Self::this_sm().pinctrl().modify(|w| { w.set_set_base(base); w.set_set_count(count) }); @@ -698,23 +626,20 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { /// Get the range of out pins affected by a set instruction. Returns (base, count). fn get_set_range(&self) -> (u8, u8) { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); + let r = Self::this_sm().pinctrl().read(); (r.set_base(), r.set_count()) } } fn set_in_base_pin(&mut self, base: &PioPin) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .pinctrl() - .modify(|w| w.set_in_base(base.pin())); + Self::this_sm().pinctrl().modify(|w| w.set_in_base(base.pin())); } } fn get_in_base(&self) -> u8 { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); + let r = Self::this_sm().pinctrl().read(); r.in_base() } } @@ -722,7 +647,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn set_out_range(&mut self, base: u8, count: u8) { assert!(base + count < 32); unsafe { - Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().modify(|w| { + Self::this_sm().pinctrl().modify(|w| { w.set_out_base(base); w.set_out_count(count) }); @@ -732,7 +657,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { /// Get the range of out pins affected by a set instruction. Returns (base, count). fn get_out_range(&self) -> (u8, u8) { unsafe { - let r = Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).pinctrl().read(); + let r = Self::this_sm().pinctrl().read(); (r.out_base(), r.out_count()) } } @@ -760,15 +685,12 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn get_current_instr() -> u32 { - unsafe { Self::Pio::PIO.sm(Self::Sm::SM_NO as usize).instr().read().0 } + unsafe { Self::this_sm().instr().read().0 } } fn exec_instr(&mut self, instr: u16) { unsafe { - Self::Pio::PIO - .sm(Self::Sm::SM_NO as usize) - .instr() - .write(|w| w.set_instr(instr)); + Self::this_sm().instr().write(|w| w.set_instr(instr)); } } @@ -1031,6 +953,11 @@ mod sealed { pub trait PioStateMachine { type Pio: super::PioInstance; type Sm: super::SmInstance; + + #[inline(always)] + fn this_sm() -> crate::pac::pio::StateMachine { + Self::Pio::PIO.sm(Self::Sm::SM_NO as usize) + } } pub trait SmInstance { From 6cec6fa09b3bcc01ada4d5d5decd02cc2a3a8e4f Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 25 Apr 2023 23:17:57 +0200 Subject: [PATCH 08/19] rp/pio: don't use modify on shared registers pio control registers are notionally shared between state machines as well. state machine operations that change these registers must use atomic accesses (or critical sections, which would be overkill). notably PioPin::set_input_sync_bypass was even wrong, enabling the bypass on a pin requires the corresponding bit to be set (not cleared). the PioCommon function got it right. --- embassy-rp/src/pio.rs | 34 +++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index b20ebbc77..e9a67fd48 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -298,9 +298,11 @@ impl PioPin { pub fn set_input_sync_bypass<'a>(&mut self, bypass: bool) { let mask = 1 << self.pin(); unsafe { - PIO::PIO - .input_sync_bypass() - .modify(|w| *w = if bypass { *w & !mask } else { *w | mask }); + if bypass { + PIO::PIO.input_sync_bypass().write_set(|w| *w = mask); + } else { + PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask); + } } } @@ -336,18 +338,19 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn restart(&mut self) { + let mask = 1u8 << Self::Sm::SM_NO; unsafe { - Self::Pio::PIO - .ctrl() - .modify(|w| w.set_sm_restart(1u8 << Self::Sm::SM_NO)); + Self::Pio::PIO.ctrl().write_set(|w| w.set_sm_restart(mask)); } } fn set_enable(&mut self, enable: bool) { let mask = 1u8 << Self::Sm::SM_NO; unsafe { - Self::Pio::PIO - .ctrl() - .modify(|w| w.set_sm_enable((w.sm_enable() & !mask) | (if enable { mask } else { 0 }))); + if enable { + Self::Pio::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); + } else { + Self::Pio::PIO.ctrl().write_clear(|w| w.set_sm_enable(mask)); + } } } @@ -419,10 +422,9 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn clkdiv_restart(&mut self) { + let mask = 1u8 << Self::Sm::SM_NO; unsafe { - Self::Pio::PIO - .ctrl() - .modify(|w| w.set_clkdiv_restart(1u8 << Self::Sm::SM_NO)); + Self::Pio::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask)); } } @@ -869,9 +871,11 @@ pub trait PioCommon: sealed::PioCommon + Sized { fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { unsafe { - Self::Pio::PIO - .input_sync_bypass() - .modify(|w| *w = (*w & !mask) | (bypass & mask)); + // this can interfere with per-pin bypass functions. splitting the + // modification is going to be fine since nothing that relies on + // it can reasonably run before we finish. + Self::Pio::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass); + Self::Pio::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass); } } From 849011b8261194629830b4b85d062394e8eb3c58 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 08:39:05 +0200 Subject: [PATCH 09/19] rp/gpio: set up gpio interrupts only once doing this setup work repeatedly, on every wait, is unnecessary. with nothing ever disabling the interrupt it is sufficient to enable it once during device init and never touch it again. --- embassy-rp/src/gpio.rs | 13 +++--- embassy-rp/src/lib.rs | 1 + embassy-rp/src/multicore.rs | 5 ++- tests/rp/src/bin/gpio_multicore.rs | 63 ++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+), 7 deletions(-) create mode 100644 tests/rp/src/bin/gpio_multicore.rs diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 66b9af04b..da8efba91 100644 --- a/embassy-rp/src/gpio.rs +++ b/embassy-rp/src/gpio.rs @@ -136,6 +136,13 @@ pub enum InterruptTrigger { AnyEdge, } +pub(crate) unsafe fn init() { + let irq = interrupt::IO_IRQ_BANK0::steal(); + irq.disable(); + irq.set_priority(interrupt::Priority::P3); + irq.enable(); +} + #[interrupt] unsafe fn IO_IRQ_BANK0() { let cpu = SIO.cpuid().read() as usize; @@ -179,10 +186,6 @@ impl<'d, T: Pin> InputFuture<'d, T> { pub fn new(pin: impl Peripheral

+ 'd, level: InterruptTrigger) -> Self { into_ref!(pin); unsafe { - let irq = interrupt::IO_IRQ_BANK0::steal(); - irq.disable(); - irq.set_priority(interrupt::Priority::P3); - let pin_group = (pin.pin() % 8) as usize; // first, clear the INTR register bits. without this INTR will still // contain reports of previous edges, causing the IRQ to fire early @@ -221,8 +224,6 @@ impl<'d, T: Pin> InputFuture<'d, T> { w.set_edge_low(pin_group, true); } }); - - irq.enable(); } Self { pin, level } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d69d12a30..cba7559df 100644 --- a/embassy-rp/src/lib.rs +++ b/embassy-rp/src/lib.rs @@ -157,6 +157,7 @@ pub fn init(_config: config::Config) -> Peripherals { timer::init(); dma::init(); pio::init(); + gpio::init(); } peripherals diff --git a/embassy-rp/src/multicore.rs b/embassy-rp/src/multicore.rs index e51fc218a..c84fea5c8 100644 --- a/embassy-rp/src/multicore.rs +++ b/embassy-rp/src/multicore.rs @@ -33,7 +33,7 @@ use core::sync::atomic::{compiler_fence, AtomicBool, Ordering}; use crate::interrupt::{Interrupt, InterruptExt}; use crate::peripherals::CORE1; -use crate::{interrupt, pac}; +use crate::{gpio, interrupt, pac}; const PAUSE_TOKEN: u32 = 0xDEADBEEF; const RESUME_TOKEN: u32 = !0xDEADBEEF; @@ -68,6 +68,9 @@ fn install_stack_guard(stack_bottom: *mut usize) { #[inline(always)] fn core1_setup(stack_bottom: *mut usize) { install_stack_guard(stack_bottom); + unsafe { + gpio::init(); + } } /// Data type for a properly aligned stack of N bytes diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs new file mode 100644 index 000000000..6c13ccaae --- /dev/null +++ b/tests/rp/src/bin/gpio_multicore.rs @@ -0,0 +1,63 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy_executor::Executor; +use embassy_executor::_export::StaticCell; +use embassy_rp::gpio::{Input, Level, Output, Pull}; +use embassy_rp::multicore::{spawn_core1, Stack}; +use embassy_rp::peripherals::{PIN_0, PIN_1}; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; +use {defmt_rtt as _, panic_probe as _}; + +static mut CORE1_STACK: Stack<1024> = Stack::new(); +static EXECUTOR0: StaticCell = StaticCell::new(); +static EXECUTOR1: StaticCell = StaticCell::new(); +static CHANNEL0: Channel = Channel::new(); +static CHANNEL1: Channel = Channel::new(); + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_rp::init(Default::default()); + spawn_core1(p.CORE1, unsafe { &mut CORE1_STACK }, move || { + let executor1 = EXECUTOR1.init(Executor::new()); + executor1.run(|spawner| unwrap!(spawner.spawn(core1_task(p.PIN_1)))); + }); + let executor0 = EXECUTOR0.init(Executor::new()); + executor0.run(|spawner| unwrap!(spawner.spawn(core0_task(p.PIN_0)))); +} + +#[embassy_executor::task] +async fn core0_task(p: PIN_0) { + info!("CORE0 is running"); + + let mut pin = Output::new(p, Level::Low); + + CHANNEL0.send(()).await; + CHANNEL1.recv().await; + + pin.set_high(); + + CHANNEL1.recv().await; + + info!("Test OK"); + cortex_m::asm::bkpt(); +} + +#[embassy_executor::task] +async fn core1_task(p: PIN_1) { + info!("CORE1 is running"); + + CHANNEL0.recv().await; + + let mut pin = Input::new(p, Pull::Down); + let wait = pin.wait_for_rising_edge(); + + CHANNEL1.send(()).await; + + wait.await; + + CHANNEL1.send(()).await; +} From 3229b5e809688d99a592bbfd1f803e1fb9d62050 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 26 Apr 2023 00:23:18 +0200 Subject: [PATCH 10/19] rp/pio: remove PioPeripheral merge into PioInstance instead. PioPeripheral was mostly a wrapper around PioInstance anyway, and the way the wrapping was done required PioInstanceBase types where PIO{N} could've been used instead. --- embassy-rp/src/pio.rs | 64 ++++++++++-------------------- examples/rp/src/bin/pio_async.rs | 16 ++++---- examples/rp/src/bin/pio_dma.rs | 2 +- examples/rp/src/bin/pio_hd44780.rs | 5 +-- examples/rp/src/bin/ws2812-pio.rs | 4 +- 5 files changed, 33 insertions(+), 58 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index e9a67fd48..459b7806b 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -904,19 +904,19 @@ impl sealed::SmInstance for SmInstanceBase { } impl SmInstance for SmInstanceBase {} -pub trait PioPeripheral: sealed::PioPeripheral + Sized { +pub trait PioInstance: sealed::PioInstance + Sized + Unpin { fn pio(&self) -> u8 { - Self::Pio::PIO_NO + Self::PIO_NO } fn split( self, ) -> ( - PioCommonInstance, - PioStateMachineInstance>, - PioStateMachineInstance>, - PioStateMachineInstance>, - PioStateMachineInstance>, + PioCommonInstance, + PioStateMachineInstance>, + PioStateMachineInstance>, + PioStateMachineInstance>, + PioStateMachineInstance>, ) { ( PioCommonInstance { @@ -944,12 +944,6 @@ pub trait PioPeripheral: sealed::PioPeripheral + Sized { } mod sealed { - pub trait PioInstance { - const PIO_NO: u8; - const PIO: &'static crate::pac::pio::Pio; - const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; - } - pub trait PioCommon { type Pio: super::PioInstance; } @@ -968,46 +962,28 @@ mod sealed { const SM_NO: u8; } - pub trait PioPeripheral { - type Pio: super::PioInstance; + pub trait PioInstance { + const PIO_NO: u8; + const PIO: &'static crate::pac::pio::Pio; + const FUNCSEL: crate::pac::io::vals::Gpio0ctrlFuncsel; } } -// Identifies a specific PIO device -pub struct PioInstanceBase {} - -pub trait PioInstance: sealed::PioInstance + Unpin {} - -impl sealed::PioInstance for PioInstanceBase<0> { - const PIO_NO: u8 = 0; - const PIO: &'static pac::pio::Pio = &pac::PIO0; - const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::PIO0_0; -} -impl PioInstance for PioInstanceBase<0> {} - -impl sealed::PioInstance for PioInstanceBase<1> { - const PIO_NO: u8 = 1; - const PIO: &'static pac::pio::Pio = &pac::PIO1; - const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::PIO1_0; -} -impl PioInstance for PioInstanceBase<1> {} - -pub type Pio0 = PioInstanceBase<0>; -pub type Pio1 = PioInstanceBase<1>; - pub type Sm0 = SmInstanceBase<0>; pub type Sm1 = SmInstanceBase<1>; pub type Sm2 = SmInstanceBase<2>; pub type Sm3 = SmInstanceBase<3>; -macro_rules! impl_pio_sm { - ($name:ident, $pio:expr) => { - impl sealed::PioPeripheral for peripherals::$name { - type Pio = PioInstanceBase<$pio>; +macro_rules! impl_pio { + ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { + impl sealed::PioInstance for peripherals::$name { + const PIO_NO: u8 = $pio; + const PIO: &'static pac::pio::Pio = &pac::$pac; + const FUNCSEL: pac::io::vals::Gpio0ctrlFuncsel = pac::io::vals::Gpio0ctrlFuncsel::$funcsel; } - impl PioPeripheral for peripherals::$name {} + impl PioInstance for peripherals::$name {} }; } -impl_pio_sm!(PIO0, 0); -impl_pio_sm!(PIO1, 1); +impl_pio!(PIO0, 0, PIO0, PIO0_0); +impl_pio!(PIO1, 1, PIO1, PIO1_0); diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 1b075b8fd..16a09327f 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -4,15 +4,15 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; +use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ - Pio0, PioCommon, PioCommonInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, - Sm1, Sm2, + PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance, pin: AnyPin) { +fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance, pin: AnyPin) { // Setup sm0 // Send data serially to pin @@ -40,7 +40,7 @@ fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm0(mut sm: PioStateMachineInstance) { +async fn pio_task_sm0(mut sm: PioStateMachineInstance) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -51,7 +51,7 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance) { } } -fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full @@ -70,7 +70,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm1(mut sm: PioStateMachineInstance) { +async fn pio_task_sm1(mut sm: PioStateMachineInstance) { sm.set_enable(true); loop { let rx = sm.wait_pull().await; @@ -78,7 +78,7 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance) { } } -fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -102,7 +102,7 @@ fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm2(mut sm: PioStateMachineInstance) { +async fn pio_task_sm2(mut sm: PioStateMachineInstance) { sm.set_enable(true); loop { sm.wait_irq(3).await; diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 7d4919f75..ccbc70fe2 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{PioCommon, PioPeripheral, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{PioCommon, PioInstance, PioStateMachine, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 6bcd0652b..1b24897b0 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -9,8 +9,7 @@ use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::gpio::Pin; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ - FifoJoin, PioCommon, PioInstanceBase, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, - SmInstanceBase, + FifoJoin, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase, }; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; @@ -68,7 +67,7 @@ async fn main(_spawner: Spawner) { pub struct HD44780<'l> { dma: PeripheralRef<'l, AnyChannel>, - sm: PioStateMachineInstance, SmInstanceBase<0>>, + sm: PioStateMachineInstance>, buf: [u8; 40], } diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 041e8ae11..592caf244 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -6,8 +6,8 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{self, Pin}; use embassy_rp::pio::{ - FifoJoin, PioCommon, PioCommonInstance, PioInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, - ShiftDirection, SmInstance, + FifoJoin, PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, + SmInstance, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; From ac111f40d894dec12106638e47317d13728c52a1 Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 13:46:51 +0200 Subject: [PATCH 11/19] rp/pio: fix PioPin::set_pull, set_schmitt comment --- embassy-rp/src/pio.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 459b7806b..27a6068f9 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -277,15 +277,14 @@ impl PioPin { #[inline] pub fn set_pull(&mut self, pull: Pull) { unsafe { - self.pad_ctrl().modify(|w| match pull { - Pull::Up => w.set_pue(true), - Pull::Down => w.set_pde(true), - Pull::None => {} + self.pad_ctrl().modify(|w| { + w.set_pue(pull == Pull::Up); + w.set_pde(pull == Pull::Down); }); } } - /// Set the pin's pull. + /// Set the pin's schmitt trigger. #[inline] pub fn set_schmitt(&mut self, enable: bool) { unsafe { From 8839f3f62ab85a1abd066fcbfa15693965e6fae8 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 26 Apr 2023 19:43:57 +0200 Subject: [PATCH 12/19] rp/pio: PioInstance::split -> Pio::new not requiring a PioInstance for splitting lets us split from a PeripheralRef or borrowed PIO as well, mirroring every other peripheral in embassy_rp. pio pins still have to be constructed from owned pin instances for now. --- embassy-rp/src/pio.rs | 92 +++++++++++++++--------------- examples/rp/src/bin/pio_async.rs | 22 ++++--- examples/rp/src/bin/pio_dma.rs | 10 +++- examples/rp/src/bin/pio_hd44780.rs | 10 ++-- examples/rp/src/bin/ws2812-pio.rs | 18 +++--- 5 files changed, 85 insertions(+), 67 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 27a6068f9..97e97b749 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -5,7 +5,7 @@ use core::sync::atomic::{compiler_fence, Ordering}; use core::task::{Context, Poll}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; -use embassy_hal_common::PeripheralRef; +use embassy_hal_common::{Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; use crate::dma::{Channel, Transfer, Word}; @@ -316,16 +316,16 @@ impl SealedPin for PioPin { } } -pub struct PioStateMachineInstance { - pio: PhantomData, +pub struct PioStateMachineInstance<'d, PIO: PioInstance, SM: SmInstance> { + pio: PhantomData<&'d PIO>, sm: PhantomData, } -impl sealed::PioStateMachine for PioStateMachineInstance { +impl<'d, PIO: PioInstance, SM: SmInstance> sealed::PioStateMachine for PioStateMachineInstance<'d, PIO, SM> { type Pio = PIO; type Sm = SM; } -impl PioStateMachine for PioStateMachineInstance {} +impl<'d, PIO: PioInstance, SM: SmInstance> PioStateMachine for PioStateMachineInstance<'d, PIO, SM> {} pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn pio_no(&self) -> u8 { @@ -792,21 +792,21 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } -pub struct PioCommonInstance { +pub struct PioCommonInstance<'d, PIO: PioInstance> { instructions_used: u32, - pio: PhantomData, + pio: PhantomData<&'d PIO>, } -pub struct PioInstanceMemory { +pub struct PioInstanceMemory<'d, PIO: PioInstance> { used_mask: u32, - pio: PhantomData, + pio: PhantomData<&'d PIO>, } -impl sealed::PioCommon for PioCommonInstance { +impl<'d, PIO: PioInstance> sealed::PioCommon for PioCommonInstance<'d, PIO> { type Pio = PIO; } -impl PioCommon for PioCommonInstance { - fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory +impl<'d, PIO: PioInstance> PioCommon for PioCommonInstance<'d, PIO> { + fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, Self::Pio> where I: Iterator, { @@ -903,43 +903,45 @@ impl sealed::SmInstance for SmInstanceBase { } impl SmInstance for SmInstanceBase {} +pub struct Pio<'d, PIO: PioInstance> { + pub common: PioCommonInstance<'d, PIO>, + pub sm0: PioStateMachineInstance<'d, PIO, SmInstanceBase<0>>, + pub sm1: PioStateMachineInstance<'d, PIO, SmInstanceBase<1>>, + pub sm2: PioStateMachineInstance<'d, PIO, SmInstanceBase<2>>, + pub sm3: PioStateMachineInstance<'d, PIO, SmInstanceBase<3>>, +} + +impl<'d, PIO: PioInstance> Pio<'d, PIO> { + pub fn new(_pio: impl Peripheral

+ 'd) -> Self { + Self { + common: PioCommonInstance { + instructions_used: 0, + pio: PhantomData, + }, + sm0: PioStateMachineInstance { + sm: PhantomData, + pio: PhantomData, + }, + sm1: PioStateMachineInstance { + sm: PhantomData, + pio: PhantomData, + }, + sm2: PioStateMachineInstance { + sm: PhantomData, + pio: PhantomData, + }, + sm3: PioStateMachineInstance { + sm: PhantomData, + pio: PhantomData, + }, + } + } +} + pub trait PioInstance: sealed::PioInstance + Sized + Unpin { fn pio(&self) -> u8 { Self::PIO_NO } - - fn split( - self, - ) -> ( - PioCommonInstance, - PioStateMachineInstance>, - PioStateMachineInstance>, - PioStateMachineInstance>, - PioStateMachineInstance>, - ) { - ( - PioCommonInstance { - instructions_used: 0, - pio: PhantomData::default(), - }, - PioStateMachineInstance { - sm: PhantomData::default(), - pio: PhantomData::default(), - }, - PioStateMachineInstance { - sm: PhantomData::default(), - pio: PhantomData::default(), - }, - PioStateMachineInstance { - sm: PhantomData::default(), - pio: PhantomData::default(), - }, - PioStateMachineInstance { - sm: PhantomData::default(), - pio: PhantomData::default(), - }, - ) - } } mod sealed { diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 16a09327f..69a22f238 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -6,7 +6,7 @@ use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ - PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2, + Pio, PioCommon, PioCommonInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; @@ -40,7 +40,7 @@ fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm0(mut sm: PioStateMachineInstance) { +async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, Sm0>) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -70,7 +70,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm1(mut sm: PioStateMachineInstance) { +async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, Sm1>) { sm.set_enable(true); loop { let rx = sm.wait_pull().await; @@ -102,7 +102,7 @@ fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachin } #[embassy_executor::task] -async fn pio_task_sm2(mut sm: PioStateMachineInstance) { +async fn pio_task_sm2(mut sm: PioStateMachineInstance<'static, PIO0, Sm2>) { sm.set_enable(true); loop { sm.wait_irq(3).await; @@ -115,11 +115,17 @@ async fn main(spawner: Spawner) { let p = embassy_rp::init(Default::default()); let pio = p.PIO0; - let (mut pio0, mut sm0, mut sm1, mut sm2, ..) = pio.split(); + let Pio { + mut common, + mut sm0, + mut sm1, + mut sm2, + .. + } = Pio::new(pio); - setup_pio_task_sm0(&mut pio0, &mut sm0, p.PIN_0.degrade()); - setup_pio_task_sm1(&mut pio0, &mut sm1); - setup_pio_task_sm2(&mut pio0, &mut sm2); + setup_pio_task_sm0(&mut common, &mut sm0, p.PIN_0.degrade()); + setup_pio_task_sm1(&mut common, &mut sm1); + setup_pio_task_sm2(&mut common, &mut sm2); spawner.spawn(pio_task_sm0(sm0)).unwrap(); spawner.spawn(pio_task_sm1(sm1)).unwrap(); spawner.spawn(pio_task_sm2(sm2)).unwrap(); diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index ccbc70fe2..33c320b8f 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{PioCommon, PioInstance, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; @@ -19,7 +19,11 @@ fn swap_nibbles(v: u32) -> u32 { async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let pio = p.PIO0; - let (mut pio0, mut sm, ..) = pio.split(); + let Pio { + mut common, + sm0: mut sm, + .. + } = Pio::new(pio); let prg = pio_proc::pio_asm!( ".origin 0", @@ -34,7 +38,7 @@ async fn main(_spawner: Spawner) { ); let relocated = RelocatedProgram::new(&prg.program); - pio0.write_instr(relocated.origin() as usize, relocated.code()); + common.write_instr(relocated.origin() as usize, relocated.code()); pio_instr_util::exec_jmp(&mut sm, relocated.origin()); sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); let pio::Wrap { source, target } = relocated.wrap(); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 1b24897b0..994d4600a 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -9,7 +9,7 @@ use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::gpio::Pin; use embassy_rp::peripherals::PIO0; use embassy_rp::pio::{ - FifoJoin, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase, + FifoJoin, Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase, }; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; @@ -67,14 +67,14 @@ async fn main(_spawner: Spawner) { pub struct HD44780<'l> { dma: PeripheralRef<'l, AnyChannel>, - sm: PioStateMachineInstance>, + sm: PioStateMachineInstance<'l, PIO0, SmInstanceBase<0>>, buf: [u8; 40], } impl<'l> HD44780<'l> { pub async fn new( - pio: PIO0, + pio: impl Peripheral

+ 'l, dma: impl Peripheral

+ 'l, rs: impl Pin, rw: impl Pin, @@ -87,7 +87,9 @@ impl<'l> HD44780<'l> { into_ref!(dma); let db7pin = db7.pin(); - let (mut common, mut sm0, ..) = pio.split(); + let Pio { + mut common, mut sm0, .. + } = Pio::new(pio); // takes command words ( <0:4>) let prg = pio_proc::pio_asm!( diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 592caf244..42c731bd7 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -6,7 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{self, Pin}; use embassy_rp::pio::{ - FifoJoin, PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, + FifoJoin, Pio, PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstance, }; use embassy_rp::pio_instr_util; @@ -14,12 +14,16 @@ use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; -pub struct Ws2812 { - sm: PioStateMachineInstance, +pub struct Ws2812<'d, P: PioInstance, S: SmInstance> { + sm: PioStateMachineInstance<'d, P, S>, } -impl Ws2812 { - pub fn new(mut pio: PioCommonInstance

, mut sm: PioStateMachineInstance, pin: gpio::AnyPin) -> Self { +impl<'d, P: PioInstance, S: SmInstance> Ws2812<'d, P, S> { + pub fn new( + mut pio: PioCommonInstance<'d, P>, + mut sm: PioStateMachineInstance<'d, P, S>, + pin: gpio::AnyPin, + ) -> Self { // Setup sm0 // prepare the PIO program @@ -116,7 +120,7 @@ async fn main(_spawner: Spawner) { info!("Start"); let p = embassy_rp::init(Default::default()); - let (pio0, sm0, _sm1, _sm2, _sm3) = p.PIO0.split(); + let Pio { common, sm0, .. } = Pio::new(p.PIO0); // This is the number of leds in the string. Helpfully, the sparkfun thing plus and adafruit // feather boards for the 2040 both have one built in. @@ -125,7 +129,7 @@ async fn main(_spawner: Spawner) { // For the thing plus, use pin 8 // For the feather, use pin 16 - let mut ws2812 = Ws2812::new(pio0, sm0, p.PIN_8.degrade()); + let mut ws2812 = Ws2812::new(common, sm0, p.PIN_8.degrade()); // Loop forever making RGB values and pushing them out to the WS2812. loop { From a167c77d3928e1304ccccec6ddf7572d1e3c4cd9 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 26 Apr 2023 20:27:31 +0200 Subject: [PATCH 13/19] rp/pio: make PioCommon a struct the PioCommon trait does not serve much of a purpose; there can be only two implementations and they only differ in a few associated constants. --- embassy-rp/src/pio.rs | 59 +++++++++++------------------- examples/rp/src/bin/pio_async.rs | 10 ++--- examples/rp/src/bin/pio_dma.rs | 2 +- examples/rp/src/bin/pio_hd44780.rs | 4 +- examples/rp/src/bin/ws2812-pio.rs | 9 +---- 5 files changed, 30 insertions(+), 54 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 97e97b749..6827e5119 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -792,7 +792,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } -pub struct PioCommonInstance<'d, PIO: PioInstance> { +pub struct PioCommon<'d, PIO: PioInstance> { instructions_used: u32, pio: PhantomData<&'d PIO>, } @@ -802,11 +802,8 @@ pub struct PioInstanceMemory<'d, PIO: PioInstance> { pio: PhantomData<&'d PIO>, } -impl<'d, PIO: PioInstance> sealed::PioCommon for PioCommonInstance<'d, PIO> { - type Pio = PIO; -} -impl<'d, PIO: PioInstance> PioCommon for PioCommonInstance<'d, PIO> { - fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, Self::Pio> +impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { + pub fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, PIO> where I: Iterator, { @@ -833,58 +830,50 @@ impl<'d, PIO: PioInstance> PioCommon for PioCommonInstance<'d, PIO> { } } - fn free_instr(&mut self, instrs: PioInstanceMemory) { + // TODO make instruction memory that is currently in use unfreeable + pub fn free_instr(&mut self, instrs: PioInstanceMemory) { self.instructions_used &= !instrs.used_mask; } -} -pub trait PioCommon: sealed::PioCommon + Sized { - fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory - where - I: Iterator; - - // TODO make instruction memory that is currently in use unfreeable - fn free_instr(&mut self, instrs: PioInstanceMemory); - - fn is_irq_set(&self, irq_no: u8) -> bool { + pub fn is_irq_set(&self, irq_no: u8) -> bool { assert!(irq_no < 8); unsafe { - let irq_flags = Self::Pio::PIO.irq(); + let irq_flags = PIO::PIO.irq(); irq_flags.read().0 & (1 << irq_no) != 0 } } - fn clear_irq(&mut self, irq_no: usize) { + pub fn clear_irq(&mut self, irq_no: usize) { assert!(irq_no < 8); - unsafe { Self::Pio::PIO.irq().write(|w| w.set_irq(1 << irq_no)) } + unsafe { PIO::PIO.irq().write(|w| w.set_irq(1 << irq_no)) } } - fn clear_irqs(&mut self, mask: u8) { - unsafe { Self::Pio::PIO.irq().write(|w| w.set_irq(mask)) } + pub fn clear_irqs(&mut self, mask: u8) { + unsafe { PIO::PIO.irq().write(|w| w.set_irq(mask)) } } - fn force_irq(&mut self, irq_no: usize) { + pub fn force_irq(&mut self, irq_no: usize) { assert!(irq_no < 8); - unsafe { Self::Pio::PIO.irq_force().write(|w| w.set_irq_force(1 << irq_no)) } + unsafe { PIO::PIO.irq_force().write(|w| w.set_irq_force(1 << irq_no)) } } - fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { + pub fn set_input_sync_bypass<'a>(&'a mut self, bypass: u32, mask: u32) { unsafe { // this can interfere with per-pin bypass functions. splitting the // modification is going to be fine since nothing that relies on // it can reasonably run before we finish. - Self::Pio::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass); - Self::Pio::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass); + PIO::PIO.input_sync_bypass().write_set(|w| *w = mask & bypass); + PIO::PIO.input_sync_bypass().write_clear(|w| *w = mask & !bypass); } } - fn get_input_sync_bypass(&self) -> u32 { - unsafe { Self::Pio::PIO.input_sync_bypass().read() } + pub fn get_input_sync_bypass(&self) -> u32 { + unsafe { PIO::PIO.input_sync_bypass().read() } } - fn make_pio_pin(&self, pin: impl Pin) -> PioPin { + pub fn make_pio_pin(&self, pin: impl Pin) -> PioPin { unsafe { - pin.io().ctrl().write(|w| w.set_funcsel(Self::Pio::FUNCSEL.0)); + pin.io().ctrl().write(|w| w.set_funcsel(PIO::FUNCSEL.0)); } PioPin { pin_bank: pin.pin_bank(), @@ -904,7 +893,7 @@ impl sealed::SmInstance for SmInstanceBase { impl SmInstance for SmInstanceBase {} pub struct Pio<'d, PIO: PioInstance> { - pub common: PioCommonInstance<'d, PIO>, + pub common: PioCommon<'d, PIO>, pub sm0: PioStateMachineInstance<'d, PIO, SmInstanceBase<0>>, pub sm1: PioStateMachineInstance<'d, PIO, SmInstanceBase<1>>, pub sm2: PioStateMachineInstance<'d, PIO, SmInstanceBase<2>>, @@ -914,7 +903,7 @@ pub struct Pio<'d, PIO: PioInstance> { impl<'d, PIO: PioInstance> Pio<'d, PIO> { pub fn new(_pio: impl Peripheral

+ 'd) -> Self { Self { - common: PioCommonInstance { + common: PioCommon { instructions_used: 0, pio: PhantomData, }, @@ -945,10 +934,6 @@ pub trait PioInstance: sealed::PioInstance + Sized + Unpin { } mod sealed { - pub trait PioCommon { - type Pio: super::PioInstance; - } - pub trait PioStateMachine { type Pio: super::PioInstance; type Sm: super::SmInstance; diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 69a22f238..50b001b69 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -5,14 +5,12 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{ - Pio, PioCommon, PioCommonInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2, -}; +use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance, pin: AnyPin) { +fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstance, pin: AnyPin) { // Setup sm0 // Send data serially to pin @@ -51,7 +49,7 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, Sm0>) { } } -fn setup_pio_task_sm1(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full @@ -78,7 +76,7 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, Sm1>) { } } -fn setup_pio_task_sm2(pio: &mut PioCommonInstance, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { // Setup sm2 // Repeatedly trigger IRQ 3 diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 33c320b8f..0f1f6df12 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -4,7 +4,7 @@ use defmt::info; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, ShiftDirection}; +use embassy_rp::pio::{Pio, PioStateMachine, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{pio_instr_util, Peripheral}; use {defmt_rtt as _, panic_probe as _}; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 994d4600a..20c6a0565 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -8,9 +8,7 @@ use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::gpio::Pin; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{ - FifoJoin, Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase, -}; +use embassy_rp::pio::{FifoJoin, Pio, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase}; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 42c731bd7..a2121df42 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -6,8 +6,7 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{self, Pin}; use embassy_rp::pio::{ - FifoJoin, Pio, PioCommon, PioCommonInstance, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, - SmInstance, + FifoJoin, Pio, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstance, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; @@ -19,11 +18,7 @@ pub struct Ws2812<'d, P: PioInstance, S: SmInstance> { } impl<'d, P: PioInstance, S: SmInstance> Ws2812<'d, P, S> { - pub fn new( - mut pio: PioCommonInstance<'d, P>, - mut sm: PioStateMachineInstance<'d, P, S>, - pin: gpio::AnyPin, - ) -> Self { + pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachineInstance<'d, P, S>, pin: gpio::AnyPin) -> Self { // Setup sm0 // prepare the PIO program From 7a36072a15b2164a903ae3f36ee251eaf311216d Mon Sep 17 00:00:00 2001 From: pennae Date: Tue, 2 May 2023 17:55:40 +0200 Subject: [PATCH 14/19] rp/pio: drop SmInstance{,Base} these are just overly convoluted ways of writing down numbers. --- embassy-rp/src/pio.rs | 130 +++++++++++------------------ examples/rp/src/bin/pio_async.rs | 14 ++-- examples/rp/src/bin/pio_hd44780.rs | 4 +- examples/rp/src/bin/ws2812-pio.rs | 6 +- 4 files changed, 60 insertions(+), 94 deletions(-) diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 6827e5119..5433d3f21 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -12,7 +12,7 @@ use crate::dma::{Channel, Transfer, Word}; use crate::gpio::sealed::Pin as SealedPin; use crate::gpio::{Drive, Pin, Pull, SlewRate}; use crate::pac::dma::vals::TreqSel; -use crate::pio::sealed::{PioInstance as _, SmInstance as _}; +use crate::pio::sealed::PioInstance as _; use crate::{interrupt, pac, peripherals, RegExt}; struct Wakers([AtomicWaker; 12]); @@ -119,10 +119,10 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Future for FifoOutFuture if self.get_mut().sm.try_push_tx(value) { Poll::Ready(()) } else { - WAKERS[PIO::PIO_NO as usize].fifo_out()[SM::Sm::SM_NO as usize].register(cx.waker()); + WAKERS[PIO::PIO_NO as usize].fifo_out()[SM::SM as usize].register(cx.waker()); unsafe { PIO::PIO.irqs(0).inte().write_set(|m| { - m.0 = TXNFULL_MASK << SM::Sm::SM_NO; + m.0 = TXNFULL_MASK << SM::SM; }); } // debug!("Pending"); @@ -135,7 +135,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine + Unpin> Drop for FifoOutFuture<' fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { - m.0 = TXNFULL_MASK << SM::Sm::SM_NO; + m.0 = TXNFULL_MASK << SM::SM; }); } } @@ -164,10 +164,10 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Future for FifoInFuture<'d, PIO, if let Some(v) = self.sm.try_pull_rx() { Poll::Ready(v) } else { - WAKERS[PIO::PIO_NO as usize].fifo_in()[SM::Sm::SM_NO as usize].register(cx.waker()); + WAKERS[PIO::PIO_NO as usize].fifo_in()[SM::SM].register(cx.waker()); unsafe { PIO::PIO.irqs(0).inte().write_set(|m| { - m.0 = RXNEMPTY_MASK << SM::Sm::SM_NO; + m.0 = RXNEMPTY_MASK << SM::SM; }); } //debug!("Pending"); @@ -180,7 +180,7 @@ impl<'d, PIO: PioInstance, SM: PioStateMachine> Drop for FifoInFuture<'d, PIO, S fn drop(&mut self) { unsafe { PIO::PIO.irqs(0).inte().write_clear(|m| { - m.0 = RXNEMPTY_MASK << SM::Sm::SM_NO; + m.0 = RXNEMPTY_MASK << SM::SM; }); } } @@ -316,16 +316,15 @@ impl SealedPin for PioPin { } } -pub struct PioStateMachineInstance<'d, PIO: PioInstance, SM: SmInstance> { +pub struct PioStateMachineInstance<'d, PIO: PioInstance, const SM: usize> { pio: PhantomData<&'d PIO>, - sm: PhantomData, } -impl<'d, PIO: PioInstance, SM: SmInstance> sealed::PioStateMachine for PioStateMachineInstance<'d, PIO, SM> { +impl<'d, PIO: PioInstance, const SM: usize> sealed::PioStateMachine for PioStateMachineInstance<'d, PIO, SM> { type Pio = PIO; - type Sm = SM; + const SM: usize = SM; } -impl<'d, PIO: PioInstance, SM: SmInstance> PioStateMachine for PioStateMachineInstance<'d, PIO, SM> {} +impl<'d, PIO: PioInstance, const SM: usize> PioStateMachine for PioStateMachineInstance<'d, PIO, SM> {} pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn pio_no(&self) -> u8 { @@ -333,17 +332,17 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn sm_no(&self) -> u8 { - Self::Sm::SM_NO + Self::SM as u8 } fn restart(&mut self) { - let mask = 1u8 << Self::Sm::SM_NO; + let mask = 1u8 << Self::SM; unsafe { Self::Pio::PIO.ctrl().write_set(|w| w.set_sm_restart(mask)); } } fn set_enable(&mut self, enable: bool) { - let mask = 1u8 << Self::Sm::SM_NO; + let mask = 1u8 << Self::SM; unsafe { if enable { Self::Pio::PIO.ctrl().write_set(|w| w.set_sm_enable(mask)); @@ -354,40 +353,40 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn is_enabled(&self) -> bool { - unsafe { Self::Pio::PIO.ctrl().read().sm_enable() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.ctrl().read().sm_enable() & (1u8 << Self::SM) != 0 } } fn is_tx_empty(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().txempty() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().txempty() & (1u8 << Self::SM) != 0 } } fn is_tx_full(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().txfull() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().txfull() & (1u8 << Self::SM) != 0 } } fn is_rx_empty(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().rxempty() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().rxempty() & (1u8 << Self::SM) != 0 } } fn is_rx_full(&self) -> bool { - unsafe { Self::Pio::PIO.fstat().read().rxfull() & (1u8 << Self::Sm::SM_NO) != 0 } + unsafe { Self::Pio::PIO.fstat().read().rxfull() & (1u8 << Self::SM) != 0 } } fn tx_level(&self) -> u8 { unsafe { let flevel = Self::Pio::PIO.flevel().read().0; - (flevel >> (Self::Sm::SM_NO * 8)) as u8 & 0x0f + (flevel >> (Self::SM * 8)) as u8 & 0x0f } } fn rx_level(&self) -> u8 { unsafe { let flevel = Self::Pio::PIO.flevel().read().0; - (flevel >> (Self::Sm::SM_NO * 8 + 4)) as u8 & 0x0f + (flevel >> (Self::SM * 8 + 4)) as u8 & 0x0f } } fn push_tx(&mut self, v: u32) { unsafe { - Self::Pio::PIO.txf(Self::Sm::SM_NO as usize).write_value(v); + Self::Pio::PIO.txf(Self::SM).write_value(v); } } @@ -400,7 +399,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn pull_rx(&mut self) -> u32 { - unsafe { Self::Pio::PIO.rxf(Self::Sm::SM_NO as usize).read() } + unsafe { Self::Pio::PIO.rxf(Self::SM).read() } } fn try_pull_rx(&mut self) -> Option { @@ -421,7 +420,7 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } fn clkdiv_restart(&mut self) { - let mask = 1u8 << Self::Sm::SM_NO; + let mask = 1u8 << Self::SM; unsafe { Self::Pio::PIO.ctrl().write_set(|w| w.set_clkdiv_restart(mask)); } @@ -710,8 +709,8 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_tx_stalled(&self) -> bool { unsafe { let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().txstall() & (1 << Self::Sm::SM_NO) != 0; - fdebug.write(|w| w.set_txstall(1 << Self::Sm::SM_NO)); + let ret = fdebug.read().txstall() & (1 << Self::SM) != 0; + fdebug.write(|w| w.set_txstall(1 << Self::SM)); ret } } @@ -719,8 +718,8 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_tx_overflowed(&self) -> bool { unsafe { let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().txover() & (1 << Self::Sm::SM_NO) != 0; - fdebug.write(|w| w.set_txover(1 << Self::Sm::SM_NO)); + let ret = fdebug.read().txover() & (1 << Self::SM) != 0; + fdebug.write(|w| w.set_txover(1 << Self::SM)); ret } } @@ -728,8 +727,8 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_rx_stalled(&self) -> bool { unsafe { let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().rxstall() & (1 << Self::Sm::SM_NO) != 0; - fdebug.write(|w| w.set_rxstall(1 << Self::Sm::SM_NO)); + let ret = fdebug.read().rxstall() & (1 << Self::SM) != 0; + fdebug.write(|w| w.set_rxstall(1 << Self::SM)); ret } } @@ -737,8 +736,8 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn has_rx_underflowed(&self) -> bool { unsafe { let fdebug = Self::Pio::PIO.fdebug(); - let ret = fdebug.read().rxunder() & (1 << Self::Sm::SM_NO) != 0; - fdebug.write(|w| w.set_rxunder(1 << Self::Sm::SM_NO)); + let ret = fdebug.read().rxunder() & (1 << Self::SM) != 0; + fdebug.write(|w| w.set_rxunder(1 << Self::SM)); ret } } @@ -746,16 +745,15 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn dma_push<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a [W]) -> Transfer<'a, C> { unsafe { let pio_no = Self::Pio::PIO_NO; - let sm_no = Self::Sm::SM_NO; + let sm_no = Self::SM; let p = ch.regs(); p.read_addr().write_value(data.as_ptr() as u32); - p.write_addr() - .write_value(Self::Pio::PIO.txf(sm_no as usize).ptr() as u32); + p.write_addr().write_value(Self::Pio::PIO.txf(sm_no).ptr() as u32); p.trans_count().write_value(data.len() as u32); compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { // Set TX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + sm_no)); + w.set_treq_sel(TreqSel(pio_no * 8 + sm_no as u8)); w.set_data_size(W::size()); w.set_chain_to(ch.number()); w.set_incr_read(true); @@ -770,16 +768,15 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { fn dma_pull<'a, C: Channel, W: Word>(&'a self, ch: PeripheralRef<'a, C>, data: &'a mut [W]) -> Transfer<'a, C> { unsafe { let pio_no = Self::Pio::PIO_NO; - let sm_no = Self::Sm::SM_NO; + let sm_no = Self::SM; let p = ch.regs(); p.write_addr().write_value(data.as_ptr() as u32); - p.read_addr() - .write_value(Self::Pio::PIO.rxf(sm_no as usize).ptr() as u32); + p.read_addr().write_value(Self::Pio::PIO.rxf(sm_no).ptr() as u32); p.trans_count().write_value(data.len() as u32); compiler_fence(Ordering::SeqCst); p.ctrl_trig().write(|w| { // Set RX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + sm_no + 4)); + w.set_treq_sel(TreqSel(pio_no * 8 + sm_no as u8 + 4)); w.set_data_size(W::size()); w.set_chain_to(ch.number()); w.set_incr_read(false); @@ -882,22 +879,12 @@ impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { } } -// Identifies a specific state machine inside a PIO device -pub struct SmInstanceBase {} - -pub trait SmInstance: sealed::SmInstance + Unpin {} - -impl sealed::SmInstance for SmInstanceBase { - const SM_NO: u8 = SM_NO; -} -impl SmInstance for SmInstanceBase {} - pub struct Pio<'d, PIO: PioInstance> { pub common: PioCommon<'d, PIO>, - pub sm0: PioStateMachineInstance<'d, PIO, SmInstanceBase<0>>, - pub sm1: PioStateMachineInstance<'d, PIO, SmInstanceBase<1>>, - pub sm2: PioStateMachineInstance<'d, PIO, SmInstanceBase<2>>, - pub sm3: PioStateMachineInstance<'d, PIO, SmInstanceBase<3>>, + pub sm0: PioStateMachineInstance<'d, PIO, 0>, + pub sm1: PioStateMachineInstance<'d, PIO, 1>, + pub sm2: PioStateMachineInstance<'d, PIO, 2>, + pub sm3: PioStateMachineInstance<'d, PIO, 3>, } impl<'d, PIO: PioInstance> Pio<'d, PIO> { @@ -907,22 +894,10 @@ impl<'d, PIO: PioInstance> Pio<'d, PIO> { instructions_used: 0, pio: PhantomData, }, - sm0: PioStateMachineInstance { - sm: PhantomData, - pio: PhantomData, - }, - sm1: PioStateMachineInstance { - sm: PhantomData, - pio: PhantomData, - }, - sm2: PioStateMachineInstance { - sm: PhantomData, - pio: PhantomData, - }, - sm3: PioStateMachineInstance { - sm: PhantomData, - pio: PhantomData, - }, + sm0: PioStateMachineInstance { pio: PhantomData }, + sm1: PioStateMachineInstance { pio: PhantomData }, + sm2: PioStateMachineInstance { pio: PhantomData }, + sm3: PioStateMachineInstance { pio: PhantomData }, } } } @@ -936,18 +911,14 @@ pub trait PioInstance: sealed::PioInstance + Sized + Unpin { mod sealed { pub trait PioStateMachine { type Pio: super::PioInstance; - type Sm: super::SmInstance; + const SM: usize; #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { - Self::Pio::PIO.sm(Self::Sm::SM_NO as usize) + Self::Pio::PIO.sm(Self::SM as usize) } } - pub trait SmInstance { - const SM_NO: u8; - } - pub trait PioInstance { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; @@ -955,11 +926,6 @@ mod sealed { } } -pub type Sm0 = SmInstanceBase<0>; -pub type Sm1 = SmInstanceBase<1>; -pub type Sm2 = SmInstanceBase<2>; -pub type Sm3 = SmInstanceBase<3>; - macro_rules! impl_pio { ($name:ident, $pio:expr, $pac:ident, $funcsel:ident) => { impl sealed::PioInstance for peripherals::$name { diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 50b001b69..5fea7034b 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -5,12 +5,12 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2}; +use embassy_rp::pio::{Pio, PioCommon, PioStateMachine, PioStateMachineInstance, ShiftDirection}; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use {defmt_rtt as _, panic_probe as _}; -fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstance, pin: AnyPin) { +fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstance, pin: AnyPin) { // Setup sm0 // Send data serially to pin @@ -38,7 +38,7 @@ fn setup_pio_task_sm0(pio: &mut PioCommon, sm: &mut PioStateMachineInstanc } #[embassy_executor::task] -async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, Sm0>) { +async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, 0>) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -49,7 +49,7 @@ async fn pio_task_sm0(mut sm: PioStateMachineInstance<'static, PIO0, Sm0>) { } } -fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full @@ -68,7 +68,7 @@ fn setup_pio_task_sm1(pio: &mut PioCommon, sm: &mut PioStateMachineInstanc } #[embassy_executor::task] -async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, Sm1>) { +async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, 1>) { sm.set_enable(true); loop { let rx = sm.wait_pull().await; @@ -76,7 +76,7 @@ async fn pio_task_sm1(mut sm: PioStateMachineInstance<'static, PIO0, Sm1>) { } } -fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { +fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachineInstance) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -100,7 +100,7 @@ fn setup_pio_task_sm2(pio: &mut PioCommon, sm: &mut PioStateMachineInstanc } #[embassy_executor::task] -async fn pio_task_sm2(mut sm: PioStateMachineInstance<'static, PIO0, Sm2>) { +async fn pio_task_sm2(mut sm: PioStateMachineInstance<'static, PIO0, 2>) { sm.set_enable(true); loop { sm.wait_irq(3).await; diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 20c6a0565..59b4c1f52 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -8,7 +8,7 @@ use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::gpio::Pin; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{FifoJoin, Pio, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstanceBase}; +use embassy_rp::pio::{FifoJoin, Pio, PioStateMachine, PioStateMachineInstance, ShiftDirection}; use embassy_rp::pwm::{Config, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; @@ -65,7 +65,7 @@ async fn main(_spawner: Spawner) { pub struct HD44780<'l> { dma: PeripheralRef<'l, AnyChannel>, - sm: PioStateMachineInstance<'l, PIO0, SmInstanceBase<0>>, + sm: PioStateMachineInstance<'l, PIO0, 0>, buf: [u8; 40], } diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index a2121df42..0975559d7 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -6,18 +6,18 @@ use defmt::*; use embassy_executor::Spawner; use embassy_rp::gpio::{self, Pin}; use embassy_rp::pio::{ - FifoJoin, Pio, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, SmInstance, + FifoJoin, Pio, PioCommon, PioInstance, PioStateMachine, PioStateMachineInstance, ShiftDirection, }; use embassy_rp::pio_instr_util; use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; -pub struct Ws2812<'d, P: PioInstance, S: SmInstance> { +pub struct Ws2812<'d, P: PioInstance, const S: usize> { sm: PioStateMachineInstance<'d, P, S>, } -impl<'d, P: PioInstance, S: SmInstance> Ws2812<'d, P, S> { +impl<'d, P: PioInstance, const S: usize> Ws2812<'d, P, S> { pub fn new(mut pio: PioCommon<'d, P>, mut sm: PioStateMachineInstance<'d, P, S>, pin: gpio::AnyPin) -> Self { // Setup sm0 From a61701b7565536e6e1a224d64eafd373f7c18af5 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 02:51:55 +0200 Subject: [PATCH 15/19] stm32/usart: add OVER8 and PRESC support, update PAC --- embassy-stm32/Cargo.toml | 4 +- embassy-stm32/src/usart/buffered.rs | 4 +- embassy-stm32/src/usart/mod.rs | 128 +++++++++++++++++++++------- 3 files changed, 102 insertions(+), 34 deletions(-) diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9686b10ce..b9887f9b3 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -58,7 +58,7 @@ sdio-host = "0.5.0" embedded-sdmmc = { git = "https://github.com/embassy-rs/embedded-sdmmc-rs", rev = "a4f293d3a6f72158385f79c98634cb8a14d0d2fc", optional = true } critical-section = "1.1" atomic-polyfill = "1.0.1" -stm32-metapac = "6" +stm32-metapac = "7" vcell = "0.1.3" bxcan = "0.7.0" nb = "1.0.0" @@ -74,7 +74,7 @@ critical-section = { version = "1.1", features = ["std"] } [build-dependencies] proc-macro2 = "1.0.36" quote = "1.0.15" -stm32-metapac = { version = "6", default-features = false, features = ["metadata"]} +stm32-metapac = { version = "7", default-features = false, features = ["metadata"]} [features] default = ["stm32-metapac/rt"] diff --git a/embassy-stm32/src/usart/buffered.rs b/embassy-stm32/src/usart/buffered.rs index 3e23e7ca1..12cf8b0fc 100644 --- a/embassy-stm32/src/usart/buffered.rs +++ b/embassy-stm32/src/usart/buffered.rs @@ -84,7 +84,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { Self::new_inner(peri, irq, rx, tx, tx_buffer, rx_buffer, config) } - #[cfg(not(usart_v1))] + #[cfg(not(any(usart_v1, usart_v2)))] pub fn new_with_de( peri: impl Peripheral

+ 'd, irq: impl Peripheral

+ 'd, @@ -133,7 +133,7 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); } - configure(r, &config, T::frequency(), T::MULTIPLIER, true, true); + configure(r, &config, T::frequency(), T::KIND, true, true); unsafe { r.cr1().modify(|w| { diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index ad450f2b3..dbce668c2 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -12,10 +12,11 @@ use futures::future::{select, Either}; use crate::dma::{NoDma, Transfer}; use crate::gpio::sealed::AFType; -#[cfg(any(lpuart_v1, lpuart_v2))] -use crate::pac::lpuart::{regs, vals, Lpuart as Regs}; -#[cfg(not(any(lpuart_v1, lpuart_v2)))] -use crate::pac::usart::{regs, vals, Usart as Regs}; +#[cfg(not(any(usart_v1, usart_v2)))] +use crate::pac::usart::Lpuart as Regs; +#[cfg(any(usart_v1, usart_v2))] +use crate::pac::usart::Usart as Regs; +use crate::pac::usart::{regs, vals}; use crate::time::Hertz; use crate::{peripherals, Peripheral}; @@ -159,7 +160,7 @@ impl<'d, T: BasicInstance, TxDma> UartTx<'d, T, TxDma> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); } - configure(r, &config, T::frequency(), T::MULTIPLIER, false, true); + configure(r, &config, T::frequency(), T::KIND, false, true); // create state once! let _s = T::state(); @@ -261,7 +262,7 @@ impl<'d, T: BasicInstance, RxDma> UartRx<'d, T, RxDma> { rx.set_as_af(rx.af_num(), AFType::Input); } - configure(r, &config, T::frequency(), T::MULTIPLIER, true, false); + configure(r, &config, T::frequency(), T::KIND, true, false); irq.set_handler(Self::on_interrupt); irq.unpend(); @@ -653,7 +654,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) } - #[cfg(not(usart_v1))] + #[cfg(not(any(usart_v1, usart_v2)))] pub fn new_with_de( peri: impl Peripheral

+ 'd, rx: impl Peripheral

> + 'd, @@ -696,7 +697,7 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { tx.set_as_af(tx.af_num(), AFType::OutputPushPull); } - configure(r, &config, T::frequency(), T::MULTIPLIER, true, true); + configure(r, &config, T::frequency(), T::KIND, true, true); irq.set_handler(UartRx::::on_interrupt); irq.unpend(); @@ -763,16 +764,74 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> { } } -fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32, enable_rx: bool, enable_tx: bool) { +fn configure(r: Regs, config: &Config, pclk_freq: Hertz, kind: Kind, enable_rx: bool, enable_tx: bool) { if !enable_rx && !enable_tx { panic!("USART: At least one of RX or TX should be enabled"); } - // TODO: better calculation, including error checking and OVER8 if possible. - let div = (pclk_freq.0 + (config.baudrate / 2)) / config.baudrate * multiplier; + #[cfg(not(usart_v4))] + static DIVS: [(u16, ()); 1] = [(1, ())]; + + #[cfg(usart_v4)] + static DIVS: [(u16, vals::Presc); 12] = [ + (1, vals::Presc::DIV1), + (2, vals::Presc::DIV2), + (4, vals::Presc::DIV4), + (6, vals::Presc::DIV6), + (8, vals::Presc::DIV8), + (10, vals::Presc::DIV10), + (12, vals::Presc::DIV12), + (16, vals::Presc::DIV16), + (32, vals::Presc::DIV32), + (64, vals::Presc::DIV64), + (128, vals::Presc::DIV128), + (256, vals::Presc::DIV256), + ]; + + let (mul, brr_min, brr_max) = match kind { + #[cfg(any(usart_v3, usart_v4))] + Kind::Lpuart => (256, 0x300, 0x10_0000), + Kind::Uart => (1, 0x10, 0x1_0000), + }; + + #[cfg(not(usart_v1))] + let mut over8 = false; + let mut found = false; + for &(presc, _presc_val) in &DIVS { + let denom = (config.baudrate * presc as u32) as u64; + let div = (pclk_freq.0 as u64 * mul + (denom / 2)) / denom; + trace!("USART: presc={} div={:08x}", presc, div); + + if div < brr_min { + #[cfg(not(usart_v1))] + if div * 2 >= brr_min && kind == Kind::Uart && !cfg!(usart_v1) { + over8 = true; + let div = div as u32; + unsafe { + r.brr().write_value(regs::Brr(((div << 1) & !0xF) | (div & 0x07))); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); + } + found = true; + break; + } + panic!("USART: baudrate too high"); + } + + if div < brr_max { + unsafe { + r.brr().write_value(regs::Brr(div as u32)); + #[cfg(usart_v4)] + r.presc().write(|w| w.set_prescaler(_presc_val)); + } + found = true; + break; + } + } + + assert!(found, "USART: baudrate too low"); unsafe { - r.brr().write_value(regs::Brr(div)); r.cr2().write(|w| { w.set_stop(match config.stop_bits { StopBits::STOP0P5 => vals::Stop::STOP0P5, @@ -801,6 +860,8 @@ fn configure(r: Regs, config: &Config, pclk_freq: Hertz, multiplier: u32, enable Parity::ParityEven => vals::Ps::EVEN, _ => vals::Ps::EVEN, }); + #[cfg(not(usart_v1))] + w.set_over8(vals::Over8(over8 as _)); }); } } @@ -986,43 +1047,45 @@ mod rx_ringbuffered; #[cfg(not(gpdma))] pub use rx_ringbuffered::RingBufferedUartRx; -#[cfg(usart_v1)] +use self::sealed::Kind; + +#[cfg(any(usart_v1, usart_v2))] fn tdr(r: crate::pac::usart::Usart) -> *mut u8 { r.dr().ptr() as _ } -#[cfg(usart_v1)] +#[cfg(any(usart_v1, usart_v2))] fn rdr(r: crate::pac::usart::Usart) -> *mut u8 { r.dr().ptr() as _ } -#[cfg(usart_v1)] +#[cfg(any(usart_v1, usart_v2))] fn sr(r: crate::pac::usart::Usart) -> crate::pac::common::Reg { r.sr() } -#[cfg(usart_v1)] +#[cfg(any(usart_v1, usart_v2))] #[allow(unused)] unsafe fn clear_interrupt_flags(_r: Regs, _sr: regs::Sr) { // On v1 the flags are cleared implicitly by reads and writes to DR. } -#[cfg(usart_v2)] +#[cfg(any(usart_v3, usart_v4))] fn tdr(r: Regs) -> *mut u8 { r.tdr().ptr() as _ } -#[cfg(usart_v2)] +#[cfg(any(usart_v3, usart_v4))] fn rdr(r: Regs) -> *mut u8 { r.rdr().ptr() as _ } -#[cfg(usart_v2)] +#[cfg(any(usart_v3, usart_v4))] fn sr(r: Regs) -> crate::pac::common::Reg { r.isr() } -#[cfg(usart_v2)] +#[cfg(any(usart_v3, usart_v4))] #[allow(unused)] unsafe fn clear_interrupt_flags(r: Regs, sr: regs::Isr) { r.icr().write(|w| *w = regs::Icr(sr.0)); @@ -1033,6 +1096,13 @@ pub(crate) mod sealed { use super::*; + #[derive(Clone, Copy, PartialEq, Eq)] + pub enum Kind { + Uart, + #[cfg(any(usart_v3, usart_v4))] + Lpuart, + } + pub struct State { pub rx_waker: AtomicWaker, pub tx_waker: AtomicWaker, @@ -1048,7 +1118,7 @@ pub(crate) mod sealed { } pub trait BasicInstance: crate::rcc::RccPeripheral { - const MULTIPLIER: u32; + const KIND: Kind; type Interrupt: crate::interrupt::Interrupt; fn regs() -> Regs; @@ -1077,10 +1147,10 @@ pin_trait!(DePin, BasicInstance); dma_trait!(TxDma, BasicInstance); dma_trait!(RxDma, BasicInstance); -macro_rules! impl_lpuart { - ($inst:ident, $irq:ident, $mul:expr) => { +macro_rules! impl_usart { + ($inst:ident, $irq:ident, $kind:expr) => { impl sealed::BasicInstance for crate::peripherals::$inst { - const MULTIPLIER: u32 = $mul; + const KIND: Kind = $kind; type Interrupt = crate::interrupt::$irq; fn regs() -> Regs { @@ -1104,21 +1174,19 @@ macro_rules! impl_lpuart { } foreach_interrupt!( - ($inst:ident, lpuart, $block:ident, $signal_name:ident, $irq:ident) => { - impl_lpuart!($inst, $irq, 256); + ($inst:ident, usart, LPUART, $signal_name:ident, $irq:ident) => { + impl_usart!($inst, $irq, Kind::Lpuart); }; ($inst:ident, usart, $block:ident, $signal_name:ident, $irq:ident) => { - impl_lpuart!($inst, $irq, 1); + impl_usart!($inst, $irq, Kind::Uart); impl sealed::FullInstance for peripherals::$inst { - fn regs_uart() -> crate::pac::usart::Usart { crate::pac::$inst } } - impl FullInstance for peripherals::$inst { - } + impl FullInstance for peripherals::$inst {} }; ); From 2bb6e93e86af02af97b4fb39197a1d1e87e635b9 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 02:52:37 +0200 Subject: [PATCH 16/19] stm32/usart: add baudrate calc test. --- tests/stm32/Cargo.toml | 1 + tests/stm32/build.rs | 13 +++--- tests/stm32/src/bin/usart.rs | 77 +++++++++++++++++++++++-------- tests/stm32/src/example_common.rs | 5 ++ 4 files changed, 72 insertions(+), 24 deletions(-) diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 5cd949661..83bf1e9c9 100644 --- a/tests/stm32/Cargo.toml +++ b/tests/stm32/Cargo.toml @@ -3,6 +3,7 @@ edition = "2021" name = "embassy-stm32-tests" version = "0.1.0" license = "MIT OR Apache-2.0" +autobins = false [features] stm32f103c8 = ["embassy-stm32/stm32f103c8", "not-gpdma"] # Blue Pill diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 3e67a7392..4c76a8eeb 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -6,15 +6,16 @@ fn main() -> Result<(), Box> { let out = PathBuf::from(env::var("OUT_DIR").unwrap()); fs::write(out.join("link_ram.x"), include_bytes!("link_ram.x")).unwrap(); println!("cargo:rustc-link-search={}", out.display()); - println!("cargo:rerun-if-changed=link_ram.x"); - println!("cargo:rustc-link-arg-bins=--nmagic"); // too little RAM to run from RAM. - #[cfg(any(feature = "stm32c031c6"))] - println!("cargo:rustc-link-arg-bins=-Tlink.x"); - #[cfg(not(any(feature = "stm32c031c6")))] - println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); + if cfg!(any(feature = "stm32c031c6")) { + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rerun-if-changed=link.x"); + } else { + println!("cargo:rustc-link-arg-bins=-Tlink_ram.x"); + println!("cargo:rerun-if-changed=link_ram.x"); + } println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index cca8c42ee..bda2ce9c2 100644 --- a/tests/stm32/src/bin/usart.rs +++ b/tests/stm32/src/bin/usart.rs @@ -9,6 +9,7 @@ use embassy_executor::Spawner; use embassy_stm32::dma::NoDma; use embassy_stm32::interrupt; use embassy_stm32::usart::{Config, Uart}; +use embassy_time::{Duration, Instant}; use example_common::*; #[embassy_executor::main] @@ -19,36 +20,76 @@ async fn main(_spawner: Spawner) { // Arduino pins D0 and D1 // They're connected together with a 1K resistor. #[cfg(feature = "stm32f103c8")] - let (tx, rx, usart, irq) = (p.PA9, p.PA10, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PA9, p.PA10, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32g491re")] - let (tx, rx, usart, irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32g071rb")] - let (tx, rx, usart, irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PC4, p.PC5, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32f429zi")] - let (tx, rx, usart, irq) = (p.PG14, p.PG9, p.USART6, interrupt::take!(USART6)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PG14, p.PG9, p.USART6, interrupt::take!(USART6)); #[cfg(feature = "stm32wb55rg")] - let (tx, rx, usart, irq) = (p.PA2, p.PA3, p.LPUART1, interrupt::take!(LPUART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PA2, p.PA3, p.LPUART1, interrupt::take!(LPUART1)); #[cfg(feature = "stm32h755zi")] - let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); #[cfg(feature = "stm32u585ai")] - let (tx, rx, usart, irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PD8, p.PD9, p.USART3, interrupt::take!(USART3)); #[cfg(feature = "stm32h563zi")] - let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.LPUART1, interrupt::take!(LPUART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PB6, p.PB7, p.LPUART1, interrupt::take!(LPUART1)); #[cfg(feature = "stm32c031c6")] - let (tx, rx, usart, irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); + let (mut tx, mut rx, mut usart, mut irq) = (p.PB6, p.PB7, p.USART1, interrupt::take!(USART1)); - let config = Config::default(); - let mut usart = Uart::new(usart, rx, tx, irq, NoDma, NoDma, config); + { + let config = Config::default(); + let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, &mut irq, NoDma, NoDma, config); - // We can't send too many bytes, they have to fit in the FIFO. - // This is because we aren't sending+receiving at the same time. + // We can't send too many bytes, they have to fit in the FIFO. + // This is because we aren't sending+receiving at the same time. - let data = [0xC0, 0xDE]; - usart.blocking_write(&data).unwrap(); + let data = [0xC0, 0xDE]; + usart.blocking_write(&data).unwrap(); - let mut buf = [0; 2]; - usart.blocking_read(&mut buf).unwrap(); - assert_eq!(buf, data); + let mut buf = [0; 2]; + usart.blocking_read(&mut buf).unwrap(); + assert_eq!(buf, data); + } + + // Test that baudrate divider is calculated correctly. + // Do it by comparing the time it takes to send a known number of bytes. + for baudrate in [ + 300, + 9600, + 115200, + 250_000, + 337_934, + #[cfg(not(feature = "stm32f103c8"))] + 1_000_000, + #[cfg(not(feature = "stm32f103c8"))] + 2_000_000, + ] { + info!("testing baudrate {}", baudrate); + + let mut config = Config::default(); + config.baudrate = baudrate; + let mut usart = Uart::new(&mut usart, &mut rx, &mut tx, &mut irq, NoDma, NoDma, config); + + let n = (baudrate as usize / 100).max(64); + + let start = Instant::now(); + for _ in 0..n { + usart.blocking_write(&[0x00]).unwrap(); + } + let dur = Instant::now() - start; + let want_dur = Duration::from_micros(n as u64 * 10 * 1_000_000 / (baudrate as u64)); + let fuzz = want_dur / 5; + if dur < want_dur - fuzz || dur > want_dur + fuzz { + defmt::panic!( + "bad duration for baudrate {}: got {:?} want {:?}", + baudrate, + dur, + want_dur + ); + } + } info!("Test OK"); cortex_m::asm::bkpt(); diff --git a/tests/stm32/src/example_common.rs b/tests/stm32/src/example_common.rs index a4f8668c7..3d150da60 100644 --- a/tests/stm32/src/example_common.rs +++ b/tests/stm32/src/example_common.rs @@ -16,5 +16,10 @@ pub fn config() -> Config { config.rcc.pll1.q_ck = Some(Hertz(100_000_000)); } + #[cfg(feature = "stm32u585ai")] + { + config.rcc.mux = embassy_stm32::rcc::ClockSrc::MSI(embassy_stm32::rcc::MSIRange::Range48mhz); + } + config } From 1078f6f4e7c8473013c46543663cb6ca6b0e0c3d Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 19:35:02 +0200 Subject: [PATCH 17/19] stm32/test: workaround #1426 --- tests/stm32/src/bin/usart_dma.rs | 3 +++ tests/stm32/src/bin/usart_rx_ringbuffered.rs | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/stm32/src/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index de6cd41d1..62444f0a8 100644 --- a/tests/stm32/src/bin/usart_dma.rs +++ b/tests/stm32/src/bin/usart_dma.rs @@ -94,6 +94,9 @@ async fn main(_spawner: Spawner) { let rx_fut = async { rx.read(&mut rx_buf).await.unwrap(); }; + + // note: rx needs to be polled first, to workaround this bug: + // https://github.com/embassy-rs/embassy/issues/1426 join(rx_fut, tx_fut).await; assert_eq!(tx_buf, rx_buf); diff --git a/tests/stm32/src/bin/usart_rx_ringbuffered.rs b/tests/stm32/src/bin/usart_rx_ringbuffered.rs index 2c4a8fdf4..9d75dbe55 100644 --- a/tests/stm32/src/bin/usart_rx_ringbuffered.rs +++ b/tests/stm32/src/bin/usart_rx_ringbuffered.rs @@ -145,13 +145,16 @@ async fn main(spawner: Spawner) { #[embassy_executor::task] async fn transmit_task(mut tx: UartTx<'static, board::Uart, board::TxDma>) { + // workaround https://github.com/embassy-rs/embassy/issues/1426 + Timer::after(Duration::from_millis(100) as _).await; + let mut rng = ChaCha8Rng::seed_from_u64(1337); info!("Starting random transmissions into void..."); let mut i: u8 = 0; loop { - let mut buf = [0; 32]; + let mut buf = [0; 256]; let len = 1 + (rng.next_u32() as usize % buf.len()); for b in &mut buf[..len] { *b = i; @@ -172,7 +175,7 @@ async fn receive_task(mut rx: RingBufferedUartRx<'static, board::Uart, board::Rx let mut i = 0; let mut expected = 0; loop { - let mut buf = [0; 100]; + let mut buf = [0; 256]; let max_len = 1 + (rng.next_u32() as usize % buf.len()); let received = match rx.read(&mut buf[..max_len]).await { Ok(r) => r, From a85b34c1fec458f00c37db2f395345540d73d519 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 19:35:18 +0200 Subject: [PATCH 18/19] stm32/test: F1 no longer fits in RAM. --- tests/stm32/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stm32/build.rs b/tests/stm32/build.rs index 4c76a8eeb..7ae311778 100644 --- a/tests/stm32/build.rs +++ b/tests/stm32/build.rs @@ -9,7 +9,7 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-link-arg-bins=--nmagic"); // too little RAM to run from RAM. - if cfg!(any(feature = "stm32c031c6")) { + if cfg!(any(feature = "stm32f103c8", feature = "stm32c031c6")) { println!("cargo:rustc-link-arg-bins=-Tlink.x"); println!("cargo:rerun-if-changed=link.x"); } else { From 433422b9f2b08d4bf9f2cef4ded37c14914db157 Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Tue, 2 May 2023 19:35:32 +0200 Subject: [PATCH 19/19] stm32/test: remove adsfa --- tests/stm32/src/bin/spi.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/stm32/src/bin/spi.rs b/tests/stm32/src/bin/spi.rs index 0f5e563b1..a87ac3237 100644 --- a/tests/stm32/src/bin/spi.rs +++ b/tests/stm32/src/bin/spi.rs @@ -35,7 +35,6 @@ async fn main(_spawner: Spawner) { #[cfg(feature = "stm32c031c6")] let (spi, sck, mosi, miso) = (p.SPI1, p.PA5, p.PA7, p.PA6); - info!("asdfa;"); let mut spi = Spi::new( spi, sck, // Arduino D13