0

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'"

halfer
  • 19,824
  • 17
  • 99
  • 186
Cleber Marques
  • 426
  • 5
  • 20
  • 1
    Did you try linking with the library? – Ignacio Vazquez-Abrams Oct 15 '15 at 03:49
  • Looks more like a linker error than a compiler error. Check your build procedure and make sure the linker is being told where all of the libraries and object files are located. – alex.forencich Oct 15 '15 at 04:00
  • 1
    @IgnacioVazquez-Abrams, @alex.forencich, I tryied to link the library going to `Project Properties > C/C++ Build > Settings > Cross G++ Linker > Other objects` and add file path, so the whole line now looks like: `${COMMAND} -static ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}.` So the error `undefined reference to i2c_smbus_read_byte_data` desapered but other error appared: `make: *** No rule to make target '-static', needed by 'program'. Stop.` Maybe I'm not linking right? – Cleber Marques Oct 15 '15 at 15:49
  • What are the expanded linker options you are using? –  Oct 16 '15 at 00:15
  • @jdv, I don't know how to answer you... what's that? sorry for asking so elementary questions – Cleber Marques Oct 16 '15 at 01:15
  • Those ${...} references will expand into specific settings. The error you are seeing appears to be a linker error. The linker options (should) tell the linker how to find the unresolved "i2c_smbus_read_byte_data" symbol. It looks like you are using the Eclipse CDT IDE to compile and link, which implies you should be able to add the IC2 (or whatever it is called) library to the project so it is found by the project builder. –  Oct 16 '15 at 01:23
  • @jdv, after some research I still with doubts. The library is installed I dont know how to add it to the project. I'm using Eclipse IDE Version: Luna Service Release 2 (4.4.2). How may I fill in the brackets? I tryed to add as: `gcc -static /home/cleber/workspace/TCC/STRME/STRME_v2/Debug/src -o "STRME_v2.1" ./main.o` but it didnot. – Cleber Marques Oct 16 '15 at 02:22
  • 1
    Embedded development in general is a grey area for appropriate SE site, but this is clearly a **software development** question with emphasis on *software configuration* that belongs on stack overflow, not one requiring intimate knowledge of the hardware which could belong here. – Chris Stratton Oct 16 '15 at 04:05

2 Answers2

2

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>
WedaPashi
  • 3,561
  • 26
  • 42
  • I think the library is installed properly because I can open the declaration. Before I only had declared `i2c-dev.h` but i included the `i2c.h` as you suggested but still the same.I tried to link the library going to `Project Properties> C/C++ Build >Settings >Cross G++ Linker >Other objects` and add file path. Whole line looks like: `${COMMAND} -static ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}.` So the error `undefined reference to i2c_smbus_read_byte_data` desapered but this error appared: `make: *** No rule to make target '-static', needed by 'program'. Stop` – Cleber Marques Oct 15 '15 at 21:34
  • do you have any idea? – Cleber Marques Oct 15 '15 at 22:51
  • In my case, `#include ` includes `/usr/include/linux/i2c.h`, not `/usr/src/linux/include/linux/i2c.h`. How do I tell my compiler to include the latter? – AstroFloyd Oct 15 '17 at 08:01
  • The ugliest way to doing it is providing the full path to the header file that you want to include. Like, #include "/usr/src/linux/include/linux/i2c.h". The nicer way to doing it will be adding /usr/src/linux/include/linux/ into the dependencies for build. – WedaPashi Oct 16 '17 at 08:44
  • Was this really solved by playing with the includes? I would expect that it would be solved by fixing something with the linker - like pointing to the library (i2c) somehow.. – Brian Reinhold Apr 27 '22 at 11:52
0

I have seen cases where they are using "C++" to compile the code but are not using "extern C" for the includes.

"C++" likes to mess with function names and references, in those cases just adding "extern C" for all i2c headers will fix the issue.

been there, done that. hope it helps!

ERic
  • 21
  • 4