mirror of
https://github.com/ArduPilot/ardupilot.git
synced 2026-10-02 10:23:25 +08:00
The constructor called set_rotation() before register_compass() had assigned the backend instance, so it wrote the rotation of compass instance 0. Every AF9838 probe on an external bus constructs a backend, so on boards without an AF9838 each failed probe cleared the rotation of whichever compass was already registered as instance 0. An external IST8310 lost its PITCH_180 rotation this way.
173 lines
4.4 KiB
C++
173 lines
4.4 KiB
C++
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
/*
|
|
Driver by Voltafield, July 2026
|
|
*/
|
|
|
|
#include "AP_Compass_AF9838.h"
|
|
|
|
#if AP_COMPASS_AF9838_ENABLED
|
|
|
|
#include <AP_HAL/AP_HAL.h>
|
|
|
|
#include <AP_HAL/utility/sparse-endian.h>
|
|
|
|
// Resolution: 0.1 uT/LSB, converted to milligauss for AP_Compass.
|
|
static constexpr float AF9838_MILLIGAUSS_PER_LSB = 1.0f;
|
|
|
|
extern const AP_HAL::HAL &hal;
|
|
|
|
AP_Compass_AF9838::AP_Compass_AF9838(AP_HAL::OwnPtr<AP_HAL::Device> dev,
|
|
bool force_external,
|
|
enum Rotation rotation)
|
|
: AP_Compass_Backend()
|
|
, _dev(std::move(dev))
|
|
, _force_external(force_external)
|
|
, _rotation(rotation)
|
|
{
|
|
}
|
|
|
|
AP_Compass_Backend *AP_Compass_AF9838::probe(AP_HAL::OwnPtr<AP_HAL::Device> dev,
|
|
bool force_external,
|
|
enum Rotation rotation)
|
|
{
|
|
if (!dev) {
|
|
return nullptr;
|
|
}
|
|
|
|
AP_Compass_AF9838 *sensor = NEW_NOTHROW AP_Compass_AF9838(std::move(dev), force_external, rotation);
|
|
if (!sensor || !sensor->init()) {
|
|
delete sensor;
|
|
return nullptr;
|
|
}
|
|
|
|
return sensor;
|
|
}
|
|
|
|
bool AP_Compass_AF9838::init()
|
|
{
|
|
WITH_SEMAPHORE(_dev->get_semaphore());
|
|
_dev->set_retries(10);
|
|
|
|
uint8_t val = 0;
|
|
|
|
if (!_dev->read_registers(REG_PCODE, &val, 1) || val != PCODE_EXPECT) {
|
|
return false;
|
|
}
|
|
|
|
if (!_dev->write_register(REG_SWR, SWR_TRIGGER)) {
|
|
DEV_PRINTF("AF9838: reset failed\n");
|
|
return false;
|
|
}
|
|
hal.scheduler->delay(5);
|
|
|
|
if (!_dev->write_register(REG_STATE, STATE_SELF_TEST)) {
|
|
DEV_PRINTF("AF9838: self-test start failed\n");
|
|
return false;
|
|
}
|
|
hal.scheduler->delay(40);
|
|
|
|
uint8_t status = 0;
|
|
|
|
if (!_dev->read_registers(REG_STATUS, &status, 1)) {
|
|
DEV_PRINTF("AF9838: self-test status read failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (status & STATUS_STERR) {
|
|
DEV_PRINTF("AF9838: self-test failed\n");
|
|
}
|
|
|
|
_dev->set_device_type(DEVTYPE_AF9838);
|
|
|
|
if (!register_compass(_dev->get_bus_id())) {
|
|
DEV_PRINTF("AF9838: compass registration failed\n");
|
|
return false;
|
|
}
|
|
|
|
if (_force_external) {
|
|
set_external(true);
|
|
}
|
|
|
|
set_rotation(_rotation);
|
|
|
|
_dev->set_retries(3);
|
|
|
|
_dev->register_periodic_callback(10000, FUNCTOR_BIND_MEMBER(&AP_Compass_AF9838::timer, void));
|
|
|
|
return true;
|
|
}
|
|
|
|
void AP_Compass_AF9838::timer()
|
|
{
|
|
|
|
if (!_single_pending) {
|
|
if (!_dev->write_register(REG_STATE, STATE_SINGLE)) {
|
|
return;
|
|
}
|
|
_single_pending = true;
|
|
_single_start_us = AP_HAL::micros();
|
|
return;
|
|
}
|
|
|
|
uint8_t status = 0;
|
|
if (!_dev->read_registers(REG_STATUS, &status, 1)) {
|
|
_single_pending = false;
|
|
return;
|
|
}
|
|
|
|
if (status & STATUS_HOFL) {
|
|
// HOFL is cleared when the next measurement starts.
|
|
_single_pending = false;
|
|
return;
|
|
}
|
|
|
|
if ((status & STATUS_ACQ) == 0) {
|
|
const uint32_t now = AP_HAL::micros();
|
|
if ((now - _single_start_us) > 20000U) {
|
|
_single_pending = false;
|
|
}
|
|
return;
|
|
}
|
|
|
|
uint8_t buf[6];
|
|
if (!_dev->read_registers(REG_DATA, buf, sizeof(buf))) {
|
|
_single_pending = false;
|
|
return;
|
|
}
|
|
|
|
// Raw samples are little-endian signed 16-bit values.
|
|
const int16_t x = (int16_t)le16toh_ptr(&buf[0]);
|
|
const int16_t y = (int16_t)le16toh_ptr(&buf[2]);
|
|
const int16_t z = (int16_t)le16toh_ptr(&buf[4]);
|
|
|
|
Vector3f field{
|
|
float(x) * AF9838_MILLIGAUSS_PER_LSB,
|
|
float(y) * AF9838_MILLIGAUSS_PER_LSB,
|
|
float(z) * AF9838_MILLIGAUSS_PER_LSB
|
|
};
|
|
|
|
accumulate_sample(field);
|
|
|
|
if (_dev->write_register(REG_STATE, STATE_SINGLE)) {
|
|
_single_pending = true;
|
|
_single_start_us = AP_HAL::micros();
|
|
} else {
|
|
_single_pending = false;
|
|
}
|
|
}
|
|
|
|
#endif // AP_COMPASS_AF9838_ENABLED
|