~aleteoryx/muditaos

70a4b21c33a1add74442d64e0ed07cf93e99fb90 — Mateusz Grzywacz 5 years ago b033d0c
[EGD-3045] Magnetometer driver (#788)

* [EGD-3045] magnetometer driver - timer polling approach
- magneto pin mux
- magneto reg structs
- magneto irq skeleton
- bluetooth irq handler moved
- dedicated i2c methods
- bitfields parsed and populated explicitly
* [misc] big endian ↔ long conversion in Utils + UT
M changelog.md => changelog.md +1 -0
@@ 11,6 11,7 @@
* `[audio][bluetooth]` Added Bluetooth A2DP playback
* `[settings]` Added sql script for file indexer DB schema
* `[settings][bluetooth]` Create "Add device" windows
* `[hardware]` Slider driver (offline/online mode selection)

### Fixed


M module-bsp/board/linux/magnetometer/magnetometer.cpp => module-bsp/board/linux/magnetometer/magnetometer.cpp +8 -4
@@ 3,10 3,6 @@

#include "bsp/magnetometer/magnetometer.hpp"

#include "bsp/BoardDefinitions.hpp"

using namespace drivers;

static xQueueHandle qHandleIrq = NULL;

namespace bsp


@@ 31,5 27,13 @@ namespace bsp
        {
            return bsp::Board::Linux;
        }

        std::optional<bsp::KeyCodes> WorkerEventHandler()
        {
            return std::nullopt;
        }

        void enableIRQ()
        {}
    } // namespace magnetometer
} // namespace bsp

M module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp => module-bsp/board/rt1051/bluetooth/BluetoothCommon.cpp +0 -19
@@ 257,22 257,3 @@ void BluetoothCommon::set_irq(bool enable)
    LPUART_EnableRx(BSP_BLUETOOTH_UART_BASE, true);
    LPUART_EnableTx(BSP_BLUETOOTH_UART_BASE, true);
}

extern "C"
{
    void GPIO1_Combined_16_31_IRQHandler(void)
    {
        BaseType_t xHigherPriorityTaskWoken = 0;
        uint32_t irq_mask                   = GPIO_GetPinsInterruptFlags(GPIO1);

        if (irq_mask & (1 << BSP_BLUETOOTH_UART_CTS_PIN)) {
            LOG_DEBUG("CTS IRQ!\n");
        }

        // Clear all IRQs
        GPIO_PortClearInterruptFlags(GPIO1, irq_mask);

        // Switch context if necessary
        portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
    }
};

A module-bsp/board/rt1051/bsp/magnetometer/ALS31300.cpp => module-bsp/board/rt1051/bsp/magnetometer/ALS31300.cpp +119 -0
@@ 0,0 1,119 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "ALS31300.hpp"

namespace drivers::als31300
{

    conf_reg::conf_reg(whole_reg_t whole_reg)
    {
        user_eeprom       = whole_reg & 0b11111;
        int_latch_enable  = (whole_reg >> 5) & 0b1;
        channel_X_en      = (whole_reg >> 6) & 0b1;
        channel_Y_en      = (whole_reg >> 7) & 0b1;
        channel_Z_en      = (whole_reg >> 8) & 0b1;
        I2C_threshold     = (whole_reg >> 9) & 0b1;
        slave_addr        = (whole_reg >> 10) & 0b1111111;
        disable_slave_ADC = (whole_reg >> 17) & 0b1;
        I2C_CRC_en        = (whole_reg >> 18) & 0b1;
        hall_mode         = (whole_reg >> 19) & 0b11;
        bandwidth         = (whole_reg >> 21) & 0b111;
        RESERVED          = (whole_reg >> 24) & 0xFF;
    }

    conf_reg::operator whole_reg_t() const
    {
        return (user_eeprom & 0b11111) | (int_latch_enable & 0b1) << 5 | (channel_X_en & 0b1) << 6 |
               (channel_Y_en & 0b1) << 7 | (channel_Z_en & 0b1) << 8 | (I2C_threshold & 0b1) << 9 |
               (slave_addr & 0b1111111) << 10 | (disable_slave_ADC & 0b1) << 17 | (I2C_CRC_en & 0b1) << 18 |
               (hall_mode & 0b11) << 19 | (bandwidth & 0b111) << 21 | (RESERVED & 0xFF) << 24;
    }

    int_reg::int_reg(whole_reg_t whole_reg)
    {
        int_X_threshold      = whole_reg & 0b111111;
        int_Y_threshold      = (whole_reg >> 6) & 0b111111;
        int_Z_threshold      = (whole_reg >> 12) & 0b111111;
        int_X_en             = (whole_reg >> 18) & 0b1;
        int_Y_en             = (whole_reg >> 19) & 0b1;
        int_Z_en             = (whole_reg >> 20) & 0b1;
        int_eeprom_en        = (whole_reg >> 21) & 0b1;
        int_eeprom_status    = (whole_reg >> 22) & 0b1;
        int_mode             = (whole_reg >> 23) & 0b1;
        int_threshold_signed = (whole_reg >> 24) & 0b1;
        RESERVED             = (whole_reg >> 25) & 0b1111111;
    }

    int_reg::operator whole_reg_t() const
    {
        return (int_X_threshold & 0b111111) | (int_Y_threshold & 0b111111) << 6 | (int_Z_threshold & 0b111111) << 12 |
               (int_X_en & 0b1) << 18 | (int_Y_en & 0b1) << 19 | (int_Z_en & 0b1) << 20 | (int_eeprom_en & 0b1) << 21 |
               (int_eeprom_status & 0b1) << 22 | (int_mode & 0b1) << 23 | (int_threshold_signed & 0b1) << 24 |
               (RESERVED & 0b1111111) << 25;
    }

    pwr_reg::pwr_reg(whole_reg_t whole_reg)
    {
        sleep             = whole_reg & 0b11;
        I2C_loop_mode     = (whole_reg >> 2) & 0b11;
        count_max_LP_mode = (whole_reg >> 4) & 0b111;
        RESERVED          = (whole_reg >> 7) & 0x1FFFFFF;
    }

    pwr_reg::operator whole_reg_t() const
    {
        return (sleep & 0b11) | (I2C_loop_mode & 0b11) << 2 | (count_max_LP_mode & 0b111) << 4 |
               (RESERVED & 0x1FFFFFF) << 7;
    }

    measurements_MSB_reg::measurements_MSB_reg(whole_reg_t whole_reg)
    {
        temperature_MSB = whole_reg & 0b111111;
        int_flag        = (whole_reg >> 6) & 0b1;
        new_data_flag   = (whole_reg >> 7) & 0b1;
        Z_MSB           = (whole_reg >> 8) & 0xFF;
        Y_MSB           = (whole_reg >> 16) & 0xFF;
        X_MSB           = (whole_reg >> 24) & 0xFF;
    }

    measurements_MSB_reg::operator whole_reg_t() const
    {
        return (temperature_MSB & 0b111111) | (int_flag & 0b1) << 6 | (new_data_flag & 0b1) << 7 | (Z_MSB & 0xFF) << 8 |
               (Y_MSB & 0xFF) << 16 | (X_MSB & 0xFF) << 24;
    }

    measurements_LSB_reg::measurements_LSB_reg(whole_reg_t whole_reg)
    {
        temperature_LSB          = whole_reg & 0b111111;
        hall_mode_status         = (whole_reg >> 6) & 0b11;
        Z_LSB                    = (whole_reg >> 8) & 0b1111;
        Y_LSB                    = (whole_reg >> 12) & 0b1111;
        X_LSB                    = (whole_reg >> 16) & 0b1111;
        int_eeprom_write_pending = (whole_reg >> 20) & 0b1;
        RESERVED                 = (whole_reg >> 21) & 0x7FF;
    }

    measurements_LSB_reg::operator whole_reg_t() const
    {
        return (temperature_LSB & 0b111111) | (hall_mode_status & 0b11) << 6 | (Z_LSB & 0b1111) << 8 |
               (Y_LSB & 0b1111) << 12 | (X_LSB & 0b1111) << 16 | (int_eeprom_write_pending & 0b1) << 20 |
               (RESERVED & 0x7FF) << 21;
    }

    float temperature_convert(uint16_t raw_temperature)
    {
        const int32_t intermediate = raw_temperature - 1708;
        return intermediate * 0.0737;
    }

    int16_t measurement_sign_convert(uint16_t raw_measurement, uint8_t bit_length)
    {
        // via: https://stackoverflow.com/questions/16946801/n-bit-2s-binary-to-decimal-in-c
        const auto sign_flag = 1 << (bit_length - 1);
        if (raw_measurement & sign_flag) {
            raw_measurement |= -(1 << bit_length);
        }
        return raw_measurement;
    }
} // namespace drivers::als31300

A module-bsp/board/rt1051/bsp/magnetometer/ALS31300.hpp => module-bsp/board/rt1051/bsp/magnetometer/ALS31300.hpp +176 -0
@@ 0,0 1,176 @@
// Copyright (c) 2017-2020, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#pragma once

#include <cstdint>
#include <cstring>
#include <set>
#include <chrono>

// ALS31300 magnetometer driver
namespace drivers::als31300
{

    // Note: this device returns 32-bit register values in MSB order

    using whole_reg_t = uint32_t; // ALS31300 always talks 4 bytes

    constexpr auto I2C_ADDRESS = 0x64;

    // base ALS31300 register struct.
    struct base_reg
    {
        virtual operator whole_reg_t() const = 0;
    };

    // REGISTER DEFINITIONS

    // there is no 0x01 register
    // --------
    constexpr auto CONF_REG = 0x02;
    struct conf_reg : base_reg
    {
        conf_reg(whole_reg_t whole_reg);
        operator whole_reg_t() const override;
        uint8_t user_eeprom : 5;
        bool int_latch_enable : 1;
        bool channel_X_en : 1;
        bool channel_Y_en : 1;
        bool channel_Z_en : 1;
        bool I2C_threshold : 1;
        uint8_t slave_addr : 7;
        bool disable_slave_ADC : 1;
        bool I2C_CRC_en : 1;
        uint8_t hall_mode : 2;
        uint8_t bandwidth : 3;
        uint8_t RESERVED : 8;
    };

    constexpr auto CONF_REG_LATCH_disabled = 0b0;
    constexpr auto CONF_REG_LATCH_enabled  = 0b1;

    constexpr auto CONF_REG_CHANNEL_disabled = 0b0;
    constexpr auto CONF_REG_CHANNEL_enabled  = 0b1;

    constexpr auto CONF_REG_I2C_THRES_3v0 = 0b0;
    constexpr auto CONF_REG_I2C_THRES_1v8 = 0b1;

    // --------
    constexpr auto INT_REG = 0x03;

    struct int_reg : base_reg
    {
        int_reg(whole_reg_t whole_reg);
        operator whole_reg_t() const override;
        uint8_t int_X_threshold : 6;
        uint8_t int_Y_threshold : 6;
        uint8_t int_Z_threshold : 6;
        bool int_X_en : 1;
        bool int_Y_en : 1;
        bool int_Z_en : 1;
        bool int_eeprom_en : 1;
        bool int_eeprom_status : 1;
        bool int_mode : 1;
        bool int_threshold_signed : 1;
        uint8_t RESERVED : 7;
    };

    constexpr auto INT_REG_INT_CHANNEL_disabled = 0b0;
    constexpr auto INT_REG_INT_CHANNEL_enabled  = 0b1;

    constexpr auto INT_REG_INT_MODE_threshold = 0b0;
    constexpr auto INT_REG_INT_MODE_delta     = 0b1;

    constexpr auto INT_REG_THRESHOLD_absolute = 0b0;
    constexpr auto INT_REG_THRESHOLD_signed   = 0b1;

    constexpr auto INT_REG_INT_EEPROM_disable = 0b0;
    constexpr auto INT_REG_INT_EEPROM_enable  = 0b1;

    // --------
    constexpr auto PWR_REG = 0x27;

    struct pwr_reg : base_reg
    {
        pwr_reg(whole_reg_t whole_reg);
        operator whole_reg_t() const override;
        uint8_t sleep : 2;
        uint8_t I2C_loop_mode : 2;
        uint8_t count_max_LP_mode : 3;
        uint32_t RESERVED : 25;
    };

    constexpr auto PWR_REG_SLEEP_MODE_active = 0b00;
    constexpr auto PWR_REG_SLEEP_MODE_sleep  = 0b01;
    constexpr auto PWR_REG_SLEEP_MODE_LPDCM  = 0b10; // Low-Power Duty Cycle Mode

    constexpr auto PWR_REG_LOOP_MODE_single    = 0b00;
    constexpr auto PWR_REG_LOOP_MODE_fast_loop = 0b01;
    constexpr auto PWR_REG_LOOP_MODE_full_loop = 0b10;

    enum PWR_REG_SLEEP_MODE
    {
        active          = PWR_REG_SLEEP_MODE_active,
        sleep           = PWR_REG_SLEEP_MODE_sleep,
        periodic_active = PWR_REG_SLEEP_MODE_LPDCM,
    };

    constexpr auto PWR_ON_DELAY_MS = 1; //  spec'd as 600µs at most

    // --------
    constexpr auto MEASUREMENTS_MSB_REG = 0x28;

    struct measurements_MSB_reg : base_reg
    {
        measurements_MSB_reg(whole_reg_t whole_reg);
        operator whole_reg_t() const override;
        uint8_t temperature_MSB : 6;
        bool int_flag : 1;
        bool new_data_flag : 1;
        uint8_t Z_MSB : 8;
        uint8_t Y_MSB : 8;
        uint8_t X_MSB : 8;
    };

    constexpr auto MEAS_REG_NEW_DATA_not_avail = 0b0;
    constexpr auto MEAS_REG_NEW_DATA_available = 0b1;

    // --------
    constexpr auto MEASUREMENTS_LSB_REG = 0x29;

    struct measurements_LSB_reg : base_reg
    {
        measurements_LSB_reg(whole_reg_t whole_reg);
        operator whole_reg_t() const override;
        uint8_t temperature_LSB : 6;
        uint8_t hall_mode_status : 2;
        uint8_t Z_LSB : 4;
        uint8_t Y_LSB : 4;
        uint8_t X_LSB : 4;
        bool int_eeprom_write_pending : 1;
        uint16_t RESERVED : 11;
    };

    float temperature_convert(uint16_t raw_temperature);

    // NOTE: device sensitivity HW fixed at 4 LSB/Gauss == 0.4 LSB/mT
    // All measurements are supposed to be raw 4 LSB/Gauss. No need to introduce fractions
    constexpr auto MEASUREMENTS_REG_bitlength_full_scale = 12;

    int16_t measurement_sign_convert(uint16_t raw_measurement,
                                     uint8_t bit_length = MEASUREMENTS_REG_bitlength_full_scale);
    // --------
    constexpr uint8_t CUSTOMER_ACCESS_REG          = 0x35;
    constexpr whole_reg_t CUSTOMER_ACCESS_REG_code = 0x2C413534;

    // --------

    /// give eeprom registers time to complete WRITE
    const std::set<uint8_t> EEPROM_REGS = {CUSTOMER_ACCESS_REG, CONF_REG, INT_REG};

    using std::chrono_literals::operator""ms;
    constexpr auto EEPROM_REG_WRITE_DELAY_MS = 60ms; // docs say 50ms, +10ms for good measure

    /////////////////////
} // namespace drivers::als31300

M module-bsp/board/rt1051/bsp/magnetometer/magnetometer.cpp => module-bsp/board/rt1051/bsp/magnetometer/magnetometer.cpp +280 -3
@@ 3,23 3,83 @@

#include "bsp/magnetometer/magnetometer.hpp"

#include <module-utils/Utils.hpp> // for byte conversion functions. it is included first because of magic enum define

#include "ALS31300.hpp"
#include "bsp/BoardDefinitions.hpp"

#include "drivers/i2c/DriverI2C.hpp"

#include "fsl_common.h"
#include <fsl_common.h>
#include <timers.h>

using namespace drivers;
using namespace utils;

static std::shared_ptr<drivers::DriverI2C> i2c;
static drivers::I2CAddress addr = {.deviceAddress = 0x64, .subAddressSize = 1};

static I2CAddress addr = {.deviceAddress = als31300::I2C_ADDRESS, .subAddressSize = 1};

union i2c_buf_t
{
    uint8_t buf[sizeof(als31300::whole_reg_t)];
    als31300::whole_reg_t whole_reg;
};

static i2c_buf_t i2c_buf;

static xQueueHandle qHandleIrq = NULL;

namespace bsp
{

    namespace magnetometer
    {
        std::shared_ptr<DriverGPIO> gpio;

        bsp::KeyCodes current_parsed = bsp::KeyCodes::Undefined;

        static TimerHandle_t timerHandle;
        static constexpr uint16_t MAGNETOMETER_POLL_INTERVAL_MS = 500;

        static void TimerHandler(TimerHandle_t xTimer)
        {
            if (qHandleIrq != nullptr) {
                uint8_t val = 0x01;
                xQueueSend(qHandleIrq, &val, 0);
            }
        }

        bool setActive(als31300::PWR_REG_SLEEP_MODE sleep_mode);

        bool i2cRead(const uint8_t reg_addr, als31300::whole_reg_t &whole_reg)
        {
            addr.subAddress = reg_addr;
            if (i2c->Read(addr, i2c_buf.buf, sizeof(als31300::whole_reg_t)) != sizeof(als31300::whole_reg_t)) {
                return false;
            }
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
            // magnetometr talks big endian
            i2c_buf.whole_reg = swapBytes(i2c_buf.whole_reg);
#endif
            whole_reg = i2c_buf.whole_reg;
            return true;
        }

        bool i2cWrite(const uint8_t reg_addr, const als31300::whole_reg_t whole_reg)
        {
            addr.subAddress = reg_addr;
#if __BYTE_ORDER__ != __ORDER_BIG_ENDIAN__
            // magnetometer talks big endian
            i2c_buf.whole_reg = swapBytes(whole_reg);
#else
            i2c_buf.whole_reg = whole_reg;
#endif
            auto wrote = i2c->Write(addr, i2c_buf.buf, sizeof(als31300::whole_reg_t)) == sizeof(als31300::whole_reg_t);
            if (als31300::EEPROM_REGS.count(reg_addr) == 1) {
                vTaskDelay(pdMS_TO_TICKS(als31300::EEPROM_REG_WRITE_DELAY_MS.count()));
            }
            return wrote;
        }

        int32_t init(xQueueHandle qHandle)
        {


@@ 28,9 88,162 @@ namespace bsp
                DriverI2CParams{.baudrate = static_cast<uint32_t>(BoardDefinitions::MAGNETOMETER_I2C_BAUDRATE)});

            qHandleIrq = qHandle;

            // any configuration must be proceeded in active state
            setActive(als31300::PWR_REG_SLEEP_MODE::active);

            // GET WRITE ACCESS
            if (!i2cWrite(als31300::CUSTOMER_ACCESS_REG, als31300::CUSTOMER_ACCESS_REG_code)) {
                LOG_ERROR("magneto: CANNOT INIT SLIDER SENSOR");
                return kStatus_Fail;
            }

            // CONFIGURATION register read
            als31300::whole_reg_t read_reg;
            i2cRead(als31300::CONF_REG, read_reg);
            const als31300::conf_reg current_reg_conf(read_reg);
            LOG_DEBUG("CONF read:\t%" PRIu32, static_cast<uint32_t>(current_reg_conf));
            als31300::conf_reg reg_conf = current_reg_conf;
            reg_conf.I2C_threshold      = als31300::CONF_REG_I2C_THRES_1v8;
            reg_conf.int_latch_enable   = als31300::CONF_REG_LATCH_disabled; // we want to detect stable positions
            reg_conf.channel_X_en       = als31300::CONF_REG_CHANNEL_enabled;
            reg_conf.channel_Y_en       = als31300::CONF_REG_CHANNEL_enabled;
            reg_conf.channel_Z_en       = als31300::CONF_REG_CHANNEL_disabled;
            reg_conf.bandwidth          = 1; // longest unit measurement
            if (current_reg_conf != reg_conf) {
                assert(i2cWrite(als31300::CONF_REG, reg_conf));
                LOG_DEBUG("CONF wrote:\t%" PRIu32, static_cast<uint32_t>(reg_conf));

                i2cRead(als31300::CONF_REG, read_reg);
                LOG_DEBUG("CONF verify:\t%" PRIu32, static_cast<uint32_t>(als31300::conf_reg(read_reg)));
            }
            else {
                LOG_DEBUG("CONF is fine, sparing a write");
            }

            // INTERRUPTS register
            i2cRead(als31300::INT_REG, read_reg);
            const als31300::int_reg current_reg_int = read_reg;
            LOG_DEBUG("INT read:\t%" PRIu32, static_cast<uint32_t>(current_reg_int));
            als31300::int_reg reg_int    = current_reg_int;
            reg_int.int_eeprom_en        = als31300::INT_REG_INT_EEPROM_disable;
            reg_int.int_mode             = als31300::INT_REG_INT_MODE_threshold;
            reg_int.int_threshold_signed = als31300::INT_REG_THRESHOLD_absolute;
            reg_int.int_X_en             = als31300::INT_REG_INT_CHANNEL_disabled;
            reg_int.int_Y_en             = als31300::INT_REG_INT_CHANNEL_disabled;
            reg_int.int_Z_en             = als31300::INT_REG_INT_CHANNEL_disabled;
            reg_int.int_X_threshold      = 1;
            reg_int.int_Y_threshold      = 4;
            reg_int.int_Z_threshold      = 0;
            if (current_reg_int != reg_int) {
                assert(i2cWrite(als31300::INT_REG, reg_int));
                LOG_DEBUG("INT wrote:\t%" PRIu32, static_cast<uint32_t>(reg_int));

                i2cRead(als31300::INT_REG, read_reg);
                LOG_DEBUG("INT verify:\t%" PRIu32, static_cast<uint32_t>(als31300::int_reg(read_reg)));
            }
            else {
                LOG_DEBUG("INT is fine, sparing a write");
            }

            // INTERRUPT PIN
            gpio =
                DriverGPIO::Create(static_cast<GPIOInstances>(BoardDefinitions::MAGNETOMETER_GPIO), DriverGPIOParams{});

            // INTERRUPT PIN
            gpio->ClearPortInterrupts(1 << static_cast<uint32_t>(BoardDefinitions::MAGNETOMETER_IRQ));
            gpio->ConfPin(DriverGPIOPinParams{.dir     = DriverGPIOPinParams::Direction::Input,
                                              .irqMode = DriverGPIOPinParams::InterruptMode::IntFallingEdge,
                                              .pin     = static_cast<uint32_t>(BoardDefinitions::MAGNETOMETER_IRQ)});
            // NOTE: irq not yet enabled
            // this version uses timer to poll the sensor
            // the timer requests to read the magnetometer periodically
            if (timerHandle == nullptr) {
                timerHandle = xTimerCreate(
                    "SliderTimer", pdMS_TO_TICKS(MAGNETOMETER_POLL_INTERVAL_MS), true, nullptr, TimerHandler);
                if (timerHandle == nullptr) {
                    LOG_FATAL("Could not create the timer for Headset insertion/removal detection");
                    return kStatus_Fail;
                }
            }

            // POWER register
            i2cRead(als31300::PWR_REG, read_reg);
            const als31300::pwr_reg current_reg_pwr = read_reg;
            LOG_DEBUG("POWER read:\t%" PRIu32, static_cast<uint32_t>(current_reg_pwr));
            als31300::pwr_reg reg_pwr = current_reg_pwr;
            reg_pwr.I2C_loop_mode     = als31300::PWR_REG_LOOP_MODE_single; // we don't want constant data flow
            reg_pwr.sleep             = als31300::PWR_REG_SLEEP_MODE_active;
            reg_pwr.count_max_LP_mode = 3U; // get an update every 6 - 500, 7 - 1000 ms

            i2cWrite(als31300::PWR_REG, reg_pwr);
            LOG_DEBUG("POWER wrote:\t%" PRIu32, static_cast<uint32_t>(reg_pwr));

            xTimerStart(timerHandle, 1000);

            return kStatus_Success;
        }

        std::pair<bool, Measurements> getMeasurement()
        {
            als31300::whole_reg_t read_reg;

            if (!i2cRead(als31300::MEASUREMENTS_MSB_REG, read_reg)) {
                LOG_DEBUG("magneto: CANNOT READ");
                return std::make_pair(false, Measurements()); // todo: nullopt
            }
            // is there anything new ?
            als31300::measurements_MSB_reg reg_msb = read_reg;

            if (reg_msb.int_flag == true) {
                LOG_DEBUG("magneto: INT flag in register");
            }

            if (reg_msb.new_data_flag != als31300::MEAS_REG_NEW_DATA_available) {
                return std::make_pair(false, Measurements());
            }
            else {
                if (reg_msb.int_flag == true) {
                    // clear INT flag
                    if (!i2cWrite(als31300::MEASUREMENTS_MSB_REG, reg_msb)) {
                        return std::make_pair(false, Measurements()); // todo: null opt
                    }
                }
                Measurements meas;

                i2cRead(als31300::MEASUREMENTS_LSB_REG, read_reg);

                als31300::measurements_LSB_reg reg_lsb = read_reg;

                meas.X = als31300::measurement_sign_convert(reg_msb.X_MSB << 4 | reg_lsb.X_LSB);
                meas.Y = als31300::measurement_sign_convert(reg_msb.Y_MSB << 4 | reg_lsb.Z_LSB);
                meas.Z = als31300::measurement_sign_convert(reg_msb.Z_MSB << 4 | reg_lsb.Z_LSB);

                return std::pair(true, meas);
            }
        }

        bool setActive(als31300::PWR_REG_SLEEP_MODE sleep_mode)
        {
            // POWER register
            als31300::whole_reg_t read_reg;

            if (!i2cRead(als31300::PWR_REG, read_reg)) {
                return false;
            }
            als31300::pwr_reg reg_pwr = read_reg;
            reg_pwr.sleep             = sleep_mode;

            if (!i2cWrite(als31300::PWR_REG, reg_pwr)) {
                return false;
            }
            if (sleep_mode == als31300::PWR_REG_SLEEP_MODE::active ||
                sleep_mode == als31300::PWR_REG_SLEEP_MODE::periodic_active) {
                vTaskDelay(pdMS_TO_TICKS(als31300::PWR_ON_DELAY_MS)); // give it some time to wake up
            }
            return true;
        }

        bool isPresent(void)
        {
            uint8_t buf;


@@ 43,6 256,65 @@ namespace bsp
            return true;
        }

        bsp::KeyCodes parse(const Measurements &measurements)
        {
            // X is tri-stable
            const auto X_lower_boundary  = -150;
            const auto X_upper_boundary  = 150;
            const auto X_lower_threshold = -65;
            const auto X_upper_threshold = 60;
            // Y is bi-stable
            const auto Y_threshold = -175;
            // Y is used only for proofing X, so no strict thresholds
            // Z is useless

            if (measurements.X > X_lower_boundary && measurements.X < X_upper_boundary) {
                if (measurements.X < X_lower_threshold) {
                    if (measurements.Y > Y_threshold) {
                        return bsp::KeyCodes::SSwitchDown;
                    }
                }
                else if (measurements.X > X_upper_threshold) {
                    if (measurements.Y > Y_threshold) {
                        return bsp::KeyCodes::SSwitchUp;
                    }
                }
                else {
                    if (measurements.Y < Y_threshold) {
                        return bsp::KeyCodes::SSwitchMid;
                    }
                }
            }
            return bsp::KeyCodes::Undefined;
        }

        std::optional<bsp::KeyCodes> WorkerEventHandler()
        {
            // try to get new data from active magneto
            setActive(als31300::PWR_REG_SLEEP_MODE::active);
            auto [new_data, measurement] = getMeasurement();
            setActive(als31300::PWR_REG_SLEEP_MODE::sleep);
            if (new_data == true) {
                auto incoming_parsed = parse(measurement);
                if (incoming_parsed != bsp::KeyCodes::Undefined and incoming_parsed != current_parsed) {
                    current_parsed = incoming_parsed;
                    return current_parsed;
                }
            }
            return std::nullopt;
        }

        BaseType_t IRQHandler()
        {
            gpio->DisableInterrupt(1 << static_cast<uint32_t>(BoardDefinitions::MAGNETOMETER_IRQ));
            BaseType_t xHigherPriorityTaskWoken = pdFALSE;
            if (qHandleIrq != NULL) {
                uint8_t val = 0x01;
                xQueueSendFromISR(qHandleIrq, &val, &xHigherPriorityTaskWoken);
            }
            return xHigherPriorityTaskWoken;
        }

        bsp::Board GetBoard(void)
        {
            if (isPresent()) {


@@ 50,5 322,10 @@ namespace bsp
            }
            return bsp::Board::T3;
        }

        void enableIRQ()
        {
            gpio->EnableInterrupt(1 << static_cast<uint32_t>(BoardDefinitions::MAGNETOMETER_IRQ));
        }
    } // namespace magnetometer
} // namespace bsp

M module-bsp/board/rt1051/bsp/torch/torch.cpp => module-bsp/board/rt1051/bsp/torch/torch.cpp +4 -3
@@ 12,15 12,16 @@
using namespace drivers;

static std::shared_ptr<drivers::DriverI2C> i2c;
static drivers::I2CAddress addr = {.deviceAddress = 0x63, .subAddressSize = 1};

static xQueueHandle qHandleIrq = NULL;

namespace bsp
{

    namespace torch
    {
        static xQueueHandle qHandleIrq = NULL;

        static I2CAddress addr = {.deviceAddress = 0x63, .subAddressSize = 1};

        std::shared_ptr<DriverGPIO> gpio;
        const unsigned short max_current_mA = 150;
        ColourTemperature currentColourTemp = warmest;

M module-bsp/board/rt1051/common/irq/irq_gpio.cpp => module-bsp/board/rt1051/common/irq/irq_gpio.cpp +29 -0
@@ 12,6 12,8 @@
#include "bsp/battery-charger/battery_charger.hpp"
#include "bsp/cellular/bsp_cellular.hpp"
#include "bsp/keyboard/keyboard.hpp"
#include "bsp/BoardDefinitions.hpp"
#include "bsp/magnetometer/magnetometer.hpp"

#if 0 // TODO:M.P implement the rest of BSP drivers



@@ 29,6 31,7 @@ namespace bsp
    void irq_gpio_Init(void)
    {
        DisableIRQ(GPIO1_Combined_0_15_IRQn);
        DisableIRQ(GPIO1_Combined_16_31_IRQn);
        DisableIRQ(GPIO2_Combined_0_15_IRQn);
        DisableIRQ(GPIO2_Combined_16_31_IRQn);
        DisableIRQ(GPIO3_Combined_16_31_IRQn);


@@ 45,6 48,9 @@ namespace bsp
        EnableIRQ(GPIO1_Combined_0_15_IRQn);
        NVIC_SetPriority(GPIO1_Combined_0_15_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY);

        EnableIRQ(GPIO1_Combined_16_31_IRQn);
        NVIC_SetPriority(GPIO1_Combined_16_31_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY);

        EnableIRQ(GPIO2_Combined_0_15_IRQn);
        NVIC_SetPriority(GPIO2_Combined_0_15_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY);



@@ 74,6 80,29 @@ namespace bsp
            portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
        }

        void GPIO1_Combined_16_31_IRQHandler(void)
        {
            BaseType_t xHigherPriorityTaskWoken = 0;
            uint32_t irq_mask                   = GPIO_GetPinsInterruptFlags(GPIO1);

            if (irq_mask & (1 << static_cast<uint32_t>(BoardDefinitions::MAGNETOMETER_IRQ))) {
                xHigherPriorityTaskWoken |= bsp::magnetometer::IRQHandler();
                LOG_DEBUG("magneto IRQ! >%s<",
                          GPIO_PinRead(GPIO1, static_cast<uint32_t>(BoardDefinitions::MAGNETOMETER_IRQ)) ? "high"
                                                                                                         : "low");
            }

            if (irq_mask & (1 << BSP_BLUETOOTH_UART_CTS_PIN)) {
                LOG_DEBUG("CTS IRQ!");
            }

            // Clear all IRQs
            GPIO_PortClearInterruptFlags(GPIO1, irq_mask);

            // Switch context if necessary
            portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
        }

        void GPIO2_Combined_0_15_IRQHandler(void)
        {
            BaseType_t xHigherPriorityTaskWoken = 0;

M module-bsp/board/rt1051/common/pin_mux.c => module-bsp/board/rt1051/common/pin_mux.c +10 -0
@@ 308,6 308,7 @@ void PINMUX_InitBootPins(void)
    PINMUX_InitJACKDET();
    PINMUX_InitVibrator();
    PINMUX_InitTorch();
    PINMUX_InitMagnetometer();
}

/*


@@ 1487,6 1488,15 @@ void PINMUX_InitTorch(void)
                            PAD_CONFIG_PULL_KEEPER_ENABLED | PAD_CONFIG_SELECT_KEEPER | PAD_CONFIG_HYSTERESIS_DISABLED);
}

void PINMUX_InitMagnetometer(void)
{
    IOMUXC_SetPinMux(PINMUX_MAGNETOMETER_IRQ_PIN, 0U);
    IOMUXC_SetPinConfig(PINMUX_MAGNETOMETER_IRQ_PIN,

                        PAD_CONFIG_SLEW_RATE_SLOW | PAD_CONFIG_DRIVER_DISABLED | PAD_CONFIG_SPEED_SLOW_50MHz |
                            PAD_CONFIG_PULL_KEEPER_ENABLED | PAD_CONFIG_SELECT_PULL | PAD_CONFIG_PULL_UP_22kOhm);
}

/***********************************************************************************************************************
 * EOF
 **********************************************************************************************************************/

M module-bsp/board/rt1051/common/pin_mux.h => module-bsp/board/rt1051/common/pin_mux.h +3 -0
@@ 217,6 217,9 @@ extern "C"
#define PINMUX_TORCH_EN_PIN IOMUXC_GPIO_AD_B1_05_GPIO1_IO21
    void PINMUX_InitTorch(void);

#define PINMUX_MAGNETOMETER_IRQ_PIN IOMUXC_GPIO_AD_B1_04_GPIO1_IO20
    void PINMUX_InitMagnetometer(void);

#if defined(__cplusplus)
}
#endif

M module-bsp/bsp/magnetometer/magnetometer.hpp => module-bsp/bsp/magnetometer/magnetometer.hpp +36 -12
@@ 1,23 1,47 @@
#pragma once

#include <stdint.h>
#include <bsp/keyboard/key_codes.hpp>

extern "C" {
	#include "FreeRTOS.h"
	#include "task.h"
	#include "queue.h"
#include <cstdint>
#include <optional>

extern "C"
{
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
}

#include "../common.hpp"

namespace bsp {
namespace bsp
{

namespace magnetometer{
    namespace magnetometer
    {

	int32_t init(xQueueHandle qHandle);
        int32_t init(xQueueHandle qHandle);

	bool isPresent(void);
	bsp::Board GetBoard(void);
}
        bool isPresent(void);

}
        bsp::Board GetBoard(void);

        /// unit: 4 LSB/Gauss
        struct Measurements
        {
            int16_t X;
            int16_t Y;
            int16_t Z;
        };

        /// returns a pair of <new_data_read?, values>
        std::pair<bool, Measurements> getMeasurement();

        bsp::KeyCodes parse(const Measurements &measurements);
        std::optional<bsp::KeyCodes> WorkerEventHandler();

        BaseType_t IRQHandler();
        void enableIRQ();
    } // namespace magnetometer

} // namespace bsp

M module-bsp/targets/Target_RT1051.cmake => module-bsp/targets/Target_RT1051.cmake +3 -2
@@ 95,8 95,9 @@ set(BOARD_SOURCES ${BOARD_SOURCES}
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/drivers/RT1051DriverDMAMux.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/drivers/RT1051DriverDMA.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/drivers/RT1051DriverGPIO.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/magnetometer/magnetometer.cpp"
	"${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/torch/torch.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/magnetometer/magnetometer.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/magnetometer/ALS31300.cpp"
    "${CMAKE_CURRENT_SOURCE_DIR}/board/rt1051/bsp/torch/torch.cpp"

	CACHE INTERNAL ""
)

M module-services/service-evtmgr/WorkerEvent.cpp => module-services/service-evtmgr/WorkerEvent.cpp +14 -2
@@ 43,7 43,7 @@ extern "C"
bool WorkerEvent::handleMessage(uint32_t queueID)
{

    QueueHandle_t queue = queues[queueID];
    xQueueHandle queue = queues[queueID];

    // service queue
    if (queueID == static_cast<uint32_t>(WorkerEventQueues::queueService)) {


@@ 173,6 173,18 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
        }
    }

    if (queueID == static_cast<uint32_t>(WorkerEventQueues::queueMagnetometerIRQ)) {
        uint8_t notification;
        if (xQueueReceive(queue, &notification, 0) != pdTRUE) {
            return false;
        }

        if (std::optional<bsp::KeyCodes> key = bsp::magnetometer::WorkerEventHandler()) {
            LOG_DEBUG("magneto IRQ handler: %s", c_str(*key));
            processKeyEvent(bsp::KeyEvents::Pressed, *key);
        }
    }

    return true;
}



@@ 189,7 201,7 @@ bool WorkerEvent::init(std::list<sys::WorkerQueueInfo> queues)
    bsp::rtc_Init(qhandles[static_cast<int32_t>(WorkerEventQueues::queueRTC)]);
    bsp::harness::Init(qhandles[static_cast<int32_t>(WorkerEventQueues::queueHarness)]);
    bsp::cellular::init(qhandles[static_cast<int32_t>(WorkerEventQueues::queueCellular)]);
    bsp::magnetometer::init(qhandles[static_cast<int32_t>(WorkerEventQueues::queueMagnetometer)]);
    bsp::magnetometer::init(qhandles[static_cast<int32_t>(WorkerEventQueues::queueMagnetometerIRQ)]);
    bsp::torch::init(qhandles[static_cast<int32_t>(WorkerEventQueues::queueTorch)]);

    time_t timestamp;

M module-services/service-evtmgr/WorkerEvent.hpp => module-services/service-evtmgr/WorkerEvent.hpp +1 -1
@@ 37,7 37,7 @@ enum class WorkerEventQueues
    queueRTC,
    queueHarness,
    queueCellular,
    queueMagnetometer,
    queueMagnetometerIRQ,
    queueTorch,
};


M module-utils/Utils.hpp => module-utils/Utils.hpp +11 -0
@@ 182,4 182,15 @@ namespace utils

        return false;
    }

    static inline uint32_t swapBytes(uint32_t toSwap)
    {
#ifdef __GNUC__
        return __builtin_bswap32(toSwap);
#else
        return ((((toSwap)&0xff000000) >> 24) | (((toSwap)&0x00ff0000) >> 8) | (((toSwap)&0x0000ff00) << 8) |
                (((toSwap)&0x000000ff) << 24));
#endif
    }

} // namespace utils

M module-utils/test/unittest_utils.cpp => module-utils/test/unittest_utils.cpp +28 -0
@@ 180,3 180,31 @@ TEST_CASE("Get value from string")
        REQUIRE(testValue == target);
    }
}

TEST_CASE("Swap endianness")
{
    uint32_t as_long = 0x11223344;

    SECTION("endiannes check")
    {
        uint8_t *as_array;
        as_array = reinterpret_cast<uint8_t *>(&as_long);
        if (as_array[0] == 0x11) {
            REQUIRE(BYTE_ORDER == BIG_ENDIAN);
        }
        else if (as_array[0] == 0x44) {
            REQUIRE(BYTE_ORDER == LITTLE_ENDIAN);
        }
        else {
            FAIL("cannot determine endiannes");
        }
    }
    SECTION("swap endiannes uint32")
    {
        uint32_t as_long_swapped = utils::swapBytes(as_long);
        REQUIRE(((as_long >> 8 * 3) & 0xFF) == ((as_long_swapped >> 8 * 0) & 0xFF));
        REQUIRE(((as_long >> 8 * 2) & 0xFF) == ((as_long_swapped >> 8 * 1) & 0xFF));
        REQUIRE(((as_long >> 8 * 1) & 0xFF) == ((as_long_swapped >> 8 * 2) & 0xFF));
        REQUIRE(((as_long >> 8 * 0) & 0xFF) == ((as_long_swapped >> 8 * 3) & 0xFF));
    }
}