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
This commit is contained in:
Wilfried Chauveau 2021-10-22 11:13:22 +01:00 committed by GitHub
parent 233f7c9475
commit 2eb7923ebe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 70 additions and 46 deletions

View file

@ -53,7 +53,7 @@ fn main() -> ! {
.clock_divisor(div) .clock_divisor(div)
.build(sm0); .build(sm0);
// The GPIO pin needs to be configured as an output. // 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(); sm.start();
// PIO runs in background, independently from CPU // PIO runs in background, independently from CPU

View file

@ -360,6 +360,28 @@ impl<P: PIOExt, SM: StateMachineIndex> 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). /// PIO State Machine (uninitialized, without a program).
#[derive(Debug)] #[derive(Debug)]
pub struct UninitStateMachine<SM: ValidStateMachine> { pub struct UninitStateMachine<SM: ValidStateMachine> {
@ -500,56 +522,58 @@ impl<SM: ValidStateMachine> StateMachine<SM, Stopped> {
} }
} }
/// 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 /// The user has to make sure that they do not select any pins that are in use by any
/// 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. /// 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 /// The iterator's item are pairs of `(pin_number, pin_state)`.
/// output pins. pub fn set_pins(&mut self, pins: impl IntoIterator<Item = (u8, PinState)>) {
pub fn set_pindirs_with_mask(&mut self, mut pins: u32, pindir: u32) { let saved_ctrl = self.sm.sm().sm_pinctrl.read();
let mut pin = 0; for (pin_num, pin_state) in pins {
let prev_pinctrl = self.sm.sm().sm_pinctrl.read().bits(); self.sm
// For each pin in the mask, we select the pin as a SET pin and then execute "set PINDIRS, .sm()
// <direction>". .sm_pinctrl
while pins != 0 { .write(|w| unsafe { w.set_base().bits(pin_num).set_count().bits(1) });
if (pins & 1) != 0 { self.set_instruction(
self.sm.sm().sm_pinctrl.write(|w| { pio::InstructionOperands::SET {
unsafe { destination: pio::SetDestination::PINS,
w.set_count().bits(1); data: if PinState::High == pin_state { 1 } else { 0 },
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,
} }
.encode(SideSet::new(false, 0, false)); .encode(),
self.sm.sm().sm_instr.write(|w| { );
unsafe {
w.sm0_instr().bits(set_pindirs);
}
w
});
}
pin += 1;
pins >>= 1;
} }
// We modified PINCTRL, yet the program assumes a certain configuration, so restore the self.sm
// previous value. .sm()
self.sm.sm().sm_pinctrl.write(|w| { .sm_pinctrl
unsafe { .write(|w| unsafe { w.bits(saved_ctrl.bits()) });
w.bits(prev_pinctrl); }
}
w /// 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<Item = (u8, PinDir)>) {
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()) });
} }
} }