X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Fcpu%2Favr%2Fdrv%2Fi2c_avr.c;h=64d77786671c6e95cdb3b4114ffe45cc052efcc0;hb=0f60d6b24cf8ec1c9bd8dca82fed6325932c5aca;hp=80b97ad645b8ee63fe7b3f565baef06c0e92a3be;hpb=1d0283ddb3ec3cf273e03207e7c6066897771e5c;p=bertos.git diff --git a/bertos/cpu/avr/drv/i2c_avr.c b/bertos/cpu/avr/drv/i2c_avr.c index 80b97ad6..64d77786 100644 --- a/bertos/cpu/avr/drv/i2c_avr.c +++ b/bertos/cpu/avr/drv/i2c_avr.c @@ -32,17 +32,21 @@ * * \brief Driver for the AVR ATMega TWI (implementation) * - * \version $Id$ - * * \author Stefano Fedrigo * \author Bernie Innocenti + * \author Daniele Basile */ -#include "i2c_avr.h" - -#include "hw/hw_cpu.h" /* CLOCK_FREQ */ #include "cfg/cfg_i2c.h" + +#include /* CPU_FREQ */ + +#define LOG_LEVEL I2C_LOG_LEVEL +#define LOG_FORMAT I2C_LOG_FORMAT + +#include + #include #include // BV() #include @@ -50,22 +54,23 @@ #include #include #include +#include + +#include #include +#if !CONFIG_I2C_DISABLE_OLD_API /* Wait for TWINT flag set: bus is ready */ #define WAIT_TWI_READY do {} while (!(TWCR & BV(TWINT))) -#define READ_BIT BV(0) - - /** * Send START condition on the bus. * * \return true on success, false otherwise. */ -static bool i2c_start(void) +static bool i2c_builtin_start(void) { TWCR = BV(TWINT) | BV(TWSTA) | BV(TWEN); WAIT_TWI_READY; @@ -73,7 +78,7 @@ static bool i2c_start(void) if (TW_STATUS == TW_START || TW_STATUS == TW_REP_START) return true; - kprintf("!TW_(REP)START: %x\n", TWSR); + LOG_ERR("!TW_(REP)START: %x\n", TWSR); return false; } @@ -85,7 +90,7 @@ static bool i2c_start(void) * * \return true on success, false otherwise. */ -bool i2c_start_w(uint8_t id) +bool i2c_builtin_start_w(uint8_t id) { /* * Loop on the select write sequence: when the eeprom is busy @@ -94,9 +99,9 @@ bool i2c_start_w(uint8_t id) * keep trying until the eeprom responds with an ACK. */ ticks_t start = timer_clock(); - while (i2c_start()) + while (i2c_builtin_start()) { - TWDR = id & ~READ_BIT; + TWDR = id & ~I2C_READBIT; TWCR = BV(TWINT) | BV(TWEN); WAIT_TWI_READY; @@ -104,12 +109,12 @@ bool i2c_start_w(uint8_t id) return true; else if (TW_STATUS != TW_MT_SLA_NACK) { - kprintf("!TW_MT_SLA_(N)ACK: %x\n", TWSR); + LOG_ERR("!TW_MT_SLA_(N)ACK: %x\n", TWSR); break; } - else if (timer_clock() - start > ms_to_ticks(CONFIG_TWI_START_TIMEOUT)) + else if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT)) { - kprintf("Timeout on TWI_MT_START\n"); + LOG_ERR("Timeout on TWI_MT_START\n"); break; } } @@ -125,18 +130,18 @@ bool i2c_start_w(uint8_t id) * * \return true on success, false otherwise. */ -bool i2c_start_r(uint8_t id) +bool i2c_builtin_start_r(uint8_t id) { - if (i2c_start()) + if (i2c_builtin_start()) { - TWDR = id | READ_BIT; + TWDR = id | I2C_READBIT; TWCR = BV(TWINT) | BV(TWEN); WAIT_TWI_READY; if (TW_STATUS == TW_MR_SLA_ACK) return true; - kprintf("!TW_MR_SLA_ACK: %x\n", TWSR); + LOG_ERR("!TW_MR_SLA_ACK: %x\n", TWSR); } return false; @@ -146,9 +151,9 @@ bool i2c_start_r(uint8_t id) /** * Send STOP condition. */ -void i2c_stop(void) +void i2c_builtin_stop(void) { - TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO); + TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO); } @@ -158,14 +163,14 @@ void i2c_stop(void) * * \return true on success, false on error. */ -bool i2c_put(const uint8_t data) +bool i2c_builtin_put(const uint8_t data) { TWDR = data; TWCR = BV(TWINT) | BV(TWEN); WAIT_TWI_READY; if (TW_STATUS != TW_MT_DATA_ACK) { - kprintf("!TW_MT_DATA_ACK: %x\n", TWSR); + LOG_ERR("!TW_MT_DATA_ACK: %x\n", TWSR); return false; } return true; @@ -179,7 +184,7 @@ bool i2c_put(const uint8_t data) * * \return the byte read if ok, EOF on errors. */ -int i2c_get(bool ack) +int i2c_builtin_get(bool ack) { TWCR = BV(TWINT) | BV(TWEN) | (ack ? BV(TWEA) : 0); WAIT_TWI_READY; @@ -188,7 +193,7 @@ int i2c_get(bool ack) { if (TW_STATUS != TW_MR_DATA_ACK) { - kprintf("!TW_MR_DATA_ACK: %x\n", TWSR); + LOG_ERR("!TW_MR_DATA_ACK: %x\n", TWSR); return EOF; } } @@ -196,7 +201,7 @@ int i2c_get(bool ack) { if (TW_STATUS != TW_MR_DATA_NACK) { - kprintf("!TW_MR_DATA_NACK: %x\n", TWSR); + LOG_ERR("!TW_MR_DATA_NACK: %x\n", TWSR); return EOF; } } @@ -205,71 +210,172 @@ int i2c_get(bool ack) return (int)(uint8_t)TWDR; } +#endif /* !CONFIG_I2C_DISABLE_OLD_API */ + +/* + * New Api + */ +struct I2cHardware +{ +}; + + +/* Wait for TWINT flag set: bus is ready */ +#define WAIT_READY() \ + do { \ + while (!(TWCR & BV(TWINT))) \ + cpu_relax(); \ + } while (0) /** - * Send a sequence of bytes in master transmitter mode - * to the selected slave device through the TWI bus. - * - * \return true on success, false on error. + * Send START condition on the bus. */ -bool i2c_send(const void *_buf, size_t count) +INLINE bool i2c_hw_start(void) { - const uint8_t *buf = (const uint8_t *)_buf; + TWCR = BV(TWINT) | BV(TWSTA) | BV(TWEN); + WAIT_READY(); - while (count--) - { - if (!i2c_put(*buf++)) - return false; - } - return true; -} + if (TW_STATUS == TW_START || TW_STATUS == TW_REP_START) + return true; + return false; +} /** - * Receive a sequence of one or more bytes from the - * selected slave device in master receive mode through - * the TWI bus. - * - * Received data is placed in \c buf. - * - * \note a NACK is automatically given on the last received - * byte. - * - * \return true on success, false on error + * Send STOP condition. */ -bool i2c_recv(void *_buf, size_t count) +INLINE void i2c_hw_stop(void) { - uint8_t *buf = (uint8_t *)_buf; + TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO); +} +static void i2c_avr_start(I2c *i2c, uint16_t slave_addr) +{ /* - * When reading the last byte the TWEA bit is not - * set, and the eeprom should answer with NACK + * Loop on the select write sequence: when the eeprom is busy + * writing previously sent data it will reply to the SLA_W + * control byte with a NACK. In this case, we must + * keep trying until the slave responds with an ACK. */ - while (count--) + ticks_t start = timer_clock(); + while (i2c_hw_start()) { - /* - * The last byte read does not has an ACK - * to stop communication. - */ - int c = i2c_get(count); - - if (c == EOF) - return false; + uint8_t sla_ack = 0; + uint8_t sla_nack = 0; + if (I2C_TEST_START(i2c->flags) == I2C_START_W) + { + TWDR = slave_addr & ~I2C_READBIT; + sla_ack = TW_MT_SLA_ACK; + sla_nack = TW_MT_SLA_NACK; + } else - *buf++ = c; + { + TWDR = slave_addr | I2C_READBIT; + sla_ack = TW_MR_SLA_ACK; + sla_nack = TW_MR_SLA_NACK; + } + + TWCR = BV(TWINT) | BV(TWEN); + WAIT_READY(); + + if (TW_STATUS == sla_ack) + return; + else if (TW_STATUS != sla_nack) + { + LOG_ERR("Start addr NACK[%x]\n", TWSR); + i2c->errors |= I2C_NO_ACK; + i2c_hw_stop(); + break; + } + else if (timer_clock() - start > ms_to_ticks(CONFIG_I2C_START_TIMEOUT)) + { + LOG_ERR("Start timeout\n"); + i2c->errors |= I2C_START_TIMEOUT; + i2c_hw_stop(); + break; + } } - return true; + LOG_ERR("I2c error\n"); + i2c->errors |= I2C_ERR; + i2c_hw_stop(); } +static void i2c_avr_putc(I2c *i2c, const uint8_t data) +{ -MOD_DEFINE(i2c); + TWDR = data; + TWCR = BV(TWINT) | BV(TWEN); + WAIT_READY(); + + if (TW_STATUS != TW_MT_DATA_ACK) + { + LOG_ERR("Data nack[%x]\n", TWSR); + i2c->errors |= I2C_DATA_NACK; + i2c_hw_stop(); + } + + if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP)) + i2c_hw_stop(); +} + +static uint8_t i2c_avr_getc(I2c *i2c) +{ + uint8_t data_flag = 0; + if (i2c->xfer_size == 1) + { + TWCR = BV(TWINT) | BV(TWEN); + data_flag = TW_MR_DATA_NACK; + } + else + { + TWCR = BV(TWINT) | BV(TWEN) | BV(TWEA); + data_flag = TW_MR_DATA_ACK; + } + + WAIT_READY(); + + if (TW_STATUS != data_flag) + { + LOG_ERR("Data nack[%x]\n", TWSR); + i2c->errors |= I2C_DATA_NACK; + i2c_hw_stop(); + + return 0xFF; + } + + uint8_t data = TWDR; + + if ((i2c->xfer_size == 1) && (I2C_TEST_STOP(i2c->flags) == I2C_STOP)) + i2c_hw_stop(); + + return data; +} + + +static const I2cVT i2c_avr_vt = +{ + .start = i2c_avr_start, + .getc = i2c_avr_getc, + .putc = i2c_avr_putc, + .write = i2c_genericWrite, + .read = i2c_genericRead, +}; + +struct I2cHardware i2c_avr_hw[] = +{ + { /* I2C0 */ + }, +}; /** - * Initialize TWI module. + * Initialize I2C module. */ -void i2c_init(void) +void i2c_hw_init(I2c *i2c, int dev, uint32_t clock) { + i2c->hw = &i2c_avr_hw[dev]; + i2c->vt = &i2c_avr_vt; + ATOMIC( /* * This is pretty useless according to AVR's datasheet, @@ -278,32 +384,28 @@ void i2c_init(void) * probably due to some unwanted interaction between the * port pin and the TWI lines. */ -#if CPU_AVR_ATMEGA64 || CPU_AVR_ATMEGA128 || CPU_AVR_ATMEGA1281 + #if CPU_AVR_ATMEGA64 || CPU_AVR_ATMEGA128 || CPU_AVR_ATMEGA1281 PORTD |= BV(PD0) | BV(PD1); DDRD |= BV(PD0) | BV(PD1); -#elif CPU_AVR_ATMEGA8 + #elif CPU_AVR_ATMEGA8 PORTC |= BV(PC4) | BV(PC5); DDRC |= BV(PC4) | BV(PC5); -#elif CPU_AVR_ATMEGA32 + #elif CPU_AVR_ATMEGA32 PORTC |= BV(PC1) | BV(PC0); DDRC |= BV(PC1) | BV(PC0); -#else + #else #error Unsupported architecture -#endif + #endif /* * Set speed: - * F = CLOCK_FREQ / (16 + 2*TWBR * 4^TWPS) + * F = CPU_FREQ / (16 + 2*TWBR * 4^TWPS) */ - #ifndef CONFIG_TWI_FREQ - #warning Using default value of 300000L for CONFIG_TWI_FREQ - #define CONFIG_TWI_FREQ 300000L /* ~300 kHz */ - #endif - #define TWI_PRESC 1 /* 4 ^ TWPS */ + ASSERT(clock); + #define TWI_PRESC 1 /* 4 ^ TWPS */ - TWBR = (CLOCK_FREQ / (2 * CONFIG_TWI_FREQ * TWI_PRESC)) - (8 / TWI_PRESC); + TWBR = (CPU_FREQ / (2 * clock * TWI_PRESC)) - (8 / TWI_PRESC); TWSR = 0; TWCR = BV(TWEN); ); - MOD_INIT(i2c); }