| /* |
| * 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 <atomic.h> |
| #include <gpio.h> |
| #include <isr.h> |
| #include <nanohubPacket.h> |
| #include <plat/exti.h> |
| #include <plat/gpio.h> |
| #include <platform.h> |
| #include <plat/syscfg.h> |
| #include <plat/rtc.h> |
| #include <sensors.h> |
| #include <seos.h> |
| #include <halIntf.h> |
| #include <slab.h> |
| #include <heap.h> |
| #include <i2c.h> |
| #include <timer.h> |
| #include <variant/sensType.h> |
| #include <cpu/cpuMath.h> |
| #include <calibration/magnetometer/mag_cal.h> |
| #include <floatRt.h> |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <variant/variant.h> |
| |
| #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); |