From 2eb7923ebe94b8e88d0aa4a4493899f4ce563124 Mon Sep 17 00:00:00 2001 From: Wilfried Chauveau Date: Fri, 22 Oct 2021 11:13:22 +0100 Subject: [PATCH] Implements methods to allow presetting the pin state & direction (#167) * Implements methods to allow presetting the pin state & direction Enabling those methods allows to save a few valuable instructions in the PIO's memory. * Update pio_proc_blink with new set_pindirs --- rp2040-hal/examples/pio_proc_blink.rs | 2 +- rp2040-hal/src/pio.rs | 114 ++++++++++++++++---------- 2 files changed, 70 insertions(+), 46 deletions(-) diff --git a/rp2040-hal/examples/pio_proc_blink.rs b/rp2040-hal/examples/pio_proc_blink.rs index d395e2d..b82658a 100644 --- a/rp2040-hal/examples/pio_proc_blink.rs +++ b/rp2040-hal/examples/pio_proc_blink.rs @@ -53,7 +53,7 @@ fn main() -> ! { .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.set_pindirs([(led_pin_id, hal::pio::PinDir::Output)]); sm.start(); // PIO runs in background, independently from CPU diff --git a/rp2040-hal/src/pio.rs b/rp2040-hal/src/pio.rs index 8b87d9f..01ceb78 100644 --- a/rp2040-hal/src/pio.rs +++ b/rp2040-hal/src/pio.rs @@ -360,6 +360,28 @@ impl ValidStateMachine for (P, SM) { } } +/// Pin State in the PIO +/// +/// Note the GPIO is able to override/invert that. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum PinState { + /// Pin in Low state. + High, + /// Pin in Low state. + Low, +} + +/// Pin direction in the PIO +/// +/// Note the GPIO is able to override/invert that. +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum PinDir { + /// Pin set as an Input + Input, + /// Pin set as an Output. + Output, +} + /// PIO State Machine (uninitialized, without a program). #[derive(Debug)] pub struct UninitStateMachine { @@ -500,56 +522,58 @@ impl StateMachine { } } - /// Sets the pin directions for the specified pins. + /// Sets the pin state 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 + /// 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) { - let mut pin = 0; - let prev_pinctrl = self.sm.sm().sm_pinctrl.read().bits(); - // For each pin in the mask, we select the pin as a SET pin and then execute "set PINDIRS, - // ". - while pins != 0 { - if (pins & 1) != 0 { - self.sm.sm().sm_pinctrl.write(|w| { - unsafe { - w.set_count().bits(1); - w.set_base().bits(pin as u8); - } - w - }); - let set_pindirs = pio::Instruction { - operands: pio::InstructionOperands::SET { - destination: pio::SetDestination::PINDIRS, - data: ((pindir >> pin) & 0x1) as u8, - }, - delay: 0, - side_set: None, + /// The iterator's item are pairs of `(pin_number, pin_state)`. + pub fn set_pins(&mut self, pins: impl IntoIterator) { + let saved_ctrl = self.sm.sm().sm_pinctrl.read(); + for (pin_num, pin_state) in pins { + self.sm + .sm() + .sm_pinctrl + .write(|w| unsafe { w.set_base().bits(pin_num).set_count().bits(1) }); + self.set_instruction( + pio::InstructionOperands::SET { + destination: pio::SetDestination::PINS, + data: if PinState::High == pin_state { 1 } else { 0 }, } - .encode(SideSet::new(false, 0, false)); - self.sm.sm().sm_instr.write(|w| { - unsafe { - w.sm0_instr().bits(set_pindirs); - } - w - }); - } - pin += 1; - pins >>= 1; + .encode(), + ); } - // We modified PINCTRL, yet the program assumes a certain configuration, so restore the - // previous value. - self.sm.sm().sm_pinctrl.write(|w| { - unsafe { - w.bits(prev_pinctrl); - } - w - }); + self.sm + .sm() + .sm_pinctrl + .write(|w| unsafe { w.bits(saved_ctrl.bits()) }); + } + + /// Set pin directions. + /// + /// 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. + /// + /// The iterator's item are pairs of `(pin_number, pin_dir)`. + pub fn set_pindirs(&mut self, pindirs: impl IntoIterator) { + let saved_ctrl = self.sm.sm().sm_pinctrl.read(); + for (pinnum, pin_dir) in pindirs { + self.sm + .sm() + .sm_pinctrl + .write(|w| unsafe { w.set_base().bits(pinnum).set_count().bits(1) }); + self.set_instruction( + pio::InstructionOperands::SET { + destination: pio::SetDestination::PINDIRS, + data: if PinDir::Output == pin_dir { 1 } else { 0 }, + } + .encode(), + ); + } + self.sm + .sm() + .sm_pinctrl + .write(|w| unsafe { w.bits(saved_ctrl.bits()) }); } }