chipKIT® Development Platform

Inspired by Arduino™

How to read data from I2C on max32?

Created Sat, 26 Oct 2013 21:39:17 +0000 by txk6632


txk6632

Sat, 26 Oct 2013 21:39:17 +0000

I'm using a ChipKit Max32 to read data in from an Accelerometer which communicates via I2C protocol. I'm really new with this so I need help with code that can read in the data. This is the code I have so far which is probably not right for the loop. It's really hard finding anything about how to read I2C.

void setup()
{

}

void loop()
{
  // print the sensor values:
  Serial.print(digitalRead(xpin));
  // print a tab between values:
  Serial.print("\t");
  Serial.print(digitalRead(ypin));
  // print a tab between values:
  Serial.print("\t");
  Serial.print(digitalRead(zpin));
  Serial.println();
  // delay before next reading:
  delay(100);
}

majenko

Sun, 27 Oct 2013 10:59:02 +0000

I see absolutely nothing there that has anything to do with I²C. You are reading 3 pins as digital signals (on, or off) and displaying them through serial.

By the way, you should wrap all code in [code] [/code] tags so it preserves the spacing and stops smilies from appearing in it.

Do you have a data sheet (or just a part number) for the accelerometer you are using? The X/Y/Z pin arrangement from that code snipped looks more like an analogue one to me, not an I²C one. If that is the case then you should be using analogRead() not digitalRead().

If it is analogue, not I²C, you may find this article I wrote a while back useful: [url]http://hacking.majenko.co.uk/inertial-accelerometers[/url]


txk6632

Sun, 27 Oct 2013 22:29:10 +0000

We went back and looked at sparkfun and got an example code to begin the basics with the Accelerometer (MMA8452Q). Even then, the IDE gives us an error for the wire.endTransmission(false) saying that it is not a valid function call in the TwoWire class. Also gives "class TwoWire has no member named read" for wire.read.

/* 
 MMA8452Q Basic Example Code
 Nathan Seidle
 SparkFun Electronics
 November 5, 2012
 
 License: This code is public domain but you buy me a beer if you use this and we meet someday (Beerware license).
 
 This example code shows how to read the X/Y/Z accelerations and basic functions of the MMA5842. It leaves out
 all the neat features this IC is capable of (tap, orientation, and inerrupts) and just displays X/Y/Z. See 
 the advanced example code to see more features.
 
 Hardware setup:
 MMA8452 Breakout ------------ Arduino
 3.3V --------------------- 3.3V
 SDA -------^^(330)^^------- A4
 SCL -------^^(330)^^------- A5
 GND ---------------------- GND
 
 The MMA8452 is 3.3V so we recommend using 330 or 1k resistors between a 5V Arduino and the MMA8452 breakout.
 
 The MMA8452 has built in pull-up resistors for I2C so you do not need additional pull-ups.
 */

#include <Wire.h> // Used for I2C

// The SparkFun breakout board defaults to 1, set to 0 if SA0 jumper on the bottom of the board is set
#define MMA8452_ADDRESS 0x1D  // 0x1D if SA0 is high, 0x1C if low

//Define a few of the registers that we will be accessing on the MMA8452
#define OUT_X_MSB 0x01
#define XYZ_DATA_CFG  0x0E
#define WHO_AM_I   0x0D
#define CTRL_REG1  0x2A

#define GSCALE 2 // Sets full-scale range to +/-2, 4, or 8g. Used to calc real g values.

void setup()
{
  Serial.begin(57600);
  Serial.println("MMA8452 Basic Example");

  Wire.begin(); //Join the bus as a master

  initMMA8452(); //Test and intialize the MMA8452
}

void loop()
{  
  int accelCount[3];  // Stores the 12-bit signed value
  readAccelData(accelCount);  // Read the x/y/z adc values

  // Now we'll calculate the accleration value into actual g's
  float accelG[3];  // Stores the real accel value in g's
  for (int i = 0 ; i < 3 ; i++)
  {
    accelG[i] = (float) accelCount[i] / ((1<<12)/(2*GSCALE));  // get actual g value, this depends on scale being set
  }

  // Print out values
  for (int i = 0 ; i < 3 ; i++)
  {
    Serial.print(accelG[i], 4);  // Print g values
    Serial.print("\t");  // tabs in between axes
  }
  Serial.println();

  delay(10);  // Delay here for visibility
}

void readAccelData(int *destination)
{
  byte rawData[6];  // x/y/z accel register data stored here

  readRegisters(OUT_X_MSB, 6, rawData);  // Read the six raw data registers into data array

  // Loop to calculate 12-bit ADC and g value for each axis
  for(int i = 0; i < 3 ; i++)
  {
    int gCount = (rawData[i*2] << 8) | rawData[(i*2)+1];  //Combine the two 8 bit registers into one 12-bit number
    gCount >>= 4; //The registers are left align, here we right align the 12-bit integer

    // If the number is negative, we have to make it so manually (no 12-bit data type)
    if (rawData[i*2] > 0x7F)
    {  
      gCount = ~gCount + 1;
      gCount *= -1;  // Transform into negative 2's complement #
    }

    destination[i] = gCount; //Record this gCount into the 3 int array
  }
}

// Initialize the MMA8452 registers 
// See the many application notes for more info on setting all of these registers:
// http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=MMA8452Q
void initMMA8452()
{
  byte c = readRegister(WHO_AM_I);  // Read WHO_AM_I register
  if (c == 0x2A) // WHO_AM_I should always be 0x2A
  {  
    Serial.println("MMA8452Q is online...");
  }
  else
  {
    Serial.print("Could not connect to MMA8452Q: 0x");
    Serial.println(c, HEX);
    while(1) ; // Loop forever if communication doesn't happen
  }

  MMA8452Standby();  // Must be in standby to change registers

  // Set up the full scale range to 2, 4, or 8g.
  byte fsr = GSCALE;
  if(fsr > 8) fsr = 8; //Easy error check
  fsr >>= 2; // Neat trick, see page 22. 00 = 2G, 01 = 4A, 10 = 8G
  writeRegister(XYZ_DATA_CFG, fsr);

  //The default data rate is 800Hz and we don't modify it in this example code

  MMA8452Active();  // Set to active to start reading
}

// Sets the MMA8452 to standby mode. It must be in standby to change most register settings
void MMA8452Standby()
{
  byte c = readRegister(CTRL_REG1);
  writeRegister(CTRL_REG1, c & ~(0x01)); //Clear the active bit to go into standby
}

// Sets the MMA8452 to active mode. Needs to be in this mode to output data
void MMA8452Active()
{
  byte c = readRegister(CTRL_REG1);
  writeRegister(CTRL_REG1, c | 0x01); //Set the active bit to begin detection
}

// Read bytesToRead sequentially, starting at addressToRead into the dest byte array
void readRegisters(byte addressToRead, int bytesToRead, byte * dest)
{
  Wire.beginTransmission(MMA8452_ADDRESS);
  Wire.write(addressToRead);
  Wire.endTransmission(false); //endTransmission but keep the connection active

  Wire.requestFrom(MMA8452_ADDRESS, bytesToRead); //Ask for bytes, once done, bus is released by default

  while(Wire.available() < bytesToRead); //Hang out until we get the # of bytes we expect

  for(int x = 0 ; x < bytesToRead ; x++)
    dest[x] = Wire.read();    
}

// Read a single byte from addressToRead and return it as a byte
byte readRegister(byte addressToRead)
{
  Wire.beginTransmission(MMA8452_ADDRESS);
  Wire.write(addressToRead);
  Wire.endTransmission(false); //endTransmission but keep the connection active

  Wire.requestFrom(MMA8452_ADDRESS, 1); //Ask for 1 byte, once done, bus is released by default

  while(!Wire.available()) ; //Wait for the data to come back
  return Wire.read(); //Return this one byte
}

// Writes a single byte (dataToWrite) into addressToWrite
void writeRegister(byte addressToWrite, byte dataToWrite)
{
  Wire.beginTransmission(MMA8452_ADDRESS);
  Wire.write(addressToWrite);
  Wire.write(dataToWrite);
  Wire.endTransmission(); //Stop transmitting
}

majenko

Mon, 28 Oct 2013 00:04:50 +0000

Instead of Wire.read() and Wire.write() you need to use Wire.recv() and Wire.send() respectively.


txk6632

Mon, 28 Oct 2013 00:57:55 +0000

Thanks! That fixed the problem, but I am still getting the error for endTransmission(false) sketch_oct26a.cpp:145:29: error: no matching function for call to 'TwoWire::endTransmission(int)' Wire.h:51:13: note: candidate is: uint8_t TwoWire::endTransmission() This is what was in the header TwoWire(); void begin(); void begin(uint8_t); void begin(int); void beginTransmission(uint8_t); void beginTransmission(int); uint8_t endTransmission(void); uint8_t requestFrom(uint8_t, uint8_t); uint8_t requestFrom(int, int); void send(uint8_t); void send(uint8_t*, uint8_t); void send(int); void send(char*); uint8_t available(void); uint8_t receive(void); void onReceive( void ()(int) ); void onRequest( void ()(void) );


Even when I changed void to int in the ehader, and in the .cpp file, still gave me the same error, any idea what it can be?


les1943

Mon, 28 Oct 2013 09:05:20 +0000

ChipKit needs pullups on Clock and data (2k2 - 10k) ?

The man is correct...


majenko

Mon, 28 Oct 2013 11:45:05 +0000

Remove the "false" from endTransmission().

And yes, the I²C specification requires that pull-up resistors are provided on the bus. 10K is perhaps a bit too high - I'd go for 4.7K or 3.3K personally.


txk6632

Wed, 30 Oct 2013 05:50:23 +0000

Thanks for the help, the pull up resistors were not needed since the accelerometer can take in 3.3V and the chipKit supplies only 3.3V, at least that is what I'm told, but even when i try with or without the resistors, I keep getting the "Can't communicate with the accelerometer. I can see after printing lines, the WHO_AM_I register is not giving me 0x2A and there for does not work, instead it gives me FF. And I must say, this code was for arduino IDE, not the ChipKit IDE because I had to delete false from "endTransmission(false)". Not sure if that maybe the issue.


// The SparkFun breakout board defaults to 1, set to 0 if SA0 jumper on the bottom of the board is set #define MMA8452_ADDRESS 0x1D // 0x1D if SA0 is high, 0x1C if low

//Define a few of the registers that we will be accessing on the MMA8452 #define OUT_X_MSB 0x01 #define XYZ_DATA_CFG 0x0E #define WHO_AM_I 0x0D #define CTRL_REG1 0x2A

void initMMA8452() { byte c = readRegister(WHO_AM_I); // Read WHO_AM_I register

if (c == 0x2A) // WHO_AM_I should always be 0x2A {
Serial.println("MMA8452Q is online..."); } else { Serial.print("Could not connect to MMA8452Q: 0x"); Serial.println(c, HEX);

while(1) ; // Loop forever if communication doesn't happen

}

// Read a single byte from addressToRead and return it as a byte byte readRegister(byte addressToRead) { Wire.beginTransmission(MMA8452_ADDRESS); Wire.send(addressToRead); Wire.endTransmission(); //endTransmission but keep the connection active

Wire.requestFrom(MMA8452_ADDRESS, 1); //Ask for 1 byte, once done, bus is released by default

while(!Wire.available()) ; //Wait for the data to come back return Wire.receive(); //Return this one byte }


majenko

Wed, 30 Oct 2013 09:07:10 +0000

The pull-up resistors have nothing at all to do with what voltage anything runs at. They are required on [b]all[/b I²C networks as I²C uses open-collector connections, not push-pull connections. Neither masters nor clients have any facility for providing any power to the data and clock lines. That power will be provided by pull-up resistors. The value of pull-up resistor should be selected to best suit the length of trace and number of devices on the network.

Some microcontrollers allow you to use the built-in pullup resistors on the ports for pulling up the network, but those resistors are invariably too large for reliable operation in larger networks with longer traces - external resistors are recommended by default. 4.7K or 3.3K is a good value to use.

Without those resistors you will get no communication at all.


les1943

Wed, 30 Oct 2013 13:25:36 +0000

Perhaps the old chestnut I2C device address, wire.begintransmission(); shifts the given address << 1 to give a valid read / write I2C device address....


caroper

Sat, 02 Nov 2013 08:19:40 +0000

Here is a concise but well written explanation of I2C that you may find helpful in understanding the Physical Layer of the link.

[url]http://www.robot-electronics.co.uk/acatalog/I2C_Tutorial.html[/url]

The example code is in C for the PIC C Compiler so it wont work directly in MPIDE, but it gives a great understanding and starting point of how to use I2C.

Here is a great piece of code written by Nick Gammon. It scans the I2C bus and reports any slave addresses found. In valuable in testing your hardware to confirm that the devices are all able to talk. It was for Arduino so I have tweaked i slightly for ChipKIT DP32, so that it waits for the user to press a button befor sending data to the serial port. It should be safe to comment that line out on the chipKIT boards that don't use internal USB for the serial connection..

// I2C Scanner
// Written by Nick Gammon
// Date: 20th April 2011

#include &lt;Wire.h&gt;

void setup() {
  Serial.begin (115200);
  pinMode(17, INPUT);  
  
  // ChipKIT DP32: wait for serial port to connect
  // Press PROG Button to continue
  while (!digitalRead(17));

  Serial.println ();
  Serial.println ("I2C scanner. Scanning ...");
  byte count = 0;
  
  Wire.begin();
  for (byte i = 1; i &lt; 120; i++)
  {
    Wire.beginTransmission (i);
    if (Wire.endTransmission () == 0)
      {
      Serial.print ("Found address: ");
      Serial.print (i, DEC);
      Serial.print (" (0x");
      Serial.print (i, HEX);
      Serial.println (")");
      count++;
      delay (1);  // maybe unneeded?
      } // end of good response
  } // end of for loop
  Serial.println ("Done.");
  Serial.print ("Found ");
  Serial.print (count, DEC);
  Serial.println (" device(s).");
}  // end of setup

void loop() {}

Cheers Chris