diff --git a/cpu/stm32_common/include/periph_cpu_common.h b/cpu/stm32_common/include/periph_cpu_common.h index 7a2cb39721..1964788cc6 100644 --- a/cpu/stm32_common/include/periph_cpu_common.h +++ b/cpu/stm32_common/include/periph_cpu_common.h @@ -159,9 +159,9 @@ typedef uint32_t gpio_t; #define PERIPH_I2C_NEED_READ_REG /** Use write reg function from periph common */ #define PERIPH_I2C_NEED_WRITE_REG +#define PERIPH_I2C_NEED_READ_REGS #if defined(CPU_FAM_STM32F1) || defined(CPU_FAM_STM32F2) || \ defined(CPU_FAM_STM32L1) || defined(CPU_FAM_STM32F4) -#define PERIPH_I2C_NEED_READ_REGS #define PERIPH_I2C_NEED_WRITE_REGS #endif /** @} */ diff --git a/cpu/stm32_common/periph/i2c_1.c b/cpu/stm32_common/periph/i2c_1.c index ffac79370a..2422539a8f 100644 --- a/cpu/stm32_common/periph/i2c_1.c +++ b/cpu/stm32_common/periph/i2c_1.c @@ -2,6 +2,7 @@ * Copyright (C) 2015 Jan Pohlmann * 2017 we-sens.com * 2018 Inria + * 2018 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 @@ -26,6 +27,7 @@ * @author Jan Pohlmann * @author AurĂ©lien Fillau * @author Alexandre Abadie + * @author Kevin Weiss * * @} */ @@ -46,22 +48,22 @@ #include "debug.h" #define TICK_TIMEOUT (0xFFFF) +#define MAX_BYTES_PER_FRAME (256) -#define I2C_IRQ_PRIO (1) -#define I2C_FLAG_READ (I2C_READ) -#define I2C_FLAG_WRITE (0) +#define I2C_IRQ_PRIO (1) +#define I2C_FLAG_READ (I2C_READ << I2C_CR2_RD_WRN_Pos) +#define I2C_FLAG_WRITE (0) -#define CLEAR_FLAG (I2C_ICR_NACKCF | I2C_ICR_ARLOCF | I2C_ICR_BERRCF) -#define ERROR_FLAG (I2C_ISR_NACKF | I2C_ISR_ARLO | I2C_ISR_BERR) +#define CLEAR_FLAG (I2C_ICR_NACKCF | I2C_ICR_ARLOCF | I2C_ICR_BERRCF | I2C_ICR_ADDRCF) /* static function definitions */ static inline void _i2c_init(I2C_TypeDef *i2c, uint32_t timing); -static inline int _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 int _stop(I2C_TypeDef *i2c); -static inline int _check_bus(I2C_TypeDef *i2c); +static int _write(I2C_TypeDef *i2c, uint16_t addr, const void *data, + size_t length, uint8_t flags, uint32_t cr2_flags); +static int _start(I2C_TypeDef *i2c, uint32_t cr2, uint8_t flags); +static int _stop(I2C_TypeDef *i2c); +static int _wait_isr_set(I2C_TypeDef *i2c, uint32_t mask, uint8_t flags); +static inline int _wait_for_bus(I2C_TypeDef *i2c); /** * @brief Array holding one pre-initialized mutex for each I2C device @@ -146,365 +148,248 @@ 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_write_regs(i2c_t dev, uint16_t addr, uint16_t reg, + const void *data, size_t len, uint8_t flags) +{ + assert(dev < I2C_NUMOF); + if (flags & (I2C_NOSTOP | I2C_NOSTART)) { + return -EOPNOTSUPP; + } + + I2C_TypeDef *i2c = i2c_config[dev].dev; + assert(i2c != NULL); + DEBUG("[i2c] write_regs: Starting\n"); + /* As a higher level function we know the bus should be free */ + if (i2c->ISR & I2C_ISR_BUSY) { + return -EAGAIN; + } + /* First set ADDR and register with no stop */ + /* No RELOAD should be set so repeated start is valid */ + int ret = _write(i2c, addr, ®, (flags & I2C_REG16) ? 2 : 1, + flags | I2C_NOSTOP, I2C_CR2_RELOAD); + if (ret < 0) { + return ret; + } + /* Then get the data from device */ + return _write(i2c, addr, data, len, I2C_NOSTART, 0); +} + int i2c_read_bytes(i2c_t dev, uint16_t address, void *data, size_t length, uint8_t flags) { - assert(dev < I2C_NUMOF); + assert(dev < I2C_NUMOF && length < MAX_BYTES_PER_FRAME); I2C_TypeDef *i2c = i2c_config[dev].dev; + assert(i2c != NULL); - if (flags & I2C_ADDR10) { + /* If reload was set, cannot send a repeated start */ + if ((i2c->ISR & I2C_ISR_TCR) && !(flags & I2C_NOSTART)) { return -EOPNOTSUPP; } - - int ret = 0; - - if (!(flags & I2C_NOSTART)) { - DEBUG("[i2c] read_bytes: send start condition\n"); - /* start reception and send slave address */ - ret = _start(i2c, address, length, I2C_FLAG_READ, flags); - if (ret < 0) { - _stop(i2c); - return ret; - } - } - - DEBUG("[i2c] read_bytes: read the data\n"); - /* read the data bytes */ - ret = _read(i2c, data, length); + DEBUG("[i2c] read_bytes: Starting\n"); + /* RELOAD is needed because we don't know the full frame */ + int ret = _start(i2c, (address << 1) | (length << I2C_CR2_NBYTES_Pos) | + I2C_CR2_RELOAD | I2C_FLAG_READ, flags); if (ret < 0) { - _stop(i2c); - DEBUG("[i2c] read_bytes: error while reading\n"); return ret; } - if (!(flags & I2C_NOSTOP)) { - DEBUG("[i2c] read_bytes: end transmission\n"); - ret = _stop(i2c); + for (size_t i = 0; i < length; i++) { + /* wait for transfer to finish */ + DEBUG("[i2c] read_bytes: Waiting for DR to be full\n"); + ret = _wait_isr_set(i2c, I2C_ISR_RXNE, flags); if (ret < 0) { return ret; } + /* read data from data register */ + ((uint8_t*)data)[i]= i2c->RXDR; + DEBUG("[i2c] read_bytes: DR full, read 0x%02X\n", ((uint8_t*)data)[i]); + } + if (flags & I2C_NOSTOP) { + /* With NOSTOP, the TCR indicates that the next command is ready */ + /* TCR is needed because RELOAD is set preventing a NACK on last byte */ + return _wait_isr_set(i2c, I2C_ISR_TCR, flags); + } + /* Wait until stop before other commands are sent */ + ret = _wait_isr_set(i2c, I2C_ISR_STOPF, flags); + if (ret < 0) { + return ret; } - return ret; -} - -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); - - if ((flags & I2C_REG16) || (flags & I2C_ADDR10)) { - return -EOPNOTSUPP; - } - - 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 (!tick) { - return -ETIMEDOUT; - } - - if (!(flags & I2C_NOSTART)) { - DEBUG("[i2c] read_regs: send start sequence\n"); - /* send start sequence and slave address */ - int ret = _start(i2c, address, 1, 0, flags); - if (ret < 0) { - _stop(i2c); - return ret; - } - } - - tick = TICK_TIMEOUT; - /* wait for ack */ - while (!(i2c->ISR & I2C_ISR_TXIS) && tick--) { - if ((i2c->ISR & ERROR_FLAG) || !tick) { - /* end transmission */ - _stop(i2c); - return -ENXIO; - } - } - - 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); + return _wait_for_bus(i2c); } +/** + * Cannot support continuous writes or frame splitting at this level. If an + * I2C_NOSTOP has been sent it must be followed by a repeated start or stop. + */ 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; + DEBUG("[i2c] write_bytes: Starting\n"); + return _write(i2c, address, data, length, flags, 0); +} - if (flags & I2C_ADDR10) { +static int _write(I2C_TypeDef *i2c, uint16_t addr, const void *data, + size_t length, uint8_t flags, uint32_t cr2_flags) +{ + assert(i2c != NULL && length < MAX_BYTES_PER_FRAME); + + /* If reload was NOT set, must either stop or start */ + if ((i2c->ISR & I2C_ISR_TC) && (flags & I2C_NOSTART)) { return -EOPNOTSUPP; } - - I2C_TypeDef *i2c = i2c_config[dev].dev; - - int ret = 0; - - if (!(flags & I2C_NOSTART)) { - DEBUG("[i2c] write_bytes: start transmission\n"); - /* start transmission and send slave address */ - ret = _start(i2c, address, length, I2C_FLAG_WRITE, flags); - if (ret < 0) { - _stop(i2c); - return ret; - } - } - - DEBUG("[i2c] write_bytes: write the data (%d bytes)\n", length); - /* send out data bytes */ - ret = _write(i2c, data, length); + int ret = _start(i2c, (addr << 1) | (length << I2C_CR2_NBYTES_Pos) | + cr2_flags, flags); if (ret < 0) { - _stop(i2c); - DEBUG("[i2c] write_bytes: nothing was written\n"); return ret; } - if (!(flags & I2C_NOSTOP)) { - DEBUG("[i2c] write_bytes: end transmission\n"); - /* end transmission */ - ret = _stop(i2c); + for (size_t i = 0; i < length; i++) { + DEBUG("[i2c] write_bytes: Waiting for TX reg to be free\n"); + ret = _wait_isr_set(i2c, I2C_ISR_TXIS, flags); if (ret < 0) { return ret; } + DEBUG("[i2c] write_bytes: TX is free so send byte\n"); + /* write data to data register */ + i2c->TXDR = ((uint8_t*)data)[i]; } - return ret; -} - -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; - - int ret = 0; - - if ((flags & I2C_REG16) || (flags & I2C_ADDR10)) { - return -EOPNOTSUPP; - } - - /* Check to see if the bus is busy */ - while ((i2c->ISR & I2C_ISR_BUSY) && tick--) {} - if (!tick) { - return -ETIMEDOUT; - } - - if (!(flags & I2C_NOSTART)) { - /* start transmission and send slave address */ - /* increase length because our data is register+data */ - ret = _start(i2c, address, length + 1, I2C_FLAG_WRITE, flags); - if (ret < 0) { - _stop(i2c); - return ret; + if (flags & I2C_NOSTOP) { + if (cr2_flags & I2C_CR2_RELOAD) { + DEBUG("[i2c] write_bytes: Waiting for TCR\n"); + /* With NOSTOP, the TCR indicates that the next command is ready */ + /* TCR is needed because RELOAD allows loading more bytes */ + return _wait_isr_set(i2c, I2C_ISR_TCR, flags); + } + else { + DEBUG("[i2c] write_bytes: Waiting for TC\n"); + /* With NOSTOP, the TC indicates that the next command is ready */ + /* TC is needed because no reload is set for repeated start */ + return _wait_isr_set(i2c, I2C_ISR_TC, flags); } } - - /* send register number */ - DEBUG("[i2c] write_regs: ACK received, write reg into DR\n"); - i2c->TXDR = reg; - - /* write out data bytes */ - ret = _write(i2c, data, length); + DEBUG("[i2c] write_bytes: Waiting for stop\n"); + /* Wait until stop before other commands are sent */ + ret = _wait_isr_set(i2c, I2C_ISR_STOPF, flags); if (ret < 0) { - _stop(i2c); return ret; } - - if (!(flags & I2C_NOSTOP)) { - /* end transmission */ - ret = _stop(i2c); - if (ret < 0) { - return ret; - } - } - - return ret; + return _wait_for_bus(i2c); } -static inline int _start(I2C_TypeDef *i2c, uint16_t address, - size_t length, uint8_t rw_flag, uint8_t flags) -{ - /* 10 bit address not supported for now */ - if (flags & I2C_ADDR10) { - return -EOPNOTSUPP; - } +static int _start(I2C_TypeDef *i2c, uint32_t cr2, uint8_t flags) +{ assert(i2c != NULL); + assert((i2c->ISR & I2C_ISR_BUSY) || !(flags & I2C_NOSTART)); - 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 */ - uint16_t tick = TICK_TIMEOUT; - while ((i2c->CR2 & I2C_CR2_START) && tick--) {} - if (!tick) { - return -ETIMEDOUT; + if (flags & I2C_ADDR10) { + return -EOPNOTSUPP; } - return _check_bus(i2c); -} - -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"); + if (!(flags & I2C_NOSTART)) { + DEBUG("[i2c] start: Generate start condition\n"); + /* Generate start condition */ + cr2 |= I2C_CR2_START; + } + if (!(flags & I2C_NOSTOP)) { + cr2 |= I2C_CR2_AUTOEND; + cr2 &= ~(I2C_CR2_RELOAD); + } + DEBUG("[i2c] start: Setting CR2=0x%08lX\n", cr2); + i2c->CR2 = cr2; + if (!(flags & I2C_NOSTART)) { uint16_t tick = TICK_TIMEOUT; - while (!(i2c->ISR & I2C_ISR_RXNE) && tick--) {} - if (i2c->ISR & ERROR_FLAG || !tick) { - return -ETIMEDOUT; + while ((i2c->CR2 & I2C_CR2_START) && tick--) { + if (!tick) { + /* Try to stop for state error recovery */ + _stop(i2c); + return -ETIMEDOUT; + } } - - 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 _check_bus(i2c); -} - -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) { - DEBUG("[i2c] write: TXIS timeout\n"); + DEBUG("[i2c] start: Start condition and address generated\n"); + /* Check if the device is there */ + if ((i2c->ISR & I2C_ISR_NACKF)) { + i2c->ICR |= I2C_ICR_NACKCF; + _stop(i2c); return -ENXIO; } - /* write data to data register */ - DEBUG("[i2c] write: Write byte %02X to DR\n", data[i]); - i2c->TXDR = data[i]; - DEBUG("[i2c] write: Sending data\n"); - - tick = TICK_TIMEOUT; - while (!(i2c->ISR & I2C_ISR_TC) && tick--) {} - if (!tick) { - return -ETIMEDOUT; - } - - int ret = _check_bus(i2c); - if (ret < 0) { - return ret; - } } - - DEBUG("[i2c] write: Waiting for write to complete\n"); - uint16_t tick = TICK_TIMEOUT; - while (!(i2c->ISR & I2C_ISR_TC) && tick--) {} - if (i2c->ISR & ERROR_FLAG || !tick) { - DEBUG("[i2c] write: write didn't complete\n"); - return -ENXIO; - } - return 0; } -static inline int _stop(I2C_TypeDef *i2c) +static int _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) { - return -EIO; - } - - /* send STOP condition */ + /* Send stop condition */ DEBUG("[i2c] stop: Generate stop condition\n"); i2c->CR2 |= I2C_CR2_STOP; /* Wait for the stop to complete */ - tick = TICK_TIMEOUT; + uint16_t tick = TICK_TIMEOUT; while ((i2c->CR2 & I2C_CR2_STOP) && tick--) {} if (!tick) { return -ETIMEDOUT; } - + DEBUG("[i2c] stop: Stop condition succeeded\n"); + if (_wait_for_bus(i2c) < 0) { + return -ETIMEDOUT; + } + DEBUG("[i2c] stop: Bus is free\n"); return 0; } -static inline int _check_bus(I2C_TypeDef *i2c) +static int _wait_isr_set(I2C_TypeDef *i2c, uint32_t mask, uint8_t flags) { - assert(i2c != NULL); + uint16_t tick = TICK_TIMEOUT; + while (tick--) { + uint32_t isr = i2c->ISR; - if (i2c->ISR & I2C_ISR_NACKF) { - DEBUG("[i2c] check_bus: NACK received\n"); - return -ENXIO; + if (isr & I2C_ISR_NACKF) { + DEBUG("[i2c] wait_isr_set: NACK received\n"); + + /* Some devices have a valid data nack, if indicated don't stop */ + if (!(flags & I2C_NOSTOP)) { + _stop(i2c); + } + i2c->ICR |= CLEAR_FLAG; + return -EIO; + } + if ((isr & I2C_ISR_ARLO) || (isr & I2C_ISR_BERR)) { + DEBUG("[i2c] wait_isr_set: Arbitration lost or bus error\n"); + _stop(i2c); + i2c->ICR |= CLEAR_FLAG; + return -EAGAIN; + } + if (isr & mask) { + DEBUG("[i2c] wait_isr_set: ISR 0x%08lX set\n", mask); + return 0; + } } + /* + * If timeout occurs this means a problem that must be handled on a higher + * level. A SWRST is recommended by the datasheet. + */ + return -ETIMEDOUT; +} - if (i2c->ISR & I2C_ISR_ARLO) { - DEBUG("[i2c] check_bus: arbitration lost\n"); - return -EAGAIN; +static inline int _wait_for_bus(I2C_TypeDef *i2c) +{ + uint16_t tick = TICK_TIMEOUT; + while (tick-- && (i2c->ISR & I2C_ISR_BUSY)) {} + if (!tick) { + return -ETIMEDOUT; } - - if (i2c->ISR & I2C_ISR_BERR) { - DEBUG("[i2c] check_bus: bus error\n"); - return -EIO; - } - return 0; }