Search code examples
csegmentation-faultraspberry-piaccelerometer

C segmentation fault in interfacing with accelerometer


I am interfacing the Raspberry Pi with an accelerometer using the code:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <linux/i2c-dev.h>
#include "LSM9DS0.h"

void readBlock(uint8_t command, uint8_t size, uint8_t *data);
void selectDevice(int file, int addr);
void readACC(int  *a);
void writeAccReg(uint8_t reg, uint8_t value);
void enableIMU();
int file;

void  readBlock(uint8_t command, uint8_t size, uint8_t *data)
{
    int result = i2c_smbus_read_i2c_block_data(file, command, size, data);
    if (result != size)
    {
        printf("Failed to read block from I2C.");
        exit(1);
    }
}

void selectDevice(int file, int addr)
{
        if (ioctl(file, I2C_SLAVE, addr) < 0) {
         printf("Failed to select I2C device.");
        }
}

void readACC(int  *a)
{
        printf("entered readACC");
        uint8_t block[6];
        selectDevice(file,ACC_ADDRESS);
        readBlock(0x80 | OUT_X_L_A, sizeof(block), block);

        *a = (int16_t)(block[0] | block[1] << 8);
        *(a+1) = (int16_t)(block[2] | block[3] << 8);
        *(a+2) = (int16_t)(block[4] | block[5] << 8);        

        printf("X axis: %4.2f, Y axis: %4.2f, Z axis: %4.2f", a, a+1, a+2);
}

void writeAccReg(uint8_t reg, uint8_t value)
{
    selectDevice(file,ACC_ADDRESS);
  int result = i2c_smbus_write_byte_data(file, reg, value);
    if (result == -1)
    {
        printf ("Failed to write byte to I2C Acc.");
        exit(1);
    }
}

void enableIMU()
{

    __u16 block[I2C_SMBUS_BLOCK_MAX];

        int res, bus,  size;


        char filename[20];
        sprintf(filename, "/dev/i2c-%d", 1);
        file = open(filename, O_RDWR);
        if (file<0) {
        printf("Unable to open I2C bus!");
                exit(1);
        }

        // Enable accelerometer.
        writeAccReg(CTRL_REG1_XM, 0b10010111); //  z,y,x axis enabled, continuos update,  100Hz data rate
        writeAccReg(CTRL_REG2_XM, 0b00100000); // +/- 16G full scale
        printf("Accelerometer is enabled\n");
}

int main(){
enableIMU();
printf("fine\n");
int *a;
readAcc(a);  
return 0; 
}

My output looks like this:

 Accelerometer is enabled 
 fine 
 Segmentation fault

Based on the output, the function enableIMU is working fine, the output also shows "fine" which is right before entering readACC, but I never enter readACC because "entered readACC" doesn't get printed. Instead, I get a segmentation fault.

Do you know what I'm doing wrong here? I would really appreciate your help!


Solution

  • The problem in your code is:

    int *a;
    readAcc(a); 
    
    …
    
    *a = (int16_t)(block[0] | block[1] << 8);
    

    In the first line, you declare a pointer to an int. This pointer points somewhere, since it is not initialized. Then, in the last line, you write to this somewhere in memory.

    Instead, you should write this:

    int16_t a[3];
    readAcc(a);
    

    That way, you define the storage where the function readAcc can write to. The parameter to the readAcc function should not be a pointer to int, but a pointer to int16_t, since you use it as such.

    Inside readAcc, you can then write:

    a[0] = …;
    a[1] = …;
    a[2] = …;
    

    Instead of the array, you could also define a struct xyz { int16_t x, y, z; }, which describes more accurately what the code does.

    I would write it like this:

    struct xyz {
        int16_t x, y, z;
    };
    
    void readACC(struct xyz *coord)
    {
        printf("entered readACC\n");
        selectDevice(file, ACC_ADDRESS);
    
        uint8_t block[6];
        readBlock(0x80 | OUT_X_L_A, sizeof(block), block);
    
        coord->x = (int16_t)(block[0] | block[1] << 8);
        coord->y = (int16_t)(block[2] | block[3] << 8);
        coord->z = (int16_t)(block[4] | block[5] << 8);        
    
        printf("X axis: %4.2f, Y axis: %4.2f, Z axis: %4.2f\n", 
            coord->x / 256.0, coord->y / 256.0, coord->z / 256.0);
    }
    
    • I defined the struct xyz to make the code self-explaining.
    • I moved the selectDevice call above the block declaration, since the block is not needed for that.
    • I replaced the a parameter with a better name, coord, which has an appropriate data type.
    • I corrected the arguments to the printf call to match the %f conversion specifier. (I hope the factor 256.0 is correct.) When you try to print an int using %f, the behavior is undefined (which is the worst thing that can happen in a C program).
    • I added newlines to the end of the printf format strings.