The driver is for the usage with the ESP8266 and esp-open-rtos. If you can't find it in folder extras/MMA845X of original repository, it is not yet merged. Please take a look to branch MMA845X of my fork in that case.
It is also working with ESP32 and ESP-IDF using a wrapper component for ESP8266 functions, see folder components/esp8266_wrapper
, as well as Linux based systems using a wrapper library.
The driver can be used with MMA8451Q, MMA8452Q, and MMA8453Q.
MMA845X sensors are smart, low-power, 3-axis accelerometers connected to I2C with a full scale of up to ±8 g.
Main features of the sensor are:
- ±2 g/±4 g/±8 g dynamically selectable full scale
- output data rates (ODR) from 1.56 Hz to 800 Hz
- 14/12/10-bit and 8-bit digital output
- two programmable interrupt pins for seven interrupt sources
- inertial event detection (freefall or motion detection)
- transient event detection (jolt detection)
- pulse detection (single and double tap detection)
- orientation detection (portrait and landscape) with programmable hysteresis (only MMA8451Q)
- automatic ODR change for auto-wake and return to sleep
- embedded 32-sample FIFO (only MMA8451Q)
- embedded high-pass filter
MMA845X sensors provides different operating modes. There are two basic modes, the standby mode and the active mode.
Standby mode is started automatically after the power up boot sequence. In this mode, only digital blocks are enabled. Analog subsystems and the internal clocks are disabled. Registers are preserved and accessible for read and write operations. The standby mode is used to configure the sensor.
Avtive mode is the measurement mode. Once the sensor is configured, it can be switched to active mode to start measurements with a resolution of 14 bit with MMA8451Q, 12 bit with MMA8452Q, or 10 bit with MMA8453Q at a defined output data rate (ODR).
In active mode, the sensor can operate in two different submodes using different output data rates (ODR)
- the wake mode with high output data rate and
- the sleep mode with a reduced output data rate.
The transition between these two active modes is automatic based on an inactivity timer and four of the interrupt sources. The sensor enters automatically the sleep mode after not detecting an interrupt for more than a user-defined timeout. When one of the following interrupts occurs, it will revert automatically from sleep to wake mode: tap detection, orientation detection, motion or freefall detection, and transient detection.
In both active modes, measurements are made at a defined output rate. The following table shows the output data rates (ODR) supported by the sensor in wake mode and in sleep mode.
ODR | Driver symbol in wake mode | Driver symbol in sleep mode |
---|---|---|
1.56 Hz | mma845x_odr_1_56 |
mma845x_aslp_rate_1_56 |
6.25 Hz | mma845x_odr_6_25 |
mma845x_aslp_rate_6_25 |
12.5 Hz | mma845x_odr_12_5 |
mma845x_aslp_rate_12_5 |
50 Hz | mma845x_odr_50 |
mma845x_aslp_rate_50 |
100 Hz | mma845x_odr_100 |
- |
200 Hz | mma845x_odr_200 |
- |
400 Hz | mma845x_odr_400 |
- |
800 Hz | mma845x_odr_800 |
- |
In the wake mode as well as the sleep mode, the sensor can operate in different oversampling modes that define the oversampling rate and affect thus the power consumption of the sensor.
Mode | Driver symbol |
---|---|
Normal | mma845x_normal |
Low Noise, Low Power | mma845x_low_noise |
Low Power | mma845x_low_power |
High Resolution | mma845x_high_res |
According to the selected oversampling mode (also called power scheme) the following oversampling rates are used by the sensor for the supported ODRs.
ODR | Low Power | Low Noise | Normal | High Resolution |
---|---|---|---|---|
1.56 Hz | 16 | 32 | 128 | 1024 |
6.25 Hz | 4 | 8 | 32 | 256 |
12.5 Hz | 2 | 4 | 16 | 128 |
50 Hz | 2 | 4 | 4 | 32 |
100 Hz | 2 | 4 | 4 | 16 |
200 Hz | 2 | 4 | 4 | 8 |
400 Hz | 2 | 4 | 4 | 4 |
800 Hz | 2 | 2 | 2 | 2 |
The simplest way to use the sensor is to initialize the sensor with mma845x_init_sensor
function and then switch it to active mode with mma845x_set_mode
function to start measurements with a given output data rate (ODR).
...
static mma845x_sensor_t* sensor;
...
if ((sensor = mma845x_init_sensor (I2C_BUS, MMA845X_I2C_ADDRESS_2, 0)))
{
...
mma845x_set_mode (sensor, mma845x_high_res, mma845x_odr_50, true, false);
...
}
...
In this example, a MMA845X sensor connected to I2C with address MMA845X_I2C_ADDRESS_2
is initialized and set to the high-resolution mode to start measurements with an output data rate (ODR) of 50 Hz. The additional boolean parameters in this example define that the noise reduction function with reduced full dynamic range is activated and that the fast reading of 8-bit output data is not used (see register CTRL_REG1
in datasheet of MMA8541Q for more details). Auto-sleep/auto-wake functionality is disabled per default.
Please note:
mma845x_init_sensor
function resets the sensor completely, switches it to the standby mode, and returns a pointer to a sensor device data structure on success. All registers are reset to default values and the embedded FIFO is cleared.- All sensor configurations should be done before calling
mma845x_set_mode
function. In particular, the interrupt configuration should be performed before to avoid loosing the first interrupt and locking the system.
The sensor determines periodically the accelerations for all axes and produces output data with the selected output data rate (ODR).
Raw output data (raw data) are given as 16-bit signed integer values in 2’s complement representation with a resolution of 14-bit for MMA8451Q, 12-bit for MMA8452Q, or 10-bit for MMA8453Q. These data are always left-aligned. The sensitivity depends on the selected full scale. MMA845X sensor allows to select the following full scales with the following sensitivities:
Full Scale | Driver symbol | 14-bit data MMA8451Q |
12-bit data MMA8452Q |
10-bit data MMA8451Q |
---|---|---|---|---|
±2 g | mma845x_scale_2_g |
1/4096 g | 1/1024 g | 1/256 g |
±4 g | mma845x_scale_4_g |
1/2048 g | 1/512 g | 1/128 g |
±8 g | mma845x_scale_8_g |
1/1024 g | 1/256 g | 1/64 g |
By default, a full scale of ±2 g is used. mma845x_set_scale
function can be used to change it.
mma845x_set_scale(sensor, mma845x_scale_4_g);
Please note: In fast read mode, only the high byte of the 16-bit output data is used.
To get the information whether new data are available, the user task can either use
- the
mma845x_new_data
function to check periodically whether new output data are available, or - the data ready interrupt which is generated as soon as new output data are available (see below).
Last measurement results can then be fetched either
- as raw data using
mma845x_get_raw_data
function or - as floating point values measured in g using
mma845x_get_float_data
function.
It is recommended to use mma845x_get_float_data
function since it already converts measurement results to real values according to the selected full scale.
void user_task_periodic(void *pvParameters)
{
mma845x_float_data_t data;
while (1)
{
// execute task every 100 ms
vTaskDelay (100/portTICK_PERIOD_MS);
...
// test for new data
if (!mma845x_new_data (sensor))
continue;
// fetch new data
if (mma845x_get_float_data (sensor, &data))
{
// do something with data
...
}
}
}
Please note:
mma845x_get_float_data
and mma845x_get_raw_data
functions always return the last available results. If these functions are called more often than measurements are taken, some measurement results are retrieved multiple times. If these functions are called too rarely, some measurement results will be lost.
MMA845X sensors have a built-in high-pass filter to remove the offset and slower changing acceleration data, also called static acceleration. Please refer the application note AN4071 for more details.
The high-pass filter can be applied to
- the raw output data,
- the data used for transient event detection function (jolt detection), and
- the data used for pulse detection function (single and double tap detection)
The cutoff frequency of the high-pass filter depends on the output data rate (ODR) and the selected oversampling mode. For each output data rate and oversampling mode, four different HPF cutoff frequencies can be selected (see register HP_FILTER_CUTOFF
in the datasheet of MMA8541Q).
The cutoff frequency is selected with mma845x_config_hpf
function. The additional boolean parameters is used to enable or disable high-pass filtering for raw output data. For other functions, the high-pass filter is enabled and disabled in the configuration of the function (see below).
...
// configure HPF
mma845x_config_hpf (sensor, 0, true);
...
In order to limit the rate at which the host processor has to fetch the data, the MMA8451Q embeds a first-in first-out buffer (FIFO). This is in particular helpful at high output data rates. The FIFO buffer can work in four different modes and is able to store up to 32 accelerometer samples. Please refer the datasheet of MMA8541Q or application note AN4073 for more details.
FIFO mode | Driver symbol |
---|---|
FIFO disabled | mma845x_disabled |
Circular buffer mode | mma845x_circular |
Fill buffer mode | mma845x_fill |
Trigger buffer mode | mma845x_trigger |
While the FIFO in fill buffer mode stops accepting new samples when overflowed, the FIFO contains always the most recent samples in circular buffer mode when overflowed.
In trigger buffer mode FIFO operates in circular buffer mode up to the number of samples set in the watermark. When one of the defined triggers occur, the FIFO will switch to fill buffer mode and collect samples until the buffer if full. This allows to collect data both before and after a trigger event.
The FIFO mode can be set using mma845x_set_fifo_mode
function. This function takes as parameters
- the FIFO mode,
- a watermark value, and
- a set of trigger events that are used in trigger buffer mode.
The additional boolean parameter defines whether the FIFO gate function is used. Whith FIFO gate function, the FIFO blocks on wake / sleep transitions and preserves the contents of the FIFO buffer.
The watermark level is used by the sensor to set a watermark and to generate an interrupt if the FIFO content exceeds that level. It can be used to collect a minimum number of acceleration samples with the sensor before fetching the data as a single read operation from the sensor.
...
// activate FIFO mode
mma845x_fifo_trigger_t trigger = {};
trigger.orientation = true;
trigger.transient = true;
mma845x_set_fifo_mode (sensor, mma845x_trigger, 20, trigger, false);
...
In this example, the FIFO is used in trigger buffer mode. The FIFO always collects the last 20 acceleration data samples in circular buffer mode. Once the orientation is changed or a transient event is detected, the FIFO fills the remaining 12 acceleration samples before blocking.
Please note:
- To flush the FIFO at any time, set the FIFO mode to
mma845x_disabled
and back to the desired FIFO mode. - The FIFO is cleared also on transition from standby mode to active mode, or during an automatic ODR change on auto-sleep/wake transitions when FIFO gate function is disabled.
To fetch data from the FIFO, simply use either
- the
mma845x_get_raw_data_fifo
function to all get raw output data stored in FIFO or - the
mma845x_get_float_data_fifo
function to get all data stored in FIFO and converted to real values in g.
Both functions flush the FIFO implicitly and return the number of samples read from the FIFO.
void user_task_periodic (void *pvParameters)
{
mma845x_float_data_fifo_t fifo;
while (1)
{
// execute task every 100 ms
vTaskDelay (100/portTICK_PERIOD_MS);
...
// test for new data
if (!mma845x_new_data (sensor))
continue;
// fetch data from fifo
uint8_t num = mma845x_get_float_data_fifo (sensor, fifo);
for (int i=0; i < num; i++)
{
// do something with data fifo[i] ...
}
}
The MMA845X sensors support two dedicated interrupt signals INT1
and INT2
as well as different types of interrupts:
- data interrupts (data ready and FIFO status),
- inertial event detection interrupts (freefall and motion detection),
- transient event detection interrupts (jolt detection),
- orientation detection interrupts (landscape and portrait detection),
- pulse detection interrupts (single and double tap detection),
- auto-sleep/wake transition interrupts.
Each of these interrupt types can be enabled or disabled separately with mma845x_enable_int
function. By default all interrupts are disabled. All interrupts can be routed to both interrupt signals.
mma845x_enable_int (sensor, mma845x_int_event, mma845x_int2_signal, true);
Whenever an interrupt is generated, the mma845x_get_int_status
function can be used to determine what type of interrupt is generating the interrupt signal.
mma845x_int_status_t int_status;
mma845x_get_int_status (sensor, &int_status);
if (int_status.event)
{
// inertial event happened
...
}
if (int_status.orientation)
{
// orientation changed
...
}
...
For each type of interrupt there are further functions to get more information about the source of the interrupt, see below.
Following sources can generate data interrupts:
Interrupt source | Driver symbol |
---|---|
Output data become ready to read | mma845x_int_data_ready |
FIFO content exceeds the watermark level | mma845x_int_fifo_watermark |
FIFO is completely filled | mma845x_int_fifo_overrun |
Each of these interrupt sources can be enabled or disabled separately with the mma845x_enable_int
function.
mma845x_enable_int (sensor, mma845x_int_data_ready, mma845x_int2_signal, true);
Whenever a data interrupt is generated, the mma845x_get_int_data_source
function can be used to determine the source of the data interrupt. This function returns a data structure of type mma845x_int_data_source_t
that contain a boolean member for each source that can be tested for true.
void int_handler ()
{
mma845x_int_data_source_t data_src;
// get the source of the interrupt
mma845x_int_data_source_t data_src;
mma845x_get_int_data_source (sensor, &data_src);
// in case of data ready interrupt read one data sample and do something with data
if (data_src.data_ready)
... // read data
// in case of FIFO interrupts read the whole FIFO and do something with data
else if (data_src.fifo_watermark || data_src.fifo_overrun)
... // read data
...
}
Please note: While FIFO interrupts are reset as soon as the interrupt source is read, the data ready interrupts are not reset until the data has been read.
The motion and freefall detection function of the sensor allows to generate interrupts when certain inertial events occur (event interrupts), that is, the static acceleration of enabled axes is higher or lower than a defined threshold. If activated, the acceleration of each enabled axis is compared with the defined threshold to check whether it is below or above the threshold. The results of all comparisons are then combined AND or OR to generate the interrupt signal.
The configuration of the threshold, the activated comparisons and the selected AND/OR combination allows to recognize special situations:
- motion detection refers the special condition that the acceleration measured along any axis is above the defined threshold (OR =
mma845x_motion
). - Free fall detection refers the special condition that the acceleration measured along all the axes goes to zero (AND =
mma845x_freefall
).
Please refer the datasheet of MMA8541Q or application note AN4070 for more details.
Inertial event interrupts can be configured with the mma845x_set_int_event_config
function, which takes the configuration of type mma845x_int_event_config_t
as parameter. The configuration defines
- the inertial event that should be detected (
mma845x_motion
ormma845x_freefall
), - the threshold value given in 0.063 g used for the event detection,
- the axes which are used for the event detection,
- a debounce counter that defines the minimum time over which the interrupt condition has to be present,
- the behavior of the debounce counter when interrupt condition is not given any longer,
- whether the interrupt should be latched until the interrupt source is read, and
- whether the event detection is also active in sleep mode and can wake up the sensor.
Once the inertial event detection is configured, mma845x_enable_int
function is used to enable the interrupt. The interrupt signal to which the interrupt is routed is given as parameter. For example, motion detection interrupts generated on signal INT1
could be configured and enabled as following:
mma845x_int_event_config_t event_config;
event_config.mode = mma845x_motion; // OR-condition
event_config.threshold = 2; // 0.063 g/count = 0.1266 g
event_config.x_enabled = true; // x-axis is used for event detection
event_config.y_enabled = true; // y-axis is used for event detection
event_config.z_enabled = false; // z-axis is not used for event detection
event_config.debounce_cnt = 5; // 100 ms at ODR=50Hz in normal oversampling mode
event_config.debounce_cnt_clr = false; // increment and decrement debounce counter
event_config.latch = true; // interrupt is latched until its source is read
event_config.sleep_active = true; // active in sleep mode
mma845x_set_int_event_config (sensor, &event_config);
mma845x_enable_int (sensor, mma845x_int_event1, mma845x_int1_signal, true);
Please note: The time that is defined by the debounce counter depends on current ODR and the selected oversampling mode, see register FF_MT_COUNT
in datasheet of MMA8541Q for more details. In the example, the time step counted by the debounce counter is 20 ms.
As with data interrupts, mma845x_get_int_event_source
function can be used to determine the source of an inertial event interrupt whenever it is generated. This function returns a data structure of type mma845x_int_event_source_t
which contains a boolean member for each source that can be tested for true.
void int_handler ()
{
mma845x_int_status_t int_status;
mma845x_get_int_status (sensor, &int_status);
...
// in case of inertial event interrupt
if (int_status.event)
{
// get the source of the interrupt and reset *INTx* signals
mma845x_int_event_source_t event_src = {};
mma845x_get_int_event_source (sensor, &event_src);
// test for the interrupt source
if (event_src.x_motion) ... // do something
if (event_src.y_motion) ... // do something
if (event_src.z_motion) ... // do something
...
}
...
}
Please note: If the interrupt is configured to be latched, the interrupt signal is active until the interrupt source is read. Otherwise the interrupt signal is only active as long as the interrupt condition is present.
The transient event detection function of the sensor allows to generate interrupts when the change in acceleration of any axis exceeds a defined threshold. For this purpose, only the dynamic part of the measured acceleration data is used. Therefore, the data used for transient event detection are high-pass filtered to remove the static part of the acceleration data.
Please note: High-pass filtering of data for transient event detection can be disabled. In that case the transient event detection works like motion detection.
Please refer the datasheet of MMA8541Q or application note AN4071 for more details.
Transient event interrupts can be configured with the mma845x_set_int_transient_config
function, which takes the configuration of type mma845x_int_transient_config_t
as parameter. The configuration defines
- the threshold value given in 0.063 g used for the event detection,
- the axes which are used for the event detection,
- a debounce counter that defines the minimum time over which the interrupt condition has to be present,
- the behavior of the debounce counter when interrupt condition is not given any longer,
- whether the interrupt should be latched until the interrupt source is read,
- whether the high-pass filtering of data should be disabled, and
- whether the event detection is also active in sleep mode and can wake up the sensor.
Once the transient event detection is configured, mma845x_enable_int
function is used to enable the interrupt. The interrupt signal to which the interrupt is routed is given as parameter. For example, transient event interrupts generated on signal INT1
could be configured and enabled as following:
mma845x_int_transient_config_t trans_config;
trans_config.threshold = 8; // 0.063 g/count = 0.504 g
trans_config.x_enabled = true; // x-axis is used for event detection
trans_config.y_enabled = true; // y-axis is used for event detection
trans_config.z_enabled = false; // z-axis is not used for event detection
trans_config.debounce_cnt = 2; // 40 ms at ODR=50Hz in normal oversampling mode
trans_config.debounce_cnt_clr = true; // increment and clear debounce counter
trans_config.latch = true; // interrupt is latched until its source is read
trans_config.hpf_bypassed = false; // HPF is not bypassed (default)
trans_config.sleep_active = true; // active in sleep mode
mma845x_set_int_transient_config (sensor, &trans_config);
mma845x_enable_int (sensor, mma845x_int_transient, mma845x_int1_signal, true);
Please note: The time that is defined by the debounce counter depends on current ODR and the selected oversampling mode, see register TRANSIENT_COUNT
in datasheet of MMA8541Q for more details. In the example, the time step counted by the debounce counter is 20 ms.
As with other interrupts, mma845x_get_int_transient_source
function can be used to determine the source of a transient event interrupt whenever it is generated. This function returns a data structure of type mma845x_int_transient_source_t
which contains a boolean member for each source that can be tested for true.
void int_handler ()
{
mma845x_int_status_t int_status;
mma845x_get_int_status (sensor, &int_status);
...
// in case of transient event interrupt
if (int_status.transient)
{
// get the source of the interrupt and reset *INTx* signals
mma845x_int_transient_source_t trans_src = {};
mma845x_get_int_transient_source (sensor, &trans_src);
// test for the interrupt source
if (trans_src.x_event) ... // do something
if (trans_src.y_event) ... // do something
if (trans_src.z_event) ... // do something
}
...
}
Please note: If the interrupt is configured to be latched, the interrupt signal is active until the interrupt source is read. Otherwise the interrupt signal is only active as long as the interrupt condition is present.
The signature of accelerations, that is, the sequence of acceleration data over time measured along certain axes can be used to detect single and double taps.
A single tap is given when the acceleration due to a pulse exceeds a specified acceleration threshold and crosses up and down within a specified time limit, also called pulse limit. A double tap is given, if a second tap is recognized within a time limit called pulse window. This pulse window starts after waiting a specified pulse latency after the first tap.
The data used for pulse detection are high-pass filtered to remove the static part of the acceleration data.
Please refer the datasheet of MMA8541Q or application note AN4072 for more details.
Pulse detection interrupts can be configured with the mma845x_set_int_pulse_config
function, which takes the configuration of type mma845x_int_pulse_config_t
as parameter. The configuration defines
- the threshold values given in 0.063 g used for the pulse detection for each axis,
- the axes enabled for the single tap detection,
- the axes enabled for the double tap detection,
- a pulse limit that defines the time limit within the acceleration must cross up and down the threshold
- a pulse latency that defines the time before the second tap event can be recognized for double tap detection
- a pulse window that defines the time window within the second tap have to be recognized for double tap detection
- whether the interrupt should be latched until the interrupt source is read,
- whether the high-pass filtering of data should be disabled,
- whether the low-pass filtering of data should be enabled, and
- whether the event detection is also active in sleep mode and can wake up the sensor.
Once the pulse detection is configured, mma845x_enable_int
function is used to enable the interrupt. The interrupt signal to which the interrupt is routed is given as parameter. For example, single tap detection interrupts generated on signal INT1
could be configured and enabled as following:
mma845x_int_pulse_config_t pulse_config;
pulse_config.x_threshold = 25; // 0.063 g/count = 1.575 g
pulse_config.y_threshold = 25; // 0.063 g/count = 1.575 g
pulse_config.z_threshold = 42; // 0.063 g/count = 1.65 g
pulse_config.x_single = true; // single tap detection in x direction enabled
pulse_config.x_double = false; // double tap detection in x direction enabled
pulse_config.y_single = true; // single tap detection in y direction enabled
pulse_config.y_double = false; // double tap detection in y direction enabled
pulse_config.z_single = true; // single tap detection in z direction enabled
pulse_config.z_double = false; // double tap detection in z direction enabled
pulse_config.pulse_limit = 10; // 50 ms at ODR=50 Hz in normal mode and no LPF
pulse_config.pulse_latency = 20; // 200 ms at ODR=50 Hz in normal mode and no LPF
pulse_config.pulse_window = 30; // 300 ms at ODR=50 Hz in normal mode and no LPF
pulse_config.latch = true; // interrupt is latched until its source is read
pulse_config.hpf_bypassed = false; // HPF is not bypassed (default)
pulse_config.lpf_enabled = false; // LPF is not used (default)
pulse_config.sleep_active = true; // active in sleep mode
mma845x_set_int_pulse_config (sensor, &pulse_config);
mma845x_enable_int (sensor, mma845x_int_pulse, mma845x_int1_signal, true);
Please note: The times that are defined as pulse limit, pulse latency and pulse window depend on current ODR, the selected oversampling mode, and whether low-pass filtering is enabled, see register PULSE_TMLT
, PULSE_LTCY
, and PULSE_WIND
in datasheet of MMA8541Q for more details. In the example, the pulse limit is given in 5 ms time steps while pulse latency and pulse window are given in 10 ms time steps.
As with other interrupts, mma845x_get_int_pulse_source
function can be used to determine the source of an transient event interrupt whenever it is generated. This function returns a data structure of type mma845x_int_pulse_source_t
which contains a boolean member for each source that can be tested for true.
void int_handler ()
{
mma845x_int_status_t int_status;
mma845x_get_int_status (sensor, &int_status);
...
// in case of tap detection interrupt
if (int_status.pulse)
{
// get the source of the interrupt and reset *INTx* signals
mma845x_int_pulse_source_t pulse_src = {};
mma845x_get_int_pulse_source (sensor, &pulse_src);
if (pulse.x_pulse) ... // do something
if (pulse.y_pulse) ... // do something
if (pulse.z_pulse) ... // do something
}
...
}
Please note: If the interrupt is configured to be latched, the interrupt signal is active until the interrupt source is read. Otherwise the interrupt signal is only active as long as the interrupt condition is present.
The orientation detection function (MMA8451Q only) uses the gravity and measured accelerations to determine the orientation of the sensor. Detected orientations supported by the sensor are:
Orientation | Driver symbol |
---|---|
Portrait Up | mma845x_portrait_up |
Portrait Down | mma845x_portrait_down |
Landscape Left | mma845x_lanscape_left |
Landscape Right | mma845x_lanscape_right |
Back | mma845x_back |
Front | mma845x_front |
The orientation of the device is given by one of the four landscape/portrait orientations in combination with front/back orientation.
The orientation detection function is configured with the mma845x_set_orientation_config
function, which takes the configuration of type mma845x_orientation_config_t
as parameter. This configuration defines
- a portrait/landscape orientation trip angle threshold,
- a hysteresis that is added to the trip angle threshold for smother transitions,
- a back/front orientation trip angle threshold,
- a z-lock angle threshold,
- a debounce counter that defines the minimum time over which the orientation has to be present,
- the behavior of the debounce counter when orientation is not given any longer, and
- whether the orientation detection is also active in sleep mode and can wake up the sensor.
Please refer the datasheet of MMA8541Q or application note AN4068 for more details.
Once the orientation detection function is configured, mma845x_get_orientation
function can be used to retrieve the current orientation of the sensor at any time. This function returns a datastructure of type mma845x_orientation_status_t
that consists of the two members portrait_landscape
and back_front
that contain the current orientation.
For example, the orientation detection could be configured and used as following:
...
mma845x_orientation_config_t orient_config;
orient_config.enabled = true; // enable portrait/landscape detection
orient_config.sleep_active = true; // active in sleep mode
orient_config.pl_threshold = 16; // trip angle 45°
orient_config.pl_hysteresis= 4; // hysteresis +-14°
orient_config.bf_threshold = 1; // 285° < z < 75° back->front,
// 105° < z < 255° front->back
orient_config.z_lock = 4; // z >= 29°
orient_config.debounce_cnt = 5; // 100 ms at ODR=50Hz in normal mode
orient_config.debounce_cnt_clr = false; // increment and decrement debounce counter
mma845x_set_orientation_config (sensor, &orient_config);
...
...
mma845x_orientation_status_t orientation;
if (mma845x_get_orientation (sensor, &orientation)
{
switch (orientation.portrait_landscape)
{
case mma845x_portrait_up: ... // do something
case mma845x_portrait_down: ... // do something
case mma845x_landscape_left: ... // do something
case mma845x_landscape_right: ... // do something
}
switch (orientation.back_front)
{
case mma845x_back: ... // do something
case mma845x_front: ... // do something
}
}
...
It is possible to generate interrupts when the orientation is changed. Once the orientation detection function is configured, mma845x_enable_int
function can used to enable the interrupt. The interrupt signal to which the interrupt is routed is given as parameter.
mma845x_enable_int (sensor, mma845x_int_orientation, mma845x_int1_signal, true);
mma845x_get_int_status
function in conjunction with mma845x_get_orientation
function can be used to determine the source of an orientation detection interrupt whenever it is generated. Member changed
of the data structure of type mma845x_orientation_status_t
can be tested for true.
void int_handler ()
{
mma845x_int_status_t int_status;
mma845x_get_int_status (sensor, &int_status);
...
// in case of orientation change detection interrupt
if (int_status.orientation)
{
// get the source of the interrupt and reset *INTx* signals
mma845x_orientation_status_t orient;
mma845x_get_orientation (sensor, &orient);
if (orient.changed)
{
.. // do something
}
}
...
}
The sensor can be configured to enter automatically in the sleep mode after not detecting an interrupt for more than a user-defined timeout. In sleep mode it operates with a reduced output data rate, also called auto-wake sample frequency. When one of the following interrupts occurs, it will revert automatically from sleep to wake mode:
- pulse detection (single or double tap)
- orientation changed detection (landscape and portrait detection)
- inertial event detection (motion or free fall)
- transient event detection (jolt detection)
The auto-sleep/wake function is configured with the mma845x_set_autosleep
function, which takes following parameters as configuration:
- the oversampling mode in sleep mode
- the auto-wake sample frequency (output data rate in sleep mode)
- an inactivity counter that defines the inactivity time-out before auto-sleep
- a flag that activates the auto-sleep/wake function
Please refer the datasheet of MMA8541Q or application note AN4074 for more details.
For example, the auto-sleep/wake function could be configured as following:
mma845x_set_autosleep (sensor, mma845x_normal, mma845x_aslp_rate_1_56, 10, true);
In this example, the normal oversampling mode and an output data rate of 1.56 Hz are used in sleep mode. The sensor enters the sleep mode after not detecting an interrupt for 10 time units, i.e., 3.2 seconds. The boolean parameter enables the auto-sleep/wake function.
Please note: The time that is defined by the inactivity counter depends on current ODR, see register ASLP_COUNT
in datasheet of MMA8541Q for more details. In the example, the time step counted by the inactivity counter is 320 ms.
It is possible to generate interrupts when the mode changes from wake to sleep and vise versa. Once the auto-sleep/wake function is configured, mma845x_enable_int
function can used to enable the interrupt. The interrupt signal to which the interrupt is routed is given as parameter.
mma845x_enable_int (sensor, mma845x_int_autosleep, mma845x_int1_signal, true);
mma845x_get_int_status
function in conjunction with mma845x_get_system_mode
function can be used to determine the source of an auto-sleep/wake function interrupt whenever it is generated. mma845x_get_system_mode
function returns the current system mode.
void int_handler ()
{
mma845x_int_status_t int_status;
mma845x_get_int_status (sensor, &int_status);
...
// in case of auto-sleep/wake interrupt
if (int_status.autosleep)
{
switch (mma845x_get_system_mode(sensor))
{
case mma845x_standby_mode: ... // do something
case mma845x_wake_mode: ... // do something
case mma845x_sleep_mode: ... // do something
}
}
...
}
By default, interrupt signals are low active. Using mma845x_config_int_signals
function, the polarity of the interrupt signal and the mode of the interrupt outputs can be changed.
Driver symbol | Meaning |
---|---|
mma845x_high_active |
Interrupt signal is high active |
mma845x_low_active |
Interrupt signal is low active (default) |
Driver symbol | Meaning |
---|---|
mma845x_push_pull |
Interrupt output is pushed/pulled |
mma845x_open_drain |
Interrupt output is open-drain |
The MMA845X is a very complex and flexible sensor with a lot of features. It can be used for a large number of different use cases. Since it is quite impossible to implement a high level interface which is generic enough to cover all the functionality of the sensor for all different use cases, there are two low level interface functions that allow direct read and write access to the registers of the sensor.
bool mma845x_reg_read (mma845x_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len);
bool mma845x_reg_write (mma845x_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len);
Please note These functions should only be used to do something special that is not covered by drivers's high level interface AND if you exactly know what you do and what it might affect. Please be aware that it might always affect the high level interface.
First, the hardware configuration has to be established.
Following figure shows a possible hardware configuration for ESP8266 and ESP32 to connect the sensor using an I2C interface .
+-----------------+ +----------+
| ESP8266 / ESP32 | | MMA845X |
| | | |
| GPIO 14 (SCL) >-----> SCL |
| GPIO 13 (SDA) <-----> SDA |
| GPIO 5 <------ INT1 |
| GPIO 4 <------ INT2 |
+-----------------+ +----------+
Dependent on the hardware configuration, the communication interface and interrupt settings have to be defined. In case ESP32 is used, the configuration could look like
#ifdef ESP_PLATFORM // ESP32 (ESP-IDF)
// user task stack depth for ESP32
#define TASK_STACK_DEPTH 2048
#else // ESP8266 (esp-open-rtos)
// user task stack depth for ESP8266
#define TASK_STACK_DEPTH 512
#endif // ESP_PLATFORM
// I2C interface defintions for ESP32 and ESP8266
#define I2C_BUS 0
#define I2C_SCL_PIN 14
#define I2C_SDA_PIN 13
#define I2C_FREQ I2C_FREQ_400K
// interrupt GPIOs defintions for ESP8266 and ESP32
#define INT1_PIN 5
#define INT2_PIN 4
First, I2C interfaces that are used have to be initialized.
i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ);
Once the interfaces are initialized, function mma845x_init_sensor
has to be called for each MMA845X sensor in order to initialize the sensor and to check its availability as well as its error state. This function returns a pointer to a sensor device data structure or NULL in case of error.
The parameter bus specifies the ID of the I2C bus to which the sensor is connected.
static mma845x_sensor_t* sensor;
For sensors connected to an I2C interface, a valid I2C slave address has to be defined as parameter addr.
sensor = mma845x_init_sensor (I2C_BUS, MMA845X_I2C_ADDRESS_2);
Optionally, you could wish to set some measurement parameters. For details see the sections above, the header file of the driver mma845x.h
, and of course the datasheet and application notes of the sensor.
Once the sensor is configured, the sensor mode has be set to start periodic measurement. This sensor mode can be changed anytime later.
...
// start periodic measurement with output data rate of 50 Hz
mma845x_set_mode (sensor, mma845x_odr_10, mma845x_high_res, true, true, true);
...
Finally, a user task that uses the sensor has to be created.
Please note: To avoid concurrency situations when driver functions are used to access the sensor, for example to read data, the user task must not be created until the sensor configuration is completed.
The user task can use different approaches to fetch new data. Either new data are fetched periodically or interrupt signals are used when new data are available or configured events occur.
If new data are fetched periodically the implementation of the user task is quite simple and could look like following.
void user_task_periodic(void *pvParameters)
{
// small delay before accessing the sensor the first time
vTaskDelay (100/portTICK_PERIOD_MS);
TickType_t last_wakeup = xTaskGetTickCount();
while (1)
{
// test for new data
if (!mma845x_new_data (sensor))
continue;
// fetch new data
if (mma845x_get_float_data (sensor, &data))
{
// do something with data
...
}
// passive waiting until 100 ms are over
vTaskDelayUntil(&last_wakeup, 100/portTICK_PERIOD_MS);
}
}
...
// create a user task that fetches data from sensor periodically
xTaskCreate(user_task_periodic, "user_task_periodic", TASK_STACK_DEPTH, NULL, 2, NULL);
The user task simply tests periodically with a given rate whether new data are available. If new data are available, it fetches the data and does something with them.
Another approach is to use one of the interrupts signals INT1
or INT2
. In this case, the user has to implement an interrupt handler. Since interrupt handlers must be kept small in execution time, the interrupt handler should not fetch the data directly, but only send an event to a waiting task that processes the interrupts from different sources.
static QueueHandle_t gpio_evt_queue = NULL;
// Interrupt handler which sends an interrupt event to a user task
void IRAM int_signal_handler (uint8_t gpio)
{
// send an event with GPIO to the interrupt user task
xQueueSendFromISR(gpio_evt_queue, &gpio, NULL);
}
// User task that reacts on interrupt events
void user_task_interrupt (void *pvParameters)
{
uint8_t gpio_num;
while (1)
{
if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY))
{
mma845x_float_data_t data;
// test for new data
if (!mma845x_new_data (sensor))
continue;
// fetch new data
if (mma845x_get_float_data (sensor, &data))
{
// do something with data
...
}
}
}
}
...
// create a task that is triggered in case of interrupts
xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL);
...
Finally, interrupt handlers have to be activated for the GPIOs which are connected to the interrupt signals.
// configure interrupt pins for *INT1* and *INT2* signals and set the interrupt handler
gpio_set_interrupt(INT1_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler);
gpio_set_interrupt(INT2_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler);
Furthermore, the interrupts have to be enabled and configured in the MMA845X sensor, see section Interrupts above.
In the following example, all the different functions of the sensor can be tested by defining one or more of the following constants. The default example mode is PERIODIC
.
A good demonstration of the sensor's features is the combination of the constants PERIODIC
, FIFO_MODE
, INT_TRANS
, and AUTOSLEEP
. Using these constants, the sensor generates output data at a rate of 50 Hz that are stored in the FIFO. The FIFO is periodically read by the user task every 100 ms. After 3.2 seconds without interrupts, the sensor goes into sleep mode and reduces the output data rate to 1.56 Hz. If a transient event is detected, e.g., by shaking the sensor, an interrupt is generated and the sensor returns to the output data rate of 50 Hz.
/* -- use following constants to define the example mode ----------- */
// #define PERIODIC // fetch data periodically (not possible in combination with INT_DATA)
// #define FIFO_MODE // multiple sample read mode
// #define INT_DATA // data interrupts used (data ready and FIFO status)
// #define INT_EVENT // inertial event interrupts used (free fall, motion)
// #define INT_TRANS // transient event interrupts used
// #define INT_PULSE // single/double tap detection interrupts used
// #define INT_ORIENT // orientation change interrupts used
// #define AUTOSLEEP // autosleep used
#if !defined(PERIODIC)
#define PERIODIC
#endif
#if defined(INT_DATA)
#define INT_USED
#undef PERIODIC // to avoid the combination of INT_DATA and PERIODIC
#endif
#if defined(INT_EVENT) || defined(INT_TRANS) || defined(INT_PULSE) || defined(INT_ORIENT)
#define INT_USED
#endif
#if defined(AUTOSLEEP)
#define INT_USED
#endif
/* -- includes ----------------------------------------------------- */
#include "mma845x.h"
/** -- platform dependent definitions ------------------------------ */
#ifdef ESP_PLATFORM // ESP32 (ESP-IDF)
// user task stack depth for ESP32
#define TASK_STACK_DEPTH 2048
#else // ESP8266 (esp-open-rtos)
// user task stack depth for ESP8266
#define TASK_STACK_DEPTH 512
#endif // ESP_PLATFORM
// I2C interface defintions for ESP32 and ESP8266
#define I2C_BUS 0
#define I2C_SCL_PIN 14
#define I2C_SDA_PIN 13
#define I2C_FREQ I2C_FREQ_400K
// interrupt GPIOs defintions for ESP8266 and ESP32
#define INT1_PIN 5
#define INT2_PIN 4
/* -- user tasks --------------------------------------------------- */
static mma845x_sensor_t* sensor;
/**
* Common function used to get sensor data.
*/
void read_data ()
{
#ifdef FIFO_MODE
mma845x_float_data_fifo_t fifo;
if (mma845x_new_data (sensor))
{
uint8_t num = mma845x_get_float_data_fifo (sensor, fifo);
printf("%.3f MMA845X num=%d\n", (double)sdk_system_get_time()*1e-3, num);
for (int i=0; i < num; i++)
// max. full scale is +-16 g and best resolution is 1 mg, i.e. 5 digits
printf("%.3f MMA845X (xyz)[g] ax=%+7.3f ay=%+7.3f az=%+7.3f\n",
(double)sdk_system_get_time()*1e-3,
fifo[i].ax, fifo[i].ay, fifo[i].az);
}
#else
mma845x_float_data_t data;
if (mma845x_new_data (sensor) &&
mma845x_get_float_data (sensor, &data))
// max. full scale is +-16 g and best resolution is 1 mg, i.e. 5 digits
printf("%.3f MMA845X (xyz)[g] ax=%+7.3f ay=%+7.3f az=%+7.3f\n",
(double)sdk_system_get_time()*1e-3,
data.ax, data.ay, data.az);
#endif // FIFO_MODE
}
#ifdef INT_USED
/**
* When interrupts are used, handlers must be defined that must be kept small.
* Therefore, the interrupt handler only sends an event to a waiting task that
* handles the interrupts from different sources.
*/
static QueueHandle_t gpio_evt_queue = NULL;
// Interrupt handler which sends an interrupt event to a user task
void IRAM int_signal_handler (uint8_t gpio)
{
// send an event with GPIO to the interrupt user task
xQueueSendFromISR(gpio_evt_queue, &gpio, NULL);
}
// User task that reacts on interrupt events
void user_task_interrupt (void *pvParameters)
{
uint8_t gpio_num;
while (1)
{
if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY))
{
if (gpio_num == INT2_PIN)
{
// INT2: data ready and FIFO interrupts are routed to INT2
// get the source of the interrupt
mma845x_int_data_source_t data_src;
mma845x_get_int_data_source (sensor, &data_src);
// in case of data ready interrupt read one data sample and do something with data
if (data_src.data_ready)
read_data ();
// in case of FIFO interrupts read the whole FIFO and do something with data
else if (data_src.fifo_watermark || data_src.fifo_overrun)
read_data ();
}
else if (gpio_num == INT1_PIN)
{
// INT1: all other interrupts are routed to INT1 in the exampled
// get the source of the interrupt
mma845x_int_status_t int_status;
mma845x_get_int_status (sensor, &int_status);
// in case of inertial event interrupt
if (int_status.event)
{
// get the source of the interrupt and reset *INTx* signals
mma845x_int_event_source_t event_src = {};
mma845x_get_int_event_source (sensor, &event_src);
if (event_src.active)
{
printf("%.3f MMA845X motion event: ", (double)sdk_system_get_time()*1e-3);
if (event_src.x_motion) printf("%cx ", event_src.x_sign ? '-' : '+');
if (event_src.y_motion) printf("%cy ", event_src.y_sign ? '-' : '+');
if (event_src.z_motion) printf("%cz ", event_src.z_sign ? '-' : '+');
printf("\n");
}
}
// in case of transient event interrupt
if (int_status.transient)
{
// get the source of the interrupt and reset *INTx* signals
mma845x_int_transient_source_t trans_src = {};
mma845x_get_int_transient_source (sensor, &trans_src);
if (trans_src.active)
{
printf("%.3f MMA845X transient event: ", (double)sdk_system_get_time()*1e-3);
if (trans_src.x_event) printf("%cx ", trans_src.x_sign ? '-' : '+');
if (trans_src.y_event) printf("%cy ", trans_src.y_sign ? '-' : '+');
if (trans_src.z_event) printf("%cz ", trans_src.z_sign ? '-' : '+');
printf("\n");
}
}
// in case of tap detection interrupt
if (int_status.pulse)
{
// get the source of the interrupt and reset *INTx* signals
mma845x_int_pulse_source_t pulse_src = {};
mma845x_get_int_pulse_source (sensor, &pulse_src);
if (pulse_src.active)
printf("%.3f MMA845X %s\n", (double)sdk_system_get_time()*1e-3,
pulse_src.d_pulse ? "double tap" : "single tap");
}
// in case of orientation change detection interrupt
if (int_status.orientation)
{
// get the source of the interrupt and reset *INTx* signals
mma845x_orientation_status_t orient;
mma845x_get_orientation (sensor, &orient);
if (orient.changed)
{
printf("%.3f MMA845X orientation ", (double)sdk_system_get_time()*1e-3);
switch (orient.portrait_landscape)
{
case mma845x_portrait_up: printf("portait up" ); break;
case mma845x_portrait_down: printf("portait down" ); break;
case mma845x_landscape_left: printf("landscape left" ); break;
case mma845x_landscape_right: printf("landscape right" ); break;
}
printf(", %s\n", orient.back_front == mma845x_back ? "back" : "front");
}
}
// in case of auto-sleep/wake interrupt
if (int_status.autosleep)
{
printf("%.3f MMA845X new system mode: ", (double)sdk_system_get_time()*1e-3);
switch (mma845x_get_system_mode(sensor))
{
case mma845x_standby_mode: printf("standby"); break;
case mma845x_wake_mode: printf("wake"); break;
case mma845x_sleep_mode: printf("sleep"); break;
default: printf("unknown");
}
printf("\n");
}
}
}
}
}
#endif
#ifdef PERIODIC
/*
* User task that fetches the sensor values periodically every 100 ms.
*/
void user_task_periodic(void *pvParameters)
{
// small delay before accessing the sensor the first time
vTaskDelay (100/portTICK_PERIOD_MS);
TickType_t last_wakeup = xTaskGetTickCount();
while (1)
{
// read sensor data
read_data ();
// passive waiting until 100 ms are over
vTaskDelayUntil(&last_wakeup, 100/portTICK_PERIOD_MS);
}
}
#endif // defined(PERIODIC) && !defined(INT_DATA)
/* -- main program ------------------------------------------------- */
void user_init(void)
{
// Set UART Parameter.
uart_set_baud(0, 115200);
// Give the UART some time to settle
vTaskDelay(1);
/** -- MANDATORY PART -- */
#ifdef SPI_USED
// init the sensor connnected to SPI
spi_bus_init (SPI_BUS, SPI_SCK_GPIO, SPI_MISO_GPIO, SPI_MOSI_GPIO);
// init the sensor connected to SPI_BUS with SPI_CS_GPIO as chip select.
sensor = mma845x_init_sensor (SPI_BUS, 0, SPI_CS_GPIO);
#else
// init all I2C bus interfaces at which MMA845X sensors are connected
i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ);
// init the sensor with slave address MMA845X_I2C_ADDRESS_2 connected to I2C_BUS.
sensor = mma845x_init_sensor (I2C_BUS, MMA845X_I2C_ADDRESS_2);
#endif
if (sensor)
{
#ifdef INT_USED
/** --- INTERRUPT CONFIGURATION PART ---- */
// Interrupt configuration has to be done before the sensor is set
// into measurement mode to avoid losing interrupts
// create an event queue to send interrupt events from interrupt
// handler to the interrupt task
gpio_evt_queue = xQueueCreate(10, sizeof(uint8_t));
// configure interupt pins for *INT1* and *INT2* signals and set the interrupt handler
gpio_enable(INT1_PIN, GPIO_INPUT);
gpio_enable(INT2_PIN, GPIO_INPUT);
gpio_set_interrupt(INT1_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler);
gpio_set_interrupt(INT2_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler);
#endif // INT_USED
/** -- SENSOR CONFIGURATION PART --- */
// set polarity and type of INT signals if necessary
mma845x_config_int_signals (sensor, mma845x_high_active, mma845x_push_pull);
#ifdef INT_DATA
// enable data interrupts on INT2 (data ready or FIFO status interrupts)
// data ready and FIFO status interrupts must not be enabled at the same time
#ifdef FIFO_MODE
mma845x_enable_int (sensor, mma845x_int_fifo_overrun , mma845x_int2_signal, true);
mma845x_enable_int (sensor, mma845x_int_fifo_watermark, mma845x_int2_signal, true);
#else
mma845x_enable_int (sensor, mma845x_int_data_ready, mma845x_int2_signal, true);
#endif // FIFO_MODE
#endif // INT_DATA
#ifdef INT_EVENT
// enable inertial event interrupts on INT1
// configuration of registers FF_MT_CFG, FF_MT_THS, FF_MT_COUNT
// see datasheet and application note AN4070
mma845x_int_event_config_t event_config;
// event_config.mode = mma845x_free_fall;
event_config.mode = mma845x_motion; // OR-condition
event_config.threshold = 2; // 0.063 g/count = 0.1266 g
event_config.x_enabled = true; // x-axis is used for event detection
event_config.y_enabled = true; // y-axis is used for event detection
event_config.z_enabled = false; // z-axis is not used for event detection
event_config.debounce_cnt = 5; // 100 ms at ODR=50Hz in normal oversampling mode
event_config.debounce_cnt_clr = false; // increment and decrement debounce counter
event_config.latch = true; // interrupt is latched until its source is read
event_config.sleep_active = true; // active in sleep mode
mma845x_set_int_event_config (sensor, &event_config);
mma845x_enable_int (sensor, mma845x_int_event, mma845x_int1_signal, true);
#endif // INT_EVENT
#ifdef INT_TRANS
// enable transient event interrupts on INT1
// configuration of registers TRANSIENT_CFG, TRANSIENT_THS, TRANSIENT_COUNT
// see datasheet and application note AN4071
mma845x_int_transient_config_t trans_config;
trans_config.threshold = 8; // 0.063 g/count = 0.504 g
trans_config.x_enabled = true; // x-axis is used for event detection
trans_config.y_enabled = true; // y-axis is used for event detection
trans_config.z_enabled = false; // z-axis is not used for event detection
trans_config.debounce_cnt = 2; // 40 ms at ODR=50Hz in normal oversampling mode
trans_config.debounce_cnt_clr = true; // increment and clear debounce counter
trans_config.latch = true; // interrupt is latched until its source is read
trans_config.hpf_bypassed = false; // HPF is not bypassed (default)
trans_config.sleep_active = true; // active in sleep mode
mma845x_set_int_transient_config (sensor, &trans_config);
mma845x_enable_int (sensor, mma845x_int_transient, mma845x_int1_signal, true);
#endif // INT_TRANS
#ifdef INT_PULSE
// enable single/double tap detection interrupts on INT1
// configuration of registers PULSE_CFG, PULSE_THS*, PULSE_TMLT, PULSE_LTCY, PULSE_WIND
// see datasheet and application note AN4072
mma845x_int_pulse_config_t pulse_config;
pulse_config.x_threshold = 25; // 0.063 g/count = 1.575 g
pulse_config.y_threshold = 25; // 0.063 g/count = 1.575 g
pulse_config.z_threshold = 42; // 0.063 g/count = 1.65 g
pulse_config.x_single = true; // single tap detection in x direction enabled
pulse_config.x_double = false; // double tap detection in x direction enabled
pulse_config.y_single = true; // single tap detection in y direction enabled
pulse_config.y_double = false; // double tap detection in y direction enabled
pulse_config.z_single = true; // single tap detection in z direction enabled
pulse_config.z_double = false; // double tap detection in y direction enabled
pulse_config.pulse_limit = 10; // 50 ms at ODR=50 Hz in normal mode and no LPF
pulse_config.pulse_latency = 20; // 200 ms at ODR=50 Hz in normal mode and no LPF
pulse_config.pulse_window = 30; // 300 ms at ODR=50 Hz in normal mode and no LPF
pulse_config.latch = true; // interrupt is latched until its source is read
pulse_config.hpf_bypassed = false; // HPF is not bypassed (default)
pulse_config.lpf_enabled = false; // LPF is not used (default)
pulse_config.sleep_active = true; // active in sleep mode
mma845x_set_int_pulse_config (sensor, &pulse_config);
mma845x_enable_int (sensor, mma845x_int_pulse, mma845x_int1_signal, true);
#endif // INT_PULSE
#ifdef INT_ORIENT
// enable orientation change detection interrupts on INT1
// configuration of registers PL_CFG, PL_COUNT, PL_BF_ZCOMP, PL_THS
// see datashee and applicatio note AN4068
mma845x_orientation_config_t orient_config;
orient_config.enabled = true; // enable portrait/landscape detection
orient_config.sleep_active = true; // active in sleep mode
orient_config.pl_threshold = 16; // trip angle 45°
orient_config.pl_hysteresis= 4; // hysteresis +-14°
orient_config.bf_threshold = 1; // 285° < z < 75° back->front,
// 105° < z < 255° front->back
orient_config.z_lock = 4; // z >= 29°
orient_config.debounce_cnt = 5; // 100 ms at ODR=50Hz in normal mode
orient_config.debounce_cnt_clr = false; // increment and decrement debounce counter
mma845x_set_orientation_config (sensor, &orient_config);
mma845x_enable_int (sensor, mma845x_int_orientation, mma845x_int1_signal, true);
#endif // INT_ORIENT
#ifdef AUTOSLEEP
// switch to autosleep after 3.2 seconds of inactivity
// configuration of registers ASLP_COUNT, CTRL_REG1, CTRL_REG2
// see datashee and applicatio note AN4074
mma845x_set_autosleep (sensor, mma845x_normal, mma845x_aslp_rate_1_56, 10, true);
mma845x_enable_int (sensor, mma845x_int_autosleep, mma845x_int1_signal, true);
#endif // AUTOSLEEP
#ifdef FIFO_MODE
// activate FIFO mode if needed
// configuration of registers F_SETUP, TRIG_CFG, CTRL_REG3
// see datashee and applicatio note AN4073
mma845x_fifo_trigger_t trigger = {};
mma845x_set_fifo_mode (sensor, mma845x_fill, 20, trigger, false);
#endif
// config HPF and enable it for sensor output data
mma845x_config_hpf (sensor, 0, true);
// LAST STEP: Finally set scale and mode to start measurements
mma845x_set_scale(sensor, mma845x_scale_2_g);
mma845x_set_mode (sensor, mma845x_high_res, mma845x_odr_50, true, false);
/** -- TASK CREATION PART --- */
// must be done last to avoid concurrency situations with the sensor
// configuration part
#ifdef INT_USED
// create a task that is triggered in case of interrupts
xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL);
#endif
#ifdef PERIODIC
// create a user task that fetches data from sensor periodically
xTaskCreate(user_task_periodic, "user_task_periodic", TASK_STACK_DEPTH, NULL, 2, NULL);
#endif
}
else
printf("Could not initialize MMA845X sensor\n");
}