mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2024-12-24 21:21:31 +11:00
pio: Improve documentation and add an example that uses pio_proc::pio!().
This commit is contained in:
parent
515eac5553
commit
684f483859
|
@ -1,4 +1,5 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
members = [
|
members = [
|
||||||
"rp2040-hal",
|
"rp2040-hal",
|
||||||
"boards/feather_rp2040",
|
"boards/feather_rp2040",
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
62
rp2040-hal/examples/pio_proc_blink.rs
Normal file
62
rp2040-hal/examples/pio_proc_blink.rs
Normal 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 {}
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue