223 lines
5.3 KiB
C++
223 lines
5.3 KiB
C++
#include "HalTiltSensor.h"
|
|
|
|
#include <Logging.h>
|
|
|
|
HalTiltSensor halTiltSensor;
|
|
|
|
bool HalTiltSensor::writeReg(uint8_t reg, uint8_t val) const {
|
|
Wire.beginTransmission(_i2cAddr);
|
|
Wire.write(reg);
|
|
Wire.write(val);
|
|
return Wire.endTransmission() == 0;
|
|
}
|
|
|
|
bool HalTiltSensor::readReg(uint8_t reg, uint8_t* val) const {
|
|
Wire.beginTransmission(_i2cAddr);
|
|
Wire.write(reg);
|
|
if (Wire.endTransmission(false) != 0) {
|
|
return false;
|
|
}
|
|
Wire.requestFrom(_i2cAddr, (uint8_t)1);
|
|
if (Wire.available() < 1) {
|
|
return false;
|
|
}
|
|
*val = Wire.read();
|
|
return true;
|
|
}
|
|
|
|
bool HalTiltSensor::readGyro(float& gx, float& gy, float& gz) const {
|
|
Wire.beginTransmission(_i2cAddr);
|
|
Wire.write(REG_GX_L);
|
|
if (Wire.endTransmission(false) != 0) {
|
|
return false;
|
|
}
|
|
|
|
Wire.requestFrom(_i2cAddr, (uint8_t)6);
|
|
if (Wire.available() < 6) {
|
|
return false;
|
|
}
|
|
|
|
auto readInt16 = [&]() -> int16_t {
|
|
const uint8_t lo = Wire.read();
|
|
const uint8_t hi = Wire.read();
|
|
return static_cast<int16_t>((hi << 8) | lo);
|
|
};
|
|
|
|
constexpr float SCALE = 1.0f / 64.0f; // ±512 dps full scale: 32768 / 512 = 64 LSB/dps
|
|
gx = readInt16() * SCALE;
|
|
gy = readInt16() * SCALE;
|
|
gz = readInt16() * SCALE;
|
|
return true;
|
|
}
|
|
|
|
void HalTiltSensor::begin() {
|
|
if (!gpio.deviceIsX3()) {
|
|
_available = false;
|
|
return;
|
|
}
|
|
|
|
uint8_t whoami = 0;
|
|
_i2cAddr = I2C_ADDR_QMI8658;
|
|
if (!readReg(QMI8658_WHO_AM_I_REG, &whoami) || whoami != QMI8658_WHO_AM_I_VALUE) {
|
|
_i2cAddr = I2C_ADDR_QMI8658_ALT;
|
|
if (!readReg(QMI8658_WHO_AM_I_REG, &whoami) || whoami != QMI8658_WHO_AM_I_VALUE) {
|
|
LOG_ERR("GYR", "QMI8658 IMU not found");
|
|
_available = false;
|
|
return;
|
|
}
|
|
}
|
|
|
|
LOG_INF("GYR", "QMI8658 IMU found at 0x%02X", _i2cAddr);
|
|
|
|
if (!writeReg(REG_CTRL7, CTRL7_DISABLE_ALL) || !writeReg(REG_CTRL3, CTRL3_FS_512DPS | CTRL3_ODR_28HZ) ||
|
|
!writeReg(REG_CTRL1, CTRL1_BASE | CTRL1_SENSOR_DISABLE)) {
|
|
LOG_ERR("GYR", "QMI8658 register configuration failed");
|
|
_available = false;
|
|
return;
|
|
}
|
|
|
|
_available = true;
|
|
_initMs = millis();
|
|
_lastPollMs = millis();
|
|
LOG_INF("GYR", "QMI8658 gyro initialized and put to sleep");
|
|
}
|
|
|
|
bool HalTiltSensor::wake() {
|
|
if (!_available) {
|
|
return false;
|
|
}
|
|
|
|
if ((millis() - _initMs) < SLEEP_STABILIZE_MS) {
|
|
return false;
|
|
}
|
|
|
|
if (writeReg(REG_CTRL1, CTRL1_BASE) && writeReg(REG_CTRL7, CTRL7_GYRO_ENABLE)) {
|
|
_lastPollMs = millis();
|
|
_lastTiltMs = millis();
|
|
_wakeMs = millis();
|
|
LOG_INF("GYR", "QMI8658 woke up");
|
|
return true;
|
|
} else {
|
|
LOG_ERR("GYR", "Failed to wake QMI8658");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool HalTiltSensor::deepSleep() {
|
|
if (!_available) {
|
|
return false;
|
|
}
|
|
|
|
if ((millis() - _wakeMs) < SLEEP_STABILIZE_MS) {
|
|
return false;
|
|
}
|
|
|
|
if (writeReg(REG_CTRL7, CTRL7_DISABLE_ALL) && writeReg(REG_CTRL1, CTRL1_BASE | CTRL1_SENSOR_DISABLE)) {
|
|
clearPendingEvents();
|
|
_inTilt = false;
|
|
LOG_INF("GYR", "QMI8658 entered sleep mode");
|
|
return true;
|
|
} else {
|
|
LOG_ERR("GYR", "Failed to put QMI8658 to sleep");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void HalTiltSensor::update(const uint8_t mode, const uint8_t orientation, const bool inReader) {
|
|
if (!_available) {
|
|
return;
|
|
}
|
|
|
|
if ((mode != CrossPointTiltPageTurn::TILT_OFF) && !_isAwake) {
|
|
_isAwake = wake();
|
|
return;
|
|
} else if ((mode == CrossPointTiltPageTurn::TILT_OFF) && _isAwake) {
|
|
_isAwake = !deepSleep();
|
|
return;
|
|
}
|
|
|
|
if ((mode == CrossPointTiltPageTurn::TILT_OFF) || !inReader) {
|
|
return;
|
|
}
|
|
|
|
const unsigned long now = millis();
|
|
if ((now - _wakeMs) < WAKE_STABILIZE_MS) {
|
|
return;
|
|
}
|
|
|
|
if ((now - _lastPollMs) < POLL_INTERVAL_MS) {
|
|
return;
|
|
}
|
|
_lastPollMs = now;
|
|
|
|
float gx, gy, gz;
|
|
if (!readGyro(gx, gy, gz)) {
|
|
return;
|
|
}
|
|
|
|
float tiltAxis;
|
|
switch (orientation) {
|
|
case CrossPointOrientation::PORTRAIT:
|
|
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? -gx : gx;
|
|
break;
|
|
case CrossPointOrientation::INVERTED:
|
|
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? gx : -gx;
|
|
break;
|
|
case CrossPointOrientation::LANDSCAPE_CW:
|
|
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? gy : -gy;
|
|
break;
|
|
case CrossPointOrientation::LANDSCAPE_CCW:
|
|
tiltAxis = mode == CrossPointTiltPageTurn::TILT_INVERTED ? -gy : gy;
|
|
break;
|
|
default:
|
|
tiltAxis = gx;
|
|
break;
|
|
}
|
|
|
|
if (_inTilt) {
|
|
if (fabsf(tiltAxis) < NEUTRAL_RATE_DPS) {
|
|
_inTilt = false;
|
|
}
|
|
} else {
|
|
if ((now - _lastTiltMs) >= COOLDOWN_MS) {
|
|
if (tiltAxis > RATE_THRESHOLD_DPS) {
|
|
_tiltForwardEvent = true;
|
|
_hadActivity = true;
|
|
_inTilt = true;
|
|
_lastTiltMs = now;
|
|
LOG_INF("GYR", "Forward Trigger=(%.1f) dps", tiltAxis);
|
|
} else if (tiltAxis < -RATE_THRESHOLD_DPS) {
|
|
_tiltBackEvent = true;
|
|
_hadActivity = true;
|
|
_inTilt = true;
|
|
_lastTiltMs = now;
|
|
LOG_INF("GYR", "Backward Trigger=(%.1f) dps", tiltAxis);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool HalTiltSensor::wasTiltedForward() {
|
|
const bool val = _tiltForwardEvent;
|
|
_tiltForwardEvent = false;
|
|
return val;
|
|
}
|
|
|
|
bool HalTiltSensor::wasTiltedBack() {
|
|
const bool val = _tiltBackEvent;
|
|
_tiltBackEvent = false;
|
|
return val;
|
|
}
|
|
|
|
bool HalTiltSensor::hadActivity() {
|
|
const bool val = _hadActivity;
|
|
_hadActivity = false;
|
|
return val;
|
|
}
|
|
|
|
void HalTiltSensor::clearPendingEvents() {
|
|
_tiltForwardEvent = false;
|
|
_tiltBackEvent = false;
|
|
_hadActivity = false;
|
|
}
|