bmi160: Initial implementation
The Bosch BMI160 is a 3-axis Accelerometer and Gyroscope. Additionally it supports an external Magnetometer, accessed through the BMI160's register interface. This driver was developed with a BMI160 "Shuttle" board, which included a BMM150 Magnetometer. The device is driven by either 1.8v or 3.3vdc. This driver incorporates the Bosch BMI160 driver code at https://github.com/BoschSensortec/BMI160_driver . While not all of the functionality of this device is supported initially, the inclusion of the Bosch driver in the source code makes it possible to support whatever features are required that the driver bosch driver itself can support. Signed-off-by: Jon Trulson <jtrulson@ics.com> Signed-off-by: Abhishek Malik <abhishek.malik@intel.com>
This commit is contained in:
committed by
Abhishek Malik
parent
b778476597
commit
e062b9b85c
433
src/bmi160/bmi160.cxx
Normal file
433
src/bmi160/bmi160.cxx
Normal file
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* Author: Jon Trulson <jtrulson@ics.com>
|
||||
* Copyright (c) 2016 Intel Corporation.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
// we have to do it the old skool way
|
||||
#include <mraa/i2c.h>
|
||||
|
||||
#include "bmi160.h"
|
||||
|
||||
extern "C" {
|
||||
#include "bosch_bmi160.h"
|
||||
}
|
||||
|
||||
// We do not need this define anyway. It conflicts with mraa::SUCCESS.
|
||||
#undef SUCCESS
|
||||
|
||||
using namespace upm;
|
||||
using namespace std;
|
||||
|
||||
static mraa_i2c_context i2cContext = NULL;
|
||||
|
||||
// Our bmi160 info structure
|
||||
struct bmi160_t s_bmi160;
|
||||
|
||||
// bus read and write functions for use with the bmi driver code
|
||||
s8 bmi160_i2c_bus_read(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
|
||||
{
|
||||
if (!i2cContext)
|
||||
{
|
||||
throw std::runtime_error(std::string(__FUNCTION__) +
|
||||
": i2c context is NULL");
|
||||
}
|
||||
|
||||
int retries = 10;
|
||||
|
||||
// There seems to be some occasional flakyness with reads when
|
||||
// moving the sensor around
|
||||
while (retries >= 0)
|
||||
{
|
||||
int rv = mraa_i2c_read_bytes_data(i2cContext, reg_addr, reg_data, cnt);
|
||||
|
||||
if (rv < 0)
|
||||
{
|
||||
usleep(100000);
|
||||
retries--;
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
throw std::runtime_error(std::string(__FUNCTION__) +
|
||||
": mraa_i2c_read_bytes_data() failed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
s8 bmi160_i2c_bus_write(u8 dev_addr, u8 reg_addr, u8 *reg_data, u8 cnt)
|
||||
{
|
||||
if (!i2cContext)
|
||||
{
|
||||
throw std::runtime_error(std::string(__FUNCTION__) +
|
||||
": i2c context is NULL");
|
||||
}
|
||||
|
||||
// FIXME fprintf(stderr, "%s: %02x: cnt %d\n", __FUNCTION__, reg_addr, cnt);
|
||||
uint8_t buffer[cnt + 1];
|
||||
|
||||
buffer[0] = reg_addr;
|
||||
for (int i=0; i<cnt; i++)
|
||||
buffer[i+1] = reg_data[i];
|
||||
|
||||
mraa_result_t rv = mraa_i2c_write(i2cContext, buffer, cnt+1);
|
||||
|
||||
if (rv != MRAA_SUCCESS)
|
||||
{
|
||||
throw std::runtime_error(std::string(__FUNCTION__) +
|
||||
": mraa_i2c_write() failed");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// delay for some milliseconds
|
||||
void bmi160_delay_ms(u32 msek)
|
||||
{
|
||||
usleep(msek * 1000);
|
||||
}
|
||||
|
||||
|
||||
BMI160::BMI160(int bus, uint8_t address)
|
||||
{
|
||||
m_addr = address;
|
||||
|
||||
// We need to use the C MRAA interface to avoid issue with C++ <-> C
|
||||
// calling convention issues, also we need a global
|
||||
// mraa_i2c_context
|
||||
|
||||
if (!(i2cContext = mraa_i2c_init(bus)))
|
||||
{
|
||||
throw std::invalid_argument(std::string(__FUNCTION__) +
|
||||
": mraa_i2c_init() failed");
|
||||
}
|
||||
|
||||
if (mraa_i2c_address(i2cContext, m_addr) != MRAA_SUCCESS)
|
||||
{
|
||||
throw std::runtime_error(std::string(__FUNCTION__) +
|
||||
": mraa_i2c_address() failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// init the driver interface functions
|
||||
s_bmi160.bus_write = bmi160_i2c_bus_write;
|
||||
s_bmi160.bus_read = bmi160_i2c_bus_read;
|
||||
s_bmi160.delay_msec = bmi160_delay_ms;
|
||||
s_bmi160.dev_addr = m_addr;
|
||||
|
||||
// Init our driver interface pointers
|
||||
bmi160_init(&s_bmi160);
|
||||
|
||||
m_accelX = 0.0;
|
||||
m_accelY = 0.0;
|
||||
m_accelZ = 0.0;
|
||||
|
||||
m_gyroX = 0.0;
|
||||
m_gyroY = 0.0;
|
||||
m_gyroZ = 0.0;
|
||||
|
||||
m_magX = 0.0;
|
||||
m_magY = 0.0;
|
||||
m_magZ = 0.0;
|
||||
|
||||
m_accelScale = 1.0;
|
||||
m_gyroScale = 1.0;
|
||||
|
||||
m_magEnabled = false;
|
||||
|
||||
if (!init())
|
||||
{
|
||||
throw std::runtime_error(std::string(__FUNCTION__) +
|
||||
": init() failed");
|
||||
}
|
||||
}
|
||||
|
||||
BMI160::~BMI160()
|
||||
{
|
||||
mraa_i2c_stop(i2cContext);
|
||||
i2cContext = NULL;
|
||||
}
|
||||
|
||||
bool BMI160::init()
|
||||
{
|
||||
// This should be interesting...
|
||||
const u32 C_BMI160_THIRTY_U8X = 30;
|
||||
|
||||
enableMagnetometer(true);
|
||||
|
||||
/*Set the accel mode as Normal write in the register 0x7E*/
|
||||
bmi160_set_command_register(ACCEL_MODE_NORMAL);
|
||||
|
||||
/* bmi160_delay_ms in ms*/
|
||||
bmi160_delay_ms(C_BMI160_THIRTY_U8X);
|
||||
|
||||
/*Set the gyro mode as Normal write in the register 0x7E*/
|
||||
bmi160_set_command_register(GYRO_MODE_NORMAL);
|
||||
|
||||
/* bmi160_delay_ms in ms*/
|
||||
bmi160_delay_ms(C_BMI160_THIRTY_U8X);
|
||||
|
||||
/* Set the accel bandwidth as OSRS4 */
|
||||
bmi160_set_accel_bw(BMI160_ACCEL_OSR4_AVG1);
|
||||
bmi160_delay_ms(BMI160_GEN_READ_WRITE_DELAY);
|
||||
|
||||
/* Set the gryo bandwidth as Normal */
|
||||
bmi160_set_gyro_bw(BMI160_GYRO_NORMAL_MODE);
|
||||
bmi160_delay_ms(BMI160_GEN_READ_WRITE_DELAY);
|
||||
|
||||
/* set gyro data rate as 200Hz*/
|
||||
bmi160_set_gyro_output_data_rate(BMI160_GYRO_OUTPUT_DATA_RATE_200HZ);
|
||||
bmi160_delay_ms(BMI160_GEN_READ_WRITE_DELAY);
|
||||
|
||||
/* set accel data rate as 200Hz*/
|
||||
bmi160_set_accel_output_data_rate(BMI160_ACCEL_OUTPUT_DATA_RATE_200HZ,
|
||||
BMI160_ACCEL_OSR4_AVG1);
|
||||
bmi160_delay_ms(BMI160_GEN_READ_WRITE_DELAY);
|
||||
|
||||
setAccelerometerScale(ACCEL_RANGE_2G);
|
||||
setGyroscopeScale(GYRO_RANGE_125);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void BMI160::update()
|
||||
{
|
||||
struct bmi160_gyro_t gyroxyz;
|
||||
struct bmi160_accel_t accelxyz;
|
||||
struct bmi160_mag_xyz_s32_t magxyz;
|
||||
|
||||
// read gyro data
|
||||
bmi160_read_gyro_xyz(&gyroxyz);
|
||||
|
||||
// read accel data
|
||||
bmi160_read_accel_xyz(&accelxyz);
|
||||
|
||||
// read mag data
|
||||
if (m_magEnabled)
|
||||
bmi160_bmm150_mag_compensate_xyz(&magxyz);
|
||||
|
||||
// read the sensor time
|
||||
u32 v_sensor_time;
|
||||
bmi160_get_sensor_time(&v_sensor_time);
|
||||
m_sensorTime = (unsigned int)v_sensor_time;
|
||||
|
||||
m_accelX = float(accelxyz.x);
|
||||
m_accelY = float(accelxyz.y);
|
||||
m_accelZ = float(accelxyz.z);
|
||||
|
||||
m_gyroX = float(gyroxyz.x);
|
||||
m_gyroY = float(gyroxyz.y);
|
||||
m_gyroZ = float(gyroxyz.z);
|
||||
|
||||
if (m_magEnabled)
|
||||
{
|
||||
m_magX = float(magxyz.x);
|
||||
m_magY = float(magxyz.y);
|
||||
m_magZ = float(magxyz.z);
|
||||
}
|
||||
}
|
||||
|
||||
void BMI160::setAccelerometerScale(ACCEL_RANGE_T scale)
|
||||
{
|
||||
s8 v_range = BMI160_ACCEL_RANGE_2G;
|
||||
// store scaling factor
|
||||
|
||||
switch (scale)
|
||||
{
|
||||
case ACCEL_RANGE_2G:
|
||||
v_range = BMI160_ACCEL_RANGE_2G;
|
||||
m_accelScale = 16384.0;
|
||||
break;
|
||||
|
||||
case ACCEL_RANGE_4G:
|
||||
v_range = BMI160_ACCEL_RANGE_4G;
|
||||
m_accelScale = 8192.0;
|
||||
break;
|
||||
|
||||
case ACCEL_RANGE_8G:
|
||||
v_range = BMI160_ACCEL_RANGE_8G;
|
||||
m_accelScale = 4096.0;
|
||||
break;
|
||||
|
||||
case ACCEL_RANGE_16G:
|
||||
v_range = BMI160_ACCEL_RANGE_16G;
|
||||
m_accelScale = 2048.0;
|
||||
break;
|
||||
|
||||
default: // should never occur, but...
|
||||
m_accelScale = 1.0; // set a safe, though incorrect value
|
||||
throw std::logic_error(string(__FUNCTION__) +
|
||||
": internal error, unsupported scale");
|
||||
break;
|
||||
}
|
||||
|
||||
bmi160_set_accel_range(v_range);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void BMI160::setGyroscopeScale(GYRO_RANGE_T scale)
|
||||
{
|
||||
u8 v_range = BMI160_GYRO_RANGE_2000_DEG_SEC;
|
||||
|
||||
// store scaling factor
|
||||
|
||||
switch (scale)
|
||||
{
|
||||
case GYRO_RANGE_125:
|
||||
v_range = BMI160_GYRO_RANGE_125_DEG_SEC;
|
||||
m_gyroScale = 262.4;
|
||||
break;
|
||||
|
||||
case GYRO_RANGE_250:
|
||||
v_range = BMI160_GYRO_RANGE_250_DEG_SEC;
|
||||
m_gyroScale = 131.2;
|
||||
break;
|
||||
|
||||
case GYRO_RANGE_500:
|
||||
v_range = BMI160_GYRO_RANGE_500_DEG_SEC;
|
||||
m_gyroScale = 65.6;
|
||||
break;
|
||||
|
||||
case GYRO_RANGE_1000:
|
||||
v_range = BMI160_GYRO_RANGE_1000_DEG_SEC;
|
||||
m_gyroScale = 32.8;
|
||||
break;
|
||||
|
||||
case GYRO_RANGE_2000:
|
||||
v_range = BMI160_GYRO_RANGE_2000_DEG_SEC;
|
||||
m_gyroScale = 16.4;
|
||||
break;
|
||||
|
||||
default: // should never occur, but...
|
||||
m_gyroScale = 1.0; // set a safe, though incorrect value
|
||||
throw std::logic_error(string(__FUNCTION__) +
|
||||
": internal error, unsupported scale");
|
||||
break;
|
||||
}
|
||||
|
||||
bmi160_set_gyro_range(v_range);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void BMI160::getAccelerometer(float *x, float *y, float *z)
|
||||
{
|
||||
if (x)
|
||||
*x = m_accelX / m_accelScale;
|
||||
|
||||
if (y)
|
||||
*y = m_accelY / m_accelScale;
|
||||
|
||||
if (z)
|
||||
*z = m_accelZ / m_accelScale;
|
||||
}
|
||||
|
||||
void BMI160::getGyroscope(float *x, float *y, float *z)
|
||||
{
|
||||
if (x)
|
||||
*x = m_gyroX / m_gyroScale;
|
||||
|
||||
if (y)
|
||||
*y = m_gyroY / m_gyroScale;
|
||||
|
||||
if (z)
|
||||
*z = m_gyroZ / m_gyroScale;
|
||||
}
|
||||
|
||||
void BMI160::getMagnetometer(float *x, float *y, float *z)
|
||||
{
|
||||
if (x)
|
||||
*x = m_magX;
|
||||
|
||||
if (y)
|
||||
*y = m_magY;
|
||||
|
||||
if (z)
|
||||
*z = m_magZ;
|
||||
}
|
||||
|
||||
float *BMI160::getAccelerometer()
|
||||
{
|
||||
float *values = new float[3]; // x, y, and then z
|
||||
|
||||
getAccelerometer(&values[0], &values[1], &values[2]);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
float *BMI160::getGyroscope()
|
||||
{
|
||||
float *values = new float[3]; // x, y, and then z
|
||||
|
||||
getGyroscope(&values[0], &values[1], &values[2]);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
float *BMI160::getMagnetometer()
|
||||
{
|
||||
float *values = new float[3]; // x, y, and then z
|
||||
|
||||
getMagnetometer(&values[0], &values[1], &values[2]);
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
void BMI160::enableMagnetometer(bool enable)
|
||||
{
|
||||
// butchered from support example
|
||||
if (!enable)
|
||||
{
|
||||
bmi160_set_bmm150_mag_and_secondary_if_power_mode(MAG_SUSPEND_MODE);
|
||||
bmi160_delay_ms(BMI160_GEN_READ_WRITE_DELAY);
|
||||
bmi160_set_if_mode(0x00);
|
||||
bmi160_delay_ms(BMI160_GEN_READ_WRITE_DELAY);
|
||||
|
||||
m_magEnabled = false;
|
||||
m_magX = 0;
|
||||
m_magY = 0;
|
||||
m_magZ = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 v_bmm_chip_id_u8 = BMI160_INIT_VALUE;
|
||||
/* Init the magnetometer */
|
||||
bmi160_bmm150_mag_interface_init(&v_bmm_chip_id_u8);
|
||||
|
||||
/* bmi160_delay_ms in ms*/
|
||||
bmi160_delay_ms(BMI160_GEN_READ_WRITE_DELAY);
|
||||
|
||||
m_magEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int BMI160::getSensorTime()
|
||||
{
|
||||
return m_sensorTime;
|
||||
}
|
||||
Reference in New Issue
Block a user