1
votes

Hi I am trying to read and write data from the i2c bus on the beagle bone black. But I keep reading 0x00 whenever I try to access the Who Am I register on a MMA84152 (or any other register for that matter), which is a constant register which means its value does not change. I am trying to read i2c-1 character driver located in /dev and I connect the sda and scl of the MMA852 lines to pins 19 and 20 on the p9 header. The sda and scl lines are both pulled high with 10 k resistors. Both pin 19 and pin 20 show 00000073 for their pin mux which means it is set for i2c functionality and slew control is slow, reciever is active, the pin is using a pull up resistor, and pull up is enabled. I ran i2cdetect -r 1 and my device shows up as 0x1d which is its correct address. I also ran i2cdump 1 0x1d and 0x2a shows up under 0x0d which is the register I am trying to read from my device and contains the correct value according to the datasheet. But when I read it it returns 0x00 for me. I am also running the latest angstrom distribution and I am logged in under root so no need for sudo.I'm lost now. Here is the code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string>

using namespace std;

int main(int argc, char **argv){
    int X_orientation=0;
    char buffer1[256];
    string i2cDeviceDriver="/dev/i2c-1";
    int fileHandler;

    if((fileHandler=open(i2cDeviceDriver.c_str(),O_RDWR))<0){
        perror("Failed To Open i2c-1 Bus");
        exit(1);
    }
    if(ioctl(fileHandler,I2C_SLAVE,0x1d)<0){
        perror("Failed to acquire i2c bus access and talk to slave");
        exit(1);
    }

    char buffer[1]={0x0D};
    if(write(fileHandler,buffer,1)!=1){                 
        perror("Failed to write byte to accelerometer");
        exit(1);
    }

    if(read(fileHandler,buffer1,1)!=1){                 
        perror("Failed to read byte from accelerometer");
        exit(1);
    }
    printf("Contents of WHO AM I is 0x%02X\n",buffer1[0]);
}
1
Did you try "> i2cget -y 1 0x1D 0x0D"? (P9-19 is SCL and P9-20 is SDA, not P8-19 and P8-20, in other words make sure the wires are tied to the header opposite to the USR_LEDS; also make sure SCL and SDA wires are in their correct order - this was the problem I had with the BMP180 sensor)TekuConcept

1 Answers

2
votes

It is likely that your I2C device does not support using separate write() and read() commands to query registers (or the equivalent i2c_smbus_write_byte() and i2c_smbus_read_byte() commands). The kernel adds a 'stop bit' to separate your messages on the wire, and some devices do not support this mode.

To confirm:

Try using the Linux i2cget command with the -c ('write byte/read byte' mode, uses separate read and write messages with a stop bit between) flag:

$ i2cget -c 1 0x1d 0x0d
Expected result: 0x00 (Incorrect response)

Then try using i2cget with the -b ('read byte data' mode, which combines the read and write messages without a stop bit) flag:

$ i2cget -b 1 0x1d 0x0d
Expected result: 0x2a (Correct response)

To resolve:

Replace your read() and write() commands with a combined i2c_smbus_read_byte_data() command if available on your system:

const char REGISTER_ID = 0x0d;
char result = i2c_smbus_read_byte_data(fileHandler, REGISTER_ID);

Alternatively (if the above is not available), you can use the the ioctl I2C_RDWR option:

const char SLAVE_ID    = 0x1d;
const char REGISTER_ID = 0x0d;

struct i2c_rdwr_ioctl_data readByteData;
struct i2c_msg messages[2];
readByteData.nmsgs = 2;
readByteData.msgs  = messages;

// Write portion (send the register we wish to read)
char request = REGISTER_ID;
i2c_msg& message = messages[0];
message.addr  = SLAVE_ID;
message.flags = 0;            // 0 = Write
message.len   = 1;
message.buf   = &request;

// Read portion (read in the value of the register)
char response;
message = messages[1];
message.addr  = SLAVE_ID;
message.flags = I2C_M_RD;
message.len   = 1;            // Number of bytes to read
message.buf   = &response;    // Where to place the result

// Submit the combined read+write message
ioctl(fileHandler, I2C_RDWR, &readByteData);

// Output the result
printf("Contents of register 0x%02x is 0x%02x\n", REGISTER_ID, response);

More information: