diff --git a/cpu/atmega_common/include/cpu.h b/cpu/atmega_common/include/cpu.h index cfb476b253..d20d103090 100644 --- a/cpu/atmega_common/include/cpu.h +++ b/cpu/atmega_common/include/cpu.h @@ -48,6 +48,16 @@ extern "C" { #endif +/** + * @name Use shared I2C functions + * @{ + */ +#define PERIPH_I2C_NEED_READ_REG +#define PERIPH_I2C_NEED_WRITE_REG +#define PERIPH_I2C_NEED_READ_REGS +#define PERIPH_I2C_NEED_WRITE_REGS +/** @} */ + /** * @brief global in-ISR state variable */ diff --git a/cpu/atmega_common/periph/i2c.c b/cpu/atmega_common/periph/i2c.c index 809a9e5d43..7e9b6c9aeb 100644 --- a/cpu/atmega_common/periph/i2c.c +++ b/cpu/atmega_common/periph/i2c.c @@ -17,20 +17,21 @@ * @note This implementation only implements the 7-bit addressing mode. * * @author Dimitri Nahm + * @author Laurent Navet * * @} */ #include - +#include #include "cpu.h" #include "mutex.h" #include "assert.h" #include "periph/i2c.h" #include "periph_conf.h" -#include "debug.h" #define ENABLE_DEBUG (0) +#include "debug.h" #define MT_START 0x08 #define MT_START_REPEATED 0x10 @@ -38,68 +39,81 @@ #define MT_DATA_ACK 0x28 #define MR_ADDRESS_ACK 0x40 +#define ATMEGA_I2C_FLAG_WRITE 0 +#define ATMEGA_I2C_FLAG_READ 1 + + /* static function definitions */ static int _start(uint8_t address, uint8_t rw_flag); static int _write(const uint8_t *data, int length); static void _stop(void); +static void i2c_poweron(i2c_t dev); -static mutex_t lock = MUTEX_INIT; +static mutex_t locks[I2C_NUMOF]; -int i2c_init_master(i2c_t dev, i2c_speed_t speed) +/* TODO : 10 bits addresses */ + +void i2c_init(i2c_t dev) { + /* check if the line is valid */ + assert(dev < I2C_NUMOF); + + mutex_init(&locks[dev]); + /* TWI Bit Rate Register - division factor for the bit rate generator */ unsigned long twibrr; /* TWI Prescaler Bits - default 0 */ uint8_t twipb = 0; - /* check if the line is valid */ - if (dev >= I2C_NUMOF) { - return -1; - } - /* calculate speed configuration */ - switch (speed) { + switch (I2C_BUS_SPEED) { case I2C_SPEED_LOW: if ((CLOCK_CORECLOCK > 20000000UL) || (CLOCK_CORECLOCK < 1000000UL)) { - return -2; + DEBUG("[i2c] init: bus speed incompatible with core clock\n"); + return; } - twibrr = ((CLOCK_CORECLOCK / 10000UL) - 16) / (2 * 4); /* CLK Prescaler 4 */ + twibrr = ((CLOCK_CORECLOCK / 10000UL) - 16) / (2 * 4); /* CLK Prescaler 4 */ twipb = 1; break; case I2C_SPEED_NORMAL: if ((CLOCK_CORECLOCK > 50000000UL) || (CLOCK_CORECLOCK < 2000000UL)) { - return -2; + DEBUG("[i2c] init: bus speed incompatible with core clock\n"); + return; } twibrr = ((CLOCK_CORECLOCK / 100000UL) - 16) / 2; break; case I2C_SPEED_FAST: if (CLOCK_CORECLOCK < 7500000UL) { - return -2; + DEBUG("[i2c] init: bus speed incompatible with core clock\n"); + return; } twibrr = ((CLOCK_CORECLOCK / 400000UL) - 16) / 2; break; case I2C_SPEED_FAST_PLUS: if (CLOCK_CORECLOCK < 18000000UL) { - return -2; + DEBUG("[i2c] init: bus speed incompatible with core clock\n"); + return; } twibrr = ((CLOCK_CORECLOCK / 1000000UL) - 16) / 2; break; case I2C_SPEED_HIGH: if (CLOCK_CORECLOCK < 62000000UL) { - return -2; + DEBUG("[i2c] init: bus speed incompatible with core clock\n"); + return; } twibrr = ((CLOCK_CORECLOCK / 3400000UL) - 16) / 2; break; default: - return -2; + DEBUG("[i2c] init: invalid bus speed\n"); + return; } /* set pull-up on SCL and SDA */ @@ -116,175 +130,122 @@ int i2c_init_master(i2c_t dev, i2c_speed_t speed) TWSR |= twipb; /* Set TWI Prescaler Bits */ /* enable device */ TWCR |= (1 << TWEN); +} + +int i2c_read_bytes(i2c_t dev, uint16_t addr, void *data, size_t len, + uint8_t flags) +{ + assert(dev < I2C_NUMOF); + + /* Check for unsupported operations */ + if (flags & I2C_ADDR10) { + return -EOPNOTSUPP; + } + /* Check for wrong arguments given */ + if (data == NULL || len == 0) { + return -EINVAL; + } + + uint8_t *my_data = data; + + /* send start condition and slave address */ + if (!(flags & I2C_NOSTART)) { + if (_start(addr, ATMEGA_I2C_FLAG_READ) < 0) { + return -ENXIO; + } + } + + for (size_t i = 0; i < len; i++) { + /* Send NACK for last received byte */ + if ((len - i) == 1) { + TWCR = (1 << TWEN) | (1 << TWINT); + } + else { + TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWINT); + } + DEBUG("[i2c] i2c_read_bytes: Wait for byte\n"); + + /* Wait for TWINT Flag set. This indicates that DATA has been received.*/ + while (!(TWCR & (1 << TWINT))) {} + + /* receive data byte */ + my_data[i] = TWDR; + DEBUG("[i2c] i2c_read_bytes: Byte received\n"); + } + + /* end transmission */ + if (!(flags & I2C_NOSTOP)) { + _stop(); + } + + return 0; +} + +int i2c_write_bytes(i2c_t dev, uint16_t addr, const void *data, size_t len, + uint8_t flags) +{ + assert(dev < I2C_NUMOF); + + /* Check for unsupported operations */ + if (flags & I2C_ADDR10) { + return -EOPNOTSUPP; + } + /* Check for wrong arguments given */ + if (data == NULL || len == 0) { + return -EINVAL; + } + + /* start transmission and send slave address */ + if (!(flags & I2C_NOSTART)) { + if (_start(addr, ATMEGA_I2C_FLAG_WRITE) < 0) { + DEBUG("[i2c] i2c_write_bytes: start failed\n"); + return -ENXIO; + } + } + + /* send out data bytes */ + if (_write(data, len) < 0) { + DEBUG("[i2c] i2c_write_bytes: write failed\n"); + return -EIO; + } + + /* end transmission */ + if (!(flags & I2C_NOSTOP)) { + DEBUG("[i2c] i2c_write_bytes: sending stop\n"); + _stop(); + } return 0; } int i2c_acquire(i2c_t dev) { - if (!(dev < I2C_NUMOF)) { - return -1; - } - mutex_lock(&lock); + assert(dev < I2C_NUMOF); + + mutex_lock(&locks[dev]); return 0; } int i2c_release(i2c_t dev) { - if (!(dev < I2C_NUMOF)) { - return -1; - } - mutex_unlock(&lock); + assert(dev < I2C_NUMOF); + + 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) -{ - assert(length > 0); - - if (!(dev < I2C_NUMOF)) { - return -1; - } - - uint8_t *my_data = data; - /* send start condition and slave address */ - if (_start(address, I2C_FLAG_READ) != 0) { - return 0; - } - - for (int i = 0; i < length; i++) { - /* Send NACK for last received byte */ - if ((length - i) == 1) { - TWCR = (1 << TWEN) | (1 << TWINT); - } - else { - TWCR = (1 << TWEA) | (1 << TWEN) | (1 << TWINT); - } - DEBUG("Wait for byte %i\n", i+1); - /* Wait for TWINT Flag set. This indicates that DATA has been received.*/ - while (!(TWCR & (1 << TWINT))) {} - /* receive data byte */ - my_data[i] = TWDR; - DEBUG("Byte %i received\n", i+1); - } - - /* end transmission */ - _stop(); - - 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) -{ - assert(length > 0); - - if (!(dev < I2C_NUMOF)) { - return -1; - } - - /* start transmission and send slave address */ - if (_start(address, I2C_FLAG_WRITE) != 0) { - return 0; - } - - /* send register address and wait for complete transfer to be finished*/ - if (_write(®, 1) != 1) { - _stop(); - return 0; - } - - /* now start a new start condition and receive data */ - 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) -{ - int bytes = 0; - - assert(length > 0); - - if (!(dev < I2C_NUMOF)) { - return -1; - } - - /* start transmission and send slave address */ - if (_start(address, I2C_FLAG_WRITE) != 0) { - return 0; - } - - /* send out data bytes */ - bytes = _write(data, length); - - /* end transmission */ - _stop(); - - return bytes; -} - -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) -{ - int bytes = 0; - - assert(length > 0); - - if (!(dev < I2C_NUMOF)) { - return -1; - } - - /* start transmission and send slave address */ - if (_start(address, I2C_FLAG_WRITE) != 0) { - return 0; - } - /* send register address and wait for complete transfer to be finished*/ - if (_write(®, 1)) { - /* write data to register */ - bytes = _write(data, length); - } - /* finish transfer */ - _stop(); - /* return number of bytes send */ - return bytes; -} - -void i2c_poweron(i2c_t dev) +static void i2c_poweron(i2c_t dev) { assert(dev < I2C_NUMOF); (void) dev; power_twi_enable(); } -void i2c_poweroff(i2c_t dev) -{ - assert(dev < I2C_NUMOF); - (void) dev; - power_twi_disable(); -} - static int _start(uint8_t address, uint8_t rw_flag) { /* Reset I2C Interrupt Flag and transmit START condition */ TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); - DEBUG("START condition transmitted\n"); + DEBUG("[i2c] start: START condition transmitted\n"); /* Wait for TWINT Flag set. This indicates that the START has been * transmitted, and ACK/NACK has been received.*/ @@ -293,13 +254,14 @@ static int _start(uint8_t address, uint8_t rw_flag) /* Check value of TWI Status Register. Mask prescaler bits. * If status different from START go to ERROR */ if ((TWSR & 0xF8) == MT_START) { - DEBUG("I2C Status is: START\n"); + DEBUG("[i2c] start: I2C Status is: START\n"); } else if ((TWSR & 0xF8) == MT_START_REPEATED) { - DEBUG("I2C Status is: START REPEATED\n"); + DEBUG("[i2c] start: I2C Status is: START REPEATED\n"); } else { - DEBUG("I2C Status Register is different from START/START_REPEATED\n"); + DEBUG("[i2c] start: I2C Status Register is different from " + "START/START_REPEATED\n"); _stop(); return -1; } @@ -309,7 +271,7 @@ static int _start(uint8_t address, uint8_t rw_flag) * Clear TWINT bit in TWCR to start transmission of ADDRESS */ TWDR = (address << 1) | rw_flag; TWCR = (1 << TWINT) | (1 << TWEN); - DEBUG("ADDRESS and FLAG transmitted\n"); + DEBUG("[i2c] start: ADDRESS and FLAG transmitted\n"); /* Wait for TWINT Flag set. This indicates that ADDRESS has been transmitted.*/ while (!(TWCR & (1 << TWINT))) {} @@ -317,13 +279,16 @@ static int _start(uint8_t address, uint8_t rw_flag) /* Check value of TWI Status Register. Mask prescaler bits. * If status different from ADDRESS ACK go to ERROR */ if ((TWSR & 0xF8) == MT_ADDRESS_ACK) { - DEBUG("ACK has been received for ADDRESS (write)\n"); + DEBUG("[i2c] start: ACK has been received for ADDRESS %02X (write)\n", + address); } else if ((TWSR & 0xF8) == MR_ADDRESS_ACK) { - DEBUG("ACK has been received for ADDRESS (read)\n"); + DEBUG("[i2c] start: ACK has been received for ADDRESS %02X (read)\n", + address); } else { - DEBUG("NOT ACK has been received for ADDRESS\n"); + DEBUG("[i2c] start: NACK has been received for ADDRESS %02X \n", + address); _stop(); return -2; } @@ -331,6 +296,7 @@ static int _start(uint8_t address, uint8_t rw_flag) return 0; } +/* TODO : const uint8_t data instead of *data */ static int _write(const uint8_t *data, int length) { for (int i = 0; i < length; i++) { @@ -338,7 +304,7 @@ static int _write(const uint8_t *data, int length) * Clear TWINT bit in TWCR to start transmission of data */ TWDR = data[i]; TWCR = (1 << TWINT) | (1 << TWEN); - DEBUG("Byte %i transmitted\n", i+1); + DEBUG("[i2c] write: Byte transmitted\n"); /* Wait for TWINT Flag set. This indicates that DATA has been transmitted.*/ while (!(TWCR & (1 << TWINT))) {} @@ -346,15 +312,15 @@ static int _write(const uint8_t *data, int length) /* Check value of TWI Status Register. Mask prescaler bits. If status * different from MT_DATA_ACK, return number of transmitted bytes */ if ((TWSR & 0xF8) != MT_DATA_ACK) { - DEBUG("NACK has been received for BYTE %i\n", i+1); - return i; + DEBUG("[i2c] write: NACK has been received\n"); + return -1; } else { - DEBUG("ACK has been received for BYTE %i\n", i+1); + DEBUG("[i2c] write: ACK has been received\n"); } } - return length; + return 0; } static void _stop(void) @@ -363,6 +329,6 @@ static void _stop(void) TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN); /* Wait for STOP Flag reset. This indicates that STOP has been transmitted.*/ while (TWCR & (1 << TWSTO)) {} - DEBUG("STOP condition transmitted\n"); + DEBUG("[i2c] stop: STOP condition transmitted\n"); TWCR = 0; }