| /* |
| * 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> |
| |
| #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 213 // in PS units |
| #define AMS_TMD2772_THRESHOLD_DEASSERT_NEAR 96 // 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) |
| |
| /* 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 SensorData |
| { |
| union { |
| uint8_t bytes[16]; |
| struct { |
| uint8_t status; |
| uint16_t als[2]; |
| uint16_t prox; |
| } __attribute__((packed)) sample; |
| struct { |
| uint16_t prox; |
| } calibration; |
| } txrxBuf; |
| |
| uint32_t tid; |
| |
| uint32_t alsHandle; |
| uint32_t proxHandle; |
| uint32_t alsTimerHandle; |
| uint32_t proxTimerHandle; |
| uint32_t calibrationSampleTotal; |
| |
| 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) |
| { |
| if (err == 0) |
| osEnqueuePrivateEvt(EVT_SENSOR_I2C, cookie, NULL, mData.tid); |
| else |
| osLog(LOG_INFO, DRIVER_NAME "i2c error (%d)\n", err); |
| } |
| |
| 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, void *cookie) |
| { |
| mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_ENABLE; |
| mData.txrxBuf.bytes[1] = POWER_ON_BIT | WAIT_ENABLE_BIT | |
| (alsOn ? ALS_ENABLE_BIT : 0) | (proxOn ? PROX_ENABLE_BIT : 0); |
| mData.txrxBuf.bytes[2] = AMS_TMD2772_ATIME_SETTING; |
| mData.txrxBuf.bytes[3] = AMS_TMD2772_PTIME_SETTING; |
| mData.txrxBuf.bytes[4] = alsOn ? AMS_TMD2772_WTIME_SETTING_ALS_ON : AMS_TMD2772_WTIME_SETTING_ALS_OFF; |
| i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 5, |
| &i2cCallback, cookie); |
| } |
| |
| 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, (void *)(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, (void *)(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(int state) { |
| switch (state) { |
| case SENSOR_STATE_CALIBRATE_RESET: |
| mData.calibrationSampleCount = 0; |
| mData.calibrationSampleTotal = 0; |
| /* Intentional fall-through */ |
| |
| case SENSOR_STATE_CALIBRATE_START: |
| mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_ENABLE; |
| mData.txrxBuf.bytes[1] = POWER_ON_BIT | PROX_ENABLE_BIT; |
| i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 2, |
| &i2cCallback, (void *)SENSOR_STATE_CALIBRATE_ENABLING); |
| break; |
| |
| case SENSOR_STATE_CALIBRATE_ENABLING: |
| mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_STATUS; |
| i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 1, |
| mData.txrxBuf.bytes, 1, &i2cCallback, |
| (void *)SENSOR_STATE_CALIBRATE_POLLING_STATUS); |
| break; |
| |
| case SENSOR_STATE_CALIBRATE_POLLING_STATUS: |
| if (mData.txrxBuf.bytes[0] & PROX_INT_BIT) { |
| /* Done */ |
| mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_PDATAL; |
| i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 1, |
| mData.txrxBuf.bytes, 2, &i2cCallback, |
| (void *)SENSOR_STATE_CALIBRATE_AWAITING_SAMPLE); |
| } else { |
| /* Poll again; go back to previous state */ |
| handle_calibration_event(SENSOR_STATE_CALIBRATE_ENABLING); |
| } |
| break; |
| |
| case SENSOR_STATE_CALIBRATE_AWAITING_SAMPLE: |
| mData.calibrationSampleCount++; |
| mData.calibrationSampleTotal += mData.txrxBuf.calibration.prox; |
| |
| mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_ENABLE; |
| mData.txrxBuf.bytes[1] = 0x00; |
| i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 2, |
| &i2cCallback, (void *)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; |
| |
| mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_POFFSET; |
| mData.txrxBuf.bytes[1] = crosstalk; |
| i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 2, |
| &i2cCallback, (void *)SENSOR_STATE_IDLE); |
| } else { |
| /* Get another sample; go back to earlier state */ |
| handle_calibration_event(SENSOR_STATE_CALIBRATE_START); |
| } |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| static void handle_i2c_event(int state) |
| { |
| union EmbeddedDataPoint sample; |
| bool sendData; |
| |
| switch (state) { |
| case SENSOR_STATE_VERIFY_ID: |
| /* Check the sensor ID */ |
| if (mData.txrxBuf.bytes[0] != AMS_TMD2772_ID) { |
| osLog(LOG_INFO, DRIVER_NAME "not detected\n"); |
| sensorUnregister(mData.alsHandle); |
| sensorUnregister(mData.proxHandle); |
| break; |
| } |
| |
| /* Start address */ |
| mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_ENABLE; |
| /* ENABLE */ |
| mData.txrxBuf.bytes[1] = 0x00; |
| /* ATIME */ |
| mData.txrxBuf.bytes[2] = AMS_TMD2772_ATIME_SETTING; |
| /* PTIME */ |
| mData.txrxBuf.bytes[3] = AMS_TMD2772_PTIME_SETTING; |
| /* WTIME */ |
| mData.txrxBuf.bytes[4] = 0xFF; |
| i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 5, |
| &i2cCallback, (void *)SENSOR_STATE_INIT); |
| break; |
| |
| case SENSOR_STATE_INIT: |
| /* Start address */ |
| mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_PERS; |
| /* PERS */ |
| mData.txrxBuf.bytes[1] = 0x00; |
| /* CONFIG */ |
| mData.txrxBuf.bytes[2] = 0x00; |
| /* PPULSE */ |
| mData.txrxBuf.bytes[3] = AMS_TMD2772_PPULSE_SETTING; |
| /* CONTROL */ |
| mData.txrxBuf.bytes[4] = 0x20; |
| i2cMasterTx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 5, |
| &i2cCallback, (void *)SENSOR_STATE_IDLE); |
| 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: |
| /* TEST: log collected data |
| osLog(LOG_INFO, DRIVER_NAME "sample ready: status=%02x prox=%u als0=%u als1=%u\n", |
| mData.txrxBuf.sample.status, mData.txrxBuf.sample.prox, |
| mData.txrxBuf.sample.als[0], mData.txrxBuf.sample.als[1]); |
| */ |
| |
| if (mData.alsOn && mData.alsReading && |
| (mData.txrxBuf.sample.status & ALS_VALID_BIT)) { |
| /* Create event */ |
| sample.fdata = getLuxFromAlsData(mData.txrxBuf.sample.als[0], |
| mData.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 && |
| (mData.txrxBuf.sample.status & PROX_VALID_BIT)) { |
| /* Create event */ |
| sendData = true; |
| if (mData.proxState == PROX_STATE_INIT) { |
| if (mData.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 && |
| mData.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 && |
| mData.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(state); |
| break; |
| } |
| } |
| |
| /* |
| * Main driver entry points |
| */ |
| |
| static bool init_app(uint32_t myTid) |
| { |
| osLog(LOG_INFO, DRIVER_NAME "task starting\n"); |
| |
| /* 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) |
| { |
| switch (evtType) { |
| case EVT_APP_START: |
| osEventUnsubscribe(mData.tid, EVT_APP_START); |
| i2cMasterRequest(I2C_BUS_ID, I2C_SPEED); |
| |
| /* TODO: reset chip first */ |
| |
| mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_ID; |
| i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 1, |
| mData.txrxBuf.bytes, 1, &i2cCallback, |
| (void *)SENSOR_STATE_VERIFY_ID); |
| break; |
| |
| case EVT_SENSOR_I2C: |
| handle_i2c_event((int)evtData); |
| break; |
| |
| case EVT_SENSOR_ALS_TIMER: |
| case EVT_SENSOR_PROX_TIMER: |
| /* Start sampling for a value */ |
| if (!mData.alsReading && !mData.proxReading) { |
| mData.txrxBuf.bytes[0] = AMS_TMD2772_REG_STATUS; |
| i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, mData.txrxBuf.bytes, 1, |
| mData.txrxBuf.bytes, 7, &i2cCallback, |
| (void *)SENSOR_STATE_SAMPLING); |
| } |
| |
| if (evtType == EVT_SENSOR_ALS_TIMER) |
| mData.alsReading = true; |
| else |
| mData.proxReading = true; |
| break; |
| } |
| } |
| |
| INTERNAL_APP_INIT(APP_ID_MAKE(APP_ID_VENDOR_GOOGLE, 9), 0, init_app, end_app, handle_event); |