As shown in the diagram, the wakeup function can generate an interrupt by detecting defined movement, while the auto-wakeup function can switch the BMA400 from low power mode to normal mode. During switching to normal mode, the sensor configurations defined in previous normal mode (before entering low power mode), like ODR, range, OSR, FIFO, interrupts, will be restored automatically. Therefore, the host only need to configure BMA400 once in initialization and no need to reconfigure the parameters after BMA400 enters normal mode by auto-wakeup function.
The three possible triggers for wake-up from low-power mode are:
The three conditions can be enabled in parallel.
Please be notified that, the timeout wakeup can only switching BMA400 to normal mode if configured, and cannot be mapped to the interrupt pin(INT1 and INT2).
Except the serial command, there are three possible triggers can switch BMA400 to low-power automatically:
The three conditions can be enabled in parallel.
Also in timeout mode, the counter can be reset by interrupt from Gen2 interrupt. This can be used to enlarge the active time (in normal mode) of BMA400 if some activity is detected by Gen2.
In this section, several typical examples are presented with sample codes; you can modify them for your specific usage.
In this case, both Auto wakeup & auto low power function are triggered by timeout. So after configuration and enabling, BMA400 will work in below sequence:
Repeat:
Low power mode sampling (2.5ms ~ 10.24s)
Auto switches to normal mode by timeout
Normal mode sampling ((2.5ms ~ 10.24s))
Auto switches to low power mode by timeout
In this example, range = 2g, ODR=100Hz, Accel data source = filter1
Register(0x2A),Register(0x2B):
auto_lp_timeout_thres = 400 (400 * 2.5ms = 1s), unsigned 12bit, timeout threshold, 2.5ms/lsb
auto_lp_timeout: set to 1 here (enable), timeout as source for auto-low-power condition
For mode auto_lp_timeout = 0x02, gen2 interrupt will reset the timeout counter, and this can be used to delay the auto-lowpower if some condition is fulfilled. For example, in watch/band, gen2 can be configured to detect the body movement, and only if no movement is detected, the timeout counter will be continued and after defined time, the sensor eventually goes into low power mode.
wakeup_timeout_thres = 0x0FFF (max value, 0x0fff * 2.5ms = 10.24s)
Register (0x2C) and (0x2D), unsigned 12bits, auto-wakeup timeout threshold, 2.5ms/lsb
wkup_timeout: set to 1 (enable)
Register (0x2D) AUTOWAKEUP_1. wkup_timeout = 1; enable timerout triggering auto-wakeup
#### BMA400 Auto wakeup and Auto low power features
##### _Usage of auto wakeup and auto low power features in sensor_
``` c
/* Auto wake-up timeout testing setting */
int8_t bma400_timeout_autowakeup_auto_lp(struct bma400_dev *dev)
{
int8_t rslt = 0;
uint8_t power_mode;
uint16_t int_status;
struct bma400_device_setting dev_setting[2];
/* Selecting auto wakeup on timeout */
dev_setting[0].type = BMA400_AUTOWAKEUP_TIMEOUT;
/* Selecting auto low power mode*/
dev_setting[1].type = BMA400_AUTO_LOW_POWER;
/* Get the previously set settings */
rslt = bma400_get_device_setting(&dev_setting, 2, dev);
if (rslt == BMA400_OK) {
/* Enable auto wakeup on timeout feature */
dev_setting[0].conf.auto_wakeup.wakeup_timeout = BMA400_ENABLE;
/* Set the timeout value as maximum (10.24s) */
dev_setting[0].conf.auto_wakeup.timeout_thres = BMA400_AUTO_WAKEUP_TIMEOUT_MAX;
/* Set auto low power on timeout feature */
dev_setting[1].conf.auto_lp.auto_low_power_trigger = BMA400_AUTO_LP_TIMEOUT_EN;
/* Set the timeout value as 1s (400*2.5ms) */
dev_setting[1].conf.auto_lp.auto_lp_timeout_threshold = 400;
/* Set the configurations in sensor */
rslt = bma400_set_device_setting(&dev_setting, 2, dev);
/* The sensor toggles between Low-power mode and Normal mode for every 10.24s
* as configured by user which can be tested and verfied by reading the power mode
* and printing it continuously as follows
* while (1) {
* rslt = bma400_get_power_mode(&power_mode, dev);
* printf("\n POWER MODE : %d",power_mode);
* }
* The power mode toggling can be seen from the printed console output
*/
}
return rslt;
}
In this example, the wakeup interrupt is defined to detect the non-flat position of the sensor. A threshold (94mg) is configured, so if the sensor tilt over the threshold, the wakeup interrupt will be set and switch the BMA400 to normal mode(auto-wakeup function).
Once entered into normal mode, the Gen1 will continuously detect the sensor position, and check if the sensor returns back into the range of flat, this range should be smaller than 94mg(here gen1 threshold is set to 40mg). So, once the gen1 condition is fulfilled, BMA400 will automatically switch to low power mode by auto-lowpower function.
Range = 4g, ODR=100, OSR=1
Register (0x2B) AUTOLOWPOW_1.gen1_int = 1; set this bit the interrupt from Gen1 will trigger the switching from normal mode to low power mode
Register (0x3F) GEN1INT_CONFIG0:
Set gen1_act_x_en, gen1_act_y_en, gen1_act_z_en = 1 (enable all 3 axes)
gen1_data_src=1; select filter2 (fixed 100Hz) as data source
gen1_act_refu = 0; manual update(fixed reference, configured by host)
gen1_act_hyst = 0; disable
Register (0x40) GEN1INT_CONFIG1:
gen1_comb_sel = 1; logic AND
gen1_criterion_sel = 0; inactivity interrupt
This combination means that ALL (logic AND) three axes value should be WITHIN (inactivity interrupt) the threshold near the reference position.
Register (0x41) GEN1INT_CONFIG2:
gen1_int_thres = 5 (threshold = 5*8mg/lsb = 40mg)
Register (0x42) GEN1INT_CONFIG3/Register (0x43) GEN1INT_CONFIG31
gen1_int_dur = 100 (monitor duration = 100samples * 1/100Hz(filter2) = 1s)
Register (0x44) GEN1INT_CONFIG4 to Register (0x49) GEN1INT_CONFIG9:
gen1_int_th_refx/y/z = (0, 0, 512); means (0, 0, 1g )
Range sensitive, (512 lsb represents 1g) @4g range
So in this configuration gen1 interrupt will be set only if:
Condition 1: (|a_x-ref_x| <40mg) AND (|a_y-ref_y|<40mg) AND (|a_z-ref_z|<40mg)
Condition 2: more than 100 continuous past samples fulfill condition 1
wkup_int = 1; use wake-up interrupt for auto-wake-up
Register (0x2F) WKUP_INT_CONFIG0
wkup_x_en, wkup_y_en, wkup_z_en = 1; enable three axes
num_of_samples = 3; 4(=3+1) continuous samples will be monitored in low power mode
wkup_refu = 0; manual update, configured by host
Register (0x30) WKUP_INT_CONFIG1
int_wkup_thres = 3; (3 * 1/32 * 1g = 94mg) @4g range, range sensitive, 8 bit unsigned value
Register (0x31) WKUP_INT_CONFIG2 to Register (0x33) WKUP_INT_CONFIG4
int_wkup_refx/y/z = (0, 0, 32); (0, 0, 1g) @4g range, range sensitive
So the wakeup interrupt will be set only if:
Condition 1: (|acc_x - ref_x| > 94mg) OR (|acc_y - ref_y| > 94mg) OR (|acc_z - ref_z| > 94mg)
Condition 2: more than 4 continuous past samples fulfill condition 1
The sample codes are shown below, and please ignore the configuration of Gen2, which is not relative to this case.
/* #### BMA400 Auto wakeup and Auto low power features
##### _Usage of auto wakeup and auto low power features in sensor_ */
/* Auto wake-up based on position testing setting */
int8_t bma400_position_autowakeup_auto_lp(struct bma400_dev *dev)
{
int8_t rslt = 0;
uint8_t power_mode;
uint16_t int_status;
struct bma400_device_setting dev_setting[2];
/* Interrupt configuration structure */
struct interrupt_enable int_en;
/* Selecting auto wakeup on wakeup interrupt event */
dev_setting[0].type = BMA400_AUTOWAKEUP_INT;
/* Selecting auto low power mode*/
dev_setting[1].type = BMA400_AUTO_LOW_POWER;
/* Get the previously set settings */
rslt = bma400_get_device_setting(&dev_setting, 2, dev);
if (rslt == BMA400_OK) {
dev_setting[0].conf.wakeup.wakeup_axes_en = BMA400_XYZ_AXIS_EN
dev_setting[0].conf.wakeup.wakeup_ref_update = BMA400_MANUAL_UPDATE
dev_setting[0].conf.wakeup.sample_count = BMA400_SAMPLE_COUNT_4
dev_setting[0].conf.wakeup.int_wkup_threshold = 3
dev_setting[0].conf.wakeup.int_wkup_ref_x = 0
dev_setting[0].conf.wakeup.int_wkup_ref_y = 0
dev_setting[0].conf.wakeup.int_wkup_ref_z = 32 /* (0, 0, 1g) */
dev_setting[0].conf.wakeup.int_map = BMA400_INT_CHANNEL_1
/* Enable auto low power on Gen1 trigger */
dev_setting[1].conf.auto_lp.auto_low_power_trigger = BMA400_AUTO_LP_GEN1_TRIGGER;
/* Set the configurations in sensor */
rslt = bma400_set_device_setting(&dev_setting, 2, dev);
if (rslt == BMA400_OK) {
/* Enable the Generic interrupts in the sensor */
int_en.int_sel = BMA400_AUTO_WAKEUP_EN;
int_en.conf = BMA400_ENABLE;
rslt = bma400_enable_interrupt(&int_en, 1, dev);
/* The sensor toggles between Low-power mode and Normal mode if tilt the device(sensor)
* or place it back to flat
* this can be verfied by reading the power mode and printing it continuously as follows
* while (1) {
* rslt = bma400_get_power_mode(&power_mode, dev);
* printf("\n POWER MODE : %d",power_mode);
* }
* The power mode toggling can be seen from the printed console output
*/
}
return rslt;
}
/*##### BMA400 Generic interrupt configuration
##### _Usage of Generic interrupt1 and 2 for activity/inactivity detection in sensor_ */
/* Generic Interrupt feature */
int8_t bma400_generic_interrupts_by_position(struct bma400_dev *dev)
{
int8_t rslt = 0;
/* Variable to store interrupt status */
uint16_t int_status;
/* Sensor configuration structure */
struct bma400_setting accel_settin[2];
/* Interrupt configuration structure */
struct interrupt_enable int_en[2];
/* Select the GEN1 and GEN2 interrupts for configuration */
accel_settin[0].type = BMA400_GEN1_INT;
accel_settin[1].type = BMA400_GEN2_INT;
/* Get the configurations set in the sensor */
rslt = bma400_get_sensor_setting(&accel_settin[0], 2, dev);
/* Modify the required parameters from the "gen_int" structure present
* inside the "bma400_setting" structure to configure the selected
* GEN1/GEN2 interrupts */
if (rslt == BMA400_OK) {
/* Set the GEN 1 interrupt for activity detection */
accel_settin[0].conf.gen_int.int_map = BMA400_INT_CHANNEL_2;
accel_settin[0].conf.gen_int.axes_sel = BMA400_XYZ_AXIS_EN;
accel_settin[0].conf.gen_int.criterion_sel = BMA400_INACTIVITY_INT;
accel_settin[0].conf.gen_int.evaluate_axes = BMA400_ALL_AXES_INT;
accel_settin[0].conf.gen_int.ref_update = BMA400_MANUAL_UPDATE;
accel_settin[0].conf.gen_int.data_src=BMA400_DATA_SRC_ACC_FILT2;
accel_settin[0].conf.gen_int.gen_int_thres = 0x05;
accel_settin[0].conf.gen_int.gen_int_dur = 100;
accel_settin[0].conf.gen_int.hysteresis = BMA400_HYST_0_MG;
accel_settin[0].conf.gen_int.int_thres_ref_x = 0;
accel_settin[0].conf.gen_int.int_thres_ref_y = 0;
accel_settin[0].conf.gen_int.int_thres_ref_z = 512; /* (0, 0, 1g) for gen1 reference. */
/* Set the GEN 2 interrupt for in-activity detection */
accel_settin[1].conf.gen_int.int_map = BMA400_INT_CHANNEL_2;
accel_settin[1].conf.gen_int.axes_sel = BMA400_XYZ_AXIS_EN;
accel_settin[1].conf.gen_int.criterion_sel = BMA400_INACTIVITY_INT;
accel_settin[1].conf.gen_int.evaluate_axes = BMA400_ANY_AXES_INT;
accel_settin[1].conf.gen_int.ref_update = BMA400_ONE_TIME_UPDATE;
accel_settin[1].conf.gen_int.data_src=BMA400_DATA_SRC_ACC_FILT1;
accel_settin[1].conf.gen_int.gen_int_thres = 0x10;
accel_settin[1].conf.gen_int.gen_int_dur = 0x01;
accel_settin[1].conf.gen_int.hysteresis = BMA400_HYST_0_MG;
/* Set the configurations in the sensor */
rslt = bma400_set_sensor_setting(&accel_settin[0], 2, dev);
if (rslt == BMA400_OK) {
/* Enable the Generic interrupts in the sensor */
int_en[0].int_sel = BMA400_GEN1_INT_EN;
int_en[0].conf = BMA400_ENABLE;
int_en[1].int_sel = BMA400_GEN2_INT_EN;
int_en[1].conf = BMA400_DISABLE;
rslt = bma400_enable_interrupt(&int_en[0], 2, dev);
}
}
}
}
return rslt;
}
This example is suitable for activity detection, where the device (sensor) is not placed into a fixed position, like wearable devices. To achieve this, Gen1 can be used to detect status that the sensor is in stable status, like the user wearing the device is in sleep, standstill, and trigger the Auto-Lowpower to switch BMA400 into low power mode; in contrary, the auto-wakeup function can be configured to detect the activity, and wake the sensor into normal mode.
The key differences between the implementation of actvity detecting(this example) and position detecting (example described in chapter 4.2), is the reference update mode and the relative threshold and number of samples. For activity detection, the reference update mode of wakeup & Gen1 functions should be configured as everytime which means every sample of acceleration will be taken as the reference of the coming wakeup & Gen1 calculation, so the reference will be updated continuously and regardless of any fixed position(as described in chapter 4.2).
Range = 4g, ODR=100, OSR=1
Auto-lowpower relative
Register (0x2B) AUTOLOWPOW_1.gen1_int = 1; set this bit the interrupt from Gen1 will trigger the switching from normal mode to low power mode
Generic interrupt 1 (gen1) relative
Register (0x3F) GEN1INT_CONFIG0:
Set gen1_act_x_en, gen1_act_y_en, gen1_act_z_en = 1 (enable all 3 axes)
gen1_data_src=1; select filter2 (fixed 100Hz) as data source
gen1_act_refu = 2; everytime (every time automatically updated from the selected filter data)
gen1_act_hyst = 0; disable
Register (0x40) GEN1INT_CONFIG1:
gen1_comb_sel = 1; logic AND
gen1_criterion_sel = 0; inactivity interrupt
This combination means that ALL (logic AND) three axes value should be WITHIN (inactivity interrupt) the threshold near the reference position.
Register (0x41) GEN1INT_CONFIG2:
gen1_int_thres = 5 (threshold = 5*8mg/lsb = 40mg)
Register (0x42) GEN1INT_CONFIG3/Register (0x43) GEN1INT_CONFIG31
gen1_int_dur = 100 (monitor duration = 100samples * 1/100Hz(filter2) = 1s)
Register (0x44) GEN1INT_CONFIG4 to Register (0x49) GEN1INT_CONFIG9:
gen1_int_th_refx/y/z, no need to configure, if reference update mode is everytime
So in this configuration gen1 interrupt will be set only if:
Condition 1: (|a_x_current - ref_x_last| < 40mg) AND (|a_y_current - ref_y_last | < 40mg)
AND (|a_z_current - ref_z_last| < 40mg)
Condition 2: more than 100 continuous past samples fulfill condition 1
Auto-wakeup relative
Register (0x2D) AUTOWAKEUP_1
wkup_int = 1; use wake-up interrupt for auto-wake-up(switch BMA400 to normal)
Wakeup interrupt relative
Register (0x2F) WKUP_INT_CONFIG0
wkup_x_en, wkup_y_en, wkup_z_en = 1; enable three axes
num_of_samples = 3; 4(=3+1) continuous samples will be monitored in low power mode; the less the samples, the more sensitive the wakeup detecting
wkup_refu = 2; everytime update
Register (0x30) WKUP_INT_CONFIG1
int_wkup_thres = 3; (3 * 1/32 * 1g = 94mg) @4g range, range sensitive, 8 bit unsigned value
The smaller the threshold, the more sensitive the wakeup detecting
Register (0x31) WKUP_INT_CONFIG2 to Register (0x33) WKUP_INT_CONFIG4
int_wkup_refx/y/z no need to configure if update mode is everytime
So the wakeup interrupt will be set only if:
Condition 1: (|acc_x_current - ref_x_last| > 94mg) OR (|acc_y_current - ref_y_last| > 94mg)
OR (|acc_z_current - ref_z_last| > 94mg)
Condition 2: more than 4 continuous past samples fulfill condition 1
The sample codes are shown below, and please ignore the configuration of Gen2, which is not relative to this case.
In this example, wakeup interrupt is mapped to INT1 pin while GEN1 interrupt to INT2 pin.
Since the INT1 & GEN1 is configured mutually-exclusive, so mapping them to one interrupt pin will result in confusion.
/* ######## ------------------- ################ */
/* #### BMA400 Auto wakeup and Auto low power features
##### _Usage of auto wakeup and auto low power features in sensor_ */
/* Auto wake-up based on activity testing setting */
int8_t bma400_activity_autowakeup_auto_lp(struct bma400_dev *dev)
{
int8_t rslt = 0;
uint8_t power_mode;
uint16_t int_status;
struct bma400_device_setting dev_setting[2];
/* Interrupt configuration structure */
struct interrupt_enable int_en;
/* Selecting auto wakeup on wakeup interrupt event */
dev_setting[0].type = BMA400_AUTOWAKEUP_INT;
/* Selecting auto low power mode*/
dev_setting[1].type = BMA400_AUTO_LOW_POWER;
/* Get the previously set settings */
rslt = bma400_get_device_setting(&dev_setting, 2, dev);
if (rslt == BMA400_OK) {
dev_setting[0].conf.wakeup.wakeup_axes_en = BMA400_XYZ_AXIS_EN
dev_setting[0].conf.wakeup.wakeup_ref_update = BMA400_EVERY_TIME_UPDATE
dev_setting[0].conf.wakeup.sample_count = BMA400_SAMPLE_COUNT_4
dev_setting[0].conf.wakeup.int_wkup_threshold = 3
/* dev_setting[0].conf.wakeup.int_wkup_ref_x = 0 */
/* dev_setting[0].conf.wakeup.int_wkup_ref_y = 0 */
/* dev_setting[0].conf.wakeup.int_wkup_ref_z = 32 (0, 0, 1g) */
dev_setting[0].conf.wakeup.int_map = BMA400_INT_CHANNEL_1
/* Enable auto low power on Gen1 trigger */
dev_setting[1].conf.auto_lp.auto_low_power_trigger = BMA400_AUTO_LP_GEN1_TRIGGER;
/* Set the configurations in sensor */
rslt = bma400_set_device_setting(&dev_setting, 2, dev);
if (rslt == BMA400_OK) {
/* Enable the Generic interrupts in the sensor */
int_en.int_sel = BMA400_AUTO_WAKEUP_EN;
int_en.conf = BMA400_ENABLE;
rslt = bma400_enable_interrupt(&int_en, 1, dev);
/* The sensor toggles between Low-power mode and Normal mode if tilt the device(sensor)
* or place it back to flat
* this can be verfied by reading the power mode and printing it continuously as follows
* while (1) {
* rslt = bma400_get_power_mode(&power_mode, dev);
* printf("\n POWER MODE : %d",power_mode);
* }
* The power mode toggling can be seen from the printed console output
*/
}
return rslt;
}
/*##### BMA400 Generic interrupt configuration
##### _Usage of Generic interrupt1 and 2 for activity/inactivity detection in sensor_ */
/* Generic Interrupt feature */
int8_t bma400_generic_interrupts_by_activity(struct bma400_dev *dev)
{
int8_t rslt = 0;
/* Variable to store interrupt status */
uint16_t int_status;
/* Sensor configuration structure */
struct bma400_setting accel_settin[2];
/* Interrupt configuration structure */
struct interrupt_enable int_en[2];
/* Select the GEN1 and GEN2 interrupts for configuration */
accel_settin[0].type = BMA400_GEN1_INT;
accel_settin[1].type = BMA400_GEN2_INT;
/* Get the configurations set in the sensor */
rslt = bma400_get_sensor_setting(&accel_settin[0], 2, dev);
/* Modify the required parameters from the "gen_int" structure present
* inside the "bma400_setting" structure to configure the selected
* GEN1/GEN2 interrupts */
if (rslt == BMA400_OK) {
/* Set the GEN 1 interrupt for activity detection */
accel_settin[0].conf.gen_int.int_map = BMA400_INT_CHANNEL_2;
accel_settin[0].conf.gen_int.axes_sel = BMA400_XYZ_AXIS_EN;
accel_settin[0].conf.gen_int.criterion_sel = BMA400_INACTIVITY_INT;
accel_settin[0].conf.gen_int.evaluate_axes = BMA400_ALL_AXES_INT;
accel_settin[0].conf.gen_int.ref_update = BMA400_EVERY_TIME_UPDATE;
accel_settin[0].conf.gen_int.data_src=BMA400_DATA_SRC_ACC_FILT2;
accel_settin[0].conf.gen_int.gen_int_thres = 0x05;
accel_settin[0].conf.gen_int.gen_int_dur = 100;
accel_settin[0].conf.gen_int.hysteresis = BMA400_HYST_0_MG;
/* accel_settin[0].conf.gen_int.int_thres_ref_x = 0; */
/* accel_settin[0].conf.gen_int.int_thres_ref_y = 0; */
/* accel_settin[0].conf.gen_int.int_thres_ref_z = 512; */ /* (0, 0, 1g) for gen1 reference, can be ignored here. */
/* Set the GEN 2 interrupt for in-activity detection */
accel_settin[1].conf.gen_int.int_map = BMA400_INT_CHANNEL_2;
accel_settin[1].conf.gen_int.axes_sel = BMA400_XYZ_AXIS_EN;
accel_settin[1].conf.gen_int.criterion_sel = BMA400_INACTIVITY_INT;
accel_settin[1].conf.gen_int.evaluate_axes = BMA400_ANY_AXES_INT;
accel_settin[1].conf.gen_int.ref_update = BMA400_ONE_TIME_UPDATE;
accel_settin[1].conf.gen_int.data_src=BMA400_DATA_SRC_ACC_FILT1;
accel_settin[1].conf.gen_int.gen_int_thres = 0x10;
accel_settin[1].conf.gen_int.gen_int_dur = 0x01;
accel_settin[1].conf.gen_int.hysteresis = BMA400_HYST_0_MG;
/* Set the configurations in the sensor */
rslt = bma400_set_sensor_setting(&accel_settin[0], 2, dev);
if (rslt == BMA400_OK) {
/* Enable the Generic interrupts in the sensor */
int_en[0].int_sel = BMA400_GEN1_INT_EN;
int_en[0].conf = BMA400_ENABLE;
int_en[1].int_sel = BMA400_GEN2_INT_EN;
int_en[1].conf = BMA400_DISABLE; /* int this case, gen2 is disabled */
rslt = bma400_enable_interrupt(&int_en[0], 2, dev);
}
}
}
}
return rslt;
}