SensorLib 0.5.0
Multi-platform sensor driver library for Arduino, PlatformIO, and ESP-IDF
Loading...
Searching...
No Matches
sensor/magnetometer/qmc/SensorQMC6309.hpp
Go to the documentation of this file.
1
30#pragma once
31
32#include "../../../SensorBuildOpt.h"
33#if !SENSORLIB_EXCLUDE_QMC6309
34
37
38static constexpr uint8_t QMC6309_SLAVE_ADDRESS = (0x7C);
39static constexpr uint8_t QMC6309H_SLAVE_ADDRESS = (0x0C);
40
42{
43public:
44
50
51 enum class VoltageMode {
52 KEEP,
55 };
56
63 SensorQMC6309() = default;
64
71 ~SensorQMC6309() = default;
72
81 bool readData(MagnetometerData &data) override
82 {
83 uint8_t buffer[6] = {0};
84
85 int status = readReg(REG_0x09_STAT);
86 if (status < 0) {
87 SENSORLIB_LOG_E("Failed to read status register");
88 return false;
89 }
90
91 // OVL (Overflow)
92 if (sensorlib::_isBitSet(status, 1)) {
93 data.overflow = true;
94 SENSORLIB_LOG_W("Data overflow detected");
95 } else {
96 data.overflow = false;
97 }
98
99 // DRDY (Data Ready)
100 if (!sensorlib::_isBitSet(status, 0)) {
101 // SENSORLIB_LOG_E("Data not ready");
102 return false;
103 }
104
105 if (readRegBuff(REG_0x01_LSB_DX, buffer, sizeof(buffer)) < 0) {
106 SENSORLIB_LOG_E("Failed to read magnetic field data");
107 return false;
108 }
109 data.raw.x = (int16_t)(buffer[1] << 8) | (buffer[0]); // Combine X LSB and MSB
110 data.raw.y = (int16_t)(buffer[3] << 8) | (buffer[2]); // Combine Y LSB and MSB
111 data.raw.z = (int16_t)(buffer[5] << 8) | (buffer[4]); // Combine Z LSB and MSB
112
113 data.raw.x -= _x_offset;
114 data.raw.y -= _y_offset;
115 data.raw.z -= _z_offset;
116
117 // Convert raw values to Gauss using sensitivity (depends on selected magnetic range)
118 data.magnetic_field.x = (float)(data.raw.x) * _sensitivity;
119 data.magnetic_field.y = (float)(data.raw.y) * _sensitivity;
120 data.magnetic_field.z = (float)(data.raw.z) * _sensitivity;
121
122 // Calculate heading
124
125 // Convert heading to degrees
126 data.heading_degrees = data.heading * (180.0 / M_PI);
127
128 return true;
129 }
130
137 bool isDataReady() override
138 {
139 return getRegBit(REG_0x09_STAT, 0);
140 }
141
149 {
150 return getRegBit(REG_0x09_STAT, 1);
151 }
152
160 {
161 return getRegBit(REG_0x09_STAT, 3);
162 }
163
171 {
172 return getRegBit(REG_0x09_STAT, 4);
173 }
174
181 bool reset() override
182 {
183 if (writeReg(REG_0x0B_CMD2, 0x80) < 0) {
184 SENSORLIB_LOG_E("Failed to set soft reset");
185 return false;
186 }
187 if (writeReg(REG_0x0B_CMD2, 0x00) < 0) {
188 SENSORLIB_LOG_E("Failed to clear soft reset");
189 return false;
190 }
191
192 hal->delay(5);
193
194 uint8_t retry = 0;
195 while (retry++ < 5) {
196 int status = readReg(REG_0x09_STAT);
197 if (status >= 0 && sensorlib::_isBitSet(status, 4) && sensorlib::_isBitSet(status, 3)) {
198 return true;
199 }
200 hal->delay(1);
201 }
202
203 SENSORLIB_LOG_E("NVM not ready after soft reset");
204 return false;
205 }
206
213 bool selfTest() override
214 {
215 int16_t x_result = 0, y_result = 0, z_result = 0;
216 return selfTest(x_result, y_result, z_result);
217 }
218
226 bool selfTest(int16_t &x_result, int16_t &y_result, int16_t &z_result)
227 {
229 SENSORLIB_LOG_E("Failed to set SUSPEND mode for selfTest");
230 return false;
231 }
232
234 SENSORLIB_LOG_E("Failed to set CONTINUOUS_MEASUREMENT for selfTest");
235 return false;
236 }
237
238 hal->delay(20);
239
240 if (writeReg(REG_0x0E_SELFTEST_CTRL, MASK_SOFT_RST) < 0) {
241 SENSORLIB_LOG_E("Failed to trigger self-test");
242 return false;
243 }
244
245 uint8_t retry = 0;
246 while (!getRegBit(REG_0x09_STAT, 2) && retry++ < 50) {
247 hal->delay(5);
248 }
249 if (retry > 50) {
250 SENSORLIB_LOG_E("Self-test not ready (ST_RDY bit is 0)");
251 return false;
252 }
253
254 int x_test = readReg(REG_0x13_SELFTEST_X);
255 int y_test = readReg(REG_0x14_SELFTEST_Y);
256 int z_test = readReg(REG_0x15_SELFTEST_Z);
257 if (x_test < 0 || y_test < 0 || z_test < 0) {
258 SENSORLIB_LOG_E("Failed to read self-test result");
259 return false;
260 }
261
262 x_result = (int8_t)x_test;
263 y_result = (int8_t)y_test;
264 z_result = (int8_t)z_test;
265
267
268 int16_t x_abs = x_result < 0 ? -x_result : x_result;
269 int16_t y_abs = y_result < 0 ? -y_result : y_result;
270 int16_t z_abs = z_result < 0 ? -z_result : z_result;
271
272 bool x_ok = (x_abs >= 1 && x_abs <= 50);
273 bool y_ok = (y_abs >= 1 && y_abs <= 50);
274 bool z_ok = (z_abs >= 1 && z_abs <= 50);
275
276 if (!x_ok || !y_ok || !z_ok) {
277 SENSORLIB_LOG_W("Self-test data out of range: X=%d, Y=%d, Z=%d (expected abs in 1..50)",
278 x_result, y_result, z_result);
279 }
280
281 return x_ok && y_ok && z_ok;
282 }
283
293 {
294 float full_scale = 0.0f;
295 float sensitivity = 0.0f;
296 uint8_t range_value = 0;
297 switch (range) {
299 sensitivity = 0.001f; // 1000 LSB/Gauss
300 range_value = 0x00 << 2;
301 full_scale = 32.0f;
302 break;
304 sensitivity = 0.0005f; // 2000 LSB/Gauss
305 range_value = 0x01 << 2;
306 full_scale = 16.0f;
307 break;
309 sensitivity = 0.00025f; // 4000 LSB/Gauss
310 range_value = 0x02 << 2;
311 full_scale = 8.0f;
312 break;
313 default:
314 SENSORLIB_LOG_E("Invalid magnetometer range.");
315 return false;
316 }
317
318 if (updateBits(REG_0x0B_CMD2, 0x0C, range_value) < 0) {
319 SENSORLIB_LOG_E("Failed to set full scale range.");
320 return false;
321 }
322 _sensitivity = sensitivity;
323 _config.range = full_scale;
324 return true;
325 }
326
333 bool setOutputDataRate(float data_rate_hz) override
334 {
335 int rangeInt = static_cast<int>(data_rate_hz * 100 + 0.5);
336 uint8_t regValue = 0;
337 switch (rangeInt) {
338 case 100: // 1.0
339 regValue = 0x00 << 4;
340 break;
341 case 1000: // 10.0
342 regValue = 0x01 << 4;
343 break;
344 case 5000: // 50.0
345 regValue = 0x02 << 4;
346 break;
347 case 10000: // 100.0
348 regValue = 0x03 << 4;
349 break;
350 case 20000: // 200.0
351 regValue = 0x04 << 4;
352 break;
353 default:
354 SENSORLIB_LOG_E("Invalid output data rate");
355 return false;
356 }
357 if (updateBits(REG_0x0A_CMD1, 0x70, regValue) < 0) {
358 SENSORLIB_LOG_E("Failed to set bandwidth");
359 return false;
360 }
361 _config.sample_rate = data_rate_hz;
362 return true;
363 }
364
372 bool setOperationMode(OperationMode mode) override
373 {
374 uint8_t mode_val = 0x00;
375 switch (mode) {
377 mode_val = 0x00;
378 break;
380 mode_val = 0x01;
381 break;
383 mode_val = 0x02;
384 break;
386 mode_val = 0x03;
387 break;
388 default:
389 SENSORLIB_LOG_E("Invalid operation mode");
390 return false;
391 }
392 if (updateBits(REG_0x0A_CMD1, 0x03, mode_val) < 0) {
393 SENSORLIB_LOG_E("Failed to set operation mode");
394 return false;
395 }
396 _config.mode = mode;
397 return true;
398 }
399
408 {
409 uint8_t osr_val = 0x00;
410 switch (osr) {
412 osr_val = 0x00 << 3;
414 break;
416 osr_val = 0x01 << 3;
418 break;
420 osr_val = 0x02 << 3;
422 break;
424 osr_val = 0x03 << 3;
426 break;
427 default:
428 SENSORLIB_LOG_E("Invalid oversampling rate");
429 return false;
430 }
431 return updateBits(REG_0x0A_CMD1, 0x18, osr_val) == 0;
432 }
433
439 {
440 SENSORLIB_LOG_E("QMC6309 does not support downsampling rate setting");
441 return false;
442 }
443
452 {
453 uint8_t lpf_val = 0x00;
454 switch (lpf) {
456 lpf_val = 0x00 << 5;
457 break;
459 lpf_val = 0x01 << 5;
460 break;
462 lpf_val = 0x02 << 5;
463 break;
465 lpf_val = 0x03 << 5;
466 break;
468 lpf_val = 0x04 << 5;
469 break;
470 default:
471 SENSORLIB_LOG_E("Invalid low-pass filter");
472 return false;
473 }
474 return updateBits(REG_0x0A_CMD1, 0xE0, lpf_val) == 0;
475 }
476
482 uint8_t getChipID() const
483 {
484 return _info.uid;
485 }
486
497 bool configMagnetometer(OperationMode mode, MagFullScaleRange range, float data_rate_hz,
499 {
500 if (!setOperationMode(mode)) {
501 return false;
502 }
503 if (!setFullScaleRange(range)) {
504 return false;
505 }
506 if (!setOutputDataRate(data_rate_hz)) {
507 return false;
508 }
509 if (!setOversamplingRate(osr)) {
510 return false;
511 }
512 return true;
513 }
514
526 {
527 uint8_t sr_val = 0x00;
528 switch (mode) {
530 sr_val = 0x00;
531 break;
533 sr_val = 0x01;
534 break;
536 sr_val = 0x03;
537 break;
538 default:
539 SENSORLIB_LOG_E("Invalid set/reset mode");
540 return false;
541 }
542 return updateBits(REG_0x0B_CMD2, 0x03, sr_val) == 0;
543 }
544
552 {
553 _qmc6309h_vdd_mode = mode;
554 }
555
556private:
557
558 static constexpr uint8_t QMC6309_CHIP_ID = 0x90;
559 static constexpr uint8_t QMC6309H_CHIP_ID = 0xA5;
560 static constexpr uint8_t REG_0x00_CHIP_ID = 0x00;
561 static constexpr uint8_t REG_0x01_LSB_DX = 0x01;
562 static constexpr uint8_t REG_0x02_MSB_DX = 0x02;
563 static constexpr uint8_t REG_0x03_LSB_DY = 0x03;
564 static constexpr uint8_t REG_0x04_MSB_DY = 0x04;
565 static constexpr uint8_t REG_0x05_LSB_DZ = 0x05;
566 static constexpr uint8_t REG_0x06_MSB_DZ = 0x06;
567 static constexpr uint8_t REG_0x09_STAT = 0x09;
568 static constexpr uint8_t REG_0x0A_CMD1 = 0x0A;
569 static constexpr uint8_t REG_0x0B_CMD2 = 0x0B;
570 static constexpr uint8_t REG_0x40_IO_CONFIG = 0x40;
571 static constexpr uint8_t REG_0x28_OTP_CTRL = 0x28;
572 static constexpr uint8_t REG_0x29_SIGN = 0x29;
573
574 static constexpr uint8_t REG_0x0E_SELFTEST_CTRL = 0x0E; // Self-test control register
575 static constexpr uint8_t REG_0x13_SELFTEST_X = 0x13; // Self-test X-axis data register
576 static constexpr uint8_t REG_0x14_SELFTEST_Y = 0x14; // Self-test Y-axis data register
577 static constexpr uint8_t REG_0x15_SELFTEST_Z = 0x15; // Self-test Z-axis data register
578
579 static constexpr uint8_t MASK_DRDY = 0x01;
580 static constexpr uint8_t MASK_OVFL = 0x02;
581 static constexpr uint8_t MASK_ST_RDY = 0x04;
582 static constexpr uint8_t MASK_NVM_RDY = 0x08;
583 static constexpr uint8_t MASK_NVM_LOAD_DONE = 0x10;
584 static constexpr uint8_t MASK_SOFT_RST = 0x80;
585 static constexpr uint8_t MASK_ODR = 0x70;
586 static constexpr uint8_t MASK_RNG = 0x0C;
587 static constexpr uint8_t MASK_SET_RESET_MODE = 0x03;
588
589 bool _is_qmc6309h = false;
590 VoltageMode _qmc6309h_vdd_mode = VoltageMode::KEEP;
591
592 bool applyQMC6309HVoltageMode()
593 {
594 if (!_is_qmc6309h || _qmc6309h_vdd_mode == VoltageMode::KEEP) {
595 return true;
596 }
597
598 int reg = readReg(REG_0x40_IO_CONFIG);
599 if (reg < 0) {
600 SENSORLIB_LOG_E("Failed to read QMC6309H IO config");
601 return false;
602 }
603
604 uint8_t value = (uint8_t)reg;
605 switch (_qmc6309h_vdd_mode) {
607 value = (value & 0x3F) | 0x80;
608 break;
610 value = (value & 0x3F) | 0x40;
611 break;
613 return true;
614 }
615
616 if (writeReg(REG_0x40_IO_CONFIG, value) < 0) {
617 SENSORLIB_LOG_E("Failed to write QMC6309H IO config");
618 return false;
619 }
620 hal->delay(1);
621 return true;
622 }
623
624 bool reloadOTP()
625 {
626 for (uint8_t retry = 0; retry < 20; ++retry) {
627 if (writeReg(REG_0x28_OTP_CTRL, 0x02) < 0) {
628 hal->delay(2);
629 continue;
630 }
631
632 hal->delay(2);
633
634 for (uint8_t count = 0; count < 100; ++count) {
635 int status = readReg(REG_0x09_STAT);
636 if (status >= 0 && sensorlib::_isBitSet(status, 4)) {
637 return true;
638 }
639 hal->delay(1);
640 }
641 }
642
643 SENSORLIB_LOG_E("Failed to reload OTP");
644 return false;
645 }
646
647 bool initImpl(uint8_t param) override
648 {
649 (void)param;
650
651 if (!reset()) {
652 return false;
653 }
654
655 if (!reloadOTP()) {
656 return false;
657 }
658
659 _info.uid = readReg(REG_0x00_CHIP_ID);
660 _info.manufacturer = "QSTMagnetic";
663 _info.version = 1; // Set a default version
664
665 if (_info.uid == QMC6309_CHIP_ID) {
666 _is_qmc6309h = false;
667 _info.model = "QMC6309";
668 } else if (_info.uid == QMC6309H_CHIP_ID) {
669 _is_qmc6309h = true;
670 _info.model = "QMC6309H";
671 } else {
672 SENSORLIB_LOG_E("Unsupported chip ID");
673 return false;
674 }
675
676 if (!applyQMC6309HVoltageMode()) {
677 return false;
678 }
679
680 // Follow vendor init defaults: HPFM + 32G + set/reset on + OSR1=8 + LPF4.
682 return false;
683 }
685 return false;
686 }
687 if (!setOutputDataRate(1.0f)) {
688 return false;
689 }
691 return false;
692 }
694 return false;
695 }
697 return false;
698 }
699
701 _config.range = 32.0f;
702 _config.sample_rate = 1.0f;
703 _config.latency = 0;
705
706 return true;
707 }
708};
709
710#endif
@license MIT License
@license MIT License
@ MAGNETOMETER
Magnetometer sensor.
OperationMode
Enumeration of sensor operation modes.
MagLowPassFilter
@ LPF_2
2 Hz low-pass filter
@ LPF_4
4 Hz low-pass filter
@ LPF_16
16 Hz low-pass filter
@ LPF_8
8 Hz low-pass filter
@ LPF_1
1 Hz low-pass filter
MagFullScaleRange
Enumeration defining full-scale range settings for the sensor.
MagDownSampleRatio
Enumeration defining down-sample ratios for the sensor.
@ DSR_1
1x down-sample ratio
#define M_PI
MagOverSampleRatio
Enumeration defining over-sample ratios for the sensor.
@ OSR_8
8x over-sample ratio
@ OSR_4
4x over-sample ratio
@ OSR_2
2x over-sample ratio
@ OSR_1
1x over-sample ratio
#define SENSORLIB_LOG_W(...)
#define SENSORLIB_LOG_E(...)
std::unique_ptr< SensorHal > hal
SensorConfig _config
Current configuration.
SensorInfo _info
Sensor information.
bool getRegBit(uint8_t reg, uint8_t bit)
int readReg(uint8_t reg) const
int writeReg(uint8_t reg, uint8_t val)
int readRegBuff(uint8_t reg, uint8_t *buf, size_t len) const
int updateBits(uint8_t reg, uint8_t mask, uint8_t value_shifted)
bool reset() override
Resets the sensor to its default state.
bool configMagnetometer(OperationMode mode, MagFullScaleRange range, float data_rate_hz, MagOverSampleRatio osr, MagDownSampleRatio dsr=MagDownSampleRatio::DSR_1) override
Configures the magnetometer with multiple parameters.
bool isNVMReady()
Checks if the NVM is ready for access.
bool setOperationMode(OperationMode mode) override
Sets the operation mode of the magnetometer.
~SensorQMC6309()=default
Destructor.
bool isDataReady() override
Checks if new data is available from the sensor.
bool setOutputDataRate(float data_rate_hz) override
Sets the output data rate for the magnetometer.
void setVoltageMode(VoltageMode mode)
Set the voltage mode for QMC6309H variant.
SensorQMC6309()=default
Default constructor.
bool readData(MagnetometerData &data) override
Reads the magnetic field data from the sensor.
uint8_t getChipID() const
Gets the chip ID of the magnetometer.
bool setOversamplingRate(MagOverSampleRatio osr) override
Sets the oversampling rate of the magnetometer.
bool setLowPassFilter(MagLowPassFilter lpf)
Sets the low-pass filter of the magnetometer.
bool isDataOverflow()
Checks if data overflow has occurred.
bool setSetResetMode(MagSetResetMode mode)
Sets the set/reset mode of the magnetometer.
bool setFullScaleRange(MagFullScaleRange range) override
Sets the full-scale range of the magnetometer.
bool isNVMLoadDone()
Checks if the NVM load is complete.
bool setDownsamplingRate(MagDownSampleRatio dsr) override
Sets the downsampling rate of the magnetometer.
bool selfTest() override
Checks if the sensor is functioning correctly.
bool selfTest(int16_t &x_result, int16_t &y_result, int16_t &z_result)
Performs a self-test on the sensor.
float calculateHeading(const MagnetometerData &data, float declination=0.0f)
Calculate heading (yaw) angle from magnetometer data in radians.
constexpr bool _isBitSet(T value, uint8_t bit)
Definition SensorLib.h:112
Structure representing magnetometer data.
RawVector raw
raw data
float heading_degrees
heading angle (degrees)
float heading
heading angle (radians)
SensorVector magnetic_field
magnetic field strength (Gauss)
bool overflow
data overflow flag
int16_t x
X-axis raw value.
int16_t y
Y-axis raw value.
int16_t z
Z-axis raw value.
uint32_t latency
Reporting latency in milliseconds.
float sample_rate
Sample rate in Hz.
float range
Measurement range.
SensorType type
Type of the sensor.
OperationMode mode
Operation mode.
uint8_t version
Hardware/firmware version.
uint8_t i2c_address
Default I2C address.
uint32_t uid
Unique identifier.
SensorType type
Sensor type.
const char * manufacturer
Manufacturer name.
const char * model
Model name/identifier.
float x
X-axis component.
float y
Y-axis component.
float z
Z-axis component.