Use of low level libraries

Hi, I need help with I2C. I wanted to test write speed of my I2C EEPROM but the example uses delay(5); to ensure the write is complete. I wanted to implement polling on ACK as advised in the datasheet of the EEPROM but I think it cannot be done in the Wire library directly (Wire.beginTransmisson doesn't return if the response was ACK or NACK). So I looked deeper.
The example includes Wire.h
As I understand it Wire.h silently includes Wire.cpp
Wire.cpp includes utility/twi.h
twi.h silently includes twi.c
twi.c have function

uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop)

which can determine if address was ACKed.
Using this function throws error

I2C_EEPROM:18: error: 'twi_writeTo' was not declared in this scope

   twi_writeTo(deviceaddress, rdata, 2, 0, 1);

                                            ^

exit status 1
'twi_writeTo' was not declared in this scope

What is wrong?

The Arduino libraries are online. This is the Wire library for the AVR chips : https://github.com/arduino/Arduino/tree/master/hardware/arduino/avr/libraries/Wire/src

If you look at the code in Wire.cpp, the twi_writeTo return value is returned by Wire.endTransmission().

If you only want to check the ACK/NAK from a Slave as response to its address, you can use Wire.beginTransmission() and Wire.endTransmission(). That is what the i2c_scanner does : http://playground.arduino.cc/Main/I2cScanner

Do you have a link to your EEPROM ?
Most EEPROMs require a delay when writing data. Sometimes writing a multiple of 4 bytes makes it easier : Gammon Forum : Electronics : Microprocessors : I2C - Two-Wire Peripheral Interface - for Arduino

Thank you, this will call the function for me and it will work for now. But still - how can I use the function directly?

EDIT: it's not importand but when you asked it's M24C02 from ST.

I think you can include the twi.h file in your sketch and use the functions. I do this with the ethernet utility low level functions. I have not tried it with the wire low level functions tho.
#include <utility/twi.h>

//#include <Wire.h> //I2C library
#include <utility/twi.h>

returns

fatal error: utility/twi.h: No such file or directory

 #include <utility/twi.h>

                         ^

compilation terminated.

Including both returns

undefined reference to `twi_writeTo(unsigned char, unsigned char*, unsigned char, unsigned char, unsigned char)'

collect2.exe: error: ld returned 1 exit status

exit status 1
Error compiling for board Arduino Nano.

Is is possible there is problem that twi is written in C and not in C++?

I think you better not call the twi functions directly.
If you want to check if the EEPROM does a ACK to its I2C address, then you can do a Wire.beginTransmission() and a Wire.endTransmission() and check the return value of the Wire.endTransmission().
That is the normal way, and everyone uses it like that. Why do you want a workaround for something that working just fine ?

Do you really need 256 byte of EEPROM, when the Arduino Uno has already 1 kbyte.

Is is possible there is problem that twi is written in C and not in C++?

Apparently that is true. The ethernet library utility files are C++.

@Koepel
Why do I want to call it directly? I want to know what I am doing. I don’t really understand what the Wire object is - I believe the whole OOP is confusing and since I don’t need more Wire objects (I have just one I2C hardware anyway) it is better to live without it. Do you know how much resources it consumes? I do not. But if I understand it right the Wire library creates a buffer (txBuffer) to take user’s data and then it is directly copied to the twi’s buffer (twi_masterBuffer) which is sent via I2C. I doubt the optimizer will notice and remove the useless extra buffer. Basicly using Wire library uses at least BUFFER_LENGTH*2=64 bytes “for nothing”.

I use 256 byte EEPROM because I expect I will use larger one later. I just bought the cheapest one to do experiments. For example I already connected the chip with 5V to VSS and GND to VCC (the other way than it should be done) with applying signals to the data pins. It survived somehow but I wouldn’t trust the chip to hold some “important” data in the future.

I never use the Wire libraries, if I can help it. I use a very simple set of I2C routines for the ATMega328 that call the hardware directly. These routines expect an 8-bit device address (not 7 bit as many Arduino functions expect).

Here is an example. Note, the multibyte routines may be device-specific.

/*

Simple I2C routines
ATmega328 @ 16 MHz  Atmel Studio IV/ avr-gcc

*/

#define F_CPU 16000000UL
#include <avr/io.h>

// ---------------------------------------------------------------------------
// I2C (TWI) ROUTINES
//
// The standard clock rate is 100 KHz, and set by I2C_Init
// FIXED I2C_Stop() sjr

#define F_SCL 100000L // I2C clock speed 100 KHz
#define READBIT 1  //low bit of device address for read
#define TW_START 0xA4 // send start condition (TWINT,TWSTA,TWEN)
#define TW_STOP 0x94 // send stop condition (TWINT,TWSTO,TWEN)
#define TW_ACK 0xC4 // return ACK to slave
#define TW_NACK 0x84 // return NACK to slave
#define TW_SEND 0x84 // send data (TWINT,TWEN)
#define TW_READY (TWCR & 0x80) // ready when TWINT returns to logic 1.
#define TW_STATUS (TWSR & 0xF8) // returns value of status register

void I2C_Init(){

// at 16 MHz, the SCL frequency will be 16/(16+2(TWBR)), assuming prescalar of 0.
// so for 100KHz SCL, TWBR = ((F_CPU/F_SCL)-16)/2 = ((16/0.1)-16)/2 = 72
    TWSR = 0; // prescalar to zero
    TWBR = ((F_CPU/F_SCL)-16)/2; // set SCL frequency in TWI bit register
}

void I2C_Stop(void) {

	TWCR=TW_STOP;
	 // wait for stop condition to be executed on bus
  	 // TWINT is not set after a stop condition!
 	while(TWCR & _BV(TWSTO));
}

unsigned char I2C_Detect(unsigned char addr){

// look for device at specified address; return 1=found, 0=not found
    TWCR = TW_START; // send start condition
    while (!TW_READY); //wait
	TWDR = addr; // load device's bus address
    TWCR = TW_SEND; // and send it
    while (!TW_READY);
    return (TW_STATUS==0x18); // return 1 if found; 0 otherwise
}

void ShowDevices(void){

// search all 127 addresses, report those present on the I2C bus

    for (unsigned char addr=1; addr<128; addr++) {
        if (I2C_Detect(addr<<1)) // I2C detected?
           printf(" .%02X",addr<<1);
		I2C_Stop();
    	}
}

void I2C_Start (unsigned char slaveAddr) {

    I2C_Detect(slaveAddr);
}

unsigned char I2C_Write (unsigned char data) {

// sends a byte to slave

    TWDR = data; // load data to be sent
    TWCR = TW_SEND; // and send it
    while (!TW_READY); // wait
    return (TW_STATUS!=0x28); //0 if successful, 1 if not
}

unsigned char I2C_ReadACK () {

// reads a byte from slave

    TWCR = TW_ACK; // ack = will read more data
    while (!TW_READY); // wait
    return TWDR;
    //return (TW_STATUS!=0x28);
}

unsigned char I2C_ReadNACK () {

// reads a byte from slave

    TWCR = TW_NACK; // nack = not reading more data
    while (!TW_READY); // wait
    return TWDR;
	//return (TW_STATUS!=0x28);
}

void I2C_WriteByte (unsigned char busAddr, unsigned char data) {

// write byte to slave

    I2C_Start(busAddr); // send bus address
    I2C_Write(data); // then send the byte
    I2C_Stop();
}

void I2C_WriteRegister(unsigned char busAddr, unsigned char deviceRegister, unsigned char data){

    I2C_Start(busAddr); // send bus address
    I2C_Write(deviceRegister); // first unsigned char = device register address
    I2C_Write(data); // second unsigned char = data for device register
    I2C_Stop();
}

unsigned char I2C_ReadRegister(unsigned char busAddr, unsigned char deviceRegister) {

// read single byte of data in register

    unsigned char data = 0;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister); // set register pointer
    I2C_Start(busAddr+READBIT); // restart as a read operation
    data = I2C_ReadNACK(); // read the register data
    I2C_Stop(); // stop
    return data;
}

// Read a two-byte word, low order first

signed int I2C_ReadWord(unsigned char busAddr, unsigned char deviceRegister) {

    unsigned int data = 0;
	unsigned char l;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister | 0x80); // set register pointer, autoincrement
    I2C_Start(busAddr+READBIT); // restart as a read operation
    l = I2C_ReadACK(); // read the register data
	data |= I2C_ReadNACK(); //read next unsigned char
    I2C_Stop(); // stop
    return (signed int) ((data<<8)|l);
}

// read a 3 byte value, lowest order byte first

signed long I2C_ReadPressureRaw(unsigned char busAddr, unsigned char deviceRegister) {

    unsigned char pxl,pl,ph;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister | 0x80); // set register pointer, autoincrement
    I2C_Start(busAddr+READBIT); // restart as a read operation
    pxl = I2C_ReadACK(); // read ls byte
    pl = I2C_ReadACK(); // read middle
    ph = I2C_ReadNACK(); // read high
    I2C_Stop(); // stop
    return (int32_t)ph << 16 | (uint16_t)pl << 8 | pxl;
}

Recently a few bugs were solved in the Wire library (with IDE version 1.6.6) and now I can finally use a repeated start and I2C without delays between two Arduino boards. Therefor I stay with the Arduino Wire object, and hope that any future changes will not bring back those bugs.

twi is a utility of the Wire library. It lives in the utility folder within that library. It’s not something you can include by itself or use without wire. Think of twi.h as just more code in the Wire library itself that the writers just happened to have broken off into a separate file.

You could go make a copy of it and put it in a folder in your libraries folder. It would probably be a good idea to rename it. Then you can try to use the functions in it without wire.

This should work:

#include <Wire.h> //I2C library
extern "C"
{
#include <utility/twi.h>
}

After doing that I can call twi_writeTo() with no errors.

You would have to include Wire/utility/twi.h as there is no utility folder in the libraries folder where it looks. I don’t understand the process of getting includes with the IDE well enough to know if that will work or not. But I know it won’t find utility buried in the Wire library without being told where to look. Really the extern C isn’t needed I don’t think. Worst case copy the files and rename twi.c to twi.cpp.

Nice, pert. I'll remember that. Seems to work here also.

Delta_G:
You would have to include Wire/utility/twi.h as there is no utility folder in the libraries folder where it looks. I don't understand the process of getting includes with the IDE well enough to know if that will work or not.

Easy enough to find out. Write a test sketch and give it a try. I did and your suggestion doesn't work. That's why including Wire.h is required before the twi.h include.

Delta_G:
But I know it won't find utility buried in the Wire library without being told where to look. Really the extern C isn't needed I don't think.

Did you actually test my code? Before saying I'm wrong you might want to load up your IDE.

pert:
Easy enough to find out. Write a test sketch and give it a try. I did and your suggestion doesn't work. That's why including Wire.h is required before the twi.h include.
Did you actually test my code? Before saying I'm wrong you might want to load up your IDE.

I don't have the IDE with me ATM. I guess since Wire.h is included that somehow it knows which of the many utility folders to get. I don't know how, but if you say it works then I guess it does.

This is my result:

Memory usage as reported by compiler:
Nothing enabled        : 2064 bytes, 200 ram.
with ENABLE_WIRE       : 4070 bytes, 408 ram.
with ENABLE_TWI        : 3838 bytes, 407 ram.
with ENABLE_JREMINGTON : 2418 bytes, 200 ram.
SoftwareWire           : 4284 bytes, 261 ram.

I used a development version of SoftwareWire.
Perhaps the Wire object is created with the twi version as well. I could copy the twi_init and twi_writeTo into my sketch, but have not done that yet.
The Arduino Wire library is the only one that is compatible with other processors of course.

My test sketch:

// What is the difference between the Wire object and calling twi functions.
//
// forum:
//    Use of low level libraries
//    http://forum.arduino.cc/index.php?topic=392526.0
//
// Tested with Arduino Uno, Arduino IDE 1.6.8 and HMC5883L magnetometer.
// Memory usage as reported by compiler:
//      Nothing enabled        : 2064 bytes, 200 ram.
//      with ENABLE_WIRE       : 4070 bytes, 408 ram.
//      with ENABLE_TWI        : 3838 bytes, 407 ram.
//      with ENABLE_JREMINGTON : 2418 bytes, 200 ram.
//      SoftwareWire           : 4284 bytes, 261 ram. (development version of SoftwareWire)


// select an option
// ----------------
#define ENABLE_WIRE
// #define ENABLE_TWI
// #define ENABLE_JREMINGTON


#ifdef ENABLE_WIRE
#include <Wire.h>
#endif

#ifdef ENABLE_TWI
#include <Wire.h> //I2C library
extern "C"
{
#include <utility/twi.h>
}
#endif

const int compass = 0x1E;        // a HMC5883L magnetometer

void setup() 
{
  Serial.begin(9600);
  Serial.println(F( "\nStarted"));

#ifdef ENABLE_WIRE
  Wire.begin();
#endif

#ifdef ENABLE_TWI
  twi_init();
#endif

#ifdef ENABLE_JREMINGTON
  I2C_Init();
#endif
}


void loop() 
{
#ifdef ENABLE_WIRE
  Wire.beginTransmission( compass);       
  int status = Wire.endTransmission();
  Serial.println( status);
#endif

#ifdef ENABLE_TWI
  // The 'wait' must 1, or else the return value can not be used.
  uint8_t ret = twi_writeTo( compass, NULL, 0, 1 , 1);    // length = 0, wait = 1 ?, stop = 1
  Serial.println( ret);
#endif

#ifdef ENABLE_JREMINGTON
  unsigned char det = I2C_Detect( compass<<1);
  I2C_Stop();
  Serial.println( det);
#endif

  delay(1000);
}




#ifdef ENABLE_JREMINGTON

/*

Simple I2C routines
ATmega328 @ 16 MHz  Atmel Studio IV/ avr-gcc

*/

#define F_CPU 16000000UL
#include <avr/io.h>

// ---------------------------------------------------------------------------
// I2C (TWI) ROUTINES
//
// The standard clock rate is 100 KHz, and set by I2C_Init
// FIXED I2C_Stop() sjr

#define F_SCL 100000L // I2C clock speed 100 KHz
#define READBIT 1  //low bit of device address for read
#define TW_START 0xA4 // send start condition (TWINT,TWSTA,TWEN)
#define TW_STOP 0x94 // send stop condition (TWINT,TWSTO,TWEN)
#define TW_ACK 0xC4 // return ACK to slave
#define TW_NACK 0x84 // return NACK to slave
#define TW_SEND 0x84 // send data (TWINT,TWEN)
#define TW_READY (TWCR & 0x80) // ready when TWINT returns to logic 1.
#define TW_STATUS (TWSR & 0xF8) // returns value of status register

void I2C_Init(){

// at 16 MHz, the SCL frequency will be 16/(16+2(TWBR)), assuming prescalar of 0.
// so for 100KHz SCL, TWBR = ((F_CPU/F_SCL)-16)/2 = ((16/0.1)-16)/2 = 72
    TWSR = 0; // prescalar to zero
    TWBR = ((F_CPU/F_SCL)-16)/2; // set SCL frequency in TWI bit register
}

void I2C_Stop(void) {

  TWCR=TW_STOP;
  // wait for stop condition to be executed on bus
    // TWINT is not set after a stop condition!
  while(TWCR & _BV(TWSTO));
}

unsigned char I2C_Detect(unsigned char addr){

// look for device at specified address; return 1=found, 0=not found
    TWCR = TW_START; // send start condition
    while (!TW_READY); //wait
  TWDR = addr; // load device's bus address
    TWCR = TW_SEND; // and send it
    while (!TW_READY);
    return (TW_STATUS==0x18); // return 1 if found; 0 otherwise
}

void ShowDevices(void){

// search all 127 addresses, report those present on the I2C bus

    for (unsigned char addr=1; addr<128; addr++) {
        if (I2C_Detect(addr<<1)) // I2C detected?
           printf(" .%02X",addr<<1);
    I2C_Stop();
      }
}

void I2C_Start (unsigned char slaveAddr) {

    I2C_Detect(slaveAddr);
}

unsigned char I2C_Write (unsigned char data) {

// sends a byte to slave

    TWDR = data; // load data to be sent
    TWCR = TW_SEND; // and send it
    while (!TW_READY); // wait
    return (TW_STATUS!=0x28); //0 if successful, 1 if not
}

unsigned char I2C_ReadACK () {

// reads a byte from slave

    TWCR = TW_ACK; // ack = will read more data
    while (!TW_READY); // wait
    return TWDR;
    //return (TW_STATUS!=0x28);
}

unsigned char I2C_ReadNACK () {

// reads a byte from slave

    TWCR = TW_NACK; // nack = not reading more data
    while (!TW_READY); // wait
    return TWDR;
  //return (TW_STATUS!=0x28);
}

void I2C_WriteByte (unsigned char busAddr, unsigned char data) {

// write byte to slave

    I2C_Start(busAddr); // send bus address
    I2C_Write(data); // then send the byte
    I2C_Stop();
}

void I2C_WriteRegister(unsigned char busAddr, unsigned char deviceRegister, unsigned char data){

    I2C_Start(busAddr); // send bus address
    I2C_Write(deviceRegister); // first unsigned char = device register address
    I2C_Write(data); // second unsigned char = data for device register
    I2C_Stop();
}

unsigned char I2C_ReadRegister(unsigned char busAddr, unsigned char deviceRegister) {

// read single byte of data in register

    unsigned char data = 0;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister); // set register pointer
    I2C_Start(busAddr+READBIT); // restart as a read operation
    data = I2C_ReadNACK(); // read the register data
    I2C_Stop(); // stop
    return data;
}

// Read a two-byte word, low order first

signed int I2C_ReadWord(unsigned char busAddr, unsigned char deviceRegister) {

    unsigned int data = 0;
  unsigned char l;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister | 0x80); // set register pointer, autoincrement
    I2C_Start(busAddr+READBIT); // restart as a read operation
    l = I2C_ReadACK(); // read the register data
  data |= I2C_ReadNACK(); //read next unsigned char
    I2C_Stop(); // stop
    return (signed int) ((data<<8)|l);
}

// read a 3 byte value, lowest order byte first

signed long I2C_ReadPressureRaw(unsigned char busAddr, unsigned char deviceRegister) {

    unsigned char pxl,pl,ph;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister | 0x80); // set register pointer, autoincrement
    I2C_Start(busAddr+READBIT); // restart as a read operation
    pxl = I2C_ReadACK(); // read ls byte
    pl = I2C_ReadACK(); // read middle
    ph = I2C_ReadNACK(); // read high
    I2C_Stop(); // stop
    return (int32_t)ph << 16 | (uint16_t)pl << 8 | pxl;
}
#endif

The signals are the same for Wire and twi:

But the begin and end are shorter for the jremington version:

The extern “C” works, thanks for help. I was playing with the Koepel’s example and I am lost. Since the compiler reports only static variables I used

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

code form Arduino Playground to report real numbers. When using twi or Wire I got 1620 free space.

If I understand it well the Wire uses two buffers of BUFFER_LENGTH and twi uses three buffers of TWI_BUFFER_LENGTH. I expected I could get different memory usage by redefinating this two constants. But both Arduino and the IDE reports exactly the same memory usage. I tried to send some data to use the buffers but still RAM usage didn’t change. Why? The includes I used are

#define TWI_BUFFER_LENGTH 80

#define TWI_LIB_TO_USE 2

#if TWI_LIB_TO_USE==1
#define ENABLE_WIRE
#elif TWI_LIB_TO_USE==2
#define ENABLE_TWI
#elif TWI_LIB_TO_USE==3
#define ENABLE_JREMINGTON
#endif


#ifdef ENABLE_WIRE
#include <Wire.h>
#endif

#ifdef ENABLE_TWI
#include <Wire.h> //I2C library
extern "C"
{
#include <utility/twi.h>
}
#endif

#define BUFFER_LENGTH 80

RAM report is included this way

#ifdef ENABLE_WIRE
  Wire.beginTransmission( compass);
  for (byte i=0;i<repeats;i++) {
  Wire.write(18);   } 
  Serial.println(freeRam());   
  int status = Wire.endTransmission();
  Serial.println( status);
#endif

and

#ifdef ENABLE_TWI
  // The 'wait' must 1, or else the return value can not be used.
  byte data[]={1};
  uint8_t ret = twi_writeTo( compass, data , repeats, 1 , 1);    // length = 0, wait = 1 ?, stop = 1
  Serial.println(freeRam());
  Serial.println( ret);
#endif

The whole code will come in next post because this one was too long :frowning:

Now I have found another “issue”: when I try to send more than 32 bytes I get error code 1 (data longer than buffer) despite Serial.println(TWI_BUFFER_SIZE) reports correctly 80. WTF? I thought #include XXX works like copying the text in library into the sketch but clearly it is much more complicated.

The promised code:

// What is the difference between the Wire object and calling twi functions.
//
// forum:
//    Use of low level libraries
//    http://forum.arduino.cc/index.php?topic=392526.0
//
// Tested with Arduino Uno, Arduino IDE 1.6.8 and HMC5883L magnetometer.
// Memory usage as reported by compiler:
//      Nothing enabled        : 2064 bytes, 200 ram.
//      with ENABLE_WIRE       : 4070 bytes, 408 ram.
//      with ENABLE_TWI        : 3838 bytes, 407 ram.
//      with ENABLE_JREMINGTON : 2418 bytes, 200 ram.
//      SoftwareWire           : 4284 bytes, 261 ram. (development version of SoftwareWire)


// select an option
// ----------------
#define TWI_BUFFER_LENGTH 80

#define TWI_LIB_TO_USE 2

#if TWI_LIB_TO_USE==1
#define ENABLE_WIRE
#elif TWI_LIB_TO_USE==2
#define ENABLE_TWI
#elif TWI_LIB_TO_USE==3
#define ENABLE_JREMINGTON
#endif


#ifdef ENABLE_WIRE
#include <Wire.h>
#endif

#ifdef ENABLE_TWI
#include <Wire.h> //I2C library
extern "C"
{
#include <utility/twi.h>
}
#endif

#define BUFFER_LENGTH 80

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

const int compass = 0x1E;        // a HMC5883L magnetometer

void setup() 
{
  Serial.begin(9600);
  Serial.println(F( "\nStarted"));

#ifdef ENABLE_WIRE
  Wire.begin();
#endif

#ifdef ENABLE_TWI
  twi_init();
#endif

#ifdef ENABLE_JREMINGTON
  I2C_Init();
#endif
}
volatile byte repeats=40;

void loop() 
{
#ifdef ENABLE_WIRE
  Wire.beginTransmission( compass);
  for (byte i=0;i<repeats;i++) {
  Wire.write(18);   } 
  Serial.println(freeRam());   
  int status = Wire.endTransmission();
  Serial.println( status);
#endif

#ifdef ENABLE_TWI
  // The 'wait' must 1, or else the return value can not be used.
  byte data[]={1};
  uint8_t ret = twi_writeTo( compass, data , repeats, 1 , 1);    // length = 0, wait = 1 ?, stop = 1
  Serial.println(freeRam());
  Serial.println( ret);
#endif

#ifdef ENABLE_JREMINGTON
  unsigned char det = I2C_Detect( compass<<1);
  I2C_Stop();
  Serial.println( det);
#endif


Serial.println( TWI_BUFFER_LENGTH);
Serial.println( BUFFER_LENGTH);


  delay(1000);
}




#ifdef ENABLE_JREMINGTON

/*

Simple I2C routines
ATmega328 @ 16 MHz  Atmel Studio IV/ avr-gcc

*/

#define F_CPU 16000000UL
#include <avr/io.h>

// ---------------------------------------------------------------------------
// I2C (TWI) ROUTINES
//
// The standard clock rate is 100 KHz, and set by I2C_Init
// FIXED I2C_Stop() sjr

#define F_SCL 100000L // I2C clock speed 100 KHz
#define READBIT 1  //low bit of device address for read
#define TW_START 0xA4 // send start condition (TWINT,TWSTA,TWEN)
#define TW_STOP 0x94 // send stop condition (TWINT,TWSTO,TWEN)
#define TW_ACK 0xC4 // return ACK to slave
#define TW_NACK 0x84 // return NACK to slave
#define TW_SEND 0x84 // send data (TWINT,TWEN)
#define TW_READY (TWCR & 0x80) // ready when TWINT returns to logic 1.
#define TW_STATUS (TWSR & 0xF8) // returns value of status register

void I2C_Init(){

// at 16 MHz, the SCL frequency will be 16/(16+2(TWBR)), assuming prescalar of 0.
// so for 100KHz SCL, TWBR = ((F_CPU/F_SCL)-16)/2 = ((16/0.1)-16)/2 = 72
    TWSR = 0; // prescalar to zero
    TWBR = ((F_CPU/F_SCL)-16)/2; // set SCL frequency in TWI bit register
}

void I2C_Stop(void) {

  TWCR=TW_STOP;
  // wait for stop condition to be executed on bus
    // TWINT is not set after a stop condition!
  while(TWCR & _BV(TWSTO));
}

unsigned char I2C_Detect(unsigned char addr){

// look for device at specified address; return 1=found, 0=not found
    TWCR = TW_START; // send start condition
    while (!TW_READY); //wait
  TWDR = addr; // load device's bus address
    TWCR = TW_SEND; // and send it
    while (!TW_READY);
    return (TW_STATUS==0x18); // return 1 if found; 0 otherwise
}

void ShowDevices(void){

// search all 127 addresses, report those present on the I2C bus

    for (unsigned char addr=1; addr<128; addr++) {
        if (I2C_Detect(addr<<1)) // I2C detected?
           printf(" .%02X",addr<<1);
    I2C_Stop();
      }
}

void I2C_Start (unsigned char slaveAddr) {

    I2C_Detect(slaveAddr);
}

unsigned char I2C_Write (unsigned char data) {

// sends a byte to slave

    TWDR = data; // load data to be sent
    TWCR = TW_SEND; // and send it
    while (!TW_READY); // wait
    return (TW_STATUS!=0x28); //0 if successful, 1 if not
}

unsigned char I2C_ReadACK () {

// reads a byte from slave

    TWCR = TW_ACK; // ack = will read more data
    while (!TW_READY); // wait
    return TWDR;
    //return (TW_STATUS!=0x28);
}

unsigned char I2C_ReadNACK () {

// reads a byte from slave

    TWCR = TW_NACK; // nack = not reading more data
    while (!TW_READY); // wait
    return TWDR;
  //return (TW_STATUS!=0x28);
}

void I2C_WriteByte (unsigned char busAddr, unsigned char data) {

// write byte to slave

    I2C_Start(busAddr); // send bus address
    I2C_Write(data); // then send the byte
    I2C_Stop();
}

void I2C_WriteRegister(unsigned char busAddr, unsigned char deviceRegister, unsigned char data){

    I2C_Start(busAddr); // send bus address
    I2C_Write(deviceRegister); // first unsigned char = device register address
    I2C_Write(data); // second unsigned char = data for device register
    I2C_Stop();
}

unsigned char I2C_ReadRegister(unsigned char busAddr, unsigned char deviceRegister) {

// read single byte of data in register

    unsigned char data = 0;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister); // set register pointer
    I2C_Start(busAddr+READBIT); // restart as a read operation
    data = I2C_ReadNACK(); // read the register data
    I2C_Stop(); // stop
    return data;
}

// Read a two-byte word, low order first

signed int I2C_ReadWord(unsigned char busAddr, unsigned char deviceRegister) {

    unsigned int data = 0;
  unsigned char l;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister | 0x80); // set register pointer, autoincrement
    I2C_Start(busAddr+READBIT); // restart as a read operation
    l = I2C_ReadACK(); // read the register data
  data |= I2C_ReadNACK(); //read next unsigned char
    I2C_Stop(); // stop
    return (signed int) ((data<<8)|l);
}

// read a 3 byte value, lowest order byte first

signed long I2C_ReadPressureRaw(unsigned char busAddr, unsigned char deviceRegister) {

    unsigned char pxl,pl,ph;
    I2C_Start(busAddr); // send device address
    I2C_Write(deviceRegister | 0x80); // set register pointer, autoincrement
    I2C_Start(busAddr+READBIT); // restart as a read operation
    pxl = I2C_ReadACK(); // read ls byte
    pl = I2C_ReadACK(); // read middle
    ph = I2C_ReadNACK(); // read high
    I2C_Stop(); // stop
    return (int32_t)ph << 16 | (uint16_t)pl << 8 | pxl;
}
#endif