1132 lines
45 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#ifndef _HARDWARE_PIO_H_
#define _HARDWARE_PIO_H_
#include "pico.h"
#include "hardware/address_mapped.h"
#include "hardware/structs/pio.h"
#include "hardware/gpio.h"
#include "hardware/regs/dreq.h"
#include "hardware/pio_instructions.h"
// PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PIO, Enable/disable assertions in the PIO module, type=bool, default=0, group=hardware_pio
#ifndef PARAM_ASSERTIONS_ENABLED_PIO
#define PARAM_ASSERTIONS_ENABLED_PIO 0
#endif
/** \file hardware/pio.h
* \defgroup hardware_pio hardware_pio
*
* Programmable I/O (PIO) API
*
* A programmable input/output block (PIO) is a versatile hardware interface which
* can support a number of different IO standards. There are two PIO blocks in the RP2040
*
* Each PIO is programmable in the same sense as a processor: the four state machines independently
* execute short, sequential programs, to manipulate GPIOs and transfer data. Unlike a general
* purpose processor, PIO state machines are highly specialised for IO, with a focus on determinism,
* precise timing, and close integration with fixed-function hardware. Each state machine is equipped
* with:
* * Two 32-bit shift registers either direction, any shift count
* * Two 32-bit scratch registers
* * 4×32 bit bus FIFO in each direction (TX/RX), reconfigurable as 8×32 in a single direction
* * Fractional clock divider (16 integer, 8 fractional bits)
* * Flexible GPIO mapping
* * DMA interface, sustained throughput up to 1 word per clock from system DMA
* * IRQ flag set/clear/status
*
* Full details of the PIO can be found in the RP2040 datasheet.
*/
#ifdef __cplusplus
extern "C" {
#endif
static_assert(PIO_SM0_SHIFTCTRL_FJOIN_RX_LSB == PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB + 1, "");
/** \brief FIFO join states
* \ingroup hardware_pio
*/
enum pio_fifo_join {
PIO_FIFO_JOIN_NONE = 0,
PIO_FIFO_JOIN_TX = 1,
PIO_FIFO_JOIN_RX = 2,
};
enum pio_mov_status_type {
STATUS_TX_LESSTHAN = 0,
STATUS_RX_LESSTHAN = 1
};
typedef pio_hw_t *PIO;
/** Identifier for the first (PIO 0) hardware PIO instance (for use in PIO functions).
*
* e.g. pio_gpio_init(pio0, 5)
*
* \ingroup hardware_pio
* @{
*/
#define pio0 pio0_hw
/** @} */
/** Identifier for the second (PIO 1) hardware PIO instance (for use in PIO functions).
*
* e.g. pio_gpio_init(pio1, 5)
*
* \ingroup hardware_pio
* @{
*/
#define pio1 pio1_hw
/** @} */
/** \brief PIO state machine configuration
* \defgroup sm_config sm_config
* \ingroup hardware_pio
*
* A PIO block needs to be configured, these functions provide helpers to set up configuration
* structures. See \ref pio_sm_set_config
*
*/
/** \brief PIO Configuration structure
* \ingroup sm_config
*/
typedef struct {
uint32_t clkdiv;
uint32_t execctrl;
uint32_t shiftctrl;
uint32_t pinctrl;
} pio_sm_config;
static inline void check_sm_param(__unused uint sm) {
valid_params_if(PIO, sm < NUM_PIO_STATE_MACHINES);
}
static inline void check_pio_param(__unused PIO pio) {
valid_params_if(PIO, pio == pio0 || pio == pio1);
}
/*! \brief Set the 'out' pins in a state machine configuration
* \ingroup sm_config
*
* Can overlap with the 'in', 'set' and 'sideset' pins
*
* \param c Pointer to the configuration structure to modify
* \param out_base 0-31 First pin to set as output
* \param out_count 0-32 Number of pins to set.
*/
static inline void sm_config_set_out_pins(pio_sm_config *c, uint out_base, uint out_count) {
valid_params_if(PIO, out_base < 32);
valid_params_if(PIO, out_count <= 32);
c->pinctrl = (c->pinctrl & ~(PIO_SM0_PINCTRL_OUT_BASE_BITS | PIO_SM0_PINCTRL_OUT_COUNT_BITS)) |
(out_base << PIO_SM0_PINCTRL_OUT_BASE_LSB) |
(out_count << PIO_SM0_PINCTRL_OUT_COUNT_LSB);
}
/*! \brief Set the 'set' pins in a state machine configuration
* \ingroup sm_config
*
* Can overlap with the 'in', 'out' and 'sideset' pins
*
* \param c Pointer to the configuration structure to modify
* \param set_base 0-31 First pin to set as
* \param set_count 0-5 Number of pins to set.
*/
static inline void sm_config_set_set_pins(pio_sm_config *c, uint set_base, uint set_count) {
valid_params_if(PIO, set_base < 32);
valid_params_if(PIO, set_count <= 5);
c->pinctrl = (c->pinctrl & ~(PIO_SM0_PINCTRL_SET_BASE_BITS | PIO_SM0_PINCTRL_SET_COUNT_BITS)) |
(set_base << PIO_SM0_PINCTRL_SET_BASE_LSB) |
(set_count << PIO_SM0_PINCTRL_SET_COUNT_LSB);
}
/*! \brief Set the 'in' pins in a state machine configuration
* \ingroup sm_config
*
* Can overlap with the 'out', ''set' and 'sideset' pins
*
* \param c Pointer to the configuration structure to modify
* \param in_base 0-31 First pin to use as input
*/
static inline void sm_config_set_in_pins(pio_sm_config *c, uint in_base) {
valid_params_if(PIO, in_base < 32);
c->pinctrl = (c->pinctrl & ~PIO_SM0_PINCTRL_IN_BASE_BITS) |
(in_base << PIO_SM0_PINCTRL_IN_BASE_LSB);
}
/*! \brief Set the 'sideset' pins in a state machine configuration
* \ingroup sm_config
*
* Can overlap with the 'in', 'out' and 'set' pins
*
* \param c Pointer to the configuration structure to modify
* \param sideset_base 0-31 base pin for 'side set'
*/
static inline void sm_config_set_sideset_pins(pio_sm_config *c, uint sideset_base) {
valid_params_if(PIO, sideset_base < 32);
c->pinctrl = (c->pinctrl & ~PIO_SM0_PINCTRL_SIDESET_BASE_BITS) |
(sideset_base << PIO_SM0_PINCTRL_SIDESET_BASE_LSB);
}
/*! \brief Set the 'sideset' options in a state machine configuration
* \ingroup sm_config
*
* \param c Pointer to the configuration structure to modify
* \param bit_count Number of bits to steal from delay field in the instruction for use of side set (max 5)
* \param optional True if the topmost side set bit is used as a flag for whether to apply side set on that instruction
* \param pindirs True if the side set affects pin directions rather than values
*/
static inline void sm_config_set_sideset(pio_sm_config *c, uint bit_count, bool optional, bool pindirs) {
valid_params_if(PIO, bit_count <= 5);
valid_params_if(PIO, !optional || bit_count >= 1);
c->pinctrl = (c->pinctrl & ~PIO_SM0_PINCTRL_SIDESET_COUNT_BITS) |
(bit_count << PIO_SM0_PINCTRL_SIDESET_COUNT_LSB);
c->execctrl = (c->execctrl & ~(PIO_SM0_EXECCTRL_SIDE_EN_BITS | PIO_SM0_EXECCTRL_SIDE_PINDIR_BITS)) |
(bool_to_bit(optional) << PIO_SM0_EXECCTRL_SIDE_EN_LSB) |
(bool_to_bit(pindirs) << PIO_SM0_EXECCTRL_SIDE_PINDIR_LSB);
}
/*! \brief Set the state machine clock divider (from a floating point value) in a state machine configuration
* \ingroup sm_config
*
* The clock divider slows the state machine's execution by masking the
* system clock on some cycles, in a repeating pattern, so that the state
* machine does not advance. Effectively this produces a slower clock for the
* state machine to run from, which can be used to generate e.g. a particular
* UART baud rate. See the datasheet for further detail.
*
* \param c Pointer to the configuration structure to modify
* \param div The fractional divisor to be set. 1 for full speed. An integer clock divisor of n
* will cause the state machine to run 1 cycle in every n.
* Note that for small n, the jitter introduced by a fractional divider (e.g. 2.5) may be unacceptable
* although it will depend on the use case.
*/
static inline void sm_config_set_clkdiv(pio_sm_config *c, float div) {
uint div_int = (uint)div;
uint div_frac = (uint)((div - (float)div_int) * (1u << 8u));
c->clkdiv =
(div_frac << PIO_SM0_CLKDIV_FRAC_LSB) |
(div_int << PIO_SM0_CLKDIV_INT_LSB);
}
/*! \brief Set the state machine clock divider (from integer and fractional parts - 16:8) in a state machine configuration
* \ingroup sm_config
*
* The clock divider can slow the state machine's execution to some rate below
* the system clock frequency, by enabling the state machine on some cycles
* but not on others, in a regular pattern. This can be used to generate e.g.
* a given UART baud rate. See the datasheet for further detail.
*
* \param c Pointer to the configuration structure to modify
* \param div_int Integer part of the divisor
* \param div_frac Fractional part in 1/256ths
* \sa sm_config_set_clkdiv
*/
static inline void sm_config_set_clkdiv_int_frac(pio_sm_config *c, uint16_t div_int, uint8_t div_frac) {
c->clkdiv =
(((uint)div_frac) << PIO_SM0_CLKDIV_FRAC_LSB) |
(((uint)div_int) << PIO_SM0_CLKDIV_INT_LSB);
}
/*! \brief Set the wrap addresses in a state machine configuration
* \ingroup sm_config
*
* \param c Pointer to the configuration structure to modify
* \param wrap_target the instruction memory address to wrap to
* \param wrap the instruction memory address after which to set the program counter to wrap_target
* if the instruction does not itself update the program_counter
*/
static inline void sm_config_set_wrap(pio_sm_config *c, uint wrap_target, uint wrap) {
valid_params_if(PIO, wrap < PIO_INSTRUCTION_COUNT);
valid_params_if(PIO, wrap_target < PIO_INSTRUCTION_COUNT);
c->execctrl = (c->execctrl & ~(PIO_SM0_EXECCTRL_WRAP_TOP_BITS | PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) |
(wrap_target << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) |
(wrap << PIO_SM0_EXECCTRL_WRAP_TOP_LSB);
}
/*! \brief Set the 'jmp' pin in a state machine configuration
* \ingroup sm_config
*
* \param c Pointer to the configuration structure to modify
* \param pin The raw GPIO pin number to use as the source for a `jmp pin` instruction
*/
static inline void sm_config_set_jmp_pin(pio_sm_config *c, uint pin) {
valid_params_if(PIO, pin < 32);
c->execctrl = (c->execctrl & ~PIO_SM0_EXECCTRL_JMP_PIN_BITS) |
(pin << PIO_SM0_EXECCTRL_JMP_PIN_LSB);
}
/*! \brief Setup 'in' shifting parameters in a state machine configuration
* \ingroup sm_config
*
* \param c Pointer to the configuration structure to modify
* \param shift_right true to shift ISR to right, false to shift ISR to left
* \param autopush whether autopush is enabled
* \param push_threshold threshold in bits to shift in before auto/conditional re-pushing of the ISR
*/
static inline void sm_config_set_in_shift(pio_sm_config *c, bool shift_right, bool autopush, uint push_threshold) {
valid_params_if(PIO, push_threshold <= 32);
c->shiftctrl = (c->shiftctrl &
~(PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_BITS |
PIO_SM0_SHIFTCTRL_AUTOPUSH_BITS |
PIO_SM0_SHIFTCTRL_PUSH_THRESH_BITS)) |
(bool_to_bit(shift_right) << PIO_SM0_SHIFTCTRL_IN_SHIFTDIR_LSB) |
(bool_to_bit(autopush) << PIO_SM0_SHIFTCTRL_AUTOPUSH_LSB) |
((push_threshold & 0x1fu) << PIO_SM0_SHIFTCTRL_PUSH_THRESH_LSB);
}
/*! \brief Setup 'out' shifting parameters in a state machine configuration
* \ingroup sm_config
*
* \param c Pointer to the configuration structure to modify
* \param shift_right true to shift OSR to right, false to shift OSR to left
* \param autopull whether autopull is enabled
* \param pull_threshold threshold in bits to shift out before auto/conditional re-pulling of the OSR
*/
static inline void sm_config_set_out_shift(pio_sm_config *c, bool shift_right, bool autopull, uint pull_threshold) {
valid_params_if(PIO, pull_threshold <= 32);
c->shiftctrl = (c->shiftctrl &
~(PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_BITS |
PIO_SM0_SHIFTCTRL_AUTOPULL_BITS |
PIO_SM0_SHIFTCTRL_PULL_THRESH_BITS)) |
(bool_to_bit(shift_right) << PIO_SM0_SHIFTCTRL_OUT_SHIFTDIR_LSB) |
(bool_to_bit(autopull) << PIO_SM0_SHIFTCTRL_AUTOPULL_LSB) |
((pull_threshold & 0x1fu) << PIO_SM0_SHIFTCTRL_PULL_THRESH_LSB);
}
/*! \brief Setup the FIFO joining in a state machine configuration
* \ingroup sm_config
*
* \param c Pointer to the configuration structure to modify
* \param join Specifies the join type. \see enum pio_fifo_join
*/
static inline void sm_config_set_fifo_join(pio_sm_config *c, enum pio_fifo_join join) {
valid_params_if(PIO, join >= 0 && join <= 2);
c->shiftctrl = (c->shiftctrl & (uint)~(PIO_SM0_SHIFTCTRL_FJOIN_TX_BITS | PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS)) |
(((uint)join) << PIO_SM0_SHIFTCTRL_FJOIN_TX_LSB);
}
/*! \brief Set special 'out' operations in a state machine configuration
* \ingroup sm_config
*
* \param c Pointer to the configuration structure to modify
* \param sticky to enable 'sticky' output (i.e. re-asserting most recent OUT/SET pin values on subsequent cycles)
* \param has_enable_pin true to enable auxiliary OUT enable pin
* \param enable_pin_index pin index for auxiliary OUT enable
*/
static inline void sm_config_set_out_special(pio_sm_config *c, bool sticky, bool has_enable_pin, uint enable_pin_index) {
c->execctrl = (c->execctrl &
(uint)~(PIO_SM0_EXECCTRL_OUT_STICKY_BITS | PIO_SM0_EXECCTRL_INLINE_OUT_EN_BITS |
PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS)) |
(bool_to_bit(sticky) << PIO_SM0_EXECCTRL_OUT_STICKY_LSB) |
(bool_to_bit(has_enable_pin) << PIO_SM0_EXECCTRL_INLINE_OUT_EN_LSB) |
((enable_pin_index << PIO_SM0_EXECCTRL_OUT_EN_SEL_LSB) & PIO_SM0_EXECCTRL_OUT_EN_SEL_BITS);
}
/*! \brief Set source for 'mov status' in a state machine configuration
* \ingroup sm_config
*
* \param c Pointer to the configuration structure to modify
* \param status_sel the status operation selector
* \param status_n parameter for the mov status operation (currently a bit count)
*/
static inline void sm_config_set_mov_status(pio_sm_config *c, enum pio_mov_status_type status_sel, uint status_n) {
c->execctrl = (c->execctrl
& ~(PIO_SM0_EXECCTRL_STATUS_SEL_BITS | PIO_SM0_EXECCTRL_STATUS_N_BITS))
| ((((uint)status_sel) << PIO_SM0_EXECCTRL_STATUS_SEL_LSB) & PIO_SM0_EXECCTRL_STATUS_SEL_BITS)
| ((status_n << PIO_SM0_EXECCTRL_STATUS_N_LSB) & PIO_SM0_EXECCTRL_STATUS_N_BITS);
}
/*! \brief Get the default state machine configuration
* \ingroup sm_config
*
* Setting | Default
* --------|--------
* Out Pins | 32 starting at 0
* Set Pins | 0 starting at 0
* In Pins (base) | 0
* Side Set Pins (base) | 0
* Side Set | disabled
* Wrap | wrap=31, wrap_to=0
* In Shift | shift_direction=right, autopush=false, push_thrshold=32
* Out Shift | shift_direction=right, autopull=false, pull_thrshold=32
* Jmp Pin | 0
* Out Special | sticky=false, has_enable_pin=false, enable_pin_index=0
* Mov Status | status_sel=STATUS_TX_LESSTHAN, n=0
*
* \return the default state machine configuration which can then be modified.
*/
static inline pio_sm_config pio_get_default_sm_config(void) {
pio_sm_config c = {0, 0, 0, 0};
sm_config_set_clkdiv_int_frac(&c, 1, 0);
sm_config_set_wrap(&c, 0, 31);
sm_config_set_in_shift(&c, true, false, 32);
sm_config_set_out_shift(&c, true, false, 32);
return c;
}
/*! \brief Apply a state machine configuration to a state machine
* \ingroup hardware_pio
*
* \param pio Handle to PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param config the configuration to apply
*/
static inline void pio_sm_set_config(PIO pio, uint sm, const pio_sm_config *config) {
check_pio_param(pio);
check_sm_param(sm);
pio->sm[sm].clkdiv = config->clkdiv;
pio->sm[sm].execctrl = config->execctrl;
pio->sm[sm].shiftctrl = config->shiftctrl;
pio->sm[sm].pinctrl = config->pinctrl;
}
/*! \brief Return the instance number of a PIO instance
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \return the PIO instance number (either 0 or 1)
*/
static inline uint pio_get_index(PIO pio) {
check_pio_param(pio);
return pio == pio1 ? 1 : 0;
}
/*! \brief Setup the function select for a GPIO to use output from the given PIO instance
* \ingroup hardware_pio
*
* PIO appears as an alternate function in the GPIO muxing, just like an SPI
* or UART. This function configures that multiplexing to connect a given PIO
* instance to a GPIO. Note that this is not necessary for a state machine to
* be able to read the *input* value from a GPIO, but only for it to set the
* output value or output enable.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param pin the GPIO pin whose function select to set
*/
static inline void pio_gpio_init(PIO pio, uint pin) {
check_pio_param(pio);
valid_params_if(PIO, pin < 32);
gpio_set_function(pin, pio == pio0 ? GPIO_FUNC_PIO0 : GPIO_FUNC_PIO1);
}
/*! \brief Return the DREQ to use for pacing transfers to a particular state machine
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param is_tx true for sending data to the state machine, false for received data from the state machine
*/
static inline uint pio_get_dreq(PIO pio, uint sm, bool is_tx) {
check_pio_param(pio);
check_sm_param(sm);
return sm + (is_tx ? 0 : NUM_PIO_STATE_MACHINES) + (pio == pio0 ? DREQ_PIO0_TX0 : DREQ_PIO1_TX0);
}
typedef struct pio_program {
const uint16_t *instructions;
uint8_t length;
int8_t origin; // required instruction memory origin or -1
} __packed pio_program_t;
/*! \brief Determine whether the given program can (at the time of the call) be loaded onto the PIO instance
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param program the program definition
* \return true if the program can be loaded; false if there is not suitable space in the instruction memory
*/
bool pio_can_add_program(PIO pio, const pio_program_t *program);
/*! \brief Determine whether the given program can (at the time of the call) be loaded onto the PIO instance starting at a particular location
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param program the program definition
* \param offset the instruction memory offset wanted for the start of the program
* \return true if the program can be loaded at that location; false if there is not space in the instruction memory
*/
bool pio_can_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset);
/*! \brief Attempt to load the program, panicking if not possible
* \ingroup hardware_pio
*
* \see pico_can_add_program if you need to check whether the program can be loaded
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param program the program definition
* \return the instruction memory offset the program is loaded at
*/
uint pio_add_program(PIO pio, const pio_program_t *program);
/*! \brief Attempt to load the program at the specified instruction memory offset, panicking if not possible
* \ingroup hardware_pio
*
* \see pico_can_add_program_at_offset if you need to check whether the program can be loaded
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param program the program definition
* \param offset the instruction memory offset wanted for the start of the program
*/
void pio_add_program_at_offset(PIO pio, const pio_program_t *program, uint offset);
/*! \brief Remove a program from a PIO instance's instruction memory
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param program the program definition
* \param loaded_offset the loaded offset returned when the program was added
*/
void pio_remove_program(PIO pio, const pio_program_t *program, uint loaded_offset);
/*! \brief Clears all of a PIO instance's instruction memory
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
*/
void pio_clear_instruction_memory(PIO pio);
/*! \brief Resets the state machine to a consistent state, and configures it
* \ingroup hardware_pio
*
* This method:
* - Disables the state machine (if running)
* - Clears the FIFOs
* - Applies the configuration specified by 'config'
* - Resets any internal state e.g. shift counters
* - Jumps to the initial program location given by 'initial_pc'
*
* The state machine is left disabled on return from this call.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param initial_pc the initial program memory offset to run from
* \param config the configuration to apply (or NULL to apply defaults)
*/
void pio_sm_init(PIO pio, uint sm, uint initial_pc, const pio_sm_config *config);
/*! \brief Enable or disable a PIO state machine
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param enabled true to enable the state machine; false to disable
*/
static inline void pio_sm_set_enabled(PIO pio, uint sm, bool enabled) {
check_pio_param(pio);
check_sm_param(sm);
pio->ctrl = (pio->ctrl & ~(1u << sm)) | (bool_to_bit(enabled) << sm);
}
/*! \brief Enable or disable multiple PIO state machines
* \ingroup hardware_pio
*
* Note that this method just sets the enabled state of the state machine;
* if now enabled they continue exactly from where they left off.
*
* \see pio_enable_sm_mask_in_sync if you wish to enable multiple state machines
* and ensure their clock dividers are in sync.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param mask bit mask of state machine indexes to modify the enabled state of
* \param enabled true to enable the state machines; false to disable
*/
static inline void pio_set_sm_mask_enabled(PIO pio, uint32_t mask, bool enabled) {
check_pio_param(pio);
pio->ctrl = (pio->ctrl & ~mask) | (enabled ? mask : 0u);
}
/*! \brief Restart a state machine with a known state
* \ingroup hardware_pio
*
* This method clears the ISR, shift counters, clock divider counter
* pin write flags, delay counter, latched EXEC instruction, and IRQ wait condition.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
*/
static inline void pio_sm_restart(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
pio->ctrl |= 1u << (PIO_CTRL_SM_RESTART_LSB + sm);
}
/*! \brief Restart multiple state machine with a known state
* \ingroup hardware_pio
*
* This method clears the ISR, shift counters, clock divider counter
* pin write flags, delay counter, latched EXEC instruction, and IRQ wait condition.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param mask bit mask of state machine indexes to modify the enabled state of
*/
static inline void pio_restart_sm_mask(PIO pio, uint32_t mask) {
check_pio_param(pio);
pio->ctrl |= (mask << PIO_CTRL_SM_RESTART_LSB) & PIO_CTRL_SM_RESTART_BITS;
}
/*! \brief Restart a state machine's clock divider from a phase of 0
* \ingroup hardware_pio
*
* Each state machine's clock divider is a free-running piece of hardware,
* that generates a pattern of clock enable pulses for the state machine,
* based *only* on the configured integer/fractional divisor. The pattern of
* running/halted cycles slows the state machine's execution to some
* controlled rate.
*
* This function clears the divider's integer and fractional phase
* accumulators so that it restarts this pattern from the beginning. It is
* called automatically by pio_sm_init() but can also be called at a later
* time, when you enable the state machine, to ensure precisely consistent
* timing each time you load and run a given PIO program.
*
* More commonly this hardware mechanism is used to synchronise the execution
* clocks of multiple state machines -- see pio_clkdiv_restart_sm_mask().
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
*/
static inline void pio_sm_clkdiv_restart(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
pio->ctrl |= 1u << (PIO_CTRL_CLKDIV_RESTART_LSB + sm);
}
/*! \brief Restart multiple state machines' clock dividers from a phase of 0.
* \ingroup hardware_pio
*
* Each state machine's clock divider is a free-running piece of hardware,
* that generates a pattern of clock enable pulses for the state machine,
* based *only* on the configured integer/fractional divisor. The pattern of
* running/halted cycles slows the state machine's execution to some
* controlled rate.
*
* This function simultaneously clears the integer and fractional phase
* accumulators of multiple state machines' clock dividers. If these state
* machines all have the same integer and fractional divisors configured,
* their clock dividers will run in precise deterministic lockstep from this
* point.
*
* With their execution clocks synchronised in this way, it is then safe to
* e.g. have multiple state machines performing a 'wait irq' on the same flag,
* and all clear it on the same cycle.
*
* Also note that this function can be called whilst state machines are
* running (e.g. if you have just changed the clock divisors of some state
* machines and wish to resynchronise them), and that disabling a state
* machine does not halt its clock divider: that is, if multiple state
* machines have their clocks synchronised, you can safely disable and
* reenable one of the state machines without losing synchronisation.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param mask bit mask of state machine indexes to modify the enabled state of
*/
static inline void pio_clkdiv_restart_sm_mask(PIO pio, uint32_t mask) {
check_pio_param(pio);
pio->ctrl |= (mask << PIO_CTRL_CLKDIV_RESTART_LSB) & PIO_CTRL_CLKDIV_RESTART_BITS;
}
/*! \brief Enable multiple PIO state machines synchronizing their clock dividers
* \ingroup hardware_pio
*
* This is equivalent to calling both pio_set_sm_mask_enabled() and
* pio_clkdiv_restart_sm_mask() on the *same* clock cycle. All state machines
* specified by 'mask' are started simultaneously and, assuming they have the
* same clock divisors, their divided clocks will stay precisely synchronised.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param mask bit mask of state machine indexes to modify the enabled state of
*/
static inline void pio_enable_sm_mask_in_sync(PIO pio, uint32_t mask) {
check_pio_param(pio);
pio->ctrl |= ((mask << PIO_CTRL_CLKDIV_RESTART_LSB) & PIO_CTRL_CLKDIV_RESTART_BITS) |
((mask << PIO_CTRL_SM_ENABLE_LSB) & PIO_CTRL_SM_ENABLE_BITS);
}
/*! \brief Return the current program counter for a state machine
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \return the program counter
*/
static inline uint8_t pio_sm_get_pc(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
return (uint8_t) pio->sm[sm].addr;
}
/*! \brief Immediately execute an instruction on a state machine
* \ingroup hardware_pio
*
* This instruction is executed instead of the next instruction in the normal control flow on the state machine.
* Subsequent calls to this method replace the previous executed
* instruction if it is still running. \see pio_sm_is_exec_stalled to see if an executed instruction
* is still running (i.e. it is stalled on some condition)
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param instr the encoded PIO instruction
*/
inline static void pio_sm_exec(PIO pio, uint sm, uint instr) {
check_pio_param(pio);
check_sm_param(sm);
pio->sm[sm].instr = instr;
}
/*! \brief Determine if an instruction set by pio_sm_exec() is stalled executing
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \return true if the executed instruction is still running (stalled)
*/
static inline bool pio_sm_is_exec_stalled(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
return !!(pio->sm[sm].execctrl & PIO_SM0_EXECCTRL_EXEC_STALLED_BITS);
}
/*! \brief Immediately execute an instruction on a state machine and wait for it to complete
* \ingroup hardware_pio
*
* This instruction is executed instead of the next instruction in the normal control flow on the state machine.
* Subsequent calls to this method replace the previous executed
* instruction if it is still running. \see pio_sm_is_exec_stalled to see if an executed instruction
* is still running (i.e. it is stalled on some condition)
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param instr the encoded PIO instruction
*/
static inline void pio_sm_exec_wait_blocking(PIO pio, uint sm, uint instr) {
check_pio_param(pio);
check_sm_param(sm);
pio_sm_exec(pio, sm, instr);
while (pio_sm_is_exec_stalled(pio, sm)) tight_loop_contents();
}
/*! \brief Set the current wrap configuration for a state machine
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param wrap_target the instruction memory address to wrap to
* \param wrap the instruction memory address after which to set the program counter to wrap_target
* if the instruction does not itself update the program_counter
*/
static inline void pio_sm_set_wrap(PIO pio, uint sm, uint wrap_target, uint wrap) {
check_pio_param(pio);
check_sm_param(sm);
valid_params_if(PIO, wrap < PIO_INSTRUCTION_COUNT);
valid_params_if(PIO, wrap_target < PIO_INSTRUCTION_COUNT);
pio->sm[sm].execctrl =
(pio->sm[sm].execctrl & ~(PIO_SM0_EXECCTRL_WRAP_TOP_BITS | PIO_SM0_EXECCTRL_WRAP_BOTTOM_BITS)) |
(wrap_target << PIO_SM0_EXECCTRL_WRAP_BOTTOM_LSB) |
(wrap << PIO_SM0_EXECCTRL_WRAP_TOP_LSB);
}
/*! \brief Set the current 'out' pins for a state machine
* \ingroup sm_config
*
* Can overlap with the 'in', 'set' and 'sideset' pins
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param out_base 0-31 First pin to set as output
* \param out_count 0-32 Number of pins to set.
*/
static inline void pio_sm_set_out_pins(PIO pio, uint sm, uint out_base, uint out_count) {
check_pio_param(pio);
check_sm_param(sm);
valid_params_if(PIO, out_base < 32);
valid_params_if(PIO, out_count <= 32);
pio->sm[sm].pinctrl = (pio->sm[sm].pinctrl & ~(PIO_SM0_PINCTRL_OUT_BASE_BITS | PIO_SM0_PINCTRL_OUT_COUNT_BITS)) |
(out_base << PIO_SM0_PINCTRL_OUT_BASE_LSB) |
(out_count << PIO_SM0_PINCTRL_OUT_COUNT_LSB);
}
/*! \brief Set the current 'set' pins for a state machine
* \ingroup sm_config
*
* Can overlap with the 'in', 'out' and 'sideset' pins
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param set_base 0-31 First pin to set as
* \param set_count 0-5 Number of pins to set.
*/
static inline void pio_sm_set_set_pins(PIO pio, uint sm, uint set_base, uint set_count) {
check_pio_param(pio);
check_sm_param(sm);
valid_params_if(PIO, set_base < 32);
valid_params_if(PIO, set_count <= 5);
pio->sm[sm].pinctrl = (pio->sm[sm].pinctrl & ~(PIO_SM0_PINCTRL_SET_BASE_BITS | PIO_SM0_PINCTRL_SET_COUNT_BITS)) |
(set_base << PIO_SM0_PINCTRL_SET_BASE_LSB) |
(set_count << PIO_SM0_PINCTRL_SET_COUNT_LSB);
}
/*! \brief Set the current 'in' pins for a state machine
* \ingroup sm_config
*
* Can overlap with the 'out', ''set' and 'sideset' pins
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param in_base 0-31 First pin to use as input
*/
static inline void pio_sm_set_in_pins(PIO pio, uint sm, uint in_base) {
check_pio_param(pio);
check_sm_param(sm);
valid_params_if(PIO, in_base < 32);
pio->sm[sm].pinctrl = (pio->sm[sm].pinctrl & ~PIO_SM0_PINCTRL_IN_BASE_BITS) |
(in_base << PIO_SM0_PINCTRL_IN_BASE_LSB);
}
/*! \brief Set the current 'sideset' pins for a state machine
* \ingroup sm_config
*
* Can overlap with the 'in', 'out' and 'set' pins
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param sideset_base 0-31 base pin for 'side set'
*/
static inline void pio_sm_set_sideset_pins(PIO pio, uint sm, uint sideset_base) {
check_pio_param(pio);
check_sm_param(sm);
valid_params_if(PIO, sideset_base < 32);
pio->sm[sm].pinctrl = (pio->sm[sm].pinctrl & ~PIO_SM0_PINCTRL_SIDESET_BASE_BITS) |
(sideset_base << PIO_SM0_PINCTRL_SIDESET_BASE_LSB);
}
/*! \brief Write a word of data to a state machine's TX FIFO
* \ingroup hardware_pio
*
* This is a raw FIFO access that does not check for fullness. If the FIFO is
* full, the FIFO contents and state are not affected by the write attempt.
* Hardware sets the TXOVER sticky flag for this FIFO in FDEBUG, to indicate
* that the system attempted to write to a full FIFO.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param data the 32 bit data value
*
* \sa pio_sm_put_blocking
*/
static inline void pio_sm_put(PIO pio, uint sm, uint32_t data) {
check_pio_param(pio);
check_sm_param(sm);
pio->txf[sm] = data;
}
/*! \brief Read a word of data from a state machine's RX FIFO
* \ingroup hardware_pio
*
* This is a raw FIFO access that does not check for emptiness. If the FIFO is
* empty, the hardware ignores the attempt to read from the FIFO (the FIFO
* remains in an empty state following the read) and the sticky RXUNDER flag
* for this FIFO is set in FDEBUG to indicate that the system tried to read
* from this FIFO when empty. The data returned by this function is undefined
* when the FIFO is empty.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
*
* \sa pio_sm_get_blocking
*/
static inline uint32_t pio_sm_get(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
return pio->rxf[sm];
}
/*! \brief Determine if a state machine's RX FIFO is full
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \return true if the RX FIFO is full
*/
static inline bool pio_sm_is_rx_fifo_full(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
return (pio->fstat & (1u << (PIO_FSTAT_RXFULL_LSB + sm))) != 0;
}
/*! \brief Determine if a state machine's RX FIFO is empty
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \return true if the RX FIFO is empty
*/
static inline bool pio_sm_is_rx_fifo_empty(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
return (pio->fstat & (1u << (PIO_FSTAT_RXEMPTY_LSB + sm))) != 0;
}
/*! \brief Return the number of elements currently in a state machine's RX FIFO
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \return the number of elements in the RX FIFO
*/
static inline uint pio_sm_get_rx_fifo_level(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
uint bitoffs = PIO_FLEVEL_RX0_LSB + sm * (PIO_FLEVEL_RX1_LSB - PIO_FLEVEL_RX0_LSB);
const uint32_t mask = PIO_FLEVEL_RX0_BITS >> PIO_FLEVEL_RX0_LSB;
return (pio->flevel >> bitoffs) & mask;
}
/*! \brief Determine if a state machine's TX FIFO is full
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \return true if the TX FIFO is full
*/
static inline bool pio_sm_is_tx_fifo_full(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
return (pio->fstat & (1u << (PIO_FSTAT_TXFULL_LSB + sm))) != 0;
}
/*! \brief Determine if a state machine's TX FIFO is empty
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \return true if the TX FIFO is empty
*/
static inline bool pio_sm_is_tx_fifo_empty(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
return (pio->fstat & (1u << (PIO_FSTAT_TXEMPTY_LSB + sm))) != 0;
}
/*! \brief Return the number of elements currently in a state machine's TX FIFO
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \return the number of elements in the TX FIFO
*/
static inline uint pio_sm_get_tx_fifo_level(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
unsigned int bitoffs = PIO_FLEVEL_TX0_LSB + sm * (PIO_FLEVEL_TX1_LSB - PIO_FLEVEL_TX0_LSB);
const uint32_t mask = PIO_FLEVEL_TX0_BITS >> PIO_FLEVEL_TX0_LSB;
return (pio->flevel >> bitoffs) & mask;
}
/*! \brief Write a word of data to a state machine's TX FIFO, blocking if the FIFO is full
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param data the 32 bit data value
*/
static inline void pio_sm_put_blocking(PIO pio, uint sm, uint32_t data) {
check_pio_param(pio);
check_sm_param(sm);
while (pio_sm_is_tx_fifo_full(pio, sm)) tight_loop_contents();
pio_sm_put(pio, sm, data);
}
/*! \brief Read a word of data from a state machine's RX FIFO, blocking if the FIFO is empty
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
*/
static inline uint32_t pio_sm_get_blocking(PIO pio, uint sm) {
check_pio_param(pio);
check_sm_param(sm);
while (pio_sm_is_rx_fifo_empty(pio, sm)) tight_loop_contents();
return pio_sm_get(pio, sm);
}
/*! \brief Empty out a state machine's TX FIFO
* \ingroup hardware_pio
*
* This method executes `pull` instructions on the state machine until the TX
* FIFO is empty. This disturbs the contents of the OSR, so see also
* pio_sm_clear_fifos() which clears both FIFOs but leaves the state machine's
* internal state undisturbed.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
*
* \sa pio_sm_clear_fifos
*/
void pio_sm_drain_tx_fifo(PIO pio, uint sm);
/*! \brief set the current clock divider for a state machine
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param div the floating point clock divider
*/
static inline void pio_sm_set_clkdiv(PIO pio, uint sm, float div) {
check_pio_param(pio);
check_sm_param(sm);
uint div_int = (uint16_t) div;
uint div_frac = (uint8_t) ((div - (float)div_int) * (1u << 8u));
pio->sm[sm].clkdiv =
(div_frac << PIO_SM0_CLKDIV_FRAC_LSB) |
(div_int << PIO_SM0_CLKDIV_INT_LSB);
}
/*! \brief set the current clock divider for a state machine using a 16:8 fraction
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
* \param div_int the integer part of the clock divider
* \param div_frac the fractional part of the clock divider in 1/256s
*/
static inline void pio_sm_set_clkdiv_int_frac(PIO pio, uint sm, uint16_t div_int, uint8_t div_frac) {
check_pio_param(pio);
check_sm_param(sm);
pio->sm[sm].clkdiv =
(((uint)div_frac) << PIO_SM0_CLKDIV_FRAC_LSB) |
(((uint)div_int) << PIO_SM0_CLKDIV_INT_LSB);
}
/*! \brief Clear a state machine's TX and RX FIFOs
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
*/
static inline void pio_sm_clear_fifos(PIO pio, uint sm) {
// changing the FIFO join state clears the fifo
check_pio_param(pio);
check_sm_param(sm);
hw_xor_bits(&pio->sm[sm].shiftctrl, PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS);
hw_xor_bits(&pio->sm[sm].shiftctrl, PIO_SM0_SHIFTCTRL_FJOIN_RX_BITS);
}
/*! \brief Use a state machine to set a value on all pins for the PIO instance
* \ingroup hardware_pio
*
* This method repeatedly reconfigures the target state machine's pin configuration and executes 'set' instructions to set values on all 32 pins,
* before restoring the state machine's pin configuration to what it was.
*
* This method is provided as a convenience to set initial pin states, and should not be used against a state machine that is enabled.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3) to use
* \param pin_values the pin values to set
*/
void pio_sm_set_pins(PIO pio, uint sm, uint32_t pin_values);
/*! \brief Use a state machine to set a value on multiple pins for the PIO instance
* \ingroup hardware_pio
*
* This method repeatedly reconfigures the target state machine's pin configuration and executes 'set' instructions to set values on up to 32 pins,
* before restoring the state machine's pin configuration to what it was.
*
* This method is provided as a convenience to set initial pin states, and should not be used against a state machine that is enabled.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3) to use
* \param pin_values the pin values to set (if the corresponding bit in pin_mask is set)
* \param pin_mask a bit for each pin to indicate whether the corresponding pin_value for that pin should be applied.
*/
void pio_sm_set_pins_with_mask(PIO pio, uint sm, uint32_t pin_values, uint32_t pin_mask);
/*! \brief Use a state machine to set the pin directions for multiple pins for the PIO instance
* \ingroup hardware_pio
*
* This method repeatedly reconfigures the target state machine's pin configuration and executes 'set' instructions to set pin directions on up to 32 pins,
* before restoring the state machine's pin configuration to what it was.
*
* This method is provided as a convenience to set initial pin directions, and should not be used against a state machine that is enabled.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3) to use
* \param pin_dirs the pin directions to set - 1 = out, 0 = in (if the corresponding bit in pin_mask is set)
* \param pin_mask a bit for each pin to indicate whether the corresponding pin_value for that pin should be applied.
*/
void pio_sm_set_pindirs_with_mask(PIO pio, uint sm, uint32_t pin_dirs, uint32_t pin_mask);
/*! \brief Use a state machine to set the same pin direction for multiple consecutive pins for the PIO instance
* \ingroup hardware_pio
*
* This method repeatedly reconfigures the target state machine's pin configuration and executes 'set' instructions to set the pin direction on consecutive pins,
* before restoring the state machine's pin configuration to what it was.
*
* This method is provided as a convenience to set initial pin directions, and should not be used against a state machine that is enabled.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3) to use
* \param pin_base the first pin to set a direction for
* \param pin_count the count of consecutive pins to set the direction for
* \param is_out the direction to set; true = out, false = in
*/
void pio_sm_set_consecutive_pindirs(PIO pio, uint sm, uint pin_base, uint pin_count, bool is_out);
/*! \brief Mark a state machine as used
* \ingroup hardware_pio
*
* Method for cooperative claiming of hardware. Will cause a panic if the state machine
* is already claimed. Use of this method by libraries detects accidental
* configurations that would fail in unpredictable ways.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
*/
void pio_sm_claim(PIO pio, uint sm);
/*! \brief Mark multiple state machines as used
* \ingroup hardware_pio
*
* Method for cooperative claiming of hardware. Will cause a panic if any of the state machines
* are already claimed. Use of this method by libraries detects accidental
* configurations that would fail in unpredictable ways.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm_mask Mask of state machine indexes
*/
void pio_claim_sm_mask(PIO pio, uint sm_mask);
/*! \brief Mark a state machine as no longer used
* \ingroup hardware_pio
*
* Method for cooperative claiming of hardware.
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param sm State machine index (0..3)
*/
void pio_sm_unclaim(PIO pio, uint sm);
/*! \brief Claim a free state machine on a PIO instance
* \ingroup hardware_pio
*
* \param pio The PIO instance; either \ref pio0 or \ref pio1
* \param required if true the function will panic if none are available
* \return the state machine index or -1 if required was false, and none were free
*/
int pio_claim_unused_sm(PIO pio, bool required);
#ifdef __cplusplus
}
#endif
#endif // _PIO_H_