summaryrefslogtreecommitdiff
path: root/peripheral/libupm/src/bmi160/bmi160.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'peripheral/libupm/src/bmi160/bmi160.cxx')
-rw-r--r--peripheral/libupm/src/bmi160/bmi160.cxx433
1 files changed, 433 insertions, 0 deletions
diff --git a/peripheral/libupm/src/bmi160/bmi160.cxx b/peripheral/libupm/src/bmi160/bmi160.cxx
new file mode 100644
index 0000000..d9f35b7
--- /dev/null
+++ b/peripheral/libupm/src/bmi160/bmi160.cxx
@@ -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.hpp"
+
+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;
+}