/*
* This file is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*
* Driver by Lokesh Ramina, Jan 2022
*/
#include "AP_Compass_QMC5883P.h"
#include
#include
#include
#if AP_COMPASS_QMC5883P_ENABLED
//Register Address
#define QMC5883P_REG_ID 0x00
#define QMC5883P_REG_DATA_OUTPUT_X 0x01
#define QMC5883P_REG_DATA_OUTPUT_Z_MSB 0x06
#define QMC5883P_REG_STATUS 0x09
#define QMC5883P_REG_CONF1 0x0A
#define QMC5883P_REG_CONF2 0x0B
#define QMC5883P_ID_VAL 0x80
//Register values
// Sensor operation modes
#define QMC5883P_MODE_SUSPEND 0x00
#define QMC5883P_MODE_NORMAL 0x01
#define QMC5883P_MODE_SINGLE 0x02
#define QMC5883P_MODE_CONTINUOUS 0x03
// ODR data output rates for 5883L
#define QMC5883P_ODR_10HZ (0x00 << 2)
#define QMC5883P_ODR_50HZ (0x01 << 2)
#define QMC5883P_ODR_100HZ (0x02 << 2)
#define QMC5883P_ODR_200HZ (0x03 << 2)
// Over sampling Ratio OSR1
#define QMC5883P_OSR1_8 (0x00 << 4)
#define QMC5883P_OSR1_4 (0x01 << 4)
#define QMC5883P_OSR1_2 (0x02 << 4)
#define QMC5883P_OSR1_1 (0x03 << 4)
// Down sampling Rate OSR2
#define QMC5883P_OSR2_8 0x08
//RNG
#define QMC5883P_RNG_30G (0x00 << 2)
#define QMC5883P_RNG_12G (0x01 << 2)
#define QMC5883P_RNG_8G (0x10 << 2)
#define QMC5883P_RNG_2G (0x11 << 2)
#define QMC5883P_SET_XYZ_SIGN 0x29
//Reset
#define QMC5883P_RST 0x80
//Status Val
#define QMC5883P_STATUS_DATA_READY 0x01
#ifndef DEBUG
#define DEBUG 0
#endif
extern const AP_HAL::HAL &hal;
AP_Compass_Backend *AP_Compass_QMC5883P::probe(AP_HAL::OwnPtr dev,
bool force_external,
enum Rotation rotation)
{
if (!dev) {
return nullptr;
}
AP_Compass_QMC5883P *sensor = NEW_NOTHROW AP_Compass_QMC5883P(std::move(dev),force_external,rotation);
if (!sensor || !sensor->init()) {
delete sensor;
return nullptr;
}
return sensor;
}
AP_Compass_QMC5883P::AP_Compass_QMC5883P(AP_HAL::OwnPtr dev,
bool force_external,
enum Rotation rotation)
: _dev(std::move(dev))
, _rotation(rotation)
, _force_external(force_external)
{
}
bool AP_Compass_QMC5883P::init()
{
_dev->get_semaphore()->take_blocking();
_dev->set_retries(10);
#if DEBUG
_dump_registers();
#endif
if (!_check_whoami()) {
goto fail;
}
//As mentioned in the Datasheet 7.2 to do continues mode 0x29 will set sign for X,Y,Z
if (!_dev->write_register(QMC5883P_REG_DATA_OUTPUT_Z_MSB, QMC5883P_SET_XYZ_SIGN)||
!_dev->write_register(QMC5883P_REG_CONF1,
QMC5883P_MODE_CONTINUOUS|
QMC5883P_ODR_100HZ|
QMC5883P_OSR1_8|
QMC5883P_OSR2_8)||
!_dev->write_register(QMC5883P_REG_CONF2,QMC5883P_OSR2_8)) {
goto fail;
}
// lower retries for run
_dev->set_retries(3);
_dev->get_semaphore()->give();
//register compass instance
_dev->set_device_type(DEVTYPE_QMC5883P);
if (!register_compass(_dev->get_bus_id(), _instance)) {
return false;
}
set_dev_id(_instance, _dev->get_bus_id());
printf("%s found on bus %u id %u address 0x%02x\n", name,
_dev->bus_num(), unsigned(_dev->get_bus_id()), _dev->get_bus_address());
set_rotation(_instance, _rotation);
if (_force_external) {
set_external(_instance, true);
}
//Enable 100HZ
_dev->register_periodic_callback(10000,
FUNCTOR_BIND_MEMBER(&AP_Compass_QMC5883P::timer, void));
return true;
fail:
_dev->get_semaphore()->give();
return false;
}
bool AP_Compass_QMC5883P::_check_whoami()
{
uint8_t whoami;
if (!_dev->read_registers(QMC5883P_REG_ID, &whoami,1)||
whoami != QMC5883P_ID_VAL) {
return false;
}
return true;
}
void AP_Compass_QMC5883P::timer()
{
struct PACKED {
int16_t rx;
int16_t ry;
int16_t rz;
} buffer;
const float range_scale = 1000.0f / 3000.0f;
uint8_t status;
if (!_dev->read_registers(QMC5883P_REG_STATUS,&status,1)) {
return;
}
//new data is ready
if (!(status & QMC5883P_STATUS_DATA_READY)) {
return;
}
if (!_dev->read_registers(QMC5883P_REG_DATA_OUTPUT_X, (uint8_t *) &buffer, sizeof(buffer))) {
return ;
}
auto x = buffer.rx;
auto y = buffer.ry;
auto z = buffer.rz;
#if DEBUG
printf("mag.x:%d\n",x);
printf("mag.y:%d\n",y);
printf("mag.z:%d\n",z);
#endif
Vector3f field = Vector3f{x * range_scale, y * range_scale, z * range_scale };
accumulate_sample(field, _instance, 20);
}
void AP_Compass_QMC5883P::read()
{
drain_accumulated_samples(_instance);
}
void AP_Compass_QMC5883P::_dump_registers()
{
printf("QMC5883P registers dump\n");
for (uint8_t reg = QMC5883P_REG_DATA_OUTPUT_X; reg <= 0x30; reg++) {
uint8_t v;
_dev->read_registers(reg,&v,1);
printf("%02x:%02x ", (unsigned)reg, (unsigned)v);
if ((reg - ( QMC5883P_REG_DATA_OUTPUT_X-1)) % 16 == 0) {
printf("\n");
}
}
}
#endif //AP_COMPASS_QMC5883P_ENABLED