~aleteoryx/muditaos

ba0e0345b7bf2a0cbd5e3e1ef8b1b93f4fbb60c1 — Bartosz Cichocki 4 years ago abcc8d7
[EGD-6868] Restore magnetometer polling mode

To be able to properly operate with the slider under every
circumstance the polling mode has to be restored. It's related
to the bug in the magnetometer chip, which is not described in the
datasheet.
This reverts commit 744eef1d9b39cdf2cdbc35f57d6182f5e7942c2f with
few small fixes
M module-bsp/board/linux/magnetometer/magnetometer.cpp => module-bsp/board/linux/magnetometer/magnetometer.cpp +1 -5
@@ 35,11 35,7 @@ namespace bsp

        void enableIRQ()
        {}
        void initFirstReadout()
        void resetCurrentParsedValue()
        {}
        bsp::KeyCodes getCurrentSliderPosition()
        {
            return bsp::KeyCodes::Undefined;
        }
    } // namespace magnetometer
} // namespace bsp

M module-bsp/board/rt1051/bsp/magnetometer/ALS31300.hpp => module-bsp/board/rt1051/bsp/magnetometer/ALS31300.hpp +2 -22
@@ 17,7 17,7 @@ namespace drivers::als31300
    using whole_reg_t = uint32_t; // ALS31300 always talks 4 bytes

    constexpr auto I2C_ADDRESS = 0x64;
    constexpr auto DUMMY_BYTE  = 1;

    // base ALS31300 register struct.
    struct base_reg
    {


@@ 109,33 109,13 @@ namespace drivers::als31300
    constexpr auto PWR_REG_LOOP_MODE_fast_loop = 0b01;
    constexpr auto PWR_REG_LOOP_MODE_full_loop = 0b10;

    enum class PWR_REG_SLEEP_MODE
    enum PWR_REG_SLEEP_MODE
    {
        active          = PWR_REG_SLEEP_MODE_active,
        sleep           = PWR_REG_SLEEP_MODE_sleep,
        periodic_active = PWR_REG_SLEEP_MODE_LPDCM,
    };
    enum class LPDCM_INACTIVE_TIME
    {
        inactive_500us,
        inactive_1ms,
        inactive_5ms,
        inactive_10ms,
        inactive_50ms,
        inactive_100ms,
        inactive_500ms,
        inactive_1s
    };

    enum class BANDWIDTH_SELECT
    {
        bandwidth_3500Hz = 0,
        bandwidth_7kHz   = 1,
        bandwidth_14kHz  = 2,
        bandwidth_10kHz  = 4,
        bandwidth_20kHz  = 5,
        bandwidth_40kHz  = 6
    };
    constexpr auto PWR_ON_DELAY_MS = 1; //  spec'd as 600µs at most

    // --------

D module-bsp/board/rt1051/bsp/magnetometer/doc/magnetometer_values.png => module-bsp/board/rt1051/bsp/magnetometer/doc/magnetometer_values.png +0 -0
D module-bsp/board/rt1051/bsp/magnetometer/doc/readme.md => module-bsp/board/rt1051/bsp/magnetometer/doc/readme.md +0 -27
@@ 1,27 0,0 @@
# ALS31300 Hall effect sensor
This magnetometer is used as a detector of the slider position, which has a magnet attached to itself. When slider position changes,
the magnetic field intensity measured by the sensor is changing and is properly interpreted to determine the position of the slider.

## Values per position
![Magnetometer values](./magnetometer_values.png)
<center><b>Positions: down, middle, up, middle</b></center>

As shown on the graph, using only X and Y axes can clearly determine the slider position so this has been implemented.

## Magnetometer configuration
Looking at the graph it's easy to find out, that each slider position has some mean value of the magnetic field intensity. This particular
sensor has the possibility to generate an interrupt on its pin when the absolute threshold has been exceeded or the increment of
the intensity related to the last taken measurement was higher than a certain, set value.

In this case, increment (delta) mode has been used, combined with Low Power Delta Compare Mode (LPDCM) - 
magnetometer cycles between active and inactive state and takes measurements. When the change in the magnetic field has been higher than
set value, it generates an interrupt for the main processor, which then retrieves the measurements and does proper action.

## Caveats

- once the magnetometer has been put to the LPDCM mode it cannot be read nor written until the interrupt happens - despite the datasheet which claims,
that the device should respond to the I2C commands, the only answer to every one command was NAK,
- setting a too small period for LPDCM mode (too small inactivity time) caused some instability during tests, 
- to get proper measurements after receiving the interrupt, there's a need to put the magnetometer to the active state,
  then take measurements and put it again to the LPDCM mode - doing anything else means the measurements data was inaccurate and way off.


M module-bsp/board/rt1051/bsp/magnetometer/magnetometer.cpp => module-bsp/board/rt1051/bsp/magnetometer/magnetometer.cpp +84 -50
@@ 34,6 34,27 @@ namespace bsp
{
    namespace magnetometer
    {
        enum class LPDCM_INACTIVE_TIME
        {
            inactive_500us,
            inactive_1ms,
            inactive_5ms,
            inactive_10ms,
            inactive_50ms,
            inactive_100ms,
            inactive_500ms,
            inactive_1s
        };

        enum class BANDWIDTH_SELECT
        {
            bandwidth_3500Hz = 0,
            bandwidth_7kHz   = 1,
            bandwidth_14kHz  = 2,
            bandwidth_10kHz  = 4,
            bandwidth_20kHz  = 5,
            bandwidth_40kHz  = 6
        };
        namespace
        {
            bool isTimeToCompleteWriteDefinedForRegistry(std::uint8_t address)


@@ 41,28 62,29 @@ namespace bsp
                const auto it = std::find(als31300::EEPROM_REGS.begin(), als31300::EEPROM_REGS.end(), address);
                return it != als31300::EEPROM_REGS.end();
            }
            auto getThresholdLevel(std::uint16_t gaussThreshold) -> std::uint8_t
            {
                return floor(((gaussThreshold + 1) / 32.0) - 1); // calculation formula taken from ALS datasheet
            }
        } // namespace

        std::shared_ptr<DriverGPIO> gpio;

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

        bool setActive(als31300::PWR_REG_SLEEP_MODE sleep_mode);
        static TimerHandle_t timerHandle;
        static constexpr uint16_t MAGNETOMETER_POLL_INTERVAL_MS = 500;

        bsp::KeyCodes getCurrentSliderPosition()
        static void TimerHandler(TimerHandle_t xTimer)
        {
            return current_parsed;
            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;
            auto readBytes  = i2c->Read(addr, i2c_buf.buf, sizeof(als31300::whole_reg_t));
            if (readBytes != sizeof(als31300::whole_reg_t)) {
            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__


@@ 99,9 121,11 @@ namespace bsp

            // 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_WARN("magneto: Customer access already unlocked or write error");
                LOG_ERROR("magneto: CANNOT INIT SLIDER SENSOR");
                return kStatus_Fail;
            }

            // CONFIGURATION register read


@@ 115,12 139,10 @@ namespace bsp
            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          = static_cast<std::uint8_t>(als31300::BANDWIDTH_SELECT::bandwidth_7kHz);
            reg_conf.bandwidth          = static_cast<uint8_t>(BANDWIDTH_SELECT::bandwidth_7kHz);
            if (current_reg_conf != reg_conf) {
                if (!i2cWrite(als31300::CONF_REG, reg_conf)) {
                    LOG_ERROR("magneto: CANNOT INIT SLIDER SENSOR");
                    return kStatus_Fail;
                }
                [[maybe_unused]] auto ret = i2cWrite(als31300::CONF_REG, reg_conf);
                assert(ret);
                LOG_DEBUG("CONF wrote:\t%" PRIu32, static_cast<uint32_t>(reg_conf));

                i2cRead(als31300::CONF_REG, read_reg);


@@ 136,16 158,17 @@ namespace bsp
            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_delta;
            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_enabled;
            reg_int.int_Y_en             = als31300::INT_REG_INT_CHANNEL_enabled;
            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      = getThresholdLevel(32);
            reg_int.int_Y_threshold      = getThresholdLevel(128);
            reg_int.int_X_threshold      = 1;
            reg_int.int_Y_threshold      = 4;
            reg_int.int_Z_threshold      = 0;
            if (current_reg_int != reg_int) {
                i2cWrite(als31300::INT_REG, reg_int);
                [[maybe_unused]] auto ret = i2cWrite(als31300::INT_REG, reg_int);
                assert(ret);
                LOG_DEBUG("INT wrote:\t%" PRIu32, static_cast<uint32_t>(reg_int));

                i2cRead(als31300::INT_REG, read_reg);


@@ 165,8 188,17 @@ namespace bsp
                                              .irqMode  = DriverGPIOPinParams::InterruptMode::IntFallingEdge,
                                              .defLogic = 0,
                                              .pin      = static_cast<uint32_t>(BoardDefinitions::MAGNETOMETER_IRQ)});

            enableIRQ();
            // 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);


@@ 175,12 207,13 @@ namespace bsp
            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 = static_cast<std::uint8_t>(als31300::LPDCM_INACTIVE_TIME::inactive_100ms);
            reg_pwr.count_max_LP_mode = static_cast<uint8_t>(LPDCM_INACTIVE_TIME::inactive_10ms);

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

            initFirstReadout();
            xTimerStart(timerHandle, 1000);

            return kStatus_Success;
        }



@@ 197,21 230,30 @@ namespace bsp

            if (reg_msb.int_flag == true) {
                LOG_DEBUG("magneto: INT flag in register");
                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);
            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;
                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);
                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);
                return std::pair(true, meas);
            }
        }

        bool setActive(als31300::PWR_REG_SLEEP_MODE sleep_mode)


@@ 220,14 262,12 @@ namespace bsp
            als31300::whole_reg_t read_reg;

            if (!i2cRead(als31300::PWR_REG, read_reg)) {
                LOG_ERROR("setActive: cannot read PWR REG!");
                return false;
            }
            als31300::pwr_reg reg_pwr = read_reg;
            reg_pwr.sleep             = static_cast<std::uint8_t>(sleep_mode);
            reg_pwr.sleep             = sleep_mode;

            if (!i2cWrite(als31300::PWR_REG, reg_pwr)) {
                LOG_ERROR("setActive: cannot write PWR REG!");
                return false;
            }
            if (sleep_mode == als31300::PWR_REG_SLEEP_MODE::active ||


@@ 243,10 283,7 @@ namespace bsp
            addr.subAddress = 0x00;
            auto read       = i2c->Read(addr, &buf, 1);

            if (read != 1) {
                return false;
            }
            return true;
            return read == 1;
        }

        bsp::KeyCodes parse(const Measurements &measurements)


@@ 280,21 317,17 @@ namespace bsp
            }
            return bsp::KeyCodes::Undefined;
        }
        void initFirstReadout()
        void resetCurrentParsedValue()
        {
            current_parsed = bsp::KeyCodes::Undefined;
            if (qHandleIrq != NULL) {
                std::uint8_t val           = als31300::DUMMY_BYTE;
                constexpr auto ticksToWait = 1000;
                xQueueSend(qHandleIrq, &val, pdMS_TO_TICKS(ticksToWait));
            }
        }

        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::periodic_active);
            setActive(als31300::PWR_REG_SLEEP_MODE::sleep);
            if (new_data) {
                auto incoming_parsed = parse(measurement);
                if (incoming_parsed != bsp::KeyCodes::Undefined and incoming_parsed != current_parsed) {


@@ 307,9 340,10 @@ namespace bsp

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

M module-bsp/board/rt1051/drivers/RT1051DriverI2C.cpp => module-bsp/board/rt1051/drivers/RT1051DriverI2C.cpp +0 -2
@@ 50,7 50,6 @@ namespace drivers
        auto ret = BOARD_LPI2C_Send(
            base, addr.deviceAddress, addr.subAddress, addr.subAddressSize, const_cast<uint8_t *>(txBuff), size);
        if (ret != kStatus_Success) {
            LOG_ERROR("I2C Write: Error %ld", ret);
            return -1; // TODO:M.P: fix me
        }
        else {


@@ 63,7 62,6 @@ namespace drivers
        cpp_freertos::LockGuard lock(mutex);
        auto ret = BOARD_LPI2C_Receive(base, addr.deviceAddress, addr.subAddress, addr.subAddressSize, rxBuff, size);
        if (ret != kStatus_Success) {
            LOG_ERROR("I2C Read: Error %ld", ret);
            return -1; // TODO:M.P: fix me
        }
        else {

M module-bsp/bsp/BoardDefinitions.hpp => module-bsp/bsp/BoardDefinitions.hpp +2 -3
@@ 18,9 18,8 @@ enum class BoardDefinitions

    USB_FUNCTION_MUX_SELECT = 25, // GPIO_AD_B1_09, USB_MUX_SEL0
    USB_POWER_ACK           = 3,  // GPIO_B0_03  Note: pull-up in order to read
    I2C_STD_BAUDRATE   = 100000,

    I2C_STD_BAUDRATE = 100000,
    I2C_FAST_BAUDRATE = 400000,
    AUDIOCODEC_I2C_BAUDRATE   = I2C_STD_BAUDRATE,
    AUDIOCODEC_I2C            = static_cast<int>(drivers::I2CInstances ::I2C2),
    AUDIOCODEC_DMAMUX         = static_cast<int>(drivers::DMAMuxInstances ::DMAMUX0),


@@ 107,7 106,7 @@ enum class BoardDefinitions
    VIBRATOR_EN = 0, // GPIO_AD_B0_00

	MAGNETOMETER_I2C = AUDIOCODEC_I2C,
	MAGNETOMETER_I2C_BAUDRATE = I2C_FAST_BAUDRATE,
	MAGNETOMETER_I2C_BAUDRATE = AUDIOCODEC_I2C_BAUDRATE,
    MAGNETOMETER_GPIO = static_cast<int>(drivers::GPIOInstances::GPIO_1),
    MAGNETOMETER_IRQ = 20, // GPIO_AD_B1_04


M module-bsp/bsp/magnetometer/magnetometer.hpp => module-bsp/bsp/magnetometer/magnetometer.hpp +1 -2
@@ 39,8 39,7 @@ namespace bsp

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

        BaseType_t IRQHandler();
        void enableIRQ();

M module-services/service-evtmgr/WorkerEvent.cpp => module-services/service-evtmgr/WorkerEvent.cpp +5 -1
@@ 184,7 184,10 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
        if (!queue->Dequeue(&notification, 0)) {
            return false;
        }
        processKeyEvent(bsp::KeyEvents::Pressed, bsp::magnetometer::getCurrentSliderPosition());

        bsp::magnetometer::resetCurrentParsedValue();
        LOG_WARN("Received notify, current value reset!");
        handleMagnetometerEvent();
    }

    if (queueID == static_cast<uint32_t>(WorkerEventQueues::queueMagnetometerIRQ)) {


@@ 244,6 247,7 @@ bool WorkerEvent::init(std::list<sys::WorkerQueueInfo> queuesList)

    auto sentinelRegistrationMsg = std::make_shared<sys::SentinelRegistrationMessage>(cpuSentinel);
    service->bus.sendUnicast(std::move(sentinelRegistrationMsg), service::name::system_manager);

    return true;
}