diff --git a/examples/hmc5883l/Makefile b/examples/hmc5883l/Makefile new file mode 100644 index 0000000..c98c6c9 --- /dev/null +++ b/examples/hmc5883l/Makefile @@ -0,0 +1,4 @@ +PROGRAM = hmc5883l +EXTRA_COMPONENTS = extras/i2c extras/hmc5883l +#ESPBAUD = 460800 +include ../../common.mk diff --git a/examples/hmc5883l/main.c b/examples/hmc5883l/main.c new file mode 100644 index 0000000..c4b0bf4 --- /dev/null +++ b/examples/hmc5883l/main.c @@ -0,0 +1,41 @@ +/* + * Example of using HMC5883L driver + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include +#include +#include +#include +#include + +#define SCL_PIN 5 +#define SDA_PIN 4 + +void user_init(void) +{ + uart_set_baud(0, 115200); + printf("SDK version:%s\n\n", sdk_system_get_sdk_version()); + + i2c_init(SCL_PIN, SDA_PIN); + + while (!hmc5883l_init()) + printf("Device not found\n"); + + hmc5883l_set_operating_mode(HMC5883L_MODE_CONTINUOUS); + hmc5883l_set_samples_averaged(HMC5883L_SAMPLES_8); + hmc5883l_set_data_rate(HMC5883L_DATA_RATE_07_50); + hmc5883l_set_gain(HMC5883L_GAIN_1090); + + while (true) + { + hmc5883l_data_t data; + hmc5883l_get_data(&data); + printf("Magnetic data: X:%.2f mG, Y:%.2f mG, Z:%.2f mG\n", data.x, data.y, data.z); + + for (uint32_t i = 0; i < 1000; i++) + sdk_os_delay_us(250); + } +} diff --git a/extras/hmc5883l/component.mk b/extras/hmc5883l/component.mk new file mode 100644 index 0000000..c3fad9c --- /dev/null +++ b/extras/hmc5883l/component.mk @@ -0,0 +1,9 @@ +# Component makefile for extras/hmc5883l + +# expected anyone using this driver includes it as 'hmc5883l/hmc5883l.h' +INC_DIRS += $(hmc5883l_ROOT).. + +# args for passing into compile rule generation +hmc5883l_SRC_DIR = $(hmc5883l_ROOT) + +$(eval $(call component_compile_rules,hmc5883l)) diff --git a/extras/hmc5883l/hmc5883l.c b/extras/hmc5883l/hmc5883l.c new file mode 100644 index 0000000..28d9cf7 --- /dev/null +++ b/extras/hmc5883l/hmc5883l.c @@ -0,0 +1,189 @@ +/* + * Driver for 3-axis digital compass HMC5883L + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#include "hmc5883l.h" +#include +#include + +#define ADDR 0x1e + +#define REG_CR_A 0x00 +#define REG_CR_B 0x01 +#define REG_MODE 0x02 +#define REG_DX_H 0x03 +#define REG_DX_L 0x04 +#define REG_DZ_H 0x05 +#define REG_DZ_L 0x06 +#define REG_DY_H 0x07 +#define REG_DY_L 0x08 +#define REG_STAT 0x09 +#define REG_ID_A 0x0a +#define REG_ID_B 0x0b +#define REG_ID_C 0x0c + +#define BIT_MA 5 +#define BIT_DO 2 +#define BIT_GN 5 + +#define MASK_MD 0x03 +#define MASK_MA 0x60 +#define MASK_DO 0x1c +#define MASK_MS 0x03 +#define MASK_DR 0x01 +#define MASK_DL 0x02 + +#define MEASUREMENT_TIMEOUT 6000 + +static const float gain_values [] = { + [HMC5883L_GAIN_1370] = 0.73, + [HMC5883L_GAIN_1090] = 0.92, + [HMC5883L_GAIN_820] = 1.22, + [HMC5883L_GAIN_660] = 1.52, + [HMC5883L_GAIN_440] = 2.27, + [HMC5883L_GAIN_390] = 2.56, + [HMC5883L_GAIN_330] = 3.03, + [HMC5883L_GAIN_230] = 4.35 +}; + +static float current_gain; +static hmc5883l_operating_mode_t current_mode; + +static inline void write_register(uint8_t reg, uint8_t val) +{ + uint8_t buf[2] = { reg, val }; + i2c_slave_write(ADDR, buf, 2); +} + +static inline uint8_t read_register(uint8_t reg) +{ + uint8_t res; + i2c_slave_read(ADDR, reg, &res, 1); + return res; +} + +static inline void update_register(uint8_t reg, uint8_t mask, uint8_t val) +{ + write_register(reg, (read_register(reg) & mask) | val); +} + +bool hmc5883l_init() +{ + if (hmc5883l_get_id() != HMC5883L_ID) + return false; + current_gain = gain_values[hmc5883l_get_gain()]; + current_mode = hmc5883l_get_operating_mode(); + return true; +} + +uint32_t hmc5883l_get_id() +{ + uint32_t res = 0; + i2c_slave_read(ADDR, REG_ID_A, (uint8_t *)&res, 3); + return res; +} + +hmc5883l_operating_mode_t hmc5883l_get_operating_mode() +{ + uint8_t res = read_register(REG_MODE) & MASK_MD; + return res == 0 ? HMC5883L_MODE_CONTINUOUS : HMC5883L_MODE_SINGLE; +} + +void hmc5883l_set_operating_mode(hmc5883l_operating_mode_t mode) +{ + write_register(REG_MODE, mode); + current_mode = mode; +} + +hmc5883l_samples_averaged_t hmc5883l_get_samples_averaged() +{ + return (read_register(REG_CR_A) & MASK_MA) >> BIT_MA; +} + +void hmc5883l_set_samples_averaged(hmc5883l_samples_averaged_t samples) +{ + update_register(REG_CR_A, MASK_MA, samples << BIT_MA); +} + +hmc5883l_data_rate_t hmc5883l_get_data_rate() +{ + return (read_register(REG_CR_A) & MASK_DO) >> BIT_DO; +} + +void hmc5883l_set_data_rate(hmc5883l_data_rate_t rate) +{ + update_register(REG_CR_A, MASK_DO, rate << BIT_DO); +} + +hmc5883l_bias_t hmc5883l_get_bias() +{ + return read_register(REG_CR_A) & MASK_MS; +} + +void hmc5883l_set_bias(hmc5883l_bias_t bias) +{ + update_register(REG_CR_A, MASK_MS, bias); +} + +hmc5883l_gain_t hmc5883l_get_gain() +{ + return read_register(REG_CR_B) >> BIT_GN; +} + +void hmc5883l_set_gain(hmc5883l_gain_t gain) +{ + write_register(REG_CR_B, gain << BIT_GN); + current_gain = gain_values[gain]; +} + +bool hmc5883l_data_is_locked() +{ + return read_register(REG_STAT) & MASK_DL; +} + +bool hmc5883l_data_is_ready() +{ + return read_register(REG_STAT) & MASK_DR; +} + +bool hmc5883l_get_raw_data(hmc5883l_raw_data_t *data) +{ + if (current_mode == HMC5883L_MODE_SINGLE) + { + // overwrite mode register for measurement + hmc5883l_set_operating_mode(current_mode); + // wait for data + uint32_t timeout = sdk_system_get_time() + MEASUREMENT_TIMEOUT; + while (!hmc5883l_data_is_ready()) + { + if (sdk_system_get_time() >= timeout) + return false; + } + } + uint8_t buf[6]; + i2c_slave_read(ADDR, REG_DX_H, buf, 6); + data->x = ((int16_t)buf[REG_DX_H - REG_DX_H] << 8) | buf[REG_DX_L - REG_DX_H]; + data->y = ((int16_t)buf[REG_DY_H - REG_DX_H] << 8) | buf[REG_DY_L - REG_DX_H]; + data->z = ((int16_t)buf[REG_DZ_H - REG_DX_H] << 8) | buf[REG_DZ_L - REG_DX_H]; + return true; +} + +void hmc5883l_raw_to_mg(const hmc5883l_raw_data_t *raw, hmc5883l_data_t *mg) +{ + mg->x = raw->x * current_gain; + mg->y = raw->y * current_gain; + mg->z = raw->z * current_gain; +} + +bool hmc5883l_get_data(hmc5883l_data_t *data) +{ + hmc5883l_raw_data_t raw; + + if (!hmc5883l_get_raw_data(&raw)) + return false; + hmc5883l_raw_to_mg(&raw, data); + return true; +} diff --git a/extras/hmc5883l/hmc5883l.h b/extras/hmc5883l/hmc5883l.h new file mode 100644 index 0000000..720a385 --- /dev/null +++ b/extras/hmc5883l/hmc5883l.h @@ -0,0 +1,218 @@ +/* + * Driver for 3-axis digital compass HMC5883L + * + * Part of esp-open-rtos + * Copyright (C) 2016 Ruslan V. Uss + * BSD Licensed as described in the file LICENSE + */ +#ifndef EXTRAS_HMC5883L_H_ +#define EXTRAS_HMC5883L_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define HMC5883L_ID 0x00333448 // "H43" + +/** + * Device operating mode + */ +typedef enum +{ + HMC5883L_MODE_CONTINUOUS = 0, //!< Continuous mode + HMC5883L_MODE_SINGLE //!< Single measurement mode, default +} hmc5883l_operating_mode_t; + +/** + * Number of samples averaged per measurement + */ +typedef enum +{ + HMC5883L_SAMPLES_1 = 0, //!< 1 sample, default + HMC5883L_SAMPLES_2, //!< 2 samples + HMC5883L_SAMPLES_4, //!< 4 samples + HMC5883L_SAMPLES_8 //!< 8 samples +} hmc5883l_samples_averaged_t; + +/** + * Data output rate in continuous measurement mode + */ +typedef enum +{ + HMC5883L_DATA_RATE_00_75 = 0, //!< 0.75 Hz + HMC5883L_DATA_RATE_01_50, //!< 1.5 Hz + HMC5883L_DATA_RATE_03_00, //!< 3 Hz + HMC5883L_DATA_RATE_07_50, //!< 7.5 Hz + HMC5883L_DATA_RATE_15_00, //!< 15 Hz, default + HMC5883L_DATA_RATE_30_00, //!< 30 Hz + HMC5883L_DATA_RATE_75_00 //!< 75 Hz +} hmc5883l_data_rate_t; + +/** + * Measurement mode of the device (bias) + */ +typedef enum +{ + HMC5883L_BIAS_NORMAL = 0, //!< Default flow, no bias + HMC5883L_BIAS_POSITIVE, //!< Positive bias configuration all axes, used for self test (see datasheet) + HMC5883L_BIAS_NEGATIVE //!< Negative bias configuration all axes, used for self test (see datasheet) +} hmc5883l_bias_t; + +/** + * Device gain + */ +typedef enum +{ + HMC5883L_GAIN_1370 = 0, //!< 0.73 mG/LSb, range -0.88..+0.88 G + HMC5883L_GAIN_1090, //!< 0.92 mG/LSb, range -1.3..+1.3 G, default + HMC5883L_GAIN_820, //!< 1.22 mG/LSb, range -1.9..+1.9 G + HMC5883L_GAIN_660, //!< 1.52 mG/LSb, range -2.5..+2.5 G + HMC5883L_GAIN_440, //!< 2.27 mG/LSb, range -4.0..+4.0 G + HMC5883L_GAIN_390, //!< 2.56 mG/LSb, range -4.7..+4.7 G + HMC5883L_GAIN_330, //!< 3.03 mG/LSb, range -5.6..+5.6 G + HMC5883L_GAIN_230 //!< 4.35 mG/LSb, range -8.1..+8.1 G +} hmc5883l_gain_t; + +/** + * Raw measurement result + */ +typedef struct +{ + int16_t x; + int16_t y; + int16_t z; +} hmc5883l_raw_data_t; + +/** + * Measurement result, milligauss + */ +typedef struct +{ + float x; + float y; + float z; +} hmc5883l_data_t; + +/** + * \brief Init device + * \return false if error occured + */ +bool hmc5883l_init(); + +/** + * \brief Get device ID + * Always returns 0x00333448 if IC functioning properly. + * \return Device ID + */ +uint32_t hmc5883l_get_id(); + +/** + * \brief Get operating mode + * \return Measurement mode + */ +hmc5883l_operating_mode_t hmc5883l_get_operating_mode(); + +/** + * \brief Set operating mode + * \param mode Measurement mode + */ +void hmc5883l_set_operating_mode(hmc5883l_operating_mode_t mode); + +/** + * \brief Get number of samples averaged per measurement output + * \return Number of samples + */ +hmc5883l_samples_averaged_t hmc5883l_get_samples_averaged(); + +/** + * \brief Set number of samples averaged per measurement output + * \param samples Number of samples + */ +void hmc5883l_set_samples_averaged(hmc5883l_samples_averaged_t samples); + +/** + * \brief Get data output rate in continuous measurement mode + * \return Data output rate + */ +hmc5883l_data_rate_t hmc5883l_get_data_rate(); + +/** + * \brief Set data output rate in continuous measurement mode + * \param rate Data output rate + */ +void hmc5883l_set_data_rate(hmc5883l_data_rate_t rate); + +/** + * \brief Get measurement mode (bias of the axes) + * See datasheet for self test description + * \return Bias + */ +hmc5883l_bias_t hmc5883l_get_bias(); + +/** + * \brief Set measurement mode (bias of the axes) + * See datasheet for self test description + * \param bias Bias + */ +void hmc5883l_set_bias(hmc5883l_bias_t bias); + +/** + * \brief Get device gain + * \return Current gain + */ +hmc5883l_gain_t hmc5883l_get_gain(); + +/** + * \brief Set device gain + * \param gain Gain + */ +void hmc5883l_set_gain(hmc5883l_gain_t gain); + +/** + * \brief Get data state + * \return true when data is written to all six data registers + */ +bool hmc5883l_data_is_ready(); + +/** + * \brief Get lock state. + * If data is locked, any new data will not be placed in data registers until + * one of these conditions are met: + * 1. data have been read, + * 2. operating mode is changed, + * 3. the measurement configuration (bias) is changed, + * 4. power is reset. + * \return true when data registers is locked + */ +bool hmc5883l_data_is_locked(); + +/** + * \brief Get raw magnetic data + * \param data Pointer to the struct to write raw data + * \return false if error occured in single measurement mode, always true in continuous mode + */ +bool hmc5883l_get_raw_data(hmc5883l_raw_data_t *data); + +/** + * \brief Convert raw magnetic data to milligausses + * \param raw Pointer to source raw data struct + * \param mg Pointer to target struct to write converted data + */ +void hmc5883l_raw_to_mg(const hmc5883l_raw_data_t *raw, hmc5883l_data_t *mg); + +/** + * \brief Get magnetic data in milligausses + * \param data Pointer to the struct to write data + * \return false if error occured in single measurement mode, always true in continuous mode + */ +bool hmc5883l_get_data(hmc5883l_data_t *data); + +#ifdef __cplusplus +} +#endif + +#endif /* EXTRAS_HMC5883L_H_ */