This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TivaC LSM303DLHC Sensor Driver

Other Parts Discussed in Thread: SW-EK-TM4C123GXL

I have a MinIMU9v2 that utilizes both, the LSM303DLHC accelerometer, and the L3GD20H gyroscope chips, for which there are drivers in the sensorlib folder of the drivers that I found on the TI website.  I am using a TivaC tm4c123gxl launchpad and attempting to get something going initially just to read and write to the accelerometer (the gyroscope is next) but I don't really feel confident that the way that I have begun this is the correct way of going about it.  The following code doesn't compile, and I can't seem to find any examples for this.  Any help is appreciated.

#include <stdbool.h>
#include <stdint.h>
#include "inc/hw_i2c.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/gpio.h"
#include "driverlib/i2c.h"
#include "driverlib/pin_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"

#include "sensorlib/hw_lsm303dlhc.h"
#include "sensorlib/i2cm_drv.h"
#include "sensorlib/lsm303dlhc_accel.h"

// For some reason, the compiler couldn't find these

#define GPIO_PA0_U0RX 0x00000001
#define GPIO_PA1_U0TX 0x00000401
#define GPIO_PB2_I2C0SCL 0x00010802
#define GPIO_PB3_I2C0SDA 0x00010C02

//// Registers (Happen to be identical between Acc and Gyro)
//#define CTRL_REG1 0x20
//#define OUT_X_L 0x28
//#define OUT_X_H 0x29
//#define OUT_Y_L 0x2A
//#define OUT_Y_H 0x2B
//#define OUT_Z_L 0x2C
//#define OUT_Z_H 0x2D
//
//// Configurations
//#define GYRO_CONFIG 0xFF
//#define ACC_CONFIG 0x97

// Slave addresses
#define GYRO 0xD4
#define ACC 0x32

bool g_initialized = false;
bool g_read = false;

void
InitConsole(void)
{
    UARTprintf("Init Console\n");
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    UARTStdioConfig(0, 115200, 16000000);
}

void
InitI2C(void)
{
    UARTprintf("Init I2C\n");
    // Initialize the TM4C I2C hardware for I2C0
    SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC |
    SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    GPIOPinConfigure(GPIO_PB3_I2C0SDA);
    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);

    // Initialize the bus
    I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
}


void
ReadCb(void)
{
    g_read = true;
}

void
InitCb(void)
{
    g_initialized = true;
}

int
main(void)
{
    tLSM303DLHCAccel accel;

    InitConsole();
    InitI2C();

    if (LSM303DLHCAccelInit(&accel, I2C0_BASE, ACC, &InitCb))
    {
        UARTprintf("Initializing accelerometer.\n");
        while (!g_initialized) {}
    }
    else
    {
        UARTprintf("Accelerometer initialization failed.\n");
        return(1);
    }

    uint8_t pui8Data;

    if (LSM303DLHCAccelRead(accel, LSM303DLHC_O_CTRL1, &pui8Data, 1, &ReadCb))
    {
        UARTprintf("Reading accelerometer CTRL1.\n");
        while (!g_read) {}
        g_read = false;

        UARTprintf("Read byte: %x", pui8Data);
    }
    else
    {
        UARTprintf("Accelerometer read failed.\n");
        return(1);
    }

    return(0);
}

  • Hello Robert

    What is the compilation error?

    Regards

    Amit

  • Hi Amit,

    Thanks for the quick response.  I seem to be doing a number of things wrong here.  My C is a little rusty, so please excuse any syntactical errors.  I am actually having the most trouble with figuring out what it is that I'm supposed to be passing into the API functions, how I might go about getting those values, and how these callbacks are supposed to work.

    Also, am I supposed to be registering an interrupt, or does the driver take care of that?  In digging through the driver library, I'm not really sure what I'm looking for with regards to that.

    make[2]: Entering directory `sw-ek-tm4c123gxl/examples/project'
    CC project.c
    project.c: In function 'main':
    project.c:95:5: warning: passing argument 2 of 'LSM303DLHCAccelInit' makes pointer from integer without a cast [enabled by default]
    if (LSM303DLHCAccelInit(&accel, I2C0_BASE, ACC, &InitCb))
    ^
    In file included from project.c:15:0:
    ../../sensorlib/lsm303dlhc_accel.h:120:21: note: expected 'struct tI2CMInstance *' but argument is of type 'int'
    extern uint_fast8_t LSM303DLHCAccelInit(tLSM303DLHCAccel *psInst,
    ^
    project.c:95:5: warning: passing argument 4 of 'LSM303DLHCAccelInit' from incompatible pointer type [enabled by default]
    if (LSM303DLHCAccelInit(&accel, I2C0_BASE, ACC, &InitCb))
    ^
    In file included from project.c:15:0:
    ../../sensorlib/lsm303dlhc_accel.h:120:21: note: expected 'void (*)(void *, uint_fast8_t)' but argument is of type 'void (*)(void)'
    extern uint_fast8_t LSM303DLHCAccelInit(tLSM303DLHCAccel *psInst,
    ^
    project.c:95:5: error: too few arguments to function 'LSM303DLHCAccelInit'
    if (LSM303DLHCAccelInit(&accel, I2C0_BASE, ACC, &InitCb))
    ^
    In file included from project.c:15:0:
    ../../sensorlib/lsm303dlhc_accel.h:120:21: note: declared here
    extern uint_fast8_t LSM303DLHCAccelInit(tLSM303DLHCAccel *psInst,
    ^
    project.c:108:5: error: incompatible type for argument 1 of 'LSM303DLHCAccelRead'
    if (LSM303DLHCAccelRead(accel, LSM303DLHC_O_CTRL1, &pui8Data, 1, &ReadCb))
    ^
    In file included from project.c:15:0:
    ../../sensorlib/lsm303dlhc_accel.h:125:21: note: expected 'struct tLSM303DLHCAccel *' but argument is of type 'tLSM303DLHCAccel'
    extern uint_fast8_t LSM303DLHCAccelRead(tLSM303DLHCAccel *psInst,
    ^
    project.c:108:5: warning: passing argument 5 of 'LSM303DLHCAccelRead' from incompatible pointer type [enabled by default]
    if (LSM303DLHCAccelRead(accel, LSM303DLHC_O_CTRL1, &pui8Data, 1, &ReadCb))
    ^
    In file included from project.c:15:0:
    ../../sensorlib/lsm303dlhc_accel.h:125:21: note: expected 'void (*)(void *, uint_fast8_t)' but argument is of type 'void (*)(void)'
    extern uint_fast8_t LSM303DLHCAccelRead(tLSM303DLHCAccel *psInst,
    ^
    project.c:108:5: error: too few arguments to function 'LSM303DLHCAccelRead'
    if (LSM303DLHCAccelRead(accel, LSM303DLHC_O_CTRL1, &pui8Data, 1, &ReadCb))
    ^
    In file included from project.c:15:0:
    ../../sensorlib/lsm303dlhc_accel.h:125:21: note: declared here
    extern uint_fast8_t LSM303DLHCAccelRead(tLSM303DLHCAccel *psInst,
    ^
    make[2]: *** [gcc/project.o] Error 1
    make[2]: Leaving directory `/home/escher/pyenv/embedded/tivac/sw-ek-tm4c123gxl/examples/project'
    make[1]: *** [all] Error 2
    make[1]: Leaving directory `/home/escher/pyenv/embedded/tivac/sw-ek-tm4c123gxl/examples'
    make: *** [all] Error 2

    Thanks for the help!

  • Hello Robert,

    In the function call, the first parameter is the address. Hence you need to add & to accel

    The Function call expects 6 parameters but you have given only 4

        if (LSM303DLHCAccelRead(accel, LSM303DLHC_O_CTRL1, &pui8Data, 1, &ReadCb))

    The function prototype is C:\ti\TivaWare_C_Series-2.1.0.12573\sensorlib\lsm303dlhc_accel.c and C:\ti\TivaWare_C_Series-2.1.0.12573\sensorlib\lsm303dlhc_accel.h

    You may want to look a the comments to see what are the parameters to be parsed to the function call.

    Regards

    Amit

  • Okay, I'm getting closer.  I got it to compile, but for some reason it's not putting any output with UARTprintf.  I verified that it is working by using the same initialization routine and #defines in a different program, compiling it and uploading it.  Here's my main() routine as it exists now:

    int
    main(void)
    {
        InitConsole();
        UARTprintf("Console Initialized\n");

        InitI2C();

        tLSM303DLHCAccel accel;
        tI2CMInstance psInst;

        bool params = true;

        if (LSM303DLHCAccelInit(&accel, &psInst, ACC, InitCb, &params))
        {
            UARTprintf("Initializing accelerometer.\n");
            while (!g_initialized) {}
        }
        else
        {
            UARTprintf("Accelerometer initialization failed.\n");
            return(1);
        }

        uint8_t pui8Data;

        if (LSM303DLHCAccelRead(&accel, LSM303DLHC_O_CTRL1, &pui8Data, 1, ReadCb,
                &params))
        {
            UARTprintf("Reading accelerometer CTRL1.\n");
            while (!g_read) {}
            g_read = false;

            UARTprintf("Read byte: %x", pui8Data);
        }
        else
        {
            UARTprintf("Accelerometer read failed.\n");
            return(1);
        }

        return(0);
    }

  • Hello Robert,

    So InitConsole and UARTprintf works in another program but not this? Am I correct.

    Quick things to check

    1. Close the Terminal Program and restart the same to make sure PC side is clean

    2. Check the settings in the Device Manager for the correct COM Port is being used

    3. Check in the GPIO Port A Address Space if the AFSEL, DEN and PCTL are set correctly for UART-0

    4. Check in the UART-0 Address Space if the Baud Rate is correct and the Enable Bits for UART peripheral, transmit and receive are set.

    Regards

    Amit

  • All of that appears to be in working order.  You can see the initialization in my first post (I haven't modified it at all).

    In order to troubleshoot, I went through and commented out sections of the code, and it started working.  This is the main loop that works with UARTprintf:

    int
    main(void)
    {
        InitConsole();
        UARTprintf("Console Initialized\n");

        InitI2C();

        tLSM303DLHCAccel accel;
        tI2CMInstance psInst;

        bool params = true;

    }

    As soon as I add this line, it will stop working:

        LSM303DLHCAccelInit(&accel, &psInst, ACC, InitCb, &params);

    Is it possible that I'm still initializing this incorrectly somehow?  Maybe the callback function isn't being utilized correctly?  Here it is:

    void
    InitCb(void *params, uint_fast8_t retval)
    {
        UARTprintf("InitCb: %x", retval);
        g_initialized = true;
    }

  • Hello Robert,

    Yes indeed. I am still trying to re-construct the call back routine for the LSM driver. in the meantime can your parse NULL for the call back driver and check if it works.

    Regards

    Amit

  • Okay, I went back and dug through the libraries and the API quite a bit, and I came to the realization that I needed to initialize the I2CMaster API myself and then pass that on to the initialization of the lsm303dlhc_accel.  This is my new code, and I appear to only be experiencing one problem now, and that is that the callback doesn't seem to be getting called:

    #include <stdbool.h>
    #include <stdint.h>
    #include "inc/hw_i2c.h"
    #include "inc/hw_ints.h"
    #include "inc/hw_memmap.h"
    #include "inc/hw_types.h"
    #include "driverlib/pin_map.h"
    #include "driverlib/gpio.h"
    #include "driverlib/i2c.h"
    #include "driverlib/sysctl.h"

    #include "sensorlib/hw_lsm303dlhc.h"
    #include "sensorlib/i2cm_drv.h"
    #include "sensorlib/lsm303dlhc_accel.h"

    #include "driverlib/uart.h"
    #include "utils/uartstdio.h"

    // SUBs (Happens to be identical between Acc and Gyro)
    #define CTRL_REG1 0x20
    #define OUT_X_L 0x28
    #define OUT_X_H 0x29
    #define OUT_Y_L 0x2A
    #define OUT_Y_H 0x2B
    #define OUT_Z_L 0x2C
    #define OUT_Z_H 0x2D

    // Configurations
    #define GYRO_CONFIG 0xFF
    #define ACC_CONFIG 0x97

    // Slave addresses
    #define GYRO 0xD4
    #define ACC 0x32

    #ifdef DEBUG
    void
    __error__(char *pcFilename, uint32_t ui32Line)
    {
    }
    #endif


    volatile bool g_idle = false;
    tI2CMInstance g_psInst;

    void
    InitConsole(void)
    {
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
    GPIOPinConfigure(GPIO_PA0_U0RX);
    GPIOPinConfigure(GPIO_PA1_U0TX);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
    UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
    GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    UARTStdioConfig(0, 115200, 16000000);
    }

    void
    InitI2C(void)
    {
    UARTprintf("Init I2C\n");
    // Initialize the TM4C I2C hardware for I2C0
    SysCtlClockSet(SYSCTL_SYSDIV_1 | SYSCTL_USE_OSC |
    SYSCTL_OSC_MAIN | SYSCTL_XTAL_16MHZ);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_I2C0);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
    GPIOPinConfigure(GPIO_PB2_I2C0SCL);
    GPIOPinConfigure(GPIO_PB3_I2C0SDA);
    GPIOPinTypeI2C(GPIO_PORTB_BASE, GPIO_PIN_3);
    GPIOPinTypeI2CSCL(GPIO_PORTB_BASE, GPIO_PIN_2);

    // Initialize the bus
    I2CMasterInitExpClk(I2C0_BASE, SysCtlClockGet(), false);
    }


    void
    ReadCb(void *params, uint_fast8_t ui8Status)
    {
    if(ui8Status != I2CM_STATUS_SUCCESS)
    {
    UARTprintf("Failure code: %x on ReadCb\n", ui8Status);
    }
    g_idle = true;
    }

    void
    InitDoneCb(void *params, uint_fast8_t ui8Status)
    {
    if(ui8Status != I2CM_STATUS_SUCCESS)
    {
    UARTprintf("Failure code: %x on InitDoneCb\n", ui8Status);
    } else {
    UARTprintf("InitDoneCb.");
    }
    g_idle = true;
    }

    void
    I2CIntHandler(void)
    {
    I2CMIntHandler(&g_psInst);
    }


    int
    main(void)
    {
    InitConsole();
    InitI2C();

    tLSM303DLHCAccel accel;

    // Register the interrupt handler
    I2CIntRegister(I2C0_BASE, I2CIntHandler);

    //initialize the I2C API
    I2CMInit(&g_psInst, ACC, INT_I2C0, 0xff, 0xff, 80000000);

    // Initialize the Accelerometer
    if (LSM303DLHCAccelInit(&accel, &g_psInst, ACC, InitDoneCb, 0))
    {
    UARTprintf("Initializing accelerometer.\n");
    while (!g_idle) {}
    }
    else
    {
    UARTprintf("Accelerometer initialization failed.\n");
    return(1);
    }

    uint8_t pui8Data;

    if (LSM303DLHCAccelRead(&accel, LSM303DLHC_O_CTRL1, &pui8Data, 1, ReadCb, 0))
    {
    UARTprintf("Reading accelerometer CTRL1.\n");
    while (!g_idle) {}

    UARTprintf("Read byte: %x", pui8Data);
    }
    else
    {
    UARTprintf("Accelerometer read failed.\n");
    return(1);
    }

    return(0);
    }

    When I run this, the output that I get on the console is as follows:

    Console Initialized
    Init I2C
    Initializing accelerometer.

    If this code worked as I had suspected, I should have seen an "InitDoneCb." before the "Initializing accelerometer."

    Am I doing something wrong with the initialization of the I2C interrupt?

  • Hello Robert,

    Where is g_psInst being initialized?

    Regards

    Amit

  • Hi Amit.  It's at the top.  I'm using 'g_' to denote that it's a global variable.

  • Hello Robert,

    The Issue seems to be in the I2CMInit API Call. The ACC should be the base address of the I2C Instance being used.

    I2CMInit(&g_psInst, ACC, INT_I2C0, 0xff, 0xff, 80000000);

    Regards

    Amit

  • Hi Amit,

    That definitely helped.  I looks like I am getting really close now.  The only problem now seems to be that any reads that I attempt return 0's.  Here's my console output:

    Init I2C
    InitDoneCb.
    ReadCb.
    Read byte: 0
    ReadCb.
    ReadCb.
    Read raw: 0,0,0
    ReadCb.
    Read raw: 0,0,0
    ReadCb.
    Read raw: 0,0,0
    ReadCb.
    Read raw: 0,0,0

    <continues infinitely>

    Here's the main loop as it stands now:

    int
    main(void)
    {
        InitConsole();
        InitI2C();

        tLSM303DLHCAccel accel;

        // Register the interrupt handler
        I2CIntRegister(I2C0_BASE, I2CIntHandler);

        //initialize the I2C API
        I2CMInit(&g_psInst, I2C0_BASE, INT_I2C0, 0xff, 0xff, 80000000);

        // Initialize the Accelerometer
        if (LSM303DLHCAccelInit(&accel, &g_psInst, (ACC>>1), InitDoneCb, 0))
        {
            while (!g_ready) {}
        }
        else
        {
            UARTprintf("Accelerometer initialization failed.\n");
            return(1);
        }

        uint8_t pui8Data;

        g_ready = false;
        if (LSM303DLHCAccelRead(&accel, LSM303DLHC_O_CTRL1, &pui8Data, 1, ReadCb, 0))
        {
            while (!g_ready) {}
            UARTprintf("Read byte: %x\n", pui8Data);
        }
        else
        {
            UARTprintf("Accelerometer read failed.\n");
            return(1);
        }

        volatile uint32_t ui32Loop;

        while (1) {
            for(ui32Loop = 0; ui32Loop < 200000; ui32Loop++) {}

            g_ready = false;
            if (LSM303DLHCAccelDataRead(&accel, ReadCb, 0))
            {
                while (!g_ready) {}

                uint_fast16_t x,y,z;
                LSM303DLHCAccelDataAccelGetRaw(&accel, &x, &y, &z);
                UARTprintf("Read raw: %d,%d,%d\n", x, y, z);
            }
        }

        return(0);
    }

    Any ideas on why I'm only getting 0's, regardless of whether I'm reading a register or the accelerometer values?

  • Hello Robert,

    Can you check if the SCL and SDA Pins are toggling and that the I2C Frame is coming out of the device?

    Regards

    Amit