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
|
||||
select CPU_MODEL_RP2040
|
||||
select HAS_PERIPH_ADC
|
||||
select HAS_PERIPH_I2C
|
||||
select HAS_PERIPH_UART
|
||||
select HAS_PERIPH_SPI
|
||||
|
||||
|
@ -2,6 +2,7 @@ CPU := rpx0xx
|
||||
|
||||
# Put defined MCU peripherals here (in alphabetical order)
|
||||
FEATURES_PROVIDED += periph_adc
|
||||
FEATURES_PROVIDED += periph_i2c
|
||||
FEATURES_PROVIDED += periph_spi
|
||||
FEATURES_PROVIDED += periph_timer
|
||||
FEATURES_PROVIDED += periph_uart
|
||||
|
@ -28,6 +28,16 @@
|
||||
extern "C" {
|
||||
#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[] = {
|
||||
{
|
||||
.dev = UART0,
|
||||
@ -110,6 +120,16 @@ static const adc_conf_t 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
|
||||
* @{
|
||||
@ -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_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
|
||||
|
@ -16,6 +16,7 @@ config CPU_FAM_RPX0XX
|
||||
select HAS_PERIPH_TIMER_PERIODIC
|
||||
select HAS_PERIPH_UART_MODECFG
|
||||
select HAS_PERIPH_UART_RECONFIGURE
|
||||
select HAS_PIO_I2C
|
||||
|
||||
config CPU_FAM
|
||||
default "RPX0XX" if CPU_FAM_RPX0XX
|
||||
|
@ -2,4 +2,8 @@ MODULE = cpu
|
||||
|
||||
DIRS = $(RIOTCPU)/cortexm_common periph
|
||||
|
||||
ifneq (,$(filter pio_i2c,$(USEMODULE)))
|
||||
DIRS += pio/i2c
|
||||
endif
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
||||
|
@ -2,4 +2,9 @@ ifneq (,$(filter pio_%,$(USEMODULE)))
|
||||
USEMODULE += periph_pio
|
||||
endif
|
||||
|
||||
ifneq (,$(filter pio_i2c periph_i2c,$(FEATURES_USED)))
|
||||
DEFAULT_MODULE += pio_autostart_i2c
|
||||
USEMODULE += pio_i2c
|
||||
endif
|
||||
|
||||
include $(RIOTCPU)/cortexm_common/Makefile.dep
|
||||
|
@ -9,3 +9,4 @@ FEATURES_PROVIDED += periph_pio
|
||||
FEATURES_PROVIDED += periph_timer_periodic
|
||||
FEATURES_PROVIDED += periph_uart_reconfigure
|
||||
FEATURES_PROVIDED += periph_uart_modecfg
|
||||
FEATURES_PROVIDED += pio_i2c
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "vendor/RP2040.h"
|
||||
#include "io_reg.h"
|
||||
#include "macros/units.h"
|
||||
#include "periph/pio.h" /* pio_t */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
@ -440,6 +441,16 @@ typedef struct {
|
||||
IRQn_Type irqn1; /**< PIO IRQ1 interrupt number */
|
||||
} 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
|
||||
*
|
||||
|
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 "io_reg.h"
|
||||
#include "pio/pio.h"
|
||||
#include "periph/pio/i2c.h"
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
@ -141,6 +142,9 @@ void pio_init(pio_t pio)
|
||||
|
||||
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)
|
||||
|
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
|
||||
|
||||
#include "kernel_defines.h"
|
||||
#include "periph_conf.h"
|
||||
#include "periph_cpu.h"
|
||||
|
||||
#ifdef MODULE_PERIPH_INIT
|
||||
@ -124,6 +125,11 @@ void periph_init(void)
|
||||
for (int i = 0; i < (int)PIO_NUMOF; 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();
|
||||
#endif
|
||||
|
||||
|
@ -465,6 +465,11 @@ config HAS_PICOLIBC
|
||||
help
|
||||
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
|
||||
bool
|
||||
help
|
||||
|
Loading…
Reference in New Issue
Block a user