1043: Rpi Pico PIO driver r=Dirbaio a=fluffware

This is a driver for the two PIOs in the Pico. I've tried using the type system to get as much compile time checks as possible. There is asynchronous support for FIFOs an Irqs. No DMA support yet. There's an example that shows how to use the driver asynchronously.

1103: embassy-stm32: add rs485 driver enable to uart r=Dirbaio a=FrozenDroid



Co-authored-by: Simon Berg <ksb@fluffware.se>
Co-authored-by: Vincent Stakenburg <v.stakenburg@sinewave.nl>
This commit is contained in:
bors[bot] 2022-12-13 11:47:27 +00:00 committed by GitHub
commit 36639e5262
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1735 additions and 7 deletions

View File

@ -26,6 +26,7 @@ time-driver = []
rom-func-cache = [] rom-func-cache = []
intrinsics = [] intrinsics = []
rom-v2-intrinsics = [] rom-v2-intrinsics = []
pio = ["dep:pio", "dep:pio-proc"]
# Enable nightly-only features # Enable nightly-only features
nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"] nightly = ["embassy-executor/nightly", "embedded-hal-1", "embedded-hal-async", "embassy-embedded-hal/nightly", "dep:embassy-usb-driver", "dep:embedded-io"]
@ -64,3 +65,10 @@ embedded-hal-02 = { package = "embedded-hal", version = "0.2.6", features = ["un
embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true} embedded-hal-1 = { package = "embedded-hal", version = "=1.0.0-alpha.9", optional = true}
embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true} embedded-hal-async = { version = "=0.2.0-alpha.0", optional = true}
embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true} embedded-hal-nb = { version = "=1.0.0-alpha.1", optional = true}
paste = "1.0"
pio-proc = {version= "0.2", optional = true}
pio = {version= "0.2", optional = true}
[patch.crates-io]
pio = {git = "https://github.com/rp-rs/pio-rs.git"}

View File

@ -48,6 +48,21 @@ pub enum Pull {
Down, Down,
} }
/// Drive strength of an output
#[derive(Debug, Eq, PartialEq)]
pub enum Drive {
_2mA,
_4mA,
_8mA,
_12mA,
}
/// Slew rate of an output
#[derive(Debug, Eq, PartialEq)]
pub enum SlewRate {
Fast,
Slow,
}
/// A GPIO bank with up to 32 pins. /// A GPIO bank with up to 32 pins.
#[derive(Debug, Eq, PartialEq)] #[derive(Debug, Eq, PartialEq)]
pub enum Bank { pub enum Bank {
@ -459,13 +474,40 @@ impl<'d, T: Pin> Flex<'d, T> {
#[inline] #[inline]
pub fn set_pull(&mut self, pull: Pull) { pub fn set_pull(&mut self, pull: Pull) {
unsafe { unsafe {
self.pin.pad_ctrl().write(|w| { self.pin.pad_ctrl().modify(|w| {
w.set_ie(true); w.set_ie(true);
match pull { let (pu, pd) = match pull {
Pull::Up => w.set_pue(true), Pull::Up => (true, false),
Pull::Down => w.set_pde(true), Pull::Down => (false, true),
Pull::None => {} Pull::None => (false, false),
} };
w.set_pue(pu);
w.set_pde(pd);
});
}
}
/// Set the pin's drive strength.
#[inline]
pub fn set_drive_strength(&mut self, strength: Drive) {
unsafe {
self.pin.pad_ctrl().modify(|w| {
w.set_drive(match strength {
Drive::_2mA => pac::pads::vals::Drive::_2MA,
Drive::_4mA => pac::pads::vals::Drive::_4MA,
Drive::_8mA => pac::pads::vals::Drive::_8MA,
Drive::_12mA => pac::pads::vals::Drive::_12MA,
});
});
}
}
// Set the pin's slew rate.
#[inline]
pub fn set_slew_rate(&mut self, slew_rate: SlewRate) {
unsafe {
self.pin.pad_ctrl().modify(|w| {
w.set_slewfast(slew_rate == SlewRate::Fast);
}); });
} }
} }

View File

@ -12,6 +12,14 @@ pub mod dma;
pub mod gpio; pub mod gpio;
pub mod i2c; pub mod i2c;
pub mod interrupt; pub mod interrupt;
#[cfg(feature = "pio")]
pub mod pio;
#[cfg(feature = "pio")]
pub mod pio_instr_util;
#[cfg(feature = "pio")]
pub mod relocate;
pub mod rom_data; pub mod rom_data;
pub mod rtc; pub mod rtc;
pub mod spi; pub mod spi;
@ -102,6 +110,9 @@ embassy_hal_common::peripherals! {
FLASH, FLASH,
ADC, ADC,
PIO0,
PIO1,
} }
#[link_section = ".boot2"] #[link_section = ".boot2"]

1259
embassy-rp/src/pio.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,90 @@
use pio::{InSource, InstructionOperands, JmpCondition, OutDestination, SetDestination};
use crate::pio::PioStateMachine;
pub fn set_x<SM: PioStateMachine>(sm: &mut SM, value: u32) {
const OUT: u16 = InstructionOperands::OUT {
destination: OutDestination::X,
bit_count: 32,
}
.encode();
sm.push_tx(value);
sm.exec_instr(OUT);
}
pub fn get_x<SM: PioStateMachine>(sm: &mut SM) -> u32 {
const IN: u16 = InstructionOperands::IN {
source: InSource::X,
bit_count: 32,
}
.encode();
sm.exec_instr(IN);
sm.pull_rx()
}
pub fn set_y<SM: PioStateMachine>(sm: &mut SM, value: u32) {
const OUT: u16 = InstructionOperands::OUT {
destination: OutDestination::Y,
bit_count: 32,
}
.encode();
sm.push_tx(value);
sm.exec_instr(OUT);
}
pub fn get_y<SM: PioStateMachine>(sm: &mut SM) -> u32 {
const IN: u16 = InstructionOperands::IN {
source: InSource::Y,
bit_count: 32,
}
.encode();
sm.exec_instr(IN);
sm.pull_rx()
}
pub fn set_pindir<SM: PioStateMachine>(sm: &mut SM, data: u8) {
let set: u16 = InstructionOperands::SET {
destination: SetDestination::PINDIRS,
data,
}
.encode();
sm.exec_instr(set);
}
pub fn set_pin<SM: PioStateMachine>(sm: &mut SM, data: u8) {
let set: u16 = InstructionOperands::SET {
destination: SetDestination::PINS,
data,
}
.encode();
sm.exec_instr(set);
}
pub fn set_out_pin<SM: PioStateMachine>(sm: &mut SM, data: u32) {
const OUT: u16 = InstructionOperands::OUT {
destination: OutDestination::PINS,
bit_count: 32,
}
.encode();
sm.push_tx(data);
sm.exec_instr(OUT);
}
pub fn set_out_pindir<SM: PioStateMachine>(sm: &mut SM, data: u32) {
const OUT: u16 = InstructionOperands::OUT {
destination: OutDestination::PINDIRS,
bit_count: 32,
}
.encode();
sm.push_tx(data);
sm.exec_instr(OUT);
}
pub fn exec_jmp<SM: PioStateMachine>(sm: &mut SM, to_addr: u8) {
let jmp: u16 = InstructionOperands::JMP {
address: to_addr,
condition: JmpCondition::Always,
}
.encode();
sm.exec_instr(jmp);
}

View File

@ -0,0 +1,77 @@
use core::iter::Iterator;
use pio::{Program, SideSet, Wrap};
pub struct CodeIterator<'a, I>
where
I: Iterator<Item = &'a u16>,
{
iter: I,
offset: u8,
}
impl<'a, I: Iterator<Item = &'a u16>> CodeIterator<'a, I> {
pub fn new(iter: I, offset: u8) -> CodeIterator<'a, I> {
CodeIterator { iter, offset }
}
}
impl<'a, I> Iterator for CodeIterator<'a, I>
where
I: Iterator<Item = &'a u16>,
{
type Item = u16;
fn next(&mut self) -> Option<Self::Item> {
self.iter.next().and_then(|&instr| {
Some(if instr & 0b1110_0000_0000_0000 == 0 {
// this is a JMP instruction -> add offset to address
let address = (instr & 0b1_1111) as u8;
let address = address + self.offset;
assert!(
address < pio::RP2040_MAX_PROGRAM_SIZE as u8,
"Invalid JMP out of the program after offset addition"
);
instr & (!0b11111) | address as u16
} else {
instr
})
})
}
}
pub struct RelocatedProgram<'a, const PROGRAM_SIZE: usize> {
program: &'a Program<PROGRAM_SIZE>,
origin: u8,
}
impl<'a, const PROGRAM_SIZE: usize> RelocatedProgram<'a, PROGRAM_SIZE> {
pub fn new(program: &Program<PROGRAM_SIZE>) -> RelocatedProgram<PROGRAM_SIZE> {
let origin = program.origin.unwrap_or(0);
RelocatedProgram { program, origin }
}
pub fn new_with_origin(program: &Program<PROGRAM_SIZE>, origin: u8) -> RelocatedProgram<PROGRAM_SIZE> {
RelocatedProgram { program, origin }
}
pub fn code(&'a self) -> CodeIterator<'a, core::slice::Iter<'a, u16>> {
CodeIterator::new(self.program.code.iter(), self.origin)
}
pub fn wrap(&self) -> Wrap {
let wrap = self.program.wrap;
let origin = self.origin;
Wrap {
source: wrap.source + origin,
target: wrap.target + origin,
}
}
pub fn side_set(&self) -> SideSet {
self.program.side_set
}
pub fn origin(&self) -> u8 {
self.origin
}
}

View File

@ -244,11 +244,13 @@ fn main() {
(("usart", "CTS"), quote!(crate::usart::CtsPin)), (("usart", "CTS"), quote!(crate::usart::CtsPin)),
(("usart", "RTS"), quote!(crate::usart::RtsPin)), (("usart", "RTS"), quote!(crate::usart::RtsPin)),
(("usart", "CK"), quote!(crate::usart::CkPin)), (("usart", "CK"), quote!(crate::usart::CkPin)),
(("usart", "DE"), quote!(crate::usart::DePin)),
(("lpuart", "TX"), quote!(crate::usart::TxPin)), (("lpuart", "TX"), quote!(crate::usart::TxPin)),
(("lpuart", "RX"), quote!(crate::usart::RxPin)), (("lpuart", "RX"), quote!(crate::usart::RxPin)),
(("lpuart", "CTS"), quote!(crate::usart::CtsPin)), (("lpuart", "CTS"), quote!(crate::usart::CtsPin)),
(("lpuart", "RTS"), quote!(crate::usart::RtsPin)), (("lpuart", "RTS"), quote!(crate::usart::RtsPin)),
(("lpuart", "CK"), quote!(crate::usart::CkPin)), (("lpuart", "CK"), quote!(crate::usart::CkPin)),
(("lpuart", "DE"), quote!(crate::usart::DePin)),
(("spi", "SCK"), quote!(crate::spi::SckPin)), (("spi", "SCK"), quote!(crate::spi::SckPin)),
(("spi", "MOSI"), quote!(crate::spi::MosiPin)), (("spi", "MOSI"), quote!(crate::spi::MosiPin)),
(("spi", "MISO"), quote!(crate::spi::MisoPin)), (("spi", "MISO"), quote!(crate::spi::MisoPin)),

View File

@ -89,6 +89,33 @@ impl<'d, T: BasicInstance> BufferedUart<'d, T> {
Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config) Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config)
} }
#[cfg(not(usart_v1))]
pub fn new_with_de(
state: &'d mut State<'d, T>,
peri: impl Peripheral<P = T> + 'd,
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
de: impl Peripheral<P = impl DePin<T>> + 'd,
tx_buffer: &'d mut [u8],
rx_buffer: &'d mut [u8],
config: Config,
) -> BufferedUart<'d, T> {
into_ref!(de);
T::enable();
T::reset();
unsafe {
de.set_as_af(de.af_num(), AFType::OutputPushPull);
T::regs().cr3().write(|w| {
w.set_dem(true);
});
}
Self::new_inner(state, peri, rx, tx, irq, tx_buffer, rx_buffer, config)
}
fn new_inner( fn new_inner(
state: &'d mut State<'d, T>, state: &'d mut State<'d, T>,
_peri: impl Peripheral<P = T> + 'd, _peri: impl Peripheral<P = T> + 'd,

View File

@ -646,6 +646,31 @@ impl<'d, T: BasicInstance, TxDma, RxDma> Uart<'d, T, TxDma, RxDma> {
Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config) Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config)
} }
#[cfg(not(usart_v1))]
pub fn new_with_de(
peri: impl Peripheral<P = T> + 'd,
rx: impl Peripheral<P = impl RxPin<T>> + 'd,
tx: impl Peripheral<P = impl TxPin<T>> + 'd,
irq: impl Peripheral<P = T::Interrupt> + 'd,
de: impl Peripheral<P = impl DePin<T>> + 'd,
tx_dma: impl Peripheral<P = TxDma> + 'd,
rx_dma: impl Peripheral<P = RxDma> + 'd,
config: Config,
) -> Self {
into_ref!(de);
T::enable();
T::reset();
unsafe {
de.set_as_af(de.af_num(), AFType::OutputPushPull);
T::regs().cr3().write(|w| {
w.set_dem(true);
});
}
Self::new_inner(peri, rx, tx, irq, tx_dma, rx_dma, config)
}
fn new_inner( fn new_inner(
peri: impl Peripheral<P = T> + 'd, peri: impl Peripheral<P = T> + 'd,
rx: impl Peripheral<P = impl RxPin<T>> + 'd, rx: impl Peripheral<P = impl RxPin<T>> + 'd,
@ -1040,6 +1065,7 @@ pin_trait!(TxPin, BasicInstance);
pin_trait!(CtsPin, BasicInstance); pin_trait!(CtsPin, BasicInstance);
pin_trait!(RtsPin, BasicInstance); pin_trait!(RtsPin, BasicInstance);
pin_trait!(CkPin, BasicInstance); pin_trait!(CkPin, BasicInstance);
pin_trait!(DePin, BasicInstance);
dma_trait!(TxDma, BasicInstance); dma_trait!(TxDma, BasicInstance);
dma_trait!(RxDma, BasicInstance); dma_trait!(RxDma, BasicInstance);

View File

@ -9,7 +9,7 @@ license = "MIT OR Apache-2.0"
embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] } embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = ["defmt"] }
embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] } embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = ["defmt", "integrated-timers"] }
embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] } embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime"] }
embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver"] } embassy-rp = { version = "0.1.0", path = "../../embassy-rp", features = ["defmt", "unstable-traits", "nightly", "unstable-pac", "time-driver", "pio"] }
embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] }
embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] } embassy-net = { version = "0.1.0", path = "../../embassy-net", features = ["defmt", "nightly", "tcp", "dhcpv4", "medium-ethernet", "pool-16"] }
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
@ -34,6 +34,11 @@ embedded-io = { version = "0.4.0", features = ["async", "defmt"] }
embedded-storage = { version = "0.3" } embedded-storage = { version = "0.3" }
static_cell = "1.0.0" static_cell = "1.0.0"
log = "0.4" log = "0.4"
pio-proc = "0.2"
pio = "0.2"
[profile.release] [profile.release]
debug = true debug = true
[patch.crates-io]
pio = {git = "https://github.com/rp-rs/pio-rs.git"}

View File

@ -0,0 +1,112 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_executor::Spawner;
use embassy_rp::gpio::{AnyPin, Pin};
use embassy_rp::pio::{Pio0, PioPeripherial, PioStateMachine, PioStateMachineInstance, ShiftDirection, Sm0, Sm1, Sm2};
use embassy_rp::pio_instr_util;
use embassy_rp::relocate::RelocatedProgram;
use {defmt_rtt as _, panic_probe as _};
#[embassy_executor::task]
async fn pio_task_sm0(mut sm: PioStateMachineInstance<Pio0, Sm0>, pin: AnyPin) {
// Setup sm0
// Send data serially to pin
let prg = pio_proc::pio_asm!(
".origin 16",
"set pindirs, 1",
".wrap_target",
"out pins,1 [19]",
".wrap",
);
let relocated = RelocatedProgram::new(&prg.program);
let out_pin = sm.make_pio_pin(pin);
let pio_pins = [&out_pin];
sm.set_out_pins(&pio_pins);
sm.write_instr(relocated.origin() as usize, relocated.code());
pio_instr_util::exec_jmp(&mut sm, relocated.origin());
sm.set_clkdiv((125e6 / 20.0 / 2e2 * 256.0) as u32);
sm.set_set_range(0, 1);
let pio::Wrap { source, target } = relocated.wrap();
sm.set_wrap(source, target);
sm.set_autopull(true);
sm.set_out_shift_dir(ShiftDirection::Left);
sm.set_enable(true);
let mut v = 0x0f0caffa;
loop {
sm.wait_push(v).await;
v ^= 0xffff;
info!("Pushed {:032b} to FIFO", v);
}
}
#[embassy_executor::task]
async fn pio_task_sm1(mut sm: PioStateMachineInstance<Pio0, Sm1>) {
// Setupm sm1
// Read 0b10101 repeatedly until ISR is full
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.write_instr(relocated.origin() as usize, relocated.code());
pio_instr_util::exec_jmp(&mut sm, relocated.origin());
sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32);
sm.set_set_range(0, 0);
let pio::Wrap { source, target } = relocated.wrap();
sm.set_wrap(source, target);
sm.set_autopush(true);
sm.set_in_shift_dir(ShiftDirection::Right);
sm.set_enable(true);
loop {
let rx = sm.wait_pull().await;
info!("Pulled {:032b} from FIFO", rx);
}
}
#[embassy_executor::task]
async fn pio_task_sm2(mut sm: PioStateMachineInstance<Pio0, Sm2>) {
// Setup sm2
// Repeatedly trigger IRQ 3
let prg = pio_proc::pio_asm!(
".origin 0",
".wrap_target",
"set x,10",
"delay:",
"jmp x-- delay [15]",
"irq 3 [15]",
".wrap",
);
let relocated = RelocatedProgram::new(&prg.program);
sm.write_instr(relocated.origin() as usize, relocated.code());
let pio::Wrap { source, target } = relocated.wrap();
sm.set_wrap(source, target);
pio_instr_util::exec_jmp(&mut sm, relocated.origin());
sm.set_clkdiv((125e6 / 2e3 * 256.0) as u32);
sm.set_enable(true);
loop {
sm.wait_irq(3).await;
info!("IRQ trigged");
}
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let pio = p.PIO0;
let (_, sm0, sm1, sm2, ..) = pio.split();
spawner.spawn(pio_task_sm0(sm0, p.PIN_0.degrade())).unwrap();
spawner.spawn(pio_task_sm1(sm1)).unwrap();
spawner.spawn(pio_task_sm2(sm2)).unwrap();
}

View File

@ -0,0 +1,69 @@
#![no_std]
#![no_main]
#![feature(type_alias_impl_trait)]
use defmt::info;
use embassy_executor::Spawner;
use embassy_futures::join::join;
use embassy_rp::pio::{PioPeripherial, PioStateMachine, ShiftDirection};
use embassy_rp::relocate::RelocatedProgram;
use embassy_rp::{pio_instr_util, Peripheral};
use {defmt_rtt as _, panic_probe as _};
fn swap_nibbles(v: u32) -> u32 {
let v = (v & 0x0f0f_0f0f) << 4 | (v & 0xf0f0_f0f0) >> 4;
let v = (v & 0x00ff_00ff) << 8 | (v & 0xff00_ff00) >> 8;
(v & 0x0000_ffff) << 16 | (v & 0xffff_0000) >> 16
}
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_rp::init(Default::default());
let pio = p.PIO0;
let (_, mut sm, ..) = pio.split();
let prg = pio_proc::pio_asm!(
".origin 0",
"set pindirs,1",
".wrap_target",
"set y,7",
"loop:",
"out x,4",
"in x,4",
"jmp y--, loop",
".wrap",
);
let relocated = RelocatedProgram::new(&prg.program);
sm.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();
sm.set_wrap(source, target);
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);
sm.set_enable(true);
let mut dma_out_ref = p.DMA_CH0.into_ref();
let mut dma_in_ref = p.DMA_CH1.into_ref();
let mut dout = [0x12345678u32; 29];
for i in 1..dout.len() {
dout[i] = (dout[i - 1] & 0x0fff_ffff) * 13 + 7;
}
let mut din = [0u32; 29];
loop {
join(
sm.dma_push(dma_out_ref.reborrow(), &dout),
sm.dma_pull(dma_in_ref.reborrow(), &mut din),
)
.await;
for i in 0..din.len() {
assert_eq!(din[i], swap_nibbles(dout[i]));
}
info!("Swapped {} words", dout.len());
}
}