Path: blob/master/libraries/AP_Airspeed/AP_Airspeed_AUAV.cpp
4183 views
/*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/*16backend driver for airspeed from a I2C AUAV sensor17*/18#include "AP_Airspeed_AUAV.h"1920#if AP_AIRSPEED_AUAV_ENABLED2122#define AUAV_AIRSPEED_I2C_ADDR 0x2623#define MEASUREMENT_TIMEOUT_MS 2002425#include <AP_Common/AP_Common.h>26#include <AP_HAL/AP_HAL.h>27#include <AP_HAL/I2CDevice.h>28#include <AP_Math/AP_Math.h>29#include <GCS_MAVLink/GCS.h>3031extern const AP_HAL::HAL &hal;3233AUAV_Pressure_sensor::AUAV_Pressure_sensor(AP_HAL::I2CDevice *&_dev, Type _type) :34dev(_dev),35type(_type)36{37}3839AP_Airspeed_AUAV::AP_Airspeed_AUAV(AP_Airspeed &_frontend, uint8_t _instance, const float _range_inH2O) :40AP_Airspeed_Backend(_frontend, _instance),41range_inH2O(_range_inH2O)42{43}4445// probe for a sensor46bool AP_Airspeed_AUAV::probe(uint8_t bus, uint8_t address)47{48_dev = hal.i2c_mgr->get_device_ptr(bus, address);49if (!_dev) {50return false;51}5253WITH_SEMAPHORE(_dev->get_semaphore());5455_dev->set_retries(10);56if (!sensor.measure()) {57return false;58}59hal.scheduler->delay(20);6061// Don't fill in values yet as the compensation coefficients are not configured62float PComp, temperature;63return sensor.collect(PComp, temperature) == AUAV_Pressure_sensor::Status::Normal;64}6566// probe and initialise the sensor67bool AP_Airspeed_AUAV::init()68{69const auto bus = get_bus();70if (!probe(bus, AUAV_AIRSPEED_I2C_ADDR)) {71GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "AUAV AIRSPEED[%u]: no sensor found on bus %u", get_instance(), bus);72return false;73}7475_dev->set_device_type(uint8_t(DevType::AUAV));76set_bus_id(_dev->get_bus_id());7778GCS_SEND_TEXT(MAV_SEVERITY_INFO, "AUAV AIRSPEED[%u]: Found bus %u addr 0x%02x", get_instance(), _dev->bus_num(), _dev->get_bus_address());7980// drop to 2 retries for runtime81_dev->register_periodic_callback(20000,82FUNCTOR_BIND_MEMBER(&AP_Airspeed_AUAV::_timer, void));83return true;84}8586// start a measurement87bool AUAV_Pressure_sensor::measure()88{89uint8_t cmd = 0xAE; // start average890// Differential: max 16.2ms measurement time91// Absolute: 37ms92if (dev->transfer(&cmd, 1, nullptr, 0)) {93return true;94}95return false;96}9798// read the values from the sensor99// Return true if successful100// pressure corrected but not scaled to the correct units101// temperature in degrees centigrade102AUAV_Pressure_sensor::Status AUAV_Pressure_sensor::collect(float &PComp, float &temperature)103{104uint8_t inbuf[7];105if (!dev->read((uint8_t *)&inbuf, sizeof(inbuf))) {106return Status::Fault;107}108const uint8_t status = inbuf[0];109110// power on, not busy, reading normal, not out of range111const uint8_t healthy_status = type == Type::Differential ? 0x50 : 0x40;112if (status != healthy_status) {113if ((status & (1U << 5)) != 0) {114return Status::Busy;115}116return Status::Fault;117}118const int32_t Tref_Counts = 7576807; // temperature counts at 25C119const float TC50Scale = 100.0f * 100.0f * 167772.2f; // scale TC50 to 1.0% FS0120float Pdiff, TC50;121122// Convert unsigned 24-bit pressure value to signed +/- 23-bit:123const int32_t iPraw = (inbuf[1]<<16) + (inbuf[2]<<8) + inbuf[3] - 0x800000;124125// Convert signed 23-bit value to float, normalized to +/- 1.0:126const float Pnorm = float(iPraw) / float(0x7FFFFF);127const float AP3 = LIN_A * Pnorm * Pnorm * Pnorm; // A*Pout^3128const float BP2 = LIN_B * Pnorm * Pnorm; // B*Pout^2129const float CP = LIN_C * Pnorm; // C*POut130const float Corr = AP3 + BP2 + CP + LIN_D; // Linearity correction term131const float Pcorr = Pnorm + Corr; // Corrected P, range +/-1.0.132133// Compute difference from reference temperature, in sensor counts:134const int32_t iTemp = (inbuf[4]<<16) + (inbuf[5]<<8) + inbuf[6]; // 24-bit temperature135136// get temperature in degrees C137temperature = (iTemp * 155) / float(1U<<24) - 45;138139const int32_t Tdiff = iTemp - Tref_Counts; // see constant defined above.140const float Pnfso = (Pcorr + 1.0f) * 0.5;141142//TC50: Select High/Low, based on current temp above/below 25C:143if (Tdiff > 0) {144TC50 = TC50H;145} else {146TC50 = TC50L;147}148149// Find absolute difference between midrange and reading (abs(Pnfso-0.5)):150if (Pnfso > 0.5) {151Pdiff = Pnfso - 0.5;152} else {153Pdiff = 0.5f - Pnfso;154}155156const float Tcorr = (1.0f - (Es * 2.5f * Pdiff)) * Tdiff * TC50 / TC50Scale;157const float PCorrt = Pnfso - Tcorr; // corrected P: float, [0 to +1.0)158PComp = PCorrt * (float)0xFFFFFF;159160return Status::Normal;161}162163bool AUAV_Pressure_sensor::read_register(uint8_t cmd, uint16_t &result)164{165switch (coefficient_step) {166case CoefficientStep::request: {167if (dev->transfer(&cmd, sizeof(cmd), nullptr, 0)) {168coefficient_step = CoefficientStep::read;169}170return false;171}172173case CoefficientStep::read: {174// Next step is always another request, either for the next coefficient or a re-request if this read fails175coefficient_step = CoefficientStep::request;176177const uint8_t healthy_status = type == Type::Differential ? 0x50 : 0x40;178uint8_t raw_bytes[3];179if (!dev->read((uint8_t *)&raw_bytes, sizeof(raw_bytes))) {180return false;181}182if (raw_bytes[0] != healthy_status) {183return false;184}185result = ((uint16_t)raw_bytes[1] << 8) | (uint16_t)raw_bytes[2];186return true;187}188}189190return false;191192}193194void AUAV_Pressure_sensor::read_coefficients()195{196const uint8_t cmd_start = type == Type::Differential ? 0x2B : 0x2F;197198// Differential can happily run through these in one call, Absolute gets stuck, no idea why.199// Space them out anyway to be inline with the spec200201switch (stage) {202case CoefficientStage::A_high: {203uint16_t high;204if (!read_register(cmd_start + 0, high)) {205return;206}207LIN_A = (float)(high << 16)/(float)(0x7FFFFFFF);208stage = CoefficientStage::A_low;209break;210}211212case CoefficientStage::A_low: {213uint16_t low;214if (!read_register(cmd_start + 1, low)) {215return;216}217LIN_A += (float)(low)/(float)(0x7FFFFFFF);218stage = CoefficientStage::B_high;219break;220}221222case CoefficientStage::B_high: {223uint16_t high;224if (!read_register(cmd_start + 2, high)) {225return;226}227LIN_B = (float)(high << 16)/(float)(0x7FFFFFFF);228stage = CoefficientStage::B_low;229break;230}231232case CoefficientStage::B_low: {233uint16_t low;234if (!read_register(cmd_start + 3, low)) {235return;236}237LIN_B += (float)(low)/(float)(0x7FFFFFFF);238stage = CoefficientStage::C_high;239break;240}241242case CoefficientStage::C_high: {243uint16_t high;244if (!read_register(cmd_start + 4, high)) {245return;246}247LIN_C = (float)(high << 16)/(float)(0x7FFFFFFF);248stage = CoefficientStage::C_low;249break;250}251252case CoefficientStage::C_low: {253uint16_t low;254if (!read_register(cmd_start + 5, low)) {255return;256}257LIN_C += (float)(low)/(float)(0x7FFFFFFF);258stage = CoefficientStage::D_high;259break;260}261262case CoefficientStage::D_high: {263uint16_t high;264if (!read_register(cmd_start + 6, high)) {265return;266}267LIN_D = (float)(high << 16)/(float)(0x7FFFFFFF);268stage = CoefficientStage::D_low;269break;270}271272case CoefficientStage::D_low: {273uint16_t low;274if (!read_register(cmd_start + 7, low)) {275return;276}277LIN_D += (float)(low)/(float)(0x7FFFFFFF);278stage = CoefficientStage::E_high;279break;280}281282case CoefficientStage::E_high: {283uint16_t TC50HL;284if (!read_register(cmd_start + 8, TC50HL)) {285return;286}287const int8_t i8TC50H = (TC50HL >> 8) & 0xFF;288const int8_t i8TC50L = TC50HL & 0xFF;289TC50H = (float)(i8TC50H)/(float)(0x7F);290TC50L = (float)(i8TC50L)/(float)(0x7F);291stage = CoefficientStage::E_low;292break;293}294295case CoefficientStage::E_low: {296uint16_t es;297if (!read_register(cmd_start + 9, es)) {298return;299}300Es = (float)(es & 0xFF)/(float)(0x7F);301stage = CoefficientStage::Done;302303// Drop to 2 retry's for runtime304dev->set_retries(2);305break;306}307308case CoefficientStage::Done:309break;310}311}312313void AP_Airspeed_AUAV::_timer()314{315if (sensor.stage != AUAV_Pressure_sensor::CoefficientStage::Done) {316sensor.read_coefficients();317return;318}319320if (measurement_requested) {321// Read in result of last measurement322float Pcomp;323switch (sensor.collect(Pcomp, temp_C)) {324case AUAV_Pressure_sensor::Status::Normal:325// Calculate pressure and update last read time326last_sample_time_ms = AP_HAL::millis();327328// Convert to correct units and apply range329pressure = 248.8f*1.25f*((Pcomp-8388608)/16777216.0f) * 2 * range_inH2O;330break;331332case AUAV_Pressure_sensor::Status::Busy:333// Don't request a new measurement334return;335336case AUAV_Pressure_sensor::Status::Fault:337break;338}339}340341// Request a new measurement342measurement_requested = sensor.measure();343}344345// return the current differential_pressure in Pascal346bool AP_Airspeed_AUAV::get_differential_pressure(float &_pressure)347{348WITH_SEMAPHORE(sem);349_pressure = pressure;350return AP_HAL::millis() - last_sample_time_ms < MEASUREMENT_TIMEOUT_MS;351}352353// return the current temperature in degrees C, if available354bool AP_Airspeed_AUAV::get_temperature(float &_temperature)355{356WITH_SEMAPHORE(sem);357_temperature = temp_C;358return AP_HAL::millis() - last_sample_time_ms < MEASUREMENT_TIMEOUT_MS;359}360361#endif // AP_AIRSPEED_AUAV_ENABLED362363364