How to interface ST L3GD20 gyroscope with the VisionSOM-6ULL
From SomLabs Wiki
How to interface ST L3GD20 gyroscope with the VisionSOM-6ULL
Description
KAmodL3GD20 is a gyroscope module with a L3GD20 chip from STMicroelectronics. It allows to measure angular velocity at ±2000 degrees per second.
Prerequisites
This tutorial is based on a pre-prepared image for VisionSOM-6ULL module. The image is available here: SoMLabs-VisionSOM-6ULL-training-02-2018-sdcard-2gb.zip.
Then you need to write the image file to microSD card. More information you can find in manuals: How to prepare SD Card with Debian 9.2 for VisionSOM-6ULL on Linux or How to prepare SD Card with Debian 9.2 for VisionSOM-6ULL on Windows. Please remember to use a proper image file: SoMLabs-VisionSOM-6ULL-training-02-2018-sdcard-2gb.zip.
In the ready-to-use OS image, the kernel driver for the gyroscope has already been enabled in the kernel config, and the device has been added to the devicetree. |
If you need more information about how to customize, build and upload Device Tree file, please visit How to customize Debian 9.2 device tree.
Hardware set-up
Connect the KAmodL3GD20 module according to the photo below:
Reading the values
To run the gyroscope, you need to go to the directory where the example code is located.
root@localhost:~# cd /root/linux-academy/2-8
There is a program in C language gyro-i2c.c that calibrates and reads L3GD20 gyroscope:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/i2c-dev.h> #include <linux/i2c.h> #include <sys/time.h> /* L3GD20 internal registers */ #define WHO_AM_I 0x0F #define CTRL_REG1 0x20 #define CTRL_REG2 0x21 #define CTRL_REG3 0x22 #define CTRL_REG4 0x23 #define CTRL_REG5 0x24 #define REFERENCE 0x25 #define OUT_TEMP 0x26 #define STATUS_REG 0x27 #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 #define FIFO_CTRL_REG 0x2E #define FIFO_SRC_REG 0x2F #define INT1_CFG 0x30 #define INT1_SRC 0x31 #define INT1_TSH_XH 0x32 #define INT1_TSH_XL 0x33 #define INT1_TSH_YH 0x34 #define INT1_TSH_YL 0x35 #define INT1_TSH_ZH 0x36 #define INT1_TSH_ZL 0x37 #define INT1_DURATION 0x38 #define GYRO_ADDR 0x6b #define AUTO_INCREMENT 0x80 /* calibration values */ int x_low = 0, y_low = 0, z_low = 0; int x_high = 0, y_high = 0, z_high = 0; /* * get_timestamp */ static unsigned long get_timestamp () { struct timeval tv; gettimeofday (&tv,NULL); return tv.tv_sec * 1000000UL + tv.tv_usec; } /* * gyro_init */ static int gyro_init (int i2c_fd) { unsigned char init_seq[6]; init_seq[0] = (CTRL_REG1 | AUTO_INCREMENT); init_seq[1] = 0xCF; /* CTRL_REG1: normal mode, xyz enable */ init_seq[2] = 0x01; /* CTRL_REG2: <default value> */ init_seq[3] = 0x00; /* CTRL_REG3: <default value> */ init_seq[4] = 0x80; /* CTRL_REG4: 250dps, Block Data Update */ init_seq[5] = 0x02; /* CTRL_REG5: <default value> */ if (write (i2c_fd, init_seq, 6) != 6) return -1; return 0; } /* * gyro_get_status */ static int gyro_get_status (int i2c_fd) { unsigned char reg_addr = STATUS_REG; unsigned char reg_data[1]; int ret; struct i2c_msg messages[] = { { GYRO_ADDR, /* slave address */ 0, /* flags: 0 */ sizeof(reg_addr), /* transfer size */ ®_addr /* data */ }, { GYRO_ADDR, /* slave adddress */ I2C_M_RD, /* flags: READ */ sizeof(reg_data), /* transfer size */ reg_data /* data */ } }; struct i2c_rdwr_ioctl_data packets = { messages, /* address of messages */ sizeof(messages) / sizeof(struct i2c_msg) /* number of messages */ }; ret = ioctl (i2c_fd, I2C_RDWR, &packets); if (ret < 0) return ret; return (reg_data[0] & (1 << 3)); } /* * gyro_get_xyz */ static int gyro_get_xyz (int i2c_fd, float *x, float *y, float *z) { unsigned char reg_addr = OUT_X_L | AUTO_INCREMENT; unsigned char reg_data[6]; int ret; struct i2c_msg messages[] = { { GYRO_ADDR, /* slave address */ 0, /* flags: 0 */ sizeof(reg_addr), /* transfer size */ ®_addr /* data */ }, { GYRO_ADDR, /* slave adddress */ I2C_M_RD, /* flags: READ */ sizeof(reg_data), /* transfer size */ reg_data /* data */ } }; struct i2c_rdwr_ioctl_data packets = { messages, /* address of messages */ sizeof(messages) / sizeof(struct i2c_msg) /* number of messages */ }; ret = ioctl (i2c_fd, I2C_RDWR, &packets); if (ret < 0) return ret; *x = (short) (reg_data[0] + ((short)reg_data[1] << 8)); *y = (short) (reg_data[2] + ((short)reg_data[3] << 8)); *z = (short) (reg_data[4] + ((short)reg_data[5] << 8)); return 0; } /* * gyro_calib */ static int gyro_calib (int i2c_fd) { float x_raw, y_raw, z_raw; int ret; for (int i =0 ; i < 200 ; i++) { /* check when a new set of data is available */ while (!gyro_get_status (i2c_fd)); /* read xyz raw values */ ret = gyro_get_xyz (i2c_fd, &x_raw, &y_raw, &z_raw); if (ret < 0) break; /* thresholds for x-axis */ if (x_raw > x_high) x_high = x_raw; else if (x_raw < x_low) x_low = x_raw; /* thresholds for y-axis */ if (y_raw > y_high) y_high = y_raw; else if (y_raw < y_low) y_low = y_raw; /* thresholds for z-axis */ if (z_raw > z_high) z_high = z_raw; else if (z_raw < z_low) z_low = z_raw; } return ret; } /* * main */ int main (void) { int i2c_fd, ret; float x_raw, y_raw, z_raw; unsigned long pt = 0; /* actual angles */ float angX = 0; float angY = 0; float angZ = 0; /* previous angles for calculation */ float p_angX = 0; float p_angY = 0; float p_angZ = 0; /* open i2c device */ i2c_fd = open ("/dev/i2c-1", O_RDWR); if (i2c_fd < 0) { printf ("Failed to open the i2c bus\n"); return EXIT_FAILURE; } /* set slave address */ ret = ioctl (i2c_fd, I2C_SLAVE, GYRO_ADDR); if (ret < 0) { printf ("Failed to acquire bus access and/or talk to slave\n"); goto exit; } /* gyro init */ ret = gyro_init (i2c_fd); if (ret < 0) { printf ("gyro_init error!\n"); goto exit; } /* gyro calib */ puts ("Calibration..."); ret = gyro_calib (i2c_fd); if (ret < 0) { printf ("gyro_calib error!\n"); goto exit; } /* infinite loop */ while (1) { /* check when a new set of data is available */ while (!gyro_get_status (i2c_fd)); /* read xyz raw values */ gyro_get_xyz (i2c_fd, &x_raw, &y_raw, &z_raw); /* get timestamp */ unsigned long ct = get_timestamp(); if (pt == 0) { pt = get_timestamp(); continue; } float dt = (float) (ct - pt) / 1000000.0; pt = get_timestamp(); /* x-axis */ if (x_raw >= x_high || x_raw <= x_low) { angX += ((p_angX + (x_raw * 0.00875))/2) * dt; p_angX = x_raw * 0.00875; } else p_angX = 0; /* y-axis */ if (y_raw >= y_high || y_raw <= y_low) { angY += ((p_angY + (y_raw * 0.00875))/2) * dt; p_angY = y_raw * 0.00875; } else p_angY = 0; /* z-axis */ if (z_raw >= z_high || z_raw <= z_low) { angZ += ((p_angZ + (z_raw * 0.00875))/2) * dt; p_angZ = z_raw * 0.00875; } else p_angZ = 0; printf ("%.1f %.1f %.1f\n", angX, angY, angZ); fflush (stdout); } exit: return EXIT_FAILURE; } root@localhost:~/linux-academy/2-8# gh || z_raw <= z_low) -bash: syntax error near unexpected token `)' , angX, angY, angZ); fflush (stdout); } exit: return EXIT_FAILURE; }
To compile the program just type:
gcc gyro-i2c.c -o gyro-i2c
Now, it's time to run the application. The readings of three axis values from L3HGD20 chip will be displayed after executing:
root@localhost:~/linux-academy/2-8# ./gyro-i2c
Any move of the sensor board will be related with the readings changes: