Path: blob/master/libraries/AP_Compass/AP_Compass_QMC5883L.cpp
9420 views
/*1* Copyright (C) 2016 Emlid Ltd. All rights reserved.2*3* This file is free software: you can redistribute it and/or modify it4* under the terms of the GNU General Public License as published by the5* Free Software Foundation, either version 3 of the License, or6* (at your option) any later version.7*8* This file is distributed in the hope that it will be useful, but9* WITHOUT ANY WARRANTY; without even the implied warranty of10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.11* See the GNU General Public License for more details.12*13* You should have received a copy of the GNU General Public License along14* with this program. If not, see <http://www.gnu.org/licenses/>.15*16* Driver by RadioLink LjWang, Jun 201717* GPS compass module See<http://www.radiolink.com>18*/19#include "AP_Compass_QMC5883L.h"2021#if AP_COMPASS_QMC5883L_ENABLED2223#include <stdio.h>24#include <utility>2526#include <AP_HAL/AP_HAL.h>27#include <AP_HAL/utility/sparse-endian.h>28#include <AP_Math/AP_Math.h>2930#define QMC5883L_REG_CONF1 0x0931#define QMC5883L_REG_CONF2 0x0A3233// data output rates for 5883L34#define QMC5883L_ODR_10HZ (0x00 << 2)35#define QMC5883L_ODR_50HZ (0x01 << 2)36#define QMC5883L_ODR_100HZ (0x02 << 2)37#define QMC5883L_ODR_200HZ (0x03 << 2)3839// Sensor operation modes40#define QMC5883L_MODE_STANDBY 0x0041#define QMC5883L_MODE_CONTINUOUS 0x014243#define QMC5883L_RNG_2G (0x00 << 4)44#define QMC5883L_RNG_8G (0x01 << 4)4546#define QMC5883L_OSR_512 (0x00 << 6)47#define QMC5883L_OSR_256 (0x01 << 6)48#define QMC5883L_OSR_128 (0x10 << 6)49#define QMC5883L_OSR_64 (0x11 << 6)5051#define QMC5883L_RST 0x805253#define QMC5883L_REG_DATA_OUTPUT_X 0x0054#define QMC5883L_REG_STATUS 0x065556#define QMC5883L_REG_ID 0x0D57#define QMC5883_ID_VAL 0xFF5859AP_Compass_Backend *AP_Compass_QMC5883L::probe(AP_HAL::OwnPtr<AP_HAL::Device> dev,60bool force_external,61enum Rotation rotation)62{63if (!dev) {64return nullptr;65}6667AP_Compass_QMC5883L *sensor = NEW_NOTHROW AP_Compass_QMC5883L(std::move(dev),force_external,rotation);68if (!sensor || !sensor->init()) {69delete sensor;70return nullptr;71}7273return sensor;74}7576AP_Compass_QMC5883L::AP_Compass_QMC5883L(AP_HAL::OwnPtr<AP_HAL::Device> dev,77bool force_external,78enum Rotation rotation)79: _dev(std::move(dev))80, _rotation(rotation)81, _force_external(force_external)82{83}8485bool AP_Compass_QMC5883L::init()86{87_dev->get_semaphore()->take_blocking();8889_dev->set_retries(10);9091#if 092_dump_registers();93#endif9495if(!_check_whoami()){96goto fail;97}9899if (!_dev->write_register(0x0B, 0x01)||100!_dev->write_register(0x20, 0x40)||101!_dev->write_register(0x21, 0x01)||102!_dev->write_register(QMC5883L_REG_CONF1,103QMC5883L_MODE_CONTINUOUS|104QMC5883L_ODR_100HZ|105QMC5883L_OSR_512|106QMC5883L_RNG_8G)) {107goto fail;108}109110// lower retries for run111_dev->set_retries(3);112113_dev->get_semaphore()->give();114115//register compass instance116_dev->set_device_type(DEVTYPE_QMC5883L);117if (!register_compass(_dev->get_bus_id())) {118return false;119}120121printf("%s found on bus %u id %u address 0x%02x\n", name,122_dev->bus_num(), unsigned(_dev->get_bus_id()), _dev->get_bus_address());123124set_rotation(_rotation);125126if (_force_external) {127set_external(true);128}129130//Enable 100HZ131_dev->register_periodic_callback(10000,132FUNCTOR_BIND_MEMBER(&AP_Compass_QMC5883L::timer, void));133134return true;135136fail:137_dev->get_semaphore()->give();138return false;139}140bool AP_Compass_QMC5883L::_check_whoami()141{142uint8_t whoami;143//Affected by other devices,must read registers 0x00 once or reset,after can read the ID registers reliably144_dev->read_registers(0x00,&whoami,1);145if (!_dev->read_registers(0x0C, &whoami,1)||146whoami != 0x01){147return false;148}149if (!_dev->read_registers(QMC5883L_REG_ID, &whoami,1)||150whoami != QMC5883_ID_VAL){151return false;152}153return true;154}155156void AP_Compass_QMC5883L::timer()157{158struct PACKED {159le16_t rx;160le16_t ry;161le16_t rz;162} buffer;163164const float range_scale = 1000.0f / 3000.0f;165166uint8_t status;167if(!_dev->read_registers(QMC5883L_REG_STATUS,&status,1)){168return;169}170//new data is ready171if (!(status & 0x04)) {172return;173}174175if(!_dev->read_registers(QMC5883L_REG_DATA_OUTPUT_X, (uint8_t *) &buffer, sizeof(buffer))){176return ;177}178179auto x = -static_cast<int16_t>(le16toh(buffer.rx));180auto y = static_cast<int16_t>(le16toh(buffer.ry));181auto z = -static_cast<int16_t>(le16toh(buffer.rz));182183#if 0184printf("mag.x:%d\n",x);185printf("mag.y:%d\n",y);186printf("mag.z:%d\n",z);187#endif188189Vector3f field = Vector3f{x * range_scale , y * range_scale, z * range_scale };190191// rotate to the desired orientation192if (is_external()) {193field.rotate(ROTATION_YAW_90);194}195196accumulate_sample(field, 20);197}198199void AP_Compass_QMC5883L::read()200{201drain_accumulated_samples();202}203204void AP_Compass_QMC5883L::_dump_registers()205{206printf("QMC5883L registers dump\n");207for (uint8_t reg = QMC5883L_REG_DATA_OUTPUT_X; reg <= 0x30; reg++) {208uint8_t v;209_dev->read_registers(reg,&v,1);210printf("%02x:%02x ", (unsigned)reg, (unsigned)v);211if ((reg - ( QMC5883L_REG_DATA_OUTPUT_X-1)) % 16 == 0) {212printf("\n");213}214}215}216217#endif // AP_COMPASS_QMC5883L_ENABLED218219220