From ebc17acb59d3364162ff5f4a3ad5ebabc9237cf8 Mon Sep 17 00:00:00 2001 From: Alexandre Abadie Date: Fri, 25 May 2018 16:37:55 +0200 Subject: [PATCH] cpu/stm32{f0,f3,f7,l0,l4}: unify i2c driver and use new API --- .../include/cpu_conf_stm32_common.h | 118 ++++ cpu/stm32_common/include/periph_cpu_common.h | 63 ++ cpu/stm32_common/periph/i2c.c | 449 ++++++++++++++ cpu/stm32f3/periph/i2c.c | 542 ---------------- cpu/stm32l0/include/periph_cpu.h | 12 - cpu/stm32l0/periph/i2c.c | 576 ------------------ 6 files changed, 630 insertions(+), 1130 deletions(-) create mode 100644 cpu/stm32_common/include/cpu_conf_stm32_common.h create mode 100644 cpu/stm32_common/periph/i2c.c delete mode 100644 cpu/stm32f3/periph/i2c.c delete mode 100644 cpu/stm32l0/periph/i2c.c diff --git a/cpu/stm32_common/include/cpu_conf_stm32_common.h b/cpu/stm32_common/include/cpu_conf_stm32_common.h new file mode 100644 index 0000000000..6e1fe688d1 --- /dev/null +++ b/cpu/stm32_common/include/cpu_conf_stm32_common.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2018 Inria + * + * 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_stm32_common + * @{ + * + * @file + * @brief Shared CPU specific configuration for STM32 family + * + * @author Alexandre Abadie + */ + +#ifndef CPU_CONF_STM32_COMMON_H +#define CPU_CONF_STM32_COMMON_H + +#include "periph_cpu.h" + +#include "periph/i2c.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3) || \ + defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L0) || \ + defined(CPU_FAM_STM32L4) + +/** + * @brief Timing register settings + * + * @ref i2c_timing_param_t + */ +static const i2c_timing_param_t timing_params[] = { +#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32L4) || \ + defined(CPU_FAM_STM32F7) + [ I2C_SPEED_NORMAL ] = { + .presc = 0xB, + .scll = 0x13, /* t_SCLL = 5.0us */ + .sclh = 0xF, /* t_SCLH = 4.0us */ + .sdadel = 0x2, /* t_SDADEL = 500ns */ + .scldel = 0x4, /* t_SCLDEL = 1250ns */ + }, + [ I2C_SPEED_FAST ] = { + .presc = 5, + .scll = 0x9, /* t_SCLL = 1250ns */ + .sclh = 0x3, /* t_SCLH = 500ns */ + .sdadel = 0x3, /* t_SDADEL = 375ns */ + .scldel = 0x3, /* t_SCLDEL = 500ns */ + }, + [ I2C_SPEED_FAST_PLUS ] = { + .presc = 5, + .scll = 0x3, /* t_SCLL = 500ns */ + .sclh = 0x1, /* t_SCLH = 250ns */ + .sdadel = 0x0, /* t_SDADEL = 0ns */ + .scldel = 0x1, /* t_SCLDEL = 250ns */ + } +#elif defined(CPU_FAM_STM32F3) + [ I2C_SPEED_NORMAL ] = { + .presc = 1, + .scll = 0x13, /* t_SCLL = 5.0us */ + .sclh = 0xF, /* t_SCLH = 4.0us */ + .sdadel = 0x2, /* t_SDADEL = 500ns */ + .scldel = 0x4, /* t_SCLDEL = 1250ns */ + }, + [ I2C_SPEED_FAST ] = { + .presc = 0, + .scll = 0x9, /* t_SCLL = 1250ns */ + .sclh = 0x3, /* t_SCLH = 500ns */ + .sdadel = 0x1, /* t_SDADEL = 125ns */ + .scldel = 0x3, /* t_SCLDEL = 500ns */ + }, + [ I2C_SPEED_FAST_PLUS ] = { + .presc = 0, + .scll = 0x6, /* t_SCLL = 875ns */ + .sclh = 0x3, /* t_SCLH = 500ns */ + .sdadel = 0x0, /* t_SDADEL = 0ns */ + .scldel = 0x1, /* t_SCLDEL = 250ns */ + } +#elif defined(CPU_FAM_STM32L0) + [ I2C_SPEED_NORMAL ] = { + .presc = 1, + .scll = 0x56, /* t_SCLL = 5.0us */ + .sclh = 0x3E, /* t_SCLH = 4.0us */ + .sdadel = 0x1, /* t_SDADEL = 500ns */ + .scldel = 0xA, /* t_SCLDEL = 1250ns */ + }, + [ I2C_SPEED_FAST ] = { + .presc = 0, + .scll = 0x2E, /* t_SCLL = 1250ns */ + .sclh = 0x11, /* t_SCLH = 500ns */ + .sdadel = 0x1, /* t_SDADEL = 125ns */ + .scldel = 0xB, /* t_SCLDEL = 500ns */ + }, + [ I2C_SPEED_FAST_PLUS ] = { + .presc = 0, + .scll = 0x6, /* t_SCLL = 875ns */ + .sclh = 0x3, /* t_SCLH = 500ns */ + .sdadel = 0x0, /* t_SDADEL = 0ns */ + .scldel = 0x1, /* t_SCLDEL = 250ns */ + } +#endif +}; + +#endif /* CPU_FAM_STM32F0 || CPU_FAM_STM32F3 || CPU_FAM_STM32F7 || + CPU_FAM_STM32L0 || CPU_FAM_STM32L4 */ + +#ifdef __cplusplus +} +#endif + +#endif /* CPU_CONF_STM32_COMMON_H */ +/** @} */ diff --git a/cpu/stm32_common/include/periph_cpu_common.h b/cpu/stm32_common/include/periph_cpu_common.h index 56eac8b6d1..708f45a6e5 100644 --- a/cpu/stm32_common/include/periph_cpu_common.h +++ b/cpu/stm32_common/include/periph_cpu_common.h @@ -148,6 +148,16 @@ typedef uint32_t gpio_t; */ #define SPI_HWCS(x) (SPI_HWCS_MASK | x) +/** + * @name Use the shared I2C functions + * @{ + */ +/** Use read reg function from periph common */ +#define PERIPH_I2C_NEED_READ_REG +/** Use write reg function from periph common */ +#define PERIPH_I2C_NEED_WRITE_REG +/** @} */ + /** * @brief Available MUX values for configuring a pin's alternate function */ @@ -372,6 +382,59 @@ typedef struct { #endif } spi_conf_t; +#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3) || \ + defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L0) || \ + defined(CPU_FAM_STM32L4) + +/** + * @brief Default mapping of I2C bus speed values + * @{ + */ +#define HAVE_I2C_SPEED_T +typedef enum { + I2C_SPEED_NORMAL, /**< normal mode: ~100kbit/s */ + I2C_SPEED_FAST, /**< fast mode: ~400kbit/s */ + I2C_SPEED_FAST_PLUS, /**< fast plus mode: ~1Mbit/s */ +} i2c_speed_t; +/** @} */ + +/** + * @brief Structure for I2C configuration data + */ +typedef struct { + I2C_TypeDef *dev; /**< i2c device */ + i2c_speed_t speed; /**< i2c bus speed */ + gpio_t scl_pin; /**< scl pin number */ + gpio_t sda_pin; /**< sda pin number */ + gpio_af_t scl_af; /**< scl pin alternate function value */ + gpio_af_t sda_af; /**< sda pin alternate function value */ + uint8_t bus; /**< APB bus */ + uint32_t rcc_mask; /**< bit in clock enable register */ +#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3) + uint32_t rcc_sw_mask; /**< bit to switch I2C clock */ +#endif + uint8_t irqn; /**< I2C event interrupt number */ +} i2c_conf_t; + +/** + * @brief Structure for I2C timing register settings + * + * These parameters are valid for 48MHz (16MHz for L0) input clock. + * See reference manual of supported CPU for example of timing settings: + * - STM32F030/F070: see RM0360, section 22.4.10, p.560, table 76 + * - STM32F303: see RM0316, section 28.4.9, p.849, table 148 + * - STM32F72X: see RM0431, section 26.4.9, p.851, table 149 + * - STM32L0x2: see RM0376, section 27.4.10, p.686, table 117 + * - STM32L4X5/6: see RM0351, section 39.4.9, p.1297, table 234 + */ +typedef struct { + uint8_t presc; /**< Timing prescaler value */ + uint8_t scll; /**< SCL Low period */ + uint8_t sclh; /**< SCL High period */ + uint8_t sdadel; /**< Data hold time */ + uint8_t scldel; /**< Data setup time */ +} i2c_timing_param_t; +#endif /** * @brief Get the actual bus clock frequency for the APB buses diff --git a/cpu/stm32_common/periph/i2c.c b/cpu/stm32_common/periph/i2c.c new file mode 100644 index 0000000000..324f803296 --- /dev/null +++ b/cpu/stm32_common/periph/i2c.c @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2015 Jan Pohlmann + * 2017 we-sens.com + * 2018 Inria + * + * 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_stm32_common + * @ingroup drivers_periph_i2c + * @{ + * + * @file + * @brief Low-level I2C driver implementation + * + * @note This implementation only implements the 7-bit addressing polling mode + * (for now interrupt mode is not available) + * + * @author Peter Kietzmann + * @author Hauke Petersen + * @auhtor Thomas Eichinger + * @author Jan Pohlmann + * @author Aurélien Fillau + * @author Alexandre Abadie + * + * @} + */ + +#include + +#include "cpu.h" +#include "mutex.h" + +#include "cpu_conf_stm32_common.h" + +#include "periph/i2c.h" +#include "periph/gpio.h" +#include "periph_conf.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +#define TICK_TIMEOUT (0xFFFF) + +#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3) || \ + defined(CPU_FAM_STM32F7) || defined(CPU_FAM_STM32L0) || \ + defined(CPU_FAM_STM32L4) + +#define I2C_IRQ_PRIO (1) +#define CLEAR_FLAG (I2C_ICR_NACKCF | I2C_ICR_ARLOCF | I2C_ICR_BERRCF) +#define ERROR_FLAG (I2C_ISR_NACKF | I2C_ISR_ARLO | I2C_ISR_BERR) + +/* static function definitions */ +static inline void _i2c_init(I2C_TypeDef *i2c, uint32_t timing); +static inline void _start(I2C_TypeDef *dev, uint16_t address, size_t length, + uint8_t rw_flag, uint8_t flags); +static inline int _read(I2C_TypeDef *dev, uint8_t *data, size_t length); +static inline int _write(I2C_TypeDef *i2c, const uint8_t *data, size_t length); +static inline void _stop(I2C_TypeDef *i2c); + +/** + * @brief Array holding one pre-initialized mutex for each I2C device + */ +static mutex_t locks[I2C_NUMOF]; + +void i2c_init(i2c_t dev) +{ + assert(dev < I2C_NUMOF); + + DEBUG("[i2c] init: initializing device\n"); + mutex_init(&locks[dev]); + + I2C_TypeDef *i2c = i2c_config[dev].dev; + + periph_clk_en(i2c_config[dev].bus, i2c_config[dev].rcc_mask); + + NVIC_SetPriority(i2c_config[dev].irqn, I2C_IRQ_PRIO); + NVIC_EnableIRQ(i2c_config[dev].irqn); + +#if defined(CPU_FAM_STM32F0) || defined(CPU_FAM_STM32F3) + /* Set I2CSW bits to enable I2C clock source */ + RCC->CFGR3 |= i2c_config[dev].rcc_sw_mask; +#endif + + DEBUG("[i2c] init: configuring pins\n"); + /* configure pins */ + gpio_init(i2c_config[dev].scl_pin, GPIO_OD_PU); + gpio_init_af(i2c_config[dev].scl_pin, i2c_config[dev].scl_af); + gpio_init(i2c_config[dev].sda_pin, GPIO_OD_PU); + gpio_init_af(i2c_config[dev].sda_pin, i2c_config[dev].sda_af); + + DEBUG("[i2c] init: configuring device\n"); + /* set the timing register value from predefined values */ + i2c_timing_param_t tp = timing_params[i2c_config[dev].speed]; + uint32_t timing = (( (uint32_t)tp.presc << I2C_TIMINGR_PRESC_Pos) | + ( (uint32_t)tp.scldel << I2C_TIMINGR_SCLDEL_Pos) | + ( (uint32_t)tp.sdadel << I2C_TIMINGR_SDADEL_Pos) | + ( (uint16_t)tp.sclh << I2C_TIMINGR_SCLH_Pos) | + tp.scll); + _i2c_init(i2c, timing); +} + +static void _i2c_init(I2C_TypeDef *i2c, uint32_t timing) +{ + assert(i2c != NULL); + + /* disable device */ + i2c->CR1 &= ~(I2C_CR1_PE); + + /* configure analog noise filter */ + i2c->CR1 |= I2C_CR1_ANFOFF; + + /* configure digital noise filter */ + i2c->CR1 |= I2C_CR1_DNF; + + /* set timing registers */ + i2c->TIMINGR = timing; + + /* configure clock stretching */ + i2c->CR1 &= ~(I2C_CR1_NOSTRETCH); + + /* Clear interrupt */ + i2c->ICR |= CLEAR_FLAG; + + /* enable device */ + i2c->CR1 |= I2C_CR1_PE; +} + +int i2c_acquire(i2c_t dev) +{ + assert(dev < I2C_NUMOF); + + mutex_lock(&locks[dev]); + + periph_clk_en(i2c_config[dev].bus, i2c_config[dev].rcc_mask); + + return 0; +} + +int i2c_release(i2c_t dev) +{ + assert(dev < I2C_NUMOF); + + uint16_t tick = TICK_TIMEOUT; + + while ((i2c_config[dev].dev->ISR & I2C_ISR_BUSY) && tick--) {} + + periph_clk_dis(i2c_config[dev].bus, i2c_config[dev].rcc_mask); + + mutex_unlock(&locks[dev]); + return 0; +} + +int i2c_read_bytes(i2c_t dev, uint16_t address, void *data, + size_t length, uint8_t flags) +{ + assert(dev < I2C_NUMOF); + + I2C_TypeDef *i2c = i2c_config[dev].dev; + + if (!(flags & I2C_NOSTART)) { + DEBUG("[i2c] read_bytes: start transmission\n"); + /* start reception and send slave address */ + _start(i2c, address, length, I2C_READ, flags); + } + + DEBUG("[i2c] read_bytes: read the data\n"); + /* read the data bytes */ + if (_read(i2c, data, length) < 0) { + DEBUG("[i2c] read_bytes: nothing was read\n"); + length = 0; + } + + if (!(flags & I2C_NOSTOP)) { + DEBUG("[i2c] read_bytes: end transmission\n"); + _stop(i2c); + } + + return length; +} + +int i2c_read_regs(i2c_t dev, uint16_t address, uint16_t reg, void *data, + size_t length, uint8_t flags) +{ + assert(dev < I2C_NUMOF); + + DEBUG("[i2c] read_regs: addr: %04X, reg: %04X\n", address, reg); + + uint16_t tick = TICK_TIMEOUT; + + I2C_TypeDef *i2c = i2c_config[dev].dev; + + /* Check to see if the bus is busy */ + while ((i2c->ISR & I2C_ISR_BUSY) && tick--) { + if ((i2c->ISR & ERROR_FLAG) || !tick) { + /* end transmission */ + _stop(i2c); + return -1; + } + } + + DEBUG("[i2c] read_regs: send start sequence\n"); + + /* send start sequence and slave address */ + _start(i2c, address, 1, 0, flags); + + tick = TICK_TIMEOUT; + /* wait for ack */ + while (!(i2c->ISR & I2C_ISR_TXIS) && tick--) { + if ((i2c->ISR & ERROR_FLAG) || !tick) { + /* end transmission */ + _stop(i2c); + return -1; + } + } + + DEBUG("[i2c] read_regs: Write register to read\n"); + i2c->TXDR = reg; + + /* send repeated start sequence, read registers and end transmission */ + DEBUG("[i2c] read_regs: ACK received, send repeated start sequence\n"); + return i2c_read_bytes(dev, address, data, length, 0); +} + +int i2c_write_bytes(i2c_t dev, uint16_t address, const void *data, + size_t length, uint8_t flags) +{ + assert(dev < I2C_NUMOF); + + I2C_TypeDef *i2c = i2c_config[dev].dev; + + if (!(flags & I2C_NOSTART)) { + DEBUG("[i2c] write_bytes: start transmission\n"); + /* start transmission and send slave address */ + _start(i2c, address, length, 0, flags); + } + + DEBUG("[i2c] write_bytes: write the data\n"); + /* send out data bytes */ + if (_write(i2c, data, length) < 0) { + DEBUG("[i2c] write_bytes: nothing was written\n"); + length = 0; + } + + if (!(flags & I2C_NOSTOP)) { + DEBUG("[i2c] write_bytes: end transmission\n"); + /* end transmission */ + _stop(i2c); + } + + return length; +} + +int i2c_write_regs(i2c_t dev, uint16_t address, uint16_t reg, const void *data, + size_t length, uint8_t flags) +{ + assert(dev < I2C_NUMOF); + + uint16_t tick = TICK_TIMEOUT; + I2C_TypeDef *i2c = i2c_config[dev].dev; + + /* Check to see if the bus is busy */ + while ((i2c->ISR & I2C_ISR_BUSY) && tick--) { + if ((i2c->ISR & ERROR_FLAG) || !tick) { + return -1; + } + } + + /* start transmission and send slave address */ + /* increase length because our data is register+data */ + _start(i2c, address, length + 1, 0, flags); + + /* send register number */ + DEBUG("[i2c] write_regs: ACK received, write reg into DR\n"); + i2c->TXDR = reg; + + /* write out data bytes */ + if (_write(i2c, data, length) < 0) { + length = 0; + } + + /* end transmission */ + _stop(i2c); + + return length; +} + +static inline void _start(I2C_TypeDef *i2c, uint16_t address, + size_t length, uint8_t rw_flag, uint8_t flags) +{ + /* 10 bit address not supported for now */ + (void)flags; + + assert(i2c != NULL); + + uint16_t tick = TICK_TIMEOUT; + + i2c->CR2 = 0; + + DEBUG("[i2c] start: set address mode\n"); + /* set address mode to 7-bit */ + i2c->CR2 &= ~(I2C_CR2_ADD10); + + DEBUG("[i2c] start: set slave address\n"); + /* set slave address */ + i2c->CR2 &= ~(I2C_CR2_SADD); + i2c->CR2 |= (address << 1); + + DEBUG("[i2c] start: set transfert direction\n"); + /* set transfer direction */ + i2c->CR2 &= ~(I2C_CR2_RD_WRN); + i2c->CR2 |= (rw_flag << I2C_CR2_RD_WRN_Pos); + + DEBUG("[i2c] start: set number of bytes\n"); + /* set number of bytes */ + i2c->CR2 &= ~(I2C_CR2_NBYTES); + i2c->CR2 |= (length << I2C_CR2_NBYTES_Pos); + + /* configure autoend configuration */ + i2c->CR2 &= ~(I2C_CR2_AUTOEND); + + /* Clear interrupt */ + i2c->ICR |= CLEAR_FLAG; + + /* generate start condition */ + DEBUG("[i2c] start: generate start condition\n"); + i2c->CR2 |= I2C_CR2_START; + + /* Wait for the start followed by the address to be sent */ + while (!(i2c->CR2 & I2C_CR2_START) && tick--) {} +} + +static inline int _read(I2C_TypeDef *i2c, uint8_t *data, size_t length) +{ + assert(i2c != NULL); + + for (size_t i = 0; i < length; i++) { + /* wait for transfer to finish */ + DEBUG("[i2c] read: Waiting for DR to be full\n"); + uint16_t tick = TICK_TIMEOUT; + while (!(i2c->ISR & I2C_ISR_RXNE) && tick--) { + if (i2c->ISR & ERROR_FLAG || !tick) { + return -1; + } + } + + DEBUG("[i2c] read: DR is now full\n"); + + /* read data from data register */ + data[i] = i2c->RXDR; + DEBUG("[i2c] read: Read byte %i from DR\n", i); + } + + return 0; +} + +static inline int _write(I2C_TypeDef *i2c, const uint8_t *data, size_t length) +{ + assert(i2c != NULL); + + for (size_t i = 0; i < length; i++) { + /* wait for ack */ + DEBUG("[i2c] write: Waiting for ACK\n"); + uint16_t tick = TICK_TIMEOUT; + while (!(i2c->ISR & I2C_ISR_TXIS) && tick--) { + if (i2c->ISR & ERROR_FLAG || !tick) { + return -1; + } + } + + /* write data to data register */ + DEBUG("[i2c] write: Write byte %i to DR\n", i); + i2c->TXDR = data[i]; + DEBUG("[i2c] write: Sending data\n"); + } + + return 0; +} + +static inline void _stop(I2C_TypeDef *i2c) +{ + assert(i2c != NULL); + + uint16_t tick = TICK_TIMEOUT; + + /* make sure transfer is complete */ + DEBUG("[i2c] stop: Wait for transfer to be complete\n"); + while (!(i2c->ISR & I2C_ISR_TC) && tick--) { + if (i2c->ISR & ERROR_FLAG || !tick) { + break; + } + } + + /* send STOP condition */ + DEBUG("[i2c] stop: Generate stop condition\n"); + i2c->CR2 |= I2C_CR2_STOP; +} + +static inline void irq_handler(i2c_t dev) +{ + assert(dev < I2C_NUMOF); + + I2C_TypeDef *i2c = i2c_config[dev].dev; + + unsigned state = i2c->ISR; + DEBUG("\n\n### I2C ERROR OCCURED ###\n"); + DEBUG("status: %08x\n", state); + if (state & I2C_ISR_OVR) { + DEBUG("OVR\n"); + } + if (state & I2C_ISR_NACKF) { + DEBUG("AF\n"); + } + if (state & I2C_ISR_ARLO) { + DEBUG("ARLO\n"); + } + if (state & I2C_ISR_BERR) { + DEBUG("BERR\n"); + } + if (state & I2C_ISR_PECERR) { + DEBUG("PECERR\n"); + } + if (state & I2C_ISR_TIMEOUT) { + DEBUG("TIMEOUT\n"); + } + if (state & I2C_ISR_ALERT) { + DEBUG("SMBALERT\n"); + } + core_panic(PANIC_GENERAL_ERROR, "I2C FAULT"); +} + +#ifdef I2C_0_ISR +void I2C_0_ISR(void) +{ + irq_handler(I2C_DEV(0)); +} +#endif /* I2C_0_ISR */ + +#ifdef I2C_1_ISR +void I2C_1_ISR(void) +{ + irq_handler(I2C_DEV(1)); +} +#endif /* I2C_1_ISR */ + +#endif /* CPU_FAM_STM32L0 || CPU_FAM_STM32F3 */ diff --git a/cpu/stm32f3/periph/i2c.c b/cpu/stm32f3/periph/i2c.c deleted file mode 100644 index 0fada83fe7..0000000000 --- a/cpu/stm32f3/periph/i2c.c +++ /dev/null @@ -1,542 +0,0 @@ -/* - * Copyright (C) 2015 Jan Pohlmann - * - * 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_stm32f3 - * @ingroup drivers_periph_i2c - * @{ - * - * @file - * @brief Low-level I2C driver implementation - * - * @note This implementation only implements the 7-bit addressing mode. - * - * @author Peter Kietzmann - * @author Hauke Petersen - * @auhtor Thomas Eichinger - * @author Jan Pohlmann - * - * @} - */ - -#include - -#include "cpu.h" -#include "panic.h" -#include "irq.h" -#include "mutex.h" -#include "periph_conf.h" -#include "periph/i2c.h" - -#define ENABLE_DEBUG (0) -#include "debug.h" - -/* static function definitions */ -static void _i2c_init(I2C_TypeDef *i2c, uint32_t presc, uint32_t scll, - uint32_t sclh, uint32_t sdadel, uint32_t scldel, - uint32_t timing); -static void _pin_config(GPIO_TypeDef *port_scl, GPIO_TypeDef *port_sda, - int pin_scl, int pin_sda); -static void _start(I2C_TypeDef *dev, uint8_t address, uint8_t length, - uint8_t rw_flag); -static inline void _read(I2C_TypeDef *dev, uint8_t *data, int length); -static inline void _write(I2C_TypeDef *dev, const uint8_t *data, int length); -static inline void _stop(I2C_TypeDef *dev); - -/** - * @brief Array holding one pre-initialized mutex for each I2C device - */ -static mutex_t locks[] = { -#if I2C_0_EN - [I2C_0] = MUTEX_INIT, -#endif -#if I2C_1_EN - [I2C_1] = MUTEX_INIT, -#endif -#if I2C_2_EN - [I2C_2] = MUTEX_INIT -#endif -#if I2C_3_EN - [I2C_3] = MUTEX_INIT -#endif -}; - -int i2c_init_master(i2c_t dev, i2c_speed_t speed) -{ - I2C_TypeDef *i2c; - GPIO_TypeDef *port_scl; - GPIO_TypeDef *port_sda; - int pin_scl = 0, pin_sda = 0; - uint32_t presc, scll, sclh, sdadel, scldel, timing; - - /* - * Speed configuration: - * Example values can be found in the STM32F3xx Reference manual RM0316 - * Chapter 28.4.9: Table 148 Examples of timings settings for f_I2CCLK = 8MHz - * t_SCLL: SCL low level counter - * t_SCLH: SCL high level counter - * t_SDADEL: Delay before sending SDA output - * t_SCLDEL: SCL low level during setup-time - */ - switch (speed) { - case I2C_SPEED_NORMAL: - presc = 1; - scll = 0x13; /* t_SCLL = 5.0us */ - sclh = 0xF; /* t_SCLH = 4.0us */ - sdadel = 0x2; /* t_SDADEL = 500ns */ - scldel = 0x4; /* t_SCLDEL = 1250ns */ - break; - - case I2C_SPEED_FAST: - presc = 0; - scll = 0x9; /* t_SCLL = 1250ns */ - sclh = 0x3; /* t_SCLH = 500ns */ - sdadel = 0x1; /* t_SDADEL = 125ns */ - scldel = 0x3; /* t_SCLDEL = 500ns */ - break; - - case I2C_SPEED_FAST_PLUS: - presc = 0; - scll = 0x6; /* t_SCLL = 875ns */ - sclh = 0x3; /* t_SCLH = 500ns */ - sdadel = 0x0; /* t_SDADEL = 0ns */ - scldel = 0x1; /* t_SCLDEL = 250ns */ - break; - - default: - return -2; - } - - /* prepare the timing register value */ - timing = ((presc << 28) | (scldel << 20) | (sdadel << 16) | (sclh << 8) | scll); - - /* read static device configuration */ - switch (dev) { -#if I2C_0_EN - case I2C_0: - i2c = I2C_0_DEV; - port_scl = I2C_0_SCL_PORT; - pin_scl = I2C_0_SCL_PIN; - port_sda = I2C_0_SDA_PORT; - pin_sda = I2C_0_SDA_PIN; - I2C_0_CLKEN(); - I2C_0_SCL_CLKEN(); - I2C_0_SDA_CLKEN(); - NVIC_SetPriority(I2C_0_ERR_IRQ, I2C_IRQ_PRIO); - NVIC_EnableIRQ(I2C_0_ERR_IRQ); - break; -#endif -#if I2C_1_EN - case I2C_1: - i2c = I2C_1_DEV; - port_scl = I2C_1_SCL_PORT; - pin_scl = I2C_1_SCL_PIN; - port_sda = I2C_1_SDA_PORT; - pin_sda = I2C_1_SDA_PIN; - I2C_1_CLKEN(); - I2C_1_SCL_CLKEN(); - I2C_1_SDA_CLKEN(); - NVIC_SetPriority(I2C_1_ERR_IRQ, I2C_IRQ_PRIO); - NVIC_EnableIRQ(I2C_1_ERR_IRQ); - break; -#endif - - default: - return -1; - } - - /* configure pins */ - _pin_config(port_scl, port_sda, pin_scl, pin_sda); - - /* configure device */ - _i2c_init(i2c, presc, scll, sclh, sdadel, scldel, timing); - - return 0; -} - -static void _i2c_init(I2C_TypeDef *i2c, uint32_t presc, uint32_t scll, - uint32_t sclh, uint32_t sdadel, uint32_t scldel, - uint32_t timing) -{ - (void) presc; - (void) scll; - (void) sclh; - (void) sdadel; - (void) scldel; - - /* disable device */ - i2c->CR1 &= ~(I2C_CR1_PE); - - /* configure analog noise filter */ - i2c->CR1 |= I2C_CR1_ANFOFF; - - /* configure digital noise filter */ - i2c->CR1 |= I2C_CR1_DNF; - - /* set timing registers */ - i2c->TIMINGR = timing; - - /* configure clock stretching */ - i2c->CR1 &= ~(I2C_CR1_NOSTRETCH); - - /* enable device */ - i2c->CR1 |= I2C_CR1_PE; -} - -static void _pin_config(GPIO_TypeDef *port_scl, GPIO_TypeDef *port_sda, - int pin_scl, int pin_sda) -{ - /* Set GPIOs to AF mode */ - port_scl->MODER &= ~(3 << (2 * pin_scl)); - port_scl->MODER |= (2 << (2 * pin_scl)); - port_sda->MODER &= ~(3 << (2 * pin_sda)); - port_sda->MODER |= (2 << (2 * pin_sda)); - - /* Set speed high*/ - port_scl->OSPEEDR |= (3 << (2 * pin_scl)); - port_sda->OSPEEDR |= (3 << (2 * pin_sda)); - - /* Set to push-pull configuration open drain*/ - port_scl->OTYPER |= (1 << pin_scl); - port_sda->OTYPER |= (1 << pin_sda); - - /* Enable pull-up resistors */ - port_scl->PUPDR &= ~(3 << (2 * pin_scl)); - port_scl->PUPDR |= (1 << (2 * pin_scl)); - port_sda->PUPDR &= ~(3 << (2 * pin_sda)); - port_sda->PUPDR |= (1 << (2 * pin_sda)); - - /* Configure GPIOs to for the I2C alternate function */ - if (pin_scl < 8) { - port_scl->AFR[0] &= ~(0xf << (4 * pin_scl)); - port_scl->AFR[0] |= (I2C_0_SCL_AF << (4 * pin_scl)); - } - else { - port_scl->AFR[1] &= ~(0xf << (4 * (pin_scl - 8))); - port_scl->AFR[1] |= (I2C_0_SCL_AF << (4 * (pin_scl - 8))); - } - - if (pin_sda < 8) { - port_sda->AFR[0] &= ~(0xf << (4 * pin_sda)); - port_sda->AFR[0] |= (I2C_0_SDA_AF << (4 * pin_sda)); - } - else { - port_sda->AFR[1] &= ~(0xf << (4 * (pin_sda - 8))); - port_sda->AFR[1] |= (I2C_0_SDA_AF << (4 * (pin_sda - 8))); - } -} - -int i2c_acquire(i2c_t dev) -{ - if (dev >= I2C_NUMOF) { - return -1; - } - mutex_lock(&locks[dev]); - return 0; -} - -int i2c_release(i2c_t dev) -{ - if (dev >= I2C_NUMOF) { - return -1; - } - mutex_unlock(&locks[dev]); - return 0; -} - -int i2c_read_byte(i2c_t dev, uint8_t address, void *data) -{ - return i2c_read_bytes(dev, address, data, 1); -} - -int i2c_read_bytes(i2c_t dev, uint8_t address, void *data, int length) -{ - I2C_TypeDef *i2c; - - switch (dev) { -#if I2C_0_EN - case I2C_0: - i2c = I2C_0_DEV; - break; -#endif -#if I2C_1_EN - case I2C_1: - i2c = I2C_1_DEV; - break; -#endif - - default: - return -1; - } - - /* start reception and send slave address */ - _start(i2c, address, length, I2C_FLAG_READ); - - /* read the data bytes */ - _read(i2c, data, length); - - /* end transmission */ - _stop(i2c); - - return length; -} - -int i2c_read_reg(i2c_t dev, uint8_t address, uint8_t reg, void *data) -{ - return i2c_read_regs(dev, address, reg, data, 1); -} - -int i2c_read_regs(i2c_t dev, uint8_t address, uint8_t reg, void *data, int length) -{ - I2C_TypeDef *i2c; - - switch (dev) { -#if I2C_0_EN - case I2C_0: - i2c = I2C_0_DEV; - break; -#endif -#if I2C_1_EN - case I2C_1: - i2c = I2C_1_DEV; - break; -#endif - - default: - return -1; - } - - /* send start sequence and slave address */ - _start(i2c, address, length, I2C_FLAG_WRITE); - - /* wait for ack */ - DEBUG("Waiting for ACK\n"); - while (!(i2c->ISR & I2C_ISR_TXIS)) {} - - /* send register number */ - DEBUG("ACK received, write reg into DR\n"); - i2c->TXDR = reg; - - /* send repeated start sequence, read registers and end transmission */ - DEBUG("ACK received, send repeated start sequence\n"); - return i2c_read_bytes(dev, address, data, length); -} - -int i2c_write_byte(i2c_t dev, uint8_t address, uint8_t data) -{ - return i2c_write_bytes(dev, address, &data, 1); -} - -int i2c_write_bytes(i2c_t dev, uint8_t address, const void *data, int length) -{ - I2C_TypeDef *i2c; - - switch (dev) { -#if I2C_0_EN - case I2C_0: - i2c = I2C_0_DEV; - break; -#endif -#if I2C_1_EN - case I2C_1: - i2c = I2C_1_DEV; - break; -#endif - - default: - return -1; - } - - /* start transmission and send slave address */ - _start(i2c, address, length, I2C_FLAG_WRITE); - - /* send out data bytes */ - _write(i2c, data, length); - - /* end transmission */ - _stop(i2c); - - return length; -} - -int i2c_write_reg(i2c_t dev, uint8_t address, uint8_t reg, uint8_t data) -{ - return i2c_write_regs(dev, address, reg, &data, 1); -} - -int i2c_write_regs(i2c_t dev, uint8_t address, uint8_t reg, const void *data, int length) -{ - I2C_TypeDef *i2c; - - switch (dev) { -#if I2C_0_EN - case I2C_0: - i2c = I2C_0_DEV; - break; -#endif -#if I2C_1_EN - case I2C_1: - i2c = I2C_1_DEV; - break; -#endif - - default: - return -1; - } - - /* start transmission and send slave address */ - /* increase length because our data is register+data */ - _start(i2c, address, length+1, I2C_FLAG_WRITE); - - /* wait for ack */ - DEBUG("Waiting for ACK\n"); - while (!(i2c->ISR & I2C_ISR_TXIS)) {} - - /* send register number */ - DEBUG("ACK received, write reg into DR\n"); - i2c->TXDR = reg; - - /* write out data bytes */ - _write(i2c, data, length); - - /* end transmission */ - _stop(i2c); - - return length; -} - -void i2c_poweron(i2c_t dev) -{ - switch (dev) { -#if I2C_0_EN - case I2C_0: - I2C_0_CLKEN(); - break; -#endif -#if I2C_1_EN - case I2C_1: - I2C_1_CLKEN(); - break; -#endif - } -} - -void i2c_poweroff(i2c_t dev) -{ - switch (dev) { -#if I2C_0_EN - case I2C_0: - while (I2C_0_DEV->ISR & I2C_ISR_BUSY) {} - - I2C_0_CLKDIS(); - break; -#endif -#if I2C_1_EN - case I2C_1: - while (I2C_1_DEV->ISR & I2C_ISR_BUSY) {} - - I2C_0_CLKDIS(); - break; -#endif - } -} - -static void _start(I2C_TypeDef *dev, uint8_t address, uint8_t length, uint8_t rw_flag) -{ - /* set address mode to 7-bit */ - dev->CR2 &= ~(I2C_CR2_ADD10); - - /* set slave address */ - dev->CR2 &= ~(I2C_CR2_SADD); - dev->CR2 |= (address << 1); - - /* set transfer direction */ - dev->CR2 &= ~(I2C_CR2_RD_WRN); - dev->CR2 |= (rw_flag << 10); - - /* set number of bytes */ - dev->CR2 &= ~(I2C_CR2_NBYTES); - dev->CR2 |= (length << 16); - - /* configure autoend configuration */ - dev->CR2 &= ~(I2C_CR2_AUTOEND); - - /* generate start condition */ - DEBUG("Generate start condition\n"); - dev->CR2 |= I2C_CR2_START; -} - -static inline void _read(I2C_TypeDef *dev, uint8_t *data, int length) -{ - for (int i = 0; i < length; i++) { - /* wait for transfer to finish */ - DEBUG("Waiting for DR to be full\n"); - while (!(dev->ISR & I2C_ISR_RXNE)) {} - DEBUG("DR is now full\n"); - - /* read data from data register */ - data[i] = dev->RXDR; - DEBUG("Read byte %i from DR\n", i); - } -} - -static inline void _write(I2C_TypeDef *dev, const uint8_t *data, int length) -{ - for (int i = 0; i < length; i++) { - /* wait for ack */ - DEBUG("Waiting for ACK\n"); - while (!(dev->ISR & I2C_ISR_TXIS)) {} - - /* write data to data register */ - DEBUG("Write byte %i to DR\n", i); - dev->TXDR = data[i]; - DEBUG("Sending data\n"); - } -} - -static inline void _stop(I2C_TypeDef *dev) -{ - /* make sure transfer is complete */ - DEBUG("Wait for transfer to be complete\n"); - while (!(dev->ISR & I2C_ISR_TC)) {} - - /* send STOP condition */ - DEBUG("Generate stop condition\n"); - dev->CR2 |= I2C_CR2_STOP; -} - -#if I2C_0_EN -void I2C_0_ERR_ISR(void) -{ - unsigned state = I2C_0_DEV->ISR; - DEBUG("\n\n### I2C ERROR OCCURED ###\n"); - DEBUG("status: %08x\n", state); - if (state & I2C_ISR_OVR) { - DEBUG("OVR\n"); - } - if (state & I2C_ISR_NACKF) { - DEBUG("AF\n"); - } - if (state & I2C_ISR_ARLO) { - DEBUG("ARLO\n"); - } - if (state & I2C_ISR_BERR) { - DEBUG("BERR\n"); - } - if (state & I2C_ISR_PECERR) { - DEBUG("PECERR\n"); - } - if (state & I2C_ISR_TIMEOUT) { - DEBUG("TIMEOUT\n"); - } - if (state & I2C_ISR_ALERT) { - DEBUG("SMBALERT\n"); - } - core_panic(PANIC_GENERAL_ERROR, "I2C FAULT"); -} -#endif /* I2C_0_EN */ diff --git a/cpu/stm32l0/include/periph_cpu.h b/cpu/stm32l0/include/periph_cpu.h index 08fb31a7e4..edac77599f 100644 --- a/cpu/stm32l0/include/periph_cpu.h +++ b/cpu/stm32l0/include/periph_cpu.h @@ -70,18 +70,6 @@ typedef struct { uint8_t chan; /**< internal channel the pin is connected to */ } adc_conf_t; -/** - * @brief I2C configuration data structure - */ -typedef struct { - I2C_TypeDef *dev; /**< i2c device */ - gpio_t scl; /**< scl pin number */ - gpio_t sda; /**< sda pin number */ - gpio_mode_t pin_mode; /**< with or without pull resistor */ - gpio_af_t af; /**< I2C alternate function value */ - uint8_t ev_irqn; /**< event IRQ */ -} i2c_conf_t; - /** * @brief Override the default initial PM blocker * @todo we block all modes per default, until PM is cleanly implemented diff --git a/cpu/stm32l0/periph/i2c.c b/cpu/stm32l0/periph/i2c.c deleted file mode 100644 index 1f3f6ee088..0000000000 --- a/cpu/stm32l0/periph/i2c.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright (C) 2017 we-sens.com - * - * 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_stm32l0 - * @ingroup drivers_periph_i2c - * @{ - * - * @file - * @brief Low-level I2C driver implementation - * - * @note This implementation only implements the 7-bit addressing polling mode - * (for now interrupt mode is not available) - * - * @author Aurélien Fillau - * - * @} - */ - -#include - -#include "cpu.h" -#include "mutex.h" -#include "periph/i2c.h" -#include "periph/gpio.h" -#include "periph_conf.h" - -#define ENABLE_DEBUG (0) -#define TICK_TIMEOUT (0xFFFF) -#include "debug.h" - -#define CLEAR_FLAG(dev) dev->ICR |= I2C_ICR_NACKCF | \ - I2C_ICR_ARLOCF | \ - I2C_ICR_BERRCF - -#define ERROR_FLAG (I2C_ISR_NACKF | I2C_ISR_ARLO | I2C_ISR_BERR) - -/* static function definitions */ -static void _i2c_init(I2C_TypeDef *i2c, uint32_t presc, uint32_t scll, - uint32_t sclh, uint32_t sdadel, uint32_t scldel, - uint32_t timing); -static void _start(I2C_TypeDef *dev, uint8_t address, uint8_t length, uint8_t rw_flag); -static inline int _read(I2C_TypeDef *dev, uint8_t *data, int length); -static inline int _write(I2C_TypeDef *i2c, const uint8_t *data, int length); -static inline void _stop(I2C_TypeDef *i2c); - -/** - * @brief Array holding one pre-initialized mutex for each I2C device - */ -static mutex_t locks[] = { -#if I2C_0_EN - [I2C_0] = MUTEX_INIT, -#endif -#if I2C_1_EN - [I2C_1] = MUTEX_INIT, -#endif -#if I2C_2_EN - [I2C_2] = MUTEX_INIT, -#endif -#if I2C_3_EN - [I2C_3] = MUTEX_INIT -#endif -}; - -int i2c_init_master(i2c_t dev, i2c_speed_t speed) -{ - I2C_TypeDef *i2c; - gpio_t scl_pin; - gpio_t sda_pin; - gpio_af_t scl_af; - gpio_af_t sda_af; - uint32_t presc, scll, sclh, sdadel, scldel, timing; - - /* - * Speed configuration: - * Example values can be found in the STM32L0xx Reference manual RM0367 - * with f_I2CCLK = 16MHz - * t_SCLL: SCL low level counter - * t_SCLH: SCL high level counter - * t_SDADEL: Delay before sending SDA output - * t_SCLDEL: SCL low level during setup-time - */ - switch (speed) { - case I2C_SPEED_NORMAL: - presc = 1; - scll = 0x56; /* t_SCLL = 5.0us */ - sclh = 0x3E; /* t_SCLH = 4.0us */ - sdadel = 0x1; /* t_SDADEL = 500ns */ - scldel = 0xA; /* t_SCLDEL = 1250ns */ - break; - - case I2C_SPEED_FAST: - presc = 0; - scll = 0x2E; /* t_SCLL = 1250ns */ - sclh = 0x11; /* t_SCLH = 500ns */ - sdadel = 0x1; /* t_SDADEL = 125ns */ - scldel = 0xB; /* t_SCLDEL = 500ns */ - break; - - case I2C_SPEED_FAST_PLUS: - presc = 0; - scll = 0x6; /* t_SCLL = 875ns */ - sclh = 0x3; /* t_SCLH = 500ns */ - sdadel = 0x0; /* t_SDADEL = 0ns */ - scldel = 0x1; /* t_SCLDEL = 250ns */ - break; - - default: - return -2; - } - - /* prepare the timing register value */ - timing = ((presc << 28) | (scldel << 20) | (sdadel << 16) | (sclh << 8) | scll); - - /* read static device configuration */ - switch (dev) { -#if I2C_0_EN - case I2C_0: - i2c = I2C_0_DEV; - scl_pin = GPIO_PIN(I2C_0_SCL_PORT, I2C_0_SCL_PIN); /* scl pin number */ - sda_pin = GPIO_PIN(I2C_0_SDA_PORT, I2C_0_SDA_PIN); /* sda pin number */ - scl_af = I2C_0_SCL_AF; - sda_af = I2C_0_SDA_AF; - I2C_0_CLKEN(); - I2C_0_SCL_CLKEN(); - I2C_0_SDA_CLKEN(); - break; -#endif -#if I2C_1_EN - case I2C_1: - i2c = I2C_1_DEV; - scl_pin = GPIO_PIN(I2C_1_SCL_PORT, I2C_1_SCL_PIN); /* scl pin number */ - sda_pin = GPIO_PIN(I2C_1_SDA_PORT, I2C_1_SDA_PIN); /* sda pin number */ - scl_af = I2C_1_SCL_AF; - sda_af = I2C_1_SDA_AF; - I2C_1_CLKEN(); - I2C_1_SCL_CLKEN(); - I2C_1_SDA_CLKEN(); - break; -#endif -#if I2C_2_EN - case I2C_2: - i2c = I2C_2_DEV; - scl_pin = GPIO_PIN(I2C_2_SCL_PORT, I2C_2_SCL_PIN); /* scl pin number */ - sda_pin = GPIO_PIN(I2C_2_SDA_PORT, I2C_2_SDA_PIN); /* sda pin number */ - scl_af = I2C_2_SCL_AF; - sda_af = I2C_2_SDA_AF; - I2C_2_CLKEN(); - I2C_2_SCL_CLKEN(); - I2C_2_SDA_CLKEN(); - break; -#endif - - default: - return -1; - } - - /* configure pins */ - gpio_init(scl_pin, GPIO_OD_PU); - gpio_init_af(scl_pin, scl_af); - gpio_init(sda_pin, GPIO_OD_PU); - gpio_init_af(sda_pin, sda_af); - - /* configure device */ - _i2c_init(i2c, presc, scll, sclh, sdadel, scldel, timing); - - return 0; -} - -static void _i2c_init(I2C_TypeDef *i2c, uint32_t presc, uint32_t scll, - uint32_t sclh, uint32_t sdadel, uint32_t scldel, - uint32_t timing) -{ - (void) presc; - (void) scll; - (void) sclh; - (void) sdadel; - (void) scldel; - - /* disable device */ - i2c->CR1 &= ~(I2C_CR1_PE); - - /* configure analog noise filter */ - i2c->CR1 |= I2C_CR1_ANFOFF; - - /* configure digital noise filter */ - i2c->CR1 |= I2C_CR1_DNF; - - /* set timing registers */ - i2c->TIMINGR = timing; - - /* configure clock stretching */ - i2c->CR1 &= ~(I2C_CR1_NOSTRETCH); - - CLEAR_FLAG(i2c); - - /* enable device */ - i2c->CR1 |= I2C_CR1_PE; -} - -int i2c_acquire(i2c_t dev) -{ - if (dev >= I2C_NUMOF) { - return -1; - } - mutex_lock(&locks[dev]); - return 0; -} - -int i2c_release(i2c_t dev) -{ - if (dev >= I2C_NUMOF) { - return -1; - } - mutex_unlock(&locks[dev]); - return 0; -} - -int i2c_read_byte(i2c_t dev, uint8_t address, void *data) -{ - return i2c_read_bytes(dev, address, data, 1); -} - -int i2c_read_bytes(i2c_t dev, uint8_t address, void *data, int length) -{ - I2C_TypeDef *i2c; - - switch (dev) { -#if I2C_0_EN - case I2C_0: - i2c = I2C_0_DEV; - break; -#endif -#if I2C_1_EN - case I2C_1: - i2c = I2C_1_DEV; - break; -#endif -#if I2C_2_EN - case I2C_2: - i2c = I2C_2_DEV; - break; -#endif - - default: - return -1; - } - - /* start reception and send slave address */ - _start(i2c, address, length, I2C_FLAG_READ); - - /* read the data bytes */ - if (_read(i2c, data, length) < 0) { - length = 0; - } - - /* end transmission */ - _stop(i2c); - - return length; -} - -int i2c_read_reg(i2c_t dev, uint8_t address, uint8_t reg, void *data) -{ - return i2c_read_regs(dev, address, reg, data, 1); -} - -int i2c_read_regs(i2c_t dev, uint8_t address, uint8_t reg, void *data, int length) -{ - uint16_t tick = TICK_TIMEOUT; - I2C_TypeDef *i2c; - - switch (dev) { -#if I2C_0_EN - case I2C_0: - i2c = I2C_0_DEV; - break; -#endif -#if I2C_1_EN - case I2C_1: - i2c = I2C_1_DEV; - break; -#endif -#if I2C_2_EN - case I2C_2: - i2c = I2C_2_DEV; - break; -#endif - - default: - return -1; - } - - /* Check to see if the bus is busy */ - while ((i2c->ISR & I2C_ISR_BUSY) && tick--) { - if ((i2c->ISR & ERROR_FLAG) || !tick) { - /* end transmission */ - _stop(i2c); - return -1; - } - } - - /* send start sequence and slave address */ - _start(i2c, address, 1, I2C_FLAG_WRITE); - - tick = TICK_TIMEOUT; - /* wait for ack */ - DEBUG("Waiting for ACK\n"); - while (!(i2c->ISR & I2C_ISR_TXIS) && tick--) { - if ((i2c->ISR & ERROR_FLAG) || !tick) { - /* end transmission */ - _stop(i2c); - return -1; - } - } - - DEBUG("Write register to read\n"); - i2c->TXDR = reg; - - /* send repeated start sequence, read registers and end transmission */ - DEBUG("ACK received, send repeated start sequence\n"); - return i2c_read_bytes(dev, address, data, length); -} - -int i2c_write_byte(i2c_t dev, uint8_t address, uint8_t data) -{ - return i2c_write_bytes(dev, address, &data, 1); -} - -int i2c_write_bytes(i2c_t dev, uint8_t address, const void *data, int length) -{ - I2C_TypeDef *i2c; - - switch (dev) { -#if I2C_0_EN - case I2C_0: - i2c = I2C_0_DEV; - break; -#endif -#if I2C_1_EN - case I2C_1: - i2c = I2C_1_DEV; - break; -#endif -#if I2C_2_EN - case I2C_2: - i2c = I2C_2_DEV; - break; -#endif - - default: - return -1; - } - - /* start transmission and send slave address */ - _start(i2c, address, length, I2C_FLAG_WRITE); - - /* send out data bytes */ - if (_write(i2c, data, length) < 0) { - length = 0; - } - - /* end transmission */ - _stop(i2c); - - return length; -} - -int i2c_write_reg(i2c_t dev, uint8_t address, uint8_t reg, uint8_t data) -{ - return i2c_write_regs(dev, address, reg, &data, 1); -} - -int i2c_write_regs(i2c_t dev, uint8_t address, uint8_t reg, const void *data, int length) -{ - uint16_t tick = TICK_TIMEOUT; - I2C_TypeDef *i2c; - - switch (dev) { -#if I2C_0_EN - case I2C_0: - i2c = I2C_0_DEV; - break; -#endif -#if I2C_1_EN - case I2C_1: - i2c = I2C_1_DEV; - break; -#endif -#if I2C_2_EN - case I2C_2: - i2c = I2C_2_DEV; - break; -#endif - - default: - return -1; - } - - /* Check to see if the bus is busy */ - while ((i2c->ISR & I2C_ISR_BUSY) && tick--) { - if ((i2c->ISR & ERROR_FLAG) || !tick) { - return -1; - } - } - - /* start transmission and send slave address */ - /* increase length because our data is register+data */ - _start(i2c, address, length+1, I2C_FLAG_WRITE); - - /* send register number */ - DEBUG("ACK received, write reg into DR\n"); - i2c->TXDR = reg; - - /* write out data bytes */ - if (_write(i2c, data, length) < 0) { - length = 0; - } - - /* end transmission */ - _stop(i2c); - - return length; -} - -void i2c_poweron(i2c_t dev) -{ - switch (dev) { -#if I2C_0_EN - case I2C_0: - I2C_0_CLKEN(); - break; -#endif -#if I2C_1_EN - case I2C_1: - I2C_1_CLKEN(); - break; -#endif -#if I2C_2_EN - case I2C_2: - I2C_2_CLKEN(); - break; -#endif - } -} - -void i2c_poweroff(i2c_t dev) -{ -#if defined I2C_0_EN || defined I2C_1_EN || defined I2C_2_EN - uint16_t tick = TICK_TIMEOUT; -#endif - - switch (dev) { -#if I2C_0_EN - case I2C_0: - while ((I2C_0_DEV->ISR & I2C_ISR_BUSY) && tick--) {} - - I2C_0_CLKDIS(); - break; -#endif -#if I2C_1_EN - case I2C_1: - while ((I2C_1_DEV->ISR & I2C_ISR_BUSY) && tick--) {} - - I2C_1_CLKDIS(); - break; -#endif -#if I2C_2_EN - case I2C_2: - while ((I2C_2_DEV->ISR & I2C_ISR_BUSY) && tick--) {} - - I2C_2_CLKDIS(); - break; -#endif - } -} - -static void _start(I2C_TypeDef *dev, uint8_t address, uint8_t length, uint8_t rw_flag) -{ - uint16_t tick = TICK_TIMEOUT; - - dev->CR2 = 0; - /* set address mode to 7-bit */ - dev->CR2 &= ~(I2C_CR2_ADD10); - - /* set slave address */ - dev->CR2 &= ~(I2C_CR2_SADD); - dev->CR2 |= (address << 1); - - /* set transfer direction */ - dev->CR2 &= ~(I2C_CR2_RD_WRN); - dev->CR2 |= (rw_flag << I2C_CR2_RD_WRN_Pos); - - /* set number of bytes */ - dev->CR2 &= ~(I2C_CR2_NBYTES); - dev->CR2 |= (length << I2C_CR2_NBYTES_Pos); - - /* configure autoend configuration */ - dev->CR2 &= ~(I2C_CR2_AUTOEND); - - CLEAR_FLAG(dev); - - /* generate start condition */ - DEBUG("Generate start condition\n"); - dev->CR2 |= I2C_CR2_START; - - /* Wait for the start followed by the address to be sent */ - while (!(dev->CR2 & I2C_CR2_START) && tick--) {} -} - -static inline int _read(I2C_TypeDef *dev, uint8_t *data, int length) -{ - uint16_t tick = TICK_TIMEOUT; - - for (int i = 0; i < length; i++) { - /* wait for transfer to finish */ - DEBUG("Waiting for DR to be full\n"); - while (!(dev->ISR & I2C_ISR_RXNE) && tick--) { - if (dev->ISR & ERROR_FLAG || !tick) { - return -1; - } - } - - DEBUG("DR is now full\n"); - - /* read data from data register */ - data[i] = dev->RXDR; - DEBUG("Read byte %i from DR\n", i); - } - - return 0; -} - -static inline int _write(I2C_TypeDef *dev, const uint8_t *data, int length) -{ - uint16_t tick = TICK_TIMEOUT; - - for (int i = 0; i < length; i++) { - /* wait for ack */ - DEBUG("Waiting for ACK\n"); - while (!(dev->ISR & I2C_ISR_TXIS) && tick--) { - if (dev->ISR & ERROR_FLAG || !tick) { - return -1; - } - } - - /* write data to data register */ - DEBUG("Write byte %i to DR\n", i); - dev->TXDR = data[i]; - DEBUG("Sending data\n"); - } - - return 0; -} - -static inline void _stop(I2C_TypeDef *dev) -{ - uint16_t tick = TICK_TIMEOUT; - - /* make sure transfer is complete */ - DEBUG("Wait for transfer to be complete\n"); - while (!(dev->ISR & I2C_ISR_TC) && tick--) { - if (dev->ISR & ERROR_FLAG || !tick) { - break; - } - } - - /* send STOP condition */ - DEBUG("Generate stop condition\n"); - dev->CR2 |= I2C_CR2_STOP; -}