2

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 <[email protected]> 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'"

8
  • 1
    Did you try linking with the library? Commented Oct 15, 2015 at 3: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. Commented Oct 15, 2015 at 4: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? Commented Oct 15, 2015 at 15:49
  • What are the expanded linker options you are using? Commented Oct 16, 2015 at 0:15
  • 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. Commented Oct 16, 2015 at 4:05

4 Answers 4

3

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>
Sign up to request clarification or add additional context in comments.

5 Comments

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
do you have any idea?
In my case, #include <linux/i2c.h> includes /usr/include/linux/i2c.h, not /usr/src/linux/include/linux/i2c.h. How do I tell my compiler to include the latter?
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.
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..
3

My error message was slightly different:

main.cc:(.text.startup+0x44): undefined reference to `i2c_smbus_read_byte_data(int, unsigned char)'

The fact that parameter types were mentioned made me think that the linker was looking for a function with a C++-mangled name, while the library obviously supplies a C function.

I had to put some includes inside an extern "C" (didn't try to figure out if I can leave out one of the two, anyway it doesn't hurt to keep both inside):

extern "C" {
  #include <linux/i2c-dev.h>
  #include <i2c/smbus.h>
}

Furthermore -li2c is needed to actually link the I2C library.

Comments

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!

Comments

0

On a Raspberry Pi Os in 2024, I could compile a program having i2c_smbus_xxx functions in this way:

  1. adding <i2c/smbus.h> include
  2. using "-li2c" compiler (linker) flag.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.