diff --git a/cpu/sam0_common/Makefile.features b/cpu/sam0_common/Makefile.features index 38f83ce8b7..78e90ed0a0 100644 --- a/cpu/sam0_common/Makefile.features +++ b/cpu/sam0_common/Makefile.features @@ -3,6 +3,7 @@ FEATURES_PROVIDED += periph_flashpage FEATURES_PROVIDED += periph_flashpage_raw FEATURES_PROVIDED += periph_flashpage_rwee FEATURES_PROVIDED += periph_gpio periph_gpio_irq +FEATURES_PROVIDED += periph_i2c_reconfigure FEATURES_PROVIDED += periph_uart_modecfg FEATURES_PROVIDED += periph_uart_nonblocking FEATURES_PROVIDED += periph_wdt periph_wdt_cb diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 08a1868c92..e9be610222 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -300,6 +300,15 @@ typedef enum { I2C_SPEED_HIGH = 3400000U, /**< high speed mode: ~3.4Mbit/s */ } i2c_speed_t; /** @} */ + +/** + * @name I2C pin getter functions + * @{ + */ +#define i2c_pin_sda(dev) i2c_config[dev].sda_pin +#define i2c_pin_scl(dev) i2c_config[dev].scl_pin +/** @} */ + #endif /* ndef DOXYGEN */ /** diff --git a/cpu/sam0_common/periph/i2c.c b/cpu/sam0_common/periph/i2c.c index 10ac2baa63..c9cc275469 100644 --- a/cpu/sam0_common/periph/i2c.c +++ b/cpu/sam0_common/periph/i2c.c @@ -79,6 +79,7 @@ void i2c_init(i2c_t dev) int32_t tmp_baud; assert(dev < I2C_NUMOF); + /* Initialize mutex */ mutex_init(&locks[dev]); /* DISABLE I2C MASTER */ @@ -171,6 +172,31 @@ void i2c_release(i2c_t dev) mutex_unlock(&locks[dev]); } +#ifdef MODULE_PERIPH_I2C_RECONFIGURE +void i2c_init_pins(i2c_t dev) +{ + assert(dev < I2C_NUMOF); + + _i2c_poweron(dev); + + gpio_init_mux(i2c_config[dev].scl_pin, i2c_config[dev].mux); + gpio_init_mux(i2c_config[dev].sda_pin, i2c_config[dev].mux); + + mutex_unlock(&locks[dev]); +} + +void i2c_deinit_pins(i2c_t dev) +{ + assert(dev < I2C_NUMOF); + + mutex_lock(&locks[dev]); + _i2c_poweroff(dev); + + gpio_disable_mux(i2c_config[dev].sda_pin); + gpio_disable_mux(i2c_config[dev].scl_pin); +} +#endif + int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, uint8_t flags) { diff --git a/drivers/include/periph/i2c.h b/drivers/include/periph/i2c.h index 69824f8905..b8ee3bf9f0 100644 --- a/drivers/include/periph/i2c.h +++ b/drivers/include/periph/i2c.h @@ -215,6 +215,67 @@ typedef enum { */ void i2c_init(i2c_t dev); +/** + * @brief Initialize the used I2C bus pins + * + * After calling i2c_init, the pins must be initialized (i.e. spi_init is + * calling this function internally). In normal cases, this function will not be + * used. But there are some devices (e.g. ATECC608A), that use I2C bus lines also + * for other purposes and need the option to dynamically re-configure one or + * more of the used pins. + * + * @param[in] dev I2C device the pins are configure for + */ +void i2c_init_pins(i2c_t dev); + +/** + * @brief Change the pins of the given I2C bus back to plain GPIO functionality + * + * The pin mux of the SDA and SCL pins of the bus will be changed back to + * default (GPIO) mode and the I2C bus is powered off. + * This allows to use the I2C pins for another function and return to I2C + * functionality again by calling i2c_init_pins() + * + * If you want the pin to be in a defined state, call gpio_init() on it. + * + * The bus MUST not be acquired before initializing it, as this is handled + * internally by the i2c_deinit function! + * + * Calls to i2c_acquire() will block until i2c_init_pins() is called. + * + * @note Until this is implemented on all platforms, this requires the + * periph_i2c_reconfigure feature to be used. + * + * @param[in] dev the device to de-initialize + */ +void i2c_deinit_pins(i2c_t dev); + +#if DOXYGEN /* functions to be implemented as `#define` in `periph_cpu.h` */ +/** + * @brief Get the SDA pin of the given I2C bus. + * + * @param[in] dev The device to query + * + * @note Until this is implemented on all platforms, this requires the + * periph_i2c_reconfigure feature to be used. + * + * @return The GPIO used for the I2C data line. + */ +gpio_t i2c_pin_sda(i2c_t dev); + +/** + * @brief Get the SCL pin of the given I2C bus. + * + * @param[in] dev The device to query + * + * @note Until this is implemented on all platforms, this requires the + * periph_i2c_reconfigure feature to be used. + * + * @return The GPIO used for the I2C clock line. + */ +gpio_t i2c_pin_scl(i2c_t dev); +#endif /* DOXYGEN */ + /** * @brief Get mutually exclusive access to the given I2C bus * diff --git a/tests/periph_i2c/Makefile b/tests/periph_i2c/Makefile index 1aa079ca54..77e10cbeac 100644 --- a/tests/periph_i2c/Makefile +++ b/tests/periph_i2c/Makefile @@ -2,7 +2,9 @@ BOARD ?= samr21-xpro include ../Makefile.tests_common FEATURES_REQUIRED = periph_i2c +FEATURES_OPTIONAL = periph_i2c_reconfigure USEMODULE += shell +USEMODULE += xtimer include $(RIOTBASE)/Makefile.include diff --git a/tests/periph_i2c/main.c b/tests/periph_i2c/main.c index e6ab7e6110..0b5f4f87a5 100644 --- a/tests/periph_i2c/main.c +++ b/tests/periph_i2c/main.c @@ -24,9 +24,12 @@ #include #include "periph_conf.h" +#include "periph/gpio.h" #include "periph/i2c.h" #include "shell.h" +#include + #ifndef I2C_ACK #define I2C_ACK (0) #endif @@ -160,6 +163,47 @@ int cmd_i2c_release(int argc, char **argv) return 0; } +#ifdef MODULE_PERIPH_I2C_RECONFIGURE +int cmd_i2c_gpio(int argc, char **argv) +{ + int dev; + + dev = _check_param(argc, argv, 1, 1, "DEV"); + if (dev == ARG_ERROR) { + return 1; + } + + gpio_t sda_pin = i2c_pin_sda(dev); + gpio_t scl_pin = i2c_pin_scl(dev); + + printf("Command: i2c_deinit_pins(%i)\n", dev); + i2c_deinit_pins(dev); + + gpio_init(sda_pin, GPIO_OUT); + gpio_init(scl_pin, GPIO_OUT); + + xtimer_sleep(1); + + printf("Command: gpio_set()\n"); + gpio_set(sda_pin); + gpio_set(scl_pin); + + xtimer_sleep(1); + + printf("Command: gpio_clear()\n"); + gpio_clear(sda_pin); + gpio_clear(scl_pin); + + xtimer_sleep(1); + + printf("Command: i2c_init_pins(%i)\n", dev); + i2c_init_pins(dev); + + printf("Success: i2c_%i re-init\n", dev); + return 0; +} +#endif + int cmd_i2c_read_reg(int argc, char **argv) { int res; @@ -444,6 +488,9 @@ int cmd_i2c_get_id(int argc, char **argv) static const shell_command_t shell_commands[] = { { "i2c_acquire", "Get access to the I2C bus", cmd_i2c_acquire }, { "i2c_release", "Release to the I2C bus", cmd_i2c_release }, +#ifdef MODULE_PERIPH_I2C_RECONFIGURE + { "i2c_gpio", "Re-configures I2C pins to GPIO mode and back.", cmd_i2c_gpio }, +#endif { "i2c_read_reg", "Read byte from register", cmd_i2c_read_reg }, { "i2c_read_regs", "Read bytes from registers", cmd_i2c_read_regs }, { "i2c_read_byte", "Read byte from the I2C device", cmd_i2c_read_byte },