X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=bertos%2Fcpu%2Favr%2Fdrv%2Ftwi_avr.c;fp=bertos%2Fcpu%2Favr%2Fdrv%2Ftwi_avr.c;h=bd4fcfd7bbe3d83a869c29ea8d827ec81359d8d2;hb=345f93de1963f49bdb194d2b06c8c5d7ba0a3e5f;hp=0000000000000000000000000000000000000000;hpb=791e167e053bdd9250d34a9a5ccae6ccde4d6679;p=bertos.git diff --git a/bertos/cpu/avr/drv/twi_avr.c b/bertos/cpu/avr/drv/twi_avr.c new file mode 100644 index 00000000..bd4fcfd7 --- /dev/null +++ b/bertos/cpu/avr/drv/twi_avr.c @@ -0,0 +1,266 @@ +/** + * \file + * + * + * \brief Driver for the AVR ATMega TWI (implementation) + * + * \version $Id$ + * + * \author Stefano Fedrigo + * \author Bernardo Innocenti + */ + +#include "twi.h" + +#include +#include +#include +#include // BV() +#include /* CLOCK_FREQ */ +#include + +#include + + +/* 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 twi_start(void) +{ + TWCR = BV(TWINT) | BV(TWSTA) | BV(TWEN); + WAIT_TWI_READY; + + if (TW_STATUS == TW_START || TW_STATUS == TW_REP_START) + return true; + + kprintf("!TW_(REP)START: %x\n", TWSR); + return false; +} + + +/** + * Send START condition and select slave for write. + * \c id is the device id comprehensive of address left shifted by 1. + * The LSB of \c id is ignored and reset to 0 for write operation. + * + * \return true on success, false otherwise. + */ +bool twi_start_w(uint8_t id) +{ + /* + * 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 eeprom responds with an ACK. + */ + while (twi_start()) + { + TWDR = id & ~READ_BIT; + TWCR = BV(TWINT) | BV(TWEN); + WAIT_TWI_READY; + + if (TW_STATUS == TW_MT_SLA_ACK) + return true; + else if (TW_STATUS != TW_MT_SLA_NACK) + { + kprintf("!TW_MT_SLA_(N)ACK: %x\n", TWSR); + break; + } + } + + return false; +} + + +/** + * Send START condition and select slave for read. + * \c id is the device id comprehensive of address left shifted by 1. + * The LSB of \c id is ignored and set to 1 for read operation. + * + * \return true on success, false otherwise. + */ +bool twi_start_r(uint8_t id) +{ + if (twi_start()) + { + TWDR = id | READ_BIT; + 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); + } + + return false; +} + + +/** + * Send STOP condition. + */ +void twi_stop(void) +{ + TWCR = BV(TWINT) | BV(TWEN) | BV(TWSTO); +} + + +/** + * Put a single byte in master transmitter mode + * to the selected slave device through the TWI bus. + * + * \return true on success, false on error. + */ +bool twi_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); + return false; + } + return true; +} + + +/** + * 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. + */ +bool twi_send(const void *_buf, size_t count) +{ + const uint8_t *buf = (const uint8_t *)_buf; + + while (count--) + { + if (!twi_put(*buf++)) + return false; + } + return true; +} + + +/** + * 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. + * + * \return true on success, false on error + */ +bool twi_recv(void *_buf, size_t count) +{ + uint8_t *buf = (uint8_t *)_buf; + + /* + * When reading the last byte the TWEA bit is not + * set, and the eeprom should answer with NACK + */ + while (count--) + { + TWCR = BV(TWINT) | BV(TWEN) | (count ? BV(TWEA) : 0); + WAIT_TWI_READY; + + if (count) + { + if (TW_STATUS != TW_MR_DATA_ACK) + { + kprintf("!TW_MR_DATA_ACK: %x\n", TWSR); + return false; + } + } + else + { + if (TW_STATUS != TW_MR_DATA_NACK) + { + kprintf("!TW_MR_DATA_NACK: %x\n", TWSR); + return false; + } + } + *buf++ = TWDR; + } + + return true; +} + + +/** + * Initialize TWI module. + */ +void twi_init(void) +{ + ATOMIC( + /* + * This is pretty useless according to AVR's datasheet, + * but it helps us driving the TWI data lines on boards + * where the bus pull-up resistors are missing. This is + * probably due to some unwanted interaction between the + * port pin and the TWI lines. + */ +#if CPU_AVR_ATMEGA64 || CPU_AVR_ATMEGA128 || CPU_AVR_ATMEGA1281 + PORTD |= BV(PD0) | BV(PD1); + DDRD |= BV(PD0) | BV(PD1); +#elif CPU_AVR_ATMEGA8 + PORTC |= BV(PC4) | BV(PC5); + DDRC |= BV(PC4) | BV(PC5); +#else + #error Unsupported architecture +#endif + + /* + * Set speed: + * F = CLOCK_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 */ + + TWBR = (CLOCK_FREQ / (2 * CONFIG_TWI_FREQ * TWI_PRESC)) - (8 / TWI_PRESC); + TWSR = 0; + TWCR = BV(TWEN); + ); +}