mirror of
https://github.com/italicsjenga/rp-hal-boards.git
synced 2025-01-11 13:01:30 +11:00
Add PIO features required for PIO as I2C support (#179)
* Implements methods to allow presetting the pin state & direction Enabling those methods allows to save a few valuable instructions in the PIO's memory. * Use IntoIterator rather than Iterator. * Use proper types instead of bool to be more idiomatic. * Change set_pindirs_with_mask to a more idiomatic rust approach. * Update pio_proc_blink with new set_pindirs_with_iter * Force reset on startup to guaranty consistency with type-states. * Expose more accessors to allow more advanced driver to be implemented. * Add the ability to restart the state machine from its wrap point. This method also clears the ISR/OSR to give the state machine a clean restart. * Add a comment about the impact of autopull on OUT & PULL instructions * Fix broken internal doc reference & add an important note on Tx<SM>::has_stalled * rename block methods to register_block. This change is made to avoid confusion with "block" as a verb.
This commit is contained in:
parent
2eb7923ebe
commit
0eb65ee99b
|
@ -959,9 +959,13 @@ macro_rules! gpio {
|
||||||
impl Pins {
|
impl Pins {
|
||||||
/// Take ownership of the PAC peripherals and SIO slice and split it into discrete [`Pin`]s
|
/// Take ownership of the PAC peripherals and SIO slice and split it into discrete [`Pin`]s
|
||||||
pub fn new(io : [<IO_ $Group:upper>], pads: [<PADS_ $Group:upper>], sio: [<SioGpio $Group>], reset : &mut pac::RESETS) -> Self {
|
pub fn new(io : [<IO_ $Group:upper>], pads: [<PADS_ $Group:upper>], sio: [<SioGpio $Group>], reset : &mut pac::RESETS) -> Self {
|
||||||
|
pads.reset_bring_down(reset);
|
||||||
|
io.reset_bring_down(reset);
|
||||||
|
|
||||||
io.reset_bring_up(reset);
|
io.reset_bring_up(reset);
|
||||||
pads.reset_bring_up(reset);
|
pads.reset_bring_up(reset);
|
||||||
unsafe { Self {
|
unsafe {
|
||||||
|
Self {
|
||||||
_io: io,
|
_io: io,
|
||||||
_pads: pads,
|
_pads: pads,
|
||||||
_sio: sio,
|
_sio: sio,
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub trait PIOExt:
|
||||||
UninitStateMachine<(Self, SM2)>,
|
UninitStateMachine<(Self, SM2)>,
|
||||||
UninitStateMachine<(Self, SM3)>,
|
UninitStateMachine<(Self, SM3)>,
|
||||||
) {
|
) {
|
||||||
|
self.reset_bring_down(resets);
|
||||||
self.reset_bring_up(resets);
|
self.reset_bring_up(resets);
|
||||||
|
|
||||||
let sm0 = UninitStateMachine {
|
let sm0 = UninitStateMachine {
|
||||||
|
@ -112,6 +113,14 @@ impl<P: PIOExt> PIO<P> {
|
||||||
&self.interrupts
|
&self.interrupts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get raw irq flags.
|
||||||
|
///
|
||||||
|
/// The PIO has 8 IRQ flags, of which 4 are visible to the host processor. Each bit of `flags` corresponds to one of
|
||||||
|
/// the IRQ flags.
|
||||||
|
pub fn get_irq_raw(&self) -> u8 {
|
||||||
|
self.pio.irq.read().irq().bits()
|
||||||
|
}
|
||||||
|
|
||||||
/// Clear PIO's IRQ flags indicated by the bits.
|
/// Clear PIO's IRQ flags indicated by the bits.
|
||||||
///
|
///
|
||||||
/// The PIO has 8 IRQ flags, of which 4 are visible to the host processor. Each bit of `flags` corresponds to one of
|
/// The PIO has 8 IRQ flags, of which 4 are visible to the host processor. Each bit of `flags` corresponds to one of
|
||||||
|
@ -269,6 +278,11 @@ pub struct InstalledProgram<P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: PIOExt> InstalledProgram<P> {
|
impl<P: PIOExt> InstalledProgram<P> {
|
||||||
|
/// Get the warp target (entry point) of the instaled program.
|
||||||
|
pub fn wrap_target(&self) -> u8 {
|
||||||
|
self.offset + self.wrap.target
|
||||||
|
}
|
||||||
|
|
||||||
/// Clones this program handle so that it can be executed by two state machines at the same
|
/// Clones this program handle so that it can be executed by two state machines at the same
|
||||||
/// time.
|
/// time.
|
||||||
///
|
///
|
||||||
|
@ -648,6 +662,24 @@ impl<SM: ValidStateMachine> StateMachine<SM, Running> {
|
||||||
_phantom: core::marker::PhantomData,
|
_phantom: core::marker::PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Restarts the execution of the selected program from its wrap target.
|
||||||
|
pub fn restart(&mut self) {
|
||||||
|
// pause the state machine
|
||||||
|
self.sm.set_enabled(false);
|
||||||
|
// revert it to its wrap target
|
||||||
|
self.sm.set_instruction(
|
||||||
|
pio::InstructionOperands::JMP {
|
||||||
|
condition: pio::JmpCondition::Always,
|
||||||
|
address: self.program.wrap_target(),
|
||||||
|
}
|
||||||
|
.encode(),
|
||||||
|
);
|
||||||
|
// clear osr/isr
|
||||||
|
self.sm.restart();
|
||||||
|
// unpause the state machine
|
||||||
|
self.sm.set_enabled(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PIO RX FIFO handle.
|
/// PIO RX FIFO handle.
|
||||||
|
@ -657,19 +689,33 @@ pub struct Rx<SM: ValidStateMachine> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SM: ValidStateMachine> Rx<SM> {
|
impl<SM: ValidStateMachine> Rx<SM> {
|
||||||
|
fn register_block(&self) -> &pac::pio0::RegisterBlock {
|
||||||
|
// Safety: The register is unique to this Tx instance.
|
||||||
|
unsafe { &*self.block }
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the next element from RX FIFO.
|
/// Get the next element from RX FIFO.
|
||||||
///
|
///
|
||||||
/// Returns `None` if the FIFO is empty.
|
/// Returns `None` if the FIFO is empty.
|
||||||
pub fn read(&mut self) -> Option<u32> {
|
pub fn read(&mut self) -> Option<u32> {
|
||||||
// Safety: The register is never written by software.
|
if self.is_empty() {
|
||||||
let is_empty = unsafe { &*self.block }.fstat.read().rxempty().bits() & (1 << SM::id()) != 0;
|
|
||||||
|
|
||||||
if is_empty {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safety: The register is unique to this Rx instance.
|
// Safety: The register is unique to this Rx instance.
|
||||||
Some(unsafe { &*self.block }.rxf[SM::id() as usize].read().bits())
|
Some(self.register_block().rxf[SM::id() as usize].read().bits())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Enable/Disable the autopush feature of the state machine.
|
||||||
|
pub fn enable_autopush(&mut self, enable: bool) {
|
||||||
|
self.register_block().sm[SM::id()]
|
||||||
|
.sm_shiftctrl
|
||||||
|
.modify(|_, w| w.autopush().bit(enable))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicate if the tx FIFO is full
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.register_block().fstat.read().rxempty().bits() & (1 << SM::id()) != 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -680,22 +726,91 @@ pub struct Tx<SM: ValidStateMachine> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<SM: ValidStateMachine> Tx<SM> {
|
impl<SM: ValidStateMachine> Tx<SM> {
|
||||||
|
fn register_block(&self) -> &pac::pio0::RegisterBlock {
|
||||||
|
// Safety: The register is unique to this Tx instance.
|
||||||
|
unsafe { &*self.block }
|
||||||
|
}
|
||||||
|
|
||||||
/// Write an element to TX FIFO.
|
/// Write an element to TX FIFO.
|
||||||
///
|
///
|
||||||
/// Returns `true` if the value was written to FIFO, `false` otherwise.
|
/// Returns `true` if the value was written to FIFO, `false` otherwise.
|
||||||
pub fn write(&mut self, value: u32) -> bool {
|
pub fn write<T>(&mut self, value: T) -> bool {
|
||||||
// Safety: The register is never written by software.
|
// Safety: The register is never written by software.
|
||||||
let is_full = unsafe { &*self.block }.fstat.read().txfull().bits() & (1 << SM::id()) != 0;
|
let is_full = self.is_full();
|
||||||
|
|
||||||
if is_full {
|
if is_full {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safety: The register is unique to this Tx instance.
|
unsafe {
|
||||||
unsafe { &*self.block }.txf[SM::id()].write(|w| unsafe { w.bits(value) });
|
let reg_ptr = self.register_block().txf[SM::id()].as_ptr() as *mut T;
|
||||||
|
core::ptr::write_volatile(reg_ptr, value);
|
||||||
|
}
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if the state machine has stalled on empty TX FIFO during a blocking PULL, or an OUT
|
||||||
|
/// with autopull enabled.
|
||||||
|
///
|
||||||
|
/// **Note this is a sticky flag and may not reflect the current state of the machine.**
|
||||||
|
pub fn has_stalled(&self) -> bool {
|
||||||
|
let mask = 1 << SM::id();
|
||||||
|
self.register_block().fdebug.read().txstall().bits() & mask == mask
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears the `tx_stalled` flag.
|
||||||
|
pub fn clear_stalled_flag(&self) {
|
||||||
|
let mask = 1 << SM::id();
|
||||||
|
|
||||||
|
self.register_block()
|
||||||
|
.fdebug
|
||||||
|
.write(|w| unsafe { w.txstall().bits(mask) });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicate if the tx FIFO is empty
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.register_block().fstat.read().txempty().bits() & (1 << SM::id()) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicate if the tx FIFO is full
|
||||||
|
pub fn is_full(&self) -> bool {
|
||||||
|
self.register_block().fstat.read().txfull().bits() & (1 << SM::id()) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Drain Tx fifo.
|
||||||
|
pub fn drain_fifo(&mut self) {
|
||||||
|
// According to the datasheet 3.5.4.2 Page 358:
|
||||||
|
//
|
||||||
|
// When autopull is enabled, the behaviour of 'PULL' is altered: it becomes a no-op
|
||||||
|
// if the OSR is full. This is to avoid a race condition against the system
|
||||||
|
// DMA. It behaves as a fence: either an autopull has already taken place, in which case
|
||||||
|
// the 'PULL' has no effect, or the program will stall on the 'PULL' until data becomes
|
||||||
|
// available in the FIFO.
|
||||||
|
let instr = if self.register_block().sm[SM::id()]
|
||||||
|
.sm_shiftctrl
|
||||||
|
.read()
|
||||||
|
.autopull()
|
||||||
|
.bit_is_set()
|
||||||
|
{
|
||||||
|
pio::InstructionOperands::OUT {
|
||||||
|
destination: pio::OutDestination::NULL,
|
||||||
|
bit_count: 32,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pio::InstructionOperands::PULL {
|
||||||
|
if_empty: false,
|
||||||
|
block: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.encode();
|
||||||
|
let mask = 1 << SM::id();
|
||||||
|
while self.register_block().fstat.read().txempty().bits() & mask != mask {
|
||||||
|
self.register_block().sm[SM::id()]
|
||||||
|
.sm_instr
|
||||||
|
.write(|w| unsafe { w.sm0_instr().bits(instr) })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// PIO Interrupt controller.
|
/// PIO Interrupt controller.
|
||||||
|
@ -713,7 +828,7 @@ impl<P: PIOExt> Interrupt<P> {
|
||||||
/// Enable interrupts raised by state machines.
|
/// Enable interrupts raised by state machines.
|
||||||
///
|
///
|
||||||
/// The PIO peripheral has 4 outside visible interrupts that can be raised by the state machines. Note that this
|
/// The PIO peripheral has 4 outside visible interrupts that can be raised by the state machines. Note that this
|
||||||
/// don't correspond with the state machine index; any state machine can raise any one of the four interrupts.
|
/// does not correspond with the state machine index; any state machine can raise any one of the four interrupts.
|
||||||
pub fn enable_sm_interrupt(&self, id: u8) {
|
pub fn enable_sm_interrupt(&self, id: u8) {
|
||||||
match id {
|
match id {
|
||||||
0 => self.irq().irq_inte.modify(|_, w| w.sm0().set_bit()),
|
0 => self.irq().irq_inte.modify(|_, w| w.sm0().set_bit()),
|
||||||
|
@ -884,7 +999,7 @@ impl<P: PIOExt> Interrupt<P> {
|
||||||
///
|
///
|
||||||
/// This is the state of the interrupts without interrupt masking and forcing.
|
/// This is the state of the interrupts without interrupt masking and forcing.
|
||||||
pub fn raw(&self) -> InterruptState {
|
pub fn raw(&self) -> InterruptState {
|
||||||
InterruptState(self.block().intr.read().bits())
|
InterruptState(self.register_block().intr.read().bits())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the interrupt state.
|
/// Get the interrupt state.
|
||||||
|
@ -894,12 +1009,12 @@ impl<P: PIOExt> Interrupt<P> {
|
||||||
InterruptState(self.irq().irq_ints.read().bits())
|
InterruptState(self.irq().irq_ints.read().bits())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn block(&self) -> &rp2040_pac::pio0::RegisterBlock {
|
fn register_block(&self) -> &rp2040_pac::pio0::RegisterBlock {
|
||||||
unsafe { &*self.block }
|
unsafe { &*self.block }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn irq(&self) -> &rp2040_pac::pio0::SM_IRQ {
|
fn irq(&self) -> &rp2040_pac::pio0::SM_IRQ {
|
||||||
&self.block().sm_irq[self.id as usize]
|
&self.register_block().sm_irq[self.id as usize]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1097,7 +1212,7 @@ impl<P: PIOExt> PIOBuilder<P> {
|
||||||
/// Set the pins used by side-set instructions.
|
/// Set the pins used by side-set instructions.
|
||||||
///
|
///
|
||||||
/// The least-significant side-set bit asserts the state of the pin indicated by `base`, the next bit asserts the
|
/// The least-significant side-set bit asserts the state of the pin indicated by `base`, the next bit asserts the
|
||||||
/// state of the next pin, and so on up to number of bits set using [`Self::side_set`] function.
|
/// state of the next pin, and so on up to number of bits set using [`SideSet::new`] function.
|
||||||
pub fn side_set_pin_base(mut self, base: u8) -> Self {
|
pub fn side_set_pin_base(mut self, base: u8) -> Self {
|
||||||
self.side_set_base = base;
|
self.side_set_base = base;
|
||||||
self
|
self
|
||||||
|
|
Loading…
Reference in a new issue