Skip to content

Commit 7f30961

Browse files
mbaitsalkinium
authored andcommitted
[driver] Implement QMC5883L driver
1 parent 93af956 commit 7f30961

File tree

5 files changed

+315
-4
lines changed

5 files changed

+315
-4
lines changed

Diff for: README.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -789,30 +789,31 @@ your specific needs.
789789
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9535">PCA9535</a></td>
790790
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9548a">PCA9548A</a></td>
791791
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9685">PCA9685</a></td>
792+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-qmc5883l">QMC5883L</a></td>
792793
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sh1106">SH1106</a></td>
793-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s65">SIEMENS-S65</a></td>
794794
</tr><tr>
795+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s65">SIEMENS-S65</a></td>
795796
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s75">SIEMENS-S75</a></td>
796797
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk6812">SK6812</a></td>
797798
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk9822">SK9822</a></td>
798799
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ssd1306">SSD1306</a></td>
799800
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7586s">ST7586S</a></td>
800-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7789">ST7789</a></td>
801801
</tr><tr>
802+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7789">ST7789</a></td>
802803
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stts22h">STTS22H</a></td>
803804
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stusb4500">STUSB4500</a></td>
804805
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx1276">SX1276</a></td>
805806
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx128x">SX128X</a></td>
806807
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3414">TCS3414</a></td>
807-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3472">TCS3472</a></td>
808808
</tr><tr>
809+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3472">TCS3472</a></td>
809810
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tlc594x">TLC594x</a></td>
810811
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp102">TMP102</a></td>
811812
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp12x">TMP12x</a></td>
812813
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp175">TMP175</a></td>
813814
<td align="center"><a href="https://modm.io/reference/module/modm-driver-touch2046">TOUCH2046</a></td>
814-
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl53l0">VL53L0</a></td>
815815
</tr><tr>
816+
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl53l0">VL53L0</a></td>
816817
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl6180">VL6180</a></td>
817818
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ws2812">WS2812</a></td>
818819
</tr>

Diff for: examples/avr/qmc5883l/main.cpp

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (c) 2023, Alexander Solovets
3+
*
4+
* This file is part of the modm project.
5+
*
6+
* This Source Code Form is subject to the terms of the Mozilla Public
7+
* License, v. 2.0. If a copy of the MPL was not distributed with this
8+
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
9+
*/
10+
// ----------------------------------------------------------------------------
11+
12+
#include <modm/architecture/driver/atomic/flag.hpp>
13+
#include <modm/board.hpp>
14+
#include <modm/debug/logger.hpp>
15+
#include <modm/driver/inertial/qmc5883l.hpp>
16+
17+
using namespace Board;
18+
using namespace std::chrono_literals;
19+
20+
using Compass = Qmc5883l<I2cMaster>;
21+
Compass::Data data;
22+
Compass compass(data);
23+
modm::atomic::Flag dataReady(true);
24+
25+
MODM_ISR(INT2)
26+
{
27+
dataReady.set();
28+
}
29+
30+
int
31+
main()
32+
{
33+
Board::initialize();
34+
// DRDY pin must be connected to Board pin 19 (Port D2).
35+
Board::D19::setInput(Gpio::InputType::PullUp);
36+
Board::D19::setInputTrigger(Gpio::InputTrigger::RisingEdge);
37+
Board::D19::enableExternalInterrupt();
38+
39+
RF_CALL_BLOCKING(compass.initialize());
40+
auto mode = Compass::Mode_t(Compass::Mode::Continious);
41+
auto rate = Compass::OutputDataRate_t(Compass::OutputDataRate::_10Hz);
42+
auto scale = Compass::FullScale_t(Compass::FullScale::_8G);
43+
RF_CALL_BLOCKING(compass.configure(mode, rate | scale));
44+
45+
for (;;)
46+
{
47+
if (dataReady.testAndSet(false))
48+
{
49+
if (RF_CALL_BLOCKING(compass.readData()))
50+
{
51+
MODM_LOG_INFO << "X:" << compass.x() << " Y: " << compass.y()
52+
<< " Z: " << compass.z()
53+
<< " OVL: " << (compass.status() & Compass::Status::OVL)
54+
<< modm::endl;
55+
} else
56+
{
57+
serialStream << "readDataBlocking(): Error: " << uint8_t(I2cMaster::getErrorState())
58+
<< modm::endl;
59+
}
60+
}
61+
}
62+
}

Diff for: examples/avr/qmc5883l/project.xml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<library>
2+
<extends>modm:mega-2560-pro</extends>
3+
<options>
4+
<option name="modm:build:build.path">../../../build/avr/qmc5883l</option>
5+
</options>
6+
<modules>
7+
<module>modm:platform:i2c</module>
8+
<module>modm:driver:qmc5883l</module>
9+
<module>modm:build:scons</module>
10+
</modules>
11+
</library>

Diff for: src/modm/driver/inertial/qmc5883l.hpp

+196
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
#pragma once
2+
3+
#include <algorithm>
4+
#include <modm/architecture/interface/i2c_device.hpp>
5+
#include <modm/architecture/interface/register.hpp>
6+
#include <modm/math/utils/endianness.hpp>
7+
8+
template<typename I2cMaster>
9+
class Qmc5883l;
10+
11+
struct Qmc5883lRegisters
12+
{
13+
protected:
14+
/// @cond
15+
/// The addresses of the Configuration and Data Registers
16+
enum class Register : uint8_t
17+
{
18+
DataX_Lsb = 0x00,
19+
DataX_Msb = 0x01,
20+
DataY_Lsb = 0x02,
21+
DataY_Msb = 0x03,
22+
DataZ_Lsb = 0x04,
23+
DataZ_Msb = 0x05,
24+
Status = 0x06,
25+
Tout_Lsb = 0x07,
26+
Tout_Msb = 0x08,
27+
Control1 = 0x09,
28+
Control2 = 0x0a,
29+
SetReset = 0x0b,
30+
};
31+
/// @endcond
32+
public:
33+
enum class Status : uint8_t
34+
{
35+
DOR = modm::Bit2,
36+
OVL = modm::Bit1,
37+
DRDY = modm::Bit0,
38+
};
39+
MODM_FLAGS8(Status);
40+
41+
public:
42+
enum class Control1 : uint8_t
43+
{
44+
OSR1 = modm::Bit7,
45+
OSR0 = modm::Bit6,
46+
OversampleRate_Mask = OSR1 | OSR0,
47+
48+
RNG1 = modm::Bit5,
49+
RNG0 = modm::Bit4,
50+
FullScale_Mask = RNG1 | RNG0,
51+
52+
ODR1 = modm::Bit3,
53+
ODR0 = modm::Bit2,
54+
OutputDataRate_Mask = ODR1 | ODR0,
55+
56+
MODE1 = modm::Bit1,
57+
MODE0 = modm::Bit0,
58+
Mode_Mask = MODE1 | MODE0,
59+
};
60+
MODM_FLAGS8(Control1);
61+
62+
enum class Control2 : uint8_t
63+
{
64+
SOFT_RST = modm::Bit7,
65+
ROL_PNT = modm::Bit6,
66+
INT_ENB = modm::Bit0,
67+
};
68+
MODM_FLAGS8(Control2);
69+
70+
public:
71+
enum class OversampleRate : uint8_t
72+
{
73+
_512 = 0,
74+
_256 = int(Control1::OSR0),
75+
_128 = int(Control1::OSR1),
76+
_64 = int(Control1::OSR0) | int(Control1::OSR1),
77+
};
78+
79+
MODM_FLAGS_CONFIG(Control1, OversampleRate);
80+
81+
enum class FullScale : uint8_t
82+
{
83+
_2G = 0,
84+
_8G = int(Control1::RNG0),
85+
};
86+
87+
MODM_FLAGS_CONFIG(Control1, FullScale);
88+
89+
enum class OutputDataRate : uint8_t
90+
{
91+
_10Hz = 0,
92+
_50Hz = int(Control1::ODR0),
93+
_100Hz = int(Control1::ODR1),
94+
_200Hz = int(Control1::ODR0) | int(Control1::ODR1),
95+
};
96+
97+
MODM_FLAGS_CONFIG(Control1, OutputDataRate);
98+
99+
enum class Mode : uint8_t
100+
{
101+
StandBy = 0,
102+
Continious = int(Control1::MODE0),
103+
};
104+
105+
MODM_FLAGS_CONFIG(Control1, Mode);
106+
107+
public:
108+
struct modm_packed Data
109+
{
110+
template<class I2cMaster>
111+
friend class Qmc5883l;
112+
113+
protected:
114+
uint8_t data[7];
115+
116+
template<Register R, uint8_t Offset = uint8_t(R)>
117+
int16_t inline getWord()
118+
{
119+
static_assert(Offset < sizeof data);
120+
const auto value = reinterpret_cast<int16_t *>(data + Offset);
121+
return modm::fromLittleEndian(*value);
122+
}
123+
};
124+
};
125+
126+
template<class I2cMaster>
127+
class Qmc5883l : public Qmc5883lRegisters, public modm::I2cDevice<I2cMaster>
128+
{
129+
/// @cond
130+
Data &data;
131+
/// @endcond
132+
uint8_t buffer[sizeof data.data];
133+
134+
modm::ResumableResult<bool>
135+
writeRegister(Register reg, uint8_t value)
136+
{
137+
RF_BEGIN();
138+
139+
buffer[0] = uint8_t(reg);
140+
buffer[1] = value;
141+
this->transaction.configureWrite(buffer, 2);
142+
143+
RF_END_RETURN_CALL(this->runTransaction());
144+
}
145+
146+
public:
147+
Qmc5883l(Data &data, uint8_t address = 0x0d) : modm::I2cDevice<I2cMaster>(address), data(data)
148+
{}
149+
150+
auto x() { return data.getWord<Register::DataX_Lsb>(); }
151+
152+
auto y() { return data.getWord<Register::DataY_Lsb>(); }
153+
154+
auto z() { return data.getWord<Register::DataZ_Lsb>(); }
155+
156+
auto status() { return Status_t(data.data[uint8_t(Register::Status)]); }
157+
158+
public:
159+
modm::ResumableResult<bool>
160+
initialize()
161+
{
162+
// Per datasheet:
163+
// wihtout any additional explanations recommended to set to 0x01.
164+
return writeRegister(Register::SetReset, 0x01);
165+
}
166+
167+
modm::ResumableResult<bool>
168+
configure(Mode_t mode, Control1_t control)
169+
{
170+
control |= mode;
171+
return writeRegister(Register::Control1, control.value);
172+
}
173+
174+
modm::ResumableResult<bool>
175+
configure(Control2_t control)
176+
{
177+
return writeRegister(Register::Control2, control.value);
178+
}
179+
180+
modm::ResumableResult<bool>
181+
readData()
182+
{
183+
RF_BEGIN();
184+
185+
buffer[0] = uint8_t(Register::DataX_Lsb);
186+
this->transaction.configureWriteRead(buffer, 1, buffer, sizeof buffer);
187+
188+
if (RF_CALL(this->runTransaction()))
189+
{
190+
std::copy_n(buffer, sizeof data.data, data.data);
191+
RF_RETURN(true);
192+
}
193+
194+
RF_END_RETURN(false);
195+
}
196+
};

Diff for: src/modm/driver/inertial/qmc5883l.lb

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env python3
2+
# -*- coding: utf-8 -*-
3+
#
4+
# Copyright (c) 2023, Alexander Solovets
5+
#
6+
# This file is part of the modm project.
7+
#
8+
# This Source Code Form is subject to the terms of the Mozilla Public
9+
# License, v. 2.0. If a copy of the MPL was not distributed with this
10+
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
11+
# -----------------------------------------------------------------------------
12+
13+
14+
def init(module):
15+
module.name = ":driver:qmc5883l"
16+
module.description = """\
17+
# QMC5883L 3-Axis Digital Magnetometer
18+
19+
The QMC5883L is a multi-chip three-axis magnetic sensor. This surface-mount,
20+
small sized chip has integrated magnetic sensors with signal condition ASIC,
21+
targeted for high precision applications such as compassing, navigation and
22+
gaming in drone, robot, mobile and personal hand-held devices.
23+
24+
The QMC5883L is based on state-of-the-art, high resolution, magneto-resistive
25+
technology licensed from Honeywell AMR technology. Along with custom-designed
26+
16-bit ADC ASIC, it offers the advantages of low noise, high accuracy, low
27+
power consumption, offset cancellation and temperature compensation. QMC5883L
28+
enables 1° to 2° compass heading accuracy. The I²C serial bus allows for easy
29+
interface.
30+
"""
31+
32+
def prepare(module, options):
33+
module.depends(
34+
":architecture:i2c.device",
35+
":architecture:register",
36+
":math:utils")
37+
return True
38+
39+
def build(env):
40+
env.outbasepath = "modm/src/modm/driver/inertial"
41+
env.copy("qmc5883l.hpp")

0 commit comments

Comments
 (0)