diff --git a/boards/acd52832/include/periph_conf.h b/boards/acd52832/include/periph_conf.h index a2703046f7692b7a8253c4d10b9f2e47fd162a09..d1223e4902985e3dcc74d7f36fb15f71c8956c0b 100644 --- a/boards/acd52832/include/periph_conf.h +++ b/boards/acd52832/include/periph_conf.h @@ -96,6 +96,21 @@ static const spi_conf_t spi_config[] = { #define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ +/** + * @name I2C configuration + * @{ + */ +static const i2c_conf_t i2c_config[] = { + { + .dev = NRF_TWIM0, + .pin_scl = 28, + .pin_sda = 29 + } +}; + +#define I2C_NUMOF (sizeof(i2c_config) / sizeof(i2c_config[0])) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/boards/common/nrf52xxxdk/Makefile.features b/boards/common/nrf52xxxdk/Makefile.features index 5c594d4b0d28674f1c3095de7147c70c20262bb2..56ed0022c03b13df2b60ce2c12b0963bfefcafc7 100644 --- a/boards/common/nrf52xxxdk/Makefile.features +++ b/boards/common/nrf52xxxdk/Makefile.features @@ -1,5 +1,6 @@ # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_rtt FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer diff --git a/boards/common/nrf52xxxdk/include/periph_conf.h b/boards/common/nrf52xxxdk/include/periph_conf.h index 4242273a3b80a1e757fadedaa99b4243ffbc4bbf..de3202a230cd30e53ff0a21b89f55d9b3ee086f2 100644 --- a/boards/common/nrf52xxxdk/include/periph_conf.h +++ b/boards/common/nrf52xxxdk/include/periph_conf.h @@ -93,6 +93,21 @@ static const spi_conf_t spi_config[] = { #define SPI_NUMOF (sizeof(spi_config) / sizeof(spi_config[0])) /** @} */ +/** + * @name I2C configuration + * @{ + */ +static const i2c_conf_t i2c_config[] = { + { + .dev = NRF_TWIM0, + .pin_scl = 28, + .pin_sda = 29 + } +}; + +#define I2C_NUMOF (sizeof(i2c_config) / sizeof(i2c_config[0])) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/cpu/nrf52/include/periph_cpu.h b/cpu/nrf52/include/periph_cpu.h index bac6b6487e7c8d310ded0c09efe5b846338108a8..8614621958eac2b872d478d5a318384d787e6d45 100644 --- a/cpu/nrf52/include/periph_cpu.h +++ b/cpu/nrf52/include/periph_cpu.h @@ -75,6 +75,29 @@ typedef enum { } adc_res_t; /** @} */ +/** + * @brief Override I2C speed settings + * @{ + */ +#define HAVE_I2C_SPEED_T +typedef enum { + I2C_SPEED_LOW = 0xff, /**< not supported */ + I2C_SPEED_NORMAL = TWI_FREQUENCY_FREQUENCY_K100, /**< 100kbit/s */ + I2C_SPEED_FAST = TWI_FREQUENCY_FREQUENCY_K400, /**< 400kbit/s */ + I2C_SPEED_FAST_PLUS = 0xfe, /**< not supported */ + I2C_SPEED_HIGH = 0xfd, /**< not supported */ +} i2c_speed_t; +/** @} */ + +/** + * @brief I2C (TWI) configuration options + */ +typedef struct { + NRF_TWIM_Type *dev; /**< hardware device */ + uint8_t pin_scl; /**< SCL pin */ + uint8_t pin_sda; /**< SDA pin */ +} i2c_conf_t; + #ifdef __cplusplus } #endif diff --git a/cpu/nrf52/periph/i2c.c b/cpu/nrf52/periph/i2c.c new file mode 100644 index 0000000000000000000000000000000000000000..b8d681fdeb1a1ab1755fe392de2f3ac8e7d3f15f --- /dev/null +++ b/cpu/nrf52/periph/i2c.c @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * + * 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_nrf52 + * @{ + * + * @file + * @brief Low-level I2C driver implementation + * + * @author Dimitri Nahm <dimitri.nahm@haw-hamburg.de> + * + * @} + */ + +#include "cpu.h" +#include "mutex.h" +#include "assert.h" +#include "periph/i2c.h" +#include "periph_conf.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +/** + * @brief If any of the 4 lower bits are set, the speed value is invalid + */ +#define INVALID_SPEED_MASK (0x0f) + +static mutex_t locks[I2C_NUMOF]; + +static inline NRF_TWIM_Type *dev(i2c_t bus) +{ + return i2c_config[bus].dev; +} + +static int error(i2c_t bus) +{ + DEBUG("[i2c] error 0x%02x\n", (int)dev(bus)->ERRORSRC); + dev(bus)->ERRORSRC = 0; + dev(bus)->EVENTS_ERROR = 0; + return -1; +} + +static int write(i2c_t bus, uint8_t address, const void *data, int length, int stop) +{ + uint8_t *out_buf = (uint8_t *)data; + assert((bus <= I2C_NUMOF) && (length > 0)); + + DEBUG("[i2c]: writing %i bytes to the bus\n", length); + + /* disable shortcuts */ + if (stop == 0) { + dev(bus)->SHORTS = 0; + } + + /* set the client address and the data pointer */ + dev(bus)->ADDRESS = (address & 0x7f); + dev(bus)->TXD.PTR = (uint32_t)out_buf; + dev(bus)->TXD.MAXCNT = (length << TWIM_TXD_MAXCNT_MAXCNT_Pos); + + /* start write sequence */ + dev(bus)->EVENTS_LASTTX = 0; + dev(bus)->TASKS_STARTTX = 1; + + /* wait for the device to finish up */ + while ((dev(bus)->EVENTS_LASTTX == 0) && (dev(bus)->EVENTS_ERROR == 0)) {} + if (dev(bus)->EVENTS_ERROR) { + return error(bus); + } + + /* wait for the device to finish up */ + while ((dev(bus)->TXD.AMOUNT != (unsigned)length) && (dev(bus)->EVENTS_ERROR == 0)) {} + if (dev(bus)->EVENTS_ERROR) { + return error(bus); + } + + /* enable shortcuts */ + if (stop == 0) { + dev(bus)->SHORTS = (TWIM_SHORTS_LASTTX_STOP_Enabled << TWIM_SHORTS_LASTTX_STOP_Pos) | + (TWIM_SHORTS_LASTRX_STOP_Enabled << TWIM_SHORTS_LASTRX_STOP_Pos); + } + + return dev(bus)->TXD.AMOUNT; +} + +int i2c_init_master(i2c_t bus, i2c_speed_t speed) +{ + if (bus >= I2C_NUMOF) { + return -1; + } + if (speed & INVALID_SPEED_MASK) { + return -2; + } + + /* initialize lock */ + mutex_init(&locks[bus]); + + /* pin configuration */ + NRF_P0->PIN_CNF[i2c_config[bus].pin_scl] = (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos); + NRF_P0->PIN_CNF[i2c_config[bus].pin_scl] = (GPIO_PIN_CNF_DRIVE_S0D1 << GPIO_PIN_CNF_DRIVE_Pos); + dev(bus)->PSEL.SCL = i2c_config[bus].pin_scl; + dev(bus)->PSEL.SDA = i2c_config[bus].pin_sda; + + /* shortcuts configuration */ + dev(bus)->SHORTS = (TWIM_SHORTS_LASTTX_STOP_Enabled << TWIM_SHORTS_LASTTX_STOP_Pos) | + (TWIM_SHORTS_LASTRX_STOP_Enabled << TWIM_SHORTS_LASTRX_STOP_Pos); + + /* bus clock speed configuration */ + dev(bus)->FREQUENCY = (speed << TWIM_FREQUENCY_FREQUENCY_Pos); + + /* enable the device */ + dev(bus)->ENABLE = (TWIM_ENABLE_ENABLE_Enabled << TWIM_ENABLE_ENABLE_Pos); + + return 0; +} + +int i2c_acquire(i2c_t bus) +{ + assert(bus <= I2C_NUMOF); + mutex_lock(&locks[bus]); + return 0; +} + +int i2c_release(i2c_t bus) +{ + assert(bus <= I2C_NUMOF); + mutex_unlock(&locks[bus]); + return 0; +} + +int i2c_read_byte(i2c_t bus, uint8_t address, void *data) +{ + return i2c_read_bytes(bus, address, data, 1); +} + +int i2c_read_bytes(i2c_t bus, uint8_t address, void *data, int length) +{ + uint8_t *in_buf = (uint8_t *)data; + assert((bus <= I2C_NUMOF) && (length > 0)); + + DEBUG("[i2c] reading %i bytes from the bus\n", length); + + /* set the client address and the data pointer */ + dev(bus)->ADDRESS = (address & 0x7f); + dev(bus)->RXD.PTR = (uint32_t)in_buf; + dev(bus)->RXD.MAXCNT = (length << TWIM_RXD_MAXCNT_MAXCNT_Pos); + + /* start read sequence */ + dev(bus)->EVENTS_STOPPED = 0; + dev(bus)->TASKS_STARTRX = 1; + + /* wait for the device to finish up */ + while ((dev(bus)->EVENTS_STOPPED == 0) && (dev(bus)->EVENTS_ERROR == 0)) {} + if (dev(bus)->EVENTS_ERROR) { + return error(bus); + } + + return dev(bus)->RXD.AMOUNT; +} + +int i2c_read_reg(i2c_t bus, uint8_t address, uint8_t reg, void *data) +{ + write(bus, address, ®, 1, 0); + return i2c_read_bytes(bus, address, data, 1); +} + +int i2c_read_regs(i2c_t bus, uint8_t address, uint8_t reg, + void *data, int length) +{ + write(bus, address, ®, 1, 0); + return i2c_read_bytes(bus, address, data, length); +} + +int i2c_write_byte(i2c_t bus, uint8_t address, uint8_t data) +{ + return write(bus, address, &data, 1, 1); +} + +int i2c_write_bytes(i2c_t bus, uint8_t address, const void *data, int length) +{ + return write(bus, address, data, length, 1); +} + +int i2c_write_reg(i2c_t bus, uint8_t address, uint8_t reg, uint8_t data) +{ + /* send reg and data in one function call */ + uint8_t out_buf[2]; + out_buf[0] = reg; + out_buf[1] = data; + return write(bus, address, &out_buf, 2, 1) - 1; +} + +int i2c_write_regs(i2c_t bus, uint8_t address, uint8_t reg, const void *data, int length) +{ + /* send reg and data in one function call */ + uint8_t *buf = (uint8_t *)data; + uint8_t out_buf[length + 1]; + out_buf[0] = reg; + for (int i = 0; i < length ; i++) { + out_buf[i + 1] = buf[i]; + } + return write(bus, address, &out_buf, (length + 1), 1) - 1; +}