You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
496 lines
13 KiB
496 lines
13 KiB
/* |
|
This program 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 program 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/>. |
|
*/ |
|
|
|
#include "AP_Baro_ICP201XX.h" |
|
|
|
#if AP_BARO_ICP201XX_ENABLED |
|
|
|
#include <AP_HAL/AP_HAL.h> |
|
#include <AP_HAL/I2CDevice.h> |
|
#include <utility> |
|
|
|
#include <AP_Common/AP_Common.h> |
|
#include <AP_HAL/AP_HAL.h> |
|
#include <AP_Math/AP_Math.h> |
|
#include <AP_BoardConfig/AP_BoardConfig.h> |
|
|
|
#include <utility> |
|
#include <stdio.h> |
|
|
|
#include <AP_Math/AP_Math.h> |
|
#include <AP_Logger/AP_Logger.h> |
|
|
|
#include <AP_InertialSensor/AP_InertialSensor_Invensense_registers.h> |
|
|
|
extern const AP_HAL::HAL &hal; |
|
|
|
#define ICP201XX_ID 0x63 |
|
|
|
#define CONVERSION_INTERVAL 25000 |
|
|
|
#define REG_EMPTY 0x00 |
|
#define REG_TRIM1_MSB 0x05 |
|
#define REG_TRIM2_LSB 0x06 |
|
#define REG_TRIM2_MSB 0x07 |
|
#define REG_DEVICE_ID 0x0C |
|
#define REG_OTP_MTP_OTP_CFG1 0xAC |
|
#define REG_OTP_MTP_MR_LSB 0xAD |
|
#define REG_OTP_MTP_MR_MSB 0xAE |
|
#define REG_OTP_MTP_MRA_LSB 0xAF |
|
#define REG_OTP_MTP_MRA_MSB 0xB0 |
|
#define REG_OTP_MTP_MRB_LSB 0xB1 |
|
#define REG_OTP_MTP_MRB_MSB 0xB2 |
|
#define REG_OTP_MTP_OTP_ADDR 0xB5 |
|
#define REG_OTP_MTP_OTP_CMD 0xB6 |
|
#define REG_OTP_MTP_RD_DATA 0xB8 |
|
#define REG_OTP_MTP_OTP_STATUS 0xB9 |
|
#define REG_OTP_DEBUG2 0xBC |
|
#define REG_MASTER_LOCK 0xBE |
|
#define REG_OTP_MTP_OTP_STATUS2 0xBF |
|
#define REG_MODE_SELECT 0xC0 |
|
#define REG_INTERRUPT_STATUS 0xC1 |
|
#define REG_INTERRUPT_MASK 0xC2 |
|
#define REG_FIFO_CONFIG 0xC3 |
|
#define REG_FIFO_FILL 0xC4 |
|
#define REG_SPI_MODE 0xC5 |
|
#define REG_PRESS_ABS_LSB 0xC7 |
|
#define REG_PRESS_ABS_MSB 0xC8 |
|
#define REG_PRESS_DELTA_LSB 0xC9 |
|
#define REG_PRESS_DELTA_MSB 0xCA |
|
#define REG_DEVICE_STATUS 0xCD |
|
#define REG_I3C_INFO 0xCE |
|
#define REG_VERSION 0xD3 |
|
#define REG_FIFO_BASE 0xFA |
|
|
|
/* |
|
constructor |
|
*/ |
|
AP_Baro_ICP201XX::AP_Baro_ICP201XX(AP_Baro &baro, AP_HAL::OwnPtr<AP_HAL::I2CDevice> _dev) |
|
: AP_Baro_Backend(baro) |
|
, dev(std::move(_dev)) |
|
{ |
|
} |
|
|
|
AP_Baro_Backend *AP_Baro_ICP201XX::probe(AP_Baro &baro, |
|
AP_HAL::OwnPtr<AP_HAL::I2CDevice> dev) |
|
{ |
|
if (!dev) { |
|
return nullptr; |
|
} |
|
AP_Baro_ICP201XX *sensor = new AP_Baro_ICP201XX(baro, std::move(dev)); |
|
if (!sensor || !sensor->init()) { |
|
delete sensor; |
|
return nullptr; |
|
} |
|
return sensor; |
|
} |
|
|
|
bool AP_Baro_ICP201XX::init() |
|
{ |
|
if (!dev) { |
|
return false; |
|
} |
|
|
|
dev->get_semaphore()->take_blocking(); |
|
|
|
uint8_t id = 0xFF; |
|
uint8_t ver = 0xFF; |
|
read_reg(REG_DEVICE_ID, &id); |
|
read_reg(REG_VERSION, &ver); |
|
|
|
if (id != ICP201XX_ID) { |
|
goto failed; |
|
} |
|
|
|
if (ver != 0x00 && ver != 0xB2) { |
|
goto failed; |
|
} |
|
|
|
hal.scheduler->delay(10); |
|
|
|
soft_reset(); |
|
|
|
if (!boot_sequence()) { |
|
goto failed; |
|
} |
|
|
|
if (!configure()) { |
|
goto failed; |
|
} |
|
|
|
wait_read(); |
|
|
|
dev->set_retries(0); |
|
|
|
instance = _frontend.register_sensor(); |
|
|
|
dev->set_device_type(DEVTYPE_BARO_ICP201XX); |
|
set_bus_id(instance, dev->get_bus_id()); |
|
|
|
dev->get_semaphore()->give(); |
|
|
|
dev->register_periodic_callback(CONVERSION_INTERVAL/2, FUNCTOR_BIND_MEMBER(&AP_Baro_ICP201XX::timer, void)); |
|
return true; |
|
|
|
failed: |
|
dev->get_semaphore()->give(); |
|
return false; |
|
} |
|
|
|
|
|
void AP_Baro_ICP201XX::dummy_reg() |
|
{ |
|
do { |
|
uint8_t reg = REG_EMPTY; |
|
uint8_t val = 0; |
|
dev->transfer(®, 1, &val, 1); |
|
} while (0); |
|
} |
|
|
|
bool AP_Baro_ICP201XX::read_reg(uint8_t reg, uint8_t *buf, uint8_t len) |
|
{ |
|
bool ret; |
|
ret = dev->transfer(®, 1, buf, len); |
|
dummy_reg(); |
|
return ret; |
|
} |
|
|
|
bool AP_Baro_ICP201XX::read_reg(uint8_t reg, uint8_t *val) |
|
{ |
|
return read_reg(reg, val, 1); |
|
} |
|
|
|
bool AP_Baro_ICP201XX::write_reg(uint8_t reg, uint8_t val) |
|
{ |
|
bool ret; |
|
uint8_t data[2] = { reg, val }; |
|
ret = dev->transfer(data, sizeof(data), nullptr, 0); |
|
dummy_reg(); |
|
return ret; |
|
} |
|
|
|
void AP_Baro_ICP201XX::soft_reset() |
|
{ |
|
/* Stop the measurement */ |
|
mode_select(0x00); |
|
|
|
hal.scheduler->delay(2); |
|
|
|
/* Flush FIFO */ |
|
flush_fifo(); |
|
|
|
/* Mask all interrupts */ |
|
write_reg(REG_FIFO_CONFIG, 0x00); |
|
write_reg(REG_INTERRUPT_MASK, 0xFF); |
|
} |
|
|
|
bool AP_Baro_ICP201XX::mode_select(uint8_t mode) |
|
{ |
|
uint8_t mode_sync_status = 0; |
|
|
|
do { |
|
read_reg(REG_DEVICE_STATUS, &mode_sync_status, 1); |
|
|
|
if (mode_sync_status & 0x01) { |
|
break; |
|
} |
|
|
|
hal.scheduler->delay(1); |
|
} while (1); |
|
|
|
return write_reg(REG_MODE_SELECT, mode); |
|
} |
|
|
|
bool AP_Baro_ICP201XX::read_otp_data(uint8_t addr, uint8_t cmd, uint8_t *val) |
|
{ |
|
uint8_t otp_status = 0xFF; |
|
|
|
/* Write the address content and read command */ |
|
if (!write_reg(REG_OTP_MTP_OTP_ADDR, addr)) { |
|
return false; |
|
} |
|
|
|
if (!write_reg(REG_OTP_MTP_OTP_CMD, cmd)) { |
|
return false; |
|
} |
|
|
|
/* Wait for the OTP read to finish Monitor otp_status */ |
|
do { |
|
read_reg(REG_OTP_MTP_OTP_STATUS, &otp_status); |
|
|
|
if (otp_status == 0) { |
|
break; |
|
} |
|
|
|
hal.scheduler->delay_microseconds(1); |
|
} while (1); |
|
|
|
/* Read the data from register */ |
|
if (!read_reg(REG_OTP_MTP_RD_DATA, val)) { |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool AP_Baro_ICP201XX::get_sensor_data(float *pressure, float *temperature) |
|
{ |
|
uint8_t fifo_data[96] {0}; |
|
uint8_t fifo_packets = 0; |
|
int32_t data_temp = 0; |
|
int32_t data_press = 0; |
|
*pressure = 0; |
|
*temperature = 0; |
|
|
|
if (read_reg(REG_FIFO_FILL, &fifo_packets)) { |
|
fifo_packets = (uint8_t)(fifo_packets & 0x1F); |
|
if (fifo_packets > 16) { |
|
flush_fifo(); |
|
return false; |
|
} |
|
if (fifo_packets > 0 && fifo_packets <= 16 && read_reg(REG_FIFO_BASE, fifo_data, fifo_packets * 2 * 3)) { |
|
uint8_t offset = 0; |
|
|
|
for (uint8_t i = 0; i < fifo_packets; i++) { |
|
data_press = (int32_t)(((fifo_data[offset + 2] & 0x0f) << 16) | (fifo_data[offset + 1] << 8) | fifo_data[offset]); |
|
if (data_press & 0x080000) { |
|
data_press |= 0xFFF00000; |
|
} |
|
/* P = (POUT/2^17)*40kPa + 70kPa */ |
|
*pressure += ((float)(data_press) * 40 / 131072) + 70; |
|
offset += 3; |
|
|
|
data_temp = (int32_t)(((fifo_data[offset + 2] & 0x0f) << 16) | (fifo_data[offset + 1] << 8) | fifo_data[offset]); |
|
if (data_temp & 0x080000) { |
|
data_temp |= 0xFFF00000; |
|
} |
|
/* T = (TOUT/2^18)*65C + 25C */ |
|
*temperature += ((float)(data_temp) * 65 / 262144) + 25; |
|
offset += 3; |
|
} |
|
|
|
*pressure = *pressure * 1000 / fifo_packets; |
|
*temperature = *temperature / fifo_packets; |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
bool AP_Baro_ICP201XX::boot_sequence() |
|
{ |
|
uint8_t reg_value = 0; |
|
uint8_t offset = 0, gain = 0, Hfosc = 0; |
|
uint8_t version = 0; |
|
uint8_t bootup_status = 0; |
|
int ret = 1; |
|
|
|
/* read version register */ |
|
if (!read_reg(REG_VERSION, &version)) { |
|
return false; |
|
} |
|
|
|
if (version == 0xB2) { |
|
/* B2 version Asic is detected. Boot up sequence is not required for B2 Asic, so returning */ |
|
return true; |
|
} |
|
|
|
/* Read boot up status and avoid re running boot up sequence if it is already done */ |
|
if (!read_reg(REG_OTP_MTP_OTP_STATUS2, &bootup_status)) { |
|
return false; |
|
} |
|
|
|
if (bootup_status & 0x01) { |
|
/* Boot up sequence is already done, not required to repeat boot up sequence */ |
|
return true; |
|
} |
|
|
|
/* Bring the ASIC in power mode to activate the OTP power domain and get access to the main registers */ |
|
mode_select(0x04); |
|
hal.scheduler->delay(4); |
|
|
|
/* Unlock the main registers */ |
|
write_reg(REG_MASTER_LOCK, 0x1F); |
|
|
|
/* Enable the OTP and the write switch */ |
|
read_reg(REG_OTP_MTP_OTP_CFG1, ®_value); |
|
reg_value |= 0x03; |
|
write_reg(REG_OTP_MTP_OTP_CFG1, reg_value); |
|
hal.scheduler->delay_microseconds(10); |
|
|
|
/* Toggle the OTP reset pin */ |
|
read_reg(REG_OTP_DEBUG2, ®_value); |
|
reg_value |= 1 << 7; |
|
write_reg(REG_OTP_DEBUG2, reg_value); |
|
hal.scheduler->delay_microseconds(10); |
|
|
|
read_reg(REG_OTP_DEBUG2, ®_value); |
|
reg_value &= ~(1 << 7); |
|
write_reg(REG_OTP_DEBUG2, reg_value); |
|
hal.scheduler->delay_microseconds(10); |
|
|
|
/* Program redundant read */ |
|
write_reg(REG_OTP_MTP_MRA_LSB, 0x04); |
|
write_reg(REG_OTP_MTP_MRA_MSB, 0x04); |
|
write_reg(REG_OTP_MTP_MRB_LSB, 0x21); |
|
write_reg(REG_OTP_MTP_MRB_MSB, 0x20); |
|
write_reg(REG_OTP_MTP_MR_LSB, 0x10); |
|
write_reg(REG_OTP_MTP_MR_MSB, 0x80); |
|
|
|
/* Read the data from register */ |
|
ret &= read_otp_data(0xF8, 0x10, &offset); |
|
ret &= read_otp_data(0xF9, 0x10, &gain); |
|
ret &= read_otp_data(0xFA, 0x10, &Hfosc); |
|
hal.scheduler->delay_microseconds(10); |
|
|
|
/* Write OTP values to main registers */ |
|
ret &= read_reg(REG_TRIM1_MSB, ®_value); |
|
if (ret) { |
|
reg_value = (reg_value & (~0x3F)) | (offset & 0x3F); |
|
ret &= write_reg(REG_TRIM1_MSB, reg_value); |
|
} |
|
|
|
ret &= read_reg(REG_TRIM2_MSB, ®_value); |
|
if (ret) { |
|
reg_value = (reg_value & (~0x70)) | ((gain & 0x07) << 4); |
|
ret &= write_reg(REG_TRIM2_MSB, reg_value); |
|
} |
|
|
|
ret &= read_reg(REG_TRIM2_LSB, ®_value); |
|
if (ret) { |
|
reg_value = (reg_value & (~0x7F)) | (Hfosc & 0x7F); |
|
ret &= write_reg(REG_TRIM2_LSB, reg_value); |
|
} |
|
|
|
hal.scheduler->delay_microseconds(10); |
|
|
|
/* Update boot up status to 1 */ |
|
if (ret) { |
|
ret &= read_reg(REG_OTP_MTP_OTP_STATUS2, ®_value); |
|
if (!ret) { |
|
reg_value |= 0x01; |
|
ret &= write_reg(REG_OTP_MTP_OTP_STATUS2, reg_value); |
|
} |
|
} |
|
|
|
/* Disable OTP and write switch */ |
|
read_reg(REG_OTP_MTP_OTP_CFG1, ®_value); |
|
reg_value &= ~0x03; |
|
write_reg(REG_OTP_MTP_OTP_CFG1, reg_value); |
|
|
|
/* Lock the main register */ |
|
write_reg(REG_MASTER_LOCK, 0x00); |
|
|
|
/* Move to standby */ |
|
mode_select(0x00); |
|
|
|
return ret; |
|
} |
|
|
|
bool AP_Baro_ICP201XX::configure() |
|
{ |
|
uint8_t reg_value = 0; |
|
|
|
/* Initiate Triggered Operation: Stay in Standby mode */ |
|
reg_value |= (reg_value & (~0x10)) | ((uint8_t)_forced_meas_trigger << 4); |
|
|
|
/* Power Mode Selection: Normal Mode */ |
|
reg_value |= (reg_value & (~0x04)) | ((uint8_t)_power_mode << 2); |
|
|
|
/* FIFO Readout Mode Selection: Pressure first. */ |
|
reg_value |= (reg_value & (~0x03)) | ((uint8_t)(_fifo_readout_mode)); |
|
|
|
/* Measurement Configuration: Mode2*/ |
|
reg_value |= (reg_value & (~0xE0)) | (((uint8_t)_op_mode) << 5); |
|
|
|
/* Measurement Mode Selection: Continuous Measurements (duty cycled) */ |
|
reg_value |= (reg_value & (~0x08)) | ((uint8_t)_meas_mode << 3); |
|
|
|
return mode_select(reg_value); |
|
} |
|
|
|
void AP_Baro_ICP201XX::wait_read() |
|
{ |
|
/* |
|
* If FIR filter is enabled, it will cause a settling effect on the first 14 pressure values. |
|
* Therefore the first 14 pressure output values are discarded. |
|
**/ |
|
uint8_t fifo_packets = 0; |
|
uint8_t fifo_packets_to_skip = 14; |
|
|
|
do { |
|
hal.scheduler->delay(10); |
|
read_reg(REG_FIFO_FILL, &fifo_packets); |
|
fifo_packets = (uint8_t)(fifo_packets & 0x1F); |
|
} while (fifo_packets >= fifo_packets_to_skip); |
|
|
|
flush_fifo(); |
|
fifo_packets = 0; |
|
|
|
do { |
|
hal.scheduler->delay(10); |
|
read_reg(REG_FIFO_FILL, &fifo_packets); |
|
fifo_packets = (uint8_t)(fifo_packets & 0x1F); |
|
} while (fifo_packets == 0); |
|
} |
|
|
|
bool AP_Baro_ICP201XX::flush_fifo() |
|
{ |
|
uint8_t reg_value; |
|
|
|
if (!read_reg(REG_FIFO_FILL, ®_value)) { |
|
return false; |
|
} |
|
|
|
reg_value |= 0x80; |
|
|
|
if (!write_reg(REG_FIFO_FILL, reg_value)) { |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
void AP_Baro_ICP201XX::timer() |
|
{ |
|
float p = 0; |
|
float t = 0; |
|
|
|
if (get_sensor_data(&p, &t)) { |
|
WITH_SEMAPHORE(_sem); |
|
|
|
accum.psum += p; |
|
accum.tsum += t; |
|
accum.count++; |
|
last_measure_us = AP_HAL::micros(); |
|
} else { |
|
if (AP_HAL::micros() - last_measure_us > CONVERSION_INTERVAL*3) { |
|
flush_fifo(); |
|
last_measure_us = AP_HAL::micros(); |
|
} |
|
} |
|
} |
|
|
|
void AP_Baro_ICP201XX::update() |
|
{ |
|
WITH_SEMAPHORE(_sem); |
|
|
|
if (accum.count > 0) { |
|
_copy_to_frontend(instance, accum.psum/accum.count, accum.tsum/accum.count); |
|
accum.psum = accum.tsum = 0; |
|
accum.count = 0; |
|
} |
|
} |
|
|
|
#endif // AP_BARO_ICP201XX_ENABLED
|
|
|