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:
parent
2873cb93ee
commit
8e4d65e163
@ -6,9 +6,13 @@ use core::task::{Context, Poll};
|
|||||||
|
|
||||||
use atomic_polyfill::{AtomicU32, AtomicU8};
|
use atomic_polyfill::{AtomicU32, AtomicU8};
|
||||||
use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
|
use embassy_cortex_m::interrupt::{Interrupt, InterruptExt};
|
||||||
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
use embassy_hal_common::{into_ref, Peripheral, PeripheralRef};
|
||||||
use embassy_sync::waitqueue::AtomicWaker;
|
use embassy_sync::waitqueue::AtomicWaker;
|
||||||
|
use fixed::types::extra::U8;
|
||||||
|
use fixed::FixedU32;
|
||||||
use pac::io::vals::Gpio0ctrlFuncsel;
|
use pac::io::vals::Gpio0ctrlFuncsel;
|
||||||
|
use pac::pio::vals::SmExecctrlStatusSel;
|
||||||
use pio::{SideSet, Wrap};
|
use pio::{SideSet, Wrap};
|
||||||
|
|
||||||
use crate::dma::{Channel, Transfer, Word};
|
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]);
|
const PIO_WAKERS_INIT: Wakers = Wakers([NEW_AW; 12]);
|
||||||
static WAKERS: [Wakers; 2] = [PIO_WAKERS_INIT; 2];
|
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 {
|
pub enum FifoJoin {
|
||||||
/// Both TX and RX fifo is enabled
|
/// Both TX and RX fifo is enabled
|
||||||
|
#[default]
|
||||||
Duplex,
|
Duplex,
|
||||||
/// Rx fifo twice as deep. TX fifo disabled
|
/// Rx fifo twice as deep. TX fifo disabled
|
||||||
RxOnly,
|
RxOnly,
|
||||||
@ -48,8 +56,11 @@ pub enum FifoJoin {
|
|||||||
TxOnly,
|
TxOnly,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[repr(u8)]
|
||||||
pub enum ShiftDirection {
|
pub enum ShiftDirection {
|
||||||
|
#[default]
|
||||||
Right = 1,
|
Right = 1,
|
||||||
Left = 0,
|
Left = 0,
|
||||||
}
|
}
|
||||||
@ -62,6 +73,15 @@ pub enum Direction {
|
|||||||
Out = 1,
|
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 RXNEMPTY_MASK: u32 = 1 << 0;
|
||||||
const TXNFULL_MASK: u32 = 1 << 4;
|
const TXNFULL_MASK: u32 = 1 << 4;
|
||||||
const SMIRQ_MASK: u32 = 1 << 8;
|
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> {
|
impl<'d, PIO: Instance + 'd, const SM: usize> StateMachine<'d, PIO, SM> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn this_sm() -> crate::pac::pio::StateMachine {
|
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 }
|
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) {
|
pub fn clkdiv_restart(&mut self) {
|
||||||
let mask = 1u8 << SM;
|
let mask = 1u8 << SM;
|
||||||
unsafe {
|
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)) {
|
fn with_paused(&mut self, f: impl FnOnce(&mut Self)) {
|
||||||
let enabled = self.is_enabled();
|
let enabled = self.is_enabled();
|
||||||
self.set_enable(false);
|
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) {
|
pub fn clear_fifos(&mut self) {
|
||||||
// Toggle FJOIN_RX to flush FIFOs
|
// Toggle FJOIN_RX to flush FIFOs
|
||||||
unsafe {
|
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) {
|
pub fn exec_instr(&mut self, instr: u16) {
|
||||||
unsafe {
|
unsafe {
|
||||||
Self::this_sm().instr().write(|w| w.set_instr(instr));
|
Self::this_sm().instr().write(|w| w.set_instr(instr));
|
||||||
|
@ -22,6 +22,8 @@ lorawan = { version = "0.7.3", default-features = false, features = ["default-cr
|
|||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.4"
|
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 = ["critical-section-single-core"] }
|
||||||
cortex-m = { version = "0.7.6", features = ["inline-asm"] }
|
cortex-m = { version = "0.7.6", features = ["inline-asm"] }
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
use defmt::info;
|
use defmt::info;
|
||||||
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_rp::peripherals::PIO0;
|
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 embassy_rp::relocate::RelocatedProgram;
|
||||||
|
use fixed::traits::ToFixed;
|
||||||
|
use fixed_macro::types::U56F8;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
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) {
|
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);
|
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 out_pin = pio.make_pio_pin(pin);
|
||||||
let pio_pins = [&out_pin];
|
cfg.set_out_pins(&[&out_pin]);
|
||||||
sm.set_out_pins(&pio_pins);
|
cfg.set_set_pins(&[&out_pin]);
|
||||||
sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32);
|
cfg.clock_divider = (U56F8!(125_000_000) / 20 / 200).to_fixed();
|
||||||
sm.set_set_range(0, 1);
|
cfg.shift_out.auto_fill = true;
|
||||||
|
sm.set_config(&cfg);
|
||||||
sm.set_autopull(true);
|
|
||||||
sm.set_out_shift_dir(ShiftDirection::Left);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[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 prg = pio_proc::pio_asm!(".origin 8", "set x, 0x15", ".wrap_target", "in x, 5 [31]", ".wrap",);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
let relocated = RelocatedProgram::new(&prg.program);
|
||||||
sm.use_program(&pio.load_program(&relocated), &[]);
|
let mut cfg = Config::default();
|
||||||
sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32);
|
cfg.use_program(&pio.load_program(&relocated), &[]);
|
||||||
sm.set_set_range(0, 0);
|
cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
|
||||||
sm.set_autopush(true);
|
cfg.shift_in.auto_fill = true;
|
||||||
sm.set_in_shift_dir(ShiftDirection::Right);
|
cfg.shift_in.direction = ShiftDirection::Right;
|
||||||
|
sm.set_config(&cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
@ -81,8 +84,10 @@ fn setup_pio_task_sm2<'a>(pio: &mut Common<'a, PIO0>, sm: &mut StateMachine<'a,
|
|||||||
".wrap",
|
".wrap",
|
||||||
);
|
);
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
let relocated = RelocatedProgram::new(&prg.program);
|
||||||
sm.use_program(&pio.load_program(&relocated), &[]);
|
let mut cfg = Config::default();
|
||||||
sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32);
|
cfg.use_program(&pio.load_program(&relocated), &[]);
|
||||||
|
cfg.clock_divider = (U56F8!(125_000_000) / 2000).to_fixed();
|
||||||
|
sm.set_config(&cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
|
@ -2,11 +2,14 @@
|
|||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
use defmt::info;
|
use defmt::info;
|
||||||
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_futures::join::join;
|
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::relocate::RelocatedProgram;
|
||||||
use embassy_rp::Peripheral;
|
use embassy_rp::Peripheral;
|
||||||
|
use fixed::traits::ToFixed;
|
||||||
|
use fixed_macro::types::U56F8;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
fn swap_nibbles(v: u32) -> u32 {
|
fn swap_nibbles(v: u32) -> u32 {
|
||||||
@ -38,15 +41,21 @@ async fn main(_spawner: Spawner) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
let relocated = RelocatedProgram::new(&prg.program);
|
||||||
sm.use_program(&common.load_program(&relocated), &[]);
|
let mut cfg = Config::default();
|
||||||
sm.set_clkdiv((125e6 / 10e3 * 256.0) as u32);
|
cfg.use_program(&common.load_program(&relocated), &[]);
|
||||||
sm.set_autopull(true);
|
cfg.clock_divider = (U56F8!(125_000_000) / U56F8!(10_000)).to_fixed();
|
||||||
sm.set_autopush(true);
|
cfg.shift_in = ShiftConfig {
|
||||||
sm.set_pull_threshold(32);
|
auto_fill: true,
|
||||||
sm.set_push_threshold(32);
|
threshold: 32,
|
||||||
sm.set_out_shift_dir(ShiftDirection::Right);
|
direction: ShiftDirection::Left,
|
||||||
sm.set_in_shift_dir(ShiftDirection::Left);
|
};
|
||||||
|
cfg.shift_out = ShiftConfig {
|
||||||
|
auto_fill: true,
|
||||||
|
threshold: 32,
|
||||||
|
direction: ShiftDirection::Right,
|
||||||
|
};
|
||||||
|
|
||||||
|
sm.set_config(&cfg);
|
||||||
sm.set_enable(true);
|
sm.set_enable(true);
|
||||||
|
|
||||||
let mut dma_out_ref = p.DMA_CH0.into_ref();
|
let mut dma_out_ref = p.DMA_CH0.into_ref();
|
||||||
|
@ -4,11 +4,12 @@
|
|||||||
|
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_executor::Spawner;
|
use embassy_executor::Spawner;
|
||||||
use embassy_rp::dma::{AnyChannel, Channel};
|
use embassy_rp::dma::{AnyChannel, Channel};
|
||||||
use embassy_rp::peripherals::PIO0;
|
use embassy_rp::peripherals::PIO0;
|
||||||
use embassy_rp::pio::{Direction, FifoJoin, Pio, PioPin, ShiftDirection, StateMachine};
|
use embassy_rp::pio::{Config, Direction, FifoJoin, Pio, PioPin, ShiftConfig, ShiftDirection, StateMachine};
|
||||||
use embassy_rp::pwm::{Config, Pwm};
|
use embassy_rp::pwm::{self, Pwm};
|
||||||
use embassy_rp::relocate::RelocatedProgram;
|
use embassy_rp::relocate::RelocatedProgram;
|
||||||
use embassy_rp::{into_ref, Peripheral, PeripheralRef};
|
use embassy_rp::{into_ref, Peripheral, PeripheralRef};
|
||||||
use embassy_time::{Duration, Instant, Timer};
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
@ -29,7 +30,7 @@ async fn main(_spawner: Spawner) {
|
|||||||
let p = embassy_rp::init(Default::default());
|
let p = embassy_rp::init(Default::default());
|
||||||
|
|
||||||
let _pwm = Pwm::new_output_b(p.PWM_CH7, p.PIN_15, {
|
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.divider = 125.into();
|
||||||
c.top = 100;
|
c.top = 100;
|
||||||
c.compare_b = 50;
|
c.compare_b = 50;
|
||||||
@ -83,7 +84,6 @@ impl<'l> HD44780<'l> {
|
|||||||
) -> HD44780<'l> {
|
) -> HD44780<'l> {
|
||||||
into_ref!(dma);
|
into_ref!(dma);
|
||||||
|
|
||||||
let db7pin = db7.pin();
|
|
||||||
let Pio {
|
let Pio {
|
||||||
mut common,
|
mut common,
|
||||||
mut irq0,
|
mut irq0,
|
||||||
@ -118,13 +118,17 @@ impl<'l> HD44780<'l> {
|
|||||||
sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
|
sm0.set_pin_dirs(Direction::Out, &[&rs, &rw, &e, &db4, &db5, &db6, &db7]);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
let relocated = RelocatedProgram::new(&prg.program);
|
||||||
sm0.use_program(&common.load_program(&relocated), &[&e]);
|
let mut cfg = Config::default();
|
||||||
sm0.set_clkdiv(125 * 256);
|
cfg.use_program(&common.load_program(&relocated), &[&e]);
|
||||||
sm0.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
cfg.clock_divider = 125u8.into();
|
||||||
sm0.set_out_shift_dir(ShiftDirection::Left);
|
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
||||||
sm0.set_fifo_join(FifoJoin::TxOnly);
|
cfg.shift_out = ShiftConfig {
|
||||||
sm0.set_autopull(true);
|
auto_fill: true,
|
||||||
sm0.set_pull_threshold(32);
|
direction: ShiftDirection::Left,
|
||||||
|
threshold: 32,
|
||||||
|
};
|
||||||
|
cfg.fifo_join = FifoJoin::TxOnly;
|
||||||
|
sm0.set_config(&cfg);
|
||||||
|
|
||||||
sm0.set_enable(true);
|
sm0.set_enable(true);
|
||||||
// init to 8 bit thrice
|
// init to 8 bit thrice
|
||||||
@ -188,13 +192,15 @@ impl<'l> HD44780<'l> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg.program);
|
let relocated = RelocatedProgram::new(&prg.program);
|
||||||
sm0.use_program(&common.load_program(&relocated), &[&e]);
|
let mut cfg = Config::default();
|
||||||
sm0.set_clkdiv(8 * 256); // ~64ns/insn
|
cfg.use_program(&common.load_program(&relocated), &[&e]);
|
||||||
sm0.set_jmp_pin(db7pin);
|
cfg.clock_divider = 8u8.into(); // ~64ns/insn
|
||||||
sm0.set_set_pins(&[&rs, &rw]);
|
cfg.set_jmp_pin(&db7);
|
||||||
sm0.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
cfg.set_set_pins(&[&rs, &rw]);
|
||||||
sm0.set_out_shift_dir(ShiftDirection::Left);
|
cfg.set_out_pins(&[&db4, &db5, &db6, &db7]);
|
||||||
sm0.set_fifo_join(FifoJoin::TxOnly);
|
cfg.shift_out.direction = ShiftDirection::Left;
|
||||||
|
cfg.fifo_join = FifoJoin::TxOnly;
|
||||||
|
sm0.set_config(&cfg);
|
||||||
|
|
||||||
sm0.set_enable(true);
|
sm0.set_enable(true);
|
||||||
|
|
||||||
|
@ -3,10 +3,12 @@
|
|||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use defmt::*;
|
use defmt::*;
|
||||||
|
use embassy_embedded_hal::SetConfig;
|
||||||
use embassy_executor::Spawner;
|
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_rp::relocate::RelocatedProgram;
|
||||||
use embassy_time::{Duration, Timer};
|
use embassy_time::{Duration, Timer};
|
||||||
|
use fixed_macro::fixed;
|
||||||
use smart_leds::RGB8;
|
use smart_leds::RGB8;
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
pub struct Ws2812<'d, P: Instance, const S: usize> {
|
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);
|
a.bind(&mut wrap_source);
|
||||||
|
|
||||||
let prg = a.assemble_with_wrap(wrap_source, wrap_target);
|
let prg = a.assemble_with_wrap(wrap_source, wrap_target);
|
||||||
|
let mut cfg = Config::default();
|
||||||
|
|
||||||
// Pin config
|
// Pin config
|
||||||
let out_pin = pio.make_pio_pin(pin);
|
let out_pin = pio.make_pio_pin(pin);
|
||||||
|
|
||||||
let relocated = RelocatedProgram::new(&prg);
|
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
|
// TODO CLOCK_FREQ should come from embassy_rp
|
||||||
const CLOCK_FREQ: u32 = 125_000_000;
|
let clock_freq = fixed!(125_000: U24F8);
|
||||||
const WS2812_FREQ: u32 = 800_000;
|
let ws2812_freq = fixed!(800: U24F8);
|
||||||
|
let bit_freq = ws2812_freq * CYCLES_PER_BIT;
|
||||||
let bit_freq = WS2812_FREQ * CYCLES_PER_BIT;
|
cfg.clock_divider = clock_freq / bit_freq;
|
||||||
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);
|
|
||||||
|
|
||||||
// FIFO config
|
// FIFO config
|
||||||
sm.set_autopull(true);
|
cfg.fifo_join = FifoJoin::TxOnly;
|
||||||
sm.set_fifo_join(FifoJoin::TxOnly);
|
cfg.shift_out = ShiftConfig {
|
||||||
sm.set_pull_threshold(24);
|
auto_fill: true,
|
||||||
sm.set_out_shift_dir(ShiftDirection::Left);
|
threshold: 24,
|
||||||
|
direction: ShiftDirection::Left,
|
||||||
|
};
|
||||||
|
|
||||||
|
sm.set_config(&cfg);
|
||||||
sm.set_enable(true);
|
sm.set_enable(true);
|
||||||
|
|
||||||
Self { sm }
|
Self { sm }
|
||||||
|
Loading…
Reference in New Issue
Block a user