| /* |
| * 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, |
| }; |
| |
| /** \brief MOV status types |
| * \ingroup hardware_pio |
| */ |
| 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_sm_mask(__unused uint mask) { |
| valid_params_if(PIO, mask < (1u << 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 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); |
| } |
| |
| static inline void pio_calculate_clkdiv_from_float(float div, uint16_t *div_int, uint8_t *div_frac) { |
| valid_params_if(PIO, div >= 1 && div <= 65536); |
| *div_int = (uint16_t)div; |
| if (*div_int == 0) { |
| *div_frac = 0; |
| } else { |
| *div_frac = (uint8_t)((div - (float)*div_int) * (1u << 8u)); |
| } |
| } |
| |
| /*! \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) { |
| uint16_t div_int; |
| uint8_t div_frac; |
| pio_calculate_clkdiv_from_float(div, &div_int, &div_frac); |
| sm_config_set_clkdiv_int_frac(c, div_int, div_frac); |
| } |
| |
| /*! \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 == PIO_FIFO_JOIN_NONE || join == PIO_FIFO_JOIN_TX || join == PIO_FIFO_JOIN_RX); |
| 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. \see enum pio_mov_status_type |
| * \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) { |
| valid_params_if(PIO, status_sel == STATUS_TX_LESSTHAN || status_sel == STATUS_RX_LESSTHAN); |
| 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/from a particular state machine FIFO |
| * \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 receiving data from the state machine |
| */ |
| static inline uint pio_get_dreq(PIO pio, uint sm, bool is_tx) { |
| static_assert(DREQ_PIO0_TX1 == DREQ_PIO0_TX0 + 1, ""); |
| static_assert(DREQ_PIO0_TX2 == DREQ_PIO0_TX0 + 2, ""); |
| static_assert(DREQ_PIO0_TX3 == DREQ_PIO0_TX0 + 3, ""); |
| static_assert(DREQ_PIO0_RX0 == DREQ_PIO0_TX0 + NUM_PIO_STATE_MACHINES, ""); |
| static_assert(DREQ_PIO1_RX0 == DREQ_PIO1_TX0 + NUM_PIO_STATE_MACHINES, ""); |
| 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 pio_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 pio_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); |
| check_sm_mask(mask); |
| 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); |
| check_sm_mask(mask); |
| 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); |
| check_sm_mask(mask); |
| 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); |
| check_sm_mask(mask); |
| 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 PIO interrupt source numbers for pio related IRQs |
| * \ingroup hardware_pio |
| */ |
| enum pio_interrupt_source { |
| pis_interrupt0 = PIO_INTR_SM0_LSB, |
| pis_interrupt1 = PIO_INTR_SM1_LSB, |
| pis_interrupt2 = PIO_INTR_SM2_LSB, |
| pis_interrupt3 = PIO_INTR_SM3_LSB, |
| pis_sm0_tx_fifo_not_full = PIO_INTR_SM0_TXNFULL_LSB, |
| pis_sm1_tx_fifo_not_full = PIO_INTR_SM1_TXNFULL_LSB, |
| pis_sm2_tx_fifo_not_full = PIO_INTR_SM2_TXNFULL_LSB, |
| pis_sm3_tx_fifo_not_full = PIO_INTR_SM3_TXNFULL_LSB, |
| pis_sm0_rx_fifo_not_empty = PIO_INTR_SM0_RXNEMPTY_LSB, |
| pis_sm1_rx_fifo_not_empty = PIO_INTR_SM1_RXNEMPTY_LSB, |
| pis_sm2_rx_fifo_not_empty = PIO_INTR_SM2_RXNEMPTY_LSB, |
| pis_sm3_rx_fifo_not_empty = PIO_INTR_SM3_RXNEMPTY_LSB, |
| }; |
| |
| /*! \brief Enable/Disable a single source on a PIO's IRQ 0 |
| * \ingroup hardware_pio |
| * |
| * \param pio The PIO instance; either \ref pio0 or \ref pio1 |
| * \param source the source number (see \ref pio_interrupt_source) |
| * \param enabled true to enable IRQ 0 for the source, false to disable. |
| */ |
| static inline void pio_set_irq0_source_enabled(PIO pio, enum pio_interrupt_source source, bool enabled) { |
| check_pio_param(pio); |
| invalid_params_if(PIO, source >= 12); |
| if (enabled) |
| hw_set_bits(&pio->inte0, 1u << source); |
| else |
| hw_clear_bits(&pio->inte0, 1u << source); |
| } |
| |
| /*! \brief Enable/Disable a single source on a PIO's IRQ 1 |
| * \ingroup hardware_pio |
| * |
| * \param pio The PIO instance; either \ref pio0 or \ref pio1 |
| * \param source the source number (see \ref pio_interrupt_source) |
| * \param enabled true to enable IRQ 0 for the source, false to disable. |
| */ |
| static inline void pio_set_irq1_source_enabled(PIO pio, enum pio_interrupt_source source, bool enabled) { |
| check_pio_param(pio); |
| invalid_params_if(PIO, source >= 12); |
| if (enabled) |
| hw_set_bits(&pio->inte1, 1u << source); |
| else |
| hw_clear_bits(&pio->inte1, 1u << source); |
| } |
| |
| /*! \brief Enable/Disable multiple sources on a PIO's IRQ 0 |
| * \ingroup hardware_pio |
| * |
| * \param pio The PIO instance; either \ref pio0 or \ref pio1 |
| * \param source_mask Mask of bits, one for each source number (see \ref pio_interrupt_source) to affect |
| * \param enabled true to enable all the sources specified in the mask on IRQ 0, false to disable all the sources specified in the mask on IRQ 0 |
| */ |
| static inline void pio_set_irq0_source_mask_enabled(PIO pio, uint32_t source_mask, bool enabled) { |
| check_pio_param(pio); |
| invalid_params_if(PIO, source_mask > PIO_INTR_BITS); |
| if (enabled) { |
| hw_set_bits(&pio->inte0, source_mask); |
| } else { |
| hw_clear_bits(&pio->inte0, source_mask); |
| } |
| } |
| |
| /*! \brief Enable/Disable multiple sources on a PIO's IRQ 1 |
| * \ingroup hardware_pio |
| * |
| * \param pio The PIO instance; either \ref pio0 or \ref pio1 |
| * \param source_mask Mask of bits, one for each source number (see \ref pio_interrupt_source) to affect |
| * \param enabled true to enable all the sources specified in the mask on IRQ 1, false to disable all the source specified in the mask on IRQ 1 |
| */ |
| static inline void pio_set_irq1_source_mask_enabled(PIO pio, uint32_t source_mask, bool enabled) { |
| check_pio_param(pio); |
| invalid_params_if(PIO, source_mask > PIO_INTR_BITS); |
| if (enabled) { |
| hw_set_bits(&pio->inte1, source_mask); |
| } else { |
| hw_clear_bits(&pio->inte1, source_mask); |
| } |
| } |
| |
| /*! \brief Enable/Disable a single source on a PIO's specified (0/1) IRQ index |
| * \ingroup hardware_pio |
| * |
| * \param pio The PIO instance; either \ref pio0 or \ref pio1 |
| * \param irq_index the IRQ index; either 0 or 1 |
| * \param source the source number (see \ref pio_interrupt_source) |
| * \param enabled true to enable the source on the specified IRQ, false to disable. |
| */ |
| static inline void pio_set_irqn_source_enabled(PIO pio, uint irq_index, enum pio_interrupt_source source, bool enabled) { |
| invalid_params_if(PIO, irq_index > 1); |
| if (irq_index) { |
| pio_set_irq1_source_enabled(pio, source, enabled); |
| } else { |
| pio_set_irq0_source_enabled(pio, source, enabled); |
| } |
| } |
| |
| /*! \brief Enable/Disable multiple sources on a PIO's specified (0/1) IRQ index |
| * \ingroup hardware_pio |
| * |
| * \param pio The PIO instance; either \ref pio0 or \ref pio1 |
| * \param irq_index the IRQ index; either 0 or 1 |
| * \param source_mask Mask of bits, one for each source number (see \ref pio_interrupt_source) to affect |
| * \param enabled true to enable all the sources specified in the mask on the specified IRQ, false to disable all the sources specified in the mask on the specified IRQ |
| */ |
| static inline void pio_set_irqn_source_mask_enabled(PIO pio, uint irq_index, uint32_t source_mask, bool enabled) { |
| invalid_params_if(PIO, irq_index > 1); |
| if (irq_index) { |
| pio_set_irq0_source_mask_enabled(pio, source_mask, enabled); |
| } else { |
| pio_set_irq1_source_mask_enabled(pio, source_mask, enabled); |
| } |
| } |
| |
| /*! \brief Determine if a particular PIO interrupt is set |
| * \ingroup hardware_pio |
| * |
| * \param pio The PIO instance; either \ref pio0 or \ref pio1 |
| * \param pio_interrupt_num the PIO interrupt number 0-7 |
| * \return true if corresponding PIO interrupt is currently set |
| */ |
| static inline bool pio_interrupt_get(PIO pio, uint pio_interrupt_num) { |
| check_pio_param(pio); |
| invalid_params_if(PIO, pio_interrupt_num >= 8); |
| return pio->irq & (1u << pio_interrupt_num); |
| } |
| |
| /*! \brief Clear a particular PIO interrupt |
| * \ingroup hardware_pio |
| * |
| * \param pio The PIO instance; either \ref pio0 or \ref pio1 |
| * \param pio_interrupt_num the PIO interrupt number 0-7 |
| */ |
| static inline void pio_interrupt_clear(PIO pio, uint pio_interrupt_num) { |
| check_pio_param(pio); |
| invalid_params_if(PIO, pio_interrupt_num >= 8); |
| hw_set_bits(&pio->irq, (1u << pio_interrupt_num)); |
| } |
| |
| /*! \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 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 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); |
| uint16_t div_int; |
| uint8_t div_frac; |
| pio_calculate_clkdiv_from_float(div, &div_int, &div_frac); |
| pio_sm_set_clkdiv_int_frac(pio, sm, div_int, div_frac); |
| } |
| |
| /*! \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); |
| |
| /*! \brief Determine if a PIO state machine is claimed |
| * \ingroup hardware_pio |
| * |
| * \param pio The PIO instance; either \ref pio0 or \ref pio1 |
| * \param sm State machine index (0..3) |
| * \return true if claimed, false otherwise |
| * \see pio_sm_claim |
| * \see pio_claim_sm_mask |
| */ |
| bool pio_sm_is_claimed(PIO pio, uint sm); |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif // _PIO_H_ |