1064 lines
35 KiB
C
1064 lines
35 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 <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/exti.h>
|
|
#include <plat/gpio.h>
|
|
#include <plat/syscfg.h>
|
|
#include <variant/variant.h>
|
|
|
|
#define AMS_TMD4903_APP_ID APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 12)
|
|
#define AMS_TMD4903_APP_VERSION 14
|
|
|
|
#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_MAX_ALS_CHANNEL_COUNT ((256 - AMS_TMD4903_ATIME_SETTING) * 1024) // in ALS data units
|
|
#define AMS_TMD4903_ALS_MAX_REPORT_VALUE 150000.0f // in lux
|
|
#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
|
|
|
|
#define AMS_TMD4903_ALS_DEBOUNCE_SAMPLES 5
|
|
#define AMS_TMD4903_ALS_GAIN_4X_THOLD 4000.0f
|
|
#define AMS_TMD4903_ALS_GAIN_16X_THOLD 1000.0f
|
|
#define AMS_TMD4903_ALS_GAIN_64X_THOLD 250.0f
|
|
|
|
/* 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 150 // value in PS_DATA
|
|
|
|
#define AMS_TMD4903_ALS_INVALID UINT32_MAX
|
|
|
|
#define AMS_TMD4903_ALS_TIMER_DELAY 200000000ULL
|
|
|
|
#define AMS_TMD4903_MAX_PENDING_I2C_REQUESTS 8
|
|
#define AMS_TMD4903_MAX_I2C_TRANSFER_SIZE 18
|
|
|
|
#define MIN2(a,b) (((a) < (b)) ? (a) : (b))
|
|
#define MAX2(a,b) (((a) > (b)) ? (a) : (b))
|
|
|
|
// 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_ALS_2,
|
|
SENSOR_STATE_DISABLING_PROX,
|
|
SENSOR_STATE_DISABLING_PROX_2,
|
|
SENSOR_STATE_DISABLING_PROX_3,
|
|
SENSOR_STATE_ALS_CHANGING_GAIN,
|
|
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
|
|
};
|
|
|
|
enum AlsGain
|
|
{
|
|
ALS_GAIN_1X = 0,
|
|
ALS_GAIN_4X = 1,
|
|
ALS_GAIN_16X = 2,
|
|
ALS_GAIN_64X = 3
|
|
};
|
|
|
|
struct AlsProxTransfer
|
|
{
|
|
size_t tx;
|
|
size_t rx;
|
|
int err;
|
|
uint8_t txrxBuf[AMS_TMD4903_MAX_I2C_TRANSFER_SIZE];
|
|
uint8_t state;
|
|
bool inUse;
|
|
};
|
|
|
|
struct SensorData
|
|
{
|
|
struct Gpio *pin;
|
|
struct ChainedIsr isr;
|
|
|
|
uint32_t tid;
|
|
|
|
uint32_t alsHandle;
|
|
uint32_t proxHandle;
|
|
uint32_t alsTimerHandle;
|
|
|
|
float alsOffset;
|
|
|
|
union EmbeddedDataPoint lastAlsSample;
|
|
|
|
struct AlsProxTransfer transfers[AMS_TMD4903_MAX_PENDING_I2C_REQUESTS];
|
|
|
|
uint8_t lastProxState; // enum ProxState
|
|
|
|
uint8_t alsGain;
|
|
uint8_t nextAlsGain;
|
|
uint8_t alsDebounceSamples;
|
|
|
|
bool alsOn;
|
|
bool proxOn;
|
|
bool alsCalibrating;
|
|
bool proxCalibrating;
|
|
bool proxDirectMode;
|
|
bool alsChangingGain;
|
|
bool alsSkipSample;
|
|
};
|
|
|
|
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,
|
|
};
|
|
|
|
static void i2cCallback(void *cookie, size_t tx, size_t rx, int err);
|
|
|
|
/*
|
|
* Helper functions
|
|
*/
|
|
|
|
// 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 AlsProxTransfer *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];
|
|
}
|
|
}
|
|
|
|
INFO_PRINT("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 AlsProxTransfer *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);
|
|
}
|
|
|
|
return (ret == 0);
|
|
}
|
|
|
|
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)
|
|
{
|
|
struct AlsProxTransfer *xfer = cookie;
|
|
|
|
xfer->tx = tx;
|
|
xfer->rx = rx;
|
|
xfer->err = err;
|
|
|
|
osEnqueuePrivateEvt(EVT_SENSOR_I2C, cookie, NULL, mTask.tid);
|
|
if (err != 0)
|
|
INFO_PRINT("i2c error (tx: %d, rx: %d, err: %d)\n", tx, rx, err);
|
|
}
|
|
|
|
static void alsTimerCallback(uint32_t timerId, void *cookie)
|
|
{
|
|
osEnqueuePrivateEvt(EVT_SENSOR_ALS_TIMER, cookie, NULL, mTask.tid);
|
|
}
|
|
|
|
static inline uint32_t getAlsGainFromSetting(uint8_t gainSetting)
|
|
{
|
|
return 0x1 << (2 * gainSetting);
|
|
}
|
|
|
|
static float getLuxFromAlsData(uint16_t c, uint16_t r, uint16_t g, uint16_t b)
|
|
{
|
|
float lux;
|
|
|
|
// check for channel saturation
|
|
if (c >= AMS_TMD4903_MAX_ALS_CHANNEL_COUNT || r >= AMS_TMD4903_MAX_ALS_CHANNEL_COUNT ||
|
|
g >= AMS_TMD4903_MAX_ALS_CHANNEL_COUNT || b >= AMS_TMD4903_MAX_ALS_CHANNEL_COUNT)
|
|
return AMS_TMD4903_ALS_MAX_REPORT_VALUE;
|
|
|
|
lux = (ALS_GA_FACTOR *
|
|
((c * ALS_C_COEFF) + (r * ALS_R_COEFF) + (g * ALS_G_COEFF) + (b * ALS_B_COEFF)) /
|
|
(AMS_TMD4903_ATIME_MS * getAlsGainFromSetting(mTask.alsGain))) * mTask.alsOffset;
|
|
|
|
return MIN2(MAX2(0.0f, lux), AMS_TMD4903_ALS_MAX_REPORT_VALUE);
|
|
}
|
|
|
|
static bool checkForAlsAutoGain(float sample)
|
|
{
|
|
if ((mTask.alsGain != ALS_GAIN_64X) && (sample < AMS_TMD4903_ALS_GAIN_64X_THOLD)) {
|
|
mTask.alsDebounceSamples = (mTask.nextAlsGain == ALS_GAIN_64X) ? (mTask.alsDebounceSamples + 1) : 1;
|
|
mTask.nextAlsGain = ALS_GAIN_64X;
|
|
} else if ((mTask.alsGain != ALS_GAIN_16X) && (sample >= AMS_TMD4903_ALS_GAIN_64X_THOLD) && (sample < AMS_TMD4903_ALS_GAIN_16X_THOLD)) {
|
|
mTask.alsDebounceSamples = (mTask.nextAlsGain == ALS_GAIN_16X) ? (mTask.alsDebounceSamples + 1) : 1;
|
|
mTask.nextAlsGain = ALS_GAIN_16X;
|
|
} else if ((mTask.alsGain != ALS_GAIN_4X) && (sample >= AMS_TMD4903_ALS_GAIN_16X_THOLD) && (sample < AMS_TMD4903_ALS_GAIN_4X_THOLD)) {
|
|
mTask.alsDebounceSamples = (mTask.nextAlsGain == ALS_GAIN_4X) ? (mTask.alsDebounceSamples + 1) : 1;
|
|
mTask.nextAlsGain = ALS_GAIN_4X;
|
|
} else if ((mTask.alsGain != ALS_GAIN_1X) && (sample >= AMS_TMD4903_ALS_GAIN_4X_THOLD)) {
|
|
mTask.alsDebounceSamples = (mTask.nextAlsGain == ALS_GAIN_1X) ? (mTask.alsDebounceSamples + 1) : 1;
|
|
mTask.nextAlsGain = ALS_GAIN_1X;
|
|
}
|
|
|
|
return (mTask.alsDebounceSamples >= AMS_TMD4903_ALS_DEBOUNCE_SAMPLES);
|
|
}
|
|
|
|
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, uint8_t state)
|
|
{
|
|
uint8_t regEnable =
|
|
((alsOn || proxOn) ? POWER_ON_BIT : 0) |
|
|
(alsOn ? ALS_ENABLE_BIT : 0) |
|
|
(proxOn ? (PROX_INT_ENABLE_BIT | PROX_ENABLE_BIT) : 0);
|
|
writeRegister(AMS_TMD4903_REG_ENABLE, regEnable, state);
|
|
}
|
|
|
|
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;
|
|
mTask.nextAlsGain = ALS_GAIN_4X;
|
|
mTask.alsChangingGain = false;
|
|
// skip first sample to make sure we get an entire integration cycle
|
|
mTask.alsSkipSample = true;
|
|
mTask.alsDebounceSamples = 0;
|
|
|
|
setMode(on, mTask.proxOn, (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;
|
|
mTask.alsChangingGain = false;
|
|
mTask.alsSkipSample = false;
|
|
|
|
extiClearPendingGpio(mTask.pin);
|
|
enableInterrupt(mTask.pin, &mTask.isr, EXTI_TRIGGER_FALLING);
|
|
|
|
return writeRegister(AMS_TMD4903_REG_ENABLE,
|
|
(POWER_ON_BIT | ALS_ENABLE_BIT | ALS_INT_ENABLE_BIT),
|
|
SENSOR_STATE_IDLE);
|
|
}
|
|
|
|
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, (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);
|
|
|
|
return writeRegister(AMS_TMD4903_REG_ENABLE, POWER_ON_BIT,
|
|
SENSOR_STATE_START_PROX_CALIBRATION_0);
|
|
}
|
|
|
|
static bool sensorCfgDataProx(void *data, void *cookie)
|
|
{
|
|
struct AlsProxTransfer *xfer;
|
|
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]);
|
|
|
|
xfer = allocXfer(SENSOR_STATE_IDLE);
|
|
if (xfer != NULL) {
|
|
xfer->txrxBuf[0] = AMS_TMD4903_REG_OFFSETNL;
|
|
*((int16_t*)&xfer->txrxBuf[1]) = offsets[0];
|
|
*((int16_t*)&xfer->txrxBuf[3]) = offsets[1];
|
|
*((int16_t*)&xfer->txrxBuf[5]) = offsets[2];
|
|
*((int16_t*)&xfer->txrxBuf[7]) = offsets[3];
|
|
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 9, i2cCallback, xfer);
|
|
}
|
|
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
|
|
};
|
|
|
|
static void verifySensorId(const struct AlsProxTransfer *xfer)
|
|
{
|
|
struct AlsProxTransfer *nextXfer;
|
|
DEBUG_PRINT("REVID = 0x%02x, ID = 0x%02x\n", xfer->txrxBuf[0], xfer->txrxBuf[1]);
|
|
|
|
// Check the sensor ID
|
|
if (xfer->err != 0 || xfer->txrxBuf[1] != AMS_TMD4903_ID) {
|
|
INFO_PRINT("not detected\n");
|
|
sensorUnregister(mTask.alsHandle);
|
|
sensorUnregister(mTask.proxHandle);
|
|
return;
|
|
}
|
|
|
|
nextXfer = allocXfer(SENSOR_STATE_INIT_0);
|
|
if (nextXfer == NULL) {
|
|
return;
|
|
}
|
|
|
|
// There is no SW reset on the AMS TMD4903, so we have to reset all registers manually
|
|
nextXfer->txrxBuf[0] = AMS_TMD4903_REG_ENABLE;
|
|
nextXfer->txrxBuf[1] = 0x00; // REG_ENABLE - reset value from datasheet
|
|
nextXfer->txrxBuf[2] = AMS_TMD4903_ATIME_SETTING; // REG_ATIME - 100 ms
|
|
nextXfer->txrxBuf[3] = AMS_TMD4903_PTIME_SETTING; // REG_PTIME - 50 ms
|
|
nextXfer->txrxBuf[4] = 0xff; // REG_WTIME - reset value from datasheet
|
|
nextXfer->txrxBuf[5] = 0x00; // REG_AILTL - reset value from datasheet
|
|
nextXfer->txrxBuf[6] = 0x00; // REG_AILTH - reset value from datasheet
|
|
nextXfer->txrxBuf[7] = 0x00; // REG_AIHTL - reset value from datasheet
|
|
nextXfer->txrxBuf[8] = 0x00; // REG_AIHTH - reset value from datasheet
|
|
nextXfer->txrxBuf[9] = (AMS_TMD4903_PROX_THRESHOLD_LOW & 0xFF); // REG_PILTL
|
|
nextXfer->txrxBuf[10] = (AMS_TMD4903_PROX_THRESHOLD_LOW >> 8) & 0xFF; // REG_PILTH
|
|
nextXfer->txrxBuf[11] = (AMS_TMD4903_PROX_THRESHOLD_HIGH & 0xFF); // REG_PIHTL
|
|
nextXfer->txrxBuf[12] = (AMS_TMD4903_PROX_THRESHOLD_HIGH >> 8) & 0xFF; // REG_PIHTH
|
|
nextXfer->txrxBuf[13] = 0x00; // REG_PERS - reset value from datasheet
|
|
nextXfer->txrxBuf[14] = 0xa0; // REG_CFG0 - reset value from datasheet
|
|
nextXfer->txrxBuf[15] = AMS_TMD4903_PGCFG0_SETTING; // REG_PGCFG0
|
|
nextXfer->txrxBuf[16] = AMS_TMD4903_PGCFG1_SETTING; // REG_PGCFG1
|
|
nextXfer->txrxBuf[17] = mTask.alsGain; // REG_CFG1
|
|
|
|
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, nextXfer->txrxBuf, 18, i2cCallback, nextXfer);
|
|
}
|
|
|
|
static void handleAlsSample(const struct AlsProxTransfer *xfer)
|
|
{
|
|
union EmbeddedDataPoint sample;
|
|
uint16_t c = *(uint16_t*)(xfer->txrxBuf);
|
|
uint16_t r = *(uint16_t*)(xfer->txrxBuf+2);
|
|
uint16_t g = *(uint16_t*)(xfer->txrxBuf+4);
|
|
uint16_t b = *(uint16_t*)(xfer->txrxBuf+6);
|
|
|
|
if (mTask.alsOn) {
|
|
sample.fdata = getLuxFromAlsData(c, r, g, b);
|
|
DEBUG_PRINT("als sample ready: c=%u r=%u g=%u b=%u, gain=%dx, lux=%d\n", c, r, g, b,
|
|
(int)getAlsGainFromSetting(mTask.alsGain), (int)sample.fdata);
|
|
|
|
if (mTask.alsCalibrating) {
|
|
sendCalibrationResultAls(SENSOR_APP_EVT_STATUS_SUCCESS, sample.fdata);
|
|
|
|
mTask.alsOn = false;
|
|
mTask.alsCalibrating = false;
|
|
|
|
writeRegister(AMS_TMD4903_REG_ENABLE, 0, SENSOR_STATE_IDLE);
|
|
} else if (mTask.alsSkipSample) {
|
|
mTask.alsSkipSample = false;
|
|
} else if (!mTask.alsChangingGain) {
|
|
if (mTask.lastAlsSample.idata != sample.idata) {
|
|
osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_ALS), sample.vptr, NULL);
|
|
mTask.lastAlsSample.fdata = sample.fdata;
|
|
}
|
|
|
|
if (checkForAlsAutoGain(sample.fdata)) {
|
|
DEBUG_PRINT("Changing ALS gain from %dx to %dx\n", (int)getAlsGainFromSetting(mTask.alsGain),
|
|
(int)getAlsGainFromSetting(mTask.nextAlsGain));
|
|
if (writeRegister(AMS_TMD4903_REG_CFG1, mTask.nextAlsGain,
|
|
SENSOR_STATE_ALS_CHANGING_GAIN)) {
|
|
mTask.alsChangingGain = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void handleProxSample(const struct AlsProxTransfer *xfer)
|
|
{
|
|
union EmbeddedDataPoint sample;
|
|
uint16_t ps = *((uint16_t *) xfer->txrxBuf);
|
|
uint8_t 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
|
|
// Lower the bar for "near" threshold so it reports "near" when the prox
|
|
// value is within the hysteresis threshold
|
|
if (ps > AMS_TMD4903_PROX_THRESHOLD_LOW) {
|
|
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
|
|
// reset proximity interrupts
|
|
writeRegister(AMS_TMD4903_REG_INTCLEAR, 0x60, 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
|
|
writeRegister(AMS_TMD4903_REG_CFG4, 0x27, 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
|
|
writeRegister(AMS_TMD4903_REG_INTCLEAR, 0x60, SENSOR_STATE_IDLE);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Sensor i2c state machine
|
|
*/
|
|
static void handle_i2c_event(struct AlsProxTransfer *xfer)
|
|
{
|
|
int i;
|
|
struct AlsProxTransfer *nextXfer;
|
|
|
|
switch (xfer->state) {
|
|
case SENSOR_STATE_VERIFY_ID:
|
|
verifySensorId(xfer);
|
|
break;
|
|
|
|
case SENSOR_STATE_INIT_0:
|
|
// Set REG_CFG4 to reset value from datasheet
|
|
writeRegister(AMS_TMD4903_REG_CFG4, 0x07, SENSOR_STATE_INIT_1);
|
|
break;
|
|
|
|
case SENSOR_STATE_INIT_1:
|
|
nextXfer = allocXfer(SENSOR_STATE_INIT_2);
|
|
if (nextXfer != NULL) {
|
|
nextXfer->txrxBuf[0] = AMS_TMD4903_REG_OFFSETNL;
|
|
for (i = 0; i < 8; i++)
|
|
nextXfer->txrxBuf[1+i] = 0x00;
|
|
i2cMasterTx(I2C_BUS_ID, I2C_ADDR, nextXfer->txrxBuf, 9, i2cCallback, nextXfer);
|
|
}
|
|
break;
|
|
|
|
case SENSOR_STATE_INIT_2:
|
|
// Write REG_INTCLEAR to clear all interrupts
|
|
writeRegister(AMS_TMD4903_REG_INTCLEAR, 0xFA, SENSOR_STATE_FINISH_INIT);
|
|
break;
|
|
|
|
case SENSOR_STATE_FINISH_INIT:
|
|
sensorRegisterInitComplete(mTask.alsHandle);
|
|
sensorRegisterInitComplete(mTask.proxHandle);
|
|
break;
|
|
|
|
case SENSOR_STATE_START_PROX_CALIBRATION_0:
|
|
// Write REG_INTENAB to enable calibration interrupt
|
|
writeRegister(AMS_TMD4903_REG_INTENAB, CAL_INT_ENABLE_BIT,
|
|
SENSOR_STATE_START_PROX_CALIBRATION_1);
|
|
break;
|
|
|
|
case SENSOR_STATE_START_PROX_CALIBRATION_1:
|
|
// Write REG_CALIB to start calibration
|
|
writeRegister(AMS_TMD4903_REG_CALIB, 0x01, 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*)&xfer->txrxBuf[0]),
|
|
*((int16_t*)&xfer->txrxBuf[2]), *((int16_t*)&xfer->txrxBuf[4]),
|
|
*((int16_t*)&xfer->txrxBuf[6]));
|
|
|
|
// Send calibration result
|
|
sendCalibrationResultProx(SENSOR_APP_EVT_STATUS_SUCCESS, (int16_t*)xfer->txrxBuf);
|
|
|
|
// Write REG_INTENAB to disable all interrupts
|
|
writeRegister(AMS_TMD4903_REG_INTENAB, 0x00,
|
|
SENSOR_STATE_FINISH_PROX_CALIBRATION_1);
|
|
break;
|
|
|
|
case SENSOR_STATE_FINISH_PROX_CALIBRATION_1:
|
|
writeRegister(AMS_TMD4903_REG_ENABLE, 0, 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:
|
|
// Reset AGAIN to 4x
|
|
mTask.alsGain = ALS_GAIN_4X;
|
|
writeRegister(AMS_TMD4903_REG_CFG1, mTask.alsGain, SENSOR_STATE_DISABLING_ALS_2);
|
|
break;
|
|
|
|
case SENSOR_STATE_DISABLING_ALS_2:
|
|
sensorSignalInternalEvt(mTask.alsHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0);
|
|
break;
|
|
|
|
case SENSOR_STATE_DISABLING_PROX:
|
|
// Write REG_CFG4 to the reset value from datasheet
|
|
writeRegister(AMS_TMD4903_REG_CFG4, 0x07, SENSOR_STATE_DISABLING_PROX_2);
|
|
break;
|
|
|
|
case SENSOR_STATE_DISABLING_PROX_2:
|
|
// Write REG_INTCLEAR to clear proximity interrupts
|
|
writeRegister(AMS_TMD4903_REG_INTCLEAR, 0x60, 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_CHANGING_GAIN:
|
|
if (mTask.alsOn) {
|
|
mTask.alsChangingGain = false;
|
|
mTask.alsGain = mTask.nextAlsGain;
|
|
mTask.alsDebounceSamples = 0;
|
|
mTask.alsSkipSample = true;
|
|
}
|
|
break;
|
|
|
|
case SENSOR_STATE_ALS_SAMPLING:
|
|
handleAlsSample(xfer);
|
|
break;
|
|
|
|
case SENSOR_STATE_PROX_SAMPLING:
|
|
handleProxSample(xfer);
|
|
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;
|
|
}
|
|
|
|
xfer->inUse = false;
|
|
}
|
|
|
|
/*
|
|
* Main driver entry points
|
|
*/
|
|
|
|
static bool init_app(uint32_t myTid)
|
|
{
|
|
/* 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.alsGain = ALS_GAIN_4X;
|
|
|
|
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)
|
|
{
|
|
struct AlsProxTransfer *xfer;
|
|
|
|
switch (evtType) {
|
|
case EVT_APP_START:
|
|
i2cMasterRequest(I2C_BUS_ID, I2C_SPEED);
|
|
|
|
// Read the ID
|
|
xfer = allocXfer(SENSOR_STATE_VERIFY_ID);
|
|
if (xfer != NULL) {
|
|
xfer->txrxBuf[0] = AMS_TMD4903_REG_REVID;
|
|
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, 2, i2cCallback, xfer);
|
|
}
|
|
break;
|
|
|
|
case EVT_SENSOR_I2C:
|
|
// Dropping const here (we own this memory)
|
|
handle_i2c_event((struct AlsProxTransfer *) 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:
|
|
xfer = allocXfer(SENSOR_STATE_ALS_SAMPLING);
|
|
if (xfer != NULL) {
|
|
xfer->txrxBuf[0] = AMS_TMD4903_REG_CDATAL;
|
|
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, 8, i2cCallback, xfer);
|
|
}
|
|
break;
|
|
|
|
case EVT_SENSOR_PROX_INTERRUPT:
|
|
xfer = allocXfer(SENSOR_STATE_PROX_SAMPLING);
|
|
if (xfer != NULL) {
|
|
if (mTask.proxCalibrating) {
|
|
xfer->txrxBuf[0] = AMS_TMD4903_REG_OFFSETNL;
|
|
xfer->state = SENSOR_STATE_FINISH_PROX_CALIBRATION_0;
|
|
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, 8, i2cCallback, xfer);
|
|
} else {
|
|
xfer->txrxBuf[0] = AMS_TMD4903_REG_PDATAL;
|
|
i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, 2, i2cCallback, xfer);
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
INTERNAL_APP_INIT(AMS_TMD4903_APP_ID, AMS_TMD4903_APP_VERSION, init_app, end_app, handle_event);
|