Library for interfacing with the RM3100 magnetometer from PNI Sensor Corporation

Dependents:   Hello_RM3100 RM3100

Rm3100.hpp

Committer:
fwrawx
Date:
2018-05-24
Revision:
1:8646c63a8dbe
Parent:
0:b6ab7f28cde2

File content as of revision 1:8646c63a8dbe:

#ifndef RM3100_HPP
#define RM3100_HPP

#include <stdint.h>
#include "mbed.h"

/**
 * An interface for the RM3100 3-axis magnetometer from PNI Sensor Corp.
 *
 * @code
 * #include "mbed.h"
 * #include "Rm3100.hpp"
 * 
 * int main(void)
 * {
 *     Serial pc(USBTX, USBRX);
 *     pc.baud(115200);
 * 
 *     printf("### Hello RM3100 ###\r\n");
 * 
 *     int addr = ((Rm3100::RM3100_ADDR | Rm3100::RM3100_ADDR_SSN) << 1);
 *     struct Rm3100::Status status = { 0 };
 *     struct Rm3100::Sample sample = { 0 };
 * 
 *     Rm3100 sensor(I2C_SDA, I2C_SCL, addr);
 * 
 *     sensor.Begin();
 *     osDelay(1);
 * 
 *     sensor.SetCycleCounts(200);
 *     osDelay(1);
 * 
 *     sensor.SetRate(100.0f);
 *     osDelay(1);
 * 
 *     sensor.SetContinuousMeasurementMode(true);
 *     osDelay(1);
 * 
 *     while (true) {
 *         sensor.GetStatus(&status);
 *         if (status.drdy) {
 *             sensor.GetSample(&sample);
 *             printf("x: %f, y: %f, z: %f\r\n", sample.x, sample.y, sample.z);
 *         }
 *         osDelay(10);
 *     }
 * }
 * @endcode
 */
class Rm3100
{
public:

    enum ReturnCode {
        RM3100_RET_EIO = -22,
        RM3100_RET_ENODEV = -19,
        RM3100_RET_EINVAL = -5,
        RM3100_RET_OK = 0,
    };

    enum I2CAddress {
        RM3100_ADDR_SA0 =  0x01, // address bit 0
        RM3100_ADDR_SA1 =  0x02, // address bit 1
        RM3100_ADDR_MASK = 0x03,
        RM3100_ADDR =      0x20, // 7-bit base address
        RM3100_ADDR_SSN = RM3100_ADDR_SA0, // SSN high = address bit 0
        RM3100_ADDR_SO = RM3100_ADDR_SA1, // SO high = address bit 1
    };

    enum Register {
        RM3100_REG_POLL =   0x00, // polls for a single measurement
        RM3100_REG_CMM =    0x01, // initiates continuous measurement mode
        RM3100_REG_CCX =    0x04, // cycle counts -- X axis
        RM3100_REG_CCY =    0x06, // cycle counts -- Y axis
        RM3100_REG_CCZ =    0x08, // cycle counts -- Z axis
        RM3100_REG_TMRC =   0x0B, // sets continuous mode data rate
        RM3100_REG_MX =     0x24, // measurement results -- X axis
        RM3100_REG_MY =     0x27, // measurement results -- Y axis
        RM3100_REG_MZ =     0x2A, // measurement results -- Z axis
        RM3100_REG_BIST =   0x33, // built-in self test
        RM3100_REG_STATUS = 0x34, // status of DRDY
        RM3100_REG_HSHAKE = 0x35, // handshake register
        RM3100_REG_REVID =  0x36, // MagI2C revision identification
    };

    struct SingleMeasurementMode {
        uint8_t res0:4;
        uint8_t pmx:1;
        uint8_t pmy:1;
        uint8_t pmz:1;
        uint8_t res7:1;
    };

    enum DataReadyMode {
        RM3100_DRDM_RES0 =     0x00, // reserved
        RM3100_DRDM_ANY_AXES = 0x01, // drdy on measurement complete on any axis
        RM3100_DRDM_ALL_AXES = 0x02, // drdy on measurement complete on all axes
        RM3100_DRDM_MASK =     0x03,
    };

    struct ContinuousMeasurementMode {
        uint8_t start:1;   // continuous measurement mode enabled
        uint8_t res1:1;
        uint8_t drdm:2;    // data ready mode
        uint8_t cmx:1;     // X axis measurement enabled in CMM
        uint8_t cmy:1;     // Y axis measurement enabled in CMM
        uint8_t cmz:1;     // Z axis measurement enabled in CMM
        uint8_t res7:1;
    };

    struct CycleCounts {
        uint16_t x;
        uint16_t y;
        uint16_t z;
    };

    enum ContinuousMeasurementModeUpdateRate {
        RM3100_CMM_RATE_600_0_HZ = 0x02, //   ~600 Hz
        RM3100_CMM_RATE_300_0_HZ = 0x03, //   ~300 Hz
        RM3100_CMM_RATE_150_0_HZ = 0x04, //   ~150 Hz
        RM3100_CMM_RATE_75_0_HZ =  0x05, //    ~75 Hz
        RM3100_CMM_RATE_37_0_HZ =  0x06, //    ~37 Hz
        RM3100_CMM_RATE_18_0_HZ =  0x07, //    ~18 Hz
        RM3100_CMM_RATE_9_0_HZ =   0x08, //     ~9 Hz
        RM3100_CMM_RATE_4_5_HZ =   0x09, //   ~4.5 Hz
        RM3100_CMM_RATE_2_3_HZ =   0x0A, //   ~2.3 Hz
        RM3100_CMM_RATE_1_2_HZ =   0x0B, //   ~1.2 Hz
        RM3100_CMM_RATE_0_6_HZ =   0x0C, //   ~0.6 Hz
        RM3100_CMM_RATE_0_3_HZ =   0x0D, //   ~0.3 Hz
        RM3100_CMM_RATE_0_015_HZ = 0x0E, // ~0.015 Hz
        RM3100_CMM_RATE_0_075_HZ = 0x0F, // ~0.075 Hz
        RM3100_CMM_RATE_MASK = RM3100_CMM_RATE_0_075_HZ,
        RM3100_CMM_RATE_MSB =      0x90,
    };

    enum StatusFlag {
        RM3100_STATUS_FLAG_DRDY = (1 << 7),
    };

    struct Status {
        uint8_t res0:7;
        uint8_t drdy:1;
    };

    struct Measurement {
        int32_t x;
        int32_t y;
        int32_t z;
    };

    struct Sample {
        float x;
        float y;
        float z;
    };

    struct MeasurementScale {
        float x;
        float y;
        float z;
        MeasurementScale(float _x, float _y, float _z) : x(_x), y(_y), z(_z) {}
    };

    enum SelfTestCount {
        RM3100_SELF_TEST_COUNT_1 = 0x01, // 1 LR periods
        RM3100_SELF_TEST_COUNT_2 = 0x02, // 2 LR periods
        RM3100_SELF_TEST_COUNT_4 = 0x03, // 4 LR periods
        RM3100_SELF_TEST_COUNT_MASK = RM3100_SELF_TEST_COUNT_4,
    };

    enum SelfTestTimeout {
        RM3100_SELF_TEST_TIMEOUT_30_US = 0x01,  // 1 cycle  --  30 µs
        RM3100_SELF_TEST_TIMEOUT_60_US = 0x02,  // 2 cycles --  60 µs
        RM3100_SELF_TEST_TIMEOUT_120_US = 0x03, // 4 cycles -- 120 µs
        RM3100_SELF_TEST_TIMEOUT_MASK = RM3100_SELF_TEST_TIMEOUT_120_US,
    };

    struct SelfTestConfig {
        uint8_t bp:2;  // test count (LR periods)
        uint8_t bw:2;  // timeout (sleep oscillation cycles)
        uint8_t xok:1; // X result -- 0 = error, 1 = ok
        uint8_t yok:1; // Y result -- 0 = error, 1 = ok
        uint8_t zok:1; // Z result -- 0 = error, 1 = ok
        uint8_t ste:1; // self test enable -- 0 = disable, 1 = enable
    };

    struct HandShakeConfig {
        uint8_t drc0:1;  // clear drdy on any register write
        uint8_t drc1:1;  // clear drdy on measurement read
        uint8_t res2:1;  // 0
        uint8_t res3:1;  // 1
        uint8_t nack0:1; // 1 when undef register write
        uint8_t nack1:1; // 1 when write SMM when CMM or visa versa
        uint8_t nack2:1; // 1 when measurement read before data ready
    };

    static const uint8_t RM3100_REVID = 0x22;

    /**
     * Creates a new instance attached to the specified I2C pins and address
     *
     * @param sda I2C data pin
     * @param scl I2C clock pin
     * @param addr 8-bit I2C address
     */
    Rm3100(PinName sda, PinName scl, uint8_t addr);

    virtual ~Rm3100();

    /**
     * Checks hardware id and sets the data scale
     *
     * @return 0 on success
     */
    int Begin(void);

    /**
     * Gets the current single measurement mode config
     *
     * @param smm pointer to read config into
     * @return 0 on success
     */
    int GetSingleMeasurementMode(struct SingleMeasurementMode *smm);

    /**
     * Sets the current single measurement mode config
     *
     * @param smm pointer to write config from
     * @return 0 on success
     */
    int SetSingleMeasurementMode(struct SingleMeasurementMode *smm);

    /**
     * Sets the current single measurement mode config
     *
     * @param x enable measurement on X axis
     * @param y enable measurement on Y axis
     * @param z enable measurement on Z axis
     * @return 0 on success
     */
    int SetSingleMeasurementMode(bool x, bool y, bool z);

    /**
     * Gets the current continuous measurement mode config
     *
     * @param cmm pointer to read config into
     * @return 0 on success
     */
    int GetContinuousMeasurementMode(struct ContinuousMeasurementMode *cmm);

    /**
     * Sets the current continuous measurement mode config
     *
     * @param cmm pointer to write config from
     * @return 0 on success
     */
    int SetContinuousMeasurementMode(struct ContinuousMeasurementMode *cmm);

    /**
     * Sets the current continuous measurement mode config
     *
     * @param enabled enable CMM -- true = enabled, false = disabled
     * @param drdm data ready mode
     * @param x enable measurement on X axis
     * @param y enable measurement on Y axis
     * @param z enable measurement on Z axis
     * @return 0 on success
     */
    int SetContinuousMeasurementMode(bool enabled, uint8_t drdm, bool x,
                                     bool y, bool z);

    /**
     * Sets the current continuous measurement mode config
     *
     * @param enabled enable CMM -- true = enabled on all axes, false = disabled
     * @return 0 on success
     */
    int SetContinuousMeasurementMode(bool enabled);

    /**
     * Gets the current cycle counts
     *
     * @param cc pointer to read into
     * @return 0 on success
     */
    int GetCycleCounts(struct CycleCounts *cc);

    /**
     * Sets the current cycle counts
     *
     * @param cc pointer to write from
     * @return 0 on success
     */
    int SetCycleCounts(struct CycleCounts *cc);

    /*
     * Sets the current cycle counts (x, y, z)
     *
     * @param x cycle counts for X axis
     * @param y cycle counts for Y axis
     * @param z cycle counts for Z axis
     * @return 0 on success
     */
    int SetCycleCounts(uint16_t x, uint16_t y, uint16_t z);

    /*
     * Sets the current cycle counts (x = y, z)
     *
     * @param xy cycle counts for X and Y axes
     * @param z cycle counts for Z axis
     * @return 0 on success
     */
    int SetCycleCounts(uint16_t xy, uint16_t z);

    /*
     * Sets the current cycle counts (x = y = z)
     *
     * @param xyz cycle counts for all axes
     * @return 0 on success
     */
    int SetCycleCounts(uint16_t xyz);

    /**
     * Updates the measurement scale based on the given cycle counts
     *
     * @param cc pointer to cycle counts
     * @return 0 on success
     */
    void UpdateMeasurementScale(struct CycleCounts *cc);

    /**
     * Gets the continuous mode update rate
     *
     * @param tmrc pointer to read into
     * @return 0 on success
     */
    int GetContinuousMeasurementModeUpdateRate(uint8_t *tmrc);

    /**
     * Sets the continuous mode update rate (TMRC)
     *
     * @param tmrc rate to set according to TMRC table
     * @return 0 on success
     */
    int SetContinuousMeasurementModeUpdateRate(uint8_t tmrc);

    /**
     * Sets the contiunous mode update rate (Hz)
     *
     * @param rate rate to set in Hz
     * @return 0 on success
     */
    int SetRate(float rate);

    /**
     * Gets the current status
     *
     * @param status pointer to read into
     * @return 0 on success
     */
    int GetStatus(struct Status *status);

    /**
     * Gets the current measurement data (24-bit signed)
     *
     * @param m pointer to read into
     * @return 0 on success
     */
    int GetMeasurement(struct Measurement *m);

    /**
     * Gets the current measurement sample (scale to µT)
     *
     * @param s pointer to read into
     * @return 0 on success
     */
    int GetSample(struct Sample *s);

    /**
     * Gets the current self-test config/result
     *
     * @param cfg pointer to read into
     * @return 0 on success
     */
    int GetSelfTestConfig(struct SelfTestConfig *cfg);

    /**
     * Sets the current self-test config
     *
     * @param cfg pointer to write from
     * @return 0 on success
     */
    int SetSelfTestConfig(struct SelfTestConfig *cfg);

    /**
     * Sets the current self-test config
     *
     * @param count LR periods
     * @param timeout sleep oscillation cycles
     * @return 0 on success
     */
    int SetSelfTestConfig(uint8_t count, uint8_t timeout, bool enabled);

    /**
     * Performs a self-test, returning result
     *
     * @param count LR periods
     * @param timeout sleep oscillation cycles
     * @param result pointer to read result into
     * @return 0 on success
     */
    int PerformSelfTest(uint8_t count, uint8_t timeout,
                        struct SelfTestConfig *result);

    /**
     * Clears the self-test config
     *
     * @return 0 on success
     */
    int ClearSelfTest();

    /**
     * Gets the current handshake config
     *
     * @param cfg pointer to read into
     * @return 0 on success
     */
    int GetHandShakeConfig(struct HandShakeConfig *cfg);

    /**
     * Sets the current handshake config
     *
     * @param cfg pointer to write from
     * @return 0 on success
     */
    int SetHandShakeConfig(struct HandShakeConfig *cfg);

    /**
     * Sets the current data ready config
     *
     * @param on_write 1 = drdy cleared on any register write
     * @param on_read_measurement 1 = drdy cleared on measurement register read
     * @return 0 on success
     */
    int SetDrdyClearConfig(bool on_write, bool on_read_measurement);

    /**
     * Gets the current hardware revision
     *
     * @param rev pointer to read into
     * @return 0 on success
     */
    int GetHardwareRevision(uint8_t *rev);

private:
    I2C _i2c;
    uint8_t _addr;
    struct MeasurementScale _scale;
    int _sample_delay_ms;
    int Read(uint8_t reg, uint8_t *buffer, uint8_t count);
    int Write(uint8_t reg, uint8_t *buffer, uint8_t count);
};

#endif /* RM3100_HPP */