Files
Crosspoint/lib/hal/HalTiltSensor.cpp
T
2026-05-13 21:23:32 +02:00

226 lines
5.5 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();
_sleepMs = millis();
_lastPollMs = millis();
LOG_INF("GYR", "QMI8658 gyro initialized and put to sleep");
}
bool HalTiltSensor::wake() {
if (!_available) {
return false;
}
if ((millis() - _sleepMs) < 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;
_sleepMs = millis();
LOG_INF("GYR", "QMI8658 entered sleep mode");
return true;
} else {
LOG_ERR("GYR", "Failed to put QMI8658 to sleep");
return false;
}
}
void HalTiltSensor::update(CrossPointTiltPageTurn::Value mode, CrossPointOrientation::Value orientation,
bool inReader) {
if (!_available) {
return;
}
if ((mode != CrossPointTiltPageTurn::TILT_OFF && inReader) && !_isAwake) {
_isAwake = wake();
return;
} else if ((mode == CrossPointTiltPageTurn::TILT_OFF || !inReader) && _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;
}