790 lines
23 KiB
C
790 lines
23 KiB
C
|
/*
|
||
|
* Copyright (C) 2016 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <float.h>
|
||
|
|
||
|
#include <eventnums.h>
|
||
|
#include <heap.h>
|
||
|
#include <hostIntf.h>
|
||
|
#include <i2c.h>
|
||
|
#include <nanohubPacket.h>
|
||
|
#include <sensors.h>
|
||
|
#include <seos.h>
|
||
|
#include <slab.h>
|
||
|
#include <timer.h>
|
||
|
#include <util.h>
|
||
|
#include <variant/variant.h>
|
||
|
|
||
|
#define BMP280_APP_ID APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 5)
|
||
|
|
||
|
#define BMP280_APP_VERSION 4
|
||
|
|
||
|
#ifndef BMP280_I2C_BUS_ID
|
||
|
#define BMP280_I2C_BUS_ID 0
|
||
|
#endif
|
||
|
|
||
|
#define I2C_BUS_ID BMP280_I2C_BUS_ID
|
||
|
#define I2C_SPEED 400000
|
||
|
#define I2C_ADDR 0x76
|
||
|
|
||
|
#define BOSCH_BMP280_ID 0x58
|
||
|
|
||
|
#define BOSCH_BMP280_REG_RESET 0xE0
|
||
|
#define BOSCH_BMP280_REG_DIG_T1 0x88
|
||
|
#define BOSCH_BMP280_REG_ID 0xd0
|
||
|
#define BOSCH_BMP280_REG_CTRL_MEAS 0xf4
|
||
|
#define BOSCH_BMP280_REG_CONFIG 0xf5
|
||
|
#define BOSCH_BMP280_REG_PRES_MSB 0xf7
|
||
|
|
||
|
#define BOSCH_BMP280_SOFT_RESET_CMD 0xB6
|
||
|
|
||
|
#define BOSCH_BMP280_MAX_PENDING_I2C_REQUESTS 4
|
||
|
#define BOSCH_BMP280_MAX_I2C_TRANSFER_SIZE 6
|
||
|
|
||
|
// This defines how many baro events we could handle being backed up in the
|
||
|
// queue. Use this to size our slab
|
||
|
#define MAX_BARO_EVENTS 4
|
||
|
|
||
|
// temp: 2x oversampling, baro: 16x oversampling, power: normal
|
||
|
#define CTRL_ON ((2 << 5) | (5 << 2) | 3)
|
||
|
// temp: 2x oversampling, baro: 16x oversampling, power: sleep
|
||
|
#define CTRL_SLEEP ((2 << 5) | (5 << 2))
|
||
|
// config: standby time: 62.5ms, IIR filter coefficient: 4
|
||
|
#define CTRL_CFG ((1 << 5) | (2 << 2))
|
||
|
|
||
|
enum BMP280SensorEvents
|
||
|
{
|
||
|
EVT_SENSOR_I2C = EVT_APP_START + 1,
|
||
|
EVT_SENSOR_BARO_TIMER,
|
||
|
EVT_SENSOR_TEMP_TIMER,
|
||
|
EVT_SENSOR_SOFTRESET_TIMER,
|
||
|
};
|
||
|
|
||
|
enum BMP280TaskState
|
||
|
{
|
||
|
STATE_RESET,
|
||
|
STATE_SOFTRESET,
|
||
|
STATE_SOFTRESET_MODE,
|
||
|
STATE_VERIFY_ID,
|
||
|
STATE_AWAITING_COMP_PARAMS,
|
||
|
STATE_CONFIG,
|
||
|
STATE_FINISH_INIT,
|
||
|
STATE_IDLE,
|
||
|
STATE_ENABLING_BARO_TEMP,
|
||
|
STATE_ENABLING_BARO,
|
||
|
STATE_ENABLING_TEMP,
|
||
|
STATE_DISABLING_BARO,
|
||
|
STATE_DISABLING_TEMP,
|
||
|
STATE_SAMPLING,
|
||
|
};
|
||
|
|
||
|
struct BMP280CompParams
|
||
|
{
|
||
|
uint16_t dig_T1;
|
||
|
int16_t dig_T2, dig_T3;
|
||
|
uint16_t dig_P1;
|
||
|
int16_t dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;
|
||
|
} __attribute__((packed));
|
||
|
|
||
|
struct I2cTransfer
|
||
|
{
|
||
|
size_t tx;
|
||
|
size_t rx;
|
||
|
int err;
|
||
|
uint8_t txrxBuf[BOSCH_BMP280_MAX_I2C_TRANSFER_SIZE];
|
||
|
uint8_t state;
|
||
|
bool inUse;
|
||
|
};
|
||
|
|
||
|
static struct BMP280Task
|
||
|
{
|
||
|
struct BMP280CompParams comp;
|
||
|
|
||
|
struct SlabAllocator *evtSlab;
|
||
|
|
||
|
uint32_t id;
|
||
|
uint32_t baroHandle;
|
||
|
uint32_t tempHandle;
|
||
|
uint32_t baroTimerHandle;
|
||
|
uint32_t tempTimerHandle;
|
||
|
uint32_t resetHandle;
|
||
|
|
||
|
float offset;
|
||
|
|
||
|
struct I2cTransfer transfers[BOSCH_BMP280_MAX_PENDING_I2C_REQUESTS];
|
||
|
|
||
|
bool tmpbaroOn;
|
||
|
bool tmptempOn;
|
||
|
bool baroOn;
|
||
|
bool tempOn;
|
||
|
bool baroReading;
|
||
|
bool baroCalibrating;
|
||
|
bool tempReading;
|
||
|
} mTask;
|
||
|
|
||
|
struct CalibrationData {
|
||
|
struct HostHubRawPacket header;
|
||
|
struct SensorAppEventHeader data_header;
|
||
|
float value;
|
||
|
} __attribute__((packed));
|
||
|
|
||
|
static const uint32_t tempSupportedRates[] =
|
||
|
{
|
||
|
SENSOR_HZ(0.1),
|
||
|
SENSOR_HZ(1),
|
||
|
SENSOR_HZ(5),
|
||
|
SENSOR_HZ(10),
|
||
|
SENSOR_HZ(25),
|
||
|
0,
|
||
|
};
|
||
|
|
||
|
static const uint64_t rateTimerValsTemp[] = //should match "supported rates in length" and be the timer length for that rate in nanosecs
|
||
|
{
|
||
|
10 * 1000000000ULL,
|
||
|
1 * 1000000000ULL,
|
||
|
1000000000ULL / 5,
|
||
|
1000000000ULL / 10,
|
||
|
1000000000ULL / 25,
|
||
|
};
|
||
|
|
||
|
static const uint32_t baroSupportedRates[] =
|
||
|
{
|
||
|
SENSOR_HZ(0.1),
|
||
|
SENSOR_HZ(1),
|
||
|
SENSOR_HZ(5),
|
||
|
SENSOR_HZ(10),
|
||
|
0
|
||
|
};
|
||
|
|
||
|
static const uint64_t rateTimerValsBaro[] = //should match "supported rates in length" and be the timer length for that rate in nanosecs
|
||
|
{
|
||
|
10 * 1000000000ULL,
|
||
|
1 * 1000000000ULL,
|
||
|
1000000000ULL / 5,
|
||
|
1000000000ULL / 10,
|
||
|
};
|
||
|
|
||
|
static void i2cCallback(void *cookie, size_t tx, size_t rx, int err);
|
||
|
|
||
|
// Allocate a buffer and mark it as in use with the given state, or return NULL
|
||
|
// if no buffers available. Must *not* be called from interrupt context.
|
||
|
static struct I2cTransfer *allocXfer(uint8_t state)
|
||
|
{
|
||
|
size_t i;
|
||
|
|
||
|
for (i = 0; i < ARRAY_SIZE(mTask.transfers); i++) {
|
||
|
if (!mTask.transfers[i].inUse) {
|
||
|
mTask.transfers[i].inUse = true;
|
||
|
mTask.transfers[i].state = state;
|
||
|
return &mTask.transfers[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
osLog(LOG_ERROR, "[BMP280]: Ran out of i2c buffers!");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// Helper function to release I2cTransfer structure
|
||
|
static inline void releaseXfer(struct I2cTransfer *xfer)
|
||
|
{
|
||
|
xfer->inUse = false;
|
||
|
}
|
||
|
|
||
|
// Helper function to write a one byte register. Returns true if we got a
|
||
|
// successful return value from i2cMasterTx().
|
||
|
static bool writeRegister(uint8_t reg, uint8_t value, uint8_t state)
|
||
|
{
|
||
|
struct I2cTransfer *xfer = allocXfer(state);
|
||
|
int ret = -1;
|
||
|
|
||
|
if (xfer != NULL) {
|
||
|
xfer->txrxBuf[0] = reg;
|
||
|
xfer->txrxBuf[1] = value;
|
||
|
ret = i2cMasterTx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 2, i2cCallback, xfer);
|
||
|
if (ret)
|
||
|
releaseXfer(xfer);
|
||
|
}
|
||
|
|
||
|
return (ret == 0);
|
||
|
}
|
||
|
|
||
|
static bool baroAllocateEvt(struct SingleAxisDataEvent **evPtr, float sample, uint64_t time)
|
||
|
{
|
||
|
struct SingleAxisDataEvent *ev;
|
||
|
|
||
|
*evPtr = slabAllocatorAlloc(mTask.evtSlab);
|
||
|
|
||
|
ev = *evPtr;
|
||
|
if (!ev) {
|
||
|
osLog(LOG_ERROR, "[BMP280] slabAllocatorAlloc() failed\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
memset(&ev->samples[0].firstSample, 0x00, sizeof(struct SensorFirstSample));
|
||
|
ev->referenceTime = time;
|
||
|
ev->samples[0].firstSample.numSamples = 1;
|
||
|
ev->samples[0].fdata = sample;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void baroFreeEvt(void *ptr)
|
||
|
{
|
||
|
slabAllocatorFree(mTask.evtSlab, ptr);
|
||
|
}
|
||
|
|
||
|
/* sensor callbacks from nanohub */
|
||
|
|
||
|
static void i2cCallback(void *cookie, size_t tx, size_t rx, int err)
|
||
|
{
|
||
|
struct I2cTransfer *xfer = cookie;
|
||
|
|
||
|
xfer->tx = tx;
|
||
|
xfer->rx = rx;
|
||
|
xfer->err = err;
|
||
|
|
||
|
osEnqueuePrivateEvt(EVT_SENSOR_I2C, cookie, NULL, mTask.id);
|
||
|
if (err != 0)
|
||
|
osLog(LOG_INFO, "[BMP280] i2c error (tx: %d, rx: %d, err: %d)\n", tx, rx, err);
|
||
|
}
|
||
|
|
||
|
static void baroTimerCallback(uint32_t timerId, void *cookie)
|
||
|
{
|
||
|
osEnqueuePrivateEvt(EVT_SENSOR_BARO_TIMER, cookie, NULL, mTask.id);
|
||
|
}
|
||
|
|
||
|
static void tempTimerCallback(uint32_t timerId, void *cookie)
|
||
|
{
|
||
|
osEnqueuePrivateEvt(EVT_SENSOR_TEMP_TIMER, cookie, NULL, mTask.id);
|
||
|
}
|
||
|
|
||
|
static void softresetCallback(uint32_t timerId, void *cookie)
|
||
|
{
|
||
|
osEnqueuePrivateEvt(EVT_SENSOR_SOFTRESET_TIMER, cookie, NULL, mTask.id);
|
||
|
}
|
||
|
|
||
|
static void softreset()
|
||
|
{
|
||
|
writeRegister(BOSCH_BMP280_REG_RESET, BOSCH_BMP280_SOFT_RESET_CMD, STATE_SOFTRESET);
|
||
|
}
|
||
|
|
||
|
static void setMode(bool on, uint8_t state)
|
||
|
{
|
||
|
writeRegister(BOSCH_BMP280_REG_CTRL_MEAS, (on) ? CTRL_ON : CTRL_SLEEP, state);
|
||
|
}
|
||
|
|
||
|
static void sendCalibrationResult(uint8_t status, float value) {
|
||
|
struct CalibrationData *data = heapAlloc(sizeof(struct CalibrationData));
|
||
|
if (!data) {
|
||
|
osLog(LOG_WARN, "[BMP280] Couldn't alloc cal result pkt");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
data->header.appId = BMP280_APP_ID;
|
||
|
data->header.dataLen = (sizeof(struct CalibrationData) - sizeof(struct HostHubRawPacket));
|
||
|
data->data_header.msgId = SENSOR_APP_MSG_ID_CAL_RESULT;
|
||
|
data->data_header.sensorType = SENS_TYPE_BARO;
|
||
|
data->data_header.status = status;
|
||
|
|
||
|
data->value = value;
|
||
|
|
||
|
if (!osEnqueueEvtOrFree(EVT_APP_TO_HOST, data, heapFree))
|
||
|
osLog(LOG_WARN, "[BMP280] Couldn't send cal result evt");
|
||
|
}
|
||
|
|
||
|
// TODO: only turn on the timer when enabled
|
||
|
static bool sensorPowerBaro(bool on, void *cookie)
|
||
|
{
|
||
|
bool oldMode = mTask.baroOn || mTask.tempOn;
|
||
|
bool newMode = on || mTask.tempOn;
|
||
|
|
||
|
if (!on && mTask.baroTimerHandle) {
|
||
|
timTimerCancel(mTask.baroTimerHandle);
|
||
|
mTask.baroTimerHandle = 0;
|
||
|
mTask.baroReading = false;
|
||
|
}
|
||
|
|
||
|
if (!on && mTask.tmpbaroOn && mTask.resetHandle)
|
||
|
{
|
||
|
if (!mTask.tmptempOn) {
|
||
|
timTimerCancel(mTask.resetHandle);
|
||
|
mTask.resetHandle = 0;
|
||
|
}
|
||
|
mTask.tmpbaroOn = 0;
|
||
|
sensorSignalInternalEvt(mTask.baroHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
|
||
|
}
|
||
|
|
||
|
if (oldMode != newMode)
|
||
|
{
|
||
|
if (newMode == 0)
|
||
|
{
|
||
|
setMode(newMode, STATE_DISABLING_BARO);
|
||
|
mTask.baroOn = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mTask.tmpbaroOn = true;
|
||
|
if (!mTask.tmptempOn) {
|
||
|
// do soft reset first when newMode is on
|
||
|
softreset();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sensorSignalInternalEvt(mTask.baroHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0);
|
||
|
mTask.baroOn = on;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool sensorFirmwareBaro(void *cookie)
|
||
|
{
|
||
|
return sensorSignalInternalEvt(mTask.baroHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
|
||
|
}
|
||
|
|
||
|
static bool sensorRateBaro(uint32_t rate, uint64_t latency, void *cookie)
|
||
|
{
|
||
|
if (mTask.baroTimerHandle)
|
||
|
timTimerCancel(mTask.baroTimerHandle);
|
||
|
mTask.baroTimerHandle = timTimerSet(sensorTimerLookupCommon(baroSupportedRates, rateTimerValsBaro, rate), 0, 50, baroTimerCallback, NULL, false);
|
||
|
return sensorSignalInternalEvt(mTask.baroHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
|
||
|
}
|
||
|
|
||
|
static bool sensorFlushBaro(void *cookie)
|
||
|
{
|
||
|
return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_BARO), SENSOR_DATA_EVENT_FLUSH, NULL);
|
||
|
}
|
||
|
|
||
|
static bool sensorCalibrateBaro(void *cookie)
|
||
|
{
|
||
|
if (mTask.baroOn || mTask.tempOn) {
|
||
|
osLog(LOG_ERROR, "[BMP280] cannot calibrate while baro or temp are active\n");
|
||
|
sendCalibrationResult(SENSOR_APP_EVT_STATUS_BUSY, 0.0f);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (mTask.baroTimerHandle)
|
||
|
timTimerCancel(mTask.baroTimerHandle);
|
||
|
mTask.baroTimerHandle = timTimerSet(100000000ull, 0, 50, baroTimerCallback, NULL, false);
|
||
|
|
||
|
mTask.offset = 0.0f;
|
||
|
mTask.baroOn = true;
|
||
|
mTask.baroCalibrating = true;
|
||
|
|
||
|
return writeRegister(BOSCH_BMP280_REG_CTRL_MEAS, CTRL_ON, STATE_IDLE);
|
||
|
}
|
||
|
|
||
|
static bool sensorCfgDataBaro(void *data, void *cookie)
|
||
|
{
|
||
|
mTask.offset = *((float*)data) * 100.0f; // offset is given in hPa, but used as Pa in compensation
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool sensorPowerTemp(bool on, void *cookie)
|
||
|
{
|
||
|
bool oldMode = mTask.baroOn || mTask.tempOn;
|
||
|
bool newMode = on || mTask.baroOn;
|
||
|
|
||
|
if (!on && mTask.tempTimerHandle) {
|
||
|
timTimerCancel(mTask.tempTimerHandle);
|
||
|
mTask.tempTimerHandle = 0;
|
||
|
mTask.tempReading = false;
|
||
|
}
|
||
|
|
||
|
if (!on && mTask.tmptempOn && mTask.resetHandle)
|
||
|
{
|
||
|
if(!mTask.tmpbaroOn) {
|
||
|
timTimerCancel(mTask.resetHandle);
|
||
|
mTask.resetHandle = 0;
|
||
|
}
|
||
|
mTask.tmptempOn = 0;
|
||
|
sensorSignalInternalEvt(mTask.tempHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
|
||
|
}
|
||
|
|
||
|
if (oldMode != newMode)
|
||
|
{
|
||
|
if (newMode == 0)
|
||
|
{
|
||
|
setMode(newMode, STATE_DISABLING_TEMP);
|
||
|
mTask.tempOn = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mTask.tmptempOn = true;
|
||
|
if (!mTask.tmpbaroOn) {
|
||
|
// do soft reset first when newMode is on
|
||
|
softreset();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sensorSignalInternalEvt(mTask.tempHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0);
|
||
|
mTask.tempOn = on;
|
||
|
}
|
||
|
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool sensorFirmwareTemp(void *cookie)
|
||
|
{
|
||
|
sensorSignalInternalEvt(mTask.tempHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool sensorRateTemp(uint32_t rate, uint64_t latency, void *cookie)
|
||
|
{
|
||
|
if (mTask.tempTimerHandle)
|
||
|
timTimerCancel(mTask.tempTimerHandle);
|
||
|
mTask.tempTimerHandle = timTimerSet(sensorTimerLookupCommon(tempSupportedRates, rateTimerValsTemp, rate), 0, 50, tempTimerCallback, NULL, false);
|
||
|
sensorSignalInternalEvt(mTask.tempHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static bool sensorFlushTemp(void *cookie)
|
||
|
{
|
||
|
return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_TEMP), SENSOR_DATA_EVENT_FLUSH, NULL);
|
||
|
}
|
||
|
|
||
|
static const struct SensorInfo sensorInfoBaro =
|
||
|
{
|
||
|
.sensorName = "Pressure",
|
||
|
.supportedRates = baroSupportedRates,
|
||
|
.sensorType = SENS_TYPE_BARO,
|
||
|
.numAxis = NUM_AXIS_ONE,
|
||
|
.interrupt = NANOHUB_INT_NONWAKEUP,
|
||
|
.minSamples = 300
|
||
|
};
|
||
|
|
||
|
static const struct SensorOps sensorOpsBaro =
|
||
|
{
|
||
|
.sensorPower = sensorPowerBaro,
|
||
|
.sensorFirmwareUpload = sensorFirmwareBaro,
|
||
|
.sensorSetRate = sensorRateBaro,
|
||
|
.sensorFlush = sensorFlushBaro,
|
||
|
.sensorCalibrate = sensorCalibrateBaro,
|
||
|
.sensorCfgData = sensorCfgDataBaro,
|
||
|
};
|
||
|
|
||
|
static const struct SensorInfo sensorInfoTemp =
|
||
|
{
|
||
|
.sensorName = "Temperature",
|
||
|
.supportedRates = tempSupportedRates,
|
||
|
.sensorType = SENS_TYPE_TEMP,
|
||
|
.numAxis = NUM_AXIS_EMBEDDED,
|
||
|
.interrupt = NANOHUB_INT_NONWAKEUP,
|
||
|
.minSamples = 20
|
||
|
};
|
||
|
|
||
|
static const struct SensorOps sensorOpsTemp =
|
||
|
{
|
||
|
.sensorPower = sensorPowerTemp,
|
||
|
.sensorFirmwareUpload = sensorFirmwareTemp,
|
||
|
.sensorSetRate = sensorRateTemp,
|
||
|
.sensorFlush = sensorFlushTemp,
|
||
|
};
|
||
|
|
||
|
// Returns temperature in units of 0.01 degrees celsius.
|
||
|
static int32_t compensateTemp( int32_t adc_T, int32_t *t_fine)
|
||
|
{
|
||
|
int32_t var1 =
|
||
|
(((adc_T >> 3) - ((int32_t)mTask.comp.dig_T1 << 1))
|
||
|
* (int32_t)mTask.comp.dig_T2) >> 11;
|
||
|
|
||
|
int32_t tmp = (adc_T >> 4) - (int32_t)mTask.comp.dig_T1;
|
||
|
|
||
|
int32_t var2 = (((tmp * tmp) >> 12) * (int32_t)mTask.comp.dig_T3) >> 14;
|
||
|
|
||
|
int32_t sum = var1 + var2;
|
||
|
|
||
|
*t_fine = sum;
|
||
|
|
||
|
return (sum * 5 + 128) >> 8;
|
||
|
}
|
||
|
|
||
|
static float compensateBaro(int32_t t_fine, int32_t adc_P)
|
||
|
{
|
||
|
float f = t_fine - 128000, fSqr = f * f;
|
||
|
float a = 1048576 - adc_P;
|
||
|
float v1, v2, p, pSqr;
|
||
|
|
||
|
v2 = fSqr * mTask.comp.dig_P6 + f * mTask.comp.dig_P5 * (float)(1ULL << 17) + mTask.comp.dig_P4 * (float)(1ULL << 35);
|
||
|
v1 = fSqr * mTask.comp.dig_P1 * mTask.comp.dig_P3 * (1.0f/(1ULL << 41)) + f * mTask.comp.dig_P1 * mTask.comp.dig_P2 * (1.0f/(1ULL << 21)) + mTask.comp.dig_P1 * (float)(1ULL << 14);
|
||
|
|
||
|
p = (a * (float)(1ULL << 31) - v2) * 3125 / v1;
|
||
|
pSqr = p * p;
|
||
|
|
||
|
return pSqr * mTask.comp.dig_P9 * (1.0f/(1ULL << 59)) + p * (mTask.comp.dig_P8 * (1.0f/(1ULL << 19)) + 1) * (1.0f/(1ULL << 8)) + 16.0f * mTask.comp.dig_P7;
|
||
|
}
|
||
|
|
||
|
static void getTempAndBaro(const uint8_t *tmp, float *pressure_Pa, float *temp_centigrade)
|
||
|
{
|
||
|
int32_t pres_adc = ((int32_t)tmp[0] << 12) | ((int32_t)tmp[1] << 4) | (tmp[2] >> 4);
|
||
|
int32_t temp_adc = ((int32_t)tmp[3] << 12) | ((int32_t)tmp[4] << 4) | (tmp[5] >> 4);
|
||
|
|
||
|
int32_t T_fine;
|
||
|
int32_t temp = compensateTemp(temp_adc, &T_fine);
|
||
|
float pres = compensateBaro(T_fine, pres_adc);
|
||
|
|
||
|
*temp_centigrade = (float)temp * 0.01f;
|
||
|
*pressure_Pa = pres * (1.0f / 256.0f) + mTask.offset;
|
||
|
}
|
||
|
|
||
|
static void handleI2cEvent(struct I2cTransfer *xfer)
|
||
|
{
|
||
|
union EmbeddedDataPoint embeddedSample;
|
||
|
struct SingleAxisDataEvent *baroSample;
|
||
|
struct I2cTransfer *newXfer;
|
||
|
int ret;
|
||
|
|
||
|
switch (xfer->state) {
|
||
|
case STATE_RESET: {
|
||
|
newXfer = allocXfer(STATE_VERIFY_ID);
|
||
|
if (newXfer != NULL) {
|
||
|
newXfer->txrxBuf[0] = BOSCH_BMP280_REG_ID;
|
||
|
ret = i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, newXfer->txrxBuf, 1, newXfer->txrxBuf, 1, i2cCallback, newXfer);
|
||
|
if (ret)
|
||
|
releaseXfer(newXfer);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_SOFTRESET: {
|
||
|
//create timer for 2ms delay
|
||
|
mTask.resetHandle = timTimerSet(2000000ull, 0, 50, softresetCallback, NULL, true);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_SOFTRESET_MODE: {
|
||
|
if (mTask.tmpbaroOn && mTask.tmptempOn) {
|
||
|
setMode(true,STATE_ENABLING_BARO_TEMP);
|
||
|
mTask.tmpbaroOn = false;
|
||
|
mTask.baroOn = true;
|
||
|
mTask.tmptempOn = false;
|
||
|
mTask.tempOn = true;
|
||
|
} else if (mTask.tmpbaroOn) {
|
||
|
setMode(true,STATE_ENABLING_BARO);
|
||
|
mTask.tmpbaroOn = false;
|
||
|
mTask.baroOn = true;
|
||
|
} else if (mTask.tmptempOn) {
|
||
|
setMode(true,STATE_ENABLING_TEMP);
|
||
|
mTask.tmptempOn = false;
|
||
|
mTask.tempOn = true;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_VERIFY_ID: {
|
||
|
/* Check the sensor ID */
|
||
|
if (xfer->err != 0 || xfer->txrxBuf[0] != BOSCH_BMP280_ID) {
|
||
|
osLog(LOG_INFO, "[BMP280] not detected\n");
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* Get compensation parameters */
|
||
|
newXfer = allocXfer(STATE_AWAITING_COMP_PARAMS);
|
||
|
if (newXfer != NULL) {
|
||
|
newXfer->txrxBuf[0] = BOSCH_BMP280_REG_DIG_T1;
|
||
|
ret = i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, newXfer->txrxBuf, 1, (uint8_t*)&mTask.comp, 24, i2cCallback, newXfer);
|
||
|
if (ret)
|
||
|
releaseXfer(newXfer);
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_AWAITING_COMP_PARAMS: {
|
||
|
writeRegister(BOSCH_BMP280_REG_CTRL_MEAS, CTRL_SLEEP, STATE_CONFIG);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_CONFIG: {
|
||
|
// standby time: 62.5ms, IIR filter coefficient: 4
|
||
|
writeRegister(BOSCH_BMP280_REG_CONFIG, CTRL_CFG, STATE_FINISH_INIT);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_ENABLING_BARO_TEMP: {
|
||
|
sensorSignalInternalEvt(mTask.baroHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
|
||
|
sensorSignalInternalEvt(mTask.tempHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_ENABLING_BARO: {
|
||
|
sensorSignalInternalEvt(mTask.baroHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_ENABLING_TEMP: {
|
||
|
sensorSignalInternalEvt(mTask.tempHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_DISABLING_BARO: {
|
||
|
sensorSignalInternalEvt(mTask.baroHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_DISABLING_TEMP: {
|
||
|
sensorSignalInternalEvt(mTask.tempHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_FINISH_INIT: {
|
||
|
osLog(LOG_INFO, "[BMP280] detected\n");
|
||
|
sensorRegisterInitComplete(mTask.baroHandle);
|
||
|
sensorRegisterInitComplete(mTask.tempHandle);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STATE_SAMPLING: {
|
||
|
float pressure_Pa, temp_centigrade;
|
||
|
getTempAndBaro(xfer->txrxBuf, &pressure_Pa, &temp_centigrade);
|
||
|
|
||
|
if (mTask.baroOn && mTask.baroReading) {
|
||
|
if (mTask.baroCalibrating) {
|
||
|
sendCalibrationResult(SENSOR_APP_EVT_STATUS_SUCCESS, pressure_Pa * 0.01f);
|
||
|
|
||
|
if (mTask.baroTimerHandle)
|
||
|
timTimerCancel(mTask.baroTimerHandle);
|
||
|
|
||
|
mTask.baroOn = false;
|
||
|
mTask.baroCalibrating = false;
|
||
|
|
||
|
writeRegister(BOSCH_BMP280_REG_CTRL_MEAS, CTRL_SLEEP, STATE_IDLE);
|
||
|
} else {
|
||
|
if (baroAllocateEvt(&baroSample, pressure_Pa * 0.01f, sensorGetTime())) {
|
||
|
if (!osEnqueueEvtOrFree(EVENT_TYPE_BIT_DISCARDABLE | sensorGetMyEventType(SENS_TYPE_BARO), baroSample, baroFreeEvt)) {
|
||
|
osLog(LOG_ERROR, "[BMP280] failed to enqueue baro sample\n");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mTask.tempOn && mTask.tempReading) {
|
||
|
embeddedSample.fdata = temp_centigrade;
|
||
|
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_TEMP), embeddedSample.vptr, NULL);
|
||
|
}
|
||
|
|
||
|
mTask.baroReading = false;
|
||
|
mTask.tempReading = false;
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
releaseXfer(xfer);
|
||
|
}
|
||
|
|
||
|
static void handleEvent(uint32_t evtType, const void* evtData)
|
||
|
{
|
||
|
struct I2cTransfer *newXfer;
|
||
|
int ret;
|
||
|
|
||
|
switch (evtType) {
|
||
|
case EVT_APP_START:
|
||
|
{
|
||
|
osEventUnsubscribe(mTask.id, EVT_APP_START);
|
||
|
i2cMasterRequest(I2C_BUS_ID, I2C_SPEED);
|
||
|
|
||
|
/* Reset chip */
|
||
|
writeRegister(BOSCH_BMP280_REG_RESET, BOSCH_BMP280_SOFT_RESET_CMD, STATE_RESET);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case EVT_SENSOR_I2C:
|
||
|
{
|
||
|
handleI2cEvent((struct I2cTransfer *)evtData);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case EVT_SENSOR_BARO_TIMER:
|
||
|
{
|
||
|
/* Start sampling for a value */
|
||
|
if (!mTask.baroReading && !mTask.tempReading) {
|
||
|
newXfer = allocXfer(STATE_SAMPLING);
|
||
|
if (newXfer != NULL) {
|
||
|
newXfer->txrxBuf[0] = BOSCH_BMP280_REG_PRES_MSB;
|
||
|
ret = i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, newXfer->txrxBuf, 1, newXfer->txrxBuf, 6, i2cCallback, newXfer);
|
||
|
if (ret)
|
||
|
releaseXfer(newXfer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mTask.baroReading = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case EVT_SENSOR_TEMP_TIMER:
|
||
|
{
|
||
|
/* Start sampling for a value */
|
||
|
if (!mTask.baroReading && !mTask.tempReading) {
|
||
|
newXfer = allocXfer(STATE_SAMPLING);
|
||
|
if (newXfer != NULL) {
|
||
|
newXfer->txrxBuf[0] = BOSCH_BMP280_REG_PRES_MSB;
|
||
|
ret = i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, newXfer->txrxBuf, 1, newXfer->txrxBuf, 6, i2cCallback, newXfer);
|
||
|
if (ret)
|
||
|
releaseXfer(newXfer);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mTask.tempReading = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case EVT_SENSOR_SOFTRESET_TIMER:
|
||
|
{
|
||
|
writeRegister(BOSCH_BMP280_REG_CONFIG, CTRL_CFG, STATE_SOFTRESET_MODE);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static bool startTask(uint32_t taskId)
|
||
|
{
|
||
|
mTask.id = taskId;
|
||
|
mTask.offset = 0.0f;
|
||
|
|
||
|
/* Register sensors */
|
||
|
mTask.baroHandle = sensorRegister(&sensorInfoBaro, &sensorOpsBaro, NULL, false);
|
||
|
mTask.tempHandle = sensorRegister(&sensorInfoTemp, &sensorOpsTemp, NULL, false);
|
||
|
|
||
|
mTask.evtSlab = slabAllocatorNew(sizeof(struct SingleAxisDataEvent) + sizeof(struct SingleAxisDataPoint), 4, MAX_BARO_EVENTS);
|
||
|
if (!mTask.evtSlab) {
|
||
|
osLog(LOG_ERROR, "[BMP280] slabAllocatorNew() failed\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
osEventSubscribe(taskId, EVT_APP_START);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static void endTask(void)
|
||
|
{
|
||
|
slabAllocatorDestroy(mTask.evtSlab);
|
||
|
}
|
||
|
|
||
|
INTERNAL_APP_INIT(BMP280_APP_ID, BMP280_APP_VERSION, startTask, endTask, handleEvent);
|