744 lines
24 KiB
C
744 lines
24 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 <seos.h>
|
|
#include <i2c.h>
|
|
#include <timer.h>
|
|
#include <sensors.h>
|
|
#include <heap.h>
|
|
#include <hostIntf.h>
|
|
#include <nanohubPacket.h>
|
|
#include <eventnums.h>
|
|
#include <util.h>
|
|
|
|
#define AMS_TMD2772_APP_VERSION 3
|
|
|
|
#define DRIVER_NAME "AMS: "
|
|
|
|
#define I2C_BUS_ID 0
|
|
#define I2C_SPEED 400000
|
|
#define I2C_ADDR 0x39
|
|
|
|
#define AMS_TMD2772_ID 0x39
|
|
|
|
#define AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT 0xa0
|
|
|
|
#define AMS_TMD2772_REG_ENABLE (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x00)
|
|
#define AMS_TMD2772_REG_ATIME (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x01)
|
|
#define AMS_TMD2772_REG_PTIME (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x02)
|
|
#define AMS_TMD2772_REG_WTIME (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x03)
|
|
#define AMS_TMD2772_REG_AILTL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x04)
|
|
#define AMS_TMD2772_REG_AILTH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x05)
|
|
#define AMS_TMD2772_REG_AIHTL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x06)
|
|
#define AMS_TMD2772_REG_AIHTH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x07)
|
|
#define AMS_TMD2772_REG_PILTL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x08)
|
|
#define AMS_TMD2772_REG_PILTH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x09)
|
|
#define AMS_TMD2772_REG_PIHTL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0a)
|
|
#define AMS_TMD2772_REG_PIHTH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0b)
|
|
#define AMS_TMD2772_REG_PERS (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0c)
|
|
#define AMS_TMD2772_REG_CONFIG (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0d)
|
|
#define AMS_TMD2772_REG_PPULSE (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0e)
|
|
#define AMS_TMD2772_REG_CONTROL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x0f)
|
|
#define AMS_TMD2772_REG_ID (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x12)
|
|
#define AMS_TMD2772_REG_STATUS (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x13)
|
|
#define AMS_TMD2772_REG_C0DATA (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x14)
|
|
#define AMS_TMD2772_REG_C0DATAH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x15)
|
|
#define AMS_TMD2772_REG_C1DATA (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x16)
|
|
#define AMS_TMD2772_REG_C1DATAH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x17)
|
|
#define AMS_TMD2772_REG_PDATAL (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x18)
|
|
#define AMS_TMD2772_REG_PDATAH (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x19)
|
|
#define AMS_TMD2772_REG_POFFSET (AMS_TMD2772_CMD_TYPE_AUTO_INCREMENT | 0x1E)
|
|
|
|
#define AMS_TMD2772_ATIME_SETTING 0xdb
|
|
#define AMS_TMD2772_ATIME_MS ((256 - AMS_TMD2772_ATIME_SETTING) * 2.73) // in milliseconds
|
|
#define AMS_TMD2772_PTIME_SETTING 0xff
|
|
#define AMS_TMD2772_PTIME_MS ((256 - AMS_TMD2772_PTIME_SETTING) * 2.73) // in milliseconds
|
|
#define AMS_TMD2772_WTIME_SETTING_ALS_ON 0xdd // (256 - 221) * 2.73 ms = 95.55 ms
|
|
#define AMS_TMD2772_WTIME_SETTING_ALS_OFF 0xb8 // (256 - 184) * 2.73 ms = 196.56 ms
|
|
#define AMS_TMD2772_PPULSE_SETTING 8
|
|
|
|
#define AMS_TMD2772_CAL_DEFAULT_OFFSET 0
|
|
#define AMS_TMD2772_CAL_MAX_OFFSET 500
|
|
|
|
/* AMS_TMD2772_REG_ENABLE */
|
|
#define POWER_ON_BIT (1 << 0)
|
|
#define ALS_ENABLE_BIT (1 << 1)
|
|
#define PROX_ENABLE_BIT (1 << 2)
|
|
#define WAIT_ENABLE_BIT (1 << 3)
|
|
|
|
/* AMS_TMD2772_REG_STATUS */
|
|
#define PROX_INT_BIT (1 << 5)
|
|
#define ALS_INT_BIT (1 << 4)
|
|
#define PROX_VALID_BIT (1 << 1)
|
|
#define ALS_VALID_BIT (1 << 0)
|
|
|
|
#define AMS_TMD2772_REPORT_NEAR_VALUE 0.0f // centimeters
|
|
#define AMS_TMD2772_REPORT_FAR_VALUE 5.0f // centimeters
|
|
|
|
#define AMS_TMD2772_THRESHOLD_ASSERT_NEAR 300 // in PS units
|
|
#define AMS_TMD2772_THRESHOLD_DEASSERT_NEAR 150 // in PS units
|
|
|
|
#define AMS_TMD2772_ALS_MAX_CHANNEL_COUNT 37888 // in raw data
|
|
#define AMS_TMD2772_ALS_MAX_REPORT_VALUE 10000 // in lux
|
|
|
|
#define AMS_TMD2772_ALS_INVALID UINT32_MAX
|
|
|
|
/* Used when SENSOR_RATE_ONCHANGE is requested */
|
|
#define AMS_TMD2772_DEFAULT_RATE SENSOR_HZ(5)
|
|
|
|
#define AMS_TMD2772_MAX_PENDING_I2C_REQUESTS 4
|
|
#define AMS_TMD2772_MAX_I2C_TRANSFER_SIZE 16
|
|
|
|
/* Private driver events */
|
|
enum SensorEvents
|
|
{
|
|
EVT_SENSOR_I2C = EVT_APP_START + 1,
|
|
EVT_SENSOR_ALS_TIMER,
|
|
EVT_SENSOR_PROX_TIMER,
|
|
};
|
|
|
|
/* I2C state machine */
|
|
enum SensorState
|
|
{
|
|
SENSOR_STATE_VERIFY_ID,
|
|
SENSOR_STATE_INIT,
|
|
|
|
SENSOR_STATE_CALIBRATE_RESET,
|
|
SENSOR_STATE_CALIBRATE_START,
|
|
SENSOR_STATE_CALIBRATE_ENABLING,
|
|
SENSOR_STATE_CALIBRATE_POLLING_STATUS,
|
|
SENSOR_STATE_CALIBRATE_AWAITING_SAMPLE,
|
|
SENSOR_STATE_CALIBRATE_DISABLING,
|
|
|
|
SENSOR_STATE_ENABLING_ALS,
|
|
SENSOR_STATE_ENABLING_PROX,
|
|
SENSOR_STATE_DISABLING_ALS,
|
|
SENSOR_STATE_DISABLING_PROX,
|
|
|
|
SENSOR_STATE_IDLE,
|
|
SENSOR_STATE_SAMPLING,
|
|
};
|
|
|
|
enum ProxState
|
|
{
|
|
PROX_STATE_INIT,
|
|
PROX_STATE_NEAR,
|
|
PROX_STATE_FAR,
|
|
};
|
|
|
|
struct I2cTransfer
|
|
{
|
|
size_t tx;
|
|
size_t rx;
|
|
int err;
|
|
|
|
union {
|
|
uint8_t bytes[AMS_TMD2772_MAX_I2C_TRANSFER_SIZE];
|
|
struct {
|
|
uint8_t status;
|
|
uint16_t als[2];
|
|
uint16_t prox;
|
|
} __attribute__((packed)) sample;
|
|
struct {
|
|
uint16_t prox;
|
|
} calibration;
|
|
} txrxBuf;
|
|
|
|
uint8_t state;
|
|
bool inUse;
|
|
};
|
|
|
|
|
|
struct SensorData
|
|
{
|
|
uint32_t tid;
|
|
|
|
uint32_t alsHandle;
|
|
uint32_t proxHandle;
|
|
uint32_t alsTimerHandle;
|
|
uint32_t proxTimerHandle;
|
|
uint32_t calibrationSampleTotal;
|
|
|
|
struct I2cTransfer transfers[AMS_TMD2772_MAX_PENDING_I2C_REQUESTS];
|
|
|
|
union EmbeddedDataPoint lastAlsSample;
|
|
|
|
uint8_t calibrationSampleCount;
|
|
uint8_t proxState; // enum ProxState
|
|
|
|
bool alsOn;
|
|
bool alsReading;
|
|
bool proxOn;
|
|
bool proxReading;
|
|
};
|
|
|
|
static struct SensorData mData;
|
|
|
|
/* TODO: check rates are supported */
|
|
static const uint32_t supportedRates[] =
|
|
{
|
|
SENSOR_HZ(0.1),
|
|
SENSOR_HZ(1),
|
|
SENSOR_HZ(4),
|
|
SENSOR_HZ(5),
|
|
SENSOR_RATE_ONCHANGE,
|
|
0
|
|
};
|
|
|
|
static const uint64_t rateTimerVals[] = //should match "supported rates in length" and be the timer length for that rate in nanosecs
|
|
{
|
|
10 * 1000000000ULL,
|
|
1 * 1000000000ULL,
|
|
1000000000ULL / 4,
|
|
1000000000ULL / 5,
|
|
};
|
|
|
|
/*
|
|
* Helper functions
|
|
*/
|
|
|
|
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, mData.tid);
|
|
if (err != 0)
|
|
osLog(LOG_INFO, DRIVER_NAME "i2c error (tx: %d, rx: %d, err: %d)\n", tx, rx, 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(mData.transfers); i++) {
|
|
if (!mData.transfers[i].inUse) {
|
|
mData.transfers[i].inUse = true;
|
|
mData.transfers[i].state = state;
|
|
return &mData.transfers[i];
|
|
}
|
|
}
|
|
|
|
osLog(LOG_ERROR, DRIVER_NAME "Ran out of i2c buffers!");
|
|
return NULL;
|
|
}
|
|
|
|
// 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.bytes[0] = reg;
|
|
xfer->txrxBuf.bytes[1] = value;
|
|
ret = i2cMasterTx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf.bytes, 2, i2cCallback, xfer);
|
|
}
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
static void alsTimerCallback(uint32_t timerId, void *cookie)
|
|
{
|
|
osEnqueuePrivateEvt(EVT_SENSOR_ALS_TIMER, cookie, NULL, mData.tid);
|
|
}
|
|
|
|
static void proxTimerCallback(uint32_t timerId, void *cookie)
|
|
{
|
|
osEnqueuePrivateEvt(EVT_SENSOR_PROX_TIMER, cookie, NULL, mData.tid);
|
|
}
|
|
|
|
static inline float getLuxFromAlsData(uint16_t als0, uint16_t als1)
|
|
{
|
|
float cpl = 1.0f / AMS_TMD2772_ATIME_MS;
|
|
float GA;
|
|
|
|
if ((als0 * 10) < (als1 * 21)) {
|
|
// A light
|
|
GA = 0.274f;
|
|
} else if (((als0 * 10) >= (als1 * 21)) && ((als0 * 10) <= (als1 * 43)) && (als0 > 300)) {
|
|
// D65
|
|
GA = 0.592f;
|
|
} else {
|
|
// cool white
|
|
GA = 1.97f;
|
|
}
|
|
|
|
float lux1 = GA * 207 * (als0 - (1.799 * als1)) * cpl;
|
|
float lux2 = GA * 207 * ((0.188f * als0) - (0.303 * als1)) * cpl;
|
|
|
|
if ((als0 >= AMS_TMD2772_ALS_MAX_CHANNEL_COUNT) ||
|
|
(als1 >= AMS_TMD2772_ALS_MAX_CHANNEL_COUNT)) {
|
|
return AMS_TMD2772_ALS_MAX_REPORT_VALUE;
|
|
} else if ((lux1 > lux2) && (lux1 > 0.0f)) {
|
|
return lux1 > AMS_TMD2772_ALS_MAX_REPORT_VALUE ? AMS_TMD2772_ALS_MAX_REPORT_VALUE : lux1;
|
|
} else if (lux2 > 0.0f) {
|
|
return lux2 > AMS_TMD2772_ALS_MAX_REPORT_VALUE ? AMS_TMD2772_ALS_MAX_REPORT_VALUE : lux2;
|
|
} else {
|
|
return 0.0f;
|
|
}
|
|
}
|
|
|
|
static void setMode(bool alsOn, bool proxOn, uint8_t state)
|
|
{
|
|
struct I2cTransfer *xfer;
|
|
|
|
xfer = allocXfer(state);
|
|
if (xfer != NULL) {
|
|
xfer->txrxBuf.bytes[0] = AMS_TMD2772_REG_ENABLE;
|
|
xfer->txrxBuf.bytes[1] = POWER_ON_BIT | WAIT_ENABLE_BIT |
|
|
(alsOn ? ALS_ENABLE_BIT : 0) | (proxOn ? PROX_ENABLE_BIT : 0);
|
|
xfer->txrxBuf.bytes[2] = AMS_TMD2772_ATIME_SETTING;
|
|
xfer->txrxBuf.bytes[3] = AMS_TMD2772_PTIME_SETTING;
|
|
xfer->txrxBuf.bytes[4] = alsOn ? AMS_TMD2772_WTIME_SETTING_ALS_ON : AMS_TMD2772_WTIME_SETTING_ALS_OFF;
|
|
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf.bytes, 5, i2cCallback, xfer);
|
|
}
|
|
}
|
|
|
|
static bool sensorPowerAls(bool on, void *cookie)
|
|
{
|
|
osLog(LOG_INFO, DRIVER_NAME "sensorPowerAls: %d\n", on);
|
|
|
|
if (mData.alsTimerHandle) {
|
|
timTimerCancel(mData.alsTimerHandle);
|
|
mData.alsTimerHandle = 0;
|
|
mData.alsReading = false;
|
|
}
|
|
|
|
mData.lastAlsSample.idata = AMS_TMD2772_ALS_INVALID;
|
|
mData.alsOn = on;
|
|
setMode(on, mData.proxOn, (on ? SENSOR_STATE_ENABLING_ALS : SENSOR_STATE_DISABLING_ALS));
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool sensorFirmwareAls(void *cookie)
|
|
{
|
|
sensorSignalInternalEvt(mData.alsHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
|
|
return true;
|
|
}
|
|
|
|
static bool sensorRateAls(uint32_t rate, uint64_t latency, void *cookie)
|
|
{
|
|
if (rate == SENSOR_RATE_ONCHANGE) {
|
|
rate = AMS_TMD2772_DEFAULT_RATE;
|
|
}
|
|
osLog(LOG_INFO, DRIVER_NAME "sensorRateAls: %ld/%lld\n", rate, latency);
|
|
|
|
if (mData.alsTimerHandle)
|
|
timTimerCancel(mData.alsTimerHandle);
|
|
mData.alsTimerHandle = timTimerSet(sensorTimerLookupCommon(supportedRates, rateTimerVals, rate), 0, 50, alsTimerCallback, NULL, false);
|
|
osEnqueuePrivateEvt(EVT_SENSOR_ALS_TIMER, NULL, NULL, mData.tid);
|
|
sensorSignalInternalEvt(mData.alsHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool sensorFlushAls(void *cookie)
|
|
{
|
|
return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), SENSOR_DATA_EVENT_FLUSH, NULL);
|
|
}
|
|
|
|
static bool sendLastSampleAls(void *cookie, uint32_t tid) {
|
|
bool result = true;
|
|
|
|
// If we don't end up doing anything here, the expectation is that we are powering up/haven't got the
|
|
// first sample yet, so a broadcast event will go out soon with the first sample
|
|
if (mData.lastAlsSample.idata != AMS_TMD2772_ALS_INVALID) {
|
|
result = osEnqueuePrivateEvt(sensorGetMyEventType(SENS_TYPE_ALS), mData.lastAlsSample.vptr, NULL, tid);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static bool sensorPowerProx(bool on, void *cookie)
|
|
{
|
|
osLog(LOG_INFO, DRIVER_NAME "sensorPowerProx: %d\n", on);
|
|
|
|
if (mData.proxTimerHandle) {
|
|
timTimerCancel(mData.proxTimerHandle);
|
|
mData.proxTimerHandle = 0;
|
|
mData.proxReading = false;
|
|
}
|
|
|
|
mData.proxState = PROX_STATE_INIT;
|
|
mData.proxOn = on;
|
|
setMode(mData.alsOn, on, (on ? SENSOR_STATE_ENABLING_PROX : SENSOR_STATE_DISABLING_PROX));
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool sensorFirmwareProx(void *cookie)
|
|
{
|
|
sensorSignalInternalEvt(mData.proxHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
|
|
return true;
|
|
}
|
|
|
|
static bool sensorRateProx(uint32_t rate, uint64_t latency, void *cookie)
|
|
{
|
|
if (rate == SENSOR_RATE_ONCHANGE) {
|
|
rate = AMS_TMD2772_DEFAULT_RATE;
|
|
}
|
|
osLog(LOG_INFO, DRIVER_NAME "sensorRateProx: %ld/%lld\n", rate, latency);
|
|
|
|
if (mData.proxTimerHandle)
|
|
timTimerCancel(mData.proxTimerHandle);
|
|
mData.proxTimerHandle = timTimerSet(sensorTimerLookupCommon(supportedRates, rateTimerVals, rate), 0, 50, proxTimerCallback, NULL, false);
|
|
osEnqueuePrivateEvt(EVT_SENSOR_PROX_TIMER, NULL, NULL, mData.tid);
|
|
sensorSignalInternalEvt(mData.proxHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool sensorFlushProx(void *cookie)
|
|
{
|
|
return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), SENSOR_DATA_EVENT_FLUSH, NULL);
|
|
}
|
|
|
|
static bool sendLastSampleProx(void *cookie, uint32_t tid) {
|
|
union EmbeddedDataPoint sample;
|
|
bool result = true;
|
|
|
|
// See note in sendLastSampleAls
|
|
if (mData.proxState != PROX_STATE_INIT) {
|
|
sample.fdata = (mData.proxState == PROX_STATE_NEAR) ?
|
|
AMS_TMD2772_REPORT_NEAR_VALUE : AMS_TMD2772_REPORT_FAR_VALUE;
|
|
result = osEnqueuePrivateEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL, tid);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static const struct SensorInfo sensorInfoAls =
|
|
{
|
|
.sensorName = "ALS",
|
|
.supportedRates = supportedRates,
|
|
.sensorType = SENS_TYPE_ALS,
|
|
.numAxis = NUM_AXIS_EMBEDDED,
|
|
.interrupt = NANOHUB_INT_NONWAKEUP,
|
|
.minSamples = 20
|
|
};
|
|
|
|
static const struct SensorOps sensorOpsAls =
|
|
{
|
|
.sensorPower = sensorPowerAls,
|
|
.sensorFirmwareUpload = sensorFirmwareAls,
|
|
.sensorSetRate = sensorRateAls,
|
|
.sensorFlush = sensorFlushAls,
|
|
.sensorTriggerOndemand = NULL,
|
|
.sensorCalibrate = NULL,
|
|
.sensorSendOneDirectEvt = sendLastSampleAls
|
|
};
|
|
|
|
static const struct SensorInfo sensorInfoProx =
|
|
{
|
|
.sensorName = "Proximity",
|
|
.supportedRates = supportedRates,
|
|
.sensorType = SENS_TYPE_PROX,
|
|
.numAxis = NUM_AXIS_EMBEDDED,
|
|
.interrupt = NANOHUB_INT_WAKEUP,
|
|
.minSamples = 300
|
|
};
|
|
|
|
static const struct SensorOps sensorOpsProx =
|
|
{
|
|
.sensorPower = sensorPowerProx,
|
|
.sensorFirmwareUpload = sensorFirmwareProx,
|
|
.sensorSetRate = sensorRateProx,
|
|
.sensorFlush = sensorFlushProx,
|
|
.sensorTriggerOndemand = NULL,
|
|
.sensorCalibrate = NULL,
|
|
.sensorSendOneDirectEvt = sendLastSampleProx
|
|
};
|
|
|
|
/*
|
|
* Sensor i2c state machine
|
|
*/
|
|
|
|
static void handle_calibration_event(struct I2cTransfer *xfer) {
|
|
struct I2cTransfer *nextXfer;
|
|
|
|
switch (xfer->state) {
|
|
case SENSOR_STATE_CALIBRATE_RESET:
|
|
mData.calibrationSampleCount = 0;
|
|
mData.calibrationSampleTotal = 0;
|
|
/* Intentional fall-through */
|
|
|
|
case SENSOR_STATE_CALIBRATE_START:
|
|
writeRegister(AMS_TMD2772_REG_ENABLE, POWER_ON_BIT | PROX_ENABLE_BIT, SENSOR_STATE_CALIBRATE_ENABLING);
|
|
break;
|
|
|
|
case SENSOR_STATE_CALIBRATE_ENABLING:
|
|
nextXfer = allocXfer(SENSOR_STATE_CALIBRATE_POLLING_STATUS);
|
|
if (nextXfer != NULL) {
|
|
nextXfer->txrxBuf.bytes[0] = AMS_TMD2772_REG_STATUS;
|
|
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, nextXfer->txrxBuf.bytes, 1, nextXfer->txrxBuf.bytes, 1, i2cCallback, nextXfer);
|
|
}
|
|
break;
|
|
|
|
case SENSOR_STATE_CALIBRATE_POLLING_STATUS:
|
|
if (xfer->txrxBuf.bytes[0] & PROX_INT_BIT) {
|
|
/* Done */
|
|
nextXfer = allocXfer(SENSOR_STATE_CALIBRATE_AWAITING_SAMPLE);
|
|
if (nextXfer != NULL) {
|
|
nextXfer->txrxBuf.bytes[0] = AMS_TMD2772_REG_PDATAL;
|
|
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, nextXfer->txrxBuf.bytes, 1, nextXfer->txrxBuf.bytes, 2, i2cCallback, nextXfer);
|
|
}
|
|
} else {
|
|
/* Poll again; go back to previous state */
|
|
xfer->state = SENSOR_STATE_CALIBRATE_ENABLING;
|
|
handle_calibration_event(xfer);
|
|
}
|
|
break;
|
|
|
|
case SENSOR_STATE_CALIBRATE_AWAITING_SAMPLE:
|
|
mData.calibrationSampleCount++;
|
|
mData.calibrationSampleTotal += xfer->txrxBuf.calibration.prox;
|
|
|
|
writeRegister(AMS_TMD2772_REG_ENABLE, 0x00, SENSOR_STATE_CALIBRATE_DISABLING);
|
|
break;
|
|
|
|
case SENSOR_STATE_CALIBRATE_DISABLING:
|
|
if (mData.calibrationSampleCount >= 20) {
|
|
/* Done, calculate calibration */
|
|
uint16_t average = mData.calibrationSampleTotal / mData.calibrationSampleCount;
|
|
uint16_t crosstalk = (average > 0x7f) ? 0x7f : average;
|
|
|
|
writeRegister(AMS_TMD2772_REG_POFFSET, crosstalk, SENSOR_STATE_IDLE);
|
|
} else {
|
|
/* Get another sample; go back to earlier state */
|
|
xfer->state = SENSOR_STATE_CALIBRATE_START;
|
|
handle_calibration_event(xfer);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void handle_i2c_event(struct I2cTransfer *xfer)
|
|
{
|
|
union EmbeddedDataPoint sample;
|
|
bool sendData;
|
|
struct I2cTransfer *nextXfer;
|
|
|
|
switch (xfer->state) {
|
|
case SENSOR_STATE_VERIFY_ID:
|
|
/* Check the sensor ID */
|
|
if (xfer->err != 0 || xfer->txrxBuf.bytes[0] != AMS_TMD2772_ID) {
|
|
osLog(LOG_INFO, DRIVER_NAME "not detected\n");
|
|
sensorUnregister(mData.alsHandle);
|
|
sensorUnregister(mData.proxHandle);
|
|
break;
|
|
}
|
|
|
|
nextXfer = allocXfer(SENSOR_STATE_INIT);
|
|
if (nextXfer != NULL) {
|
|
/* Start address */
|
|
nextXfer->txrxBuf.bytes[0] = AMS_TMD2772_REG_ENABLE;
|
|
/* ENABLE */
|
|
nextXfer->txrxBuf.bytes[1] = 0x00;
|
|
/* ATIME */
|
|
nextXfer->txrxBuf.bytes[2] = AMS_TMD2772_ATIME_SETTING;
|
|
/* PTIME */
|
|
nextXfer->txrxBuf.bytes[3] = AMS_TMD2772_PTIME_SETTING;
|
|
/* WTIME */
|
|
nextXfer->txrxBuf.bytes[4] = 0xFF;
|
|
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, nextXfer->txrxBuf.bytes, 5, i2cCallback, nextXfer);
|
|
}
|
|
break;
|
|
|
|
case SENSOR_STATE_INIT:
|
|
nextXfer = allocXfer(SENSOR_STATE_IDLE);
|
|
if (nextXfer != NULL) {
|
|
/* Start address */
|
|
nextXfer->txrxBuf.bytes[0] = AMS_TMD2772_REG_PERS;
|
|
/* PERS */
|
|
nextXfer->txrxBuf.bytes[1] = 0x00;
|
|
/* CONFIG */
|
|
nextXfer->txrxBuf.bytes[2] = 0x00;
|
|
/* PPULSE */
|
|
nextXfer->txrxBuf.bytes[3] = AMS_TMD2772_PPULSE_SETTING;
|
|
/* CONTROL */
|
|
nextXfer->txrxBuf.bytes[4] = 0x20;
|
|
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, nextXfer->txrxBuf.bytes, 5, i2cCallback, nextXfer);
|
|
}
|
|
break;
|
|
|
|
case SENSOR_STATE_IDLE:
|
|
sensorRegisterInitComplete(mData.alsHandle);
|
|
sensorRegisterInitComplete(mData.proxHandle);
|
|
break;
|
|
|
|
case SENSOR_STATE_ENABLING_ALS:
|
|
sensorSignalInternalEvt(mData.alsHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
|
|
break;
|
|
|
|
case SENSOR_STATE_ENABLING_PROX:
|
|
sensorSignalInternalEvt(mData.proxHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
|
|
break;
|
|
|
|
case SENSOR_STATE_DISABLING_ALS:
|
|
sensorSignalInternalEvt(mData.alsHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0);
|
|
break;
|
|
|
|
case SENSOR_STATE_DISABLING_PROX:
|
|
sensorSignalInternalEvt(mData.proxHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0);
|
|
break;
|
|
|
|
case SENSOR_STATE_SAMPLING:
|
|
#if 0
|
|
osLog(LOG_INFO, DRIVER_NAME "sample ready: status=%02x prox=%u als0=%u als1=%u\n",
|
|
xfer->txrxBuf.sample.status, xfer->txrxBuf.sample.prox,
|
|
xfer->txrxBuf.sample.als[0], xfer->txrxBuf.sample.als[1]);
|
|
#endif
|
|
|
|
if (mData.alsOn && mData.alsReading &&
|
|
(xfer->txrxBuf.sample.status & ALS_VALID_BIT)) {
|
|
/* Create event */
|
|
sample.fdata = getLuxFromAlsData(xfer->txrxBuf.sample.als[0],
|
|
xfer->txrxBuf.sample.als[1]);
|
|
if (mData.lastAlsSample.idata != sample.idata) {
|
|
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), sample.vptr, NULL);
|
|
mData.lastAlsSample.fdata = sample.fdata;
|
|
}
|
|
}
|
|
|
|
if (mData.proxOn && mData.proxReading &&
|
|
(xfer->txrxBuf.sample.status & PROX_VALID_BIT)) {
|
|
/* Create event */
|
|
sendData = true;
|
|
if (mData.proxState == PROX_STATE_INIT) {
|
|
if (xfer->txrxBuf.sample.prox > AMS_TMD2772_THRESHOLD_ASSERT_NEAR) {
|
|
sample.fdata = AMS_TMD2772_REPORT_NEAR_VALUE;
|
|
mData.proxState = PROX_STATE_NEAR;
|
|
} else {
|
|
sample.fdata = AMS_TMD2772_REPORT_FAR_VALUE;
|
|
mData.proxState = PROX_STATE_FAR;
|
|
}
|
|
} else {
|
|
if (mData.proxState == PROX_STATE_NEAR &&
|
|
xfer->txrxBuf.sample.prox < AMS_TMD2772_THRESHOLD_DEASSERT_NEAR) {
|
|
sample.fdata = AMS_TMD2772_REPORT_FAR_VALUE;
|
|
mData.proxState = PROX_STATE_FAR;
|
|
} else if (mData.proxState == PROX_STATE_FAR &&
|
|
xfer->txrxBuf.sample.prox > AMS_TMD2772_THRESHOLD_ASSERT_NEAR) {
|
|
sample.fdata = AMS_TMD2772_REPORT_NEAR_VALUE;
|
|
mData.proxState = PROX_STATE_NEAR;
|
|
} else {
|
|
sendData = false;
|
|
}
|
|
}
|
|
|
|
if (sendData)
|
|
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL);
|
|
}
|
|
|
|
mData.alsReading = false;
|
|
mData.proxReading = false;
|
|
break;
|
|
|
|
default:
|
|
handle_calibration_event(xfer);
|
|
break;
|
|
}
|
|
|
|
xfer->inUse = false;
|
|
}
|
|
|
|
/*
|
|
* Main driver entry points
|
|
*/
|
|
|
|
static bool init_app(uint32_t myTid)
|
|
{
|
|
/* Set up driver private data */
|
|
mData.tid = myTid;
|
|
mData.alsOn = false;
|
|
mData.alsReading = false;
|
|
mData.proxOn = false;
|
|
mData.proxReading = false;
|
|
mData.lastAlsSample.idata = AMS_TMD2772_ALS_INVALID;
|
|
mData.proxState = PROX_STATE_INIT;
|
|
|
|
/* Register sensors */
|
|
mData.alsHandle = sensorRegister(&sensorInfoAls, &sensorOpsAls, NULL, false);
|
|
mData.proxHandle = sensorRegister(&sensorInfoProx, &sensorOpsProx, NULL, false);
|
|
|
|
osEventSubscribe(myTid, EVT_APP_START);
|
|
|
|
return true;
|
|
}
|
|
|
|
static void end_app(void)
|
|
{
|
|
sensorUnregister(mData.alsHandle);
|
|
sensorUnregister(mData.proxHandle);
|
|
|
|
i2cMasterRelease(I2C_BUS_ID);
|
|
}
|
|
|
|
static void handle_event(uint32_t evtType, const void* evtData)
|
|
{
|
|
struct I2cTransfer *xfer;
|
|
|
|
switch (evtType) {
|
|
case EVT_APP_START:
|
|
osEventUnsubscribe(mData.tid, EVT_APP_START);
|
|
i2cMasterRequest(I2C_BUS_ID, I2C_SPEED);
|
|
|
|
/* TODO: reset chip first */
|
|
xfer = allocXfer(SENSOR_STATE_VERIFY_ID);
|
|
if (xfer != NULL) {
|
|
xfer->txrxBuf.bytes[0] = AMS_TMD2772_REG_ID;
|
|
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf.bytes, 1, xfer->txrxBuf.bytes, 1, i2cCallback, xfer);
|
|
}
|
|
break;
|
|
|
|
case EVT_SENSOR_I2C:
|
|
handle_i2c_event((struct I2cTransfer *)evtData);
|
|
break;
|
|
|
|
case EVT_SENSOR_ALS_TIMER:
|
|
case EVT_SENSOR_PROX_TIMER:
|
|
/* Start sampling for a value */
|
|
if (!mData.alsReading && !mData.proxReading) {
|
|
xfer = allocXfer(SENSOR_STATE_SAMPLING);
|
|
if (xfer != NULL) {
|
|
xfer->txrxBuf.bytes[0] = AMS_TMD2772_REG_STATUS;
|
|
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf.bytes, 1, xfer->txrxBuf.bytes, 7, i2cCallback, xfer);
|
|
}
|
|
}
|
|
|
|
if (evtType == EVT_SENSOR_ALS_TIMER)
|
|
mData.alsReading = true;
|
|
else
|
|
mData.proxReading = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
INTERNAL_APP_INIT(APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 9), AMS_TMD2772_APP_VERSION, init_app, end_app, handle_event);
|