| /* |
| * 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> |
| |
| #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 |
| |
| #ifndef PROX_I2C_BUS_ID |
| #define PROX_I2C_BUS_ID 0 |
| #endif |
| |
| #define RPR0521_APP_VERSION 3 |
| |
| #define I2C_BUS_ID PROX_I2C_BUS_ID |
| #define I2C_SPEED 400000 |
| #define I2C_ADDR 0x38 |
| |
| #define ROHM_RPR0521_REG_ID 0x92 |
| #define ROHM_RPR0521_REG_SYSTEM_CONTROL 0x40 |
| #define ROHM_RPR0521_REG_MODE_CONTROL 0x41 |
| #define ROHM_RPR0521_REG_ALS_PS_CONTROL 0x42 |
| #define ROHM_RPR0521_REG_PS_CONTROL 0x43 |
| #define ROHM_RPR0521_REG_PS_DATA_LSB 0x44 |
| #define ROHM_RPR0521_REG_ALS_DATA0_LSB 0x46 |
| #define ROHM_RPR0521_REG_INTERRUPT 0x4a |
| #define ROHM_RPR0521_REG_PS_TH_LSB 0x4b |
| #define ROHM_RPR0521_REG_PS_TH_MSB 0x4c |
| #define ROHM_RPR0521_REG_PS_TL_LSB 0x4d |
| #define ROHM_RPR0521_REG_PS_TL_MSB 0x4e |
| #define ROHM_RPR0521_REG_ALS_DATA0_TH_LSB 0x4f |
| #define ROHM_RPR0521_REG_ALS_DATA0_TL_LSB 0x51 |
| #define ROHM_RPR0521_REG_PS_OFFSET_LSB 0x53 |
| #define ROHM_RPR0521_REG_PS_OFFSET_MSB 0x54 |
| |
| #define ROHM_RPR0521_ID 0xe0 |
| |
| #define ROHM_RPR0521_DEFAULT_RATE SENSOR_HZ(5) |
| |
| enum { |
| ALS_GAIN_X1 = 0, |
| ALS_GAIN_X2 = 1, |
| ALS_GAIN_X64 = 2, |
| ALS_GAIN_X128 = 3, |
| }; |
| #define ROHM_RPR0521_GAIN_ALS0 ALS_GAIN_X1 |
| #define ROHM_RPR0521_GAIN_ALS1 ALS_GAIN_X1 |
| |
| enum { |
| LED_CURRENT_25MA = 0, |
| LED_CURRENT_50MA = 1, |
| LED_CURRENT_100MA = 2, |
| LED_CURRENT_200MA = 3, |
| }; |
| #define ROHM_RPR0521_LED_CURRENT LED_CURRENT_100MA |
| |
| /* ROHM_RPR0521_REG_SYSTEM_CONTROL */ |
| #define SW_RESET_BIT (1 << 7) |
| #define INT_RESET_BIT (1 << 6) |
| |
| /* ROHM_RPR0521_REG_MODE_CONTROL */ |
| #define ALS_EN_BIT (1 << 7) |
| #define PS_EN_BIT (1 << 6) |
| |
| /* ROHM_RPR0521_REG_PS_CONTROL */ |
| enum { |
| PS_GAIN_X1 = 0, |
| PS_GAIN_X2 = 1, |
| PS_GAIN_X4 = 2, |
| }; |
| enum { |
| PS_PERSISTENCE_ACTIVE_AT_EACH_MEASUREMENT_END = 0, |
| PS_PERSISTENCE_STATUS_UPDATED_AT_EACH_MEASUREMENT_END = 1, |
| }; |
| #define ROHM_RPR0521_GAIN_PS PS_GAIN_X1 |
| |
| |
| /* ROHM_RPR0521_REG_INTERRUPT */ |
| #define INTERRUPT_LATCH_BIT (1 << 2) |
| enum { |
| INTERRUPT_MODE_PS_TH_H_ONLY = 0, |
| INTERRUPT_MODE_PS_HYSTERESIS = 1, |
| INTERRUPT_MODE_PS_OUTSIDE_DETECT = 2 |
| }; |
| enum { |
| INTERRUPT_TRIGGER_INACTIVE = 0, |
| INTERRUPT_TRIGGER_PS = 1, |
| INTERRUPT_TRIGGER_ALS = 2, |
| INTERRUPT_TRIGGER_BOTH = 3 |
| }; |
| |
| |
| #define ROHM_RPR0521_REPORT_NEAR_VALUE 0.0f // centimeters |
| #define ROHM_RPR0521_REPORT_FAR_VALUE 5.0f // centimeters |
| #define ROHM_RPR0521_THRESHOLD_ASSERT_NEAR 12 // value in PS_DATA |
| #define ROHM_RPR0521_THRESHOLD_DEASSERT_NEAR 7 // value in PS_DATA |
| |
| #define ROHM_RPR0521_ALS_INVALID UINT32_MAX |
| |
| #define ROHM_RPR0521_ALS_TIMER_DELAY 200000000ULL |
| |
| #define ROHM_RPR0521_MAX_PENDING_I2C_REQUESTS 4 |
| #define ROHM_RPR0521_MAX_I2C_TRANSFER_SIZE 16 |
| |
| #define VERBOSE_PRINT(fmt, ...) do { \ |
| osLog(LOG_VERBOSE, "[Rohm RPR-0521] " fmt, ##__VA_ARGS__); \ |
| } while (0); |
| |
| #define INFO_PRINT(fmt, ...) do { \ |
| osLog(LOG_INFO, "[Rohm RPR-0521] " fmt, ##__VA_ARGS__); \ |
| } while (0); |
| |
| #define ERROR_PRINT(fmt, ...) do { \ |
| osLog(LOG_ERROR, "[Rohm RPR-0521] " fmt, ##__VA_ARGS__); \ |
| } while (0); |
| |
| #define DEBUG_PRINT(fmt, ...) do { \ |
| if (enable_debug) { \ |
| osLog(LOG_INFO, "[Rohm RPR-0521] " 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_PROX_INTERRUPT, |
| }; |
| |
| /* I2C state machine */ |
| enum SensorState |
| { |
| SENSOR_STATE_RESET, |
| SENSOR_STATE_VERIFY_ID, |
| SENSOR_STATE_INIT_GAINS, |
| SENSOR_STATE_INIT_THRESHOLDS, |
| SENSOR_STATE_INIT_OFFSETS, |
| SENSOR_STATE_FINISH_INIT, |
| 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_IDLE, |
| }; |
| |
| enum ProxState |
| { |
| PROX_STATE_INIT, |
| PROX_STATE_NEAR, |
| PROX_STATE_FAR, |
| }; |
| |
| enum MeasurementTime { |
| MEASUREMENT_TIME_ALS_STANDBY_PS_STANDBY = 0, |
| MEASUREMENT_TIME_ALS_STANDBY_PS_10 = 1, |
| MEASUREMENT_TIME_ALS_STANDBY_PS_40 = 2, |
| MEASUREMENT_TIME_ALS_STANDBY_PS_100 = 3, |
| MEASUREMENT_TIME_ALS_STANDBY_PS_400 = 4, |
| MEASUREMENT_TIME_ALS_100_PS_50 = 5, |
| MEASUREMENT_TIME_ALS_100_PS_100 = 6, |
| MEASUREMENT_TIME_ALS_100_PS_400 = 7, |
| MEASUREMENT_TIME_ALS_400_PS_50 = 8, |
| MEASUREMENT_TIME_ALS_400_PS_100 = 9, |
| MEASUREMENT_TIME_ALS_400_PS_STANDBY = 10, |
| MEASUREMENT_TIME_ALS_400_PS_400 = 11, |
| MEASUREMENT_TIME_ALS_50_PS_50 = 12, |
| }; |
| |
| struct I2cTransfer |
| { |
| size_t tx; |
| size_t rx; |
| int err; |
| uint8_t txrxBuf[ROHM_RPR0521_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; |
| |
| union EmbeddedDataPoint lastAlsSample; |
| |
| struct I2cTransfer transfers[ROHM_RPR0521_MAX_PENDING_I2C_REQUESTS]; |
| |
| uint8_t proxState; // enum ProxState |
| |
| bool alsOn; |
| bool proxOn; |
| }; |
| |
| static struct SensorData mTask; |
| |
| 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); |
| bool firstProxSample = (data->proxState == PROX_STATE_INIT); |
| uint8_t lastProxState = data->proxState; |
| bool pinState; |
| union EmbeddedDataPoint sample; |
| |
| if (!extiIsPendingGpio(data->pin)) { |
| return false; |
| } |
| |
| if (data->proxOn) { |
| pinState = gpioGet(data->pin); |
| |
| if (firstProxSample && !pinState) { |
| osEnqueuePrivateEvt(EVT_SENSOR_PROX_INTERRUPT, NULL, NULL, mTask.tid); |
| } else if (!firstProxSample) { |
| sample.fdata = (pinState) ? ROHM_RPR0521_REPORT_FAR_VALUE : ROHM_RPR0521_REPORT_NEAR_VALUE; |
| data->proxState = (pinState) ? PROX_STATE_FAR : PROX_STATE_NEAR; |
| if (data->proxState != lastProxState) |
| osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL); |
| } |
| } |
| |
| extiClearPendingGpio(data->pin); |
| return true; |
| } |
| |
| static bool enableInterrupt(struct Gpio *pin, struct ChainedIsr *isr) |
| { |
| extiEnableIntGpio(pin, EXTI_TRIGGER_BOTH); |
| 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 I2cTransfer *xfer = cookie; |
| |
| xfer->tx = tx; |
| xfer->rx = rx; |
| xfer->err = err; |
| |
| osEnqueuePrivateEvt(EVT_SENSOR_I2C, cookie, NULL, mTask.tid); |
| if (err != 0) |
| ERROR_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); |
| } |
| |
| // Allocate a buffer and mark it as in use with the given state, or return NULL |
| // if no buffers available. Must *not* be called from interrupt context. |
| static struct I2cTransfer *allocXfer(uint8_t state) |
| { |
| size_t i; |
| |
| for (i = 0; i < ARRAY_SIZE(mTask.transfers); i++) { |
| if (!mTask.transfers[i].inUse) { |
| mTask.transfers[i].inUse = true; |
| mTask.transfers[i].state = state; |
| return &mTask.transfers[i]; |
| } |
| } |
| |
| ERROR_PRINT("Ran out of i2c buffers!\n"); |
| return NULL; |
| } |
| |
| static inline void releaseXfer(struct I2cTransfer *xfer) |
| { |
| xfer->inUse = false; |
| } |
| |
| static bool i2cWrite(struct I2cTransfer *xfer, size_t txSize) |
| { |
| int ret = i2cMasterTx(I2C_BUS_ID, I2C_ADDR, xfer->txrxBuf, txSize, i2cCallback, xfer); |
| if (ret < 0) { |
| ERROR_PRINT("%s: i2cMasterTx failed (%d)\n", __func__, ret); |
| releaseXfer(xfer); |
| } |
| |
| return (ret == 0); |
| } |
| |
| // 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); |
| |
| if (xfer != NULL) { |
| xfer->txrxBuf[0] = reg; |
| xfer->txrxBuf[1] = value; |
| return i2cWrite(xfer, 2); |
| } |
| |
| return false; |
| } |
| |
| static bool readRegister(uint8_t reg, uint8_t len, uint8_t state) |
| { |
| struct I2cTransfer *xfer = allocXfer(state); |
| int ret = -1; |
| |
| if (xfer != NULL) { |
| xfer->txrxBuf[0] = reg; |
| ret = i2cMasterTxRx(I2C_BUS_ID, I2C_ADDR, |
| xfer->txrxBuf, 1, xfer->txrxBuf, len, i2cCallback, xfer); |
| if (ret < 0) { |
| ERROR_PRINT("%s: i2cMasterTxRx failed (%d)\n", __func__, ret); |
| releaseXfer(xfer); |
| } |
| } |
| |
| return (ret == 0); |
| } |
| |
| static inline float getLuxFromAlsData(uint16_t als0, uint16_t als1) |
| { |
| static const float invGain[] = {1.0f, 0.5f, 1.0f / 64.0f, 1.0f / 128.0f}; |
| float d0 = (float)als0 * invGain[ROHM_RPR0521_GAIN_ALS0]; |
| float d1 = (float)als1 * invGain[ROHM_RPR0521_GAIN_ALS1]; |
| float ratio = d1 / d0; |
| float c1; |
| float c2; |
| |
| if (ratio < 1.221f) { |
| c1 = 6.323f; |
| c2 = -3.917f; |
| } else if (ratio < 1.432f) { |
| c1 = 5.350f; |
| c2 = -3.121f; |
| } else if (ratio < 1.710f) { |
| c1 = 2.449f; |
| c2 = -1.096f; |
| } else if (ratio < 3.393f) { |
| c1 = 1.155f; |
| c2 = -0.340f; |
| } else { |
| c1 = c2 = 0.0f; |
| } |
| |
| return c1 * d0 + c2 * d1; |
| } |
| |
| static void setMode(bool alsOn, bool proxOn, uint8_t state) |
| { |
| uint8_t ctrl; |
| |
| static const uint8_t measurementTime[] = { |
| MEASUREMENT_TIME_ALS_STANDBY_PS_STANDBY, /* als disabled, prox disabled */ |
| MEASUREMENT_TIME_ALS_100_PS_100, /* als enabled, prox disabled */ |
| MEASUREMENT_TIME_ALS_STANDBY_PS_100, /* als disabled, prox enabled */ |
| MEASUREMENT_TIME_ALS_100_PS_100, /* als enabled, prox enabled */ |
| }; |
| |
| ctrl = measurementTime[alsOn ? 1 : 0 + proxOn ? 2 : 0] | (alsOn ? ALS_EN_BIT : 0) | (proxOn ? PS_EN_BIT : 0); |
| writeRegister(ROHM_RPR0521_REG_MODE_CONTROL, ctrl, state); |
| } |
| |
| static bool sensorPowerAls(bool on, void *cookie) |
| { |
| VERBOSE_PRINT("sensorPowerAls: %d\n", on); |
| |
| if (on && !mTask.alsTimerHandle) { |
| mTask.alsTimerHandle = timTimerSet(ROHM_RPR0521_ALS_TIMER_DELAY, 0, 50, alsTimerCallback, NULL, false); |
| } else if (!on && mTask.alsTimerHandle) { |
| timTimerCancel(mTask.alsTimerHandle); |
| mTask.alsTimerHandle = 0; |
| } |
| |
| mTask.lastAlsSample.idata = ROHM_RPR0521_ALS_INVALID; |
| mTask.alsOn = on; |
| |
| 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 = ROHM_RPR0521_DEFAULT_RATE; |
| |
| VERBOSE_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 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 != ROHM_RPR0521_ALS_INVALID) { |
| result = osEnqueuePrivateEvt(sensorGetMyEventType(SENS_TYPE_ALS), mTask.lastAlsSample.vptr, NULL, tid); |
| } |
| return result; |
| } |
| |
| static bool sensorPowerProx(bool on, void *cookie) |
| { |
| VERBOSE_PRINT("sensorPowerProx: %d\n", on); |
| |
| if (on) { |
| extiClearPendingGpio(mTask.pin); |
| enableInterrupt(mTask.pin, &mTask.isr); |
| } else { |
| disableInterrupt(mTask.pin, &mTask.isr); |
| extiClearPendingGpio(mTask.pin); |
| } |
| |
| mTask.proxState = PROX_STATE_INIT; |
| mTask.proxOn = on; |
| |
| 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 = ROHM_RPR0521_DEFAULT_RATE; |
| |
| VERBOSE_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 sensorCfgDataProx(void *data, void *cookie) |
| { |
| struct I2cTransfer *xfer; |
| |
| int32_t offset = *(int32_t*)data; |
| |
| INFO_PRINT("Received cfg data: %d\n", (int)offset); |
| |
| xfer = allocXfer(SENSOR_STATE_IDLE); |
| if (xfer != NULL) { |
| xfer->txrxBuf[0] = ROHM_RPR0521_REG_PS_OFFSET_LSB; |
| xfer->txrxBuf[1] = offset & 0xFF; |
| xfer->txrxBuf[2] = (offset >> 8) & 0x3; |
| return i2cWrite(xfer, 3); |
| } |
| |
| return false; |
| } |
| |
| static bool sendLastSampleProx(void *cookie, uint32_t tid) { |
| union EmbeddedDataPoint sample; |
| bool result = true; |
| |
| // See note in sendLastSampleAls |
| if (mTask.proxState != PROX_STATE_INIT) { |
| sample.fdata = (mTask.proxState == PROX_STATE_NEAR) ? ROHM_RPR0521_REPORT_NEAR_VALUE : ROHM_RPR0521_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, |
| .sensorCfgData = sensorCfgDataProx, |
| .sensorSendOneDirectEvt = sendLastSampleProx |
| }; |
| |
| /* |
| * Sensor i2c state machine |
| */ |
| |
| static void __attribute__((unused)) sensorAlsFree(void *ptr) |
| { |
| } |
| |
| static void __attribute__((unused)) sensorProxFree(void *ptr) |
| { |
| } |
| |
| static void handle_i2c_event(struct I2cTransfer *xfer) |
| { |
| union EmbeddedDataPoint sample; |
| uint16_t als0, als1, ps; |
| uint8_t lastProxState; |
| struct I2cTransfer *newXfer; |
| uint8_t regData; |
| |
| switch (xfer->state) { |
| case SENSOR_STATE_RESET: |
| readRegister(ROHM_RPR0521_REG_ID, 1, SENSOR_STATE_VERIFY_ID); |
| break; |
| |
| case SENSOR_STATE_VERIFY_ID: |
| /* Check the sensor ID */ |
| if (xfer->err != 0 || xfer->txrxBuf[0] != ROHM_RPR0521_ID) { |
| INFO_PRINT("not detected\n"); |
| sensorUnregister(mTask.alsHandle); |
| sensorUnregister(mTask.proxHandle); |
| break; |
| } |
| |
| newXfer = allocXfer(SENSOR_STATE_INIT_GAINS); |
| if (newXfer != NULL) { |
| newXfer->txrxBuf[0] = ROHM_RPR0521_REG_ALS_PS_CONTROL; |
| newXfer->txrxBuf[1] = (ROHM_RPR0521_GAIN_ALS0 << 4) | (ROHM_RPR0521_GAIN_ALS1 << 2) | ROHM_RPR0521_LED_CURRENT; |
| newXfer->txrxBuf[2] = (ROHM_RPR0521_GAIN_PS << 4) | PS_PERSISTENCE_ACTIVE_AT_EACH_MEASUREMENT_END; |
| i2cWrite(newXfer, 3); |
| } |
| break; |
| |
| case SENSOR_STATE_INIT_GAINS: |
| /* Offset register */ |
| newXfer = allocXfer(SENSOR_STATE_INIT_OFFSETS); |
| if (newXfer != NULL) { |
| newXfer->txrxBuf[0] = ROHM_RPR0521_REG_PS_OFFSET_LSB; |
| newXfer->txrxBuf[1] = 0; |
| newXfer->txrxBuf[2] = 0; |
| i2cWrite(newXfer, 3); |
| } |
| break; |
| |
| case SENSOR_STATE_INIT_OFFSETS: |
| /* PS Threshold register */ |
| newXfer = allocXfer(SENSOR_STATE_INIT_THRESHOLDS); |
| if (newXfer != NULL) { |
| newXfer->txrxBuf[0] = ROHM_RPR0521_REG_PS_TH_LSB; |
| newXfer->txrxBuf[1] = (ROHM_RPR0521_THRESHOLD_ASSERT_NEAR & 0xFF); |
| newXfer->txrxBuf[2] = (ROHM_RPR0521_THRESHOLD_ASSERT_NEAR & 0xFF00) >> 8; |
| newXfer->txrxBuf[3] = (ROHM_RPR0521_THRESHOLD_DEASSERT_NEAR & 0xFF); |
| newXfer->txrxBuf[4] = (ROHM_RPR0521_THRESHOLD_DEASSERT_NEAR & 0xFF00) >> 8; |
| i2cWrite(newXfer, 5); |
| } |
| break; |
| |
| case SENSOR_STATE_INIT_THRESHOLDS: |
| /* Interrupt register */ |
| regData = (INTERRUPT_MODE_PS_HYSTERESIS << 4) | INTERRUPT_LATCH_BIT | INTERRUPT_TRIGGER_PS; |
| writeRegister(ROHM_RPR0521_REG_INTERRUPT, regData, SENSOR_STATE_FINISH_INIT); |
| break; |
| |
| case SENSOR_STATE_FINISH_INIT: |
| sensorRegisterInitComplete(mTask.alsHandle); |
| sensorRegisterInitComplete(mTask.proxHandle); |
| 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 persistence setting |
| regData = (ROHM_RPR0521_GAIN_PS << 4) | PS_PERSISTENCE_ACTIVE_AT_EACH_MEASUREMENT_END; |
| writeRegister(ROHM_RPR0521_REG_PS_CONTROL, regData, SENSOR_STATE_DISABLING_PROX_2); |
| break; |
| |
| case SENSOR_STATE_DISABLING_PROX_2: |
| // Reset interrupt |
| writeRegister(ROHM_RPR0521_REG_SYSTEM_CONTROL, INT_RESET_BIT, 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: |
| als0 = *(uint16_t*)(xfer->txrxBuf); |
| als1 = *(uint16_t*)(xfer->txrxBuf+2); |
| |
| DEBUG_PRINT("als sample ready: als0=%u als1=%u\n", als0, als1); |
| |
| if (mTask.alsOn) { |
| sample.fdata = getLuxFromAlsData(als0, als1); |
| 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*)(xfer->txrxBuf); |
| lastProxState = mTask.proxState; |
| |
| DEBUG_PRINT("prox sample ready: prox=%u\n", ps); |
| |
| if (mTask.proxOn) { |
| if (ps > ROHM_RPR0521_THRESHOLD_ASSERT_NEAR) { |
| sample.fdata = ROHM_RPR0521_REPORT_NEAR_VALUE; |
| mTask.proxState = PROX_STATE_NEAR; |
| } else { |
| sample.fdata = ROHM_RPR0521_REPORT_FAR_VALUE; |
| mTask.proxState = PROX_STATE_FAR; |
| } |
| |
| if (mTask.proxState != lastProxState) |
| osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_PROX), sample.vptr, NULL); |
| |
| // After the first prox sample, change the persistance setting to assert |
| // interrupt on-change, rather than after every sample |
| regData = (ROHM_RPR0521_GAIN_PS << 4) | PS_PERSISTENCE_STATUS_UPDATED_AT_EACH_MEASUREMENT_END; |
| writeRegister(ROHM_RPR0521_REG_PS_CONTROL, regData, SENSOR_STATE_IDLE); |
| } |
| |
| break; |
| |
| default: |
| break; |
| } |
| |
| releaseXfer(xfer); |
| } |
| |
| /* |
| * 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 = ROHM_RPR0521_ALS_INVALID; |
| mTask.proxState = PROX_STATE_INIT; |
| |
| mTask.pin = gpioRequest(PROX_INT_PIN); |
| gpioConfigInput(mTask.pin, GPIO_SPEED_LOW, GPIO_PULL_NONE); |
| syscfgSetExtiPort(mTask.pin); |
| mTask.isr.func = proxIsr; |
| |
| /* Register sensors */ |
| 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); |
| |
| /* Reset chip */ |
| writeRegister(ROHM_RPR0521_REG_SYSTEM_CONTROL, SW_RESET_BIT, SENSOR_STATE_RESET); |
| break; |
| |
| case EVT_SENSOR_I2C: |
| handle_i2c_event((struct I2cTransfer*)evtData); |
| break; |
| |
| case EVT_SENSOR_ALS_TIMER: |
| readRegister(ROHM_RPR0521_REG_ALS_DATA0_LSB, 4, SENSOR_STATE_ALS_SAMPLING); |
| break; |
| |
| case EVT_SENSOR_PROX_INTERRUPT: |
| // Over-read to read the INTERRUPT register to clear the interrupt |
| readRegister(ROHM_RPR0521_REG_PS_DATA_LSB, 7, SENSOR_STATE_PROX_SAMPLING); |
| break; |
| } |
| } |
| |
| INTERNAL_APP_INIT(APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 10), RPR0521_APP_VERSION, init_app, end_app, handle_event); |