I2C trouble with the MPR084

Hello,
I'm trying to talk to the MPR084 using my Arduino Duemilanove. I've got the level shifting figured out (I believe), but I'm not getting any response from the MPR084. I have tried 2 of the chips with the same results.

Part of my problem is that I don't really understand exactly what the TWI subroutines do. That is, where is the 1:1 correspondence between the TWI commands and the i2c communication? When does the data start flowing during a write? How about during a read? The abstraction (Wire library) of the physical underlying process (i2c data stream) is not well documented, and I am having trouble gluing them together.

For example, one of the first Google hits for examples is http://www.neufeld.newton.ks.us/electronics/?p=241. When reading i2c data, he recommends doing

  //  Send input register address
  Wire.beginTransmission(address); Wire.send(REGISTER_INPUT); Wire.endTransmission();
  //  Connect to device and request two bytes
  Wire.beginTransmission(address); Wire.requestFrom(address, 2); // 2 bytes.
  if (Wire.available()) { data = Wire.receive(); } ; Wire.endTransmission();

...Because the documentation for his device shows that in order to read his chip, it requires: Slave Address, then Command Byte, then Slave Address again, then the Arduino receives the data bytes as specified.

But for the MPR084, to read it you do: Slave Address, then Command Byte, then receive the n data bytes.

I notice that the Wire.requestFrom() function actually requires the address as one of its arguments, but I don't think I want to do that again because it's not part of the data flow as specified by the MPR084. What I think I need to do is something roughly like:

beginTransmission(address); endTransmission(address); //without sending any data...
receiveFrom(REGISTER_INPUT, 1); // to receive 1 byte from the register I'm interested in
store=receive();

...but I've tried it and it didn't work. Any suggestions would be appreciated. Thanks for any help.
-Mike Schwager

I looked into that with the logic analyzer and looking closely at the code:

There are examples of sending, receiving, broadcasting, request/response and so on.

Plus, screenshots of the exact bits that fly back and forwards.

Thanks, Nick! I'll look into it. It sounds like exactly what I need!

  • the Gnome

Can you post the code you are using? Make sure the address you are using matches the address of the IC with address line. Can you post a schematic also so we can see if you have it wired up correctly?

You could start by checking the revision number of the device (just to see if it's working). The below sketch should the get the rev ID from address 0x14 and display it on the Serial Monitor.

#include "Wire.h"

unsigned int alt1 = 0;

void setup(){
  Wire.begin();
  Serial.begin(115200);
}

void loop(){
  Wire.beginTransmission(0x5D); //address assuming AD0 is set high
  Wire.send(0x14); //sensor information register
  Wire.endTransmission();
  Wire.requestFrom(0x5D,1); //request one byte
  alt1 = Wire.receive();
  Serial.println(alt1,DEC);
  delay(500);
}

GreyGnome:
I notice that the Wire.requestFrom() function actually requires the address as one of its arguments, but I don't think I want to do that again because it's not part of the data flow as specified by the MPR084.

Look at page 8 of their spec. The address is part of reading, that's normal for I2C. You send the slave address with the read bit set, then the requestFrom clocks out the data bytes, as required.

You're right, but the question is: How does it work with Arduino's Wire library? That's what I don't know. The example that I have shows the sequence as: Slave Address, Command, Slave Address, Data (see http://www2.neufeld.newton.ks.us/images/electronics/2008/05/26/PCA9535-figure-8.png). And that's what the receive code that I have found does. But I don't want to do that, p.8 of the data sheet says that reading the MPR084 proceeds like this, in a nutshell: Slave Address, Command, Data.

...In your example at www.gammon.com.au, say under "Request Response" about 3/4 through the page, I see that you do your sendCommand() this way:

Wire.beginTransmission (SLAVE_ADDRESS);
  Wire.send (cmd);
  Wire.endTransmission ();
  
  Wire.requestFrom (SLAVE_ADDRESS, responseSize);

...my interpretation is that the beginTransmission / send / endTransmission will actually send the Slave Address and the Command onto the wire. Now the MPR084 will send data. But the requestFrom requires the Slave Address, which would also fit into the scheme shown at http://www2.neufeld.newton.ks.us/images/electronics/2008/05/26/PCA9535-figure-8.png - It appears to me that the Slave Address will then be sent out onto the wire. But I don't want to send it again, because the MPR084 should be sending at this point, so what would I do? How to just receive the data at this point, without addressing further?

Well I have been out all day today and I have to go to bed so I will send the schematic and my code asap. Thanks for your attention.

Well I hate to say this, but it looks like the documentation is wrong, and it wouldn't be the first time, eh? Remember, the documenters are not necessarily the chip designers.

For one thing, the image you refer to looks plain wrong. On figure 12 it shows the upwards arrows indicating the data transfer from the I2C stream into the device for the entire transaction. Well, that's a write not a read. I think they copied and pasted the previous figure and changed the 0 to a 1 in the R/~W position, and nothing else (note how everything else looks the same).

They say on page 7:

Thus, a read is initiated by first configuring the MPR084’s command byte by performing a write (Figure 12). The master can now read ‘n’ consecutive bytes from the MPR084, with the first data byte being read from the register addressed by the initialized command byte.

Now I would interpret that as: send the command byte (address pointer) using a write, and switch to a read (which by the I2C protocol requires you to send the 7-bit address again, there is no other way) to get that address's contents back. Because it says "performing a write" and then "read 'n' bytes back". And that's exactly how other devices (like EEPROMs) work. You write the (memory) address and then read back the contents.

eg.

  Wire.beginTransmission (SLAVE_ADDRESS);
  Wire.send (cmd);   // send command byte (slave register)
  Wire.endTransmission ();
  
  Wire.requestFrom (SLAVE_ADDRESS, (byte) 1);   // get a data byte back (switch to read mode)
  byte value = Wire.receive ();  // get that byte from the buffer

Subject to testing of course. But I don't see how else it can work.

Another thing to keep in mind, because it's a Freescale device, is that it may require the use of a repeated start, instead of a stop bit, after sending the command. If you guys are saying there are typos in the datasheet then they too may have left this bit of information out. Most of the I2C accelerometers from Freescale will not work without sending the repeated start bit. If that is the case you will have to modify the Wire/TWI library to compensate or else it will not work. Did you try the sample sketch I posted to see if you got a response?

wayneft:
Can you post the code you are using? Make sure the address you are using matches the address of the IC with address line. Can you post a schematic also so we can see if you have it wired up correctly?

Here is a picture of my schematic.
Imgur

As I'm a working/family man, I have yet to check some of the other suggestions in these replies. I have yet to procure a USB<->Serial cable to read the output of my Arduino.

Thanks, I appreciate the attention to this problem. I will certainly post my code when I get this figured out.

wayneft:
Another thing to keep in mind, because it's a Freescale device, is that it may require the use of a repeated start, instead of a stop bit, after sending the command....

This is true! Page 5 of the Datasheet shows the repeated start! Oh boy, now I have a bit of work to do... thanks for the information!

Well, here it is, a couple of months later and a few Wiki pages. Finally, I got my MPR084 to squawk! I just got it working an hour ago, and now on my second celebratory beer I am posting my results.

One thing I determined, I wasn't going to get this to work properly without a deep understanding of the whole catastrophe. I'm not there yet, but I'm on my way. I've created two Wiki pages, one to log my deep dive into the whole thing: Arduino Playground - ATMELTWI and the other because I found the Wire library documentation lacking, so I expanded upon it: http://arduino.cc/playground/Main/WireLibraryDetailedReference

I hope those are found useful one day by someone. If I was able to see at all, it was only by standing on the shoulders of giants. I am very grateful to all who have created this interesting platform.

Without further ado, here is the code that got the MPR084 to talk to me:

#include <Wire.h>
// For reference. These are the ANALOG pins:
const int i2cSCLPin = 5; // the number of the I2C interface, SCL pin
const int i2cSDAPin = 4; // SDA pin

#define MPR084_ADDR 0x5C // assumes the MPR084 AD0 pin is low
#define regINFO 0x14 // Sensor Information Register

char c;
void setup() {
delay (1000);
Serial.begin(19200); // For reporting
Serial.print("MPR084 test.\n");
Wire.begin();
}

void loop() {
Wire.beginTransmission(MPR084_ADDR);
Wire.send(regINFO); // This would be my command byte
Wire.endTransmission();
Wire.requestFrom(MPR084_ADDR, 32); // Not sure how much data there is, let's try this.
Wire.endTransmission();
while (Wire.available()) {
c = Wire.receive();
if (c != 0) {
Serial.print(c);
}
}
Serial.print('\n');
delay(1000);
}

And here is an example of my glorious output:

VER:1_0_0Freescale,PN:MPR084,QUAL:EXTERNAL,

And that's it...!

  • GreyGnome