Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place. Commercial Alternative to JupyterHub.
Path: blob/master/libraries/AP_Compass/AP_Compass_LSM303D.cpp
Views: 1798
/*1This program is free software: you can redistribute it and/or modify2it under the terms of the GNU General Public License as published by3the Free Software Foundation, either version 3 of the License, or4(at your option) any later version.56This program is distributed in the hope that it will be useful,7but WITHOUT ANY WARRANTY; without even the implied warranty of8MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the9GNU General Public License for more details.1011You should have received a copy of the GNU General Public License12along with this program. If not, see <http://www.gnu.org/licenses/>.13*/1415#include "AP_Compass_LSM303D.h"1617#if AP_COMPASS_LSM303D_ENABLED1819#include <utility>2021#include <AP_HAL/AP_HAL.h>22#include <AP_Math/AP_Math.h>2324extern const AP_HAL::HAL &hal;2526#if CONFIG_HAL_BOARD == HAL_BOARD_LINUX27#include <AP_HAL_Linux/GPIO.h>28#endif2930#ifndef LSM303D_DRDY_M_PIN31#define LSM303D_DRDY_M_PIN -132#endif3334/* SPI protocol address bits */35#define DIR_READ (1<<7)36#define DIR_WRITE (0<<7)37#define ADDR_INCREMENT (1<<6)3839/* register addresses: A: accel, M: mag, T: temp */40#define ADDR_WHO_AM_I 0x0F41#define WHO_I_AM 0x494243#define ADDR_OUT_TEMP_L 0x0544#define ADDR_OUT_TEMP_H 0x0645#define ADDR_STATUS_M 0x0746#define ADDR_OUT_X_L_M 0x0847#define ADDR_OUT_X_H_M 0x0948#define ADDR_OUT_Y_L_M 0x0A49#define ADDR_OUT_Y_H_M 0x0B50#define ADDR_OUT_Z_L_M 0x0C51#define ADDR_OUT_Z_H_M 0x0D5253#define ADDR_INT_CTRL_M 0x1254#define ADDR_INT_SRC_M 0x1355#define ADDR_REFERENCE_X 0x1c56#define ADDR_REFERENCE_Y 0x1d57#define ADDR_REFERENCE_Z 0x1e5859#define ADDR_STATUS_A 0x2760#define ADDR_OUT_X_L_A 0x2861#define ADDR_OUT_X_H_A 0x2962#define ADDR_OUT_Y_L_A 0x2A63#define ADDR_OUT_Y_H_A 0x2B64#define ADDR_OUT_Z_L_A 0x2C65#define ADDR_OUT_Z_H_A 0x2D6667#define ADDR_CTRL_REG0 0x1F68#define ADDR_CTRL_REG1 0x2069#define ADDR_CTRL_REG2 0x2170#define ADDR_CTRL_REG3 0x2271#define ADDR_CTRL_REG4 0x2372#define ADDR_CTRL_REG5 0x2473#define ADDR_CTRL_REG6 0x2574#define ADDR_CTRL_REG7 0x267576#define ADDR_FIFO_CTRL 0x2e77#define ADDR_FIFO_SRC 0x2f7879#define ADDR_IG_CFG1 0x3080#define ADDR_IG_SRC1 0x3181#define ADDR_IG_THS1 0x3282#define ADDR_IG_DUR1 0x3383#define ADDR_IG_CFG2 0x3484#define ADDR_IG_SRC2 0x3585#define ADDR_IG_THS2 0x3686#define ADDR_IG_DUR2 0x3787#define ADDR_CLICK_CFG 0x3888#define ADDR_CLICK_SRC 0x3989#define ADDR_CLICK_THS 0x3a90#define ADDR_TIME_LIMIT 0x3b91#define ADDR_TIME_LATENCY 0x3c92#define ADDR_TIME_WINDOW 0x3d93#define ADDR_ACT_THS 0x3e94#define ADDR_ACT_DUR 0x3f9596#define REG1_RATE_BITS_A ((1<<7) | (1<<6) | (1<<5) | (1<<4))97#define REG1_POWERDOWN_A ((0<<7) | (0<<6) | (0<<5) | (0<<4))98#define REG1_RATE_3_125HZ_A ((0<<7) | (0<<6) | (0<<5) | (1<<4))99#define REG1_RATE_6_25HZ_A ((0<<7) | (0<<6) | (1<<5) | (0<<4))100#define REG1_RATE_12_5HZ_A ((0<<7) | (0<<6) | (1<<5) | (1<<4))101#define REG1_RATE_25HZ_A ((0<<7) | (1<<6) | (0<<5) | (0<<4))102#define REG1_RATE_50HZ_A ((0<<7) | (1<<6) | (0<<5) | (1<<4))103#define REG1_RATE_100HZ_A ((0<<7) | (1<<6) | (1<<5) | (0<<4))104#define REG1_RATE_200HZ_A ((0<<7) | (1<<6) | (1<<5) | (1<<4))105#define REG1_RATE_400HZ_A ((1<<7) | (0<<6) | (0<<5) | (0<<4))106#define REG1_RATE_800HZ_A ((1<<7) | (0<<6) | (0<<5) | (1<<4))107#define REG1_RATE_1600HZ_A ((1<<7) | (0<<6) | (1<<5) | (0<<4))108109#define REG1_BDU_UPDATE (1<<3)110#define REG1_Z_ENABLE_A (1<<2)111#define REG1_Y_ENABLE_A (1<<1)112#define REG1_X_ENABLE_A (1<<0)113114#define REG2_ANTIALIAS_FILTER_BW_BITS_A ((1<<7) | (1<<6))115#define REG2_AA_FILTER_BW_773HZ_A ((0<<7) | (0<<6))116#define REG2_AA_FILTER_BW_194HZ_A ((0<<7) | (1<<6))117#define REG2_AA_FILTER_BW_362HZ_A ((1<<7) | (0<<6))118#define REG2_AA_FILTER_BW_50HZ_A ((1<<7) | (1<<6))119120#define REG2_FULL_SCALE_BITS_A ((1<<5) | (1<<4) | (1<<3))121#define REG2_FULL_SCALE_2G_A ((0<<5) | (0<<4) | (0<<3))122#define REG2_FULL_SCALE_4G_A ((0<<5) | (0<<4) | (1<<3))123#define REG2_FULL_SCALE_6G_A ((0<<5) | (1<<4) | (0<<3))124#define REG2_FULL_SCALE_8G_A ((0<<5) | (1<<4) | (1<<3))125#define REG2_FULL_SCALE_16G_A ((1<<5) | (0<<4) | (0<<3))126127#define REG5_ENABLE_T (1<<7)128129#define REG5_RES_HIGH_M ((1<<6) | (1<<5) | (1<<7))130#define REG5_RES_LOW_M ((0<<6) | (0<<5))131132#define REG5_RATE_BITS_M ((1<<4) | (1<<3) | (1<<2))133#define REG5_RATE_3_125HZ_M ((0<<4) | (0<<3) | (0<<2))134#define REG5_RATE_6_25HZ_M ((0<<4) | (0<<3) | (1<<2))135#define REG5_RATE_12_5HZ_M ((0<<4) | (1<<3) | (0<<2))136#define REG5_RATE_25HZ_M ((0<<4) | (1<<3) | (1<<2))137#define REG5_RATE_50HZ_M ((1<<4) | (0<<3) | (0<<2))138#define REG5_RATE_100HZ_M ((1<<4) | (0<<3) | (1<<2))139#define REG5_RATE_DO_NOT_USE_M ((1<<4) | (1<<3) | (0<<2))140141#define REG6_FULL_SCALE_BITS_M ((1<<6) | (1<<5))142#define REG6_FULL_SCALE_2GA_M ((0<<6) | (0<<5))143#define REG6_FULL_SCALE_4GA_M ((0<<6) | (1<<5))144#define REG6_FULL_SCALE_8GA_M ((1<<6) | (0<<5))145#define REG6_FULL_SCALE_12GA_M ((1<<6) | (1<<5))146147#define REG7_CONT_MODE_M ((0<<1) | (0<<0))148149#define INT_CTRL_M 0x12150#define INT_SRC_M 0x13151152#define LSM303D_MAG_DEFAULT_RANGE_GA 2153#define LSM303D_MAG_DEFAULT_RATE 100154155AP_Compass_LSM303D::AP_Compass_LSM303D(AP_HAL::OwnPtr<AP_HAL::Device> dev)156: _dev(std::move(dev))157{158}159160AP_Compass_Backend *AP_Compass_LSM303D::probe(AP_HAL::OwnPtr<AP_HAL::Device> dev,161enum Rotation rotation)162{163if (!dev) {164return nullptr;165}166AP_Compass_LSM303D *sensor = NEW_NOTHROW AP_Compass_LSM303D(std::move(dev));167if (!sensor || !sensor->init(rotation)) {168delete sensor;169return nullptr;170}171172return sensor;173}174175uint8_t AP_Compass_LSM303D::_register_read(uint8_t reg)176{177uint8_t val = 0;178179reg |= DIR_READ;180_dev->read_registers(reg, &val, 1);181182return val;183}184185bool AP_Compass_LSM303D::_block_read(uint8_t reg, uint8_t *buf, uint32_t size)186{187reg |= DIR_READ | ADDR_INCREMENT;188return _dev->read_registers(reg, buf, size);189}190191void AP_Compass_LSM303D::_register_write(uint8_t reg, uint8_t val)192{193_dev->write_register(reg, val);194}195196void AP_Compass_LSM303D::_register_modify(uint8_t reg, uint8_t clearbits, uint8_t setbits)197{198uint8_t val;199200val = _register_read(reg);201val &= ~clearbits;202val |= setbits;203_register_write(reg, val);204}205206/**207* Return true if the LSM303D has new data available for both the mag and208* the accels.209*/210bool AP_Compass_LSM303D::_data_ready()211{212return _drdy_pin_m == nullptr || (_drdy_pin_m->read() != 0);213}214215216// Read Sensor data217bool AP_Compass_LSM303D::_read_sample()218{219struct PACKED {220uint8_t status;221int16_t x;222int16_t y;223int16_t z;224} rx;225226if (_register_read(ADDR_CTRL_REG7) != _reg7_expected) {227DEV_PRINTF("LSM303D _read_data_transaction_accel: _reg7_expected unexpected\n");228return false;229}230231if (!_data_ready()) {232return false;233}234235if (!_block_read(ADDR_STATUS_M, (uint8_t *) &rx, sizeof(rx))) {236return false;237}238239/* check for overrun */240if ((rx.status & 0x70) != 0) {241return false;242}243244if (rx.x == 0 && rx.y == 0 && rx.z == 0) {245return false;246}247248_mag_x = rx.x;249_mag_y = rx.y;250_mag_z = rx.z;251252return true;253}254255bool AP_Compass_LSM303D::init(enum Rotation rotation)256{257if (LSM303D_DRDY_M_PIN >= 0) {258_drdy_pin_m = hal.gpio->channel(LSM303D_DRDY_M_PIN);259_drdy_pin_m->mode(HAL_GPIO_INPUT);260}261262bool success = _hardware_init();263264if (!success) {265return false;266}267268_initialised = true;269270/* register the compass instance in the frontend */271_dev->set_device_type(DEVTYPE_LSM303D);272if (!register_compass(_dev->get_bus_id(), _compass_instance)) {273return false;274}275set_dev_id(_compass_instance, _dev->get_bus_id());276277set_rotation(_compass_instance, rotation);278279// read at 91Hz. We don't run at 100Hz as fetching data too fast can cause some very280// odd periodic changes in the output data281_dev->register_periodic_callback(11000, FUNCTOR_BIND_MEMBER(&AP_Compass_LSM303D::_update, void));282283return true;284}285286bool AP_Compass_LSM303D::_hardware_init()287{288_dev->get_semaphore()->take_blocking();289290// initially run the bus at low speed291_dev->set_speed(AP_HAL::Device::SPEED_LOW);292293// Test WHOAMI294uint8_t whoami = _register_read(ADDR_WHO_AM_I);295if (whoami != WHO_I_AM) {296goto fail_whoami;297}298299uint8_t tries;300for (tries = 0; tries < 5; tries++) {301// ensure the chip doesn't interpret any other bus traffic as I2C302_disable_i2c();303304/* enable mag */305_reg7_expected = REG7_CONT_MODE_M;306_register_write(ADDR_CTRL_REG7, _reg7_expected);307_register_write(ADDR_CTRL_REG5, REG5_RES_HIGH_M);308309// DRDY on MAG on INT2310_register_write(ADDR_CTRL_REG4, 0x04);311312_mag_set_range(LSM303D_MAG_DEFAULT_RANGE_GA);313_mag_set_samplerate(LSM303D_MAG_DEFAULT_RATE);314315hal.scheduler->delay(10);316if (_data_ready()) {317break;318}319}320if (tries == 5) {321DEV_PRINTF("Failed to boot LSM303D 5 times\n");322goto fail_tries;323}324325_dev->set_speed(AP_HAL::Device::SPEED_HIGH);326_dev->get_semaphore()->give();327328return true;329330fail_tries:331fail_whoami:332_dev->get_semaphore()->give();333_dev->set_speed(AP_HAL::Device::SPEED_HIGH);334return false;335}336337void AP_Compass_LSM303D::_update()338{339if (!_read_sample()) {340return;341}342343Vector3f raw_field = Vector3f(_mag_x, _mag_y, _mag_z) * _mag_range_scale;344345accumulate_sample(raw_field, _compass_instance, 10);346}347348// Read Sensor data349void AP_Compass_LSM303D::read()350{351if (!_initialised) {352return;353}354355drain_accumulated_samples(_compass_instance);356}357358void AP_Compass_LSM303D::_disable_i2c()359{360// TODO: use the register names361uint8_t a = _register_read(0x02);362_register_write(0x02, (0x10 | a));363a = _register_read(0x02);364_register_write(0x02, (0xF7 & a));365a = _register_read(0x15);366_register_write(0x15, (0x80 | a));367a = _register_read(0x02);368_register_write(0x02, (0xE7 & a));369}370371bool AP_Compass_LSM303D::_mag_set_range(uint8_t max_ga)372{373uint8_t setbits = 0;374uint8_t clearbits = REG6_FULL_SCALE_BITS_M;375float new_scale_ga_digit = 0.0f;376377if (max_ga == 0) {378max_ga = 12;379}380381if (max_ga <= 2) {382_mag_range_ga = 2;383setbits |= REG6_FULL_SCALE_2GA_M;384new_scale_ga_digit = 0.080f;385} else if (max_ga <= 4) {386_mag_range_ga = 4;387setbits |= REG6_FULL_SCALE_4GA_M;388new_scale_ga_digit = 0.160f;389} else if (max_ga <= 8) {390_mag_range_ga = 8;391setbits |= REG6_FULL_SCALE_8GA_M;392new_scale_ga_digit = 0.320f;393} else if (max_ga <= 12) {394_mag_range_ga = 12;395setbits |= REG6_FULL_SCALE_12GA_M;396new_scale_ga_digit = 0.479f;397} else {398return false;399}400401_mag_range_scale = new_scale_ga_digit;402_register_modify(ADDR_CTRL_REG6, clearbits, setbits);403404return true;405}406407bool AP_Compass_LSM303D::_mag_set_samplerate(uint16_t frequency)408{409uint8_t setbits = 0;410uint8_t clearbits = REG5_RATE_BITS_M;411412if (frequency == 0) {413frequency = 100;414}415416if (frequency <= 25) {417setbits |= REG5_RATE_25HZ_M;418_mag_samplerate = 25;419} else if (frequency <= 50) {420setbits |= REG5_RATE_50HZ_M;421_mag_samplerate = 50;422} else if (frequency <= 100) {423setbits |= REG5_RATE_100HZ_M;424_mag_samplerate = 100;425} else {426return false;427}428429_register_modify(ADDR_CTRL_REG5, clearbits, setbits);430431return true;432}433434#endif // AP_COMPASS_LSM303D_ENABLED435436437