blob: 32d7545e9a209757ec907ef8b1200b868dddfbc2 [file] [log] [blame]
/*
* 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 <gpio.h>
#include <timer.h>
#include <sensors.h>
#include <heap.h>
#include <hostIntf.h>
#include <isr.h>
#include <i2c.h>
#include <nanohubPacket.h>
#include <sensors.h>
#include <seos.h>
#include <plat/inc/exti.h>
#include <plat/inc/gpio.h>
#include <plat/inc/syscfg.h>
#include <variant/inc/variant.h>
#define AMS_TMD4903_APP_ID APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 12)
#define AMS_TMD4903_APP_VERSION 6
#ifndef PROX_INT_PIN
#error "PROX_INT_PIN is not defined; please define in variant.h"
#endif
#ifndef PROX_IRQ
#error "PROX_IRQ is not defined; please define in variant.h"
#endif
#define I2C_BUS_ID 0
#define I2C_SPEED 400000
#define I2C_ADDR 0x39
#define AMS_TMD4903_REG_ENABLE 0x80
#define AMS_TMD4903_REG_ATIME 0x81
#define AMS_TMD4903_REG_PTIME 0x82
#define AMS_TMD4903_REG_WTIME 0x83
#define AMS_TMD4903_REG_AILTL 0x84
#define AMS_TMD4903_REG_AILTH 0x85
#define AMS_TMD4903_REG_AIHTL 0x86
#define AMS_TMD4903_REG_AIHTH 0x87
#define AMS_TMD4903_REG_PILTL 0x88
#define AMS_TMD4903_REG_PILTH 0x89
#define AMS_TMD4903_REG_PIHTL 0x8a
#define AMS_TMD4903_REG_PIHTH 0x8b
#define AMS_TMD4903_REG_PERS 0x8c
#define AMS_TMD4903_REG_CFG0 0x8d
#define AMS_TMD4903_REG_PGCFG0 0x8e
#define AMS_TMD4903_REG_PGCFG1 0x8f
#define AMS_TMD4903_REG_CFG1 0x90
#define AMS_TMD4903_REG_REVID 0x91
#define AMS_TMD4903_REG_ID 0x92
#define AMS_TMD4903_REG_STATUS 0x93
#define AMS_TMD4903_REG_CDATAL 0x94
#define AMS_TMD4903_REG_CDATAH 0x95
#define AMS_TMD4903_REG_RDATAL 0x96
#define AMS_TMD4903_REG_RDATAH 0x97
#define AMS_TMD4903_REG_GDATAL 0x98
#define AMS_TMD4903_REG_GDATAH 0x99
#define AMS_TMD4903_REG_BDATAL 0x9A
#define AMS_TMD4903_REG_BDATAH 0x9B
#define AMS_TMD4903_REG_PDATAL 0x9C
#define AMS_TMD4903_REG_PDATAH 0x9D
#define AMS_TMD4903_REG_STATUS2 0x9E
#define AMS_TMD4903_REG_CFG4 0xAC
#define AMS_TMD4903_REG_OFFSETNL 0xC0
#define AMS_TMD4903_REG_OFFSETNH 0xC1
#define AMS_TMD4903_REG_OFFSETSL 0xC2
#define AMS_TMD4903_REG_OFFSETSH 0xC3
#define AMS_TMD4903_REG_OFFSETWL 0xC4
#define AMS_TMD4903_REG_OFFSETWH 0xC5
#define AMS_TMD4903_REG_OFFSETEL 0xC6
#define AMS_TMD4903_REG_OFFSETEH 0xC7
#define AMS_TMD4903_REG_CALIB 0xD7
#define AMS_TMD4903_REG_INTENAB 0xDD
#define AMS_TMD4903_REG_INTCLEAR 0xDE
#define AMS_TMD4903_ID 0xB8
#define AMS_TMD4903_DEFAULT_RATE SENSOR_HZ(5)
#define AMS_TMD4903_ATIME_SETTING 0xdc
#define AMS_TMD4903_ATIME_MS ((256 - AMS_TMD4903_ATIME_SETTING) * 2.78) // in milliseconds
#define AMS_TMD4903_PTIME_SETTING 0x11
#define AMS_TMD4903_PGCFG0_SETTING 0x41 // pulse length: 8 us, pulse count: 2
#define AMS_TMD4903_PGCFG1_SETTING 0x04 // gain: 1x, drive: 50 mA
/* AMS_TMD4903_REG_ENABLE */
#define PROX_INT_ENABLE_BIT (1 << 5)
#define ALS_INT_ENABLE_BIT (1 << 4)
#define PROX_ENABLE_BIT (1 << 2)
#define ALS_ENABLE_BIT (1 << 1)
#define POWER_ON_BIT (1 << 0)
/* AMS_TMD4903_REG_INTENAB */
#define CAL_INT_ENABLE_BIT (1 << 1)
#define AMS_TMD4903_REPORT_NEAR_VALUE 0.0f // centimeters
#define AMS_TMD4903_REPORT_FAR_VALUE 5.0f // centimeters
#define AMS_TMD4903_PROX_THRESHOLD_HIGH 350 // value in PS_DATA
#define AMS_TMD4903_PROX_THRESHOLD_LOW 250 // value in PS_DATA
#define AMS_TMD4903_ALS_INVALID UINT32_MAX
#define AMS_TMD4903_ALS_TIMER_DELAY 200000000ULL
// NOTE: Define this to be 1 to enable streaming of proximity samples instead of
// using the interrupt
#define PROX_STREAMING 0
#define INFO_PRINT(fmt, ...) do { \
osLog(LOG_INFO, "%s " fmt, "[TMD4903]", ##__VA_ARGS__); \
} while (0);
#define DEBUG_PRINT(fmt, ...) do { \
if (enable_debug) { \
INFO_PRINT(fmt, ##__VA_ARGS__); \
} \
} while (0);
static const bool enable_debug = 0;
/* Private driver events */
enum SensorEvents
{
EVT_SENSOR_I2C = EVT_APP_START + 1,
EVT_SENSOR_ALS_TIMER,
EVT_SENSOR_ALS_INTERRUPT,
EVT_SENSOR_PROX_INTERRUPT,
};
/* I2C state machine */
enum SensorState
{
SENSOR_STATE_VERIFY_ID,
SENSOR_STATE_INIT_0,
SENSOR_STATE_INIT_1,
SENSOR_STATE_INIT_2,
SENSOR_STATE_FINISH_INIT,
SENSOR_STATE_START_PROX_CALIBRATION_0,
SENSOR_STATE_START_PROX_CALIBRATION_1,
SENSOR_STATE_FINISH_PROX_CALIBRATION_0,
SENSOR_STATE_FINISH_PROX_CALIBRATION_1,
SENSOR_STATE_POLL_STATUS,
SENSOR_STATE_ENABLING_ALS,
SENSOR_STATE_ENABLING_PROX,
SENSOR_STATE_DISABLING_ALS,
SENSOR_STATE_DISABLING_PROX,
SENSOR_STATE_DISABLING_PROX_2,
SENSOR_STATE_DISABLING_PROX_3,
SENSOR_STATE_ALS_SAMPLING,
SENSOR_STATE_PROX_SAMPLING,
SENSOR_STATE_PROX_TRANSITION_0,
SENSOR_STATE_IDLE,
};
enum ProxState
{
PROX_STATE_INIT,
PROX_STATE_NEAR,
PROX_STATE_FAR,
};
enum ProxOffsetIndex
{
PROX_OFFSET_NORTH = 0,
PROX_OFFSET_SOUTH = 1,
PROX_OFFSET_WEST = 2,
PROX_OFFSET_EAST = 3
};
struct SensorData
{
struct Gpio *pin;
struct ChainedIsr isr;
uint8_t txrxBuf[18];
uint32_t tid;
uint32_t alsHandle;
uint32_t proxHandle;
uint32_t alsTimerHandle;
float alsOffset;
union EmbeddedDataPoint lastAlsSample;
uint8_t lastProxState; // enum ProxState
bool alsOn;
bool proxOn;
bool alsCalibrating;
bool proxCalibrating;
bool proxDirectMode;
};
static struct SensorData mTask;
struct AlsCalibrationData {
struct HostHubRawPacket header;
struct SensorAppEventHeader data_header;
float offset;
} __attribute__((packed));
struct ProxCalibrationData {
struct HostHubRawPacket header;
struct SensorAppEventHeader data_header;
int32_t offsets[4];
} __attribute__((packed));
static const uint32_t supportedRates[] =
{
SENSOR_HZ(5),
SENSOR_RATE_ONCHANGE,
0,
};
/*
* Helper functions
*/
static bool proxIsr(struct ChainedIsr *localIsr)
{
struct SensorData *data = container_of(localIsr, struct SensorData, isr);
uint8_t lastProxState = data->lastProxState;
union EmbeddedDataPoint sample;
bool pinState;
if (!extiIsPendingGpio(data->pin)) {
return false;
}
pinState = gpioGet(data->pin);
if (data->proxOn) {
#if PROX_STREAMING
(void)sample;
(void)pinState;
(void)lastProxState;
if (!pinState)
osEnqueuePrivateEvt(EVT_SENSOR_PROX_INTERRUPT, NULL, NULL, mTask.tid);
#else
if (data->proxDirectMode) {
sample.fdata = (pinState) ? AMS_TMD4903_REPORT_FAR_VALUE : AMS_TMD4903_REPORT_NEAR_VALUE;
data->lastProxState = (pinState) ? PROX_STATE_FAR : PROX_STATE_NEAR;
if (data->lastProxState != lastProxState)
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL);
} else {
osEnqueuePrivateEvt(EVT_SENSOR_PROX_INTERRUPT, NULL, NULL, mTask.tid);
}
#endif
} else if (data->alsOn && data->alsCalibrating && !pinState) {
osEnqueuePrivateEvt(EVT_SENSOR_ALS_INTERRUPT, NULL, NULL, mTask.tid);
}
extiClearPendingGpio(data->pin);
return true;
}
static bool enableInterrupt(struct Gpio *pin, struct ChainedIsr *isr, enum ExtiTrigger trigger)
{
extiEnableIntGpio(pin, trigger);
extiChainIsr(PROX_IRQ, isr);
return true;
}
static bool disableInterrupt(struct Gpio *pin, struct ChainedIsr *isr)
{
extiUnchainIsr(PROX_IRQ, isr);
extiDisableIntGpio(pin);
return true;
}
static void i2cCallback(void *cookie, size_t tx, size_t rx, int err)
{
if (err == 0)
osEnqueuePrivateEvt(EVT_SENSOR_I2C, cookie, NULL, mTask.tid);
else
INFO_PRINT("i2c error (%d)\n", err);
}
static void alsTimerCallback(uint32_t timerId, void *cookie)
{
osEnqueuePrivateEvt(EVT_SENSOR_ALS_TIMER, cookie, NULL, mTask.tid);
}
#define LUX_PER_COUNTS (799.397f/AMS_TMD4903_ATIME_MS)
#define C_COEFF 2.387f
#define R_COEFF -1.57f
#define G_COEFF 2.69f
#define B_COEFF -3.307f
static inline float getLuxFromAlsData(uint16_t c, uint16_t r, uint16_t g, uint16_t b)
{
// TODO (trevorbunker): need to check for c saturation
// AMS_TMG4903_ALS_MAX_CHANNEL_COUNT
// TODO (trevorbunker): You can use IR ratio (depends on light source) to
// select between different R, G, and B coefficients
return LUX_PER_COUNTS * ((c * C_COEFF) + (r * R_COEFF) + (g * G_COEFF) + (b * B_COEFF)) * mTask.alsOffset;
}
static void sendCalibrationResultAls(uint8_t status, float offset) {
struct AlsCalibrationData *data = heapAlloc(sizeof(struct AlsCalibrationData));
if (!data) {
osLog(LOG_WARN, "Couldn't alloc als cal result pkt");
return;
}
data->header.appId = AMS_TMD4903_APP_ID;
data->header.dataLen = (sizeof(struct AlsCalibrationData) - sizeof(struct HostHubRawPacket));
data->data_header.msgId = SENSOR_APP_MSG_ID_CAL_RESULT;
data->data_header.sensorType = SENS_TYPE_ALS;
data->data_header.status = status;
data->offset = offset;
if (!osEnqueueEvtOrFree(EVT_APP_TO_HOST, data, heapFree))
osLog(LOG_WARN, "Couldn't send als cal result evt");
}
static void sendCalibrationResultProx(uint8_t status, int16_t *offsets) {
int i;
struct ProxCalibrationData *data = heapAlloc(sizeof(struct ProxCalibrationData));
if (!data) {
osLog(LOG_WARN, "Couldn't alloc prox cal result pkt");
return;
}
data->header.appId = AMS_TMD4903_APP_ID;
data->header.dataLen = (sizeof(struct ProxCalibrationData) - sizeof(struct HostHubRawPacket));
data->data_header.msgId = SENSOR_APP_MSG_ID_CAL_RESULT;
data->data_header.sensorType = SENS_TYPE_PROX;
data->data_header.status = status;
// The offsets are cast from int16_t to int32_t, so I can't use memcpy
for (i = 0; i < 4; i++)
data->offsets[i] = offsets[i];
if (!osEnqueueEvtOrFree(EVT_APP_TO_HOST, data, heapFree))
osLog(LOG_WARN, "Couldn't send prox cal result evt");
}
static void setMode(bool alsOn, bool proxOn, void *cookie)
{
mTask.txrxBuf[0] = AMS_TMD4903_REG_ENABLE;
mTask.txrxBuf[1] =
((alsOn || proxOn) ? POWER_ON_BIT : 0) |
(alsOn ? ALS_ENABLE_BIT : 0) |
(proxOn ? (PROX_INT_ENABLE_BIT | PROX_ENABLE_BIT) : 0);
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, cookie);
}
static bool sensorPowerAls(bool on, void *cookie)
{
DEBUG_PRINT("sensorPowerAls: %d\n", on);
if (on && !mTask.alsTimerHandle) {
mTask.alsTimerHandle = timTimerSet(AMS_TMD4903_ALS_TIMER_DELAY, 0, 50, alsTimerCallback, NULL, false);
} else if (!on && mTask.alsTimerHandle) {
timTimerCancel(mTask.alsTimerHandle);
mTask.alsTimerHandle = 0;
}
mTask.lastAlsSample.idata = AMS_TMD4903_ALS_INVALID;
mTask.alsOn = on;
setMode(on, mTask.proxOn, (void *)(on ? SENSOR_STATE_ENABLING_ALS : SENSOR_STATE_DISABLING_ALS));
return true;
}
static bool sensorFirmwareAls(void *cookie)
{
return sensorSignalInternalEvt(mTask.alsHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
}
static bool sensorRateAls(uint32_t rate, uint64_t latency, void *cookie)
{
if (rate == SENSOR_RATE_ONCHANGE)
rate = AMS_TMD4903_DEFAULT_RATE;
DEBUG_PRINT("sensorRateAls: rate=%ld Hz latency=%lld ns\n", rate/1024, latency);
return sensorSignalInternalEvt(mTask.alsHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
}
static bool sensorFlushAls(void *cookie)
{
return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), SENSOR_DATA_EVENT_FLUSH, NULL);
}
static bool sensorCalibrateAls(void *cookie)
{
DEBUG_PRINT("sensorCalibrateAls");
if (mTask.alsOn || mTask.proxOn) {
INFO_PRINT("cannot calibrate while als or prox are active\n");
sendCalibrationResultAls(SENSOR_APP_EVT_STATUS_BUSY, 0.0f);
return false;
}
mTask.alsOn = true;
mTask.lastAlsSample.idata = AMS_TMD4903_ALS_INVALID;
mTask.alsCalibrating = true;
mTask.alsOffset = 1.0f;
extiClearPendingGpio(mTask.pin);
enableInterrupt(mTask.pin, &mTask.isr, EXTI_TRIGGER_FALLING);
mTask.txrxBuf[0] = AMS_TMD4903_REG_ENABLE;
mTask.txrxBuf[1] = POWER_ON_BIT | ALS_ENABLE_BIT | ALS_INT_ENABLE_BIT;
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void*)SENSOR_STATE_IDLE);
return true;
}
static bool sensorCfgDataAls(void *data, void *cookie)
{
DEBUG_PRINT("sensorCfgDataAls");
mTask.alsOffset = *(float*)data;
INFO_PRINT("Received als cfg data: %d\n", (int)mTask.alsOffset);
return true;
}
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 the client will get a broadcast event soon
if (mTask.lastAlsSample.idata != AMS_TMD4903_ALS_INVALID) {
result = osEnqueuePrivateEvt(sensorGetMyEventType(SENS_TYPE_ALS), mTask.lastAlsSample.vptr, NULL, tid);
}
return result;
}
static bool sensorPowerProx(bool on, void *cookie)
{
DEBUG_PRINT("sensorPowerProx: %d\n", on);
if (on) {
extiClearPendingGpio(mTask.pin);
enableInterrupt(mTask.pin, &mTask.isr, EXTI_TRIGGER_FALLING);
} else {
disableInterrupt(mTask.pin, &mTask.isr);
extiClearPendingGpio(mTask.pin);
}
mTask.lastProxState = PROX_STATE_INIT;
mTask.proxOn = on;
mTask.proxDirectMode = false;
setMode(mTask.alsOn, on, (void *)(on ? SENSOR_STATE_ENABLING_PROX : SENSOR_STATE_DISABLING_PROX));
return true;
}
static bool sensorFirmwareProx(void *cookie)
{
return sensorSignalInternalEvt(mTask.proxHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0);
}
static bool sensorRateProx(uint32_t rate, uint64_t latency, void *cookie)
{
if (rate == SENSOR_RATE_ONCHANGE)
rate = AMS_TMD4903_DEFAULT_RATE;
DEBUG_PRINT("sensorRateProx: rate=%ld Hz latency=%lld ns\n", rate/1024, latency);
return sensorSignalInternalEvt(mTask.proxHandle, SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency);
}
static bool sensorFlushProx(void *cookie)
{
return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), SENSOR_DATA_EVENT_FLUSH, NULL);
}
static bool sensorCalibrateProx(void *cookie)
{
int16_t failOffsets[4] = {0, 0, 0, 0};
DEBUG_PRINT("sensorCalibrateProx");
if (mTask.alsOn || mTask.proxOn) {
INFO_PRINT("cannot calibrate while als or prox are active\n");
sendCalibrationResultProx(SENSOR_APP_EVT_STATUS_BUSY, failOffsets);
return false;
}
mTask.lastProxState = PROX_STATE_INIT;
mTask.proxOn = true;
mTask.proxCalibrating = true;
mTask.proxDirectMode = false;
extiClearPendingGpio(mTask.pin);
enableInterrupt(mTask.pin, &mTask.isr, EXTI_TRIGGER_FALLING);
mTask.txrxBuf[0] = AMS_TMD4903_REG_ENABLE;
mTask.txrxBuf[1] = POWER_ON_BIT; // REG_ENABLE
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void*)SENSOR_STATE_START_PROX_CALIBRATION_0);
return true;
}
static bool sensorCfgDataProx(void *data, void *cookie)
{
DEBUG_PRINT("sensorCfgDataProx");
int32_t *offsets = (int32_t*)data;
INFO_PRINT("Received cfg data: {%d, %d, %d, %d}\n",
(int)offsets[0], (int)offsets[1], (int)offsets[2], (int)offsets[3]);
mTask.txrxBuf[0] = AMS_TMD4903_REG_OFFSETNL;
*((int16_t*)&mTask.txrxBuf[1]) = offsets[0];
*((int16_t*)&mTask.txrxBuf[3]) = offsets[1];
*((int16_t*)&mTask.txrxBuf[5]) = offsets[2];
*((int16_t*)&mTask.txrxBuf[7]) = offsets[3];
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 9, &i2cCallback, (void *)SENSOR_STATE_IDLE);
return true;
}
static bool sendLastSampleProx(void *cookie, uint32_t tid) {
union EmbeddedDataPoint sample;
bool result = true;
// See note in sendLastSampleAls
if (mTask.lastProxState != PROX_STATE_INIT) {
sample.fdata = (mTask.lastProxState == PROX_STATE_NEAR) ? AMS_TMD4903_REPORT_NEAR_VALUE : AMS_TMD4903_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 = sensorCalibrateAls,
.sensorCfgData = sensorCfgDataAls,
.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 = sensorCalibrateProx,
.sensorCfgData = sensorCfgDataProx,
.sensorSendOneDirectEvt = sendLastSampleProx
};
/*
* Sensor i2c state machine
*/
static void handle_i2c_event(int state)
{
union EmbeddedDataPoint sample;
uint16_t c, r, g, b, ps;
uint8_t lastProxState;
int i;
switch (state) {
case SENSOR_STATE_VERIFY_ID:
DEBUG_PRINT("REVID = 0x%02x, ID = 0x%02x\n", mTask.txrxBuf[0], mTask.txrxBuf[1]);
// Check the sensor ID
if (mTask.txrxBuf[1] != AMS_TMD4903_ID) {
INFO_PRINT("not detected\n");
sensorUnregister(mTask.alsHandle);
sensorUnregister(mTask.proxHandle);
break;
}
// There is no SW reset on the AMS TMD4903, so we have to reset all registers manually
mTask.txrxBuf[0] = AMS_TMD4903_REG_ENABLE;
mTask.txrxBuf[1] = 0x00; // REG_ENABLE - reset value from datasheet
mTask.txrxBuf[2] = AMS_TMD4903_ATIME_SETTING; // REG_ATIME - 100 ms
mTask.txrxBuf[3] = AMS_TMD4903_PTIME_SETTING; // REG_PTIME - 50 ms
mTask.txrxBuf[4] = 0xff; // REG_WTIME - reset value from datasheet
mTask.txrxBuf[5] = 0x00; // REG_AILTL - reset value from datasheet
mTask.txrxBuf[6] = 0x00; // REG_AILTH - reset value from datasheet
mTask.txrxBuf[7] = 0x00; // REG_AIHTL - reset value from datasheet
mTask.txrxBuf[8] = 0x00; // REG_AIHTH - reset value from datasheet
mTask.txrxBuf[9] = (AMS_TMD4903_PROX_THRESHOLD_LOW & 0xFF); // REG_PILTL
mTask.txrxBuf[10] = (AMS_TMD4903_PROX_THRESHOLD_LOW >> 8) & 0xFF; // REG_PILTH
mTask.txrxBuf[11] = (AMS_TMD4903_PROX_THRESHOLD_HIGH & 0xFF); // REG_PIHTL
mTask.txrxBuf[12] = (AMS_TMD4903_PROX_THRESHOLD_HIGH >> 8) & 0xFF; // REG_PIHTH
mTask.txrxBuf[13] = 0x00; // REG_PERS - reset value from datasheet
mTask.txrxBuf[14] = 0xa0; // REG_CFG0 - reset value from datasheet
mTask.txrxBuf[15] = AMS_TMD4903_PGCFG0_SETTING; // REG_PGCFG0
mTask.txrxBuf[16] = AMS_TMD4903_PGCFG1_SETTING; // REG_PGCFG1
mTask.txrxBuf[17] = 0x00; // REG_CFG1 - reset value from datasheet
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 18, &i2cCallback, (void *)SENSOR_STATE_INIT_0);
break;
case SENSOR_STATE_INIT_0:
mTask.txrxBuf[0] = AMS_TMD4903_REG_CFG4;
mTask.txrxBuf[1] = 0x07; // REG_CFG4 - reset value from datasheet
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void *)SENSOR_STATE_INIT_1);
break;
case SENSOR_STATE_INIT_1:
mTask.txrxBuf[0] = AMS_TMD4903_REG_OFFSETNL;
for (i = 0; i < 8; i++)
mTask.txrxBuf[1+i] = 0x00;
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 9, &i2cCallback, (void *)SENSOR_STATE_INIT_2);
break;
case SENSOR_STATE_INIT_2:
mTask.txrxBuf[0] = AMS_TMD4903_REG_INTCLEAR;
mTask.txrxBuf[1] = 0xFA; // REG_INTCLEAR - clear all interrupts
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void *)SENSOR_STATE_FINISH_INIT);
break;
case SENSOR_STATE_FINISH_INIT:
sensorRegisterInitComplete(mTask.alsHandle);
sensorRegisterInitComplete(mTask.proxHandle);
break;
case SENSOR_STATE_START_PROX_CALIBRATION_0:
mTask.txrxBuf[0] = AMS_TMD4903_REG_INTENAB;
mTask.txrxBuf[1] = CAL_INT_ENABLE_BIT; // REG_INTENAB - enable calibration interrupt
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void*)SENSOR_STATE_START_PROX_CALIBRATION_1);
break;
case SENSOR_STATE_START_PROX_CALIBRATION_1:
mTask.txrxBuf[0] = AMS_TMD4903_REG_CALIB;
mTask.txrxBuf[1] = 0x01; // REG_CALIB - start calibration
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void *)SENSOR_STATE_IDLE);
break;
case SENSOR_STATE_FINISH_PROX_CALIBRATION_0:
disableInterrupt(mTask.pin, &mTask.isr);
extiClearPendingGpio(mTask.pin);
mTask.proxOn = false;
mTask.proxCalibrating = false;
INFO_PRINT("Calibration offsets = {%d, %d, %d, %d}\n", *((int16_t*)&mTask.txrxBuf[0]),
*((int16_t*)&mTask.txrxBuf[2]), *((int16_t*)&mTask.txrxBuf[4]),
*((int16_t*)&mTask.txrxBuf[6]));
// Send calibration result
sendCalibrationResultProx(SENSOR_APP_EVT_STATUS_SUCCESS, (int16_t*)mTask.txrxBuf);
mTask.txrxBuf[0] = AMS_TMD4903_REG_INTENAB;
mTask.txrxBuf[1] = 0x00; // REG_INTENAB - disable all interrupts
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void*)SENSOR_STATE_FINISH_PROX_CALIBRATION_1);
break;
case SENSOR_STATE_FINISH_PROX_CALIBRATION_1:
mTask.txrxBuf[0] = AMS_TMD4903_REG_ENABLE;
mTask.txrxBuf[1] = 0x00; // REG_ENABLE
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void*)SENSOR_STATE_IDLE);
break;
case SENSOR_STATE_ENABLING_ALS:
sensorSignalInternalEvt(mTask.alsHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
break;
case SENSOR_STATE_ENABLING_PROX:
sensorSignalInternalEvt(mTask.proxHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0);
break;
case SENSOR_STATE_DISABLING_ALS:
sensorSignalInternalEvt(mTask.alsHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0);
break;
case SENSOR_STATE_DISABLING_PROX:
// Clear direct proximity to interrupt setting
mTask.txrxBuf[0] = AMS_TMD4903_REG_CFG4;
mTask.txrxBuf[1] = 0x07; // REG_CFG4 - reset value from datasheet
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void *)SENSOR_STATE_DISABLING_PROX_2);
break;
case SENSOR_STATE_DISABLING_PROX_2:
// Reset interrupt
mTask.txrxBuf[0] = AMS_TMD4903_REG_INTCLEAR;
mTask.txrxBuf[1] = 0x60; // REG_INTCLEAR - clear proximity interrupts
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void *)SENSOR_STATE_DISABLING_PROX_3);
break;
case SENSOR_STATE_DISABLING_PROX_3:
sensorSignalInternalEvt(mTask.proxHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0);
break;
case SENSOR_STATE_ALS_SAMPLING:
c = *(uint16_t*)(mTask.txrxBuf);
r = *(uint16_t*)(mTask.txrxBuf+2);
g = *(uint16_t*)(mTask.txrxBuf+4);
b = *(uint16_t*)(mTask.txrxBuf+6);
DEBUG_PRINT("als sample ready: c=%u r=%u g=%u b=%u\n", c, r, g, b);
if (mTask.alsOn) {
sample.fdata = getLuxFromAlsData(c, r, g, b);
if (mTask.alsCalibrating) {
sendCalibrationResultAls(SENSOR_APP_EVT_STATUS_SUCCESS, sample.fdata);
mTask.alsOn = false;
mTask.alsCalibrating = false;
mTask.txrxBuf[0] = AMS_TMD4903_REG_ENABLE;
mTask.txrxBuf[1] = 0; // REG_ENABLE
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void*)SENSOR_STATE_IDLE);
} else if (mTask.lastAlsSample.idata != sample.idata) {
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), sample.vptr, NULL);
mTask.lastAlsSample.fdata = sample.fdata;
}
}
break;
case SENSOR_STATE_PROX_SAMPLING:
ps = *(uint16_t*)(mTask.txrxBuf);
lastProxState = mTask.lastProxState;
DEBUG_PRINT("prox sample ready: prox=%u\n", ps);
if (mTask.proxOn) {
#if PROX_STREAMING
(void)lastProxState;
sample.fdata = ps;
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL);
#else
if (ps > AMS_TMD4903_PROX_THRESHOLD_HIGH) {
sample.fdata = AMS_TMD4903_REPORT_NEAR_VALUE;
mTask.lastProxState = PROX_STATE_NEAR;
} else {
sample.fdata = AMS_TMD4903_REPORT_FAR_VALUE;
mTask.lastProxState = PROX_STATE_FAR;
}
if (mTask.lastProxState != lastProxState)
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL);
#endif
#if PROX_STREAMING
// clear the interrupt
mTask.txrxBuf[0] = AMS_TMD4903_REG_INTCLEAR;
mTask.txrxBuf[1] = 0x60; // REG_INTCLEAR - reset proximity interrupts
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void *)SENSOR_STATE_IDLE);
#else
// The TMD4903 direct interrupt mode does not work properly if enabled while something is covering the sensor,
// so we need to wait until it is far.
if (mTask.lastProxState == PROX_STATE_FAR) {
disableInterrupt(mTask.pin, &mTask.isr);
extiClearPendingGpio(mTask.pin);
// Switch to proximity interrupt direct mode
mTask.txrxBuf[0] = AMS_TMD4903_REG_CFG4;
mTask.txrxBuf[1] = 0x27; // REG_CFG4 - proximity state direct to interrupt pin
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void *)SENSOR_STATE_PROX_TRANSITION_0);
} else {
// If we are in the "near" state, we cannot change to direct interrupt mode, so just clear the interrupt
mTask.txrxBuf[0] = AMS_TMD4903_REG_INTCLEAR;
mTask.txrxBuf[1] = 0x60; // REG_INTCLEAR - reset proximity interrupts
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 2, &i2cCallback, (void *)SENSOR_STATE_IDLE);
}
#endif
}
break;
case SENSOR_STATE_PROX_TRANSITION_0:
if (mTask.proxOn) {
mTask.proxDirectMode = true;
extiClearPendingGpio(mTask.pin);
enableInterrupt(mTask.pin, &mTask.isr, EXTI_TRIGGER_BOTH);
}
break;
default:
break;
}
}
/*
* Main driver entry points
*/
static bool init_app(uint32_t myTid)
{
INFO_PRINT("task starting\n");
/* Set up driver private data */
mTask.tid = myTid;
mTask.alsOn = false;
mTask.proxOn = false;
mTask.lastAlsSample.idata = AMS_TMD4903_ALS_INVALID;
mTask.lastProxState = PROX_STATE_INIT;
mTask.proxCalibrating = false;
mTask.alsOffset = 1.0f;
mTask.pin = gpioRequest(PROX_INT_PIN);
gpioConfigInput(mTask.pin, GPIO_SPEED_LOW, GPIO_PULL_NONE);
syscfgSetExtiPort(mTask.pin);
mTask.isr.func = proxIsr;
mTask.alsHandle = sensorRegister(&sensorInfoAls, &sensorOpsAls, NULL, false);
mTask.proxHandle = sensorRegister(&sensorInfoProx, &sensorOpsProx, NULL, false);
osEventSubscribe(myTid, EVT_APP_START);
return true;
}
static void end_app(void)
{
disableInterrupt(mTask.pin, &mTask.isr);
extiUnchainIsr(PROX_IRQ, &mTask.isr);
extiClearPendingGpio(mTask.pin);
gpioRelease(mTask.pin);
sensorUnregister(mTask.alsHandle);
sensorUnregister(mTask.proxHandle);
i2cMasterRelease(I2C_BUS_ID);
}
static void handle_event(uint32_t evtType, const void* evtData)
{
switch (evtType) {
case EVT_APP_START:
i2cMasterRequest(I2C_BUS_ID, I2C_SPEED);
// Read the ID
mTask.txrxBuf[0] = AMS_TMD4903_REG_REVID;
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 1, mTask.txrxBuf, 2, &i2cCallback, (void *)SENSOR_STATE_VERIFY_ID);
break;
case EVT_SENSOR_I2C:
handle_i2c_event((int)evtData);
break;
case EVT_SENSOR_ALS_INTERRUPT:
disableInterrupt(mTask.pin, &mTask.isr);
extiClearPendingGpio(mTask.pin);
// NOTE: fall-through to initiate read of ALS data registers
case EVT_SENSOR_ALS_TIMER:
mTask.txrxBuf[0] = AMS_TMD4903_REG_CDATAL;
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 1, mTask.txrxBuf, 8, &i2cCallback, (void *)SENSOR_STATE_ALS_SAMPLING);
break;
case EVT_SENSOR_PROX_INTERRUPT:
if (mTask.proxCalibrating) {
mTask.txrxBuf[0] = AMS_TMD4903_REG_OFFSETNL;
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 1, mTask.txrxBuf, 8, &i2cCallback, (void *)SENSOR_STATE_FINISH_PROX_CALIBRATION_0);
} else {
mTask.txrxBuf[0] = AMS_TMD4903_REG_PDATAL;
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mTask.txrxBuf, 1, mTask.txrxBuf, 2, &i2cCallback, (void *)SENSOR_STATE_PROX_SAMPLING);
}
break;
}
}
INTERNAL_APP_INIT(AMS_TMD4903_APP_ID, AMS_TMD4903_APP_VERSION, init_app, end_app, handle_event);