Line data Source code
1 : /* 2 : This program is free software: you can redistribute it and/or modify 3 : it under the terms of the GNU General Public License as published by 4 : the Free Software Foundation, either version 3 of the License, or 5 : (at your option) any later version. 6 : 7 : This program is distributed in the hope that it will be useful, 8 : but WITHOUT ANY WARRANTY; without even the implied warranty of 9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 : GNU General Public License for more details. 11 : 12 : You should have received a copy of the GNU General Public License 13 : along with this program. If not, see <http://www.gnu.org/licenses/>. 14 : */ 15 : 16 : /* 17 : backend driver for airspeed sensor from www.qio-tek.com 18 : I2C ASP5033 sensor 19 : */ 20 : 21 : #include "AP_Airspeed_ASP5033.h" 22 : 23 : #if AP_AIRSPEED_ASP5033_ENABLED 24 : 25 : #include <AP_HAL/I2CDevice.h> 26 : 27 : extern const AP_HAL::HAL &hal; 28 : 29 : #define ASP5033_I2C_ADDR_1 0x6C 30 : #define ASP5033_I2C_ADDR_2 0x6D 31 : #define REG_CMD 0x30 32 : #define REG_PRESS_DATA 0x06 33 : #define REG_TEMP_DATA 0x09 34 : #define REG_PART_ID 0x01 35 : #define REG_PART_ID_SET 0xa4 36 : #define REG_SENSOR_READY 0x08 37 : #define REG_WHOAMI_DEFAULT_ID 0X00 38 : #define REG_WHOAMI_RECHECK_ID 0X66 39 : #define CMD_MEASURE 0x0A 40 : 41 : 42 0 : bool AP_Airspeed_ASP5033::init() 43 : { 44 : // probe the sensor, supporting multiple possible I2C addresses 45 0 : const uint8_t addresses[] = { ASP5033_I2C_ADDR_1, ASP5033_I2C_ADDR_2 }; 46 0 : for (uint8_t address : addresses) { 47 0 : dev = hal.i2c_mgr->get_device_ptr(get_bus(), address); 48 0 : if (!dev) { 49 0 : continue; 50 : } 51 : 52 0 : WITH_SEMAPHORE(dev->get_semaphore()); 53 0 : dev->set_speed(AP_HAL::Device::SPEED_HIGH); 54 0 : dev->set_retries(2); 55 : 56 0 : if (!confirm_sensor_id()) { 57 0 : continue; 58 : } 59 : 60 0 : dev->set_device_type(uint8_t(DevType::ASP5033)); 61 0 : set_bus_id(dev->get_bus_id()); 62 : 63 0 : dev->register_periodic_callback(1000000UL/80U, 64 : FUNCTOR_BIND_MEMBER(&AP_Airspeed_ASP5033::timer, void)); 65 0 : return true; 66 0 : } 67 : 68 : // not found 69 0 : return false; 70 : } 71 : 72 : /* 73 : this sensor has an unusual whoami scheme. The part_id is changeable 74 : via another register. We check the sensor by looking for the 75 : expected behaviour 76 : */ 77 0 : bool AP_Airspeed_ASP5033::confirm_sensor_id(void) 78 : { 79 : uint8_t part_id; 80 0 : if (!dev->read_registers(REG_PART_ID_SET, &part_id, 1) || 81 0 : ( (part_id != REG_WHOAMI_DEFAULT_ID) && (part_id != REG_WHOAMI_RECHECK_ID) ) ) { 82 0 : return false; 83 : } 84 0 : if (!dev->write_register(REG_PART_ID_SET, REG_WHOAMI_RECHECK_ID)) { 85 0 : return false; 86 : } 87 0 : if (!dev->read_registers(REG_PART_ID, &part_id, 1) || 88 0 : part_id != REG_WHOAMI_RECHECK_ID) { 89 0 : return false; 90 : } 91 0 : return true; 92 : } 93 : 94 : 95 : // read the data from the sensor 96 0 : void AP_Airspeed_ASP5033::timer() 97 : { 98 : // request a new measurement cycle begin 99 0 : dev->write_register(REG_CMD, CMD_MEASURE); 100 : 101 : uint8_t status; 102 0 : if (!dev->read_registers(REG_CMD, &status, 1) || 103 0 : (status & REG_SENSOR_READY) == 0) { 104 : // no data ready 105 0 : return; 106 : } 107 : 108 : // read pressure and temperature as one block 109 : uint8_t data[5]; 110 0 : if (!dev->read_registers(REG_PRESS_DATA, data, sizeof(data))) { 111 0 : return; 112 : } 113 : 114 : // ADC pressure is signed 24 bit 115 0 : int32_t press = (data[0]<<24) | (data[1]<<16) | (data[2]<<8); 116 : 117 : // convert back to 24 bit 118 0 : press >>= 8; 119 : 120 : // k is a shift based on the pressure range of the device. See 121 : // table in the datasheet 122 0 : constexpr uint8_t k = 7; 123 0 : constexpr float press_scale = 1.0 / (1U<<k); 124 : 125 : // temperature is 16 bit signed in units of 1/256 C 126 0 : const int16_t temp = (data[3]<<8) | data[4]; 127 0 : constexpr float temp_scale = 1.0 / 256; 128 : 129 0 : WITH_SEMAPHORE(sem); 130 0 : press_sum += press * press_scale; 131 0 : temp_sum += temp * temp_scale; 132 0 : press_count++; 133 0 : temp_count++; 134 : 135 0 : last_sample_ms = AP_HAL::millis(); 136 0 : } 137 : 138 : 139 : // return the current differential_pressure in Pascal 140 0 : bool AP_Airspeed_ASP5033::get_differential_pressure(float &pressure) 141 : { 142 0 : WITH_SEMAPHORE(sem); 143 : 144 0 : if (AP_HAL::millis() - last_sample_ms > 100) { 145 0 : return false; 146 : } 147 : 148 0 : if (press_count == 0) { 149 0 : pressure = last_pressure; 150 0 : return true; 151 : } 152 : 153 0 : last_pressure = pressure = press_sum / press_count; 154 : 155 0 : press_count = 0; 156 0 : press_sum = 0; 157 : 158 0 : return true; 159 0 : } 160 : 161 : // return the current temperature in degrees C, if available 162 0 : bool AP_Airspeed_ASP5033::get_temperature(float &temperature) 163 : { 164 0 : WITH_SEMAPHORE(sem); 165 : 166 0 : if (AP_HAL::millis() - last_sample_ms > 100) { 167 0 : return false; 168 : } 169 0 : if (temp_count == 0) { 170 0 : temperature = last_temperature; 171 0 : return true; 172 : } 173 : 174 0 : last_temperature = temperature = temp_sum / temp_count; 175 0 : temp_count = 0; 176 0 : temp_sum = 0; 177 : 178 0 : return true; 179 0 : } 180 : 181 : #endif