/** * IIC_UTILS.c * * Utility functions for using I2C on a Diligent Zybo board. * Supports the SparkFun MPU9150 and the LiDAR lite v2 sensor */ #include <stdio.h> #include <math.h> #include "iic_utils.h" static struct I2CDriver *i2c; static struct SystemDriver *sys; double magX_correction = -1, magY_correction, magZ_correction; void iic_set_globals(struct I2CDriver *given_i2c, struct SystemDriver *given_system) { i2c = given_i2c; sys = given_system; } int iic0_mpu9150_start(){ // Device Reset & Wake up iic0_mpu9150_write(0x6B, 0x80); sys->sleep(sys, 5000); // Set clock reference to Z Gyro iic0_mpu9150_write(0x6B, 0x03); // Configure Digital Low/High Pass filter iic0_mpu9150_write(0x1A,0x06); // Level 6 low pass on gyroscope // Configure Gyro to 2000dps, Accel. to +/-8G iic0_mpu9150_write(0x1B, 0x18); iic0_mpu9150_write(0x1C, 0x10); // Enable I2C bypass for AUX I2C (Magnetometer) iic0_mpu9150_write(0x37, 0x02); // Setup Mag iic0_mpu9150_write(0x37, 0x02); //INT_PIN_CFG -- INT_LEVEL=0 ; INT_OPEN=0 ; LATCH_INT_EN=0 ; INT_RD_CLEAR=0 ; FSYNC_INT_LEVEL=0 ; FSYNC_INT_EN=0 ; I2C_BYPASS_EN=0 ; CLKOUT_EN=0 sys->sleep(sys, 100000); int i; gam_t temp_gam; // Do about 20 reads to warm up the device for(i=0; i < 20; ++i){ if(iic0_mpu9150_read_gam(&temp_gam) == -1){ return -1; } sys->sleep(sys, 1000); } return 0; } void iic0_mpu9150_stop(){ //Put MPU to sleep iic0_mpu9150_write(0x6B, 0b01000000); } void iic0_mpu9150_write(u8 register_addr, u8 data){ u16 device_addr = MPU9150_DEVICE_ADDR; u8 buf[] = {register_addr, data}; // Check if within register range if(register_addr < 0 || register_addr > 0x75){ return; } if(register_addr <= 0x12){ device_addr = MPU9150_COMPASS_ADDR; } i2c->write(i2c, device_addr, buf, 2); } void iic0_mpu9150_read(u8* recv_buffer, u8 register_addr, int size){ u16 device_addr = MPU9150_DEVICE_ADDR; u8 buf[] = {register_addr}; // Check if within register range if(register_addr < 0 || register_addr > 0x75){ } // Set device address to the if 0x00 <= register address <= 0x12 if(register_addr <= 0x12){ device_addr = MPU9150_COMPASS_ADDR; } i2c->write(i2c, device_addr, buf, 1); i2c->read(i2c, device_addr, recv_buffer, size); } void iic0_mpu9150_calc_mag_sensitivity(){ u8 buf[3]; u8 ASAX, ASAY, ASAZ; // Quickly read from the factory ROM to get correction coefficents iic0_mpu9150_write(0x0A, 0x0F); sys->sleep(sys, 10000); // Read raw adjustment values iic0_mpu9150_read(buf, 0x10,3); ASAX = buf[0]; ASAY = buf[1]; ASAZ = buf[2]; // Set the correction coefficients magX_correction = (ASAX-128)*0.5/128 + 1; magY_correction = (ASAY-128)*0.5/128 + 1; magZ_correction = (ASAZ-128)*0.5/128 + 1; } void iic0_mpu9150_read_mag(gam_t* gam){ u8 mag_data[6]; i16 raw_magX, raw_magY, raw_magZ; // Grab calibrations if not done already if(magX_correction == -1){ iic0_mpu9150_calc_mag_sensitivity(); } // Set Mag to single read mode iic0_mpu9150_write(0x0A, 0x01); sys->sleep(sys, 10000); mag_data[0] = 0; // Keep checking if data is ready before reading new mag data while(mag_data[0] == 0x00){ iic0_mpu9150_read(mag_data, 0x02, 1); } // Get mag data iic0_mpu9150_read(mag_data, 0x03, 6); raw_magX = (mag_data[1] << 8) | mag_data[0]; raw_magY = (mag_data[3] << 8) | mag_data[2]; raw_magZ = (mag_data[5] << 8) | mag_data[4]; // Set magnetometer data to output gam->mag_x = raw_magX * magX_correction; gam->mag_y = raw_magY * magY_correction; gam->mag_z = raw_magZ * magZ_correction; } /** * Get Gyro Accel Mag (GAM) information */ int iic0_mpu9150_read_gam(gam_t* gam) { i16 raw_accel_x, raw_accel_y, raw_accel_z; i16 gyro_x, gyro_y, gyro_z; u8 sensor_data[ACCEL_GYRO_READ_SIZE] = {}; // We should only get mag_data ~10Hz //Xint8 mag_data[6] = {}; //readHandler = iic0_read_bytes(sensor_data, ACCEL_GYRO_BASE_ADDR, ACCEL_GYRO_READ_SIZE); iic0_mpu9150_read(sensor_data, ACCEL_GYRO_BASE_ADDR, ACCEL_GYRO_READ_SIZE); //Calculate accelerometer data raw_accel_x = sensor_data[ACC_X_H] << 8 | sensor_data[ACC_X_L]; raw_accel_y = sensor_data[ACC_Y_H] << 8 | sensor_data[ACC_Y_L]; raw_accel_z = sensor_data[ACC_Z_H] << 8 | sensor_data[ACC_Z_L]; // put in G's gam->accel_x = (raw_accel_x / 4096.0) + ACCEL_X_BIAS; // 4,096 is the gain per LSB of the measurement reading based on a configuration range of +-8g gam->accel_y = (raw_accel_y / 4096.0) + ACCEL_Y_BIAS; gam->accel_z = (raw_accel_z / 4096.0) + ACCEL_Z_BIAS; //Get X and Y angles // javey: this assigns accel_(pitch/roll) in units of radians gam->accel_pitch = atan(gam->accel_x / sqrt(gam->accel_y*gam->accel_y + gam->accel_z*gam->accel_z)); gam->accel_roll = -atan(gam->accel_y / sqrt(gam->accel_x*gam->accel_x + gam->accel_z*gam->accel_z)); // negative because sensor board is upside down //Convert gyro data to rate (we're only using the most 12 significant bits) gyro_x = (sensor_data[GYR_X_H] << 8) | (sensor_data[GYR_X_L]); //* G_GAIN; gyro_y = (sensor_data[GYR_Y_H] << 8 | sensor_data[GYR_Y_L]);// * G_GAIN; gyro_z = (sensor_data[GYR_Z_H] << 8 | sensor_data[GYR_Z_L]);// * G_GAIN; //Get the number of degrees //javey: converted to radians to following SI units gam->gyro_xVel_p = ((gyro_x / GYRO_SENS) * DEG_TO_RAD) + GYRO_X_BIAS; gam->gyro_yVel_q = ((gyro_y / GYRO_SENS) * DEG_TO_RAD) + GYRO_Y_BIAS; gam->gyro_zVel_r = ((gyro_z / GYRO_SENS) * DEG_TO_RAD) + GYRO_Z_BIAS; return 0; } ////////////////////////////////////////////////// // LIDAR ////////////////////////////////////////////////// int iic0_lidarlite_write(u8 register_addr, u8 data) { u8 buf[] = {register_addr, data}; return i2c->write(i2c, LIDARLITE_DEVICE_ADDR, buf, 2); } int iic0_lidarlite_read(u8* recv_buffer, u8 register_addr, int size) { u8 buf[] = {register_addr}; int status = 0; status |= i2c->write(i2c, LIDARLITE_DEVICE_ADDR, buf, 1); status |= i2c->read(i2c, LIDARLITE_DEVICE_ADDR, recv_buffer, size); return status; } int iic0_lidarlite_init() { int status = 0; // Device Reset & Wake up with default settings status = iic0_lidarlite_write(0x00, 0x00); sys->sleep(sys, 15000); // Enable Free Running Mode and distance measurements with correction status |= iic0_lidarlite_write(0x11, 0xff); status |= iic0_lidarlite_write(0x00, 0x04); return status; } int iic0_lidarlite_sleep() { return iic0_lidarlite_write(0x65, 0x84); } int iic0_lidarlite_read_distance(lidar_t *lidar) { u8 buf[2]; int status = 0; // Read the sensor value status = iic0_lidarlite_read(buf, 0x8f, 2); lidar->distance_cm = buf[0] << 8 | buf[1]; return status; }