igng igng - 2 months ago 9
Linux Question

Access device's register i2c

I recently bought a gy-521 board and I'm trying to use it with a Raspberry Pi 3 through the following connections

RPi3 | GY-521
---------------------
3.3V <-------> Vcc
GND <-------> GND
SCL <-------> SCL
SDA <-------> SDA


Using
i2cdetect -y 1
I obtain the following

0 1 2 3 4 5 6 7 8 9 a b c d e f
00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --


so the address of the device is 0x68. Reading the datasheet I found that for example the acceleration on the X axis is store in registers 3B (higher bits) and 3C (lower bits). My question is how do I access those registers?

My idea is that if I open
/dev/i2c-1
as a file descriptor, I can use the normal
read
and
write
functions. Then instead of getting data all the time, I can use
poll
in case of new available data.

I tried to use the
read
function as suggested in the documentation but that's not working (I only get zeros) and when I use
poll
it seems like there's no one on the other side and the timeout (100ms) expires. I think I should say to the chip "give me the value of the 3B register" but I can't figure how to do it.

CODE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <stdarg.h>
#include <poll.h>
#include <errno.h>

const char *filename = "/dev/i2c-1";
int DEBUG = 0;
int ADDRESS = 0x68;
struct pollfd pfd;

void debug(const char* format, ...)
{
if (DEBUG)
{
va_list argptr;
va_start(argptr, format);
fprintf(stdout, "### ");
vfprintf(stdout, format, argptr);
va_end(argptr);
}
}

void error_handler(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}

void set_debug(const char *deb_lev)
{
unsigned long num;
char *p;
errno = 0;

num = strtoul(deb_lev, &p, 10);

if (errno != 0 || *p != '\0')
error_handler("set_debug | strtoul");

DEBUG = (num > 0);
}

int open_file(const char *filename)
{
int fd;

if ((fd = open(filename, O_RDWR)) == -1)
error_handler("open_file | open");

debug("\"%s\" opened at %d\n", filename, fd);
return fd;
}

int8_t read_value(int fd)
{
debug("Reading from %d\n", fd);

int8_t num;
char *p = (char *)&num;
ssize_t size = sizeof(int8_t);
ssize_t r = 0;

while (size > 0)
{
if ((r = read(fd, p, size)) == -1)
error_handler("read_value | read");

size -= r;
p += r;
}

return num;
}

void command(uint16_t reg, int fd)
{
debug("Writing to %d\n", fd);

unsigned char reg_buf[2];
ssize_t w = 0;
ssize_t size = sizeof(unsigned char)*2;

reg_buf[0] = (reg >> 0) & 0xFF;
reg_buf[1] = (reg >> 8) & 0xFF;

if ((w = write(fd, reg_buf, size)) == -1)
error_handler("command | write");
}

void read_data_from_imu(struct pollfd *pfd)
{
int8_t val;
int p;

for (;;)
{
command(0x3b, pfd->fd);

switch (p = poll(pfd, 1, 100))
{
case -1:
error_handler("read_data_from_imu | poll");
case 0:
fprintf(stderr, "Timeout expired\n");
break;
default:
val = read_value(pfd->fd);
printf("Read: %u\n", val);
break;
}
}
}

int main(int argc, const char **argv)
{
if (argc < 2)
{
fprintf(stderr, "Usage: %s debug_flag\n", argv[0]);
return EXIT_FAILURE;
}

set_debug(argv[1]);

pfd.fd = open_file(filename);

debug("Setting slave address\n");
if (ioctl(pfd.fd, I2C_SLAVE, ADDRESS) == -1)
error_handler("main | ioctl");

read_data_from_imu(&pfd);

return EXIT_SUCCESS;
}

Answer

You might need to understand the basic of i2c.

http://www.ti.com/lit/an/slva704/slva704.pdf

3.2 Reading From a Slave On The I2C Bus

enter image description here

to read I2C register, you need to write slave addr, register addr and again slave addr and then read the data from the bus. but it is done by the driver. and the slave address is set in fd by ioctl. but you still need to write register addr.

from your link..

/* Using SMBus commands */
  res = i2c_smbus_read_word_data(file, reg);
  if (res < 0) {
    /* ERROR HANDLING: i2c transaction failed */
  } else {
    /* res contains the read word */
  }

i2c_smbus_read_word_data has reg which contains register addr. but read() is not. you need to write(reg) and then can read().

and you need to only read 1 byte unless using burst mode or something. it reads 1 byte since size is 1. but meaningless while and p++.

you are writing 3b before reading by command(0x3b, pfd->fd);. but it will be like writing

68 > 3B , 68 > 00 , 68 <

and trying to read. (where > for the read bit, 0, < for 1) maybe you need just write(pfd->fd, 0x3b, 1) instead of command.