/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ST_MAG40_APP_ID APP_ID_MAKE(NANOHUB_VENDOR_STMICRO, 3) /* Sensor registers */ #define ST_MAG40_WAI_REG_ADDR 0x4F #define ST_MAG40_WAI_REG_VAL 0x40 #define ST_MAG40_CFG_A_REG_ADDR 0x60 #define ST_MAG40_TEMP_COMP_EN 0x80 #define ST_MAG40_SOFT_RESET_BIT 0x20 #define ST_MAG40_ODR_10_HZ 0x00 #define ST_MAG40_ODR_20_HZ 0x04 #define ST_MAG40_ODR_50_HZ 0x08 #define ST_MAG40_ODR_100_HZ 0x0C #define ST_MAG40_POWER_ON 0x00 #define ST_MAG40_POWER_IDLE 0x03 #define ST_MAG40_CFG_B_REG_ADDR 0x61 #define ST_MAG40_OFF_CANC 0x02 #define ST_MAG40_CFG_C_REG_ADDR 0x62 #define ST_MAG40_I2C_DIS 0x20 #define ST_MAG40_BDU_ON 0x10 #define ST_MAG40_SELFTEST_EN 0x02 #define ST_MAG40_INT_MAG 0x01 #define ST_MAG40_OUTXL_REG_ADDR 0x68 /* Enable auto-increment of the I2C subaddress (to allow I2C multiple ops) */ #define ST_MAG40_I2C_AUTO_INCR 0x80 enum st_mag40_SensorEvents { EVT_COMM_DONE = EVT_APP_START + 1, EVT_SENSOR_INTERRUPT, }; enum st_mag40_TestState { MAG_SELFTEST_INIT, MAG_SELFTEST_RUN_ST_OFF, MAG_SELFTEST_INIT_ST_EN, MAG_SELFTEST_RUN_ST_ON, MAG_SELFTEST_VERIFY, MAG_SELFTEST_DONE, }; enum st_mag40_SensorState { SENSOR_BOOT, SENSOR_VERIFY_ID, SENSOR_INITIALIZATION, SENSOR_IDLE, SENSOR_MAG_CONFIGURATION, SENSOR_READ_SAMPLES, SENSOR_SELF_TEST, }; enum st_mag40_subState { NO_SUBSTATE = 0, INIT_START, INIT_ENABLE_DRDY, INIT_I2C_DISABLE_ACCEL, INIT_DONE, CONFIG_POWER_UP, CONFIG_POWER_UP_2, CONFIG_POWER_DOWN, CONFIG_POWER_DOWN_2, CONFIG_SET_RATE, CONFIG_SET_RATE_2, CONFIG_DONE, }; struct TestResultData { struct HostHubRawPacket header; struct SensorAppEventHeader data_header; } __attribute__((packed)); #ifndef ST_MAG40_I2C_BUS_ID #error "ST_MAG40_I2C_BUS_ID is not defined; please define in variant.h" #endif #ifndef ST_MAG40_I2C_SPEED #error "ST_MAG40_I2C_SPEED is not defined; please define in variant.h" #endif #ifndef ST_MAG40_I2C_ADDR #error "ST_MAG40_I2C_ADDR is not defined; please define in variant.h" #endif #ifndef ST_MAG40_INT_PIN #error "ST_MAG40_INT_PIN is not defined; please define in variant.h" #endif #ifndef ST_MAG40_INT_IRQ #error "ST_MAG40_INT_IRQ is not defined; please define in variant.h" #endif #ifndef ST_MAG40_ROT_MATRIX #error "ST_MAG40_ROT_MATRIX is not defined; please define in variant.h" #endif #define ST_MAG40_X_MAP(x, y, z, r11, r12, r13, r21, r22, r23, r31, r32, r33) \ ((r11 == 1 ? x : (r11 == -1 ? -x : 0)) + \ (r12 == 1 ? y : (r12 == -1 ? -y : 0)) + \ (r13 == 1 ? z : (r13 == -1 ? -z : 0))) #define ST_MAG40_Y_MAP(x, y, z, r11, r12, r13, r21, r22, r23, r31, r32, r33) \ ((r21 == 1 ? x : (r21 == -1 ? -x : 0)) + \ (r22 == 1 ? y : (r22 == -1 ? -y : 0)) + \ (r23 == 1 ? z : (r23 == -1 ? -z : 0))) #define ST_MAG40_Z_MAP(x, y, z, r11, r12, r13, r21, r22, r23, r31, r32, r33) \ ((r31 == 1 ? x : (r31 == -1 ? -x : 0)) + \ (r32 == 1 ? y : (r32 == -1 ? -y : 0)) + \ (r33 == 1 ? z : (r33 == -1 ? -z : 0))) #define ST_MAG40_REMAP_X_DATA(...) ST_MAG40_X_MAP(__VA_ARGS__) #define ST_MAG40_REMAP_Y_DATA(...) ST_MAG40_Y_MAP(__VA_ARGS__) #define ST_MAG40_REMAP_Z_DATA(...) ST_MAG40_Z_MAP(__VA_ARGS__) /* Self Test macros */ #define ST_MAG40_ST_NUM_OF_SAMPLES 50 #define ST_MAG40_ST_MIN_THRESHOLD 10 /* 15 mGa */ #define ST_MAG40_ST_MAX_THRESHOLD 333 /* 500 mGa */ #define INFO_PRINT(fmt, ...) \ do { \ osLog(LOG_INFO, "%s " fmt, "[ST_MAG40]", ##__VA_ARGS__); \ } while (0); #define DEBUG_PRINT(fmt, ...) \ do { \ if (ST_MAG40_DBG_ENABLED) { \ osLog(LOG_DEBUG, "%s " fmt, "[ST_MAG40]", ##__VA_ARGS__); \ } \ } while (0); #define ERROR_PRINT(fmt, ...) \ do { \ osLog(LOG_ERROR, "%s " fmt, "[ST_MAG40]", ##__VA_ARGS__); \ } while (0); /* DO NOT MODIFY, just to avoid compiler error if not defined using FLAGS */ #ifndef ST_MAG40_DBG_ENABLED #define ST_MAG40_DBG_ENABLED 0 #endif /* ST_MAG40_DBG_ENABLED */ #define ST_MAG40_MAX_PENDING_I2C_REQUESTS 4 #define ST_MAG40_MAX_I2C_TRANSFER_SIZE 6 #define ST_MAG40_MAX_MAG_EVENTS 20 struct I2cTransfer { size_t tx; size_t rx; int err; uint8_t txrxBuf[ST_MAG40_MAX_I2C_TRANSFER_SIZE]; bool last; bool inUse; uint32_t delay; }; /* Task structure */ struct st_mag40_Task { uint32_t tid; struct SlabAllocator *magDataSlab; uint64_t timestampInt; volatile uint8_t state; //task state, type enum st_mag40_SensorState, do NOT change this directly uint8_t subState; /* sensor flags */ uint8_t samplesToDiscard; uint32_t rate; uint64_t latency; bool magOn; bool pendingInt; uint8_t pendingSubState; uint8_t currentODR; #if defined(ST_MAG40_CAL_ENABLED) struct MagCal moc; #endif unsigned char sens_buf[7]; struct I2cTransfer transfers[ST_MAG40_MAX_PENDING_I2C_REQUESTS]; /* Communication functions */ void (*comm_tx)(uint8_t addr, uint8_t data, uint32_t delay, bool last); void (*comm_rx)(uint8_t addr, uint16_t len, uint32_t delay, bool last); /* irq */ struct Gpio *Int1; struct ChainedIsr Isr1; /* Self Test */ enum st_mag40_TestState mag_test_state; uint32_t mag_selftest_num; int32_t dataST[3]; int32_t dataNOST[3]; /* sensors */ uint32_t magHandle; }; static struct st_mag40_Task mTask; static void sensorMagConfig(void); #define PRI_STATE PRIi32 static int32_t getStateName(int32_t s) { return s; } // Atomic get state #define GET_STATE() (atomicReadByte(&mTask.state)) // Atomic set state, this set the state to arbitrary value, use with caution #define SET_STATE(s) do{\ DEBUG_PRINT("set state %" PRI_STATE "\n", getStateName(s));\ atomicWriteByte(&mTask.state, (s));\ }while(0) // Atomic switch state from IDLE to desired state. static bool trySwitchState(enum st_mag40_SensorState newState) { #if DBG_STATE bool ret = atomicCmpXchgByte(&mTask.state, SENSOR_IDLE, newState); uint8_t prevState = ret ? SENSOR_IDLE : GET_STATE(); DEBUG_PRINT("switch state %" PRI_STATE "->%" PRI_STATE ", %s\n", getStateName(prevState), getStateName(newState), ret ? "ok" : "failed"); return ret; #else return atomicCmpXchgByte(&mTask.state, SENSOR_IDLE, newState); #endif } static bool magAllocateEvt(struct TripleAxisDataEvent **evPtr) { struct TripleAxisDataEvent *ev; ev = *evPtr = slabAllocatorAlloc(mTask.magDataSlab); if (!ev) { ERROR_PRINT("Failed to allocate mag event memory"); return false; } memset(&ev->samples[0].firstSample, 0x00, sizeof(struct SensorFirstSample)); return true; } static void magFreeEvt(void *ptr) { slabAllocatorFree(mTask.magDataSlab, ptr); } // 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(void) { size_t i; for (i = 0; i < ARRAY_SIZE(mTask.transfers); i++) { if (!mTask.transfers[i].inUse) { mTask.transfers[i].inUse = true; 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); /* delayed callback */ static void i2cDelayCallback(uint32_t timerId, void *data) { struct I2cTransfer *xfer = data; i2cCallback((void *)xfer, xfer->tx, xfer->rx, xfer->err); } static void i2cCallback(void *cookie, size_t tx, size_t rx, int err) { struct I2cTransfer *xfer = cookie; /* Do not run callback if not the last one in a set of i2c transfers */ if (xfer && !xfer->last) { releaseXfer(xfer); return; } /* delay callback if it is the case */ if (xfer->delay > 0) { xfer->tx = tx; xfer->rx = rx; xfer->err = err; if (!timTimerSet(xfer->delay * 1000, 0, 50, i2cDelayCallback, xfer, true)) { ERROR_PRINT("Cannot do delayed i2cCallback\n"); goto handle_now; } xfer->delay = 0; return; } handle_now: xfer->tx = tx; xfer->rx = rx; xfer->err = err; osEnqueuePrivateEvt(EVT_COMM_DONE, cookie, NULL, mTask.tid); if (err != 0) ERROR_PRINT("i2c error (tx: %d, rx: %d, err: %d)\n", tx, rx, err); } static void i2c_read(uint8_t addr, uint16_t len, uint32_t delay, bool last) { struct I2cTransfer *xfer = allocXfer(); if (xfer != NULL) { xfer->delay = delay; xfer->last = last; xfer->txrxBuf[0] = ST_MAG40_I2C_AUTO_INCR | addr; i2cMasterTxRx(ST_MAG40_I2C_BUS_ID, ST_MAG40_I2C_ADDR, xfer->txrxBuf, 1, xfer->txrxBuf, len, i2cCallback, xfer); } } static void i2c_write(uint8_t addr, uint8_t data, uint32_t delay, bool last) { struct I2cTransfer *xfer = allocXfer(); if (xfer != NULL) { xfer->delay = delay; xfer->last = last; xfer->txrxBuf[0] = addr; xfer->txrxBuf[1] = data; i2cMasterTx(ST_MAG40_I2C_BUS_ID, ST_MAG40_I2C_ADDR, xfer->txrxBuf, 2, i2cCallback, xfer); } } #define DEC_INFO_BIAS(name, type, axis, inter, samples, rates, raw, scale, bias) \ .sensorName = name, \ .sensorType = type, \ .numAxis = axis, \ .interrupt = inter, \ .minSamples = samples, \ .supportedRates = rates, \ .rawType = raw, \ .rawScale = scale, \ .biasType = bias #define DEC_INFO(name, type, axis, inter, samples, rates, raw, scale) \ .sensorName = name, \ .sensorType = type, \ .numAxis = axis, \ .interrupt = inter, \ .minSamples = samples, \ .supportedRates = rates, \ .rawType = raw, \ .rawScale = scale, static uint32_t st_mag40_Rates[] = { SENSOR_HZ(10.0f), SENSOR_HZ(20.0f), SENSOR_HZ(50.0f), SENSOR_HZ(100.0f), 0 }; static uint32_t st_mag40_regVal[] = { ST_MAG40_ODR_10_HZ, ST_MAG40_ODR_20_HZ, ST_MAG40_ODR_50_HZ, ST_MAG40_ODR_100_HZ, }; static uint8_t st_mag40_computeOdr(uint32_t rate) { int i; for (i = 0; i < (ARRAY_SIZE(st_mag40_Rates) - 1); i++) { if (st_mag40_Rates[i] == rate) break; } if (i == (ARRAY_SIZE(st_mag40_Rates) -1 )) { ERROR_PRINT("ODR not valid! Choosed smallest ODR available\n"); i = 0; } return i; } static const struct SensorInfo st_mag40_SensorInfo = { #if defined(ST_MAG40_CAL_ENABLED) DEC_INFO_BIAS("Magnetometer", SENS_TYPE_MAG, NUM_AXIS_THREE, NANOHUB_INT_NONWAKEUP, 600, st_mag40_Rates, 0, 0, SENS_TYPE_MAG_BIAS) #else DEC_INFO("Magnetometer", SENS_TYPE_MAG, NUM_AXIS_THREE, NANOHUB_INT_NONWAKEUP, 600, st_mag40_Rates, 0, 0) #endif }; /* Sensor Operations */ static bool magPower(bool on, void *cookie) { INFO_PRINT("magPower %s\n", on ? "on" : "off"); if (trySwitchState(SENSOR_MAG_CONFIGURATION)) { mTask.subState = on ? CONFIG_POWER_UP : CONFIG_POWER_DOWN; sensorMagConfig(); } else { mTask.pendingSubState = on ? CONFIG_POWER_UP : CONFIG_POWER_DOWN; } return true; } static bool magFwUpload(void *cookie) { return sensorSignalInternalEvt(mTask.magHandle, SENSOR_INTERNAL_EVT_FW_STATE_CHG, 1, 0); } static bool magSetRate(uint32_t rate, uint64_t latency, void *cookie) { uint8_t num = 0; INFO_PRINT("magSetRate %lu Hz - %llu ns\n", rate, latency); num = st_mag40_computeOdr(rate); mTask.currentODR = st_mag40_regVal[num]; mTask.rate = rate; mTask.latency = latency; mTask.samplesToDiscard = 2; if (trySwitchState(SENSOR_MAG_CONFIGURATION)) { mTask.subState = CONFIG_SET_RATE; sensorMagConfig(); } else { mTask.pendingSubState = CONFIG_SET_RATE; } return true; } static bool magFlush(void *cookie) { INFO_PRINT("magFlush\n"); return osEnqueueEvt(sensorGetMyEventType(SENS_TYPE_MAG), SENSOR_DATA_EVENT_FLUSH, NULL); } static bool magCfgData(void *data, void *cookie) { #if defined(ST_MAG40_CAL_ENABLED) const struct AppToSensorHalDataPayload *p = data; if (p->type == HALINTF_TYPE_MAG_CAL_BIAS && p->size == sizeof(struct MagCalBias)) { const struct MagCalBias *d = p->magCalBias; INFO_PRINT("magCfgData: calibration %ldnT, %ldnT, %ldnT\n", (int32_t)(d->bias[0] * 1000), (int32_t)(d->bias[1] * 1000), (int32_t)(d->bias[2] * 1000)); mTask.moc.x_bias = d->bias[0]; mTask.moc.y_bias = d->bias[1]; mTask.moc.z_bias = d->bias[2]; } else if (p->type == HALINTF_TYPE_MAG_LOCAL_FIELD && p->size == sizeof(struct MagLocalField)) { const struct MagLocalField *d = p->magLocalField; INFO_PRINT("magCfgData: local field strength %dnT, dec %ddeg, inc %ddeg\n", (int)(d->strength * 1000), (int)(d->declination * 180 / M_PI + 0.5f), (int)(d->inclination * 180 / M_PI + 0.5f)); // Passing local field information to mag calibration routine #ifdef DIVERSITY_CHECK_ENABLED diversityCheckerLocalFieldUpdate(&mTask.moc.diversity_checker, d->strength); #endif // TODO: pass local field information to rotation vector sensor. } else { ERROR_PRINT("magCfgData: unknown type 0x%04x, size %d", p->type, p->size); } #endif /* ST_MAG40_CAL_ENABLED */ return true; } static void sendTestResult(uint8_t status, uint8_t sensorType) { struct TestResultData *data = heapAlloc(sizeof(struct TestResultData)); if (!data) { ERROR_PRINT("Couldn't alloc test result packet"); return; } data->header.appId = ST_MAG40_APP_ID; data->header.dataLen = (sizeof(struct TestResultData) - sizeof(struct HostHubRawPacket)); data->data_header.msgId = SENSOR_APP_MSG_ID_TEST_RESULT; data->data_header.sensorType = sensorType; data->data_header.status = status; if (!osEnqueueEvtOrFree(EVT_APP_TO_HOST, data, heapFree)) ERROR_PRINT("Couldn't send test result packet"); } static void magTestHandling(struct I2cTransfer *xfer) { int32_t dataGap[3]; switch(mTask.mag_test_state) { case MAG_SELFTEST_INIT: mTask.mag_selftest_num = 0; memset(mTask.dataNOST, 0, 3 * sizeof(int32_t)); mTask.mag_test_state = MAG_SELFTEST_RUN_ST_OFF; mTask.comm_tx(ST_MAG40_CFG_A_REG_ADDR, ST_MAG40_ODR_100_HZ, 0, false); mTask.comm_tx(ST_MAG40_CFG_B_REG_ADDR, ST_MAG40_OFF_CANC, 0, false); mTask.comm_tx(ST_MAG40_CFG_C_REG_ADDR, ST_MAG40_BDU_ON, 0, true); break; case MAG_SELFTEST_RUN_ST_OFF: if (mTask.mag_selftest_num++ > 0) { uint8_t *raw = &xfer->txrxBuf[0]; mTask.dataNOST[0] += (*(int16_t *)&raw[0]); mTask.dataNOST[1] += (*(int16_t *)&raw[2]); mTask.dataNOST[2] += (*(int16_t *)&raw[4]); } if (mTask.mag_selftest_num <= ST_MAG40_ST_NUM_OF_SAMPLES) { mTask.comm_rx(ST_MAG40_OUTXL_REG_ADDR, 6, 10000, true); break; } mTask.dataNOST[0] /= ST_MAG40_ST_NUM_OF_SAMPLES; mTask.dataNOST[1] /= ST_MAG40_ST_NUM_OF_SAMPLES; mTask.dataNOST[2] /= ST_MAG40_ST_NUM_OF_SAMPLES; mTask.mag_test_state = MAG_SELFTEST_INIT_ST_EN; /* fall through */ case MAG_SELFTEST_INIT_ST_EN: mTask.mag_selftest_num = 0; memset(mTask.dataST, 0, 3 * sizeof(int32_t)); mTask.mag_test_state = MAG_SELFTEST_RUN_ST_ON; mTask.comm_tx(ST_MAG40_CFG_C_REG_ADDR, ST_MAG40_BDU_ON | ST_MAG40_SELFTEST_EN, 0, true); break; case MAG_SELFTEST_RUN_ST_ON: if (mTask.mag_selftest_num++ > 0) { uint8_t *raw = &xfer->txrxBuf[0]; mTask.dataST[0] += (*(int16_t *)&raw[0]); mTask.dataST[1] += (*(int16_t *)&raw[2]); mTask.dataST[2] += (*(int16_t *)&raw[4]); } if (mTask.mag_selftest_num <= ST_MAG40_ST_NUM_OF_SAMPLES) { mTask.comm_rx(ST_MAG40_OUTXL_REG_ADDR, 6, 10000, true); break; } mTask.dataST[0] /= ST_MAG40_ST_NUM_OF_SAMPLES; mTask.dataST[1] /= ST_MAG40_ST_NUM_OF_SAMPLES; mTask.dataST[2] /= ST_MAG40_ST_NUM_OF_SAMPLES; mTask.mag_test_state = MAG_SELFTEST_VERIFY; /* fall through */ case MAG_SELFTEST_VERIFY: dataGap[0] = abs(mTask.dataST[0] - mTask.dataNOST[0]); dataGap[1] = abs(mTask.dataST[1] - mTask.dataNOST[1]); dataGap[2] = abs(mTask.dataST[2] - mTask.dataNOST[2]); if (dataGap[0] >= ST_MAG40_ST_MIN_THRESHOLD && dataGap[0] <= ST_MAG40_ST_MAX_THRESHOLD && dataGap[1] >= ST_MAG40_ST_MIN_THRESHOLD && dataGap[1] <= ST_MAG40_ST_MAX_THRESHOLD && dataGap[2] >= ST_MAG40_ST_MIN_THRESHOLD && dataGap[2] <= ST_MAG40_ST_MAX_THRESHOLD) sendTestResult(SENSOR_APP_EVT_STATUS_SUCCESS, SENS_TYPE_MAG); else sendTestResult(SENSOR_APP_EVT_STATUS_ERROR, SENS_TYPE_MAG); mTask.mag_test_state = MAG_SELFTEST_DONE; mTask.comm_tx(ST_MAG40_CFG_A_REG_ADDR, ST_MAG40_TEMP_COMP_EN | ST_MAG40_POWER_IDLE, 0, false); mTask.comm_tx(ST_MAG40_CFG_C_REG_ADDR, ST_MAG40_BDU_ON | ST_MAG40_INT_MAG, 0, true); break; case MAG_SELFTEST_DONE: break; } } static bool magSelfTest(void *cookie) { INFO_PRINT("magSelfTest\n"); if (!mTask.magOn && trySwitchState(SENSOR_SELF_TEST)) { mTask.mag_test_state = MAG_SELFTEST_INIT; magTestHandling(NULL); return true; } else { ERROR_PRINT("cannot test mag because sensor is busy\n"); sendTestResult(SENSOR_APP_EVT_STATUS_BUSY, SENS_TYPE_MAG); return false; } } #define DEC_OPS(power, firmware, rate, flush, test, cal, cfg) \ .sensorPower = power, \ .sensorFirmwareUpload = firmware, \ .sensorSetRate = rate, \ .sensorFlush = flush, \ .sensorCalibrate = cal, \ .sensorSelfTest = test, \ .sensorCfgData = cfg static const struct SensorOps st_mag40_SensorOps = { DEC_OPS(magPower, magFwUpload, magSetRate, magFlush, magSelfTest, NULL, magCfgData), }; static void enableInterrupt(struct Gpio *pin, struct ChainedIsr *isr) { gpioConfigInput(pin, GPIO_SPEED_LOW, GPIO_PULL_NONE); syscfgSetExtiPort(pin); extiEnableIntGpio(pin, EXTI_TRIGGER_RISING); extiChainIsr(ST_MAG40_INT_IRQ, isr); } static void disableInterrupt(struct Gpio *pin, struct ChainedIsr *isr) { extiUnchainIsr(ST_MAG40_INT_IRQ, isr); extiDisableIntGpio(pin); } static bool st_mag40_int1_isr(struct ChainedIsr *isr) { if (!extiIsPendingGpio(mTask.Int1)) return false; /* Start sampling for a value */ if (!osEnqueuePrivateEvt(EVT_SENSOR_INTERRUPT, NULL, NULL, mTask.tid)) ERROR_PRINT("st_mag40_int1_isr: osEnqueuePrivateEvt() failed\n"); extiClearPendingGpio(mTask.Int1); return true; } #define TIME_NS_TO_US(ns) cpuMathU64DivByU16(ns, 1000) #define kScale_mag 0.15f /* in uT - (1.5f / 10) */ static void parseRawData(uint8_t *raw) { struct TripleAxisDataEvent *magSample; int32_t raw_x = (*(int16_t *)&raw[0]); int32_t raw_y = (*(int16_t *)&raw[2]); int32_t raw_z = (*(int16_t *)&raw[4]); float x, y, z; float xs, ys, zs; bool newMagnCalibData; #if defined(ST_MAG40_CAL_ENABLED) float xi, yi, zi; #endif mTask.timestampInt = sensorGetTime(); /* Discard samples generated during sensor turn-on time */ if (mTask.samplesToDiscard > 0) { mTask.samplesToDiscard--; return; } /* in uT */ xs = (float)raw_x * kScale_mag; ys = (float)raw_y * kScale_mag; zs = (float)raw_z * kScale_mag; /* rotate axes */ x = ST_MAG40_REMAP_X_DATA(xs, ys, zs, ST_MAG40_ROT_MATRIX); y = ST_MAG40_REMAP_Y_DATA(xs, ys, zs, ST_MAG40_ROT_MATRIX); z = ST_MAG40_REMAP_Z_DATA(xs, ys, zs, ST_MAG40_ROT_MATRIX); #if defined(ST_MAG40_CAL_ENABLED) magCalRemoveSoftiron(&mTask.moc, x, y, z, &xi, &yi, &zi); newMagnCalibData = magCalUpdate(&mTask.moc, TIME_NS_TO_US(mTask.timestampInt), xi, yi, zi); magCalRemoveBias(&mTask.moc, xi, yi, zi, &x, &y, &z); #endif if (magAllocateEvt(&magSample) == false) return; magSample->referenceTime = mTask.timestampInt; magSample->samples[0].deltaTime = 0; magSample->samples[0].firstSample.numSamples = 1; magSample->samples[0].x = x; magSample->samples[0].y = y; magSample->samples[0].z = z; #if defined(ST_MAG40_CAL_ENABLED) if (newMagnCalibData) { magSample->samples[1].deltaTime = 0; magCalGetBias(&mTask.moc, &magSample->samples[1].x, &magSample->samples[1].y, &magSample->samples[1].z); magSample->referenceTime = mTask.timestampInt; magSample->samples[0].firstSample.numSamples = 2; magSample->samples[0].firstSample.biasCurrent = true; magSample->samples[0].firstSample.biasPresent = 1; magSample->samples[0].firstSample.biasSample = 1; } #endif osEnqueueEvtOrFree(sensorGetMyEventType(SENS_TYPE_MAG), magSample, magFreeEvt); } static uint8_t *wai; static void int2Evt(void) { if (trySwitchState(SENSOR_READ_SAMPLES)) { mTask.comm_rx(ST_MAG40_OUTXL_REG_ADDR, 6, 0, true); } else { mTask.pendingInt = true; } } static void processPendingEvt(void) { if (mTask.pendingInt) { mTask.pendingInt = false; int2Evt(); return; } if (mTask.pendingSubState != NO_SUBSTATE) { if (trySwitchState(SENSOR_MAG_CONFIGURATION)) { mTask.subState = mTask.pendingSubState; mTask.pendingSubState = NO_SUBSTATE; sensorMagConfig(); } } } static void sensorMagConfig(void) { uint8_t tmp; switch (mTask.subState) { case CONFIG_POWER_UP: mTask.subState = CONFIG_POWER_UP_2; mTask.comm_tx(ST_MAG40_CFG_B_REG_ADDR, ST_MAG40_OFF_CANC, 0, false); mTask.comm_tx(ST_MAG40_CFG_A_REG_ADDR, ST_MAG40_TEMP_COMP_EN | ST_MAG40_POWER_ON | mTask.currentODR, 0, true); break; case CONFIG_POWER_UP_2: mTask.subState = CONFIG_DONE; mTask.magOn = true; sensorSignalInternalEvt(mTask.magHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, true, 0); break; case CONFIG_POWER_DOWN: mTask.subState = CONFIG_POWER_DOWN_2; mTask.comm_tx(ST_MAG40_CFG_A_REG_ADDR, ST_MAG40_TEMP_COMP_EN | ST_MAG40_POWER_IDLE | mTask.currentODR, 0, true); break; case CONFIG_POWER_DOWN_2: mTask.subState = CONFIG_DONE; mTask.magOn = false; sensorSignalInternalEvt(mTask.magHandle, SENSOR_INTERNAL_EVT_POWER_STATE_CHG, false, 0); break; case CONFIG_SET_RATE: mTask.subState = CONFIG_SET_RATE_2; tmp = mTask.magOn ? ST_MAG40_POWER_ON : ST_MAG40_POWER_IDLE; tmp |= mTask.currentODR; mTask.comm_tx(ST_MAG40_CFG_A_REG_ADDR, ST_MAG40_TEMP_COMP_EN | tmp, 0, true); break; case CONFIG_SET_RATE_2: mTask.subState = CONFIG_DONE; sensorSignalInternalEvt(mTask.magHandle, SENSOR_INTERNAL_EVT_RATE_CHG, mTask.rate, mTask.latency); break; default: /* Something weird happened */ ERROR_PRINT("sensorMagConfig() subState=%d\n", mTask.subState); mTask.subState = CONFIG_DONE; break; } } /* initial sensor configuration */ static void sensorInit(void) { switch (mTask.subState) { case INIT_START: mTask.subState = INIT_ENABLE_DRDY; mTask.comm_tx(ST_MAG40_CFG_A_REG_ADDR, ST_MAG40_SOFT_RESET_BIT, 0, true); break; case INIT_ENABLE_DRDY: mTask.subState = INIT_DONE; mTask.comm_rx(ST_MAG40_OUTXL_REG_ADDR, 6, 0, false); mTask.comm_tx(ST_MAG40_CFG_C_REG_ADDR, ST_MAG40_BDU_ON | ST_MAG40_INT_MAG, 0, true); break; default: /* Something weird happened */ ERROR_PRINT("sensorInit() subState=%d\n", mTask.subState); mTask.subState = INIT_DONE; break; } } static void handleCommDoneEvt(const void* evtData) { bool returnIdle = false; struct I2cTransfer *xfer = (struct I2cTransfer *)evtData; switch (GET_STATE()) { case SENSOR_BOOT: SET_STATE(SENSOR_VERIFY_ID); mTask.comm_rx(ST_MAG40_WAI_REG_ADDR, 1, 0, true); break; case SENSOR_VERIFY_ID: /* Check the sensor ID */ wai = &xfer->txrxBuf[0]; if (ST_MAG40_WAI_REG_VAL != wai[0]) { DEBUG_PRINT("WAI returned is: %02x\n\n", *wai); SET_STATE(SENSOR_BOOT); mTask.comm_tx(ST_MAG40_CFG_A_REG_ADDR, ST_MAG40_SOFT_RESET_BIT, 0, true); break; } INFO_PRINT( "Device ID is correct! (%02x)\n", *wai); SET_STATE(SENSOR_INITIALIZATION); mTask.subState = INIT_START; sensorInit(); break; case SENSOR_INITIALIZATION: if (mTask.subState == INIT_DONE) { INFO_PRINT( "Initialization completed\n"); returnIdle = true; sensorRegisterInitComplete(mTask.magHandle); } else { sensorInit(); } break; case SENSOR_MAG_CONFIGURATION: if (mTask.subState != CONFIG_DONE) sensorMagConfig(); if (mTask.subState == CONFIG_DONE) returnIdle = true; break; case SENSOR_READ_SAMPLES: returnIdle = true; if (gpioGet(mTask.Int1)) { ERROR_PRINT("error read sensor, retry!\n"); } if (mTask.magOn) parseRawData(&xfer->txrxBuf[0]); break; case SENSOR_SELF_TEST: if (mTask.mag_test_state == MAG_SELFTEST_DONE) returnIdle = true; else magTestHandling(xfer); break; case SENSOR_IDLE: default: break; } releaseXfer(xfer); if (returnIdle) { SET_STATE(SENSOR_IDLE); processPendingEvt(); } } static void handleEvent(uint32_t evtType, const void* evtData) { switch (evtType) { case EVT_APP_START: INFO_PRINT("EVT_APP_START\n"); osEventUnsubscribe(mTask.tid, EVT_APP_START); SET_STATE(SENSOR_BOOT); mTask.comm_tx(ST_MAG40_CFG_A_REG_ADDR, ST_MAG40_SOFT_RESET_BIT, 0, true); break; case EVT_COMM_DONE: handleCommDoneEvt(evtData); break; case EVT_SENSOR_INTERRUPT: int2Evt(); break; default: break; } } static bool startTask(uint32_t task_id) { size_t slabSize; mTask.tid = task_id; INFO_PRINT("I2C DRIVER started\n"); mTask.magOn = false; mTask.pendingInt = false; mTask.pendingSubState = NO_SUBSTATE; mTask.currentODR = ST_MAG40_ODR_10_HZ; mTask.timestampInt = 0; slabSize = sizeof(struct TripleAxisDataEvent) + sizeof(struct TripleAxisDataPoint); #if defined(ST_MAG40_CAL_ENABLED) slabSize += sizeof(struct TripleAxisDataPoint); #endif mTask.magDataSlab = slabAllocatorNew(slabSize, 4, ST_MAG40_MAX_MAG_EVENTS); if (!mTask.magDataSlab) { ERROR_PRINT("Failed to allocate magDataSlab memory\n"); return false; } /* Init the communication part */ i2cMasterRequest(ST_MAG40_I2C_BUS_ID, ST_MAG40_I2C_SPEED); mTask.comm_tx = i2c_write; mTask.comm_rx = i2c_read; /* irq */ mTask.Int1 = gpioRequest(ST_MAG40_INT_PIN); gpioConfigInput(mTask.Int1, GPIO_SPEED_LOW, GPIO_PULL_NONE); mTask.Isr1.func = st_mag40_int1_isr; enableInterrupt(mTask.Int1, &mTask.Isr1); #if defined(ST_MAG40_CAL_ENABLED) #ifdef DIVERSITY_CHECK_ENABLED initMagCal(&mTask.moc, 0.0f, 0.0f, 0.0f, // bias x, y, z 1.0f, 0.0f, 0.0f, // c00, c01, c02 0.0f, 1.0f, 0.0f, // c10, c11, c12 0.0f, 0.0f, 1.0f, // c20, c21, c22 3000000, // min_batch_window_in_micros 8, // min_num_diverse_vectors 1, // max_num_max_distance 6.0f, // var_threshold 10.0f, // max_min_threshold 48.f, // local_field 0.5f, // threshold_tuning_param 2.552f); // max_distance_tuning_param #else initMagCal(&mTask.moc, 0.0f, 0.0f, 0.0f, // bias x, y, z 1.0f, 0.0f, 0.0f, // c00, c01, c02 0.0f, 1.0f, 0.0f, // c10, c11, c12 0.0f, 0.0f, 1.0f, // c20, c21, c22 3000000); // min_batch_window_in_micros #endif /* DIVERSITY_CHECK_ENABLED */ #endif /* ST_MAG40_CAL_ENABLED */ mTask.magHandle = sensorRegister(&st_mag40_SensorInfo, &st_mag40_SensorOps, NULL, false); osEventSubscribe(mTask.tid, EVT_APP_START); return true; } static void endTask(void) { INFO_PRINT("ended\n"); #if defined(ST_MAG40_CAL_ENABLED) magCalDestroy(&mTask.moc); #endif /* ST_MAG40_CAL_ENABLED */ slabAllocatorDestroy(mTask.magDataSlab); disableInterrupt(mTask.Int1, &mTask.Isr1); } INTERNAL_APP_INIT(ST_MAG40_APP_ID, 0, startTask, endTask, handleEvent);