Rev 123 | Rev 128 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
/*twi.c - TWI/I2C library for Wiring & ArduinoCopyright (c) 2006 Nicholas Zambetti. All right reserved.This library is free software; you can redistribute it and/ormodify it under the terms of the GNU Lesser General PublicLicense as published by the Free Software Foundation; eitherversion 2.1 of the License, or (at your option) any later version.This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNULesser General Public License for more details.You should have received a copy of the GNU Lesser General PublicLicense along with this library; if not, write to the Free SoftwareFoundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USAModified 2012 by Todd Krein (todd@krein.org) to implement repeated starts*/#include <math.h>#include <stdlib.h>#include <inttypes.h>#include <avr/io.h>#include <avr/interrupt.h>#ifndef cbi#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))#endif#ifndef sbi#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))#endif#include "twi.h"static volatile uint8_t twi_state;static volatile uint8_t twi_slarw;static volatile uint8_t twi_sendStop; // should the transaction end with a stopstatic volatile uint8_t twi_inRepStart; // in the middle of a repeated startstatic void (*twi_onSlaveTransmit)(void);static void (*twi_onSlaveReceive)(uint8_t*, int);static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];static volatile uint8_t twi_masterBufferIndex;static volatile uint8_t twi_masterBufferLength;static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];static volatile uint8_t twi_txBufferIndex;static volatile uint8_t twi_txBufferLength;static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];static volatile uint8_t twi_rxBufferIndex;static volatile uint8_t twi_error;/** Function twi_init* Desc readys twi pins and sets twi bitrate* Input none* Output none*/void twi_init(void){// initialize statetwi_state = TWI_READY;twi_sendStop = true; // default valuetwi_inRepStart = false;// activate internal pullups for twi// @TODO: activate pullups (If not provided externally)//digitalWrite(SDA, 1);//digitalWrite(SCL, 1);// initialize twi prescaler and bit ratecbi(TWSR, TWPS0);cbi(TWSR, TWPS1);TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;/* twi bit rate formula from atmega128 manual pg 204SCL Frequency = CPU Clock Frequency / (16 + (2 * TWBR))note: TWBR should be 10 or higher for master modeIt is 72 for a 16mhz Wiring board with 100kHz TWI */// enable twi module, acks, and twi interruptTWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);}/** Function twi_slaveInit* Desc sets slave address and enables interrupt* Input none* Output none*/void twi_setAddress(uint8_t address){// set twi slave address (skip over TWGCE bit)TWAR = address << 1;}/** Function twi_readFrom* Desc attempts to become twi bus master and read a* series of bytes from a device on the bus* Input address: 7bit i2c device address* data: pointer to byte array* length: number of bytes to read into array* sendStop: Boolean indicating whether to send a stop at the end* Output number of bytes read*/uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop){uint8_t i;// ensure data will fit into bufferif(TWI_BUFFER_LENGTH < length){return 0;}// wait until twi is ready, become master receiverwhile(TWI_READY != twi_state){continue;}twi_state = TWI_MRX;twi_sendStop = sendStop;// reset error state (0xFF.. no error occured)twi_error = 0xFF;// initialize buffer iteration varstwi_masterBufferIndex = 0;twi_masterBufferLength = length-1; // This is not intuitive, read on...// On receive, the previously configured ACK/NACK setting is transmitted in// response to the received byte before the interrupt is signalled.// Therefor we must actually set NACK when the _next_ to last byte is// received, causing that NACK to be sent in response to receiving the last// expected byte of data.// build sla+w, slave device address + w bittwi_slarw = TW_READ;twi_slarw |= address << 1;if (true == twi_inRepStart) {// if we're in the repeated start state, then we've already sent the start,// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.// We need to remove ourselves from the repeated start state before we enable interrupts,// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning// up. Also, don't enable the START interrupt. There may be one pending from the// repeated start that we sent outselves, and that would really confuse things.twi_inRepStart = false; // remember, we're dealing with an ASYNC ISRTWDR = twi_slarw;TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START}else// send start conditionTWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);// wait for read operation to completewhile(TWI_MRX == twi_state){continue;}if (twi_masterBufferIndex < length)length = twi_masterBufferIndex;// copy twi buffer to datafor(i = 0; i < length; ++i){data[i] = twi_masterBuffer[i];}return length;}/** Function twi_writeTo* Desc attempts to become twi bus master and write a* series of bytes to a device on the bus* Input address: 7bit i2c device address* data: pointer to byte array* length: number of bytes in array* wait: boolean indicating to wait for write or not* sendStop: boolean indicating whether or not to send a stop at the end* Output 0 .. success* 1 .. length to long for buffer* 2 .. address send, NACK received* 3 .. data send, NACK received* 4 .. other twi error (lost bus arbitration, bus error, ..)*/uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop){uint8_t i;// ensure data will fit into bufferif(TWI_BUFFER_LENGTH < length){return 1;}// wait until twi is ready, become master transmitterwhile(TWI_READY != twi_state){continue;}twi_state = TWI_MTX;twi_sendStop = sendStop;// reset error state (0xFF.. no error occured)twi_error = 0xFF;// initialize buffer iteration varstwi_masterBufferIndex = 0;twi_masterBufferLength = length;// copy data to twi bufferfor(i = 0; i < length; ++i){twi_masterBuffer[i] = data[i];}// build sla+w, slave device address + w bittwi_slarw = TW_WRITE;twi_slarw |= address << 1;// if we're in a repeated start, then we've already sent the START// in the ISR. Don't do it again.//if (true == twi_inRepStart) {// if we're in the repeated start state, then we've already sent the start,// (@@@ we hope), and the TWI statemachine is just waiting for the address byte.// We need to remove ourselves from the repeated start state before we enable interrupts,// since the ISR is ASYNC, and we could get confused if we hit the ISR before cleaning// up. Also, don't enable the START interrupt. There may be one pending from the// repeated start that we sent outselves, and that would really confuse things.twi_inRepStart = false; // remember, we're dealing with an ASYNC ISRTWDR = twi_slarw;TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // enable INTs, but not START}else// send start conditionTWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // enable INTs// wait for write operation to completewhile(wait && (TWI_MTX == twi_state)){continue;}if (twi_error == 0xFF)return 0; // successelse if (twi_error == TW_MT_SLA_NACK)return 2; // error: address send, nack receivedelse if (twi_error == TW_MT_DATA_NACK)return 3; // error: data send, nack receivedelsereturn 4; // other twi error}/** Function twi_transmit* Desc fills slave tx buffer with data* must be called in slave tx event callback* Input data: pointer to byte array* length: number of bytes in array* Output 1 length too long for buffer* 2 not slave transmitter* 0 ok*/uint8_t twi_transmit(const uint8_t* data, uint8_t length){uint8_t i;// ensure data will fit into bufferif(TWI_BUFFER_LENGTH < length){return 1;}// ensure we are currently a slave transmitterif(TWI_STX != twi_state){return 2;}// set length and copy data into tx buffertwi_txBufferLength = length;for(i = 0; i < length; ++i){twi_txBuffer[i] = data[i];}return 0;}/** Function twi_attachSlaveRxEvent* Desc sets function called before a slave read operation* Input function: callback function to use* Output none*/void twi_attachSlaveRxEvent( void (*function)(uint8_t*, int) ){twi_onSlaveReceive = function;}/** Function twi_attachSlaveTxEvent* Desc sets function called before a slave write operation* Input function: callback function to use* Output none*/void twi_attachSlaveTxEvent( void (*function)(void) ){twi_onSlaveTransmit = function;}/** Function twi_reply* Desc sends byte or readys receive line* Input ack: byte indicating to ack or to nack* Output none*/void twi_reply(uint8_t ack){// transmit master read ready signal, with or without ackif(ack){TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);}else{TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);}}/** Function twi_stop* Desc relinquishes bus master status* Input none* Output none*/void twi_stop(void){// send stop conditionTWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);// wait for stop condition to be exectued on bus// TWINT is not set after a stop condition!while(TWCR & _BV(TWSTO)){continue;}// update twi statetwi_state = TWI_READY;}/** Function twi_releaseBus* Desc releases bus control* Input none* Output none*/void twi_releaseBus(void){// release busTWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);// update twi statetwi_state = TWI_READY;}SIGNAL(TWI_vect){switch(TW_STATUS){// All Mastercase TW_START: // sent start conditioncase TW_REP_START: // sent repeated start condition// copy device address and r/w bit to output register and ackTWDR = twi_slarw;twi_reply(1);break;// Master Transmittercase TW_MT_SLA_ACK: // slave receiver acked addresscase TW_MT_DATA_ACK: // slave receiver acked data// if there is data to send, send it, otherwise stopif(twi_masterBufferIndex < twi_masterBufferLength){// copy data to output register and ackTWDR = twi_masterBuffer[twi_masterBufferIndex++];twi_reply(1);}else{if (twi_sendStop)twi_stop();else {twi_inRepStart = true; // we're gonna send the START// don't enable the interrupt. We'll generate the start, but we// avoid handling the interrupt until we're in the next transaction,// at the point where we would normally issue the start.TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;twi_state = TWI_READY;}}break;case TW_MT_SLA_NACK: // address sent, nack receivedtwi_error = TW_MT_SLA_NACK;twi_stop();break;case TW_MT_DATA_NACK: // data sent, nack receivedtwi_error = TW_MT_DATA_NACK;twi_stop();break;case TW_MT_ARB_LOST: // lost bus arbitrationtwi_error = TW_MT_ARB_LOST;twi_releaseBus();break;// Master Receivercase TW_MR_DATA_ACK: // data received, ack sent// put byte into buffertwi_masterBuffer[twi_masterBufferIndex++] = TWDR;case TW_MR_SLA_ACK: // address sent, ack received// ack if more bytes are expected, otherwise nackif(twi_masterBufferIndex < twi_masterBufferLength){twi_reply(1);}else{twi_reply(0);}break;case TW_MR_DATA_NACK: // data received, nack sent// put final byte into buffertwi_masterBuffer[twi_masterBufferIndex++] = TWDR;if (twi_sendStop)twi_stop();else {twi_inRepStart = true; // we're gonna send the START// don't enable the interrupt. We'll generate the start, but we// avoid handling the interrupt until we're in the next transaction,// at the point where we would normally issue the start.TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;twi_state = TWI_READY;}break;case TW_MR_SLA_NACK: // address sent, nack receivedtwi_stop();break;// TW_MR_ARB_LOST handled by TW_MT_ARB_LOST case// Slave Receivercase TW_SR_SLA_ACK: // addressed, returned ackcase TW_SR_GCALL_ACK: // addressed generally, returned ackcase TW_SR_ARB_LOST_SLA_ACK: // lost arbitration, returned ackcase TW_SR_ARB_LOST_GCALL_ACK: // lost arbitration, returned ack// enter slave receiver modetwi_state = TWI_SRX;// indicate that rx buffer can be overwritten and acktwi_rxBufferIndex = 0;twi_reply(1);break;case TW_SR_DATA_ACK: // data received, returned ackcase TW_SR_GCALL_DATA_ACK: // data received generally, returned ack// if there is still room in the rx bufferif(twi_rxBufferIndex < TWI_BUFFER_LENGTH){// put byte in buffer and acktwi_rxBuffer[twi_rxBufferIndex++] = TWDR;twi_reply(1);}else{// otherwise nacktwi_reply(0);}break;case TW_SR_STOP: // stop or repeated start condition received// put a null char after data if there's roomif(twi_rxBufferIndex < TWI_BUFFER_LENGTH){twi_rxBuffer[twi_rxBufferIndex] = '\0';}// sends ack and stops interface for clock stretchingtwi_stop();// callback to user defined callbacktwi_onSlaveReceive(twi_rxBuffer, twi_rxBufferIndex);// since we submit rx buffer to "wire" library, we can reset ittwi_rxBufferIndex = 0;// ack future responses and leave slave receiver statetwi_releaseBus();break;case TW_SR_DATA_NACK: // data received, returned nackcase TW_SR_GCALL_DATA_NACK: // data received generally, returned nack// nack back at mastertwi_reply(0);break;// Slave Transmittercase TW_ST_SLA_ACK: // addressed, returned ackcase TW_ST_ARB_LOST_SLA_ACK: // arbitration lost, returned ack// enter slave transmitter modetwi_state = TWI_STX;// ready the tx buffer index for iterationtwi_txBufferIndex = 0;// set tx buffer length to be zero, to verify if user changes ittwi_txBufferLength = 0;// request for txBuffer to be filled and length to be set// note: user must call twi_transmit(bytes, length) to do thistwi_onSlaveTransmit();// if they didn't change buffer & length, initialize itif(0 == twi_txBufferLength){twi_txBufferLength = 1;twi_txBuffer[0] = 0x00;}// transmit first byte from buffer, fallcase TW_ST_DATA_ACK: // byte sent, ack returned// copy data to output registerTWDR = twi_txBuffer[twi_txBufferIndex++];// if there is more to send, ack, otherwise nackif(twi_txBufferIndex < twi_txBufferLength){twi_reply(1);}else{twi_reply(0);}break;case TW_ST_DATA_NACK: // received nack, we are donecase TW_ST_LAST_DATA: // received ack, but we are done already!// ack future responsestwi_reply(1);// leave slave receiver statetwi_state = TWI_READY;break;// Allcase TW_NO_INFO: // no state informationbreak;case TW_BUS_ERROR: // bus error, illegal stop/starttwi_error = TW_BUS_ERROR;twi_stop();break;}}