mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
cpu/rpx0xx: Add PIO I2C implementation
This commit is contained in:
parent
117a901d6f
commit
116c579cb5
@ -13,6 +13,7 @@ config BOARD_RPI_PICO
|
|||||||
default y
|
default y
|
||||||
select CPU_MODEL_RP2040
|
select CPU_MODEL_RP2040
|
||||||
select HAS_PERIPH_ADC
|
select HAS_PERIPH_ADC
|
||||||
|
select HAS_PERIPH_I2C
|
||||||
select HAS_PERIPH_UART
|
select HAS_PERIPH_UART
|
||||||
select HAS_PERIPH_SPI
|
select HAS_PERIPH_SPI
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ CPU := rpx0xx
|
|||||||
|
|
||||||
# Put defined MCU peripherals here (in alphabetical order)
|
# Put defined MCU peripherals here (in alphabetical order)
|
||||||
FEATURES_PROVIDED += periph_adc
|
FEATURES_PROVIDED += periph_adc
|
||||||
|
FEATURES_PROVIDED += periph_i2c
|
||||||
FEATURES_PROVIDED += periph_spi
|
FEATURES_PROVIDED += periph_spi
|
||||||
FEATURES_PROVIDED += periph_timer
|
FEATURES_PROVIDED += periph_timer
|
||||||
FEATURES_PROVIDED += periph_uart
|
FEATURES_PROVIDED += periph_uart
|
||||||
|
@ -28,6 +28,16 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Silences the warning when an unsigned value is compared to 0
|
||||||
|
*
|
||||||
|
* This can be deleted when I2C is properly implemented.
|
||||||
|
*/
|
||||||
|
static inline unsigned _periph_numof_is_unsigned_0(void)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static const uart_conf_t uart_config[] = {
|
static const uart_conf_t uart_config[] = {
|
||||||
{
|
{
|
||||||
.dev = UART0,
|
.dev = UART0,
|
||||||
@ -110,6 +120,16 @@ static const adc_conf_t adc_config[] = {
|
|||||||
#define ADC_NUMOF ARRAY_SIZE(adc_config)
|
#define ADC_NUMOF ARRAY_SIZE(adc_config)
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @name I2C configuration
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @brief Number of I2C interfaces
|
||||||
|
*/
|
||||||
|
#define I2C_NUMOF _periph_numof_is_unsigned_0()
|
||||||
|
/** @} */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @name PIO configuration
|
* @name PIO configuration
|
||||||
* @{
|
* @{
|
||||||
@ -136,6 +156,24 @@ static const pio_conf_t pio_config[] = {
|
|||||||
#define PIO_1_ISR1 isr_pio11 /**< ISR name of PIO 1 IRQ 1 */
|
#define PIO_1_ISR1 isr_pio11 /**< ISR name of PIO 1 IRQ 1 */
|
||||||
|
|
||||||
#define PIO_NUMOF ARRAY_SIZE(pio_config) /**< Number of PIOs */
|
#define PIO_NUMOF ARRAY_SIZE(pio_config) /**< Number of PIOs */
|
||||||
|
|
||||||
|
#if defined(PIO_I2C_CONFIG) || defined(DOXYGEN)
|
||||||
|
/**
|
||||||
|
* @brief PIO I2C configuration
|
||||||
|
*
|
||||||
|
* PIO_I2C_CONFIG should be defined during the build process to fit
|
||||||
|
* the users pin selection.
|
||||||
|
*/
|
||||||
|
static const pio_i2c_conf_t pio_i2c_config[] = {
|
||||||
|
PIO_I2C_CONFIG
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* @brief Number of PIO I2C configurations
|
||||||
|
*/
|
||||||
|
#define PIO_I2C_NUMOF ARRAY_SIZE(pio_i2c_config)
|
||||||
|
#else
|
||||||
|
#define pio_i2c_config ((pio_i2c_conf_t *)NULL)
|
||||||
|
#endif
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -16,6 +16,7 @@ config CPU_FAM_RPX0XX
|
|||||||
select HAS_PERIPH_TIMER_PERIODIC
|
select HAS_PERIPH_TIMER_PERIODIC
|
||||||
select HAS_PERIPH_UART_MODECFG
|
select HAS_PERIPH_UART_MODECFG
|
||||||
select HAS_PERIPH_UART_RECONFIGURE
|
select HAS_PERIPH_UART_RECONFIGURE
|
||||||
|
select HAS_PIO_I2C
|
||||||
|
|
||||||
config CPU_FAM
|
config CPU_FAM
|
||||||
default "RPX0XX" if CPU_FAM_RPX0XX
|
default "RPX0XX" if CPU_FAM_RPX0XX
|
||||||
|
@ -2,4 +2,8 @@ MODULE = cpu
|
|||||||
|
|
||||||
DIRS = $(RIOTCPU)/cortexm_common periph
|
DIRS = $(RIOTCPU)/cortexm_common periph
|
||||||
|
|
||||||
|
ifneq (,$(filter pio_i2c,$(USEMODULE)))
|
||||||
|
DIRS += pio/i2c
|
||||||
|
endif
|
||||||
|
|
||||||
include $(RIOTBASE)/Makefile.base
|
include $(RIOTBASE)/Makefile.base
|
||||||
|
@ -2,4 +2,9 @@ ifneq (,$(filter pio_%,$(USEMODULE)))
|
|||||||
USEMODULE += periph_pio
|
USEMODULE += periph_pio
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
ifneq (,$(filter pio_i2c periph_i2c,$(FEATURES_USED)))
|
||||||
|
DEFAULT_MODULE += pio_autostart_i2c
|
||||||
|
USEMODULE += pio_i2c
|
||||||
|
endif
|
||||||
|
|
||||||
include $(RIOTCPU)/cortexm_common/Makefile.dep
|
include $(RIOTCPU)/cortexm_common/Makefile.dep
|
||||||
|
@ -9,3 +9,4 @@ FEATURES_PROVIDED += periph_pio
|
|||||||
FEATURES_PROVIDED += periph_timer_periodic
|
FEATURES_PROVIDED += periph_timer_periodic
|
||||||
FEATURES_PROVIDED += periph_uart_reconfigure
|
FEATURES_PROVIDED += periph_uart_reconfigure
|
||||||
FEATURES_PROVIDED += periph_uart_modecfg
|
FEATURES_PROVIDED += periph_uart_modecfg
|
||||||
|
FEATURES_PROVIDED += pio_i2c
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "vendor/RP2040.h"
|
#include "vendor/RP2040.h"
|
||||||
#include "io_reg.h"
|
#include "io_reg.h"
|
||||||
#include "macros/units.h"
|
#include "macros/units.h"
|
||||||
|
#include "periph/pio.h" /* pio_t */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -440,6 +441,16 @@ typedef struct {
|
|||||||
IRQn_Type irqn1; /**< PIO IRQ1 interrupt number */
|
IRQn_Type irqn1; /**< PIO IRQ1 interrupt number */
|
||||||
} pio_conf_t;
|
} pio_conf_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PIO I2C configuration type
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
pio_t pio; /**< PIO number of the PIO to run this configuration */
|
||||||
|
gpio_t sda; /**< Pin to use as SDA pin */
|
||||||
|
gpio_t scl; /**< Pin to use as SCL pin */
|
||||||
|
unsigned irq; /**< PIO IRQ line to use */
|
||||||
|
} pio_i2c_conf_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the PAD control register for the given GPIO pin as word
|
* @brief Get the PAD control register for the given GPIO pin as word
|
||||||
*
|
*
|
||||||
|
129
cpu/rpx0xx/periph/i2c.c
Normal file
129
cpu/rpx0xx/periph/i2c.c
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Otto-von-Guericke Universität Magdeburg
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup cpu_rpx0xx
|
||||||
|
* @ingroup drivers_periph_i2c
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief Low-level I2C driver implementation
|
||||||
|
*
|
||||||
|
* @note This driver is so far only a dummy implementation
|
||||||
|
* to test the PIO I2C interface.
|
||||||
|
*
|
||||||
|
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include "periph_conf.h"
|
||||||
|
#include "periph/i2c.h"
|
||||||
|
#include "periph/pio/i2c.h"
|
||||||
|
|
||||||
|
#define ENABLE_DEBUG 0
|
||||||
|
#include "debug.h"
|
||||||
|
|
||||||
|
void i2c_init(i2c_t dev)
|
||||||
|
{
|
||||||
|
if (IS_USED(MODULE_PIO_I2C) && dev >= I2C_NUMOF) {
|
||||||
|
pio_i2c_bus_t *i2c = pio_i2c_get(dev - I2C_NUMOF);
|
||||||
|
assert(i2c);
|
||||||
|
pio_t pio = pio_i2c_config[dev - I2C_NUMOF].pio;
|
||||||
|
if (pio_i2c_init_program(pio)) {
|
||||||
|
DEBUG("[i2c] init: PIO program allocation failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pio_sm_t sm = pio_i2c_sm_lock(pio, i2c);
|
||||||
|
if (sm < 0) {
|
||||||
|
DEBUG("[i2c] init: PIO state machine allocation failed\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (pio_i2c_init(i2c, pio_i2c_get_program(pio),
|
||||||
|
pio_i2c_config[dev - I2C_NUMOF].sda,
|
||||||
|
pio_i2c_config[dev - I2C_NUMOF].scl,
|
||||||
|
pio_i2c_config[dev - I2C_NUMOF].irq)) {
|
||||||
|
DEBUG("[i2c] init: PIO I2C initialization failed\n");
|
||||||
|
pio_i2c_sm_unlock(i2c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_acquire(i2c_t dev)
|
||||||
|
{
|
||||||
|
if (IS_USED(MODULE_PIO_I2C) && dev >= I2C_NUMOF) {
|
||||||
|
pio_i2c_bus_t *i2c = pio_i2c_get(dev - I2C_NUMOF);
|
||||||
|
if (i2c) {
|
||||||
|
pio_i2c_acquire(i2c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void i2c_release(i2c_t dev)
|
||||||
|
{
|
||||||
|
if (IS_USED(MODULE_PIO_I2C) && dev >= I2C_NUMOF) {
|
||||||
|
pio_i2c_bus_t *i2c = pio_i2c_get(dev - I2C_NUMOF);
|
||||||
|
if (i2c) {
|
||||||
|
pio_i2c_release(i2c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data,
|
||||||
|
size_t len, uint8_t flags)
|
||||||
|
{
|
||||||
|
if (IS_USED(MODULE_PIO_I2C) && dev >= I2C_NUMOF) {
|
||||||
|
pio_i2c_bus_t *i2c = pio_i2c_get(dev - I2C_NUMOF);
|
||||||
|
return i2c ? pio_i2c_read_bytes(i2c->pio, i2c->sm, addr, data, len, flags) : -EINVAL;
|
||||||
|
}
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i2c_read_regs(i2c_t dev, uint16_t addr, uint16_t reg,
|
||||||
|
void *data, size_t len, uint8_t flags)
|
||||||
|
{
|
||||||
|
if (IS_USED(MODULE_PIO_I2C) && dev >= I2C_NUMOF) {
|
||||||
|
pio_i2c_bus_t *i2c = pio_i2c_get(dev - I2C_NUMOF);
|
||||||
|
return i2c ? pio_i2c_read_regs(i2c->pio, i2c->sm, addr, reg, data, len, flags) : -EINVAL;
|
||||||
|
}
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i2c_read_reg(i2c_t dev, uint16_t addr, uint16_t reg,
|
||||||
|
void *data, uint8_t flags)
|
||||||
|
{
|
||||||
|
return i2c_read_regs(dev, addr, reg, data, 1, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data,
|
||||||
|
size_t len, uint8_t flags)
|
||||||
|
{
|
||||||
|
if (IS_USED(MODULE_PIO_I2C) && dev >= I2C_NUMOF) {
|
||||||
|
pio_i2c_bus_t *i2c = pio_i2c_get(dev - I2C_NUMOF);
|
||||||
|
return i2c ? pio_i2c_write_bytes(i2c->pio, i2c->sm, addr, data, len, flags) : -EINVAL;
|
||||||
|
}
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i2c_write_regs(i2c_t dev, uint16_t addr, uint16_t reg,
|
||||||
|
const void *data, size_t len, uint8_t flags)
|
||||||
|
{
|
||||||
|
if (IS_USED(MODULE_PIO_I2C) && dev >= I2C_NUMOF) {
|
||||||
|
pio_i2c_bus_t *i2c = pio_i2c_get(dev - I2C_NUMOF);
|
||||||
|
return i2c ? pio_i2c_write_regs(i2c->pio, i2c->sm, addr, reg, data, len, flags) : -EINVAL;
|
||||||
|
}
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i2c_write_reg(i2c_t dev, uint16_t addr, uint16_t reg,
|
||||||
|
uint8_t data, uint8_t flags)
|
||||||
|
{
|
||||||
|
return i2c_write_regs(dev, addr, reg, &data, 1, flags);
|
||||||
|
}
|
@ -28,6 +28,7 @@
|
|||||||
#include "bitarithm.h"
|
#include "bitarithm.h"
|
||||||
#include "io_reg.h"
|
#include "io_reg.h"
|
||||||
#include "pio/pio.h"
|
#include "pio/pio.h"
|
||||||
|
#include "periph/pio/i2c.h"
|
||||||
#define ENABLE_DEBUG 0
|
#define ENABLE_DEBUG 0
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
@ -141,6 +142,9 @@ void pio_init(pio_t pio)
|
|||||||
|
|
||||||
void pio_start_programs(void)
|
void pio_start_programs(void)
|
||||||
{
|
{
|
||||||
|
if (IS_USED(MODULE_PIO_AUTOSTART_I2C)) {
|
||||||
|
pio_i2c_start_programs();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pio_sm_t pio_sm_lock(pio_t pio)
|
pio_sm_t pio_sm_lock(pio_t pio)
|
||||||
|
6
cpu/rpx0xx/pio/i2c/Makefile
Normal file
6
cpu/rpx0xx/pio/i2c/Makefile
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
MODULE = pio_i2c
|
||||||
|
|
||||||
|
include $(RIOTBASE)/Makefile.base
|
||||||
|
include $(RIOTMAKE)/pio.inc.mk
|
||||||
|
|
||||||
|
i2c.c: i2c.pio.h i2c_set_scl_sda.pio.h
|
478
cpu/rpx0xx/pio/i2c/i2c.c
Normal file
478
cpu/rpx0xx/pio/i2c/i2c.c
Normal file
@ -0,0 +1,478 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Otto-von-Guericke Universität Magdeburg
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @ingroup cpu_rpx0xx
|
||||||
|
* @{
|
||||||
|
*
|
||||||
|
* @file
|
||||||
|
* @brief PIO program to emulate an I2C interface
|
||||||
|
*
|
||||||
|
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
|
||||||
|
*
|
||||||
|
* @}
|
||||||
|
*/
|
||||||
|
#include <errno.h>
|
||||||
|
#include "byteorder.h"
|
||||||
|
#include "periph_conf.h"
|
||||||
|
#include "periph/gpio.h"
|
||||||
|
#include "periph/pio/i2c.h"
|
||||||
|
#include "pio/pio.h"
|
||||||
|
#include "i2c_set_scl_sda.pio.h"
|
||||||
|
#include "i2c.pio.h"
|
||||||
|
|
||||||
|
#define I2C_PIO_IRQN 0 /* use irq flag 0 */
|
||||||
|
#define I2C_PIO_IRQN_SM(sm) PIO_IRQ_REL_MASK(I2C_PIO_IRQN, sm) /* IRQ is relative */
|
||||||
|
|
||||||
|
#define INSTR_I2C_SCL0_SDA0 (((pio_instr_t[])PIO_SET_SCL_SDA_PROGRAM)[0])
|
||||||
|
#define INSTR_I2C_SCL0_SDA1 (((pio_instr_t[])PIO_SET_SCL_SDA_PROGRAM)[1])
|
||||||
|
#define INSTR_I2C_SCL1_SDA0 (((pio_instr_t[])PIO_SET_SCL_SDA_PROGRAM)[2])
|
||||||
|
#define INSTR_I2C_SCL1_SDA1 (((pio_instr_t[])PIO_SET_SCL_SDA_PROGRAM)[3])
|
||||||
|
|
||||||
|
#define I2C_INSTR_FRAME(icount) ((uint32_t)(((uint32_t)(icount)) << 10))
|
||||||
|
#define I2C_INSTR(instr) ((uint32_t)(instr))
|
||||||
|
#define I2C_DATA_FRAME(final, data, nak) \
|
||||||
|
((uint32_t)((((uint32_t)(final)) << 9) | \
|
||||||
|
(((uint32_t)(data)) << 1) | \
|
||||||
|
((uint32_t)(nak))))
|
||||||
|
|
||||||
|
enum {
|
||||||
|
I2C_REPSTART = 0x40 /* make sure this flag is not used by the I2C driver */
|
||||||
|
};
|
||||||
|
|
||||||
|
#if defined(PIO_I2C_NUMOF) || defined(DOXYGEN)
|
||||||
|
/**
|
||||||
|
* @brief Internal PIO I2C buses initialized from PIO_I2C_CONFIG
|
||||||
|
*/
|
||||||
|
static pio_i2c_bus_t _bus[PIO_I2C_NUMOF];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Shared programs between I2C buses per PIO
|
||||||
|
*/
|
||||||
|
static pio_program_i2c_t _prog[PIO_NUMOF];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PIO interrupt mask for all state machines per PIO
|
||||||
|
*/
|
||||||
|
static volatile uint32_t _irq[PIO_NUMOF];
|
||||||
|
|
||||||
|
static void pio_i2c_init_pins(pio_t pio, pio_sm_t sm, gpio_t sda, gpio_t scl)
|
||||||
|
{
|
||||||
|
const gpio_pad_ctrl_t pad_ctrl = {
|
||||||
|
.pull_up_enable = 1,
|
||||||
|
.input_enable = 1,
|
||||||
|
.drive_strength = DRIVE_STRENGTH_4MA,
|
||||||
|
.schmitt_trig_enable = 1,
|
||||||
|
};
|
||||||
|
const gpio_io_ctrl_t io_ctrl = {
|
||||||
|
.function_select = pio ? FUNCTION_SELECT_PIO1 : FUNCTION_SELECT_PIO0,
|
||||||
|
.output_enable_override = OUTPUT_ENABLE_OVERRIDE_INVERT
|
||||||
|
};
|
||||||
|
/* Assume that SDA is mapped as the first set pin (bit 0),
|
||||||
|
and SCL is mapped as the second set pin (bit 1).
|
||||||
|
Try to avoid glitching the bus while connecting the IOs. Get things set
|
||||||
|
up so that pin is driven down when PIO asserts OE low, and pulled up
|
||||||
|
otherwise. */
|
||||||
|
gpio_set_pad_config(scl, pad_ctrl);
|
||||||
|
gpio_set_pad_config(sda, pad_ctrl);
|
||||||
|
pio_sm_set_pins_with_mask(pio, sm, (1u << sda) | (1u << scl), (1u << sda) | (1u << scl));
|
||||||
|
pio_sm_set_pindirs_with_mask(pio, sm, (1u << sda) | (1u << scl), (1u << sda) | (1u << scl));
|
||||||
|
gpio_set_io_config(scl, io_ctrl);
|
||||||
|
gpio_set_io_config(sda, io_ctrl);
|
||||||
|
pio_sm_set_pins_with_mask(pio, sm, 0, (1u << sda) | (1u << scl));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool pio_i2c_check_error(pio_t pio, pio_sm_t sm)
|
||||||
|
{
|
||||||
|
bool error;
|
||||||
|
uint32_t status = irq_disable();
|
||||||
|
error = !!(_irq[pio] & I2C_PIO_IRQN_SM(sm));
|
||||||
|
irq_restore(status);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void pio_i2c_clear_error(pio_t pio, pio_sm_t sm)
|
||||||
|
{
|
||||||
|
uint32_t status = irq_disable();
|
||||||
|
_irq[pio] &= ~I2C_PIO_IRQN_SM(sm);
|
||||||
|
irq_restore(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pio_i2c_resume_after_error(pio_t pio, pio_sm_t sm)
|
||||||
|
{
|
||||||
|
PIO0_Type *dev = pio_config[pio].dev;
|
||||||
|
pio_sm_ctrl_regs_t *ctrl = &PIO_SM_CTRL_BASE(dev)[sm];
|
||||||
|
uint32_t jmp_addr = (ctrl->execctrl & PIO0_SM0_EXECCTRL_WRAP_BOTTOM_Msk)
|
||||||
|
>> PIO0_SM0_EXECCTRL_WRAP_BOTTOM_Pos;
|
||||||
|
pio_sm_exec(pio, sm, pio_inst_jmp(PIO_INST_JMP_COND_NONE, jmp_addr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool pio_i2c_check_idle(pio_t pio, pio_sm_t sm)
|
||||||
|
{
|
||||||
|
PIO0_Type *dev = pio_config[pio].dev;
|
||||||
|
pio_sm_ctrl_regs_t *ctrl = &PIO_SM_CTRL_BASE(dev)[sm];
|
||||||
|
uint32_t idle = (ctrl->execctrl & PIO0_SM0_EXECCTRL_WRAP_BOTTOM_Msk)
|
||||||
|
>> PIO0_SM0_EXECCTRL_WRAP_BOTTOM_Pos;
|
||||||
|
return ctrl->addr == idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _pio_i2c_put(pio_t pio, pio_sm_t sm, uint32_t word)
|
||||||
|
{
|
||||||
|
while (pio_sm_transmit_word(pio, sm, word)) {
|
||||||
|
/* do not block on unexpected receive */
|
||||||
|
pio_sm_receive_word(pio, sm, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pio_i2c_start(pio_t pio, pio_sm_t sm)
|
||||||
|
{
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR_FRAME(1) << 16);
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR(INSTR_I2C_SCL1_SDA0) << 16);
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR(INSTR_I2C_SCL0_SDA0) << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pio_i2c_stop(pio_t pio, pio_sm_t sm)
|
||||||
|
{
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR_FRAME(2) << 16);
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR(INSTR_I2C_SCL0_SDA0) << 16);
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR(INSTR_I2C_SCL1_SDA0) << 16);
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR(INSTR_I2C_SCL1_SDA1) << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pio_i2c_repstart(pio_t pio, pio_sm_t sm)
|
||||||
|
{
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR_FRAME(3) << 16);
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR(INSTR_I2C_SCL0_SDA1) << 16);
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR(INSTR_I2C_SCL1_SDA1) << 16);
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR(INSTR_I2C_SCL1_SDA0) << 16);
|
||||||
|
_pio_i2c_put(pio, sm, I2C_INSTR(INSTR_I2C_SCL0_SDA0) << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _irq_sm(pio_t pio, unsigned irq)
|
||||||
|
{
|
||||||
|
_irq[pio] |= PIO_IRQ_MASK(irq);
|
||||||
|
pio_sm_t sm = pio_irq_rel_sm(I2C_PIO_IRQN, irq);
|
||||||
|
pio_i2c_resume_after_error(pio, sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static pio_isr_sm_vec_t _vec = {
|
||||||
|
.sm = _irq_sm,
|
||||||
|
};
|
||||||
|
|
||||||
|
pio_i2c_bus_t *pio_i2c_get(pio_i2c_t id)
|
||||||
|
{
|
||||||
|
#ifdef PIO_I2C_NUMOF
|
||||||
|
return (id < PIO_I2C_NUMOF) ? &_bus[id] : NULL;
|
||||||
|
#endif
|
||||||
|
(void)id;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned pio_i2c_numof(void)
|
||||||
|
{
|
||||||
|
#ifdef PIO_I2C_NUMOF
|
||||||
|
return PIO_I2C_NUMOF;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pio_program_i2c_t *pio_i2c_get_program(pio_t pio)
|
||||||
|
{
|
||||||
|
assert(pio < PIO_NUMOF);
|
||||||
|
return &_prog[pio];
|
||||||
|
}
|
||||||
|
|
||||||
|
int pio_i2c_init_program(pio_t pio)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
if (!_prog[pio].base.instr_numof) {
|
||||||
|
_prog[pio].base = pio_i2c_create_program();
|
||||||
|
}
|
||||||
|
if (_prog[pio].base.location == PIO_PROGRAM_NOT_LOADED) {
|
||||||
|
if ((ret = pio_alloc_program(pio, &_prog[pio].base))) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
if (!_prog[pio].base.written) {
|
||||||
|
if ((ret = pio_i2c_write_program(pio, &_prog[pio]))) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_i2c_deinit_program(pio_t pio)
|
||||||
|
{
|
||||||
|
pio_free_program(pio, &_prog[pio].base);
|
||||||
|
}
|
||||||
|
|
||||||
|
pio_sm_t pio_i2c_sm_lock(pio_t pio, pio_i2c_bus_t *i2c)
|
||||||
|
{
|
||||||
|
pio_sm_t sm = i2c->sm;
|
||||||
|
if (!(_prog[pio].ref_mask & PIO_SM_MASK(sm))) {
|
||||||
|
if ((sm = pio_sm_lock(pio)) >= 0) {
|
||||||
|
_prog[pio].ref_mask |= PIO_SM_MASK(sm);
|
||||||
|
i2c->pio = pio;
|
||||||
|
i2c->sm = sm;
|
||||||
|
mutex_init(&i2c->mtx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_i2c_sm_unlock(pio_i2c_bus_t *i2c)
|
||||||
|
{
|
||||||
|
_prog[i2c->pio].ref_mask &= ~PIO_SM_MASK(i2c->sm);
|
||||||
|
pio_sm_unlock(i2c->pio, i2c->sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_i2c_start_programs(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < (int)pio_i2c_numof(); i++) {
|
||||||
|
pio_i2c_bus_t *i2c = pio_i2c_get(i);
|
||||||
|
if (i2c) {
|
||||||
|
pio_sm_start(i2c->pio, i2c->sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_i2c_stop_programs(void)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < (int)pio_i2c_numof(); i++) {
|
||||||
|
pio_i2c_bus_t *i2c = pio_i2c_get(i);
|
||||||
|
if (i2c) {
|
||||||
|
pio_sm_stop(i2c->pio, i2c->sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int pio_i2c_write_program(pio_t pio, pio_program_i2c_t *pro)
|
||||||
|
{
|
||||||
|
pio_instr_t instr[] = PIO_I2C_PROGRAM;
|
||||||
|
int ret;
|
||||||
|
if ((ret = pio_write_program(pio, &pro->base, instr))) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pio_i2c_init(pio_i2c_bus_t *bus,
|
||||||
|
const pio_program_i2c_t *pro,
|
||||||
|
gpio_t sda, gpio_t scl, unsigned irq)
|
||||||
|
{
|
||||||
|
pio_program_conf_t conf = PIO_I2C_PROGRAM_CONF;
|
||||||
|
pio_t pio = bus->pio;
|
||||||
|
pio_sm_t sm = bus->sm;
|
||||||
|
int ret;
|
||||||
|
if (sda >= 32 || scl >= 32 || irq >= 2) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
if ((ret = pio_sm_init_common(pio, sm, &pro->base, &conf))) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
pio_sm_set_out_pins(pio, sm, sda, 1);
|
||||||
|
pio_sm_set_set_pins(pio, sm, sda, 2);
|
||||||
|
pio_sm_set_in_pins(pio, sm, sda);
|
||||||
|
pio_sm_set_sideset_pins(pio, sm, scl);
|
||||||
|
pio_sm_set_out_shift(pio, sm, false, true, 16);
|
||||||
|
pio_sm_set_in_shift(pio, sm, false, true, 8);
|
||||||
|
pio_sm_set_jmp_pin(pio, sm, sda);
|
||||||
|
pio_sm_set_clkdiv(pio, sm, pio_sm_clkdiv(3200000));
|
||||||
|
pio_i2c_init_pins(pio, sm, sda, scl);
|
||||||
|
pio_irq_clear(pio, pio_irq_rel_index(I2C_PIO_IRQN, sm));
|
||||||
|
/* Given the absolute interrupt index I2C_PIO_IRQN
|
||||||
|
and the relative interrupt instruction in the PIO program,
|
||||||
|
sm will raise the interrupt flag pio_irq_rel_index(I2C_PIO_IRQN, sm)
|
||||||
|
in the IRQ register. */
|
||||||
|
pio_set_isr_sm_vec(pio, pio_irq_rel_index(I2C_PIO_IRQN, sm), &_vec);
|
||||||
|
pio_irq_enable(pio, irq, PIO_IRQ_SM_0 << pio_irq_rel_index(I2C_PIO_IRQN, sm));
|
||||||
|
pio_sm_clear_fifos(pio, sm);
|
||||||
|
pio_sm_restart(pio, sm);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_i2c_acquire(pio_i2c_bus_t *bus)
|
||||||
|
{
|
||||||
|
assert(bus);
|
||||||
|
mutex_lock(&bus->mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pio_i2c_release(pio_i2c_bus_t *bus)
|
||||||
|
{
|
||||||
|
assert(bus);
|
||||||
|
mutex_unlock(&bus->mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pio_i2c_read_regs(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
uint16_t reg, void *data, size_t len, uint8_t flags)
|
||||||
|
{
|
||||||
|
if (flags & (I2C_NOSTOP | I2C_NOSTART)) {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
reg = htons(reg);
|
||||||
|
int error;
|
||||||
|
if ((error = pio_i2c_write_bytes(pio, sm, addr,
|
||||||
|
(flags & I2C_REG16) ? ((uint8_t *)®)
|
||||||
|
: ((uint8_t *)®) + 1,
|
||||||
|
(flags & I2C_REG16) ? 2 : 1,
|
||||||
|
flags | I2C_NOSTOP))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = pio_i2c_read_bytes(pio, sm, addr, data, len,
|
||||||
|
flags | I2C_REPSTART))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pio_i2c_read_bytes(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
void *data, size_t len, uint8_t flags)
|
||||||
|
{
|
||||||
|
bool first = true;
|
||||||
|
unsigned len_rx = len;
|
||||||
|
uint32_t word;
|
||||||
|
int error = 0; /* potential error */
|
||||||
|
int status = 0; /* actual error */
|
||||||
|
pio_i2c_clear_error(pio, sm);
|
||||||
|
if (flags & I2C_ADDR10) {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
addr = (uint8_t)addr;
|
||||||
|
if (!(flags & I2C_NOSTART)) {
|
||||||
|
if (flags & I2C_REPSTART) {
|
||||||
|
pio_i2c_repstart(pio, sm);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pio_i2c_start(pio, sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!pio_sm_receive_word(pio, sm, NULL)) {}
|
||||||
|
if (!(flags & I2C_NOSTART)) {
|
||||||
|
_pio_i2c_put(pio, sm, I2C_DATA_FRAME(0, (addr << 1) | I2C_READ, 1) << 16);
|
||||||
|
error = -ENXIO;
|
||||||
|
len_rx += 1;
|
||||||
|
while (!pio_sm_tx_fifo_empty(pio, sm) || !pio_i2c_check_idle(pio, sm)) {}
|
||||||
|
if ((status = pio_i2c_check_error(pio, sm))) {
|
||||||
|
pio_sm_clear_fifos(pio, sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!pio_sm_tx_fifo_empty(pio, sm) || !pio_i2c_check_idle(pio, sm)) {}
|
||||||
|
while ((len_rx || len) && !status) {
|
||||||
|
if ((status = pio_i2c_check_error(pio, sm))) {
|
||||||
|
pio_sm_clear_fifos(pio, sm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* len == 1 means final (last byte in transfer) */
|
||||||
|
/* 0xff is a dummy data byte */
|
||||||
|
/* Any NAK except for the last byte will cause the transfer to fail */
|
||||||
|
/* see frame documentation in i2c.pio */
|
||||||
|
if (len && !pio_sm_transmit_word(pio, sm, I2C_DATA_FRAME(len == 1, 0xff, len == 1) << 16)) {
|
||||||
|
len--;
|
||||||
|
error = -EIO;
|
||||||
|
}
|
||||||
|
/* read a byte for every byte we sent */
|
||||||
|
if (len_rx && !pio_sm_receive_word(pio, sm, &word)) {
|
||||||
|
if (!first) {
|
||||||
|
*((uint8_t *)data) = (uint8_t)word;
|
||||||
|
data = ((uint8_t *)data) + 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
len_rx--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!pio_sm_tx_fifo_empty(pio, sm)) {}
|
||||||
|
if (!(flags & I2C_NOSTOP) || status || (status = pio_i2c_check_error(pio, sm))) {
|
||||||
|
pio_i2c_stop(pio, sm);
|
||||||
|
}
|
||||||
|
while (!pio_sm_tx_fifo_empty(pio, sm)) {}
|
||||||
|
return status ? error : (pio_i2c_check_error(pio, sm) ? -EIO : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pio_i2c_write_bytes(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
const void *data, size_t len, uint8_t flags)
|
||||||
|
{
|
||||||
|
unsigned len_rx = len;
|
||||||
|
int error = 0; /* potential error */
|
||||||
|
int status = 0; /* actual error */
|
||||||
|
pio_i2c_clear_error(pio, sm);
|
||||||
|
if (flags & I2C_ADDR10) {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
addr = (uint8_t)addr;
|
||||||
|
if (!(flags & I2C_NOSTART)) {
|
||||||
|
if (flags & I2C_REPSTART) {
|
||||||
|
pio_i2c_repstart(pio, sm);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pio_i2c_start(pio, sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!pio_sm_receive_word(pio, sm, NULL)) {}
|
||||||
|
if (!(flags & I2C_NOSTART)) {
|
||||||
|
_pio_i2c_put(pio, sm, I2C_DATA_FRAME(0, (addr << 1), 1) << 16);
|
||||||
|
error = -ENXIO;
|
||||||
|
len_rx += 1;
|
||||||
|
while (!pio_sm_tx_fifo_empty(pio, sm) || !pio_i2c_check_idle(pio, sm)) {}
|
||||||
|
if ((status = pio_i2c_check_error(pio, sm))) {
|
||||||
|
pio_sm_clear_fifos(pio, sm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!pio_sm_tx_fifo_empty(pio, sm) || !pio_i2c_check_idle(pio, sm)) {}
|
||||||
|
while ((len_rx || len) && !status) {
|
||||||
|
if ((status = pio_i2c_check_error(pio, sm))) {
|
||||||
|
pio_sm_clear_fifos(pio, sm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* len == 1 means final (last byte in transfer) */
|
||||||
|
/* transmit next data byte */
|
||||||
|
/* don´t abort on NAK */
|
||||||
|
/* see frame documentation in i2c.pio */
|
||||||
|
if (len && !pio_sm_transmit_word(pio, sm, I2C_DATA_FRAME(len == 1, *(uint8_t *)data, 1) << 16)) {
|
||||||
|
len--;
|
||||||
|
data = ((uint8_t *)data) + 1;
|
||||||
|
error = -EIO;
|
||||||
|
}
|
||||||
|
/* dummy receive for every sent byte */
|
||||||
|
if (len_rx && !pio_sm_receive_word(pio, sm, NULL)) {
|
||||||
|
/* https://github.com/raspberrypi/pico-examples/issues/168
|
||||||
|
* Switching autopush on for Rx and off for Tx seems to bring a mysterious bug with it.
|
||||||
|
* So instead we also drain the Rx FIFO while transmitting. */
|
||||||
|
len_rx--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!pio_sm_tx_fifo_empty(pio, sm)) {}
|
||||||
|
if (!(flags & I2C_NOSTOP) || status || (status = pio_i2c_check_error(pio, sm))) {
|
||||||
|
pio_i2c_stop(pio, sm);
|
||||||
|
}
|
||||||
|
while (!pio_sm_tx_fifo_empty(pio, sm)) {}
|
||||||
|
return status ? error : (pio_i2c_check_error(pio, sm) ? -EIO : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int pio_i2c_write_regs(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
uint16_t reg, const void *data, size_t len, uint8_t flags)
|
||||||
|
{
|
||||||
|
if (flags & (I2C_NOSTOP | I2C_NOSTART)) {
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
reg = htons(reg);
|
||||||
|
int error;
|
||||||
|
if ((error = pio_i2c_write_bytes(pio, sm, addr,
|
||||||
|
(flags & I2C_REG16) ? ((uint8_t *)®)
|
||||||
|
: ((uint8_t *)®) + 1,
|
||||||
|
(flags & I2C_REG16) ? 2 : 1,
|
||||||
|
flags | I2C_NOSTOP))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
if ((error = pio_i2c_write_bytes(pio, sm, addr, data, len, flags | I2C_NOSTART))) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
121
cpu/rpx0xx/pio/i2c/i2c.pio
Normal file
121
cpu/rpx0xx/pio/i2c/i2c.pio
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
;
|
||||||
|
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
;
|
||||||
|
|
||||||
|
.program i2c
|
||||||
|
.side_set 1 opt pindirs
|
||||||
|
|
||||||
|
; TX Encoding:
|
||||||
|
; | 15:10 | 9 | 8:1 | 0 |
|
||||||
|
; | Instr | Final | Data | NAK |
|
||||||
|
;
|
||||||
|
; If Instr has a value n > 0, then this FIFO word has no
|
||||||
|
; data payload, and the next n + 1 words will be executed as instructions.
|
||||||
|
; Otherwise, shift out the 8 data bits, followed by the ACK bit.
|
||||||
|
;
|
||||||
|
; The Instr mechanism allows stop/start/repstart sequences to be programmed
|
||||||
|
; by the processor, and then carried out by the state machine at defined points
|
||||||
|
; in the datastream.
|
||||||
|
;
|
||||||
|
; The "Final" field should be set for the final byte in a transfer.
|
||||||
|
; This tells the state machine to ignore a NAK: if this field is not
|
||||||
|
; set, then any NAK will cause the state machine to halt and interrupt.
|
||||||
|
;
|
||||||
|
; Autopull should be enabled, with a threshold of 16.
|
||||||
|
; Autopush should be enabled, with a threshold of 8.
|
||||||
|
; The TX FIFO should be accessed with halfword writes, to ensure
|
||||||
|
; the data is immediately available in the OSR.
|
||||||
|
;
|
||||||
|
; Pin mapping:
|
||||||
|
; - Input pin 0 is SDA, 1 is SCL (if clock stretching used)
|
||||||
|
; - Jump pin is SDA
|
||||||
|
; - Side-set pin 0 is SCL
|
||||||
|
; - Set pin 0 is SDA
|
||||||
|
; - OUT pin 0 is SDA
|
||||||
|
; - SCL must be SDA + 1 (for wait mapping)
|
||||||
|
;
|
||||||
|
; The OE outputs should be inverted in the system IO controls!
|
||||||
|
; (It's possible for the inversion to be done in this program,
|
||||||
|
; but costs 2 instructions: 1 for inversion, and one to cope
|
||||||
|
; with the side effect of the MOV on TX shift counter.)
|
||||||
|
|
||||||
|
do_nack:
|
||||||
|
jmp y-- entry_point ; Continue if NAK was expected
|
||||||
|
irq wait 0 rel ; Otherwise stop, ask for help
|
||||||
|
|
||||||
|
do_byte:
|
||||||
|
set x, 7 ; Loop 8 times
|
||||||
|
bitloop:
|
||||||
|
out pindirs, 1 [7] ; Serialise write data (all-ones if reading)
|
||||||
|
nop side 1 [2] ; SCL rising edge
|
||||||
|
wait 1 pin, 1 [4] ; Allow clock to be stretched
|
||||||
|
in pins, 1 [7] ; Sample read data in middle of SCL pulse
|
||||||
|
jmp x-- bitloop side 0 [7] ; SCL falling edge
|
||||||
|
|
||||||
|
; Handle ACK pulse
|
||||||
|
out pindirs, 1 [7] ; On reads, we provide the ACK.
|
||||||
|
nop side 1 [7] ; SCL rising edge
|
||||||
|
wait 1 pin, 1 [7] ; Allow clock to be stretched
|
||||||
|
jmp pin do_nack side 0 [2] ; Test SDA for ACK/NAK, fall through if ACK
|
||||||
|
|
||||||
|
public entry_point:
|
||||||
|
.wrap_target
|
||||||
|
out x, 6 ; Unpack Instr count
|
||||||
|
out y, 1 ; Unpack the NAK ignore bit
|
||||||
|
jmp !x do_byte ; Instr == 0, this is a data record.
|
||||||
|
out null, 32 ; Instr > 0, remainder of this OSR is invalid
|
||||||
|
do_exec:
|
||||||
|
out exec, 16 ; Execute one instruction per FIFO word
|
||||||
|
jmp x-- do_exec ; Repeat n + 1 times
|
||||||
|
.wrap
|
||||||
|
|
||||||
|
% c-sdk {
|
||||||
|
|
||||||
|
#include "hardware/clocks.h"
|
||||||
|
#include "hardware/gpio.h"
|
||||||
|
|
||||||
|
|
||||||
|
static inline void i2c_program_init(PIO pio, uint sm, uint offset, uint pin_sda, uint pin_scl) {
|
||||||
|
assert(pin_scl == pin_sda + 1);
|
||||||
|
pio_sm_config c = i2c_program_get_default_config(offset);
|
||||||
|
|
||||||
|
// IO mapping
|
||||||
|
sm_config_set_out_pins(&c, pin_sda, 1);
|
||||||
|
sm_config_set_set_pins(&c, pin_sda, 1);
|
||||||
|
sm_config_set_in_pins(&c, pin_sda);
|
||||||
|
sm_config_set_sideset_pins(&c, pin_scl);
|
||||||
|
sm_config_set_jmp_pin(&c, pin_sda);
|
||||||
|
|
||||||
|
sm_config_set_out_shift(&c, false, true, 16);
|
||||||
|
sm_config_set_in_shift(&c, false, true, 8);
|
||||||
|
|
||||||
|
float div = (float)clock_get_hz(clk_sys) / (32 * 100000);
|
||||||
|
sm_config_set_clkdiv(&c, div);
|
||||||
|
|
||||||
|
// Try to avoid glitching the bus while connecting the IOs. Get things set
|
||||||
|
// up so that pin is driven down when PIO asserts OE low, and pulled up
|
||||||
|
// otherwise.
|
||||||
|
gpio_pull_up(pin_scl);
|
||||||
|
gpio_pull_up(pin_sda);
|
||||||
|
uint32_t both_pins = (1u << pin_sda) | (1u << pin_scl);
|
||||||
|
pio_sm_set_pins_with_mask(pio, sm, both_pins, both_pins);
|
||||||
|
pio_sm_set_pindirs_with_mask(pio, sm, both_pins, both_pins);
|
||||||
|
pio_gpio_init(pio, pin_sda);
|
||||||
|
gpio_set_oeover(pin_sda, GPIO_OVERRIDE_INVERT);
|
||||||
|
pio_gpio_init(pio, pin_scl);
|
||||||
|
gpio_set_oeover(pin_scl, GPIO_OVERRIDE_INVERT);
|
||||||
|
pio_sm_set_pins_with_mask(pio, sm, 0, both_pins);
|
||||||
|
|
||||||
|
// Clear IRQ flag before starting
|
||||||
|
hw_clear_bits(&pio->inte0, 1u << sm);
|
||||||
|
hw_clear_bits(&pio->inte1, 1u << sm);
|
||||||
|
pio->irq = 1u << sm;
|
||||||
|
|
||||||
|
// Configure and start SM
|
||||||
|
pio_sm_init(pio, sm, offset + i2c_offset_entry_point, &c);
|
||||||
|
pio_sm_set_enabled(pio, sm, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
%}
|
27
cpu/rpx0xx/pio/i2c/i2c_set_scl_sda.pio
Normal file
27
cpu/rpx0xx/pio/i2c/i2c_set_scl_sda.pio
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
;
|
||||||
|
; Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||||
|
;
|
||||||
|
; SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
;
|
||||||
|
|
||||||
|
.program set_scl_sda
|
||||||
|
.side_set 1 opt
|
||||||
|
|
||||||
|
; Assemble a table of instructions which software can select from, and pass
|
||||||
|
; into the FIFO, to issue START/STOP/RSTART. This isn't intended to be run as
|
||||||
|
; a complete program.
|
||||||
|
|
||||||
|
set pindirs, 0 side 0 [7] ; SCL = 0, SDA = 0
|
||||||
|
set pindirs, 1 side 0 [7] ; SCL = 0, SDA = 1
|
||||||
|
set pindirs, 0 side 1 [7] ; SCL = 1, SDA = 0
|
||||||
|
set pindirs, 1 side 1 [7] ; SCL = 1, SDA = 1
|
||||||
|
|
||||||
|
% c-sdk {
|
||||||
|
// Define order of our instruction table
|
||||||
|
enum {
|
||||||
|
I2C_SC0_SD0 = 0,
|
||||||
|
I2C_SC0_SD1,
|
||||||
|
I2C_SC1_SD0,
|
||||||
|
I2C_SC1_SD1
|
||||||
|
};
|
||||||
|
%}
|
318
drivers/include/periph/pio/i2c.h
Normal file
318
drivers/include/periph/pio/i2c.h
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 Otto-von-Guericke Universität Magdeburg
|
||||||
|
*
|
||||||
|
* This file is subject to the terms and conditions of the GNU Lesser
|
||||||
|
* General Public License v2.1. See the file LICENSE in the top level
|
||||||
|
* directory for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @defgroup drivers_periph_pio_i2c PIO I2C program
|
||||||
|
* @ingroup drivers_periph
|
||||||
|
* @brief PIO I2C program interface
|
||||||
|
*
|
||||||
|
* @experimental This API is experimental and in an early state -
|
||||||
|
* expect changes!
|
||||||
|
* @{
|
||||||
|
* @file
|
||||||
|
* @brief PIO I2C program interface
|
||||||
|
*
|
||||||
|
* @author Fabian Hüßler <fabian.huessler@ovgu.de>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PERIPH_PIO_I2C_H
|
||||||
|
#define PERIPH_PIO_I2C_H
|
||||||
|
|
||||||
|
#include "periph/i2c.h"
|
||||||
|
#include "periph/pio.h"
|
||||||
|
#include "mutex.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PIO I2C descriptor type compatible with @ref i2c_t
|
||||||
|
*/
|
||||||
|
typedef i2c_t pio_i2c_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PIO I2C program type
|
||||||
|
*/
|
||||||
|
typedef struct pio_program_i2c {
|
||||||
|
pio_program_t base; /**< PIO base program */
|
||||||
|
unsigned ref_mask; /**< Mask of referencing PIO state machines */
|
||||||
|
} pio_program_i2c_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief PIO I2C emulated bus type
|
||||||
|
*/
|
||||||
|
typedef struct pio_i2c_bus {
|
||||||
|
pio_t pio; /**< PIO index */
|
||||||
|
pio_sm_t sm; /**< State machine index */
|
||||||
|
mutex_t mtx; /**< Mutex to protect the bus from concurrent accesses */
|
||||||
|
} pio_i2c_bus_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get access to a PIO I2C instance configured with PIO_I2C_CONFIG
|
||||||
|
*
|
||||||
|
* @param[in] id PIO I2C ID
|
||||||
|
*
|
||||||
|
* @return PIO I2C objects
|
||||||
|
*/
|
||||||
|
pio_i2c_bus_t *pio_i2c_get(pio_i2c_t id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Query the number of PIO I2C instances configured with PIO_I2C_CONFIG
|
||||||
|
*
|
||||||
|
* @return Number of PIO I2C instances
|
||||||
|
*/
|
||||||
|
unsigned pio_i2c_numof(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get const I2C program reference
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
*
|
||||||
|
* @return PIO I2C program allocated to PIO @p pio
|
||||||
|
*/
|
||||||
|
const pio_program_i2c_t *pio_i2c_get_program(pio_t pio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Create, allocate, and write a PIO I2C program
|
||||||
|
*
|
||||||
|
* This function does nothing if the program is already
|
||||||
|
* created, allocated, and written.
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
int pio_i2c_init_program(pio_t pio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free a PIO I2C program
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
*/
|
||||||
|
void pio_i2c_deinit_program(pio_t pio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Acquire a PIO state machine of PIO @p pio to run the PIO I2C program
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
* @param[out] i2c PIO I2C bus
|
||||||
|
*
|
||||||
|
* @return A valid state machine index or a negative number on error
|
||||||
|
*/
|
||||||
|
pio_sm_t pio_i2c_sm_lock(pio_t pio, pio_i2c_bus_t *i2c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Release a PIO state machine of PIO @p pio
|
||||||
|
*
|
||||||
|
* @param[in, out] i2c PIO I2C bus
|
||||||
|
*/
|
||||||
|
void pio_i2c_sm_unlock(pio_i2c_bus_t *i2c);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Start PIO I2C programs configured with PIO_I2C_CONFIG
|
||||||
|
*
|
||||||
|
* @note No execution is started if
|
||||||
|
* "DISABLE_MODULE += pio_autostart_i2c" is set
|
||||||
|
*/
|
||||||
|
void pio_i2c_start_programs(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Stop PIO I2C programs configured with PIO_I2C_CONFIG
|
||||||
|
*/
|
||||||
|
void pio_i2c_stop_programs(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Write a PIO I2C program to instruction memory
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
* @param[in, out] pro Allocated PIO I2C program
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
int pio_i2c_write_program(pio_t pio, pio_program_i2c_t *pro);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Setup a state machine to run the I2C program
|
||||||
|
*
|
||||||
|
* @pre The program @p pro must have been allocated.
|
||||||
|
*
|
||||||
|
* @param[out] bus PIO I2C bus
|
||||||
|
* @param[in] pro Shared program base
|
||||||
|
* @param[in] sda SDA pin
|
||||||
|
* @param[in] scl SCL pin
|
||||||
|
* @param[in] irq IRQ line, 0 or 1
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
int pio_i2c_init(pio_i2c_bus_t *bus,
|
||||||
|
const pio_program_i2c_t *pro,
|
||||||
|
gpio_t sda, gpio_t scl, unsigned irq);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get exclusive access to the emulated I2C bus
|
||||||
|
*
|
||||||
|
* @param[in] bus PIO I2C bus
|
||||||
|
*/
|
||||||
|
void pio_i2c_acquire(pio_i2c_bus_t *bus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Release emulated I2C bus
|
||||||
|
*
|
||||||
|
* @param[in] bus PIO I2C bus
|
||||||
|
*/
|
||||||
|
void pio_i2c_release(pio_i2c_bus_t *bus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emulate @ref i2c_read_regs
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
* @param[in] sm PIO state machine index
|
||||||
|
* @param[in] addr 7-bit or 10-bit device address (right-aligned)
|
||||||
|
* @param[in] reg Register address to read from (8- or 16-bit right-aligned)
|
||||||
|
* @param[out] data Memory location to store received data
|
||||||
|
* @param[in] len The number of bytes to read into @p data
|
||||||
|
* @param[in] flags Optional flags (see @ref i2c_flags_t)
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
int pio_i2c_read_regs(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
uint16_t reg, void *data, size_t len, uint8_t flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emulate @ref i2c_read_reg
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
* @param[in] sm PIO state machine index
|
||||||
|
* @param[in] addr 7-bit or 10-bit device address (right-aligned)
|
||||||
|
* @param[in] reg Register address to read from (8- or 16-bit right-aligned)
|
||||||
|
* @param[out] data Memory location to store received data
|
||||||
|
* @param[in] flags Optional flags (see @ref i2c_flags_t)
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
static inline int pio_i2c_read_reg(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
uint16_t reg, void *data, uint8_t flags)
|
||||||
|
{
|
||||||
|
return pio_i2c_read_regs(pio, sm, addr, reg, data, 1, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emulate @ref i2c_read_bytes
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
* @param[in] sm PIO state machine index
|
||||||
|
* @param[in] addr 7-bit or 10-bit device address (right-aligned)
|
||||||
|
* @param[out] data Memory location to store received data
|
||||||
|
* @param[in] len The number of bytes to read into @p data
|
||||||
|
* @param[in] flags Optional flags (see @ref i2c_flags_t)
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
int pio_i2c_read_bytes(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
void *data, size_t len, uint8_t flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emulate @ref i2c_read_byte
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
* @param[in] sm PIO state machine index
|
||||||
|
* @param[in] addr 7-bit or 10-bit device address (right-aligned)
|
||||||
|
* @param[out] data Memory location to store received data
|
||||||
|
* @param[in] flags Optional flags (see @ref i2c_flags_t)
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
static inline int pio_i2c_read_byte(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
void *data, uint8_t flags)
|
||||||
|
{
|
||||||
|
return pio_i2c_read_bytes(pio, sm, addr, data, 1, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emulate @ref i2c_write_bytes
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
* @param[in] sm PIO state machine index
|
||||||
|
* @param[in] addr 7-bit or 10-bit device address (right-aligned)
|
||||||
|
* @param[in] data Array holding the bytes to write to the device
|
||||||
|
* @param[in] len The number of bytes to write
|
||||||
|
* @param[in] flags Optional flags (see @ref i2c_flags_t)
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
int pio_i2c_write_bytes(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
const void *data, size_t len, uint8_t flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emulate @ref i2c_write_byte
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
* @param[in] sm PIO state machine index
|
||||||
|
* @param[in] addr 7-bit or 10-bit device address (right-aligned)
|
||||||
|
* @param[in] data Byte to write to the device
|
||||||
|
* @param[in] flags Optional flags (see @ref i2c_flags_t)
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
static inline int pio_i2c_write_byte(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
uint8_t data, uint8_t flags)
|
||||||
|
{
|
||||||
|
return pio_i2c_write_bytes(pio, sm, addr, &data, 1, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emulate @ref i2c_write_regs
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
* @param[in] sm PIO state machine index
|
||||||
|
* @param[in] addr 7-bit or 10-bit device address (right-aligned)
|
||||||
|
* @param[in] reg register address to read from (8- or 16-bit, right-aligned)
|
||||||
|
* @param[in] data Array holding the bytes to write to the device
|
||||||
|
* @param[in] len The number of bytes to write
|
||||||
|
* @param[in] flags Optional flags (see @ref i2c_flags_t)
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
int pio_i2c_write_regs(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
uint16_t reg, const void *data, size_t len, uint8_t flags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Emulate @ref i2c_write_reg
|
||||||
|
*
|
||||||
|
* @param[in] pio PIO index
|
||||||
|
* @param[in] sm PIO state machine index
|
||||||
|
* @param[in] addr 7-bit or 10-bit device address (right-aligned)
|
||||||
|
* @param[in] reg register address to read from (8- or 16-bit, right-aligned)
|
||||||
|
* @param[in] data Array holding the bytes to write to the device
|
||||||
|
* @param[in] flags Optional flags (see @ref i2c_flags_t)
|
||||||
|
*
|
||||||
|
* @return Success: 0
|
||||||
|
* Failure: != 0
|
||||||
|
*/
|
||||||
|
static inline int pio_i2c_write_reg(pio_t pio, pio_sm_t sm, uint16_t addr,
|
||||||
|
uint16_t reg, uint8_t data, uint8_t flags)
|
||||||
|
{
|
||||||
|
return pio_i2c_write_regs(pio, sm, addr, reg, &data, 1, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif /* PERIPH_PIO_I2C_H */
|
||||||
|
/** @} */
|
@ -23,6 +23,7 @@
|
|||||||
#define USB_H_USER_IS_RIOT_INTERNAL
|
#define USB_H_USER_IS_RIOT_INTERNAL
|
||||||
|
|
||||||
#include "kernel_defines.h"
|
#include "kernel_defines.h"
|
||||||
|
#include "periph_conf.h"
|
||||||
#include "periph_cpu.h"
|
#include "periph_cpu.h"
|
||||||
|
|
||||||
#ifdef MODULE_PERIPH_INIT
|
#ifdef MODULE_PERIPH_INIT
|
||||||
@ -124,6 +125,11 @@ void periph_init(void)
|
|||||||
for (int i = 0; i < (int)PIO_NUMOF; i++) {
|
for (int i = 0; i < (int)PIO_NUMOF; i++) {
|
||||||
pio_init(PIO_DEV(i));
|
pio_init(PIO_DEV(i));
|
||||||
}
|
}
|
||||||
|
#if defined(MODULE_PERIPH_INIT_I2C) && defined(PIO_I2C_NUMOF)
|
||||||
|
for (unsigned i = 0; i < PIO_I2C_NUMOF; i++) {
|
||||||
|
i2c_init(I2C_DEV(I2C_NUMOF + i));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
pio_start_programs();
|
pio_start_programs();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -465,6 +465,11 @@ config HAS_PICOLIBC
|
|||||||
help
|
help
|
||||||
Indicates that the picolibc C library is available for the platform.
|
Indicates that the picolibc C library is available for the platform.
|
||||||
|
|
||||||
|
config HAS_PIO_I2C
|
||||||
|
bool
|
||||||
|
help
|
||||||
|
Indicates that there is a PIO program to provide emulated I2C
|
||||||
|
|
||||||
config HAS_NEWLIB
|
config HAS_NEWLIB
|
||||||
bool
|
bool
|
||||||
help
|
help
|
||||||
|
Loading…
Reference in New Issue
Block a user