| /* |
| * Copyright (C) 2017 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 <atomic.h> |
| #include <gpio.h> |
| #include <nanohubPacket.h> |
| #include <plat/exti.h> |
| #include <plat/gpio.h> |
| #include <platform.h> |
| #include <plat/syscfg.h> |
| #include <sensors.h> |
| #include <seos.h> |
| #include <slab.h> |
| #include <i2c.h> |
| #include <timer.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <variant/variant.h> |
| |
| #define SI7034A10_APP_ID APP_ID_MAKE(NANOHUB_VENDOR_GOOGLE, 22) |
| |
| /* Sensor defs */ |
| #define SI7034_ID_SAMPLE 0xFF |
| #define SI7034_ID_PROD 0x22 |
| |
| #define SI7034_RESET_CMD 0xFE |
| #define SI7034_READID_0_CMD 0xFC |
| #define SI7034_READID_1_CMD 0xC9 |
| #define SI7034_READDATA_0_CMD 0x7C |
| #define SI7034_READDATA_1_CMD 0xA2 |
| |
| #define SI7034_HUMIGRADES(humi_val) ((humi_val * 12500) >> 13) |
| #define SI7034_CENTIGRADES(temp_val) (((temp_val * 21875) >> 13) - 45000) |
| |
| #define INFO_PRINT(fmt, ...) \ |
| do { \ |
| osLog(LOG_INFO, "%s " fmt, "[SI7034]", ##__VA_ARGS__); \ |
| } while (0); |
| |
| #define DEBUG_PRINT(fmt, ...) \ |
| do { \ |
| if (SI7034_DBG_ENABLED) { \ |
| osLog(LOG_DEBUG, "%s " fmt, "[SI7034]", ##__VA_ARGS__); \ |
| } \ |
| } while (0); |
| |
| #define ERROR_PRINT(fmt, ...) \ |
| do { \ |
| osLog(LOG_ERROR, "%s " fmt, "[SI7034]", ##__VA_ARGS__); \ |
| } while (0); |
| |
| /* DO NOT MODIFY, just to avoid compiler error if not defined using FLAGS */ |
| #ifndef SI7034_DBG_ENABLED |
| #define SI7034_DBG_ENABLED 0 |
| #endif /* SI7034_DBG_ENABLED */ |
| |
| enum si7034SensorEvents |
| { |
| EVT_SENSOR_I2C = EVT_APP_START + 1, |
| EVT_SENSOR_HUMIDITY_TIMER, |
| EVT_SENSOR_TEMP_TIMER, |
| EVT_TEST, |
| }; |
| |
| enum si7034SensorState { |
| SENSOR_BOOT, |
| SENSOR_VERIFY_ID, |
| SENSOR_READ_SAMPLES, |
| }; |
| |
| #ifndef SI7034A10_I2C_BUS_ID |
| #error "SI7034A10_I2C_BUS_ID is not defined; please define in variant.h" |
| #endif |
| |
| #ifndef SI7034A10_I2C_SPEED |
| #define SI7034A10_I2C_SPEED 400000 |
| #endif |
| |
| #ifndef SI7034A10_I2C_ADDR |
| #define SI7034A10_I2C_ADDR 0x70 |
| #endif |
| |
| enum si7034SensorIndex { |
| HUMIDITY = 0, |
| TEMP, |
| NUM_OF_SENSOR, |
| }; |
| |
| struct si7034Sensor { |
| uint32_t handle; |
| }; |
| |
| #define SI7034_MAX_PENDING_I2C_REQUESTS 4 |
| #define SI7034_MAX_I2C_TRANSFER_SIZE 6 |
| |
| struct I2cTransfer |
| { |
| size_t tx; |
| size_t rx; |
| int err; |
| uint8_t txrxBuf[SI7034_MAX_I2C_TRANSFER_SIZE]; |
| uint8_t state; |
| bool inUse; |
| }; |
| |
| /* Task structure */ |
| struct si7034Task { |
| uint32_t tid; |
| |
| /* timer */ |
| uint32_t humiTimerHandle; |
| uint32_t tempTimerHandle; |
| |
| /* sensor flags */ |
| bool humiOn; |
| bool humiReading; |
| bool tempOn; |
| bool tempReading; |
| |
| struct I2cTransfer transfers[SI7034_MAX_PENDING_I2C_REQUESTS]; |
| |
| /* sensors */ |
| struct si7034Sensor sensors[NUM_OF_SENSOR]; |
| }; |
| |
| static struct si7034Task mTask; |
| |
| // 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!"); |
| return NULL; |
| } |
| |
| static inline void releaseXfer(struct I2cTransfer *xfer) |
| { |
| xfer->inUse = false; |
| } |
| |
| 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 bool si7034_i2c_read(uint8_t addr0, uint8_t addr1, uint8_t state) |
| { |
| struct I2cTransfer *xfer = allocXfer(state); |
| int ret = -1; |
| |
| if (xfer != NULL) { |
| xfer->txrxBuf[0] = addr0; |
| xfer->txrxBuf[1] = addr1; |
| ret = i2cMasterTxRx(SI7034A10_I2C_BUS_ID, SI7034A10_I2C_ADDR, |
| xfer->txrxBuf, 2, xfer->txrxBuf, 6, i2cCallback, xfer); |
| if (ret) { |
| releaseXfer(xfer); |
| return false; |
| } |
| } |
| |
| return (ret == -1) ? false : true; |
| } |
| |
| static bool si7034_i2c_write(uint8_t data, uint8_t state) |
| { |
| struct I2cTransfer *xfer = allocXfer(state); |
| int ret = -1; |
| |
| if (xfer != NULL) { |
| xfer->txrxBuf[0] = data; |
| ret = i2cMasterTx(SI7034A10_I2C_BUS_ID, SI7034A10_I2C_ADDR, |
| xfer->txrxBuf, 1, i2cCallback, xfer); |
| if (ret) { |
| releaseXfer(xfer); |
| return false; |
| } |
| } |
| |
| return (ret == -1) ? false : true; |
| } |
| |
| /* Sensor Info */ |
| static void sensorHumiTimerCallback(uint32_t timerId, void *data) |
| { |
| osEnqueuePrivateEvt(EVT_SENSOR_HUMIDITY_TIMER, data, NULL, mTask.tid); |
| } |
| |
| static void sensorTempTimerCallback(uint32_t timerId, void *data) |
| { |
| osEnqueuePrivateEvt(EVT_SENSOR_TEMP_TIMER, data, NULL, mTask.tid); |
| } |
| |
| #define DEC_INFO(name, type, axis, inter, samples, rates) \ |
| .sensorName = name, \ |
| .sensorType = type, \ |
| .numAxis = axis, \ |
| .interrupt = inter, \ |
| .minSamples = samples, \ |
| .supportedRates = rates |
| |
| static uint32_t si7034Rates[] = { |
| SENSOR_HZ(0.1), |
| SENSOR_HZ(1.0f), |
| SENSOR_HZ(5.0f), |
| SENSOR_HZ(10.0f), |
| SENSOR_HZ(25.0f), |
| 0 |
| }; |
| |
| // should match "supported rates in length" and be the timer length for that rate in nanosecs |
| static const uint64_t si7034RatesRateVals[] = |
| { |
| 10 * 1000000000ULL, |
| 1 * 1000000000ULL, |
| 1000000000ULL / 5, |
| 1000000000ULL / 10, |
| 1000000000ULL / 25, |
| }; |
| |
| |
| static const struct SensorInfo si7034SensorInfo[NUM_OF_SENSOR] = |
| { |
| { DEC_INFO("Humidity", SENS_TYPE_HUMIDITY, NUM_AXIS_EMBEDDED, NANOHUB_INT_NONWAKEUP, |
| 300, si7034Rates) }, |
| { DEC_INFO("Temperature", SENS_TYPE_AMBIENT_TEMP, NUM_AXIS_EMBEDDED, NANOHUB_INT_NONWAKEUP, |
| 20, si7034Rates) }, |
| }; |
| |
| /* Sensor Operations */ |
| static bool humiPower(bool on, void *cookie) |
| { |
| DEBUG_PRINT("%s: %d\n", __func__, on); |
| |
| if (mTask.humiTimerHandle) { |
| timTimerCancel(mTask.humiTimerHandle); |
| mTask.humiTimerHandle = 0; |
| mTask.humiReading = false; |
| } |
| mTask.humiOn = on; |
| return sensorSignalInternalEvt(mTask.sensors[HUMIDITY].handle, |
| SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0); |
| } |
| |
| static bool humiFwUpload(void *cookie) |
| { |
| DEBUG_PRINT("%s\n", __func__); |
| |
| return sensorSignalInternalEvt(mTask.sensors[HUMIDITY].handle, |
| SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0); |
| } |
| |
| static bool humiSetRate(uint32_t rate, uint64_t latency, void *cookie) |
| { |
| DEBUG_PRINT("%s %ld (%lld)\n", __func__, rate, latency); |
| |
| if (mTask.humiTimerHandle) |
| timTimerCancel(mTask.humiTimerHandle); |
| |
| mTask.humiTimerHandle = timTimerSet(sensorTimerLookupCommon(si7034Rates, |
| si7034RatesRateVals, rate), 0, 50, sensorHumiTimerCallback, NULL, false); |
| |
| return sensorSignalInternalEvt(mTask.sensors[HUMIDITY].handle, |
| SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency); |
| } |
| |
| static bool humiFlush(void *cookie) |
| { |
| return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_HUMIDITY), SENSOR_DATA_EVENT_FLUSH, NULL); |
| } |
| |
| static bool tempPower(bool on, void *cookie) |
| { |
| DEBUG_PRINT("%s: %d\n", __func__, on); |
| |
| if (mTask.tempTimerHandle) { |
| timTimerCancel(mTask.tempTimerHandle); |
| mTask.tempTimerHandle = 0; |
| mTask.tempReading = false; |
| } |
| mTask.tempOn = on; |
| return sensorSignalInternalEvt(mTask.sensors[TEMP].handle, |
| SENSOR_INTERNAL_EVT_POWER_STATE_CHG, on, 0); |
| } |
| |
| static bool tempFwUpload(void *cookie) |
| { |
| DEBUG_PRINT("%s\n", __func__); |
| |
| return sensorSignalInternalEvt(mTask.sensors[TEMP].handle, |
| SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0); |
| } |
| |
| static bool tempSetRate(uint32_t rate, uint64_t latency, void *cookie) |
| { |
| DEBUG_PRINT("%s %ld (%lld)\n", __func__, rate, latency); |
| |
| if (mTask.tempTimerHandle) |
| timTimerCancel(mTask.tempTimerHandle); |
| |
| mTask.tempTimerHandle = timTimerSet(sensorTimerLookupCommon(si7034Rates, |
| si7034RatesRateVals, rate), 0, 50, sensorTempTimerCallback, NULL, false); |
| |
| return sensorSignalInternalEvt(mTask.sensors[TEMP].handle, |
| SENSOR_INTERNAL_EVT_RATE_CHG, rate, latency); |
| } |
| |
| static bool tempFlush(void *cookie) |
| { |
| return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_AMBIENT_TEMP), SENSOR_DATA_EVENT_FLUSH, NULL); |
| } |
| |
| #define DEC_OPS(power, firmware, rate, flush, cal, cfg) \ |
| .sensorPower = power, \ |
| .sensorFirmwareUpload = firmware, \ |
| .sensorSetRate = rate, \ |
| .sensorFlush = flush, \ |
| .sensorCalibrate = cal, \ |
| .sensorCfgData = cfg |
| |
| static const struct SensorOps si7034SensorOps[NUM_OF_SENSOR] = |
| { |
| { DEC_OPS(humiPower, humiFwUpload, humiSetRate, humiFlush, NULL, NULL) }, |
| { DEC_OPS(tempPower, tempFwUpload, tempSetRate, tempFlush, NULL, NULL) }, |
| }; |
| |
| static void handleI2cEvent(const void *evtData) |
| { |
| struct I2cTransfer *xfer = (struct I2cTransfer *)evtData; |
| union EmbeddedDataPoint sample; |
| uint32_t value; |
| uint8_t i; |
| |
| switch (xfer->state) { |
| case SENSOR_BOOT: |
| if (!si7034_i2c_read(SI7034_READID_0_CMD, SI7034_READID_1_CMD, SENSOR_VERIFY_ID)) { |
| DEBUG_PRINT("Not able to read ID\n"); |
| return; |
| } |
| break; |
| |
| case SENSOR_VERIFY_ID: |
| /* Check the sensor ID */ |
| if (xfer->err != 0) |
| return; |
| INFO_PRINT("Device ID = (%02x)\n", xfer->txrxBuf[0]); |
| if ((xfer->txrxBuf[0] != SI7034_ID_SAMPLE) && |
| (xfer->txrxBuf[0] != SI7034_ID_PROD)) |
| break; |
| INFO_PRINT("detected\n"); |
| for (i = 0; i < NUM_OF_SENSOR; i++) |
| sensorRegisterInitComplete(mTask.sensors[i].handle); |
| |
| /* TEST the environment in standalone mode */ |
| if (SI7034_DBG_ENABLED) { |
| mTask.humiOn = mTask.tempOn = true; |
| osEnqueuePrivateEvt(EVT_TEST, NULL, NULL, mTask.tid); |
| } |
| break; |
| |
| case SENSOR_READ_SAMPLES: |
| if (mTask.humiOn && mTask.humiReading) { |
| value = ((uint32_t)(xfer->txrxBuf[3]) << 8) | xfer->txrxBuf[4]; |
| value = SI7034_HUMIGRADES(value); |
| value = (value > 100000) ? 100000 : value; |
| DEBUG_PRINT("Humidity = %u\n", (unsigned)value); |
| sample.fdata = (float)value / 1000.0f; |
| |
| osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_HUMIDITY), sample.vptr, NULL); |
| } |
| |
| if (mTask.tempOn && mTask.tempReading) { |
| value = ((uint32_t)(xfer->txrxBuf[0]) << 8) | xfer->txrxBuf[1]; |
| value = SI7034_CENTIGRADES(value); |
| DEBUG_PRINT("Temp = %u\n", (unsigned)value); |
| sample.fdata = (float)value / 1000.0f; |
| |
| osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_AMBIENT_TEMP), sample.vptr, NULL); |
| } |
| |
| mTask.humiReading = mTask.tempReading = false; |
| break; |
| |
| default: |
| break; |
| } |
| |
| releaseXfer(xfer); |
| } |
| |
| static void handleEvent(uint32_t evtType, const void* evtData) |
| { |
| switch (evtType) { |
| case EVT_APP_START: |
| osEventUnsubscribe(mTask.tid, EVT_APP_START); |
| si7034_i2c_write(SI7034_RESET_CMD, SENSOR_BOOT); |
| break; |
| |
| case EVT_SENSOR_I2C: |
| handleI2cEvent(evtData); |
| break; |
| |
| case EVT_SENSOR_HUMIDITY_TIMER: |
| DEBUG_PRINT("EVT_SENSOR_HUMIDITY_TIMER\n"); |
| |
| if (!mTask.humiOn) |
| break; |
| /* Start sampling for a value */ |
| if (!mTask.humiReading && !mTask.tempReading) |
| si7034_i2c_read(SI7034_READDATA_0_CMD, SI7034_READDATA_1_CMD, SENSOR_READ_SAMPLES); |
| mTask.humiReading = true; |
| break; |
| |
| case EVT_SENSOR_TEMP_TIMER: |
| DEBUG_PRINT("EVT_SENSOR_TEMP_TIMER\n"); |
| |
| if (!mTask.tempOn) |
| break; |
| /* Start sampling for a value */ |
| if (!mTask.humiReading && !mTask.tempReading) |
| si7034_i2c_read(SI7034_READDATA_0_CMD, SI7034_READDATA_1_CMD, SENSOR_READ_SAMPLES); |
| mTask.tempReading = true; |
| break; |
| |
| case EVT_TEST: |
| DEBUG_PRINT("EVT_TEST\n"); |
| |
| humiSetRate(SENSOR_HZ(1), 0, NULL); |
| tempSetRate(SENSOR_HZ(1), 0, NULL); |
| break; |
| |
| default: |
| break; |
| } |
| } |
| |
| static bool startTask(uint32_t task_id) |
| { |
| uint8_t i; |
| |
| mTask.tid = task_id; |
| |
| DEBUG_PRINT("task started\n"); |
| |
| mTask.humiOn = mTask.humiReading = false; |
| mTask.tempOn = mTask.tempReading = false; |
| |
| /* Init the communication part */ |
| i2cMasterRequest(SI7034A10_I2C_BUS_ID, SI7034A10_I2C_SPEED); |
| |
| for (i = 0; i < NUM_OF_SENSOR; i++) { |
| mTask.sensors[i].handle = |
| sensorRegister(&si7034SensorInfo[i], &si7034SensorOps[i], NULL, false); |
| } |
| |
| osEventSubscribe(mTask.tid, EVT_APP_START); |
| |
| return true; |
| } |
| |
| static void endTask(void) |
| { |
| uint8_t i; |
| |
| DEBUG_PRINT("task ended\n"); |
| |
| for (i = 0; i < NUM_OF_SENSOR; i++) { |
| sensorUnregister(mTask.sensors[i].handle); |
| } |
| } |
| |
| INTERNAL_APP_INIT(SI7034A10_APP_ID, 0, startTask, endTask, handleEvent); |