// // Module Name: I2C.c // // Description: This module contains the low and high level I2C access routine for the Atmel TWI (I2C) interface (on the mega128). // // History: // - Created on May 1st 2002 by Stephane Gauthier (email: stephane.gauthier@alcatel.com URL:http://robots.dnsq.org) // - May 21st 2002 - S. Gauthier - Modified i2c_readbyte() and i2c_writebyte() to exit cleanly (run i2c_stop, set mutex and re-enable ints). // - June 2nd 2002 - S. Gauthier - Cleaned up and added I2C error count var for troubleshooting. // - June 25th 2002 - S. Gauthier - Added _SFR_ASM_COMPAT define to prevent warnings with new AVRGCC compiler (3.2) // #include // For I/O macros #include // AVRGCC TWI include. #include "I2C.h" // For our own routines #include "avrx.h" // For semaphores #include "generalio.h" // for PrintString #include "serialio.h" // For error reporting. (PutChar()) #include "hardware.h" // sets up hardware specifics defines, macros and constants. #define I2C_ERROR 1 unsigned int i2c_errors; unsigned int i2c_requests; // Globals AVRX_MUTEX(I2C_Mutex); // For controlling access to I2C hardware. // ************************** // Low-level I2C routines. // ************************** // Function Name: i2c_init() // // Description: Initializes the I2C hardware // // void i2c_init(void) { // Prime I2C semaphore AvrXSetSemaphore(&I2C_Mutex); // Configure Bit rate generator for 100Khz TWBR = TWBR_INIT; // Prescaler of 1 TWSR = TWPS_INIT; // Enable TWI TWCR = TWCR_INIT; } // Function Name: i2c_start() // // Description: Prepare to take over bus. Return 0 if successfull, otherwise 1. // // unsigned char i2c_start(unsigned char data) { // Initiate Start outp((BV(TWINT) | BV(TWSTA) | BV(TWEN)), TWCR); // Wait 'till it's done... while (bit_is_clear(TWCR, TWINT)) ; // check for proper status in TWSR.. if ((inp(TWSR) & 0xF8) != TW_START) { return (1); // BAD!! } // Send SLA_W outp(data, TWDR); // start transmission outp((BV(TWINT) | BV(TWEN)), TWCR); // Wait 'till it's done... while (bit_is_clear(TWCR, TWINT)) ; // check for proper status in TWSR.. if ((inp(TWSR) & 0xF8) != TW_MT_SLA_ACK) { return (1); // BAD!! } // Success return (0); } // Function Name: i2c_rep_start() // // Description: Repeated Start. Switch mode without losing bus. Return 0 if successfull, otherwise 1. // // unsigned char i2c_rep_start(unsigned char data) { // Initiate Start outp((BV(TWINT) | BV(TWSTA) | BV(TWEN)), TWCR); // Wait 'till it's done... while (bit_is_clear(TWCR, TWINT)) ; // check for proper status in TWSR.. if ((inp(TWSR) & 0xF8) != TW_REP_START) { return (1); // BAD!! } // Send SLA_W or SLA_R outp(data, TWDR); // start transmission outp((BV(TWINT) | BV(TWEN)), TWCR); // Wait 'till it's done... while (bit_is_clear(TWCR, TWINT)) ; // check for proper status in TWSR.. if ((inp(TWSR) & 0xF8) != TW_MR_SLA_ACK) { return (1); // BAD!! } // Success return (0); } // Function Name: i2c_stop() // // Description: Release I2C bus. // // void i2c_stop(void) { // Initiate Stop outp((BV(TWINT) | BV(TWSTO) | BV(TWEN)), TWCR); } // Function Name: i2c_write() // // Description: Write byte to currently addressed slave // // unsigned char i2c_write(unsigned char data) { // Send Data outp(data, TWDR); // Data // Start transmission outp((BV(TWINT) | BV(TWEN)), TWCR); // Wait 'till it's done... while (bit_is_clear(TWCR, TWINT)) ; // check for proper status in TWSR.. if ((inp(TWSR) & 0xF8) != TW_MT_DATA_ACK) { return (1); // BAD!! } // Success return (0); } // Function Name: i2c_read() // // Description: Read byte from currently addressed slave. ack parameter determines if we should slave ack or not. // // unsigned char i2c_read(unsigned char ack) { // Prepare to receive next byte if (ack == ACK) outp((BV(TWINT) | BV(TWEA) | BV(TWEN)), TWCR); // Send ACK else outp((BV(TWINT) | BV(TWEN)), TWCR); // Send NACK // Wait 'till it's done... while (bit_is_clear(TWCR, TWINT)) ; // Get Data return(inp(TWDR)); } // ************************** // High-level I2C routines. // ************************** // Function Name: i2c_readbyte() // // Description: Read a single byte from device 'i2c_address' at address 'location'. // // unsigned char i2c_readbyte(unsigned char i2c_address, unsigned char location) { unsigned char temp = 0; AvrXWaitSemaphore(&I2C_Mutex); //i2c_requests++; // set device address and write mode (exit with error if it fails.) if (i2c_start(i2c_address+I2C_WRITE)) { i2c_errors++; #ifdef I2C_ERROR PutChar('r'); PutChar('1'); #endif } else { // write byte address if (i2c_write(location)) { i2c_errors++; #ifdef I2C_ERROR PutChar('r'); PutChar('2'); #endif } else { // set device address and read mode if (i2c_rep_start(i2c_address+I2C_READ)) { i2c_errors++; #ifdef I2C_ERROR PutChar('r'); PutChar('3'); #endif } else temp = i2c_read(NACK); // read one byte only } } i2c_stop(); AvrXSetSemaphore(&I2C_Mutex); return(temp); } // Function Name: i2c_writebyte() // // Description: Write single byte 'data' to device 'i2c_address' at address 'location'. Returns results of operation. // // unsigned char i2c_writebyte(unsigned char i2c_address, unsigned char location, unsigned char data) { unsigned char temp = 0; AvrXWaitSemaphore(&I2C_Mutex); //i2c_requests++; // set device address and write mode if (i2c_start(i2c_address+I2C_WRITE)) { i2c_errors++; #ifdef I2C_ERROR PutChar('w'); PutChar('1'); #endif } else { if (i2c_write(location)) // write location address to write to { i2c_errors++; #ifdef I2C_ERROR PutChar('w'); PutChar('2'); #endif } else { if ((temp = i2c_write(data))) // ret=0 -> Ok, ret=1 -> no ACK { i2c_errors++; #ifdef I2C_ERROR PutChar('w'); PutChar('3'); #endif } } } i2c_stop(); // set stop conditon = release bus AvrXSetSemaphore(&I2C_Mutex); return(temp); } // Function Name: i2c_readword() // // Description: Read a single word from device 'i2c_address' at address 'location'. // // unsigned int i2c_readword(unsigned char i2c_address, unsigned char location) { unsigned int temp = 0; AvrXWaitSemaphore(&I2C_Mutex); if (i2c_start(i2c_address+I2C_WRITE)) // set device address and write mode { i2c_errors++; } else { if (i2c_write(location)) // write byte address { i2c_errors++; } else { if (i2c_rep_start(i2c_address+I2C_READ)) // set device address and read mode { i2c_errors++; } else { temp = i2c_read(ACK) << 8; // Get High Byte temp += i2c_read(NACK); // Get Low Byte } } } i2c_stop(); AvrXSetSemaphore(&I2C_Mutex); return(temp); } // Function Name: i2c_writeword() // // Description: Write single word 'data' to device 'i2c_address' at address 'location'. Returns results of operation. // // unsigned char i2c_writeword(unsigned char i2c_address, unsigned char location, unsigned int data) { unsigned char temp=0; AvrXWaitSemaphore(&I2C_Mutex); if (i2c_start(i2c_address+I2C_WRITE)) // set device address and write mode { i2c_errors++; } else { if (i2c_write(location)) // write location address to write to { i2c_errors++; } else { if ((temp = i2c_write((unsigned char)(data >> 8)))) // Write High Byte first { i2c_errors++; } else { if (temp += i2c_write((unsigned char)data)) // Write Low Byte { i2c_errors++; } } } } i2c_stop(); // set stop conditon = release bus AvrXSetSemaphore(&I2C_Mutex); return(temp); }