TWI (I2C) with an atmega328P

Go To Last Post
8 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

I am trying to communicate with a L3G4200D gyrosensor (which is on the minimu-9 module: http://www.pololu.com/catalog/pr...) using an Atmega328P.

I am trying to read one byte from the gyrosensor. However, I am kind of confused when sending the address of the gyrosensor via TWI (I'm not sure how to do so!). According to the gyrosensor's datasheet, by default the address is 1101001b. When I call my ReadByte function, I am getting a value of just "1" everytime though. I am assigning the address like:

uint8_t address = 0x1101001;

Can you please help me get on the right track?

Here are the important segments of my current code:

TWI Set-up functions

// setups TWI (Two Wire Interface A.K.A I2C)
void TWISetup(){

    // Set up TWI module to 27.9KHz
    // SCLfreq = Fclock/(16+2*TWBR*(prescaler))

    // Set up TWI but rate to 2
    TWBR = 4;
    // Prescaler to 64
    TWSR |= (1<<TWPS1) | (1<<TWPS0);
    // Enable TWI
    TWCR |= (1<<TWEN);

}

// Start signal function
void TWIStart(){

    // From datasheet page 226
    TWCR = (1<<TWINT)|(1<<TWSTA)|(1<<TWEN); // Send start condition

    while (!(TWCR & (1<<TWINT))); // Wait for TWINT Flag set. This indicates that the START condition has been transmitted

}

void TWIStop(){

    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTO); // Transmit STOP condition
}

void TWIWrite(uint8_t data){

    //Load SLA_W into TWDR Register. Clear TWINT bit in
    //TWCR to start transmission of address
    TWDR = data;
    TWCR = (1<<TWINT) | (1<<TWEN);

    //Wait for TWINT Flag set. This indicates that the SLA+W has
    //been transmitted, and ACK/NACK has been received.
    while (!(TWCR & (1<<TWINT)));
}

// Read with ACK (Acknowledge bit)
uint8_t TWIReadACK(){

    //TWI Interrupt Flag, TWI Enable Bit, TWI Enable Acknowledge Bit
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWEA);
    //Wait for TWINT Flag set.
    while (!(TWCR & (1<<TWINT)));
    return TWDR;

}

//read byte with NACK
uint8_t TWIReadNACK()
{
    TWCR = (1<<TWINT)|(1<<TWEN);
    //Wait for TWINT Flag set.
    while (!(TWCR & (1<<TWINT)));
    return TWDR;
}

Read byte from gyro:

uint8_t ReadByte(uint8_t address,uint8_t *data){

    TWIStart(); // Starts TWI
    if (TWIReadStatus() != 0x08)
        return -1;
   //send address
    TWIWrite(address);
    if (TWIReadStatus() != 0x28)
        return -1;
    // Send Start
    TWIStart();
   if (TWIReadStatus() != 0x10)
        return -1;
    //select device and send read bit
    TWIWrite(address|1);
    if (TWIReadStatus() != 0x40)
        return -1;

    //Reads
    *data = TWIReadACK();
     if (TWIReadStatus() != 0x58)
        return -1;
    TWIStop();
    return 0;
}

This is where I am trying to read each byte:

uint8_t data;
uint8_t address = 0x1101001;

 while(1){
    ReadByte(address,&data);
 }

Thanks in advance!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's a good idea to wait for the STOP bit to clear before returning from TWIStop();

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lcruz007 wrote:
According to the gyrosensor's datasheet, by default the address is 1101001b. When I call my ReadByte function, I am getting a value of just "1" everytime though. I am assigning the address like:
uint8_t address = 0x1101001;

Can you please help me get on the right track?


0x1101001 is a large hexadecimal number.
1101001b = 0x69

Edit:
You can also use 0b1101001 (Link)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Just waiting for the interrupt alone doesn't mean success . You have to read the status register and look for completion.

Read TWSR and look for any error codes.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

Thank you all for your replies.

Now I am sending the right device address of the L3G4200D gyrosensor (0x69) followed by a read bit, and the register address I want to read from. However, no matter what register address I send, I am always getting a value of 255 (0xFF). What's wrong with my code?

This is how I am sending the addresses to the L3G4200D:

TWIWrite((DEVADDR)|1|(address));

This is how I am trying to read a byte from the device:

#define DEVADDR 0x69

...

uint8_t ReadByte(uint8_t address){

  //send start
    TWIStart();
    if (TWIReadStatus() != 0x10)
        return -1;
    //select devise,send read bit and send info to an specific register address
    TWIWrite((DEVADDR)|1|(address));
    if (TWIReadStatus() != 0x40)
        return -1;
    uint8_t data = TWIReadACK();
    if (TWIReadStatus() != 0x58)
        return -1;
    TWIStop();
    return data;
}

This is the function that gets the current status from TWSR:

uint8_t TWIReadStatus(){

    uint8_t status;
    status = TWSR & 0xF8;

    //Check value of TWI Status Register. Mask prescaler bits. If
    //status different from START go to ERROR
    /*
    if ((TWSR & 0xF8) != START)
    ERROR();
    */

    return status;

}

When I call readByte(), it always returns 255. What's wrong with the code?

Thanks in advance!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Now I am sending the right device address of the L3G4200D gyrosensor (0x69)......by default the address is 1101001b
I don't think you are sending the correct address. When you see a 7 bit address it needs to be rotated LEFT by 1 and then add the R/W bit (ie 1101001R/W bit or 0xD2 + R/W bit)

edit also this is wrong

uint8_t address = 0x1101001;

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lcruz007 wrote:
This is how I am sending the addresses to the L3G4200D:

TWIWrite((DEVADDR)|1|(address));

This is how I am trying to read a byte from the device:

#define DEVADDR 0x69

First, "TWIWrite((DEVADDR)|1|(address)); " is wrong.

DEVADDR is the 7 most significant bits of the first byte sent,
so it should be shifted up 1 bit (DEVADDR<<1).

The "|1|" is the least significant bit and indicates a read operation follows.
This is wrong. It should be a write to send the sub-address of the register to read.

I don't know what (address) is. Is it the address of the device's register to read?
It doesn't belong here.

Table 16 of the datasheet shows the correct sequence to read a single register from the device.
Your code does not follow that sequence.

uint8_t ReadByte(uint8_t address){

  //send start
    TWIStart();
    if (TWIReadStatus() != 0x10)
        return -1;
    //select devise,send read bit and send info to an specific register address
    TWIWrite((DEVADDR)|1|(address));
    if (TWIReadStatus() != 0x40)
        return -1;
    uint8_t data = TWIReadACK();
    if (TWIReadStatus() != 0x58)
        return -1;
    TWIStop();
    return data;
} 

The 255 you get from ReadByte is an error code.
ReadByte returns an unsigned 8-bit number.

For error conditions you use "return -1;" which returns 255.

Try using uint16_t and "return (1<<8)", "(2<<8)", etc for the various error conditions.

This way you can tell if an error occurred and which error occurred.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Chuck99 wrote:
DEVADDR is the 7 most significant bits of the first byte sent,
so it should be shifted up 1 bit (DEVADDR<<1).

The "|1|" is the least significant bit and indicates a read operation follows.
This is wrong. It should be a write to send the sub-address of the register to read.

Table 16 of the datasheet shows the correct sequence to read a single register from the device.
Your code does not follow that sequence.

Right! It makes much more sense now. Thanks a lot, I followed that sequence in the datasheet and it works now. Thanks a lot. I still need to debug the code a little bit more, but I am now reading bytes from the IC.

Thanks guys!!