Skip to content

Commit

Permalink
[driver] Implement QMC5883L driver
Browse files Browse the repository at this point in the history
  • Loading branch information
mbait authored and salkinium committed Sep 10, 2023
1 parent 93af956 commit 7f30961
Show file tree
Hide file tree
Showing 5 changed files with 315 additions and 4 deletions.
9 changes: 5 additions & 4 deletions README.md
Expand Up @@ -789,30 +789,31 @@ your specific needs.
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9535">PCA9535</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9548a">PCA9548A</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-pca9685">PCA9685</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-qmc5883l">QMC5883L</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sh1106">SH1106</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s65">SIEMENS-S65</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s65">SIEMENS-S65</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-siemens_s75">SIEMENS-S75</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk6812">SK6812</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sk9822">SK9822</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ssd1306">SSD1306</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7586s">ST7586S</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7789">ST7789</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-st7789">ST7789</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stts22h">STTS22H</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-stusb4500">STUSB4500</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx1276">SX1276</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-sx128x">SX128X</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3414">TCS3414</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3472">TCS3472</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tcs3472">TCS3472</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tlc594x">TLC594x</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp102">TMP102</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp12x">TMP12x</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-tmp175">TMP175</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-touch2046">TOUCH2046</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl53l0">VL53L0</a></td>
</tr><tr>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl53l0">VL53L0</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-vl6180">VL6180</a></td>
<td align="center"><a href="https://modm.io/reference/module/modm-driver-ws2812">WS2812</a></td>
</tr>
Expand Down
62 changes: 62 additions & 0 deletions examples/avr/qmc5883l/main.cpp
@@ -0,0 +1,62 @@
/*
* Copyright (c) 2023, Alexander Solovets
*
* This file is part of the modm project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
// ----------------------------------------------------------------------------

#include <modm/architecture/driver/atomic/flag.hpp>
#include <modm/board.hpp>
#include <modm/debug/logger.hpp>
#include <modm/driver/inertial/qmc5883l.hpp>

using namespace Board;
using namespace std::chrono_literals;

using Compass = Qmc5883l<I2cMaster>;
Compass::Data data;
Compass compass(data);
modm::atomic::Flag dataReady(true);

MODM_ISR(INT2)
{
dataReady.set();
}

int
main()
{
Board::initialize();
// DRDY pin must be connected to Board pin 19 (Port D2).
Board::D19::setInput(Gpio::InputType::PullUp);
Board::D19::setInputTrigger(Gpio::InputTrigger::RisingEdge);
Board::D19::enableExternalInterrupt();

RF_CALL_BLOCKING(compass.initialize());
auto mode = Compass::Mode_t(Compass::Mode::Continious);
auto rate = Compass::OutputDataRate_t(Compass::OutputDataRate::_10Hz);
auto scale = Compass::FullScale_t(Compass::FullScale::_8G);
RF_CALL_BLOCKING(compass.configure(mode, rate | scale));

for (;;)
{
if (dataReady.testAndSet(false))
{
if (RF_CALL_BLOCKING(compass.readData()))
{
MODM_LOG_INFO << "X:" << compass.x() << " Y: " << compass.y()
<< " Z: " << compass.z()
<< " OVL: " << (compass.status() & Compass::Status::OVL)
<< modm::endl;
} else
{
serialStream << "readDataBlocking(): Error: " << uint8_t(I2cMaster::getErrorState())
<< modm::endl;
}
}
}
}
11 changes: 11 additions & 0 deletions examples/avr/qmc5883l/project.xml
@@ -0,0 +1,11 @@
<library>
<extends>modm:mega-2560-pro</extends>
<options>
<option name="modm:build:build.path">../../../build/avr/qmc5883l</option>
</options>
<modules>
<module>modm:platform:i2c</module>
<module>modm:driver:qmc5883l</module>
<module>modm:build:scons</module>
</modules>
</library>
196 changes: 196 additions & 0 deletions src/modm/driver/inertial/qmc5883l.hpp
@@ -0,0 +1,196 @@
#pragma once

#include <algorithm>
#include <modm/architecture/interface/i2c_device.hpp>
#include <modm/architecture/interface/register.hpp>
#include <modm/math/utils/endianness.hpp>

template<typename I2cMaster>
class Qmc5883l;

struct Qmc5883lRegisters
{
protected:
/// @cond
/// The addresses of the Configuration and Data Registers
enum class Register : uint8_t
{
DataX_Lsb = 0x00,
DataX_Msb = 0x01,
DataY_Lsb = 0x02,
DataY_Msb = 0x03,
DataZ_Lsb = 0x04,
DataZ_Msb = 0x05,
Status = 0x06,
Tout_Lsb = 0x07,
Tout_Msb = 0x08,
Control1 = 0x09,
Control2 = 0x0a,
SetReset = 0x0b,
};
/// @endcond
public:
enum class Status : uint8_t
{
DOR = modm::Bit2,
OVL = modm::Bit1,
DRDY = modm::Bit0,
};
MODM_FLAGS8(Status);

public:
enum class Control1 : uint8_t
{
OSR1 = modm::Bit7,
OSR0 = modm::Bit6,
OversampleRate_Mask = OSR1 | OSR0,

RNG1 = modm::Bit5,
RNG0 = modm::Bit4,
FullScale_Mask = RNG1 | RNG0,

ODR1 = modm::Bit3,
ODR0 = modm::Bit2,
OutputDataRate_Mask = ODR1 | ODR0,

MODE1 = modm::Bit1,
MODE0 = modm::Bit0,
Mode_Mask = MODE1 | MODE0,
};
MODM_FLAGS8(Control1);

enum class Control2 : uint8_t
{
SOFT_RST = modm::Bit7,
ROL_PNT = modm::Bit6,
INT_ENB = modm::Bit0,
};
MODM_FLAGS8(Control2);

public:
enum class OversampleRate : uint8_t
{
_512 = 0,
_256 = int(Control1::OSR0),
_128 = int(Control1::OSR1),
_64 = int(Control1::OSR0) | int(Control1::OSR1),
};

MODM_FLAGS_CONFIG(Control1, OversampleRate);

enum class FullScale : uint8_t
{
_2G = 0,
_8G = int(Control1::RNG0),
};

MODM_FLAGS_CONFIG(Control1, FullScale);

enum class OutputDataRate : uint8_t
{
_10Hz = 0,
_50Hz = int(Control1::ODR0),
_100Hz = int(Control1::ODR1),
_200Hz = int(Control1::ODR0) | int(Control1::ODR1),
};

MODM_FLAGS_CONFIG(Control1, OutputDataRate);

enum class Mode : uint8_t
{
StandBy = 0,
Continious = int(Control1::MODE0),
};

MODM_FLAGS_CONFIG(Control1, Mode);

public:
struct modm_packed Data
{
template<class I2cMaster>
friend class Qmc5883l;

protected:
uint8_t data[7];

template<Register R, uint8_t Offset = uint8_t(R)>
int16_t inline getWord()
{
static_assert(Offset < sizeof data);
const auto value = reinterpret_cast<int16_t *>(data + Offset);
return modm::fromLittleEndian(*value);
}
};
};

template<class I2cMaster>
class Qmc5883l : public Qmc5883lRegisters, public modm::I2cDevice<I2cMaster>
{
/// @cond
Data &data;
/// @endcond
uint8_t buffer[sizeof data.data];

modm::ResumableResult<bool>
writeRegister(Register reg, uint8_t value)
{
RF_BEGIN();

buffer[0] = uint8_t(reg);
buffer[1] = value;
this->transaction.configureWrite(buffer, 2);

RF_END_RETURN_CALL(this->runTransaction());
}

public:
Qmc5883l(Data &data, uint8_t address = 0x0d) : modm::I2cDevice<I2cMaster>(address), data(data)
{}

auto x() { return data.getWord<Register::DataX_Lsb>(); }

auto y() { return data.getWord<Register::DataY_Lsb>(); }

auto z() { return data.getWord<Register::DataZ_Lsb>(); }

auto status() { return Status_t(data.data[uint8_t(Register::Status)]); }

public:
modm::ResumableResult<bool>
initialize()
{
// Per datasheet:
// wihtout any additional explanations recommended to set to 0x01.
return writeRegister(Register::SetReset, 0x01);
}

modm::ResumableResult<bool>
configure(Mode_t mode, Control1_t control)
{
control |= mode;
return writeRegister(Register::Control1, control.value);
}

modm::ResumableResult<bool>
configure(Control2_t control)
{
return writeRegister(Register::Control2, control.value);
}

modm::ResumableResult<bool>
readData()
{
RF_BEGIN();

buffer[0] = uint8_t(Register::DataX_Lsb);
this->transaction.configureWriteRead(buffer, 1, buffer, sizeof buffer);

if (RF_CALL(this->runTransaction()))
{
std::copy_n(buffer, sizeof data.data, data.data);
RF_RETURN(true);
}

RF_END_RETURN(false);
}
};
41 changes: 41 additions & 0 deletions src/modm/driver/inertial/qmc5883l.lb
@@ -0,0 +1,41 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Copyright (c) 2023, Alexander Solovets
#
# This file is part of the modm project.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# -----------------------------------------------------------------------------


def init(module):
module.name = ":driver:qmc5883l"
module.description = """\
# QMC5883L 3-Axis Digital Magnetometer
The QMC5883L is a multi-chip three-axis magnetic sensor. This surface-mount,
small sized chip has integrated magnetic sensors with signal condition ASIC,
targeted for high precision applications such as compassing, navigation and
gaming in drone, robot, mobile and personal hand-held devices.
The QMC5883L is based on state-of-the-art, high resolution, magneto-resistive
technology licensed from Honeywell AMR technology. Along with custom-designed
16-bit ADC ASIC, it offers the advantages of low noise, high accuracy, low
power consumption, offset cancellation and temperature compensation. QMC5883L
enables 1° to 2° compass heading accuracy. The I²C serial bus allows for easy
interface.
"""

def prepare(module, options):
module.depends(
":architecture:i2c.device",
":architecture:register",
":math:utils")
return True

def build(env):
env.outbasepath = "modm/src/modm/driver/inertial"
env.copy("qmc5883l.hpp")

0 comments on commit 7f30961

Please sign in to comment.