X-Git-Url: https://codewiz.org/gitweb?a=blobdiff_plain;f=cpu%2Favr%2Fdrv%2Ftwi_avr.c;fp=cpu%2Favr%2Fdrv%2Ftwi_avr.c;h=0d03950f95fe22d74e5763be804c0f0abd448ff5;hb=a93d7b6104de6de7cfdc16a3434593bbad87a601;hp=0000000000000000000000000000000000000000;hpb=1121818c76981217b8f9224a20cd084cc4f113a4;p=bertos.git diff --git a/cpu/avr/drv/twi_avr.c b/cpu/avr/drv/twi_avr.c new file mode 100644 index 00000000..0d03950f --- /dev/null +++ b/cpu/avr/drv/twi_avr.c @@ -0,0 +1,293 @@ +/** + * \file + * + * + * \brief Driver for the AVR ATMega TWI (implementation) + * + * \version $Id$ + * + * \author Stefano Fedrigo + * \author Bernardo Innocenti + */ + +/*#* + *#* $Log$ + *#* Revision 1.8 2007/06/07 14:35:12 batt + *#* Merge from project_ks. + *#* + *#* Revision 1.7 2006/07/19 12:56:26 bernie + *#* Convert to new Doxygen style. + *#* + *#* Revision 1.6 2006/03/20 17:49:50 bernie + *#* Make the TWI driver more generic to work with devices other than EEPROMS. + *#* + *#* Revision 1.5 2005/11/27 23:33:40 bernie + *#* Use appconfig.h instead of cfg/config.h. + *#* + *#* Revision 1.4 2005/04/11 19:10:28 bernie + *#* Include top-level headers from cfg/ subdir. + *#* + *#* Revision 1.3 2005/03/01 23:26:00 bernie + *#* Header fix. + *#* + *#* Revision 1.2 2005/01/25 08:36:56 bernie + *#* CONFIG_TWI_FREQ: New config param. + *#* + *#* Revision 1.1 2005/01/06 16:09:40 aleph + *#* Split twi/eeprom functions from eeprom module in separate twi module + *#* + *#*/ + +#include "twi.h" + +#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); + ); +}