diff --git a/embassy-rp/src/gpio.rs b/embassy-rp/src/gpio.rs index 98e18286..da8efba9 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(); @@ -136,16 +136,11 @@ 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, - } - } +pub(crate) unsafe fn init() { + let irq = interrupt::IO_IRQ_BANK0::steal(); + irq.disable(); + irq.set_priority(interrupt::Priority::P3); + irq.enable(); } #[interrupt] @@ -166,27 +161,15 @@ 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) { - 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); - } - }); + // 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 { + 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(); } @@ -203,16 +186,26 @@ 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 + // 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 { + 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); @@ -227,12 +220,10 @@ 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); } }); - }); - - irq.enable(); } Self { pin, level } @@ -257,47 +248,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 +609,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; } } diff --git a/embassy-rp/src/lib.rs b/embassy-rp/src/lib.rs index d69d12a3..cba7559d 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 e51fc218..c84fea5c 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/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 7faec10b..5433d3f2 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -5,14 +5,14 @@ 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}; +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::pio::sealed::{PioInstance as _, SmInstance as _}; +use crate::pac::dma::vals::TreqSel; +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; }); } } @@ -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 { @@ -298,9 +297,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); + } } } @@ -315,16 +316,15 @@ impl SealedPin for PioPin { } } -pub struct PioStateMachineInstance { - pio: PhantomData, - sm: PhantomData, +pub struct PioStateMachineInstance<'d, PIO: PioInstance, const SM: usize> { + pio: PhantomData<&'d PIO>, } -impl sealed::PioStateMachine for PioStateMachineInstance { +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 PioStateMachine for PioStateMachineInstance {} +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 { @@ -332,60 +332,61 @@ 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; 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; + let mask = 1u8 << Self::SM; 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)); + } } } 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); } } @@ -398,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 { @@ -410,73 +411,54 @@ 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) { + let mask = 1u8 << Self::SM; 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)); } } 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)); } } @@ -787,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 } } @@ -796,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 } } @@ -805,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 } } @@ -814,25 +736,25 @@ 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 } } - 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; + 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_data_size(DataSize::SIZE_WORD); + 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); w.set_incr_write(false); @@ -843,19 +765,19 @@ 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; + 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 TX DREQ for this statemachine - w.set_treq_sel(TreqSel(pio_no * 8 + sm_no + 4)); - w.set_data_size(DataSize::SIZE_WORD); + // Set RX DREQ for this statemachine + 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); w.set_incr_write(true); @@ -867,21 +789,18 @@ pub trait PioStateMachine: sealed::PioStateMachine + Sized + Unpin { } } -pub struct PioCommonInstance { +pub struct PioCommon<'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 { - type Pio = PIO; -} -impl PioCommon for PioCommonInstance { - fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory +impl<'d, PIO: PioInstance> PioCommon<'d, PIO> { + pub fn write_instr(&mut self, start: usize, instrs: I) -> PioInstanceMemory<'d, PIO> where I: Iterator, { @@ -908,66 +827,50 @@ impl PioCommon for PioCommonInstance { } } - 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 { - 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. + 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( - 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(PIO::FUNCSEL.0)); } PioPin { pin_bank: pin.pin_bank(), @@ -976,112 +879,63 @@ pub trait PioCommon: sealed::PioCommon + Sized { } } -// 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; +pub struct Pio<'d, PIO: PioInstance> { + pub common: PioCommon<'d, PIO>, + 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 SmInstance for SmInstanceBase {} -pub trait PioPeripheral: sealed::PioPeripheral + Sized { - fn pio(&self) -> u8 { - Self::Pio::PIO_NO - } - - fn split( - self, - ) -> ( - PioCommonInstance, - PioStateMachineInstance>, - PioStateMachineInstance>, - PioStateMachineInstance>, - PioStateMachineInstance>, - ) { - ( - PioCommonInstance { +impl<'d, PIO: PioInstance> Pio<'d, PIO> { + pub fn new(_pio: impl Peripheral

+ 'd) -> Self { + Self { + common: PioCommon { instructions_used: 0, - pio: PhantomData::default(), + pio: PhantomData, }, - 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(), - }, - ) + sm0: PioStateMachineInstance { pio: PhantomData }, + sm1: PioStateMachineInstance { pio: PhantomData }, + sm2: PioStateMachineInstance { pio: PhantomData }, + sm3: PioStateMachineInstance { pio: PhantomData }, + } + } +} + +pub trait PioInstance: sealed::PioInstance + Sized + Unpin { + fn pio(&self) -> u8 { + Self::PIO_NO } } mod sealed { + pub trait PioStateMachine { + type Pio: super::PioInstance; + const SM: usize; + + #[inline(always)] + fn this_sm() -> crate::pac::pio::StateMachine { + Self::Pio::PIO.sm(Self::SM as usize) + } + } + pub trait PioInstance { const PIO_NO: u8; const PIO: &'static crate::pac::pio::Pio; - } - - pub trait PioCommon { - type Pio: super::PioInstance; - } - - pub trait PioStateMachine { - type Pio: super::PioInstance; - type Sm: super::SmInstance; - } - - pub trait SmInstance { - const SM_NO: u8; - } - - pub trait PioPeripheral { - type Pio: super::PioInstance; + 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; -} -impl PioInstance for PioInstanceBase<0> {} - -impl sealed::PioInstance for PioInstanceBase<1> { - const PIO_NO: u8 = 1; - const PIO: &'static pac::pio::Pio = &pac::PIO1; -} -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/embassy-rp/src/uart/mod.rs b/embassy-rp/src/uart/mod.rs index ebe32cc6..4084c158 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/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 9686b10c..b9887f9b 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 3e23e7ca..12cf8b0f 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 ad450f2b..dbce668c 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 {} }; ); diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 1b075b8f..5fea7034 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -4,15 +4,13 @@ use defmt::info; use embassy_executor::Spawner; use embassy_rp::gpio::{AnyPin, Pin}; -use embassy_rp::pio::{ - Pio0, PioCommon, PioCommonInstance, PioPeripheral, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, - Sm1, Sm2, -}; +use embassy_rp::peripherals::PIO0; +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 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 @@ -40,7 +38,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, 0>) { sm.set_enable(true); let mut v = 0x0f0caffa; @@ -51,7 +49,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 PioCommon, sm: &mut PioStateMachineInstance) { // Setupm sm1 // Read 0b10101 repeatedly until ISR is full @@ -70,7 +68,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, 1>) { sm.set_enable(true); loop { let rx = sm.wait_pull().await; @@ -78,7 +76,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 PioCommon, sm: &mut PioStateMachineInstance) { // Setup sm2 // Repeatedly trigger IRQ 3 @@ -102,7 +100,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, 2>) { sm.set_enable(true); loop { sm.wait_irq(3).await; @@ -115,11 +113,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 7d4919f7..0f1f6df1 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::{Pio, 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 new file mode 100644 index 00000000..59b4c1f5 --- /dev/null +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -0,0 +1,243 @@ +#![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, Pio, PioStateMachine, PioStateMachineInstance, ShiftDirection}; +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<'l, PIO0, 0>, + + buf: [u8; 40], +} + +impl<'l> HD44780<'l> { + pub async fn new( + pio: impl Peripheral

+ 'l, + 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 Pio { + mut common, mut sm0, .. + } = Pio::new(pio); + + // 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; + } +} diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 041e8ae1..0975559d 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -6,20 +6,19 @@ 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, 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 { - sm: PioStateMachineInstance, +pub struct Ws2812<'d, P: PioInstance, const S: usize> { + sm: PioStateMachineInstance<'d, P, S>, } -impl Ws2812 { - pub fn new(mut pio: PioCommonInstance

, mut sm: PioStateMachineInstance, pin: gpio::AnyPin) -> Self { +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 // prepare the PIO program @@ -116,7 +115,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 +124,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 { diff --git a/tests/rp/src/bin/gpio_multicore.rs b/tests/rp/src/bin/gpio_multicore.rs new file mode 100644 index 00000000..6c13ccaa --- /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; +} diff --git a/tests/rp/src/bin/uart_dma.rs b/tests/rp/src/bin/uart_dma.rs index 92aa205c..52f42e58 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. { diff --git a/tests/stm32/Cargo.toml b/tests/stm32/Cargo.toml index 5cd94966..83bf1e9c 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 3e67a739..7ae31177 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 = "stm32f103c8", 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/spi.rs b/tests/stm32/src/bin/spi.rs index 0f5e563b..a87ac323 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 diff --git a/tests/stm32/src/bin/usart.rs b/tests/stm32/src/bin/usart.rs index cca8c42e..bda2ce9c 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/bin/usart_dma.rs b/tests/stm32/src/bin/usart_dma.rs index de6cd41d..62444f0a 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 2c4a8fdf..9d75dbe5 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, diff --git a/tests/stm32/src/example_common.rs b/tests/stm32/src/example_common.rs index a4f8668c..3d150da6 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 }