Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
1 contributor

Users who have contributed to this file

1928 lines (1746 sloc) 64.3 KB
/**\mainpage
* Copyright (C) 2015 - 2016 Bosch Sensortec GmbH
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of the copyright holder nor the names of the
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
* OR CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE
*
* The information provided is believed to be accurate and reliable.
* The copyright holder assumes no responsibility
* for the consequences of use
* of such information nor for any infringement of patents or
* other rights of third parties which may result from its use.
* No license is granted by implication or otherwise under any patent or
* patent rights of the copyright holder.
*
* File bmm150.c
* Date 12 Sep 2017
* Version 1.0.0
*
*/
/*! @file bmm150.c
@brief Sensor driver for BMM150 sensor */
#include "bmm150.h"
/************************** Internal macros *******************************/
/* Sensor ODR, Repetition and axes enable/disable settings */
#define MODE_SETTING_SEL UINT16_C(0x000F)
/* Interrupt pin settings like polarity,latch and int_pin enable */
#define INTERRUPT_PIN_SETTING_SEL UINT16_C(0x01F0)
/* Settings to enable/disable interrupts */
#define INTERRUPT_CONFIG_SEL UINT16_C(0x1E00)
/* Interrupt settings for configuring threshold values */
#define INTERRUPT_THRESHOLD_CONFIG_SEL UINT16_C(0x6000)
/********************** Static function declarations ************************/
/*!
* @brief This internal API is used to validate the device pointer for
* null conditions.
*
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t null_ptr_check(const struct bmm150_dev *dev);
/*!
* @brief This internal API sets/resets the power control bit of 0x4B register.
*
* @param[in] pwrcntrl_bit : Variable used to select/deselect the suspend mode.
* @param[in,out] dev : Structure instance of bmm150_dev
*
* pwrcntrl_bit | power mode
* -----------------|-------------------------
* 0x00 | Suspend mode
* 0x01 | Sleep/Active modes
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error.
*/
static int8_t set_power_control_bit(uint8_t pwrcntrl_bit, struct bmm150_dev *dev);
/*!
* @brief This internal API reads the trim registers of the sensor and stores
* the trim values in the "trim_data" of device structure.
*
* @param[in,out] dev : Structure instance of bmm150_dev
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error.
*/
static int8_t read_trim_registers(struct bmm150_dev *dev);
/*!
* @brief This internal API writes the op_mode value in the Opmode bits
* (bits 1 and 2) of 0x4C register.
*
* op_mode | Power mode
* ------------|-----------------------
* 0x00 | BMM150_NORMAL_MODE
* 0x01 | BMM150_FORCED_MODE
* 0x03 | BMM150_SLEEP_MODE
*
* @param[in,out] dev : Structure instance of bmm150_dev
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error.
*/
static int8_t write_op_mode(uint8_t op_mode, const struct bmm150_dev *dev);
/*!
* @brief This internal API sets the device from suspend to sleep mode
* by setting the power control bit to '1' of 0x4B register
*
* @param[in,out] dev : Structure instance of bmm150_dev
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error.
*/
static int8_t suspend_to_sleep_mode(struct bmm150_dev *dev);
/*!
* @brief This internal API sets the xy repetition value in the 0x51 register.
*
* @param[in,out] dev : Structure instance of bmm150_dev
*
* dev->settings.xy_rep | nXY(XY Repetitions)
* -------------------------|-----------------------
* 0x00 | 1
* 0x01 | 3
* 0x02 | 5
* . | .
* . | .
* 0xFF | 511
*
* @note number of XY Repetitions nXY = 1+2(dev->settings.xy_rep)
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error.
*/
static int8_t set_xy_rep(const struct bmm150_dev *dev);
/*!
* @brief This internal API sets the z repetition value in the 0x52 register.
*
* @param[in,out] dev : Structure instance of bmm150_dev
*
* dev->settings.z_rep | nZ(Z Repetitions)
* -------------------------|-----------------------
* 0x00 | 1
* 0x01 | 2
* 0x02 | 3
* . | .
* . | .
* 0xFF | 256
*
* @note number of Z Repetitions nZ = 1+(dev->settings.z_rep)
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error.
*/
static int8_t set_z_rep(const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to set the output data rate of the sensor
*
* @param[in] dev : Structure instance of bmm150_dev.
*
* dev->settings.data_rate | Data rate (ODR)
* -------------------------|-----------------------
* 0x00 | BMM150_DATA_RATE_10HZ
* 0x01 | BMM150_DATA_RATE_02HZ
* 0x02 | BMM150_DATA_RATE_06HZ
* 0x03 | BMM150_DATA_RATE_08HZ
* 0x04 | BMM150_DATA_RATE_15HZ
* 0x05 | BMM150_DATA_RATE_20HZ
* 0x06 | BMM150_DATA_RATE_25HZ
* 0x07 | BMM150_DATA_RATE_30HZ
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t set_odr(const struct bmm150_dev *dev);
/*!
* @brief This internal API sets the preset mode ODR and repetition settings.
* @param[in] dev : Structure instance of bmm150_dev
*
* API settings | Representation
* -------------------------|------------------------------
* dev->settings.data_rate | Output Data Rate (ODR)
* dev->settings.xy_rep | XY repetition value
* dev->settings.z_rep | Z-repetition value
*
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error.
*/
static int8_t set_odr_xyz_rep(const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to enable or disable the magnetic
* measurement of x,y,z axes based on the value of xyz_axes_control.
*
* @param[in] dev : Structure instance of bmm150_dev.
*
* dev->settings.xyz_axes_control | Measurement axes/channel
* -------------------------------|--------------------------
* Bit 0 | X - Channel
* Bit 1 | Y - Channel
* Bit 2 | Z - Channel
*
* @note Setting 1 - Disables Channel measurement
* @note Setting 0 - Enables Channel measurement
*
* dev->settings.xyz_axes_control | Measurement axes Enabled/disabled
* -------------------------------|------------------------------------
* 0x01 | Disables X axis (Y,Z axes enabled)
* 0x02 | Disables Y axis (X,Z axes enabled)
* 0x04 | Disables Z axis (X,Y axes enabled)
* 0x07 | Disables all X,Y,Z axes measurement
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t set_control_measurement_xyz(const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to identify the settings which the user
* wants to modify in the sensor.
*
* @param[in] sub_settings : Contains the settings subset to identify particular
* group of settings which the user is interested to change.
* @param[in] settings : Contains the user specified settings.
*
* @return Indicates whether user is interested to modify the settings which
* are related to sub_settings.
* @retval True -> User wants to modify this group of settings
* @retval False -> User does not want to modify this group of settings
*/
static uint8_t are_settings_changed(uint16_t sub_settings, uint16_t settings);
/*!
* @brief This API sets the ODR , measurement axes control ,
* repetition values of xy,z.
*
* @param[in] desired_settings : Contains the settings which user wants to
* change.
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t mode_settings(uint16_t desired_settings, const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to parse and store the sensor
* settings in the device structure
*
* @param[in] reg_data : Pointer of an array consisting all sensor
* setting data from 0x4B to 0x52 registers.
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static void parse_setting(const uint8_t *reg_data, struct bmm150_dev *dev);
/*!
* @brief This API is used to enable the interrupts and map them to the
* corresponding interrupt pins and specify the pin characteristics like the
* polarity , latch settings for the interrupt pins.
*
* @note The other interrupts can be latched or non-latched but,
* Data ready interrupt is always cleared after reading out the data
*
* @param[in] desired_settings : Contains the settings which user wants to
* change.
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t interrupt_pin_settings(uint16_t desired_settings, const struct bmm150_dev *dev);
/*!
* @brief This API is used to enable data overrun , overflow interrupts and
* enable/disable high/low threshold interrupts for x,y,z axis based on the
* threshold values set by the user in the High threshold (0x50) and
* Low threshold (0x4F) registers.
*
* @param[in] desired_settings : Contains the settings which user wants to
* change.
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t interrupt_config(uint16_t desired_settings, const struct bmm150_dev *dev);
/*!
* @brief This API is used to write the user specified High/Low threshold value
* as a reference to generate the high/low threshold interrupt.
*
* @param[in] desired_settings : Contains the settings which user wants to
* change.
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t interrupt_threshold_settings(uint16_t desired_settings, const struct bmm150_dev *dev);
#ifdef BMM150_USE_FLOATING_POINT
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer X axis data in float.
*
* @param[in] mag_data_x : The value of raw X data
* @param[in] data_rhall : The value of raw RHALL data
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of compensated X data value in float
*/
static float compensate_x(int16_t mag_data_x, uint16_t data_rhall, const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer Y axis data in float.
*
* @param[in] mag_data_y : The value of raw Y data
* @param[in] data_rhall : The value of raw RHALL data
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of compensated Y data value in float
*/
static float compensate_y(int16_t mag_data_y, uint16_t data_rhall, const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer Z axis data in float.
*
* @param[in] mag_data_z : The value of raw Z data
* @param[in] data_rhall : The value of raw RHALL data
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of compensated Z data value in float
*/
static float compensate_z(int16_t mag_data_z, uint16_t data_rhall, const struct bmm150_dev *dev);
#else
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer X axis data in int16_t.
*
* @param[in] mag_data_x : The value of raw X data
* @param[in] data_rhall : The value of raw RHALL data
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of compensated X data value in int16_t format
*/
static int16_t compensate_x(int16_t mag_data_x, uint16_t data_rhall, const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer Y axis data in int16_t.
*
* @param[in] mag_data_y : The value of raw Y data
* @param[in] data_rhall : The value of raw RHALL data
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of compensated Y data value in int16_t format
*/
static int16_t compensate_y(int16_t mag_data_y, uint16_t data_rhall, const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer Z axis data in int16_t.
*
* @param[in] mag_data_z : The value of raw Z data
* @param[in] data_rhall : The value of raw RHALL data
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of compensated Z data value in int16_t format
*/
static int16_t compensate_z(int16_t mag_data_z, uint16_t data_rhall, const struct bmm150_dev *dev);
#endif
/*!
* @brief This internal API is used to perform the normal self test
* of the sensor and return the self test result as return value
*
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t perform_normal_self_test(const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to enable the normal self test by setting
* the Self Test bit (bit0) of the 0x4C register,
* which triggers the start of self test
*
* @param[out] self_test_enable : The value of self test bit0 in 0x4C register
* @param[in] dev : Structure instance of bmm150_dev.
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t enable_normal_self_test(uint8_t *self_test_enable, const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to validate the results of normal self test
* by using the self test status available in the bit0 of registers 0x42,0x44
* and 0x46.
*
* @param[in] dev : Structure instance of bmm150_dev
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t validate_normal_self_test(const struct bmm150_dev *dev);
/*!
* @brief This internal API is used to perform advanced self test for Z axis
*
* @param[in] dev : Structure instance of bmm150_dev
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*
* Return value | Status of self-test
*----------------------|---------------------------
* 0 | BMM150_OK
* 8 | BMM150_W_ADV_SELF_TEST_FAIL
*/
static int8_t perform_adv_self_test(struct bmm150_dev *dev);
/*!
* @brief This internal API is used to set the desired power mode ,
* axes control and repetition settings for advanced self test
*
* @param[in] dev : Structure instance of bmm150_dev
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t adv_self_test_settings(struct bmm150_dev *dev);
/*!
* @brief This internal API is used to set the positive or negative value of
* self-test current and obtain the corresponding magnetometer z axis data
*
* @param[in] self_test_current : Self test current either positive/negative
* @param[out] data_z : Z-axis Magnetometer data
* @param[in] dev : Structure instance of bmm150_dev
*
* self_test_current | Self-test current Direction
*-------------------------|------------------------------
* 0x03 | BMM150_ENABLE_POSITIVE_CURRENT
* 0x02 | BMM150_ENABLE_NEGATIVE_CURRENT
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t adv_self_test_measurement(uint8_t self_test_current, int16_t *data_z, struct bmm150_dev *dev);
/*!
* @brief This internal API is used to get the difference between the
* Z axis mag data obtained by positive and negative self-test current
* and validate whether the advanced self test is done successfully or not.
*
* @param[in] positive_data_z : Z-axis Mag data by positive self-test current
* @param[in] negative_data_z : Z-axis Mag data by negative self-test current
*
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*
* Return value | Status of self-test
*----------------------|---------------------------
* 0 | BMM150_OK
* 8 | BMM150_W_ADV_SELF_TEST_FAIL
*/
static int8_t validate_adv_self_test(int16_t positive_data_z, int16_t negative_data_z);
/*!
* @brief This internal API is used to set the self test current value in
* the Adv. ST bits (bit6 and bit7) of 0x4C register
*
* @param[in] self_test_current : Self test current value (+ve/-ve)
* @param[in] dev : Structure instance of bmm150_dev
*
* self_test_current | Self-test current Direction
*-------------------------|------------------------------
* 0x00 | BMM150_DISABLE_SELF_TEST_CURRENT
* 0x02 | BMM150_ENABLE_NEGATIVE_CURRENT
* 0x03 | BMM150_ENABLE_POSITIVE_CURRENT
*
* @return Result of API execution status
* @retval zero -> Success / +ve value -> Warning / -ve value -> Error
*/
static int8_t set_adv_self_test_current(uint8_t self_test_current, const struct bmm150_dev *dev);
/********************** Global function definitions ************************/
/*!
* @brief This API is the entry point, Call this API before using other APIs.
* This API reads the chip-id of the sensor which is the first step to
* verify the sensor and updates the trim parameters of the sensor.
*/
int8_t bmm150_init(struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t chip_id = 0;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
/* Power up the sensor from suspend to sleep mode */
rslt = set_power_control_bit(BMM150_POWER_CNTRL_ENABLE, dev);
/* Start-up time delay of 3ms*/
dev->delay_ms(BMM150_START_UP_TIME);
if (rslt == BMM150_OK) {
/* Chip ID of the sensor is read */
rslt = bmm150_get_regs(BMM150_CHIP_ID_ADDR, &chip_id, 1, dev);
/* Proceed if everything is fine until now */
if (rslt == BMM150_OK) {
/* Check for chip id validity */
if (chip_id == BMM150_CHIP_ID) {
dev->chip_id = chip_id;
/* Function to update trim values */
rslt = read_trim_registers(dev);
} else {
rslt = BMM150_E_DEV_NOT_FOUND;
}
}
}
}
return rslt;
}
/*!
* @brief This API writes the given data to the register address
* of the sensor.
*/
int8_t bmm150_set_regs(uint8_t reg_addr, uint8_t *reg_data, uint8_t len, const struct bmm150_dev *dev)
{
int8_t rslt;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if ((rslt == BMM150_OK) && (reg_data != NULL) && (len != 0)) {
/* Write the data to the reg_addr */
/* SPI write requires to set The MSB of reg_addr as 0
but in default the MSB is always 0 */
rslt = dev->write(dev->dev_id, reg_addr, reg_data, len);
} else {
rslt = BMM150_E_NULL_PTR;
}
return rslt;
}
/*!
* @brief This API reads the data from the given register address of the sensor.
*/
int8_t bmm150_get_regs(uint8_t reg_addr, uint8_t *reg_data, uint8_t len, const struct bmm150_dev *dev)
{
int8_t rslt;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if ((rslt == BMM150_OK) && (reg_data != NULL)) {
if (dev->intf != BMM150_I2C_INTF) {
/* If interface selected is SPI */
reg_addr = reg_addr | 0x80;
}
/* Read the data from the reg_addr */
rslt = dev->read(dev->dev_id, reg_addr, reg_data, len);
} else {
rslt = BMM150_E_NULL_PTR;
}
return rslt;
}
/*!
* @brief This API is used to perform soft-reset of the sensor
* where all the registers are reset to their default values except 0x4B.
*/
int8_t bmm150_soft_reset(const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t reg_data;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
rslt = bmm150_get_regs(BMM150_POWER_CONTROL_ADDR, &reg_data, 1, dev);
if (rslt == BMM150_OK) {
reg_data = reg_data | BMM150_SET_SOFT_RESET;
rslt = bmm150_set_regs(BMM150_POWER_CONTROL_ADDR, &reg_data, 1, dev);
dev->delay_ms(BMM150_SOFT_RESET_DELAY);
}
}
return rslt;
}
/*!
* @brief This API is used to set the power mode of the sensor.
*/
int8_t bmm150_set_op_mode(struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t pwr_mode = dev->settings.pwr_mode;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
/* Select the power mode to set */
switch (pwr_mode) {
case BMM150_NORMAL_MODE:
/* If the sensor is in suspend mode
put the device to sleep mode */
rslt = suspend_to_sleep_mode(dev);
if (rslt == BMM150_OK) {
/* write the op mode */
rslt = write_op_mode(pwr_mode, dev);
}
break;
case BMM150_FORCED_MODE:
/* If the sensor is in suspend mode
put the device to sleep mode */
rslt = suspend_to_sleep_mode(dev);
if (rslt == BMM150_OK) {
/* write the op mode */
rslt = write_op_mode(pwr_mode, dev);
}
break;
case BMM150_SLEEP_MODE:
/* If the sensor is in suspend mode
put the device to sleep mode */
rslt = suspend_to_sleep_mode(dev);
if (rslt == BMM150_OK) {
/* write the op mode */
rslt = write_op_mode(pwr_mode, dev);
}
break;
case BMM150_SUSPEND_MODE:
/* Set the power control bit to zero */
rslt = set_power_control_bit(BMM150_POWER_CNTRL_DISABLE, dev);
break;
default:
rslt = BMM150_E_INVALID_CONFIG;
break;
}
}
return rslt;
}
/*!
* @brief This API is used to get the power mode of the sensor.
*/
int8_t bmm150_get_op_mode(uint8_t *op_mode, const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t reg_data;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
if (dev->settings.pwr_cntrl_bit == BMM150_POWER_CNTRL_DISABLE) {
/* Power mode set is suspend mode*/
*op_mode = BMM150_SUSPEND_MODE;
} else {
/*Power mode set is stored in the op_mode */
rslt = bmm150_get_regs(BMM150_OP_MODE_ADDR, &reg_data, 1, dev);
*op_mode = BMM150_GET_BITS(reg_data, BMM150_OP_MODE);
}
}
return rslt;
}
/*!
* @brief This API is used to set the preset mode of the sensor.
*/
int8_t bmm150_set_presetmode(struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t preset_mode;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
preset_mode = dev->settings.preset_mode;
switch (preset_mode) {
case BMM150_PRESETMODE_LOWPOWER:
/* Set the data rate x,y,z repetition
for Low Power mode */
dev->settings.data_rate = BMM150_DATA_RATE_10HZ;
dev->settings.xy_rep = BMM150_LOWPOWER_REPXY;
dev->settings.z_rep = BMM150_LOWPOWER_REPZ;
rslt = set_odr_xyz_rep(dev);
break;
case BMM150_PRESETMODE_REGULAR:
/* Set the data rate x,y,z repetition
for Regular mode */
dev->settings.data_rate = BMM150_DATA_RATE_10HZ;
dev->settings.xy_rep = BMM150_REGULAR_REPXY;
dev->settings.z_rep = BMM150_REGULAR_REPZ;
rslt = set_odr_xyz_rep(dev);
break;
case BMM150_PRESETMODE_HIGHACCURACY:
/* Set the data rate x,y,z repetition
for High Accuracy mode */
dev->settings.data_rate = BMM150_DATA_RATE_20HZ;
dev->settings.xy_rep = BMM150_HIGHACCURACY_REPXY;
dev->settings.z_rep = BMM150_HIGHACCURACY_REPZ;
rslt = set_odr_xyz_rep(dev);
break;
case BMM150_PRESETMODE_ENHANCED:
/* Set the data rate x,y,z repetition
for Enhanced Accuracy mode */
dev->settings.data_rate = BMM150_DATA_RATE_10HZ;
dev->settings.xy_rep = BMM150_ENHANCED_REPXY;
dev->settings.z_rep = BMM150_ENHANCED_REPZ;
rslt = set_odr_xyz_rep(dev);
break;
default:
rslt = BMM150_E_INVALID_CONFIG;
break;
}
}
return rslt;
}
/*!
* @brief This API sets the sensor settings based on the desired_settings
* and the dev structure configuration
*/
int8_t bmm150_set_sensor_settings(uint16_t desired_settings, const struct bmm150_dev *dev)
{
int8_t rslt;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
if (are_settings_changed(MODE_SETTING_SEL, desired_settings)) {
/* ODR, Control measurement, XY,Z repetition values*/
rslt = mode_settings(desired_settings, dev);
}
if ((!rslt) && are_settings_changed(INTERRUPT_PIN_SETTING_SEL, desired_settings)) {
/* Interrupt pin settings */
rslt = interrupt_pin_settings(desired_settings, dev);
}
if ((!rslt) && are_settings_changed(INTERRUPT_CONFIG_SEL, desired_settings)) {
/* Interrupt configuration settings */
rslt = interrupt_config(desired_settings, dev);
}
if ((!rslt) && are_settings_changed(INTERRUPT_THRESHOLD_CONFIG_SEL, desired_settings)) {
/* Interrupt threshold settings */
rslt = interrupt_threshold_settings(desired_settings, dev);
}
}
return rslt;
}
/*!
* @brief This API gets the sensor settings and updates the dev structure
*/
int8_t bmm150_get_sensor_settings(struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t setting[BMM150_SETTING_DATA_LEN] = {0};
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
/*Read the entire sensor settings */
rslt = bmm150_get_regs(BMM150_POWER_CONTROL_ADDR, setting, BMM150_SETTING_DATA_LEN, dev);
if (rslt == BMM150_OK) {
/*Parse and store the settings */
parse_setting(setting, dev);
}
}
return rslt;
}
/*!
* @brief This API is used to read the magnetometer data from registers
* 0x42 to 0x49 and update the dev structure with the
* compensated mag data in micro-tesla.
*/
int8_t bmm150_read_mag_data(struct bmm150_dev *dev)
{
int8_t rslt;
int16_t msb_data;
uint8_t reg_data[BMM150_XYZR_DATA_LEN] = {0};
struct bmm150_raw_mag_data raw_mag_data;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
/*Read the mag data registers */
rslt = bmm150_get_regs(BMM150_DATA_X_LSB, reg_data, BMM150_XYZR_DATA_LEN, dev);
if (rslt == BMM150_OK) {
/* Mag X axis data */
reg_data[0] = BMM150_GET_BITS(reg_data[0], BMM150_DATA_X);
/* Shift the MSB data to left by 5 bits */
/* Multiply by 32 to get the shift left by 5 value */
msb_data = ((int16_t)((int8_t)reg_data[1])) * 32;
/* Raw mag X axis data */
raw_mag_data.raw_datax = (int16_t)(msb_data | reg_data[0]);
/* Mag Y axis data */
reg_data[2] = BMM150_GET_BITS(reg_data[2], BMM150_DATA_Y);
/* Shift the MSB data to left by 5 bits */
/* Multiply by 32 to get the shift left by 5 value */
msb_data = ((int16_t)((int8_t)reg_data[3])) * 32;
/* Raw mag Y axis data */
raw_mag_data.raw_datay = (int16_t)(msb_data | reg_data[2]);
/* Mag Z axis data */
reg_data[4] = BMM150_GET_BITS(reg_data[4], BMM150_DATA_Z);
/* Shift the MSB data to left by 7 bits */
/* Multiply by 128 to get the shift left by 7 value */
msb_data = ((int16_t)((int8_t)reg_data[5])) * 128;
/* Raw mag Z axis data */
raw_mag_data.raw_dataz = (int16_t)(msb_data | reg_data[4]);
/* Mag R-HALL data */
reg_data[6] = BMM150_GET_BITS(reg_data[6], BMM150_DATA_RHALL);
raw_mag_data.raw_data_r = (uint16_t)(((uint16_t)reg_data[7] << 6) | reg_data[6]);
/* Compensated Mag X data in int16_t format */
dev->data.x = compensate_x(raw_mag_data.raw_datax, raw_mag_data.raw_data_r, dev);
/* Compensated Mag Y data in int16_t format */
dev->data.y = compensate_y(raw_mag_data.raw_datay, raw_mag_data.raw_data_r, dev);
/* Compensated Mag Z data in int16_t format */
dev->data.z = compensate_z(raw_mag_data.raw_dataz, raw_mag_data.raw_data_r, dev);
}
}
return rslt;
}
/*!
* @brief This API is used to perform the complete self test
* (both normal and advanced) for the BMM150 sensor
*/
int8_t bmm150_perform_self_test(uint8_t self_test_mode, struct bmm150_dev *dev)
{
int8_t rslt;
int8_t self_test_rslt = 0;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
switch (self_test_mode) {
case BMM150_NORMAL_SELF_TEST:
/* Set the sensor in sleep mode */
dev->settings.pwr_mode = BMM150_SLEEP_MODE;
rslt = bmm150_set_op_mode(dev);
if (rslt == BMM150_OK) {
/* Perform the normal self test */
rslt = perform_normal_self_test(dev);
}
break;
case BMM150_ADVANCED_SELF_TEST:
/* Perform the advanced self test */
rslt = perform_adv_self_test(dev);
/* Check to ensure bus error does not occur */
if (rslt >= BMM150_OK) {
/* Store the status of self test result */
self_test_rslt = rslt;
/* Perform soft reset */
rslt = bmm150_soft_reset(dev);
}
/* Check to ensure bus operations are success */
if (rslt == BMM150_OK) {
/* Restore self_test_rslt as return value */
rslt = self_test_rslt;
}
break;
default:
rslt = BMM150_E_INVALID_CONFIG;
break;
}
}
return rslt;
}
/*!
* @brief This API is used to get the status flags of all interrupt
* which is used to check for the assertion of interrupts
*/
int8_t bmm150_get_interrupt_status(struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t interrupt_status;
uint8_t data_ready_status;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
/* Read the data ready status from the register 0x48 */
rslt = bmm150_get_regs(BMM150_DATA_READY_STATUS, &data_ready_status, 1, dev);
if (rslt == BMM150_OK) {
/* Read the interrupt status from the register 0x50 */
rslt = bmm150_get_regs(BMM150_INTERRUPT_STATUS, &interrupt_status, 1, dev);
if (rslt == BMM150_OK) {
/* Mask and store the data ready status bit*/
data_ready_status = BMM150_GET_BITS_POS_0(data_ready_status, BMM150_DRDY_STATUS);
/* store the entire interrupt status in dev */
dev->int_status = (data_ready_status << 8) | interrupt_status;
}
}
}
return rslt;
}
/****************************************************************************/
/**\name BMM150 as Auxiliary Mag */
/*!
* @brief This API is used to compensate the raw mag data
*/
int8_t bmm150_aux_mag_data(uint8_t *aux_data, struct bmm150_dev *dev)
{
int8_t rslt;
int16_t msb_data;
struct bmm150_raw_mag_data raw_mag_data;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if ((rslt == BMM150_OK) && (aux_data != NULL)) {
/* Mag X axis data */
aux_data[0] = BMM150_GET_BITS(aux_data[0], BMM150_DATA_X);
/* Shift the MSB data to left by 5 bits */
/* Multiply by 32 to get the shift left by 5 value */
msb_data = ((int16_t)((int8_t)aux_data[1])) * 32;
/* Raw mag X axis data */
raw_mag_data.raw_datax = (int16_t)(msb_data | aux_data[0]);
/* Mag Y axis data */
aux_data[2] = BMM150_GET_BITS(aux_data[2], BMM150_DATA_Y);
/* Shift the MSB data to left by 5 bits */
/* Multiply by 32 to get the shift left by 5 value */
msb_data = ((int16_t)((int8_t)aux_data[3])) * 32;
/* Raw mag Y axis data */
raw_mag_data.raw_datay = (int16_t)(msb_data | aux_data[2]);
/* Mag Z axis data */
aux_data[4] = BMM150_GET_BITS(aux_data[4], BMM150_DATA_Z);
/* Shift the MSB data to left by 7 bits */
/* Multiply by 128 to get the shift left by 7 value */
msb_data = ((int16_t)((int8_t)aux_data[5])) * 128;
/* Raw mag Z axis data */
raw_mag_data.raw_dataz = (int16_t)(msb_data | aux_data[4]);
/* Mag R-HALL data */
aux_data[6] = BMM150_GET_BITS(aux_data[6], BMM150_DATA_RHALL);
raw_mag_data.raw_data_r = (uint16_t)(((uint16_t)aux_data[7] << 6) | aux_data[6]);
/* Compensated Mag X data in int16_t format */
dev->data.x = compensate_x(raw_mag_data.raw_datax, raw_mag_data.raw_data_r, dev);
/* Compensated Mag Y data in int16_t format */
dev->data.y = compensate_y(raw_mag_data.raw_datay, raw_mag_data.raw_data_r, dev);
/* Compensated Mag Z data in int16_t format */
dev->data.z = compensate_z(raw_mag_data.raw_dataz, raw_mag_data.raw_data_r, dev);
}
return rslt;
}
/****************************************************************************/
/**\name INTERNAL APIs */
/*!
* @brief This internal API is used to validate the device structure pointer for
* null conditions.
*/
static int8_t null_ptr_check(const struct bmm150_dev *dev)
{
int8_t rslt;
if ((dev == NULL) || (dev->read == NULL) || (dev->write == NULL) || (dev->delay_ms == NULL)) {
/* Device structure pointer is not valid */
rslt = BMM150_E_NULL_PTR;
} else {
/* Device structure is fine */
rslt = BMM150_OK;
}
return rslt;
}
/*!
* @brief This internal API sets/resets the power control bit of 0x4B register.
*/
static int8_t set_power_control_bit(uint8_t pwrcntrl_bit, struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t reg_data = 0;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
/* Power control register 0x4B is read */
rslt = bmm150_get_regs(BMM150_POWER_CONTROL_ADDR, &reg_data, 1, dev);
/* Proceed if everything is fine until now */
if (rslt == BMM150_OK) {
/* Sets the value of power control bit */
reg_data = BMM150_SET_BITS_POS_0(reg_data, BMM150_PWR_CNTRL, pwrcntrl_bit);
rslt = bmm150_set_regs(BMM150_POWER_CONTROL_ADDR, &reg_data, 1, dev);
if (rslt == BMM150_OK) {
/*Store the power control bit
value in dev structure*/
dev->settings.pwr_cntrl_bit = pwrcntrl_bit;
}
}
}
return rslt;
}
/*!
* @brief This internal API reads the trim registers of the sensor and stores
* the trim values in the "trim_data" of device structure.
*/
static int8_t read_trim_registers(struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t trim_x1y1[2] = {0};
uint8_t trim_xyz_data[4] = {0};
uint8_t trim_xy1xy2[10] = {0};
uint16_t temp_msb = 0;
/* Trim register value is read */
rslt = bmm150_get_regs(BMM150_DIG_X1, trim_x1y1, 2, dev);
if (rslt == BMM150_OK) {
rslt = bmm150_get_regs(BMM150_DIG_Z4_LSB, trim_xyz_data, 4, dev);
if (rslt == BMM150_OK) {
rslt = bmm150_get_regs(BMM150_DIG_Z2_LSB, trim_xy1xy2, 10, dev);
if (rslt == BMM150_OK) {
/* Trim data which is read is updated
in the device structure */
dev->trim_data.dig_x1 = (int8_t)trim_x1y1[0];
dev->trim_data.dig_y1 = (int8_t)trim_x1y1[1];
dev->trim_data.dig_x2 = (int8_t)trim_xyz_data[2];
dev->trim_data.dig_y2 = (int8_t)trim_xyz_data[3];
temp_msb = ((uint16_t)trim_xy1xy2[3]) << 8;
dev->trim_data.dig_z1 = (uint16_t)(temp_msb | trim_xy1xy2[2]);
temp_msb = ((uint16_t)trim_xy1xy2[1]) << 8;
dev->trim_data.dig_z2 = (int16_t)(temp_msb | trim_xy1xy2[0]);
temp_msb = ((uint16_t)trim_xy1xy2[7]) << 8;
dev->trim_data.dig_z3 = (int16_t)(temp_msb | trim_xy1xy2[6]);
temp_msb = ((uint16_t)trim_xyz_data[1]) << 8;
dev->trim_data.dig_z4 = (int16_t)(temp_msb | trim_xyz_data[0]);
dev->trim_data.dig_xy1 = trim_xy1xy2[9];
dev->trim_data.dig_xy2 = (int8_t)trim_xy1xy2[8];
temp_msb = ((uint16_t)(trim_xy1xy2[5] & 0x7F)) << 8;
dev->trim_data.dig_xyz1 = (uint16_t)(temp_msb | trim_xy1xy2[4]);
}
}
}
return rslt;
}
/*!
* @brief This internal API writes the op_mode value in the Opmode bits
* (bits 1 and 2) of 0x4C register.
*/
static int8_t write_op_mode(uint8_t op_mode, const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t reg_data;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
/* Read the 0x4C register */
rslt = bmm150_get_regs(BMM150_OP_MODE_ADDR, &reg_data, 1, dev);
if (rslt == BMM150_OK) {
/* Set the op_mode value in Opmode bits of 0x4C */
reg_data = BMM150_SET_BITS(reg_data, BMM150_OP_MODE, op_mode);
rslt = bmm150_set_regs(BMM150_OP_MODE_ADDR, &reg_data, 1, dev);
}
}
return rslt;
}
/*!
* @brief This internal API sets the device from suspend to sleep mode
* by setting the power control bit to '1' of 0x4B register
*/
static int8_t suspend_to_sleep_mode(struct bmm150_dev *dev)
{
int8_t rslt;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
if (dev->settings.pwr_cntrl_bit == BMM150_POWER_CNTRL_DISABLE) {
rslt = set_power_control_bit(BMM150_POWER_CNTRL_ENABLE, dev);
/* Start-up time delay of 3ms*/
dev->delay_ms(BMM150_START_UP_TIME);
}
}
return rslt;
}
/*!
* @brief This internal API sets the xy repetition value in the 0x51 register.
*/
static int8_t set_xy_rep(const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t rep_xy;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
/* set the xy repetition */
rep_xy = dev->settings.xy_rep;
rslt = bmm150_set_regs(BMM150_REP_XY_ADDR, &rep_xy, 1, dev);
}
return rslt;
}
/*!
* @brief This internal API sets the z repetition value in the 0x52 register.
*/
static int8_t set_z_rep(const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t rep_z;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
/* set the z repetition */
rep_z = dev->settings.z_rep;
rslt = bmm150_set_regs(BMM150_REP_Z_ADDR, &rep_z, 1, dev);
}
return rslt;
}
/*!
* @brief This internal API is used to set the output data rate of the sensor.
*/
static int8_t set_odr(const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t reg_data;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
/*Read the 0x4C register */
rslt = bmm150_get_regs(BMM150_OP_MODE_ADDR, &reg_data, 1, dev);
if (rslt == BMM150_OK) {
/*Set the ODR value */
reg_data = BMM150_SET_BITS(reg_data, BMM150_ODR, dev->settings.data_rate);
rslt = bmm150_set_regs(BMM150_OP_MODE_ADDR, &reg_data, 1, dev);
}
}
return rslt;
}
/*!
* @brief This internal API sets the preset mode ODR and repetition settings.
*/
static int8_t set_odr_xyz_rep(const struct bmm150_dev *dev)
{
int8_t rslt;
/* Set the ODR */
rslt = set_odr(dev);
if (rslt == BMM150_OK) {
/* Set the XY-repetitions number */
rslt = set_xy_rep(dev);
if (rslt == BMM150_OK) {
/* Set the Z-repetitions number */
rslt = set_z_rep(dev);
}
}
return rslt;
}
/*!
* @brief This internal API is used to enable or disable the magnetic
* measurement of x,y,z axes based on the value of xyz_axes_control.
*/
static int8_t set_control_measurement_xyz(const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t reg_data;
/* Check for null pointer in the device structure*/
rslt = null_ptr_check(dev);
/* Proceed if null check is fine */
if (rslt == BMM150_OK) {
rslt = bmm150_get_regs(BMM150_AXES_ENABLE_ADDR, &reg_data, 1, dev);
if (rslt == BMM150_OK) {
/* Set the axes to be enabled/disabled*/
reg_data = BMM150_SET_BITS(reg_data, BMM150_CONTROL_MEASURE, dev->settings.xyz_axes_control);
rslt = bmm150_set_regs(BMM150_AXES_ENABLE_ADDR, &reg_data, 1, dev);
}
}
return rslt;
}
/*!
* @brief This internal API is used to identify the settings which the user
* wants to modify in the sensor.
*/
static uint8_t are_settings_changed(uint16_t sub_settings, uint16_t desired_settings)
{
uint8_t settings_changed = FALSE;
if (sub_settings & desired_settings) {
/* User wants to modify this particular settings */
settings_changed = TRUE;
} else {
/* User don't want to modify this particular settings */
settings_changed = FALSE;
}
return settings_changed;
}
/*!
* @brief This API sets the ODR , measurement axes control ,
* repetition values of xy,z.
*/
static int8_t mode_settings(uint16_t desired_settings, const struct bmm150_dev *dev)
{
int8_t rslt = BMM150_E_INVALID_CONFIG;
if (desired_settings & BMM150_DATA_RATE_SEL) {
/* Sets the ODR */
rslt = set_odr(dev);
}
if (desired_settings & BMM150_CONTROL_MEASURE_SEL) {
/* Enables/Disables the control measurement axes */
rslt = set_control_measurement_xyz(dev);
}
if (desired_settings & BMM150_XY_REP_SEL) {
/* Sets the XY repetition */
rslt = set_xy_rep(dev);
}
if (desired_settings & BMM150_Z_REP_SEL) {
/* Sets the Z repetition */
rslt = set_z_rep(dev);
}
return rslt;
}
/*!
* @brief This internal API is used to parse and store the sensor
* settings in the device structure
*/
static void parse_setting(const uint8_t *reg_data, struct bmm150_dev *dev)
{
/* Parse all the w/r registers and update the
current sensor settings in the dev structure*/
dev->settings.z_rep = reg_data[7];
dev->settings.xy_rep = reg_data[6];
dev->settings.int_settings.high_threshold = reg_data[5];
dev->settings.int_settings.low_threshold = reg_data[4];
dev->settings.xyz_axes_control = BMM150_GET_BITS(reg_data[3], BMM150_CONTROL_MEASURE);
dev->settings.int_settings.drdy_pin_en = BMM150_GET_BITS(reg_data[3], BMM150_DRDY_EN);
dev->settings.int_settings.int_pin_en = BMM150_GET_BITS(reg_data[3], BMM150_INT_PIN_EN);
dev->settings.int_settings.drdy_polarity = BMM150_GET_BITS(reg_data[3], BMM150_DRDY_POLARITY);
dev->settings.int_settings.int_latch = BMM150_GET_BITS(reg_data[3], BMM150_INT_LATCH);
dev->settings.int_settings.int_polarity = BMM150_GET_BITS_POS_0(reg_data[3], BMM150_INT_POLARITY);
dev->settings.int_settings.data_overrun_en = BMM150_GET_BITS(reg_data[2], BMM150_DATA_OVERRUN_INT);
dev->settings.int_settings.overflow_int_en = BMM150_GET_BITS(reg_data[2], BMM150_OVERFLOW_INT);
dev->settings.int_settings.high_int_en = BMM150_GET_BITS(reg_data[2], BMM150_HIGH_THRESHOLD_INT);
dev->settings.int_settings.low_int_en = BMM150_GET_BITS_POS_0(reg_data[2], BMM150_LOW_THRESHOLD_INT);
dev->settings.data_rate = BMM150_GET_BITS(reg_data[1], BMM150_ODR);
}
/*!
* @brief This API is used to enable the interrupts and map them to the
* corresponding interrupt pins and specify the pin characteristics like the
* polarity , latch settings for the interrupt pins.
*/
static int8_t interrupt_pin_settings(uint16_t desired_settings, const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t reg_data;
struct bmm150_int_ctrl_settings int_settings;
rslt = bmm150_get_regs(BMM150_AXES_ENABLE_ADDR, &reg_data, 1, dev);
if (rslt == BMM150_OK) {
int_settings = dev->settings.int_settings;
if (desired_settings & BMM150_DRDY_PIN_EN_SEL) {
/* Enables the Data ready interrupt and
maps it to the DRDY pin of the sensor */
reg_data = BMM150_SET_BITS(reg_data, BMM150_DRDY_EN, int_settings.drdy_pin_en);
}
if (desired_settings & BMM150_INT_PIN_EN_SEL) {
/* Sets interrupt pin enable */
reg_data = BMM150_SET_BITS(reg_data, BMM150_INT_PIN_EN, int_settings.int_pin_en);
}
if (desired_settings & BMM150_DRDY_POLARITY_SEL) {
/* Sets Data ready pin's polarity */
reg_data = BMM150_SET_BITS(reg_data, BMM150_DRDY_POLARITY, int_settings.drdy_polarity);
}
if (desired_settings & BMM150_INT_LATCH_SEL) {
/* Sets Interrupt in latched or non-latched mode */
reg_data = BMM150_SET_BITS(reg_data, BMM150_INT_LATCH, int_settings.int_latch);
}
if (desired_settings & BMM150_INT_POLARITY_SEL) {
/* Sets Interrupt pin's polarity */
reg_data = BMM150_SET_BITS_POS_0(reg_data, BMM150_INT_POLARITY, int_settings.int_polarity);
}
/* Set the interrupt configurations in the 0x4E register */
rslt = bmm150_set_regs(BMM150_AXES_ENABLE_ADDR, &reg_data, 1, dev);
}
return rslt;
}
/*!
* @brief This API is used to enable data overrun , overflow interrupts and
* enable/disable high/low threshold interrupts for x,y,z axis based on the
* threshold values set by the user in the High threshold (0x50) and
* Low threshold (0x4F) registers.
*/
static int8_t interrupt_config(uint16_t desired_settings, const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t reg_data;
struct bmm150_int_ctrl_settings int_settings;
rslt = bmm150_get_regs(BMM150_INT_CONFIG_ADDR, &reg_data, 1, dev);
if (rslt == BMM150_OK) {
int_settings = dev->settings.int_settings;
if (desired_settings & BMM150_DATA_OVERRUN_INT_SEL) {
/* Sets Data overrun interrupt */
reg_data = BMM150_SET_BITS(reg_data, BMM150_DATA_OVERRUN_INT, int_settings.data_overrun_en);
}
if (desired_settings & BMM150_OVERFLOW_INT_SEL) {
/* Sets Data overflow interrupt */
reg_data = BMM150_SET_BITS(reg_data, BMM150_OVERFLOW_INT, int_settings.overflow_int_en);
}
if (desired_settings & BMM150_HIGH_THRESHOLD_INT_SEL) {
/* Sets high threshold interrupt */
reg_data = BMM150_SET_BITS(reg_data, BMM150_HIGH_THRESHOLD_INT, int_settings.high_int_en);
}
if (desired_settings & BMM150_LOW_THRESHOLD_INT_SEL) {
/* Sets low threshold interrupt */
reg_data = BMM150_SET_BITS_POS_0(reg_data, BMM150_LOW_THRESHOLD_INT, int_settings.low_int_en);
}
/* Set the interrupt configurations in the 0x4D register */
rslt = bmm150_set_regs(BMM150_INT_CONFIG_ADDR, &reg_data, 1, dev);
}
return rslt;
}
/*!
* @brief This API is used to write the user specified High/Low threshold value
* as a reference to generate the high/low threshold interrupt.
*/
static int8_t interrupt_threshold_settings(uint16_t desired_settings, const struct bmm150_dev *dev)
{
int8_t rslt = BMM150_E_INVALID_CONFIG;
uint8_t reg_data;
if (desired_settings & BMM150_LOW_THRESHOLD_SETTING_SEL) {
/* Sets the Low threshold value to trigger interrupt */
reg_data = dev->settings.int_settings.low_threshold;
rslt = bmm150_set_regs(BMM150_LOW_THRESHOLD_ADDR, &reg_data, 1, dev);
}
if (desired_settings & BMM150_HIGH_THRESHOLD_SETTING_SEL) {
/* Sets the High threshold value to trigger interrupt */
reg_data = dev->settings.int_settings.high_threshold;
rslt = bmm150_set_regs(BMM150_HIGH_THRESHOLD_ADDR, &reg_data, 1, dev);
}
return rslt;
}
#ifdef BMM150_USE_FLOATING_POINT
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer x axis data(micro-tesla) in float.
*/
static float compensate_x(int16_t mag_data_x, uint16_t data_rhall, const struct bmm150_dev *dev)
{
float retval = 0;
float process_comp_x0;
float process_comp_x1;
float process_comp_x2;
float process_comp_x3;
float process_comp_x4;
/* Overflow condition check */
if ((mag_data_x != BMM150_XYAXES_FLIP_OVERFLOW_ADCVAL) &&
(data_rhall != 0) && (dev->trim_data.dig_xyz1 != 0)) {
/*Processing compensation equations*/
process_comp_x0 = (((float)dev->trim_data.dig_xyz1) * 16384.0f / data_rhall);
retval = (process_comp_x0 - 16384.0f);
process_comp_x1 = ((float)dev->trim_data.dig_xy2) * (retval * retval / 268435456.0f);
process_comp_x2 = process_comp_x1 + retval * ((float)dev->trim_data.dig_xy1) / 16384.0f;
process_comp_x3 = ((float)dev->trim_data.dig_x2) + 160.0f;
process_comp_x4 = mag_data_x * ((process_comp_x2 + 256.0f) * process_comp_x3);
retval = ((process_comp_x4 / 8192.0f) + (((float)dev->trim_data.dig_x1) * 8.0f)) / 16.0f;
} else {
/* overflow, set output to 0.0f */
retval = BMM150_OVERFLOW_OUTPUT_FLOAT;
}
return retval;
}
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer y axis data(micro-tesla) in float.
*/
static float compensate_y(int16_t mag_data_y, uint16_t data_rhall, const struct bmm150_dev *dev)
{
float retval = 0;
float process_comp_y0;
float process_comp_y1;
float process_comp_y2;
float process_comp_y3;
float process_comp_y4;
/* Overflow condition check */
if ((mag_data_y != BMM150_XYAXES_FLIP_OVERFLOW_ADCVAL)
&& (data_rhall != 0) && (dev->trim_data.dig_xyz1 != 0)) {
/*Processing compensation equations*/
process_comp_y0 = ((float)dev->trim_data.dig_xyz1) * 16384.0f / data_rhall;
retval = process_comp_y0 - 16384.0f;
process_comp_y1 = ((float)dev->trim_data.dig_xy2) * (retval * retval / 268435456.0f);
process_comp_y2 = process_comp_y1 + retval * ((float)dev->trim_data.dig_xy1) / 16384.0f;
process_comp_y3 = ((float)dev->trim_data.dig_y2) + 160.0f;
process_comp_y4 = mag_data_y * (((process_comp_y2) + 256.0f) * process_comp_y3);
retval = ((process_comp_y4 / 8192.0f) + (((float)dev->trim_data.dig_y1) * 8.0f)) / 16.0f;
} else {
/* overflow, set output to 0.0f */
retval = BMM150_OVERFLOW_OUTPUT_FLOAT;
}
return retval;
}
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer z axis data(micro-tesla) in float.
*/
static float compensate_z(int16_t mag_data_z, uint16_t data_rhall, const struct bmm150_dev *dev)
{
float retval = 0;
float process_comp_z0;
float process_comp_z1;
float process_comp_z2;
float process_comp_z3;
float process_comp_z4;
float process_comp_z5;
/* Overflow condition check */
if ((mag_data_z != BMM150_ZAXIS_HALL_OVERFLOW_ADCVAL) &&
(dev->trim_data.dig_z2 != 0) && (dev->trim_data.dig_z1 != 0)
&& (dev->trim_data.dig_xyz1 != 0) && (data_rhall != 0)) {
/* Processing compensation equations */
process_comp_z0 = ((float)mag_data_z) - ((float)dev->trim_data.dig_z4);
process_comp_z1 = ((float)data_rhall) - ((float)dev->trim_data.dig_xyz1);
process_comp_z2 = (((float)dev->trim_data.dig_z3) * process_comp_z1);
process_comp_z3 = ((float)dev->trim_data.dig_z1) * ((float)data_rhall) / 32768.0f;
process_comp_z4 = ((float)dev->trim_data.dig_z2) + process_comp_z3;
process_comp_z5 = (process_comp_z0 * 131072.0f) - process_comp_z2;
retval = (process_comp_z5 / ((process_comp_z4) * 4.0f)) / 16.0f;
} else {
/* overflow, set output to 0.0f */
retval = BMM150_OVERFLOW_OUTPUT_FLOAT;
}
return retval;
}
#else
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer X axis data(micro-tesla) in int16_t.
*/
static int16_t compensate_x(int16_t mag_data_x, uint16_t data_rhall, const struct bmm150_dev *dev)
{
int16_t retval;
uint16_t process_comp_x0 = 0;
int32_t process_comp_x1;
uint16_t process_comp_x2;
int32_t process_comp_x3;
int32_t process_comp_x4;
int32_t process_comp_x5;
int32_t process_comp_x6;
int32_t process_comp_x7;
int32_t process_comp_x8;
int32_t process_comp_x9;
int32_t process_comp_x10;
/* Overflow condition check */
if (mag_data_x != BMM150_XYAXES_FLIP_OVERFLOW_ADCVAL) {
if (data_rhall != 0) {
/* Availability of valid data*/
process_comp_x0 = data_rhall;
} else if (dev->trim_data.dig_xyz1 != 0) {
process_comp_x0 = dev->trim_data.dig_xyz1;
} else {
process_comp_x0 = 0;
}
if (process_comp_x0 != 0) {
/* Processing compensation equations*/
process_comp_x1 = ((int32_t)dev->trim_data.dig_xyz1) * 16384;
process_comp_x2 = ((uint16_t)(process_comp_x1 / process_comp_x0)) - ((uint16_t)0x4000);
retval = ((int16_t)process_comp_x2);
process_comp_x3 = (((int32_t)retval) * ((int32_t)retval));
process_comp_x4 = (((int32_t)dev->trim_data.dig_xy2) * (process_comp_x3 / 128));
process_comp_x5 = (int32_t)(((int16_t)dev->trim_data.dig_xy1) * 128);
process_comp_x6 = ((int32_t)retval) * process_comp_x5;
process_comp_x7 = (((process_comp_x4 + process_comp_x6) / 512) + ((int32_t)0x100000));
process_comp_x8 = ((int32_t)(((int16_t)dev->trim_data.dig_x2) + ((int16_t)0xA0)));
process_comp_x9 = ((process_comp_x7 * process_comp_x8) / 4096);
process_comp_x10 = ((int32_t)mag_data_x) * process_comp_x9;
retval = ((int16_t)(process_comp_x10 / 8192));
retval = (retval + (((int16_t)dev->trim_data.dig_x1) * 8)) / 16;
} else {
retval = BMM150_OVERFLOW_OUTPUT;
}
} else {
/* Overflow condition */
retval = BMM150_OVERFLOW_OUTPUT;
}
return retval;
}
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer Y axis data(micro-tesla) in int16_t.
*/
static int16_t compensate_y(int16_t mag_data_y, uint16_t data_rhall, const struct bmm150_dev *dev)
{
int16_t retval;
uint16_t process_comp_y0 = 0;
int32_t process_comp_y1;
uint16_t process_comp_y2;
int32_t process_comp_y3;
int32_t process_comp_y4;
int32_t process_comp_y5;
int32_t process_comp_y6;
int32_t process_comp_y7;
int32_t process_comp_y8;
int32_t process_comp_y9;
/* Overflow condition check */
if (mag_data_y != BMM150_XYAXES_FLIP_OVERFLOW_ADCVAL) {
if (data_rhall != 0) {
/* Availability of valid data*/
process_comp_y0 = data_rhall;
} else if (dev->trim_data.dig_xyz1 != 0) {
process_comp_y0 = dev->trim_data.dig_xyz1;
} else {
process_comp_y0 = 0;
}
if (process_comp_y0 != 0) {
/*Processing compensation equations*/
process_comp_y1 = (((int32_t)dev->trim_data.dig_xyz1) * 16384) / process_comp_y0;
process_comp_y2 = ((uint16_t)process_comp_y1) - ((uint16_t)0x4000);
retval = ((int16_t)process_comp_y2);
process_comp_y3 = ((int32_t) retval) * ((int32_t)retval);
process_comp_y4 = ((int32_t)dev->trim_data.dig_xy2) * (process_comp_y3 / 128);
process_comp_y5 = ((int32_t)(((int16_t)dev->trim_data.dig_xy1) * 128));
process_comp_y6 = ((process_comp_y4 + (((int32_t)retval) * process_comp_y5)) / 512);
process_comp_y7 = ((int32_t)(((int16_t)dev->trim_data.dig_y2) + ((int16_t)0xA0)));
process_comp_y8 = (((process_comp_y6 + ((int32_t)0x100000)) * process_comp_y7) / 4096);
process_comp_y9 = (((int32_t)mag_data_y) * process_comp_y8);
retval = (int16_t)(process_comp_y9 / 8192);
retval = (retval + (((int16_t)dev->trim_data.dig_y1) * 8)) / 16;
} else {
retval = BMM150_OVERFLOW_OUTPUT;
}
} else {
/* Overflow condition*/
retval = BMM150_OVERFLOW_OUTPUT;
}
return retval;
}
/*!
* @brief This internal API is used to obtain the compensated
* magnetometer Z axis data(micro-tesla) in int16_t.
*/
static int16_t compensate_z(int16_t mag_data_z, uint16_t data_rhall, const struct bmm150_dev *dev)
{
int32_t retval;
int16_t process_comp_z0;
int32_t process_comp_z1;
int32_t process_comp_z2;
int32_t process_comp_z3;
int16_t process_comp_z4;
if (mag_data_z != BMM150_ZAXIS_HALL_OVERFLOW_ADCVAL) {
if ((dev->trim_data.dig_z2 != 0) && (dev->trim_data.dig_z1 != 0)
&& (data_rhall != 0) && (dev->trim_data.dig_xyz1 != 0)) {
/*Processing compensation equations*/
process_comp_z0 = ((int16_t)data_rhall) - ((int16_t) dev->trim_data.dig_xyz1);
process_comp_z1 = (((int32_t)dev->trim_data.dig_z3) * ((int32_t)(process_comp_z0))) / 4;
process_comp_z2 = (((int32_t)(mag_data_z - dev->trim_data.dig_z4)) * 32768);
process_comp_z3 = ((int32_t)dev->trim_data.dig_z1) * (((int16_t)data_rhall) * 2);
process_comp_z4 = (int16_t)((process_comp_z3 + (32768)) / 65536);
retval = ((process_comp_z2 - process_comp_z1) / (dev->trim_data.dig_z2 + process_comp_z4));
/* saturate result to +/- 2 micro-tesla */
if (retval > BMM150_POSITIVE_SATURATION_Z) {
retval = BMM150_POSITIVE_SATURATION_Z;
} else {
if (retval < BMM150_NEGATIVE_SATURATION_Z)
retval = BMM150_NEGATIVE_SATURATION_Z;
}
/* Conversion of LSB to micro-tesla*/
retval = retval / 16;
} else {
retval = BMM150_OVERFLOW_OUTPUT;
}
} else {
/* Overflow condition*/
retval = BMM150_OVERFLOW_OUTPUT;
}
return (int16_t)retval;
}
#endif
/*!
* @brief This internal API is used to perform the normal self test
* of the sensor and return the self test result as return value
*/
static int8_t perform_normal_self_test(const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t self_test_bit;
/* Triggers the start of normal self test */
rslt = enable_normal_self_test(&self_test_bit, dev);
/* Check for self test completion status */
if ((rslt == BMM150_OK) && (self_test_bit == 0)) {
/* Validates the self test results for all 3 axes */
rslt = validate_normal_self_test(dev);
}
return rslt;
}
/*!
* @brief This internal API is used to enable the normal self test by setting
* the Self Test bit (bit0) of the 0x4C register,
* which triggers the start of self test
*/
static int8_t enable_normal_self_test(uint8_t *self_test_enable, const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t reg_data;
uint8_t self_test_val;
/* Read the data from register 0x4C */
rslt = bmm150_get_regs(BMM150_OP_MODE_ADDR, &reg_data, 1, dev);
if (rslt == BMM150_OK) {
/* Set the Self Test bit(bit0) of the 0x4C register */
self_test_val = 1;
reg_data = BMM150_SET_BITS_POS_0(reg_data, BMM150_SELF_TEST, self_test_val);
/* Write the data to 0x4C register to trigger self test */
rslt = bmm150_set_regs(BMM150_OP_MODE_ADDR, &reg_data, 1, dev);
dev->delay_ms(BMM150_NORMAL_SELF_TEST_DELAY);
if (rslt == BMM150_OK) {
/* Read the data from register 0x4C */
rslt = bmm150_get_regs(BMM150_OP_MODE_ADDR, &reg_data, 1, dev);
/* Self Test bit(bit0) is stored in self_test_enable,
It will be reset to zero after the self test is over */
*self_test_enable = BMM150_GET_BITS_POS_0(reg_data, BMM150_SELF_TEST);
}
}
return rslt;
}
/*!
* @brief This internal API is used to validate the results of normal self test
* by using the self test status available in the bit0 of registers 0x42,0x44
* and 0x46.
*/
static int8_t validate_normal_self_test(const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t status;
uint8_t self_test_rslt[5];
/* Read the data from register 0x42 to 0x46 */
rslt = bmm150_get_regs(BMM150_DATA_X_LSB, self_test_rslt, BMM150_SELF_TEST_LEN, dev);
if (rslt == BMM150_OK) {
/* Parse and get the self test status bits */
/* X-Self-Test (bit0) of 0x42 register is stored*/
self_test_rslt[0] = BMM150_GET_BITS_POS_0(self_test_rslt[0], BMM150_SELF_TEST);
/* Y-Self-Test (bit0) of 0x44 register is stored */
self_test_rslt[2] = BMM150_GET_BITS_POS_0(self_test_rslt[2], BMM150_SELF_TEST);
/* Z-Self-Test (bit0) of 0x46 register is stored */
self_test_rslt[4] = BMM150_GET_BITS_POS_0(self_test_rslt[4], BMM150_SELF_TEST);
/* Combine the self test status and store it in the first
3 bits of the status variable for processing*/
status = (uint8_t)((self_test_rslt[4] << 2) | (self_test_rslt[2] << 1) | self_test_rslt[0]);
/* Validate status and store Self test result in "rslt" */
if (status == BMM150_SELF_TEST_STATUS_SUCCESS) {
/* Self test is success when all status bits are set */
rslt = BMM150_OK;
} else {
if (status == BMM150_SELF_TEST_STATUS_XYZ_FAIL) {
/* Self test - all axis fail condition */
rslt = BMM150_W_NORMAL_SELF_TEST_XYZ_FAIL;
} else {
/* Self test - some axis fail condition */
rslt = (int8_t)status;
}
}
}
return rslt;
}
/*!
* @brief This internal API is used to perform advanced self test for Z axis
*/
static int8_t perform_adv_self_test(struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t self_test_current;
int16_t positive_data_z;
int16_t negative_data_z;
/* Set the desired power mode ,axes control and repetition settings */
rslt = adv_self_test_settings(dev);
if (rslt == BMM150_OK) {
/* Measure the Z axes data with positive self-test current */
self_test_current = BMM150_ENABLE_POSITIVE_CURRENT;
rslt = adv_self_test_measurement(self_test_current, &positive_data_z, dev);
if (rslt == BMM150_OK) {
/* Measure the Z axes data with
negative self-test current */
self_test_current = BMM150_ENABLE_NEGATIVE_CURRENT;
rslt = adv_self_test_measurement(self_test_current, &negative_data_z, dev);
if (rslt == BMM150_OK) {
/* Disable self-test current */
self_test_current = BMM150_DISABLE_SELF_TEST_CURRENT;
rslt = set_adv_self_test_current(self_test_current, dev);
if (rslt == BMM150_OK) {
/* Validate the advanced self test */
rslt = validate_adv_self_test(positive_data_z, negative_data_z);
}
}
}
}
return rslt;
}
/*!
* @brief This internal API is used to set the desired power mode ,
* axes control and repetition settings for advanced self test
*/
static int8_t adv_self_test_settings(struct bmm150_dev *dev)
{
int8_t rslt;
/* Set the power mode as sleep mode */
dev->settings.pwr_mode = BMM150_SLEEP_MODE;
rslt = bmm150_set_op_mode(dev);
if (rslt == BMM150_OK) {
/* Disable XY-axis measurement */
dev->settings.xyz_axes_control = BMM150_DISABLE_XY_AXIS;
rslt = set_control_measurement_xyz(dev);
if (rslt == BMM150_OK) {
/* Repetition value is set as 0x04 */
dev->settings.z_rep = BMM150_SELF_TEST_REP_Z;
rslt = set_z_rep(dev);
}
}
return rslt;
}
/*!
* @brief This internal API is used to set the positive or negative value of
* self-test current and obtain the corresponding magnetometer z axis data
*/
static int8_t adv_self_test_measurement(uint8_t self_test_current, int16_t *data_z, struct bmm150_dev *dev)
{
int8_t rslt;
/* Set the advanced self test current as positive or
negative based on the value of parameter "self_test_current" */
rslt = set_adv_self_test_current(self_test_current, dev);
if (rslt == BMM150_OK) {
/* Set the device in forced mode*/
dev->settings.pwr_mode = BMM150_FORCED_MODE;
rslt = bmm150_set_op_mode(dev);
/* Delay to ensure measurement is complete */
dev->delay_ms(BMM150_ADV_SELF_TEST_DELAY);
if (rslt == BMM150_OK) {
/* Read Mag data and store the value of Z axis data */
rslt = bmm150_read_mag_data(dev);
if (rslt == BMM150_OK) {
/* Mag Z axis data is stored */
*data_z = dev->data.z;
}
}
}
return rslt;
}
/*!
* @brief This internal API is used to get the difference between the
* Z axis mag data obtained by positive and negative self-test current
* and validate whether the advanced self test is done successfully or not.
*/
static int8_t validate_adv_self_test(int16_t positive_data_z, int16_t negative_data_z)
{
int32_t adv_self_test_rslt;
int8_t rslt;
/* Advanced self test difference between the Z axis mag data
obtained by the positive and negative self-test current */
adv_self_test_rslt = positive_data_z - negative_data_z;
/* Advanced self test validation */
/*Value of adv_self_test_rslt should be in between 180-240 micro-tesla*/
if ((adv_self_test_rslt > 180) && (adv_self_test_rslt < 240)) {
/* Advanced self test success */
rslt = BMM150_OK;
} else {
/* Advanced self test fail */
rslt = BMM150_W_ADV_SELF_TEST_FAIL;
}
return rslt;
}
/*!
* @brief This internal API is used to set the self test current value in
* the Adv. ST bits (bit6 and bit7) of 0x4C register
*/
static int8_t set_adv_self_test_current(uint8_t self_test_current, const struct bmm150_dev *dev)
{
int8_t rslt;
uint8_t reg_data;
/* Read the 0x4C register */
rslt = bmm150_get_regs(BMM150_OP_MODE_ADDR, &reg_data, 1, dev);
if (rslt == BMM150_OK) {
/* Set the self test current value in the Adv. ST bits
(bit6 and bit7) of 0x4c register */
reg_data = BMM150_SET_BITS(reg_data, BMM150_ADV_SELF_TEST, self_test_current);
rslt = bmm150_set_regs(BMM150_OP_MODE_ADDR, &reg_data, 1, dev);
}
return rslt;
}
You can’t perform that action at this time.