cpu/atmega_common: adapt to new i2c api

Rework atmega i2c to match new i2c interface.
Only i2c_read_bytes and i2c_write_bytes are implemented.
This commit is contained in:
Laurent Navet 2018-05-30 21:34:22 +02:00 committed by dylad
parent 12e282d527
commit c7fed1526c
2 changed files with 151 additions and 175 deletions

View File

@ -48,6 +48,16 @@ extern "C"
{ {
#endif #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 * @brief global in-ISR state variable
*/ */

View File

@ -17,20 +17,21 @@
* @note This implementation only implements the 7-bit addressing mode. * @note This implementation only implements the 7-bit addressing mode.
* *
* @author Dimitri Nahm <dimitri.nahm@haw-hamburg.de> * @author Dimitri Nahm <dimitri.nahm@haw-hamburg.de>
* @author Laurent Navet <laurent.navet@gmail.com>
* *
* @} * @}
*/ */
#include <stdint.h> #include <stdint.h>
#include <errno.h>
#include "cpu.h" #include "cpu.h"
#include "mutex.h" #include "mutex.h"
#include "assert.h" #include "assert.h"
#include "periph/i2c.h" #include "periph/i2c.h"
#include "periph_conf.h" #include "periph_conf.h"
#include "debug.h"
#define ENABLE_DEBUG (0) #define ENABLE_DEBUG (0)
#include "debug.h"
#define MT_START 0x08 #define MT_START 0x08
#define MT_START_REPEATED 0x10 #define MT_START_REPEATED 0x10
@ -38,68 +39,81 @@
#define MT_DATA_ACK 0x28 #define MT_DATA_ACK 0x28
#define MR_ADDRESS_ACK 0x40 #define MR_ADDRESS_ACK 0x40
#define ATMEGA_I2C_FLAG_WRITE 0
#define ATMEGA_I2C_FLAG_READ 1
/* static function definitions */ /* static function definitions */
static int _start(uint8_t address, uint8_t rw_flag); static int _start(uint8_t address, uint8_t rw_flag);
static int _write(const uint8_t *data, int length); static int _write(const uint8_t *data, int length);
static void _stop(void); 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 */ /* TWI Bit Rate Register - division factor for the bit rate generator */
unsigned long twibrr; unsigned long twibrr;
/* TWI Prescaler Bits - default 0 */ /* TWI Prescaler Bits - default 0 */
uint8_t twipb = 0; uint8_t twipb = 0;
/* check if the line is valid */
if (dev >= I2C_NUMOF) {
return -1;
}
/* calculate speed configuration */ /* calculate speed configuration */
switch (speed) { switch (I2C_BUS_SPEED) {
case I2C_SPEED_LOW: case I2C_SPEED_LOW:
if ((CLOCK_CORECLOCK > 20000000UL) if ((CLOCK_CORECLOCK > 20000000UL)
|| (CLOCK_CORECLOCK < 1000000UL)) { || (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; twipb = 1;
break; break;
case I2C_SPEED_NORMAL: case I2C_SPEED_NORMAL:
if ((CLOCK_CORECLOCK > 50000000UL) if ((CLOCK_CORECLOCK > 50000000UL)
|| (CLOCK_CORECLOCK < 2000000UL)) { || (CLOCK_CORECLOCK < 2000000UL)) {
return -2; DEBUG("[i2c] init: bus speed incompatible with core clock\n");
return;
} }
twibrr = ((CLOCK_CORECLOCK / 100000UL) - 16) / 2; twibrr = ((CLOCK_CORECLOCK / 100000UL) - 16) / 2;
break; break;
case I2C_SPEED_FAST: case I2C_SPEED_FAST:
if (CLOCK_CORECLOCK < 7500000UL) { if (CLOCK_CORECLOCK < 7500000UL) {
return -2; DEBUG("[i2c] init: bus speed incompatible with core clock\n");
return;
} }
twibrr = ((CLOCK_CORECLOCK / 400000UL) - 16) / 2; twibrr = ((CLOCK_CORECLOCK / 400000UL) - 16) / 2;
break; break;
case I2C_SPEED_FAST_PLUS: case I2C_SPEED_FAST_PLUS:
if (CLOCK_CORECLOCK < 18000000UL) { if (CLOCK_CORECLOCK < 18000000UL) {
return -2; DEBUG("[i2c] init: bus speed incompatible with core clock\n");
return;
} }
twibrr = ((CLOCK_CORECLOCK / 1000000UL) - 16) / 2; twibrr = ((CLOCK_CORECLOCK / 1000000UL) - 16) / 2;
break; break;
case I2C_SPEED_HIGH: case I2C_SPEED_HIGH:
if (CLOCK_CORECLOCK < 62000000UL) { if (CLOCK_CORECLOCK < 62000000UL) {
return -2; DEBUG("[i2c] init: bus speed incompatible with core clock\n");
return;
} }
twibrr = ((CLOCK_CORECLOCK / 3400000UL) - 16) / 2; twibrr = ((CLOCK_CORECLOCK / 3400000UL) - 16) / 2;
break; break;
default: default:
return -2; DEBUG("[i2c] init: invalid bus speed\n");
return;
} }
/* set pull-up on SCL and SDA */ /* 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 */ TWSR |= twipb; /* Set TWI Prescaler Bits */
/* enable device */ /* enable device */
TWCR |= (1 << TWEN); 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; return 0;
} }
int i2c_acquire(i2c_t dev) int i2c_acquire(i2c_t dev)
{ {
if (!(dev < I2C_NUMOF)) { assert(dev < I2C_NUMOF);
return -1;
} mutex_lock(&locks[dev]);
mutex_lock(&lock);
return 0; return 0;
} }
int i2c_release(i2c_t dev) int i2c_release(i2c_t dev)
{ {
if (!(dev < I2C_NUMOF)) { assert(dev < I2C_NUMOF);
return -1;
} mutex_unlock(&locks[dev]);
mutex_unlock(&lock);
return 0; return 0;
} }
int i2c_read_byte(i2c_t dev, uint8_t address, void *data) static void i2c_poweron(i2c_t dev)
{
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(&reg, 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(&reg, 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)
{ {
assert(dev < I2C_NUMOF); assert(dev < I2C_NUMOF);
(void) dev; (void) dev;
power_twi_enable(); 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) static int _start(uint8_t address, uint8_t rw_flag)
{ {
/* Reset I2C Interrupt Flag and transmit START condition */ /* Reset I2C Interrupt Flag and transmit START condition */
TWCR = (1 << TWINT) | (1 << TWSTA) | (1 << TWEN); 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 /* Wait for TWINT Flag set. This indicates that the START has been
* transmitted, and ACK/NACK has been received.*/ * 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. /* Check value of TWI Status Register. Mask prescaler bits.
* If status different from START go to ERROR */ * If status different from START go to ERROR */
if ((TWSR & 0xF8) == MT_START) { 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) { else if ((TWSR & 0xF8) == MT_START_REPEATED) {
DEBUG("I2C Status is: START REPEATED\n"); DEBUG("[i2c] start: I2C Status is: START REPEATED\n");
} }
else { 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(); _stop();
return -1; 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 */ * Clear TWINT bit in TWCR to start transmission of ADDRESS */
TWDR = (address << 1) | rw_flag; TWDR = (address << 1) | rw_flag;
TWCR = (1 << TWINT) | (1 << TWEN); 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.*/ /* Wait for TWINT Flag set. This indicates that ADDRESS has been transmitted.*/
while (!(TWCR & (1 << TWINT))) {} 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. /* Check value of TWI Status Register. Mask prescaler bits.
* If status different from ADDRESS ACK go to ERROR */ * If status different from ADDRESS ACK go to ERROR */
if ((TWSR & 0xF8) == MT_ADDRESS_ACK) { 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) { 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 { else {
DEBUG("NOT ACK has been received for ADDRESS\n"); DEBUG("[i2c] start: NACK has been received for ADDRESS %02X \n",
address);
_stop(); _stop();
return -2; return -2;
} }
@ -331,6 +296,7 @@ static int _start(uint8_t address, uint8_t rw_flag)
return 0; return 0;
} }
/* TODO : const uint8_t data instead of *data */
static int _write(const uint8_t *data, int length) static int _write(const uint8_t *data, int length)
{ {
for (int i = 0; i < length; i++) { 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 */ * Clear TWINT bit in TWCR to start transmission of data */
TWDR = data[i]; TWDR = data[i];
TWCR = (1 << TWINT) | (1 << TWEN); 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.*/ /* Wait for TWINT Flag set. This indicates that DATA has been transmitted.*/
while (!(TWCR & (1 << TWINT))) {} 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 /* Check value of TWI Status Register. Mask prescaler bits. If status
* different from MT_DATA_ACK, return number of transmitted bytes */ * different from MT_DATA_ACK, return number of transmitted bytes */
if ((TWSR & 0xF8) != MT_DATA_ACK) { if ((TWSR & 0xF8) != MT_DATA_ACK) {
DEBUG("NACK has been received for BYTE %i\n", i+1); DEBUG("[i2c] write: NACK has been received\n");
return i; return -1;
} }
else { 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) static void _stop(void)
@ -363,6 +329,6 @@ static void _stop(void)
TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN); TWCR = (1 << TWINT) | (1 << TWSTO) | (1 << TWEN);
/* Wait for STOP Flag reset. This indicates that STOP has been transmitted.*/ /* Wait for STOP Flag reset. This indicates that STOP has been transmitted.*/
while (TWCR & (1 << TWSTO)) {} while (TWCR & (1 << TWSTO)) {}
DEBUG("STOP condition transmitted\n"); DEBUG("[i2c] stop: STOP condition transmitted\n");
TWCR = 0; TWCR = 0;
} }