ci2c

Why doesn't this program in C compile? Error: undefined reference to `i2c_smbus_read_byte_data'


I've tried to compile a single program in C to read and write to a device through I2C bus but I'm getting this error:

Error: undefined reference to i2c_smbus_read_byte_data

I have already installed these packages: libi2c-dev and i2c-tools.

I'm using Ubuntu and arm-linux-gnueabi-gcc compiler (cross compile with Eclipse Luna IDE)

Here is the whole code:

/*
http://www.zerozone.it/2014/05/primi-esperimenti-con-la-beaglebone-black-collegare-10dof-via-i2c/

DOF10 I2C Test program

v0.1 - 05.05.2014

I wrote this program just to test DOF10 funcionality with my BeagleBone Black.

You can buy DOF10 module and a beagleBone from eBay with few dollars...have fun !

Written by Michele <o-zone@zerozone.it> Pinassi

BLOG @ www.zerozone.it

Feel free to use this code as you want. No any warranty, in any case: use at your own risks !

*/

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>

#define DEBUG

#define L3G4200D_I2C_ADDR 0x69
#define ADXL345_I2C_ADDR 0x53

typedef signed char s8;
typedef unsigned char u8;
typedef signed short s16;
typedef unsigned short u16;
typedef signed int s32;
typedef unsigned int u32;
typedef signed long s64;
typedef unsigned long u64;

int i2cHandle; // Bus I2C file handle

int I2C_setAddress(unsigned char deviceAddr) {
    if (ioctl(i2cHandle, I2C_SLAVE, deviceAddr) < 0) {
        printf("Error while set I2C address 0x%x: %d error\n",deviceAddr,errno);
    return -1;
    }
    return 0;
}

unsigned char I2C_readByte(unsigned char deviceAddr,unsigned char regAddr) {
    unsigned char res;

    I2C_setAddress(deviceAddr);

    res = i2c_smbus_read_byte_data(i2cHandle,regAddr);

#ifdef DEBUG
    printf("[DEBUG] 0x%x @ 0x%x => 0x%x\n",deviceAddr,regAddr,res);
#endif

    return res;
}

int I2C_writeWord(unsigned char deviceAddr, __u8 regAddr, __u16 value) {
    int res;

    I2C_setAddress(deviceAddr);

    res = i2c_smbus_write_word_data(i2cHandle, regAddr, value);
    if(res < 0) {
    printf("Error writing 0x%x to 0x%x register on i2c bus\n",value, regAddr);
    return -1;
    }
    return 1;
}

int I2C_writeByte(unsigned char deviceAddr, __u8 regAddr, __u8 value) {
    int res;

    I2C_setAddress(deviceAddr);

    res = i2c_smbus_write_byte_data(i2cHandle, regAddr, value);
    if(res < 0) {
    printf("Error writing 0x%x to 0x%x register on i2c bus\n",value, regAddr);
    return -1;
    }
    return 1;
}

// 0: 250 dps - 1: 500 dps - 2: 2000 dps
int L3G4200D_init(char fullScale) {
    if(I2C_readByte(L3G4200D_I2C_ADDR,0x0F)!=0xD3) {
    printf("ERROR communicating with L3D4200D !\n");
    return -1;
    }

    // Enable x, y, z and turn off power down:
    I2C_writeByte(L3G4200D_I2C_ADDR, 0x20, 0b00001111);

    // If you'd like to adjust/use the HPF, you can edit the line below to configure CTRL_REG2:
    I2C_writeByte(L3G4200D_I2C_ADDR, 0x21, 0b00000000);

    // Configure CTRL_REG3 to generate data ready interrupt on INT2
    // No interrupts used on INT1, if you'd like to configure INT1
    // or INT2 otherwise, consult the datasheet:
    I2C_writeByte(L3G4200D_I2C_ADDR, 0x22, 0b00001000);

    // CTRL_REG4 controls the full-scale range, among other things:
    fullScale &= 0x03;
    I2C_writeByte(L3G4200D_I2C_ADDR, 0x23, fullScale<<4);

    // CTRL_REG5 controls high-pass filtering of outputs, use it if you'd like:
    I2C_writeByte(L3G4200D_I2C_ADDR, 0x24, 0b00000000);
}

void L3G4200D_getGyroValues() {
    int x,y,z;

    x = (I2C_readByte(L3G4200D_I2C_ADDR, 0x29)&0xFF)<<8; // MSB
    x |= (I2C_readByte(L3G4200D_I2C_ADDR, 0x28)&0xFF); // LSB

    y = (I2C_readByte(L3G4200D_I2C_ADDR, 0x2B)&0xFF)<<8;
    y |= (I2C_readByte(L3G4200D_I2C_ADDR, 0x2A)&0xFF);

    z = (I2C_readByte(L3G4200D_I2C_ADDR, 0x2D)&0xFF)<<8;
    z |= (I2C_readByte(L3G4200D_I2C_ADDR, 0x2C)&0xFF);

    printf("L2D4200D = X:%d Y:%d Z:%d\n",x,y,z);
}

void ADXL345_init() {
    // Set +/- 4G range by writing the value 0x01 to the DATA_FORMAT register.
    I2C_writeByte(ADXL345_I2C_ADDR,0x31,0x01);

    // Put the ADXL345 into Measurement Mode by writing 0x08 to the POWER_CTL register.
    I2C_writeByte(ADXL345_I2C_ADDR,0x2D,0x08);
}

void ADXL345_readAccel() {
    int x,y,z;
    // each axis reading comes in 10 bit resolution, ie 2 bytes. LSB first, MSB next

    x = (I2C_readByte(ADXL345_I2C_ADDR, 0x33)&0xFF)<<8; // MSB
    x |= (I2C_readByte(ADXL345_I2C_ADDR, 0x32)&0xFF); // LSB

    y = (I2C_readByte(ADXL345_I2C_ADDR, 0x35)&0xFF)<<8; // MSB
    y |= (I2C_readByte(ADXL345_I2C_ADDR, 0x34)&0xFF); // LSB

    z = (I2C_readByte(ADXL345_I2C_ADDR, 0x37)&0xFF)<<8; // MSB
    z |= (I2C_readByte(ADXL345_I2C_ADDR, 0x36)&0xFF); // LSB

    printf("ADXL345 = X:%d Y:%d Z:%d\n",x,y,z);
}

void main(int argc, char *argv[]) {
    char filename[20];

    printf("Open I2C bus...");

    snprintf(filename, 19, "/dev/i2c-1");
    i2cHandle = open(filename, O_RDWR);
    if (i2cHandle < 0) {
        printf("Error while opening device %s: %d error\n",filename,errno);
        exit(1);
    }

    printf("OK !\nInitializing L3G4200D...");
    L3G4200D_init(2);

    printf("OK !\nInitializing ADXL345...");
    ADXL345_init();

    printf("OK !\n");

    while(1) {
    L3G4200D_getGyroValues();
    ADXL345_readAccel();
    sleep(1);
    }

    close(i2cHandle);
}

Update

I created another topic with title and more appropriate/clear details:

How to resolve the link error "undefined reference to `i2c_smbus_read_byte_data'"


Solution

  • If the library is installed properly then this looks like a linker issue by all means.
    You can verify that i2c_smbus_read_byte_data() is defined in

    Linux/drivers/i2c/i2c-core.c
    

    and has a function prototype in,

    include/linux/i2c.h
    

    So may be you could do a

    #include <linux/i2c.h>