Fix T1000-E QMA6100P I2C probing (#10713)

* Fix T1000-E QMA6100P I2C probing

* Refactored to make generic

* address copilot comments

* fix(i2c): address copilot comments on QMA6100P scanning and hardware init

- ScanI2CTwoWire: gate bounded QMA6100P probing to addresses 0x12/0x13 only
  (Copilot comment: avoid blocking other I2C device detection on nRF52)
  Falls back to normal Wire probing for all other addresses, allowing BMM150
  and other devices at overlapping addresses to be properly detected.

- Nrf52Twim: suppress cppcheck redundantAssignment on ENABLE register write
  (intentional disable→configure→enable pattern for hardware safety; explicit
  disable ensures known state before configuration even if already disabled)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>

* Nrf52Twim: gate on HAS_QMA6100P; suppress cppcheck redundantAssignment

The TWIM helper is only used by the QMA6100P probing path (T1000-E, the sole
board defining HAS_QMA6100P). It was gated only on ARCH_NRF52, so it compiled
as dead code on every other nRF52 board and was analyzed by the rak4631
cppcheck job -- which failed on a redundantAssignment false positive.

Gate the header and source on HAS_QMA6100P so the file is only built/analyzed
where it is actually used. Also keep an inline suppression on the ENABLE
re-assignment (a required volatile disable->reconfigure->enable sequence) for
the case where the T1000-E build is run through cppcheck locally.

Fixes the rak4631 cppcheck CI failure for PR #10713.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
Co-authored-by: nomdetom <nomdetom@protonmail.com>
Co-authored-by: Tom <116762865+NomDeTom@users.noreply.github.com>
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Benjamin Faershtein
2026-06-30 14:52:06 -05:00
committed by GitHub
co-authored by Claude Opus 4.8 Ben Meadors nomdetom Tom
parent 2fdb722e00
commit 0eaad08735
4 changed files with 341 additions and 1 deletions
+100
View File
@@ -2,9 +2,83 @@
#if !defined(ARCH_STM32WL) && !MESHTASTIC_EXCLUDE_I2C && defined(HAS_QMA6100P)
#ifdef ARCH_NRF52
#include "platform/nrf52/Nrf52Twim.h"
#endif
// Flag when an interrupt has been detected
volatile static bool QMA6100P_IRQ = false;
#ifdef ARCH_NRF52
namespace
{
bool qma6100pUpdateRegister(uint8_t address, uint8_t reg, uint8_t (*mutate)(uint8_t))
{
uint8_t value = 0;
return Nrf52Twim::readRegister(address, reg, value) && Nrf52Twim::writeRegister(address, reg, mutate(value));
}
uint8_t qma6100pSetRange(uint8_t value)
{
sfe_qma6100p_fsr_bitfield_t fsr;
fsr.all = value;
fsr.bits.range = QMA_6100P_MPU_ACCEL_SCALE;
return fsr.all;
}
uint8_t qma6100pEnableAccel(uint8_t value)
{
sfe_qma6100p_pm_bitfield_t pm;
pm.all = value;
pm.bits.mode_bit = 1;
return pm.all;
}
uint8_t qma6100pConfigureIntPin(uint8_t value)
{
return value | 0b00000010;
}
uint8_t qma6100pConfigureIntLatch(uint8_t value)
{
return value | 0b10000001;
}
uint8_t qma6100pMapAnyMotionToInt1(uint8_t value)
{
sfe_qma6100p_int_map1_bitfield_t intMap1;
intMap1.all = value;
intMap1.bits.int1_any_mot = 1;
return intMap1.all;
}
bool qma6100pInitBounded(uint8_t address)
{
// SFE_QMA6100P_SR: write 0xB6 to trigger soft-reset, then 0x00 to release
constexpr uint8_t QMA6100P_SOFT_RESET = 0xb6;
constexpr uint8_t QMA6100P_SOFT_RESET_RELEASE = 0x00;
// SFE_QMA6100P_INT_EN2: bits[2:0] = any-motion enable for Z, Y, X axes
constexpr uint8_t QMA6100P_INT_EN2_ANY_MOT_XYZ = 0b00000111;
uint8_t chipID = 0;
Nrf52Twim::restoreBus();
const bool ok = Nrf52Twim::readRegister(address, SFE_QMA6100P_CHIP_ID, chipID) && chipID == QMA6100P_CHIP_ID &&
Nrf52Twim::writeRegister(address, SFE_QMA6100P_SR, QMA6100P_SOFT_RESET) &&
Nrf52Twim::writeRegister(address, SFE_QMA6100P_SR, QMA6100P_SOFT_RESET_RELEASE) &&
qma6100pUpdateRegister(address, SFE_QMA6100P_FSR, qma6100pSetRange) &&
qma6100pUpdateRegister(address, SFE_QMA6100P_PM, qma6100pEnableAccel) &&
Nrf52Twim::writeRegister(address, SFE_QMA6100P_INT_EN2, QMA6100P_INT_EN2_ANY_MOT_XYZ) &&
qma6100pUpdateRegister(address, SFE_QMA6100P_INT_MAP1, qma6100pMapAnyMotionToInt1) &&
qma6100pUpdateRegister(address, SFE_QMA6100P_INTPINT_CONF, qma6100pConfigureIntPin) &&
qma6100pUpdateRegister(address, SFE_QMA6100P_INT_CFG, qma6100pConfigureIntLatch);
Nrf52Twim::restoreBus();
return ok;
}
} // namespace
#endif
// Interrupt service routine
void QMA6100PSetInterrupt()
{
@@ -15,6 +89,20 @@ QMA6100PSensor::QMA6100PSensor(ScanI2C::FoundDevice foundDevice) : MotionSensor:
bool QMA6100PSensor::init()
{
#ifdef ARCH_NRF52
if (!qma6100pInitBounded(device.address.address)) {
LOG_WARN("QMA6100P init failed");
return false;
}
QMA6100P_IRQ = false;
#ifdef QMA_6100P_INT_PIN
pinMode(QMA_6100P_INT_PIN, INPUT_PULLUP);
attachInterrupt(QMA_6100P_INT_PIN, QMA6100PSetInterrupt, FALLING);
#endif
return true;
#else
// Initialise the sensor
sensor = QMA6100PSingleton::GetInstance();
if (!sensor->init(device))
@@ -22,6 +110,7 @@ bool QMA6100PSensor::init()
// Enable simple Wake on Motion
return sensor->setWakeOnMotion();
#endif
}
#ifdef QMA_6100P_INT_PIN
@@ -40,6 +129,16 @@ int32_t QMA6100PSensor::runOnce()
int32_t QMA6100PSensor::runOnce()
{
#ifdef ARCH_NRF52
// restoreBus() is intentionally omitted here: it tears down and re-initialises Wire,
// which would be destructive on every poll. The bus is stable after init(); a failed
// read is handled gracefully below.
uint8_t tempVal = 0;
if (!Nrf52Twim::readRegister(device.address.address, SFE_QMA6100P_INT_ST0, tempVal)) {
LOG_DEBUG("QMA6100PS isWakeOnMotion failed to read interrupts");
return MOTION_SENSOR_CHECK_INTERVAL_MS;
}
#else
// Wake on motion using polling - this is not as efficient as using hardware interrupt pin (see above)
uint8_t tempVal;
@@ -47,6 +146,7 @@ int32_t QMA6100PSensor::runOnce()
LOG_DEBUG("QMA6100PS isWakeOnMotion failed to read interrupts");
return MOTION_SENSOR_CHECK_INTERVAL_MS;
}
#endif
if ((tempVal & 7) != 0) {
// Wake up!