diff --git a/embassy-rp/src/pio.rs b/embassy-rp/src/pio.rs index 8d054790..a1b7cf1c 100644 --- a/embassy-rp/src/pio.rs +++ b/embassy-rp/src/pio.rs @@ -6,9 +6,13 @@ use core::task::{Context, Poll}; use atomic_polyfill::{AtomicU32, AtomicU8}; use embassy_cortex_m::interrupt::{Interrupt, InterruptExt}; +use embassy_embedded_hal::SetConfig; use embassy_hal_common::{into_ref, Peripheral, PeripheralRef}; use embassy_sync::waitqueue::AtomicWaker; +use fixed::types::extra::U8; +use fixed::FixedU32; use pac::io::vals::Gpio0ctrlFuncsel; +use pac::pio::vals::SmExecctrlStatusSel; use pio::{SideSet, Wrap}; use crate::dma::{Channel, Transfer, Word}; @@ -39,8 +43,12 @@ const NEW_AW: AtomicWaker = AtomicWaker::new(); const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]); static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2]; +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] pub enum FifoJoin { /// Both TX and RX fifo is enabled + #[default] Duplex, /// Rx fifo twice as deep. TX fifo disabled RxOnly, @@ -48,8 +56,11 @@ pub enum FifoJoin { TxOnly, } -#[derive(PartialEq)] +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] pub enum ShiftDirection { + #[default] Right = 1, Left = 0, } @@ -62,6 +73,15 @@ pub enum Direction { Out = 1, } +#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u8)] +pub enum StatusSource { + #[default] + TxFifoLevel = 0, + RxFifoLevel = 1, +} + const RXNEMPTY_MASK: u32 = 1 << 0; const TXNFULL_MASK: u32 = 1 << 4; const SMIRQ_MASK: u32 = 1 << 8; @@ -477,6 +497,202 @@ fn assert_consecutive<'d, PIO: Instance>(pins: &[&Pin<'d, PIO>]) { } } +#[derive(Clone, Copy, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub struct ExecConfig { + pub side_en: bool, + pub side_pindir: bool, + pub jmp_pin: u8, + pub wrap_top: u8, + pub wrap_bottom: u8, +} + +#[derive(Clone, Copy, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct ShiftConfig { + pub threshold: u8, + pub direction: ShiftDirection, + pub auto_fill: bool, +} + +#[derive(Clone, Copy, Default, Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PinConfig { + pub sideset_count: u8, + pub set_count: u8, + pub out_count: u8, + pub in_base: u8, + pub sideset_base: u8, + pub set_base: u8, + pub out_base: u8, +} + +#[derive(Clone, Copy, Debug)] +pub struct Config<'d, PIO: Instance> { + // CLKDIV + pub clock_divider: FixedU32, + // EXECCTRL + pub out_en_sel: u8, + pub inline_out_en: bool, + pub out_sticky: bool, + pub status_sel: StatusSource, + pub status_n: u8, + exec: ExecConfig, + origin: Option, + // SHIFTCTRL + pub fifo_join: FifoJoin, + pub shift_in: ShiftConfig, + pub shift_out: ShiftConfig, + // PINCTRL + pins: PinConfig, + in_count: u8, + _pio: PhantomData<&'d mut PIO>, +} + +impl<'d, PIO: Instance> Default for Config<'d, PIO> { + fn default() -> Self { + Self { + clock_divider: 1u8.into(), + out_en_sel: Default::default(), + inline_out_en: Default::default(), + out_sticky: Default::default(), + status_sel: Default::default(), + status_n: Default::default(), + exec: Default::default(), + origin: Default::default(), + fifo_join: Default::default(), + shift_in: Default::default(), + shift_out: Default::default(), + pins: Default::default(), + in_count: Default::default(), + _pio: Default::default(), + } + } +} + +impl<'d, PIO: Instance> Config<'d, PIO> { + pub fn get_exec(&self) -> ExecConfig { + self.exec + } + pub unsafe fn set_exec(&mut self, e: ExecConfig) { + self.exec = e; + } + + pub fn get_pins(&self) -> PinConfig { + self.pins + } + pub unsafe fn set_pins(&mut self, p: PinConfig) { + self.pins = p; + } + + /// Configures this state machine to use the given program, including jumping to the origin + /// of the program. The state machine is not started. + /// + /// `side_set` sets the range of pins affected by side-sets. The range must be consecutive. + /// Side-set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) { + assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len()); + assert_consecutive(side_set); + self.exec.side_en = prog.side_set.optional(); + self.exec.side_pindir = prog.side_set.pindirs(); + self.exec.wrap_bottom = prog.wrap.target; + self.exec.wrap_top = prog.wrap.source; + self.pins.sideset_count = prog.side_set.bits(); + self.pins.sideset_base = side_set.first().map_or(0, |p| p.pin()); + self.origin = Some(prog.origin); + } + + pub fn set_jmp_pin(&mut self, pin: &Pin<'d, PIO>) { + self.exec.jmp_pin = pin.pin(); + } + + /// Sets the range of pins affected by SET instructions. The range must be consecutive. + /// Set pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn set_set_pins(&mut self, pins: &[&Pin<'d, PIO>]) { + assert!(pins.len() <= 5); + assert_consecutive(pins); + self.pins.set_base = pins.first().map_or(0, |p| p.pin()); + self.pins.set_count = pins.len() as u8; + } + + /// Sets the range of pins affected by OUT instructions. The range must be consecutive. + /// Out pins must configured as outputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn set_out_pins(&mut self, pins: &[&Pin<'d, PIO>]) { + assert_consecutive(pins); + self.pins.out_base = pins.first().map_or(0, |p| p.pin()); + self.pins.out_count = pins.len() as u8; + } + + /// Sets the range of pins used by IN instructions. The range must be consecutive. + /// In pins must configured as inputs using [`StateMachine::set_pin_dirs`] to be + /// effective. + pub fn set_in_pins(&mut self, pins: &[&Pin<'d, PIO>]) { + assert_consecutive(pins); + self.pins.in_base = pins.first().map_or(0, |p| p.pin()); + self.in_count = pins.len() as u8; + } +} + +impl<'d, PIO: Instance, const SM: usize> SetConfig for StateMachine<'d, PIO, SM> { + type Config = Config<'d, PIO>; + + fn set_config(&mut self, config: &Self::Config) { + // sm expects 0 for 65536, truncation makes that happen + assert!(config.clock_divider <= 65536, "clkdiv must be <= 65536"); + assert!(config.clock_divider >= 1, "clkdiv must be >= 1"); + assert!(config.out_en_sel < 32, "out_en_sel must be < 32"); + assert!(config.status_n < 32, "status_n must be < 32"); + // sm expects 0 for 32, truncation makes that happen + assert!(config.shift_in.threshold <= 32, "shift_in.threshold must be <= 32"); + assert!(config.shift_out.threshold <= 32, "shift_out.threshold must be <= 32"); + let sm = Self::this_sm(); + unsafe { + sm.clkdiv().write(|w| w.0 = config.clock_divider.to_bits() << 8); + sm.execctrl().write(|w| { + w.set_side_en(config.exec.side_en); + w.set_side_pindir(config.exec.side_pindir); + w.set_jmp_pin(config.exec.jmp_pin); + w.set_out_en_sel(config.out_en_sel); + w.set_inline_out_en(config.inline_out_en); + w.set_out_sticky(config.out_sticky); + w.set_wrap_top(config.exec.wrap_top); + w.set_wrap_bottom(config.exec.wrap_bottom); + w.set_status_sel(match config.status_sel { + StatusSource::TxFifoLevel => SmExecctrlStatusSel::TXLEVEL, + StatusSource::RxFifoLevel => SmExecctrlStatusSel::RXLEVEL, + }); + w.set_status_n(config.status_n); + }); + sm.shiftctrl().write(|w| { + w.set_fjoin_rx(config.fifo_join == FifoJoin::RxOnly); + w.set_fjoin_tx(config.fifo_join == FifoJoin::TxOnly); + w.set_pull_thresh(config.shift_out.threshold); + w.set_push_thresh(config.shift_in.threshold); + w.set_out_shiftdir(config.shift_out.direction == ShiftDirection::Right); + w.set_in_shiftdir(config.shift_in.direction == ShiftDirection::Right); + w.set_autopull(config.shift_out.auto_fill); + w.set_autopush(config.shift_in.auto_fill); + }); + sm.pinctrl().write(|w| { + w.set_sideset_count(config.pins.sideset_count); + w.set_set_count(config.pins.set_count); + w.set_out_count(config.pins.out_count); + w.set_in_base(config.pins.in_base); + w.set_sideset_base(config.pins.sideset_base); + w.set_set_base(config.pins.set_base); + w.set_out_base(config.pins.out_base); + }); + if let Some(origin) = config.origin { + pio_instr_util::exec_jmp(self, origin); + } + } + } +} + impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { #[inline(always)] fn this_sm() -> crate::pac::pio::StateMachine { @@ -504,16 +720,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { unsafe { PIO::PIO.ctrl().read().sm_enable() & (1u8 << SM) != 0 } } - pub fn set_clkdiv(&mut self, div_x_256: u32) { - unsafe { - Self::this_sm().clkdiv().write(|w| w.0 = div_x_256 << 8); - } - } - - pub fn get_clkdiv(&self) -> u32 { - unsafe { Self::this_sm().clkdiv().read().0 >> 8 } - } - pub fn clkdiv_restart(&mut self) { let mask = 1u8 << SM; unsafe { @@ -521,26 +727,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } - /// Configures this state machine to use the given program, including jumping to the origin - /// of the program. The state machine is not started. - pub fn use_program(&mut self, prog: &LoadedProgram<'d, PIO>, side_set: &[&Pin<'d, PIO>]) { - assert!((prog.side_set.bits() - prog.side_set.optional() as u8) as usize == side_set.len()); - assert_consecutive(side_set); - unsafe { - Self::this_sm().execctrl().modify(|w| { - w.set_side_en(prog.side_set.optional()); - w.set_side_pindir(prog.side_set.pindirs()); - w.set_wrap_bottom(prog.wrap.target); - w.set_wrap_top(prog.wrap.source); - }); - Self::this_sm().pinctrl().modify(|w| { - w.set_sideset_count(prog.side_set.bits()); - w.set_sideset_base(side_set.first().map_or(0, |p| p.pin())); - }); - pio_instr_util::exec_jmp(self, prog.origin); - } - } - fn with_paused(&mut self, f: impl FnOnce(&mut Self)) { let enabled = self.is_enabled(); self.set_enable(false); @@ -591,43 +777,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { }); } - pub fn set_jmp_pin(&mut self, pin: u8) { - unsafe { - Self::this_sm().execctrl().modify(|w| w.set_jmp_pin(pin)); - } - } - - pub fn get_jmp_pin(&mut self) -> u8 { - unsafe { Self::this_sm().execctrl().read().jmp_pin() } - } - - pub fn set_fifo_join(&mut self, join: FifoJoin) { - let (rx, tx) = match join { - FifoJoin::Duplex => (false, false), - FifoJoin::RxOnly => (true, false), - FifoJoin::TxOnly => (false, true), - }; - unsafe { - Self::this_sm().shiftctrl().modify(|w| { - w.set_fjoin_rx(rx); - w.set_fjoin_tx(tx) - }); - } - } - pub fn get_fifo_join(&self) -> FifoJoin { - unsafe { - let r = Self::this_sm().shiftctrl().read(); - // Ignores the invalid state when both bits are set - if r.fjoin_rx() { - FifoJoin::RxOnly - } else if r.fjoin_tx() { - FifoJoin::TxOnly - } else { - FifoJoin::Duplex - } - } - } - pub fn clear_fifos(&mut self) { // Toggle FJOIN_RX to flush FIFOs unsafe { @@ -641,159 +790,6 @@ impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> { } } - pub fn set_pull_threshold(&mut self, threshold: u8) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_pull_thresh(threshold)); - } - } - - pub fn get_pull_threshold(&self) -> u8 { - unsafe { Self::this_sm().shiftctrl().read().pull_thresh() } - } - pub fn set_push_threshold(&mut self, threshold: u8) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_push_thresh(threshold)); - } - } - - pub fn get_push_threshold(&self) -> u8 { - unsafe { Self::this_sm().shiftctrl().read().push_thresh() } - } - - pub fn set_out_shift_dir(&mut self, dir: ShiftDirection) { - unsafe { - Self::this_sm() - .shiftctrl() - .modify(|w| w.set_out_shiftdir(dir == ShiftDirection::Right)); - } - } - pub fn get_out_shiftdir(&self) -> ShiftDirection { - unsafe { - if Self::this_sm().shiftctrl().read().out_shiftdir() { - ShiftDirection::Right - } else { - ShiftDirection::Left - } - } - } - - pub fn set_in_shift_dir(&mut self, dir: ShiftDirection) { - unsafe { - Self::this_sm() - .shiftctrl() - .modify(|w| w.set_in_shiftdir(dir == ShiftDirection::Right)); - } - } - pub fn get_in_shiftdir(&self) -> ShiftDirection { - unsafe { - if Self::this_sm().shiftctrl().read().in_shiftdir() { - ShiftDirection::Right - } else { - ShiftDirection::Left - } - } - } - - pub fn set_autopull(&mut self, auto: bool) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_autopull(auto)); - } - } - - pub fn is_autopull(&self) -> bool { - unsafe { Self::this_sm().shiftctrl().read().autopull() } - } - - pub fn set_autopush(&mut self, auto: bool) { - unsafe { - Self::this_sm().shiftctrl().modify(|w| w.set_autopush(auto)); - } - } - - pub fn is_autopush(&self) -> bool { - unsafe { Self::this_sm().shiftctrl().read().autopush() } - } - - pub fn get_addr(&self) -> u8 { - unsafe { Self::this_sm().addr().read().addr() } - } - - /// Set the range of out pins affected by a set instruction. - pub fn set_set_range(&mut self, base: u8, count: u8) { - assert!(base + count < 32); - unsafe { - Self::this_sm().pinctrl().modify(|w| { - w.set_set_base(base); - w.set_set_count(count) - }); - } - } - - /// Get the range of out pins affected by a set instruction. Returns (base, count). - pub fn get_set_range(&self) -> (u8, u8) { - unsafe { - let r = Self::this_sm().pinctrl().read(); - (r.set_base(), r.set_count()) - } - } - - pub fn set_in_base_pin(&mut self, base: &Pin) { - unsafe { - Self::this_sm().pinctrl().modify(|w| w.set_in_base(base.pin())); - } - } - - pub fn get_in_base(&self) -> u8 { - unsafe { - let r = Self::this_sm().pinctrl().read(); - r.in_base() - } - } - - pub fn set_out_range(&mut self, base: u8, count: u8) { - assert!(base + count < 32); - unsafe { - Self::this_sm().pinctrl().modify(|w| { - w.set_out_base(base); - w.set_out_count(count) - }); - } - } - - /// Get the range of out pins affected by a set instruction. Returns (base, count). - pub fn get_out_range(&self) -> (u8, u8) { - unsafe { - let r = Self::this_sm().pinctrl().read(); - (r.out_base(), r.out_count()) - } - } - - pub fn set_out_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { - let count = pins.len(); - assert!(count >= 1); - let start = pins[0].pin() as usize; - assert!(start + pins.len() <= 32); - for i in 0..count { - assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); - } - self.set_out_range(start as u8, count as u8); - } - - pub fn set_set_pins<'a, 'b: 'a>(&'a mut self, pins: &'b [&Pin]) { - let count = pins.len(); - assert!(count >= 1); - let start = pins[0].pin() as usize; - assert!(start + pins.len() <= 32); - for i in 0..count { - assert!(pins[i].pin() as usize == start + i, "Pins must be sequential"); - } - self.set_set_range(start as u8, count as u8); - } - - pub fn get_current_instr() -> u32 { - unsafe { Self::this_sm().instr().read().0 } - } - pub fn exec_instr(&mut self, instr: u16) { unsafe { Self::this_sm().instr().write(|w| w.set_instr(instr)); diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 57f6f5c6..d2829df9 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -22,6 +22,8 @@ lorawan = { version = "0.7.3", default-features = false, features = ["default-cr defmt = "0.3" defmt-rtt = "0.4" +fixed = "1.23.1" +fixed-macro = "1.2" #cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } cortex-m = { version = "0.7.6", features = ["inline-asm"] } diff --git a/examples/rp/src/bin/pio_async.rs b/examples/rp/src/bin/pio_async.rs index 9f47c231..12484e88 100644 --- a/examples/rp/src/bin/pio_async.rs +++ b/examples/rp/src/bin/pio_async.rs @@ -2,10 +2,13 @@ #![no_main] #![feature(type_alias_impl_trait)] use defmt::info; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Common, Irq, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::pio::{Common, Config, Irq, Pio, PioPin, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; +use fixed::traits::ToFixed; +use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, PIO0, 0>, pin: impl PioPin) { @@ -21,15 +24,14 @@ fn setup_pio_task_sm0<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&pio.load_program(&relocated), &[]); + let mut cfg = Config::default(); + cfg.use_program(&pio.load_program(&relocated), &[]); let out_pin = pio.make_pio_pin(pin); - let pio_pins = [&out_pin]; - sm.set_out_pins(&pio_pins); - sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32); - sm.set_set_range(0, 1); - - sm.set_autopull(true); - sm.set_out_shift_dir(ShiftDirection::Left); + cfg.set_out_pins(&[&out_pin]); + cfg.set_set_pins(&[&out_pin]); + cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed(); + cfg.shift_out.auto_fill = true; + sm.set_config(&cfg); } #[embassy_executor::task] @@ -51,11 +53,12 @@ fn setup_pio_task_sm1<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, let prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&pio.load_program(&relocated), &[]); - sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); - sm.set_set_range(0, 0); - sm.set_autopush(true); - sm.set_in_shift_dir(ShiftDirection::Right); + let mut cfg = Config::default(); + cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); + cfg.shift_in.auto_fill = true; + cfg.shift_in.direction = ShiftDirection::Right; + sm.set_config(&cfg); } #[embassy_executor::task] @@ -81,8 +84,10 @@ fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a, ".wrap", ); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&pio.load_program(&relocated), &[]); - sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32); + let mut cfg = Config::default(); + cfg.use_program(&pio.load_program(&relocated), &[]); + cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed(); + sm.set_config(&cfg); } #[embassy_executor::task] diff --git a/examples/rp/src/bin/pio_dma.rs b/examples/rp/src/bin/pio_dma.rs index 1c4e127c..7f85288b 100644 --- a/examples/rp/src/bin/pio_dma.rs +++ b/examples/rp/src/bin/pio_dma.rs @@ -2,11 +2,14 @@ #![no_main] #![feature(type_alias_impl_trait)] use defmt::info; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_futures::join::join; -use embassy_rp::pio::{Pio, ShiftDirection}; +use embassy_rp::pio::{Config, Pio, ShiftConfig, ShiftDirection}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::Peripheral; +use fixed::traits::ToFixed; +use fixed_macro::types::U56F8; use {defmt_rtt as _, panic_probe as _}; fn swap_nibbles(v: u32) -> u32 { @@ -38,15 +41,21 @@ async fn main(_spawner: Spawner) { ); let relocated = RelocatedProgram::new(&prg.program); - sm.use_program(&common.load_program(&relocated), &[]); - sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32); - sm.set_autopull(true); - sm.set_autopush(true); - sm.set_pull_threshold(32); - sm.set_push_threshold(32); - sm.set_out_shift_dir(ShiftDirection::Right); - sm.set_in_shift_dir(ShiftDirection::Left); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[]); + cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed(); + cfg.shift_in = ShiftConfig { + auto_fill: true, + threshold: 32, + direction: ShiftDirection::Left, + }; + cfg.shift_out = ShiftConfig { + auto_fill: true, + threshold: 32, + direction: ShiftDirection::Right, + }; + sm.set_config(&cfg); sm.set_enable(true); let mut dma_out_ref = p.DMA_CH0.into_ref(); diff --git a/examples/rp/src/bin/pio_hd44780.rs b/examples/rp/src/bin/pio_hd44780.rs index 40dee1c4..61c5565d 100644 --- a/examples/rp/src/bin/pio_hd44780.rs +++ b/examples/rp/src/bin/pio_hd44780.rs @@ -4,11 +4,12 @@ use core::fmt::Write; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; use embassy_rp::dma::{AnyChannel, Channel}; use embassy_rp::peripherals::PIO0; -use embassy_rp::pio::{Direction, FifoJoin, Pio, PioPin, ShiftDirection, StateMachine}; -use embassy_rp::pwm::{Config, Pwm}; +use embassy_rp::pio::{Config, Direction, FifoJoin, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; +use embassy_rp::pwm::{self, Pwm}; use embassy_rp::relocate::RelocatedProgram; use embassy_rp::{into_ref, Peripheral, PeripheralRef}; use embassy_time::{Duration, Instant, Timer}; @@ -29,7 +30,7 @@ async fn main(_spawner: Spawner) { let p = embassy_rp::init(Default::default()); let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, { - let mut c = Config::default(); + let mut c = pwm::Config::default(); c.divider = 125.into(); c.top = 100; c.compare_b = 50; @@ -83,7 +84,6 @@ impl<'l> HD44780<'l> { ) -> HD44780<'l> { into_ref!(dma); - let db7pin = db7.pin(); let Pio { mut common, mut irq0, @@ -118,13 +118,17 @@ impl<'l> HD44780<'l> { sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]); let relocated = RelocatedProgram::new(&prg.program); - sm0.use_program(&common.load_program(&relocated), &[&e]); - sm0.set_clkdiv(125 * 256); - sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); - sm0.set_out_shift_dir(ShiftDirection::Left); - sm0.set_fifo_join(FifoJoin::TxOnly); - sm0.set_autopull(true); - sm0.set_pull_threshold(32); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.clock_divider = 125u8.into(); + cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); + cfg.shift_out = ShiftConfig { + auto_fill: true, + direction: ShiftDirection::Left, + threshold: 32, + }; + cfg.fifo_join = FifoJoin::TxOnly; + sm0.set_config(&cfg); sm0.set_enable(true); // init to 8 bit thrice @@ -188,13 +192,15 @@ impl<'l> HD44780<'l> { ); let relocated = RelocatedProgram::new(&prg.program); - sm0.use_program(&common.load_program(&relocated), &[&e]); - sm0.set_clkdiv(8 * 256); // ~64ns/insn - sm0.set_jmp_pin(db7pin); - sm0.set_set_pins(&[&rs, &rw]); - sm0.set_out_pins(&[&db4, &db5, &db6, &db7]); - sm0.set_out_shift_dir(ShiftDirection::Left); - sm0.set_fifo_join(FifoJoin::TxOnly); + let mut cfg = Config::default(); + cfg.use_program(&common.load_program(&relocated), &[&e]); + cfg.clock_divider = 8u8.into(); // ~64ns/insn + cfg.set_jmp_pin(&db7); + cfg.set_set_pins(&[&rs, &rw]); + cfg.set_out_pins(&[&db4, &db5, &db6, &db7]); + cfg.shift_out.direction = ShiftDirection::Left; + cfg.fifo_join = FifoJoin::TxOnly; + sm0.set_config(&cfg); sm0.set_enable(true); diff --git a/examples/rp/src/bin/ws2812-pio.rs b/examples/rp/src/bin/ws2812-pio.rs index 88997054..d7c4742d 100644 --- a/examples/rp/src/bin/ws2812-pio.rs +++ b/examples/rp/src/bin/ws2812-pio.rs @@ -3,10 +3,12 @@ #![feature(type_alias_impl_trait)] use defmt::*; +use embassy_embedded_hal::SetConfig; use embassy_executor::Spawner; -use embassy_rp::pio::{Common, FifoJoin, Instance, Pio, PioPin, ShiftDirection, StateMachine}; +use embassy_rp::pio::{Common, Config, FifoJoin, Instance, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine}; use embassy_rp::relocate::RelocatedProgram; use embassy_time::{Duration, Timer}; +use fixed_macro::fixed; use smart_leds::RGB8; use {defmt_rtt as _, panic_probe as _}; pub struct Ws2812<'d, P: Instance, const S: usize> { @@ -43,35 +45,30 @@ impl<'d, P: Instance, const S: usize> Ws2812<'d, P, S> { a.bind(&mut wrap_source); let prg = a.assemble_with_wrap(wrap_source, wrap_target); + let mut cfg = Config::default(); // Pin config let out_pin = pio.make_pio_pin(pin); let relocated = RelocatedProgram::new(&prg); - sm.use_program(&pio.load_program(&relocated), &[&out_pin]); + cfg.use_program(&pio.load_program(&relocated), &[&out_pin]); - // Clock config + // Clock config, measured in kHz to avoid overflows // TODO CLOCK_FREQ should come from embassy_rp - const CLOCK_FREQ: u32 = 125_000_000; - const WS2812_FREQ: u32 = 800_000; - - let bit_freq = WS2812_FREQ * CYCLES_PER_BIT; - let mut int = CLOCK_FREQ / bit_freq; - let rem = CLOCK_FREQ - (int * bit_freq); - let frac = (rem * 256) / bit_freq; - // 65536.0 is represented as 0 in the pio's clock divider - if int == 65536 { - int = 0; - } - - sm.set_clkdiv((int << 8) | frac); + let clock_freq = fixed!(125_000: U24F8); + let ws2812_freq = fixed!(800: U24F8); + let bit_freq = ws2812_freq * CYCLES_PER_BIT; + cfg.clock_divider = clock_freq / bit_freq; // FIFO config - sm.set_autopull(true); - sm.set_fifo_join(FifoJoin::TxOnly); - sm.set_pull_threshold(24); - sm.set_out_shift_dir(ShiftDirection::Left); + cfg.fifo_join = FifoJoin::TxOnly; + cfg.shift_out = ShiftConfig { + auto_fill: true, + threshold: 24, + direction: ShiftDirection::Left, + }; + sm.set_config(&cfg); sm.set_enable(true); Self { sm }