rp/pio: configure state machines with Config struct

the many individual sets aren't very efficient, and almost no checks
were done to ensure that the configuration written to the hardware was
actually valid. this adresses both of these.
This commit is contained in:
pennae 2023-05-06 11:36:07 +02:00
parent 2873cb93ee
commit 8e4d65e163
6 changed files with 299 additions and 284 deletions

View file

@ -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<U8>,
// 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<u8>,
// 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<PIO>) {
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<PIO>]) {
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<PIO>]) {
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));

View file

@ -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"] }

View file

@ -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]

View file

@ -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();

View file

@ -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);

View file

@ -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 }