/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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);