Jump to content
43oh

Arduino i2C ACK, how does it work?


Recommended Posts

As i mentioned previously in another discussion, i have a project that has an ITG3200 digital output gyroscope that communicates through I2C. After i struggle a lot in the MSP registers, i found it easier to use the Arduino libraries which provides a lot on wire so thanks to energia :)

https://www.sparkfun.com/products/9801

This is the gyro product that i'm using and they also provided an Arduino example here https://www.sparkfun.com/tutorials/265

 

My circuit is quite simple. Since gyro is working near 3V and luckily 2553 has the same output VCC i had no problems with that. Gyro input VLogic had to be smaller than VDD at all times for the voltage difference so i serial connected 2 10K resistors and get 1.75V between them to the VLogic. Connected 2 external pull ups with 10K to the 1.7 and 1.6 ports to SDA and SCL. Soldered GND and CLK to the ground and INT floating,

After i'm done, i uploaded this code:

 

//The Wire library is used for I2C communication
#include <Wire.h>

//This is a list of registers in the ITG-3200. Registers are parameters that determine how the sensor will behave, or they can hold data that represent the
//sensors current status.
//To learn more about the registers on the ITG-3200, download and read the datasheet.
char WHO_AM_I = 0x00;
char SMPLRT_DIV= 0x15;
char DLPF_FS = 0x16;
char GYRO_XOUT_H = 0x1D;
char GYRO_XOUT_L = 0x1E;
char GYRO_YOUT_H = 0x1F;
char GYRO_YOUT_L = 0x20;
char GYRO_ZOUT_H = 0x21;
char GYRO_ZOUT_L = 0x22;

//This is a list of settings that can be loaded into the registers.
//DLPF, Full Scale Register Bits
//FS_SEL must be set to 3 for proper operation
//Set DLPF_CFG to 3 for 1kHz Fint and 42 Hz Low Pass Filter
char DLPF_CFG_0 = 1<<0;
char DLPF_CFG_1 = 1<<1;
char DLPF_CFG_2 = 1<<2;
char DLPF_FS_SEL_0 = 1<<3;
char DLPF_FS_SEL_1 = 1<<4;

//I2C devices each have an address. The address is defined in the datasheet for the device. The ITG-3200 breakout board can have different address depending on how
//the jumper on top of the board is configured. By default, the jumper is connected to the VDD pin. When the jumper is connected to the VDD pin the I2C address
//is 0x69.
char itgAddress = 0x69;


 

//In the setup section of the sketch the serial port will be configured, the i2c communication will be initialized, and the itg-3200 will be configured.
void setup()
{
  //Create a serial connection using a 9600bps baud rate.
  Serial.begin(9600);
  
  //Initialize the I2C communication. This will set the Arduino up as the 'Master' device.
  Wire.begin();
  
  //Read the WHO_AM_I register and print the result
  char id=0;
  id = itgRead(itgAddress, 0x00);  
  Serial.print("ID: ");
  Serial.println(id, HEX);
  
  //Configure the gyroscope
  //Set the gyroscope scale for the outputs to +/-2000 degrees per second
  itgWrite(itgAddress, DLPF_FS, (DLPF_FS_SEL_0|DLPF_FS_SEL_1|DLPF_CFG_0));
  //Set the sample rate to 100 hz
  itgWrite(itgAddress, SMPLRT_DIV, 9);
}

//The loop section of the sketch will read the X,Y and Z output rates from the gyroscope and output them in the Serial Terminal
void loop()
{
  //Create variables to hold the output rates.
  int xRate, yRate, zRate;

  //Read the x,y and z output rates from the gyroscope.
  xRate = readX();
  yRate = readY();
  zRate = readZ();

  //Print the output rates to the terminal, seperated by a TAB character.
  Serial.print(xRate);
  Serial.print('\t');
  Serial.print(yRate);
  Serial.print('\t');
  Serial.println(zRate);

  //Wait 10ms before reading the values again. (Remember, the output rate was set to 100hz and 1reading per 10ms = 100hz.)
  delay(10);
}

//This function will write a value to a register on the itg-3200.
//Parameters:
//  char address: The I2C address of the sensor. For the ITG-3200 breakout the address is 0x69.
//  char registerAddress: The address of the register on the sensor that should be written to.
//  char data: The value to be written to the specified register.
void itgWrite(char address, char registerAddress, char data)
{
  //Initiate a communication sequence with the desired i2c device
  Wire.beginTransmission(address);
  //Tell the I2C address which register we are writing to
  Wire.send(registerAddress);
  //Send the value to write to the specified register
  Wire.send(data);
  //End the communication sequence
  Wire.endTransmission();
}

//This function will read the data from a specified register on the ITG-3200 and return the value.
//Parameters:
//  char address: The I2C address of the sensor. For the ITG-3200 breakout the address is 0x69.
//  char registerAddress: The address of the register on the sensor that should be read
//Return:
//  unsigned char: The value currently residing in the specified register
unsigned char itgRead(char address, char registerAddress)
{
  //This variable will hold the contents read from the i2c device.
  unsigned char data=0;
  
  //Send the register address to be read.
  Wire.beginTransmission(address);
  //Send the Register Address
  Wire.send(registerAddress);
  //End the communication sequence.
  Wire.endTransmission();
  
  //Ask the I2C device for data
  Wire.beginTransmission(address);
  Wire.requestFrom(address, 1);
  
  //Wait for a response from the I2C device
  if(Wire.available()){
    //Save the data sent from the I2C device
    data = Wire.receive();
  }
  
  //End the communication sequence.
  Wire.endTransmission();
  
  //Return the data read during the operation
  return data;
}

//This function is used to read the X-Axis rate of the gyroscope. The function returns the ADC value from the Gyroscope
//NOTE: This value is NOT in degrees per second. 
//Usage: int xRate = readX();
int readX(void)
{
  int data=0;
  data = itgRead(itgAddress, GYRO_XOUT_H)<<8;
  data |= itgRead(itgAddress, GYRO_XOUT_L);  
  
  return data;
}

//This function is used to read the Y-Axis rate of the gyroscope. The function returns the ADC value from the Gyroscope
//NOTE: This value is NOT in degrees per second. 
//Usage: int yRate = readY();
int readY(void)
{
  int data=0;
  data = itgRead(itgAddress, GYRO_YOUT_H)<<8;
  data |= itgRead(itgAddress, GYRO_YOUT_L);  
  
  return data;
}

//This function is used to read the Z-Axis rate of the gyroscope. The function returns the ADC value from the Gyroscope
//NOTE: This value is NOT in degrees per second. 
//Usage: int zRate = readZ();
int readZ(void)
{
  int data=0;
  data = itgRead(itgAddress, GYRO_ZOUT_H)<<8;
  data |= itgRead(itgAddress, GYRO_ZOUT_L);  
  
  return data;
}

 

 

I removed the writing part for the angle configuration for the later work since i had to change the shifting registers according to 2553. 

 

When i'm done, i provided oscilloscope monitoring to SCL and SDA bus to check what was really going on since i only saw 0 0 0 (later -1 -1 -1 for some reason) as an X Y Z output. 

SCL was seem to be fine. 9 clock cycles frequently.

SDA seemed to be fine, 7bit which i calculated 1101001 which was 0x69, slave address for the gyro that i was writing. The problem was the 9th cycle because i saw the HIGH value on SDA which should be lowered by the gyroscope as an ACK. I tested on another gyro thinking that mine had some problems but the result was same. So the only explanation was that Master does not release the SDA bus for ACK. 

I checked the Wire.startTransmission, Wire.write and Wire.endTransmission functions to see how the master releases the BUS and how it waits for the ACK but didn't quite understand it. Can anyone that dealed with this kind of problem can explain me what is the problem here?

 

 

Link to post
Share on other sites

Oh and i just realise this part 

char DLPF_CFG_0 = 1<<0;
char DLPF_CFG_1 = 1<<1;
char DLPF_CFG_2 = 1<<2;
char DLPF_FS_SEL_0 = 1<<3;
char DLPF_FS_SEL_1 = 1<<4;

 

is not the arduino registers but the configuration for the registers inside the gyro. So i added the Wire.Write part realising that it also configures the sample rate. But still i get 0 0 0. I really don't know what i'm missing here...

Link to post
Share on other sites

As far as I can read in the Arduino manual, you should not use beginTransmission and endTransmission when receiving data. To use a repeated start, after sending the register address use false as parameter to endTransmission, this is usually required for a register-result sequence. You have them in the beginning and end of this code snippet

  Wire.endTransmission(/*false*/); //<< insert false here

  //Ask the I2C device for data
  Wire.beginTransmission(address); //<< remove this line
  Wire.requestFrom(address, 1);
  
  //Wait for a response from the I2C device
  if(Wire.available()){
    //Save the data sent from the I2C device
    data = Wire.receive();
  }
  
  //End the communication sequence.
  Wire.endTransmission(); //<< remove this line

Failing to suppress the stop token (with inserting false) will cause the master to issue a NACK to indicate the transmission will now end.

Link to post
Share on other sites

Yes exactly like you said. even the single byte read operation requires a repeated start condition. This is the read sequence in the ITG3200 data sheet https://www.sparkfun.com/datasheets/Sensors/Gyro/PS-ITG-3200-00-01.4.pdf

 

m4brb.jpg

And this is the corresponding part 

unsigned char itgRead(char address, char registerAddress)
{
  //This variable will hold the contents read from the i2c device.
  unsigned char data=0;
  
  //Send the register address to be read.
  Wire.beginTransmission(address);
  //Send the Register Address
  Wire.write(registerAddress);
  //End the communication sequence.
  Wire.endTransmission(false);
  
  //Ask the I2C device for data
  Wire.requestFrom(address, 1);
  
  //Wait for a response from the I2C device
  if(Wire.available()){
    //Save the data sent from the I2C device
    data = Wire.read();
  }

for some reason i can't open the serial monitor right now but in oscilloscope i can see the repeated start but still, on the 9th cycle i see no ACK from the gyro.

m4cdt.jpg

 

By the way, in my understanding that if a master uses write() it only write the bytes into a queue and send them with endTransition() as i see in  http://arduino.cc/en/Reference/WireBeginTransmission

Link to post
Share on other sites

It's quite interesting that many Arduino examples and libraries (e.g. by Adafruit) use begin/endTransmission when reading. Any insights why that might be?

 

As to the missing NACK at the end of one-byte-read, you could try the fix discussed in this thread:

http://forum.43oh.com/topic/3154-i2c-issues/

 

If you give it a try, let us know if it solves your problem.

 

Edit: Never mind, re-reading the thread it looks like you're stuck before the read.

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...