pio: Improve documentation and add an example that uses pio_proc::pio!().

This commit is contained in:
Mathias Gottschlag 2021-09-28 21:48:05 +02:00
parent 515eac5553
commit 684f483859
6 changed files with 112 additions and 11 deletions

View file

@ -1,4 +1,5 @@
[workspace] [workspace]
resolver = "2"
members = [ members = [
"rp2040-hal", "rp2040-hal",
"boards/feather_rp2040", "boards/feather_rp2040",

View file

@ -18,6 +18,7 @@ nb = "1.0"
rp2040-pac = "0.1.5" rp2040-pac = "0.1.5"
paste = "1.0" paste = "1.0"
pio = { git = "https://github.com/rp-rs/pio-rs.git", branch = "main" } pio = { git = "https://github.com/rp-rs/pio-rs.git", branch = "main" }
pio-proc = { git = "https://github.com/rp-rs/pio-rs.git", branch = "main" }
usb-device = "0.2.8" usb-device = "0.2.8"
vcell = "0.1" vcell = "0.1"
void = { version = "1.0.2", default-features = false } void = { version = "1.0.2", default-features = false }

View file

@ -7,6 +7,7 @@
use cortex_m_rt::entry; use cortex_m_rt::entry;
use hal::gpio::{FunctionPio0, Pin}; use hal::gpio::{FunctionPio0, Pin};
use hal::pac; use hal::pac;
use hal::pio::PIOExt;
use hal::sio::Sio; use hal::sio::Sio;
use panic_halt as _; use panic_halt as _;
use rp2040_hal as hal; use rp2040_hal as hal;
@ -53,15 +54,14 @@ fn main() -> ! {
let program = a.assemble_with_wrap(wrap_source, wrap_target); let program = a.assemble_with_wrap(wrap_source, wrap_target);
// Initialize and start PIO // Initialize and start PIO
let pio = rp2040_hal::pio::PIO::new(pac.PIO0, &mut pac.RESETS); let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
let sm = &pio.state_machines()[0]; let installed = pio.install(&program).unwrap();
let div = 0f32; // as slow as possible (0 is interpreted as 65536) let div = 0f32; // as slow as possible (0 is interpreted as 65536)
rp2040_hal::pio::PIOBuilder::default() let sm = rp2040_hal::pio::PIOBuilder::from_program(installed)
.with_program(&program)
.set_pins(led_pin_id, 1) .set_pins(led_pin_id, 1)
.clock_divisor(div) .clock_divisor(div)
.build(&pio, sm) .build(sm0);
.unwrap(); sm.start();
// PIO runs in background, independently from CPU // PIO runs in background, independently from CPU
#[allow(clippy::empty_loop)] #[allow(clippy::empty_loop)]

View file

@ -0,0 +1,62 @@
//! This example toggles the GPIO25 pin, using a PIO program compiled via pio_proc::pio!().
//!
//! If a LED is connected to that pin, like on a Pico board, the LED should blink.
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use hal::gpio::{FunctionPio0, Pin};
use hal::pac;
use hal::pio::PIOExt;
use hal::sio::Sio;
use panic_halt as _;
use rp2040_hal as hal;
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER;
#[entry]
fn main() -> ! {
let mut pac = pac::Peripherals::take().unwrap();
let sio = Sio::new(pac.SIO);
let pins = hal::gpio::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);
// configure LED pin for Pio0.
let _led: Pin<_, FunctionPio0> = pins.gpio25.into_mode();
// PIN id for use inside of PIO
let led_pin_id = 25;
// Define some simple PIO program.
let program = pio_proc::pio!(
32,
"
.wrap_target
set pins, 1 [31]
set pins, 0 [31]
.wrap
"
);
// Initialize and start PIO
let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
let installed = pio.install(&program.program).unwrap();
let div = 0f32; // as slow as possible (0 is interpreted as 65536)
let mut sm = rp2040_hal::pio::PIOBuilder::from_program(installed)
.set_pins(led_pin_id, 1)
.clock_divisor(div)
.build(sm0);
// The GPIO pin needs to be configured as an output.
sm.set_pindirs_with_mask(1 << led_pin_id, 1 << led_pin_id);
sm.start();
// PIO runs in background, independently from CPU
#[allow(clippy::empty_loop)]
loop {}
}

View file

@ -96,6 +96,8 @@ unsafe impl<P: PIOExt + Send> Send for PIO<P> {}
impl<P: PIOExt> PIO<P> { impl<P: PIOExt> PIO<P> {
/// Free this instance. /// Free this instance.
///
/// All output pins are left in their current state.
pub fn free( pub fn free(
self, self,
_sm0: UninitStateMachine<P>, _sm0: UninitStateMachine<P>,
@ -103,7 +105,7 @@ impl<P: PIOExt> PIO<P> {
_sm2: UninitStateMachine<P>, _sm2: UninitStateMachine<P>,
_sm3: UninitStateMachine<P>, _sm3: UninitStateMachine<P>,
) -> P { ) -> P {
// TODO: Disable the PIO block. // All state machines have already been stopped.
self.pio self.pio
} }
@ -242,7 +244,19 @@ impl<P: PIOExt> PIO<P> {
/// `PIO::uninstall(program)` can be used to free the space occupied by the program once it is no /// `PIO::uninstall(program)` can be used to free the space occupied by the program once it is no
/// longer used. /// longer used.
/// ///
/// TODO: Write an example? /// # Examples
///
/// ```
/// let (mut pio, sm0, _, _, _) = pac.PIO0.split(&mut pac.RESETS);
/// // Install a program in instruction memory.
/// let installed = pio.install(&program).unwrap();
/// // Configure a state machine to use the program.
/// let sm = rp2040_hal::pio::PIOBuilder::from_program(installed).build(sm0);
/// // Uninitialize the state machine again, freeing the program.
/// let (sm, installed) = sm.uninit();
/// // Uninstall the program to free instruction memory.
/// pio.uninstall(installed);
/// ```
/// ///
/// # Safety /// # Safety
/// ///
@ -251,7 +265,16 @@ impl<P: PIOExt> PIO<P> {
/// the program anymore. The user must therefore make sure that `uninstall()` is only called on the /// the program anymore. The user must therefore make sure that `uninstall()` is only called on the
/// PIO object which was used to install the program. /// PIO object which was used to install the program.
/// ///
/// TODO: Write an example? /// ```
/// let (mut pio, sm0, sm1, sm2, sm3) = pac.PIO0.split(&mut pac.RESETS);
/// // Install a program in instruction memory.
/// let installed = pio.install(&program).unwrap();
/// // Reinitialize PIO.
/// let pio0 = pio.free(sm0, sm1, sm2, sm3);
/// let (mut pio, _, _, _, _) = pio0.split(&mut pac.RESETS);
/// // Do not do the following, the program is not in instruction memory anymore!
/// pio.uninstall(installed);
/// ```
#[derive(Debug)] #[derive(Debug)]
pub struct InstalledProgram<P> { pub struct InstalledProgram<P> {
offset: u8, offset: u8,
@ -350,7 +373,6 @@ impl<P: PIOExt> UninitStateMachine<P> {
/// Set the current instruction. /// Set the current instruction.
fn set_instruction(&mut self, instruction: u16) { fn set_instruction(&mut self, instruction: u16) {
// TODO: Check if this function is safe to call while the state machine is running.
self.sm() self.sm()
.sm_instr .sm_instr
.write(|w| unsafe { w.sm0_instr().bits(instruction) }) .write(|w| unsafe { w.sm0_instr().bits(instruction) })
@ -361,6 +383,7 @@ impl<P: PIOExt> UninitStateMachine<P> {
} }
} }
/// PIO State Machine with an associated program.
pub struct StateMachine<P: PIOExt, State> { pub struct StateMachine<P: PIOExt, State> {
sm: UninitStateMachine<P>, sm: UninitStateMachine<P>,
program: InstalledProgram<P>, program: InstalledProgram<P>,
@ -373,6 +396,10 @@ pub struct Stopped;
pub struct Running; pub struct Running;
impl<P: PIOExt, State> StateMachine<P, State> { impl<P: PIOExt, State> StateMachine<P, State> {
/// Stops the state machine if it is still running and returns its program.
///
/// The program can be uninstalled to free space once it is no longer used by any state
/// machine.
pub fn uninit(mut self) -> (UninitStateMachine<P>, InstalledProgram<P>) { pub fn uninit(mut self) -> (UninitStateMachine<P>, InstalledProgram<P>) {
self.sm.set_enabled(false); self.sm.set_enabled(false);
(self.sm, self.program) (self.sm, self.program)
@ -385,6 +412,7 @@ impl<P: PIOExt, State> StateMachine<P, State> {
/// Set the current instruction. /// Set the current instruction.
pub fn set_instruction(&mut self, instruction: u16) { pub fn set_instruction(&mut self, instruction: u16) {
// TODO: Check if this function is safe to call while the state machine is running.
self.sm.set_instruction(instruction); self.sm.set_instruction(instruction);
} }
@ -445,6 +473,15 @@ impl<P: PIOExt> StateMachine<P, Stopped> {
} }
} }
/// Sets the pin directions for the specified pins.
///
/// The `pins` parameter specifies a set of pins as a mask, and `pindir` contains the
/// directions that are configured for these pins. The bits in both masks correspond to the pin
/// number. The user has to make sure that they do not select any pins that are in use by any
/// other state machines of the same PIO block.
///
/// This function needs to be called for sideset pins if they are supposed to be used as
/// output pins.
pub fn set_pindirs_with_mask(&mut self, mut pins: u32, pindir: u32) { pub fn set_pindirs_with_mask(&mut self, mut pins: u32, pindir: u32) {
let mut pin = 0; let mut pin = 0;
let prev_pinctrl = self.sm.sm().sm_pinctrl.read().bits(); let prev_pinctrl = self.sm.sm().sm_pinctrl.read().bits();
@ -984,7 +1021,6 @@ impl<P: PIOExt> PIOBuilder<P> {
/// Build the config and deploy it to a StateMachine. /// Build the config and deploy it to a StateMachine.
pub fn build(self, mut sm: UninitStateMachine<P>) -> StateMachine<P, Stopped> { pub fn build(self, mut sm: UninitStateMachine<P>) -> StateMachine<P, Stopped> {
// TODO: Currently, the program is just lost and can never be uninstalled again.
let offset = self.program.offset; let offset = self.program.offset;
// Stop the SM // Stop the SM

View file

@ -1,2 +1,3 @@
//! Prelude //! Prelude
pub use crate::clocks::Clock as _rphal_clocks_Clock; pub use crate::clocks::Clock as _rphal_clocks_Clock;
pub use crate::pio::PIOExt as _rphal_pio_PIOExt;