From a943619dfc59785f024da8c30884dd6c474c171a Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Thu, 21 Dec 2017 19:04:59 +0100 Subject: [PATCH 01/10] LIS3DH driver added --- examples/lis3dh/Makefile | 3 + examples/lis3dh/lis3dh_example.c | 338 ++++++++ extras/lis3dh/README.md | 999 ++++++++++++++++++++++++ extras/lis3dh/component.mk | 10 + extras/lis3dh/lis3dh.c | 1239 ++++++++++++++++++++++++++++++ extras/lis3dh/lis3dh.h | 462 +++++++++++ extras/lis3dh/lis3dh_platform.h | 112 +++ extras/lis3dh/lis3dh_types.h | 336 ++++++++ 8 files changed, 3499 insertions(+) create mode 100644 examples/lis3dh/Makefile create mode 100644 examples/lis3dh/lis3dh_example.c create mode 100644 extras/lis3dh/README.md create mode 100644 extras/lis3dh/component.mk create mode 100644 extras/lis3dh/lis3dh.c create mode 100644 extras/lis3dh/lis3dh.h create mode 100644 extras/lis3dh/lis3dh_platform.h create mode 100644 extras/lis3dh/lis3dh_types.h diff --git a/examples/lis3dh/Makefile b/examples/lis3dh/Makefile new file mode 100644 index 0000000..99a893a --- /dev/null +++ b/examples/lis3dh/Makefile @@ -0,0 +1,3 @@ +PROGRAM=LIS3DH +EXTRA_COMPONENTS = extras/i2c extras/lis3dh +include ../../common.mk diff --git a/examples/lis3dh/lis3dh_example.c b/examples/lis3dh/lis3dh_example.c new file mode 100644 index 0000000..b18eaab --- /dev/null +++ b/examples/lis3dh/lis3dh_example.c @@ -0,0 +1,338 @@ +/** + * Simple example with one sensor connected to I2C or SPI. It demonstrates the + * different approaches to fetch the data. Either one of the interrupt signals + * is used or new data are fetched periodically. + * + * Harware configuration: + * + * I2C + * + * +-----------------+ +----------+ + * | ESP8266 / ESP32 | | LIS3DH | + * | | | | + * | GPIO 14 (SCL) ----> SCL | + * | GPIO 13 (SDA) <---> SDA | + * | GPIO 5 <---- INT1 | + * | GPIO 4 <---- DRDY/INT2| + * +-----------------+ +----------+ + * + * SPI + * + * +-----------------+ +----------+ +-----------------+ +----------+ + * | ESP8266 | | LIS3DH | | ESP32 | | LIS3DH | + * | | | | | | | | + * | GPIO 14 (SCK) ----> SCK | | GPIO 16 (SCK) ----> SCK | + * | GPIO 13 (MOSI)----> SDI | | GPIO 17 (MOSI)----> SDI | + * | GPIO 12 (MISO)<---- SDO | | GPIO 18 (MISO)<---- SDO | + * | GPIO 2 (CS) ----> CS | | GPIO 19 (CS) ----> CS | + * | GPIO 5 <---- INT1 | | GPIO 5 <---- INT1 | + * | GPIO 4 <---- DRDY/INT2| | GPIO 4 <---- DRDY/INT2| + * +-----------------+ +---------+ +-----------------+ +----------+ + */ + +/* -- use following constants to define the example mode ----------- */ + +// #define SPI_USED // if defined SPI is used, otherwise I2C + #define DATA_INT // data ready and FIFO status interrupts +// #define CLICK_INT // click detection interrupt +// #define ACTIVITY_INT // wake-up, free fall or 6D/4D orientation detection +// #define FIFO_MODE // multiple sample read mode + +#if defined(DATA_INT) || defined(ACTIVITY_INT) || defined(CLICK_INT) +#define INT_USED +#endif + +/* -- includes ----------------------------------------------------- */ + +#include "lis3dh.h" + +/** -- platform dependent definitions ------------------------------ */ + +#ifdef ESP_PLATFORM // ESP32 (ESP-IDF) + +// user task stack depth for ESP32 +#define TASK_STACK_DEPTH 2048 + +// SPI interface definitions for ESP32 +#define SPI_BUS HSPI_HOST +#define SPI_SCK_GPIO 16 +#define SPI_MOSI_GPIO 17 +#define SPI_MISO_GPIO 18 +#define SPI_CS_GPIO 19 + +#else // ESP8266 (esp-open-rtos) + +// user task stack depth for ESP8266 +#define TASK_STACK_DEPTH 256 + +// SPI interface definitions for ESP8266 +#define SPI_BUS 1 +#define SPI_SCK_GPIO 14 +#define SPI_MOSI_GPIO 13 +#define SPI_MISO_GPIO 12 +#define SPI_CS_GPIO 2 // GPIO 15, the default CS of SPI bus 1, can't be used + +#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_100K + +// interrupt GPIOs defintions for ESP8266 and ESP32 +#define INT1_PIN 5 +#define INT2_PIN 4 + +/* -- user tasks --------------------------------------------------- */ + +static lis3dh_sensor_t* sensor; + +/** + * Common function used to get sensor data. + */ +void read_data () +{ + #ifdef FIFO_MODE + + lis3dh_float_data_fifo_t fifo; + + if (lis3dh_new_data (sensor)) + { + uint8_t num = lis3dh_get_float_data_fifo (sensor, fifo); + + printf("%.3f LIS3DH 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 LIS3DH (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 + + lis3dh_float_data_t data; + + if (lis3dh_new_data (sensor) && + lis3dh_get_float_data (sensor, &data)) + // max. full scale is +-16 g and best resolution is 1 mg, i.e. 5 digits + printf("%.3f LIS3DH (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 +/** + * In this case, any of the possible interrupts on interrupt signal *INT1* is + * used to fetch the data. + * + * When interrupts are used, the user has to define interrupt handlers that + * either fetches the data directly or triggers a task which is waiting to + * fetch the data. In this example, the interrupt handler sends an event to + * a waiting task to trigger the data gathering. + */ + +static QueueHandle_t gpio_evt_queue = NULL; + +// User task that fetches the sensor values. + +void user_task_interrupt (void *pvParameters) +{ + uint32_t gpio_num; + + while (1) + { + if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY)) + { + lis3dh_int_activity_source_t activity_src; + lis3dh_int_data_source_t data_src; + lis3dh_int_click_source_t click_src; + + // get the source of the interrupt and reset INT signals + lis3dh_get_int_activity_source (sensor, &activity_src, lis3dh_int1_signal); + lis3dh_get_int_data_source (sensor, &data_src); + lis3dh_get_int_click_source (sensor, &click_src); + + // in case of DRDY interrupt or activity interrupt read one data sample + if (data_src.data_ready || activity_src.active) + read_data (); + + // in case of FIFO interrupts read the whole FIFO + else if (data_src.fifo_watermark || data_src.fifo_overrun) + read_data (); + + else if (click_src.active) + printf("%.3f LIS3DH %s\n", (double)sdk_system_get_time()*1e-3, + click_src.s_click ? "single click" : "double click"); + } + } +} + +// Interrupt handler which resumes user_task_interrupt on interrupt + +void IRAM int_signal_handler (uint8_t gpio) +{ + // send an event with GPIO to the interrupt user task + xQueueSendFromISR(gpio_evt_queue, &gpio, NULL); +} + +#else // !INT_USED + +/* + * In this example, user task fetches the sensor values every seconds. + */ + +void user_task_periodic(void *pvParameters) +{ + vTaskDelay (100/portTICK_PERIOD_MS); + + while (1) + { + // read sensor data + read_data (); + + // passive waiting until 1 second is over + vTaskDelay(100/portTICK_PERIOD_MS); + } +} + +#endif // INT_USED + +/* -- 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 = lis3dh_init_sensor (SPI_BUS, 0, SPI_CS_GPIO); + + #else + + // init all I2C bus interfaces at which LIS3DH sensors are connected + i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ); + + // init the sensor with slave address LIS3DH_I2C_ADDRESS_1 connected to I2C_BUS. + sensor = lis3dh_init_sensor (I2C_BUS, LIS3DH_I2C_ADDRESS_1, 0); + + #endif + + if (sensor) + { + // --- SYSTEM CONFIGURATION PART ---- + + #if !defined (INT_USED) + + // create a user task that fetches data from sensor periodically + xTaskCreate(user_task_periodic, "user_task_periodic", TASK_STACK_DEPTH, NULL, 2, NULL); + + #else // INT_USED + + // create a task that is triggered only in case of interrupts to fetch the data + xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL); + + // create event queue + 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_set_interrupt(INT1_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler); + + #endif // !defined(INT_USED) + + // -- SENSOR CONFIGURATION PART --- + + // Interrupt configuration has to be done before the sensor is set + // into measurement mode + + // set polarity of INT signals if necessary + // lis3dh_config_int_signals (sensor, lis3dh_high_active); + + #ifdef DATA_INT + // enable data interrupts on INT1 (data ready or FIFO status interrupts) + // data ready and FIFO status interrupts must not be enabled at the same time + #ifdef FIFO_MODE + lis3dh_enable_int_data (sensor, lis3dh_fifo_overrun, true); + lis3dh_enable_int_data (sensor, lis3dh_fifo_watermark, true); + #else + lis3dh_enable_int_data (sensor, lis3dh_data_ready, true); + #endif // FIFO_MODE + #endif // DATA_INT + + #ifdef ACTIVITY_INT + // enable data interrupts on INT1 + lis3dh_int_activity_config_t act_config; + + act_config.activity = lis3dh_wake_up; + // act_config.activity = lis3dh_free_fall; + // act_config.activity = lis3dh_6d_movement; + // act_config.activity = lis3dh_6d_position; + // act_config.activity = lis3dh_4d_movement; + // act_config.activity = lis3dh_4d_position; + act_config.threshold = 10; + act_config.x_low_enabled = false; + act_config.x_high_enabled = true; + act_config.y_low_enabled = false; + act_config.y_high_enabled = true; + act_config.z_low_enabled = false; + act_config.z_high_enabled = true; + act_config.duration = 0; + act_config.latch = true; + + lis3dh_set_int_activity_config (sensor, lis3dh_int1_signal, &act_config); + #endif // ACTIVITY_INT + + #ifdef CLICK_INT + // enable click interrupt on INT1 + lis3dh_int_click_config_t click_config; + + click_config.threshold = 10; + click_config.x_single = false; + click_config.x_double = false; + click_config.y_single = false; + click_config.y_double = false; + click_config.z_single = true; + click_config.z_double = false; + click_config.latch = true; + click_config.time_limit = 1; + click_config.time_latency = 1; + click_config.time_window = 3; + + lis3dh_set_int_click_config (sensor, lis3dh_int1_signal, &click_config); + #endif // CLICK_INT + + #ifdef FIFO_MODE + // clear FIFO and activate FIFO mode if needed + lis3dh_set_fifo_mode (sensor, lis3dh_bypass, 0, lis3dh_int1_signal); + lis3dh_set_fifo_mode (sensor, lis3dh_stream, 10, lis3dh_int1_signal); + #endif + + // configure HPF and reset the reference by dummy read + lis3dh_config_hpf (sensor, lis3dh_hpf_normal, 0, true, true, true, true); + lis3dh_get_hpf_ref (sensor); + + // enable ADC inputs and temperature sensor for ADC input 3 + lis3dh_enable_adc (sensor, true, true); + + // LAST STEP: Finally set scale and mode to start measurements + lis3dh_set_scale(sensor, lis3dh_scale_2g); + lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); + + // -- SENSOR CONFIGURATION PART --- + } +} + diff --git a/extras/lis3dh/README.md b/extras/lis3dh/README.md new file mode 100644 index 0000000..d689b42 --- /dev/null +++ b/extras/lis3dh/README.md @@ -0,0 +1,999 @@ +# Driver for the LIS3DH 3-axes digital output accelerometer + +The driver is for the usage with the ESP8266 and [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos). + +It is also working with ESP32 and [ESP-IDF](https://github.com/espressif/esp-idf.git) 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 also be used with LIS3DE, LIS2DH, LIS2DH12, LIS2DE, and LIS2DE12 + +## About the sensor + +LIS3DH is a low-power high performance **3-axis accelerometer sensor** connected to **I2C** or **SPI** with a full scale of up to **±16 g**. It supports different measuring rates. + +**Main features** of the sensor are: + +- 4 selectable full scales of ±2 g, ±4 g, ±8 g, and ±16 g +- 9 measuring rates from 1 Hz to 5 kHz +- 16 bit accelerometer value data output +- 2 independent programmable interrupt generators for free-fall and motion detection +- integrated high-pass filters with 3 modes and 4 different cut off frequencies +- embedded temperature sensor +- embedded 32 levels of 16 bit data output FIFO +- 6D/4D orientation detection +- Free-fall detection +- Motion detection +- click/double click recognition +- I2C and SPI digital output interface + +## Sensor operation + +### Sensor modes + +LIS3DH provides different operating modes. + +- **Power Down mode** is configured automatically after power up boot sequence. In this mode, almost all internal blocks of the device are switched off. Register content is preserved, but there are no measurements performed. + +- **Normal mode** is the standard measurement mode. In this mode measurements are performed with a resolution of **10 bit** at the defined output data rate (**ODR**). + +- **Low-power mode** is the measurement mode with reduced power consumption. Measurements are performed with a resolution of only **8 bit** at the defined output data rate (**ODR**). + +- **High-resolution mode** is the measurement mode where measurements are performed with a resolution of 12 bit at the defined output data rate (**ODR**). Only output data rates (ODR) up to 400 Hz are available. + +Switching from any mode to any another mode with the exception of high-resolution mode takes only 1/ODR. Switching from any mode to the high-resolution mode takes 7/ODRs. + +### Output Data Rates + +In normal, low-power and high-resolution modes, measurements are performed at a defined output rate. Following output data rates (ODR) are supported in the different modes: + +Driver symbol | Normal mode
```lis3dh_normal``` | Low-power mode
```lis3dh_low_power``` | High-resolution mode
```lis3dh_high_res``` +:---------------------- |:------------:|:---------------:|:--------------------: +```lis3dh_power_down``` | Power down | Power down | Power down +```lis3dh_normal_1``` | 1 Hz | 1 Hz | 1 Hz +```lis3dh_normal_10``` | 10 Hz | 10 Hz | 10 Hz +```lis3dh_normal_25``` | 25 Hz | 25 Hz | 25 Hz +```lis3dh_normal_50``` | 50 Hz | 50 Hz | 50 Hz +```lis3dh_normal_100``` | 100 Hz | 100 Hz | 100 Hz +```lis3dh_normal_200``` | 200 Hz | 200 Hz | 200 Hz +```lis3dh_normal_400``` | 400 Hz | 400 Hz | 400 Hz +```lis3dh_normal_1600```| - | 1600 Hz | - +```lis3dh_normal_5000```| 1250 Hz | 5000 Hz | - + +The **easiest way to use the sensor** is simply to initialize it with function ```lis3dh_init_sensor``` and then set it to any measurement mode with function ```lis3dh_set_mode``` to start measurements with the given output data rate (ODR). + +``` +... +static lis3dh_sensor_t* sensor = 0; +... +if ((sensor = lis3dh_init_sensor (I2C_BUS, LIS3DH_I2C_ADDRESS_2, 0))) +{ + ... + lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true) + ... +} +... + +``` +In this example, a LIS3DH sensor connected to I2C is initialized and set to high-resolution mode to start measurements for all three axes with an output data rate (ODR) of 10 Hz. + +**Please note:** +- Function ```lis3dh_init_sensor``` resets the sensor completely, switches it to the power down 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 function ```lis3dh_set_mode```. In particular, the interrupt configuration should be performed before to avoid loosing the first interrupt and locking the system. + +## Measurement results + +### Output data format + +The sensor determines periodically the accelerations for2 all axes that are enabled for measurement 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 and are always left-aligned. The resolution depends on the selected operation mode and the selected full scale. For example, in low power mode with 8-bit resolution only the high byte is used. + + + +range and the resolution of these data depend the selected measurement mode and on the sensitivity of the sensor which is selected by the **full scale** value. The LIS3DH allows to select the following full scales: + +Full Scale | Driver symbol | Resolution 12 bit
```lis3dh_high_res``` | Resolution 10 bit
```lis3dh_normal``` | Resolution 8 bit
```lis3dh_low_power``` +:--------------------- | -----------:|-----------:|:--------------- +```lis3dh_scale_2g``` | ±2 g | 1 mg | 4 mg | 16 mg +```lis3dh_scale_4g``` | ±4 g | 2 mg | 8 mg | 32 mg +```lis3dh_scale_8g``` | ±8 g | 4 mg | 16 mg | 64 mg +```lis3dh_scale_16g``` |±16 g | 12 mg | 48 mg | 192 mg + +By default, a full scale of ±2 g is used. Function ```lis3dh_set_scale``` can be used to change it. + +``` +lis3dh_set_scale(sensor, lis3dh_scale_4g); +``` + +### Fetching output data + +To get the information whether new data are available, the user task can either use + +- the function ```lis3dh_new_data``` to check periodically whether new output data are available, or +- the data ready interrupt (DRDY) which is thrown as soon as new output data are available (see below). + +Last measurement results can then be fetched either + +- as raw data using function ```lis3dh_get_raw_data``` or +- as floating point values in g using function ```lis3dh_get_float_data```. + +It is recommended to use function ```lis3dh_get_float_data``` since it already converts measurement results to real values according to the selected full scale. + +``` +void user_task_periodic(void *pvParameters) +{ + lis3dh_float_data_t data; + + while (1) + { + // execute task every 10 ms + vTaskDelay (10/portTICK_PERIOD_MS); + ... + // test for new data + if (!lis3dh_new_data (sensor)) + continue; + + // fetch new data + if (lis3dh_get_float_data (sensor, &data)) + { + // do something with data + ... + } + } +} +``` + +**Please note:** +The functions ```lis3dh_get_float_data``` and ```lis3dh_get_raw_data``` always return the last available results. If these functions are called more often than measurements are performed, some measurement results are retrieved multiple times. If these functions are called too rarely, some measurement results will be lost. + +### High pass filtering + +LIS3DH provides embedded high-pass filtering capability to improve measurement results. Please refer the [datasheet](http://www.st.com/resource/en/datasheet/lis3dh.pdf) or [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more details. + +The high pass filter can independently apply to + +- the raw output data, +- the data used for click detection, and +- the data used for interrupt generation like wake-up, free fall or 6D/4D orientation detection. + +The mode and the cutoff frequency of the high pass filter can be configured using function ```lis3dh_config_hpf```. Following HPF modes are available: + +Driver symbol | HPF mode +:--------------|:--------- +lis3dh_hpf_normal | Normal mode +lis3dh_hpf_reference | Reference mode +lis3dh_hpf_autoreset | Auto-reset on interrupt + +For each output data rate (ODR), 4 different HPF cutoff frequencies can be used. Furthermore, a number of boolean parameters indicate to which data the HPF is applied. + +``` +... +// configure HPF +lis3dh_config_hpf (sensor, lis3dh_hpf_normal, 0, true, true, true, true); + +// reset the reference by dummy read +lis3dh_get_hpf_ref (sensor); +... +``` + +### FIFO + +In order to limit the rate at which the host processor has to fetch the data, the LIS3DH 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](http://www.st.com/resource/en/datasheet/lis3dh.pdf) or [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more details. + +Driver symbol | FIFO mode +--------------|------------------------- +lis3dh_bypass | Bypass mode (FIFO is not used) +lis3dh_fifo | FIFO mode +lis3dh_stream | Stream mode +lis3dh_stream_to_fifo | Stream-to-FIFO mode + +The FIFO mode can be set using function ```lis3dh_set_fifo_mode```. This function takes three parameters + +- the FIFO mode, +- a threshold value which defines a watermark level, and +- an interrupt source that is used in Stream-to-FIFO mode. + +The watermark level is used by the sensor to set a watermark flag and to generate optionally an interrupt when the FIFO content exceeds this level. They can be used to gather a minimum number of axes acceleration samples with the sensor before the data are fetched as a single read operation from the sensor. + +``` +... +// clear FIFO +lis3dh_set_fifo_mode (sensor, lis3dh_bypass, 0, lis3dh_int1_signal); + +// activate FIFO mode +lis3dh_set_fifo_mode (sensor, lis3dh_stream, 10, lis3dh_int1_signal); +... +``` + +**Please note**: To clear the FIFO at any time, set the FIFO mode to ```lis3dh_bypass``` and back to the desired FIFO mode. + +To read data from the FIFO, simply use either + +- the function ```lis3dh_get_raw_data_fifo``` to all get raw output data stored in FIFO or +- the function ```lis3dh_get_float_data_fifo``` to get all data stored in FIFO and converted to real values in dps (degrees per second). + +Both functions clear the FIFO and return the number of samples read from the FIFO. + +``` +void user_task_periodic (void *pvParameters) +{ + lis3dh_float_data_fifo_t data; + + while (1) + { + // execute task every 500 ms + vTaskDelay (500/portTICK_PERIOD_MS); + ... + // test for new data + if (!lis3dh_new_data (sensor)) + continue; + + // fetch data from fifo + uint8_t num = lis3dh_get_float_data_fifo (sensor, data); + + for (int i = 0; i < num; i++) + { + // do something with data[i] ... + } +} +``` + +### Interrupts + +The LIS3DH supports two dedicated interrupt signals **```INT1```** and **```INT2```** and three different types of interrupts: + +- data ready and FIFO status interrupts, +- activity detection interrupts like wake-up, free fall, and 6D/4D orientation detection, and +- click detection interrupts. + +While activity detection and click detection interrupts can be configured for both interrupt signals, data ready and FIFO status interrupts can be configured only for interrupt signal ```INT1```. + +#### Data ready and FIFO status interrupts + +Following sources can generate an interrupt on signal ```INT1```: + +Interrupt source | Driver symbol +:-----------------|:------------- +Output data become ready to read | lis3dh_data_ready +FIFO content exceeds the watermark level | lis3dh_fifo_watermark +FIFO is completely filled | lis3dh_fifo_overrun + +Each of these interrupt sources can be enabled or disabled separately with function ```lis3dh_enable_int_data```. By default all interrupt sources are disabled. + +``` +lis3dh_enable_int_data (sensor, lis3dh_data_ready, true); +``` + +Whenever an interrupt is generated at interrupt signal ```INT1```, the function ```lis3dh_get_int_data_source``` can be used to determine the source of the interrupt. This function returns a data structure of type ```lis3dh_int_data_source_t``` that contain a boolean member for each source that can be tested for true. + +``` +void int1_handler () +{ + lis3dh_int_data_source_t data_src; + + // get interrupt source of INT1 + lis3dh_get_int_data_source (sensor, &data_src); + + // if data ready interrupt, get the results and do something with them + if (data_src.data_ready) + // ... read data + + // in case of FIFO interrupts read the whole FIFO + else if (data_src.fifo_watermark || data_src.fifo_overrun) + // read FIFO data + ... +} +``` + +#### Activity detection interrupts + +Activity detection allows to generate interrupts whenever a configured condition occur. If activated, the acceleration of each axis is compared with a defined threshold to check whether it is below or above the threshold. The results of all activated comparisons are then combined OR or AND to generate the interrupt signal. + +The configuration of the threshold valid for all axes, the activated comparisons and the selected AND/OR combination allows to recognize special situations: + +- **Wake-up detection** refers the special condition that the acceleration measured along any axis is above the defined threshold (```lis3dh_wake_up```). +- **Free fall detection** refers the special condition that the acceleration measured along all the axes goes to zero (```lis3dh_free_fall```). +- **6D/4D Orientation Detection** refers to the special condition that the measured acceleration along certain axes is above and along the other axes is below the threshold which indicates a particular orientation (```lis3dh_6d_movement```, ```lis3dh_6d_position```, ```lis3dh_4d_movement```, ```lis3dh_4d_position```). + +Activity detection interrupts can be configured with the function ```lis3dh_get_int_activity_config```. This function requires as parameters the configuration of type ```lis3dh_int_activity_config_t``` and the interrupt signal to be used for activity detection interrupts. + +For example, wake-up detection interrupt on signal ```INT1``` could be configured as following: + +``` +lis3dh_int_activity_config_t act_config; + +act_config.activity = lis3dh_wake_up; +act_config.threshold = 10; +act_config.x_low_enabled = false; +act_config.x_high_enabled = true; +act_config.y_low_enabled = false; +act_config.y_high_enabled = true; +act_config.z_low_enabled = false; +act_config.z_high_enabled = true; + +act_config.duration = 0; +act_config.latch = true; + +lis3dh_set_int_activity_config (sensor, lis3dh_int1_signal, &act_config); + ``` + +The parameter of type ```lis3dh_int_activity_config_t``` also configures + +- whether the interrupt signal should latched until the interrupt source is read, and +- which time in 1/ODR an interrupt condition has to be given before the interrupt is generated. + +As with data ready and FIFO status interrupts, function ```lis3dh_get_int_activity_source``` can be used to determine the source of an activity interrupt whenever it is generated. This function returns a data structure of type ```lis3dh_int_activity_source_t``` which contains a boolean member for each source that can be tested for true. + +``` +void int1_handler () +{ + lis3dh_int_data_source_t data_src; + lis3dh_int_activity_source_t activity_src; + + // get interrupt source of INT1 + lis3dh_get_int_data_source (sensor, &data_src); + lis3dh_get_int_activity_source (sensor, &activity_src, lis3dh_int1_signal); + + // if data ready interrupt, get the results and do something with them + if (data_src.data_ready) + // ... read data + + // in case of FIFO interrupts read the whole FIFO + else if (data_src.fifo_watermark || data_src.fifo_overrun) + // read FIFO data + + // in case of activity interrupt + else if (activity_src.active) + // ... read data + ... +} +``` + +**Please note** Activating all threshold comparisons and the OR combination (```lis3dh_wake_up```) is the most flexible way to deal with activity interrupts. Functions such as free fall detection and so on can then be realized by suitably combining the various interrupt sources by the user task. Following example realizes the free fall detection in user task. + +``` +lis3dh_int_activity_config_t act_config; + +act_config.activity = lis3dh_wake_up; +act_config.threshold = 10; +act_config.x_low_enabled = true; +act_config.x_high_enabled = true; +act_config.y_low_enabled = true; +act_config.y_high_enabled = true; +act_config.z_low_enabled = true; +act_config.z_high_enabled = true; + +act_config.duration = 0; +act_config.latch = true; + +lis3dh_set_int_activity_config (sensor, lis3dh_int1_signal, &act_config); +``` + +``` +void int1_handler () +{ + lis3dh_int_activity_source_t activity_src; + + // get interrupt source of INT1 + lis3dh_get_int_activity_source (sensor, &activity_src, lis3dh_int1_signal); + + // detect free fall (all accelerations are below the threshold) + if (activity_src.x_low && activity_src.y_low && activity_src.z_low) + ... + ... +} + +``` + +#### Click detection interrupts + +A sequence of acceleration values over time measured along certain axes can be used to detect single and double clicks. Please refer the [datasheet](http://www.st.com/resource/en/datasheet/lis3dh.pdf) or [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more information. + +Click detection interrupts are configured with function ```lis3dh_set_int_click_config```. This function requires as parameters the configuration of type ```lis3dh_int_click_config_t``` and the interrupt signal to be used for click detection interrupts. + +In following example, the single click detection for z-axis is enabled with a time limit of 1/ODR, a time latency of 1/ODR and a time window of 3/ODR. + +``` +lis3dh_int_click_config_t click_config; + +click_config.threshold = 10; +click_config.x_single = false; +click_config.x_double = false; +click_config.y_single = false; +click_config.y_double = false; +click_config.z_single = true; +click_config.z_double = false; +click_config.latch = true; +click_config.time_limit = 1; +click_config.time_latency = 1; +click_config.time_window = 3; + +lis3dh_set_int_click_config (sensor, lis3dh_int1_signal, &click_config); +``` + +As with other interrupts, the function ```lis3dh_get_int_click_source``` can be used to determine the source of the interrupt signal whenever it is generated. This function returns a data structure of type ```lis3dh_int_click_source_t``` that contains a boolean member for each source that can be tested for true. + +``` +void int1_handler () +{ + lis3dh_int_click_source_t click_src; + + // get interrupt source of INT1 + lis3dh_get_int_click_source (sensor, &click_src); + + // detect single click along z-axis + if (click_src.z_click && click_src.s_click) + ... + ... +} + +``` + +#### Interrupt signal properties + +By default, interrupt signals are high active. Using function ```lis3dh_config_int_signals```, the level of the interrupt signal can be changed. + +Driver symbol | Meaning +:-------------|:------- +lis3dh_high_active | Interrupt signal is high active (default) +lis3dh_low_active | Interrupt signal is low active + + +### Analog inputs and temperature sensor + +The LIS3DH sensor contains an auxiliary ADC with 3 separate dedicated inputs ADC1, ADC2, and ADC3. ADC3 can be connected to the internal temperatur sensor. The input range is 1200 ± 400 mV. The resolution of the A/D converter is 10 bit in normal and high-resolution mode, but only 8 bit in low-power mode. + +ADC inputs can be activated and deactivated (default) with function ```lis3dh_enable_adc```. If parameter ```temp``` is true, ADC3 is connected to the internal temperature sensor and provides the temperature in degrees. + +ADC sampling rate is the same the output data rate (ODR). Results are given as left-aligned 16-bit signed integer values in 2’s complement. Function ```lis3dh_get_adc``` can be used to get the results. + +### Low level functions + +The LIS3DH is a very complex and flexible sensor with a lot of features. It can be used for a big 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 lis3dh_read_reg (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); +bool lis3dh_write_reg (lis3dh_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 the high level interface AND if you exactly know what you do and what it might affect. Please be aware that it might affect the high level interface. + + +## Usage + +First, the hardware configuration has to be established. + +### Hardware configurations + +Following figure shows a possible hardware configuration for ESP8266 and ESP32 if I2C interface is used to connect the sensor. + +``` + +-----------------+ +----------+ + | ESP8266 / ESP32 | | LIS3DH | + | | | | + | GPIO 14 (SCL) >-----> SCL | + | GPIO 13 (SDA) <-----> SDA | + | GPIO 5 <------ INT1 | + | GPIO 4 <------ DRDY/INT2| + +-----------------+ +----------+ +``` + +If SPI interface is used, configuration for ESP8266 and ESP32 could look like following. + +``` + +-----------------+ +----------+ +-----------------+ +----------+ + | ESP8266 | | LIS3DH | | ESP32 | | LIS3DH | + | | | | | | | | + | GPIO 14 (SCK) ------> SCK | | GPIO 16 (SCK) ------> SCK | + | GPIO 13 (MOSI)------> SDI | | GPIO 17 (MOSI)------> SDI | + | GPIO 12 (MISO)<------ SDO | | GPIO 18 (MISO)<------ SDO | + | GPIO 2 (CS) ------> CS | | GPIO 19 (CS) ------> CS | + | GPIO 5 <------ INT1 | | GPIO 5 <------ INT1 | + +-----------------+ +----------+ +-----------------+ +----------+ +``` + +### Communication interface settings + +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 + +// SPI interface definitions for ESP32 +#define SPI_BUS HSPI_HOST +#define SPI_SCK_GPIO 16 +#define SPI_MOSI_GPIO 17 +#define SPI_MISO_GPIO 18 +#define SPI_CS_GPIO 19 + +#else // ESP8266 (esp-open-rtos) + +// user task stack depth for ESP8266 +#define TASK_STACK_DEPTH 256 + +// SPI interface definitions for ESP8266 +#define SPI_BUS 1 +#define SPI_SCK_GPIO 14 +#define SPI_MOSI_GPIO 13 +#define SPI_MISO_GPIO 12 +#define SPI_CS_GPIO 2 // GPIO 15, the default CS of SPI bus 1, can't be used + +#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_100K + +// interrupt GPIOs defintions for ESP8266 and ESP32 +#define INT1_PIN 5 +#define INT2_PIN 4 +``` + +### Main program + +#### Initialization + +If I2C interfaces are used, they have to be initialized first. + +``` +i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ); +``` + +SPI interface has only to be initialized explicitly on ESP32 platform to declare the GPIOs that are used for SPI interface. + +``` +#ifdef ESP_PLATFORM +spi_bus_init (SPI_BUS, SPI_SCK_GPIO, SPI_MISO_GPIO, SPI_MOSI_GPIO); +#endif +``` + +Once the interfaces are initialized, function ```lis3dh_init_sensor``` has to be called for each LIS3DH 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 or SPI bus to which the sensor is connected. + +``` +static lis3dh_sensor_t* sensor; +``` + +For sensors connected to an I2C interface, a valid I2C slave address has to be defined as parameter *addr*. In that case parameter *cs* is ignored. + +``` +sensor = lis3dh_init_sensor (I2C_BUS, LIS3DH_I2C_ADDRESS_1, 0); + +``` + +If parameter *addr* is 0, the sensor is connected to a SPI bus. In that case, parameter *cs* defines the GPIO used as CS signal. + +``` +sensor = lis3dh_init_sensor (SPI_BUS, 0, SPI_CS_GPIO); + +``` + +The remaining of the program is independent on the communication interface. + +#### Periodic user task + +If initialization of the sensor was successful, the user task that uses the sensor has to be created. 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 a configured event happens. + +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) +{ + lis3dh_float_data_t data; + + while (1) + { + // execute task every 10 ms + vTaskDelay (10/portTICK_PERIOD_MS); + ... + // test for new data + if (!lis3dh_new_data (sensor)) + continue; + + // fetch new data + if (lis3dh_get_float_data (sensor, &data)) + { + // do something with data + ... + } + } +} +... +// 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 higher rate than the output data rate (ODR) of the sensor whether new data are available. If new data are available, it fetches the data. + +#### Interrupt user task + +A different approach is to use one of the **interrupts** INT1 or INT2. In this case, the user has to implement an interrupt handler that either fetches the data directly or triggers a task, that is waiting to fetch the data. + +``` +static QueueHandle_t gpio_evt_queue = NULL; + +// Interrupt handler which resumes sends an event to the waiting user_task_interrupt + +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 fetches the sensor values + +void user_task_interrupt (void *pvParameters) +{ + uint32_t gpio_num; + + while (1) + { + if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY)) + { + // test for new data + if (!lis3dh_new_data (sensor)) + continue; + + // fetch new data + if (lis3dh_get_float_data (sensor, &data)) + { + // do something with data + ... + } + } + } +} +... + +// create a task that is triggered only in case of interrupts to fetch the data + +xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL); +... +``` + +In this example, there is + +- a task that is fetching data when it receives an event, and +- an interrupt handler that generates the event on interrupt. + +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 LIS3DH sensor, see section **Interrupts** above. + +#### Configuring the sensor + +Optionally, you could wish to set some measurement parameters. For details see the sections above, the header file of the driver ```lis3dh.h```, and of course the data sheet of the sensor. + +#### Starting measurements + +As last step, the sensor mode has be set to start periodic measurement. The sensor mode can be changed anytime later. + +``` +... +// start periodic measurement with output data rate of 10 Hz +lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); +... +``` + +## Full Example + +``` +/* -- use following constants to define the example mode ----------- */ + +// #define SPI_USED // if defined SPI is used, otherwise I2C + #define DATA_INT // data ready and FIFO status interrupts +// #define CLICK_INT // click detection interrupt +// #define ACTIVITY_INT // wake-up, free fall or 6D/4D orientation detection +// #define FIFO_MODE // multiple sample read mode + +#if defined(DATA_INT) || defined(ACTIVITY_INT) || defined(CLICK_INT) +#define INT_USED +#endif + +/* -- includes ----------------------------------------------------- */ + +#include "lis3dh.h" + +/** -- platform dependent definitions ------------------------------ */ + +#ifdef ESP_PLATFORM // ESP32 (ESP-IDF) + +// user task stack depth for ESP32 +#define TASK_STACK_DEPTH 2048 + +// SPI interface definitions for ESP32 +#define SPI_BUS HSPI_HOST +#define SPI_SCK_GPIO 16 +#define SPI_MOSI_GPIO 17 +#define SPI_MISO_GPIO 18 +#define SPI_CS_GPIO 19 + +#else // ESP8266 (esp-open-rtos) + +// user task stack depth for ESP8266 +#define TASK_STACK_DEPTH 256 + +// SPI interface definitions for ESP8266 +#define SPI_BUS 1 +#define SPI_SCK_GPIO 14 +#define SPI_MOSI_GPIO 13 +#define SPI_MISO_GPIO 12 +#define SPI_CS_GPIO 2 // GPIO 15, the default CS of SPI bus 1, can't be used + +#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_100K + +// interrupt GPIOs defintions for ESP8266 and ESP32 +#define INT1_PIN 5 +#define INT2_PIN 4 + +/* -- user tasks ---------------------------------------------- */ + +static lis3dh_sensor_t* sensor; + +/** + * Common function used to get sensor data. + */ +void read_data () +{ + #ifdef FIFO_MODE + + lis3dh_float_data_fifo_t fifo; + + if (lis3dh_new_data (sensor)) + { + uint8_t num = lis3dh_get_float_data_fifo (sensor, fifo); + + printf("%.3f LIS3DH 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 LIS3DH (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 + + lis3dh_float_data_t data; + + if (lis3dh_new_data (sensor) && + lis3dh_get_float_data (sensor, &data)) + // max. full scale is +-16 g and best resolution is 1 mg, i.e. 5 digits + printf("%.3f LIS3DH (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 +/** + * In this case, any of the possible interrupts on interrupt signal *INT1* is + * used to fetch the data. + * + * When interrupts are used, the user has to define interrupt handlers that + * either fetches the data directly or triggers a task which is waiting to + * fetch the data. In this example, the interrupt handler sends an event to + * a waiting task to trigger the data gathering. + */ + +static QueueHandle_t gpio_evt_queue = NULL; + +// User task that fetches the sensor values. + +void user_task_interrupt (void *pvParameters) +{ + uint32_t gpio_num; + + while (1) + { + if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY)) + { + lis3dh_int_activity_source_t activity_src; + lis3dh_int_data_source_t data_src; + lis3dh_int_click_source_t click_src; + + // get the source of the interrupt and reset INT signals + lis3dh_get_int_activity_source (sensor, &activity_src, lis3dh_int1_signal); + lis3dh_get_int_data_source (sensor, &data_src); + lis3dh_get_int_click_source (sensor, &click_src); + + // in case of DRDY interrupt or activity interrupt read one data sample + if (data_src.data_ready || activity_src.active) + read_data (); + + // in case of FIFO interrupts read the whole FIFO + else if (data_src.fifo_watermark || data_src.fifo_overrun) + read_data (); + + else if (click_src.active) + printf("%.3f LIS3DH %s\n", (double)sdk_system_get_time()*1e-3, + click_src.s_click ? "single click" : "double click"); + } + } +} + +// Interrupt handler which resumes user_task_interrupt on interrupt + +void IRAM int_signal_handler (uint8_t gpio) +{ + // send an event with GPIO to the interrupt user task + xQueueSendFromISR(gpio_evt_queue, &gpio, NULL); +} + +#else // !INT_USED + +/* + * In this example, user task fetches the sensor values every seconds. + */ + +void user_task_periodic(void *pvParameters) +{ + vTaskDelay (100/portTICK_PERIOD_MS); + + while (1) + { + // read sensor data + read_data (); + + // passive waiting until 1 second is over + vTaskDelay(100/portTICK_PERIOD_MS); + } +} + +#endif // INT_USED + +/* -- 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 = lis3dh_init_sensor (SPI_BUS, 0, SPI_CS_GPIO); + + #else + + // init all I2C bus interfaces at which LIS3DH sensors are connected + i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ); + + // init the sensor with slave address LIS3DH_I2C_ADDRESS_1 connected to I2C_BUS. + sensor = lis3dh_init_sensor (I2C_BUS, LIS3DH_I2C_ADDRESS_1, 0); + + #endif + + if (sensor) + { + // --- SYSTEM CONFIGURATION PART ---- + + #if !defined (INT_USED) + + // create a user task that fetches data from sensor periodically + xTaskCreate(user_task_periodic, "user_task_periodic", TASK_STACK_DEPTH, NULL, 2, NULL); + + #else // INT_USED + + // create a task that is triggered only in case of interrupts to fetch the data + xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL); + + // create event queue + 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_set_interrupt(INT1_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler); + + #endif // !defined(INT_USED) + + // -- SENSOR CONFIGURATION PART --- + + // Interrupt configuration has to be done before the sensor is set + // into measurement mode + + // set polarity of INT signals if necessary + // lis3dh_config_int_signals (sensor, lis3dh_high_active); + + #ifdef DATA_INT + // enable data interrupts on INT1 (data ready or FIFO status interrupts) + // data ready and FIFO status interrupts must not be enabled at the same time + #ifdef FIFO_MODE + lis3dh_enable_int_data (sensor, lis3dh_fifo_overrun, true); + lis3dh_enable_int_data (sensor, lis3dh_fifo_watermark, true); + #else + lis3dh_enable_int_data (sensor, lis3dh_data_ready, true); + #endif // FIFO_MODE + #endif // DATA_INT + + #ifdef ACTIVITY_INT + // enable data interrupts on INT1 + lis3dh_int_activity_config_t act_config; + + act_config.activity = lis3dh_wake_up; + // act_config.activity = lis3dh_free_fall; + // act_config.activity = lis3dh_6d_movement; + // act_config.activity = lis3dh_6d_position; + // act_config.activity = lis3dh_4d_movement; + // act_config.activity = lis3dh_4d_position; + act_config.threshold = 10; + act_config.x_low_enabled = false; + act_config.x_high_enabled = true; + act_config.y_low_enabled = false; + act_config.y_high_enabled = true; + act_config.z_low_enabled = false; + act_config.z_high_enabled = true; + act_config.duration = 0; + act_config.latch = true; + + lis3dh_set_int_activity_config (sensor, lis3dh_int1_signal, &act_config); + #endif // ACTIVITY_INT + + #ifdef CLICK_INT + // enable click interrupt on INT1 + lis3dh_int_click_config_t click_config; + + click_config.threshold = 10; + click_config.x_single = false; + click_config.x_double = false; + click_config.y_single = false; + click_config.y_double = false; + click_config.z_single = true; + click_config.z_double = false; + click_config.latch = true; + click_config.time_limit = 1; + click_config.time_latency = 1; + click_config.time_window = 3; + + lis3dh_set_int_click_config (sensor, lis3dh_int1_signal, &click_config); + #endif // CLICK_INT + + #ifdef FIFO_MODE + // clear FIFO and activate FIFO mode if needed + lis3dh_set_fifo_mode (sensor, lis3dh_bypass, 0, lis3dh_int1_signal); + lis3dh_set_fifo_mode (sensor, lis3dh_stream, 10, lis3dh_int1_signal); + #endif + + // configure HPF and reset the reference by dummy read + lis3dh_config_hpf (sensor, lis3dh_hpf_normal, 0, true, true, true, true); + lis3dh_get_hpf_ref (sensor); + + // enable ADC inputs and temperature sensor for ADC input 3 + lis3dh_enable_adc (sensor, true, true); + + // LAST STEP: Finally set scale and mode to start measurements + lis3dh_set_scale(sensor, lis3dh_scale_2g); + lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); + + // -- SENSOR CONFIGURATION PART --- + } +} + +``` diff --git a/extras/lis3dh/component.mk b/extras/lis3dh/component.mk new file mode 100644 index 0000000..0b732ad --- /dev/null +++ b/extras/lis3dh/component.mk @@ -0,0 +1,10 @@ +# Component makefile for extras/lis3dh + +# expected anyone using SHT3x driver includes it as 'lis3dh/lis3dh.h' +INC_DIRS += $(lis3dh_ROOT).. +INC_DIRS += $(lis3dh_ROOT) + +# args for passing into compile rule generation +lis3dh_SRC_DIR = $(lis3dh_ROOT) + +$(eval $(call component_compile_rules,lis3dh)) diff --git a/extras/lis3dh/lis3dh.c b/extras/lis3dh/lis3dh.c new file mode 100644 index 0000000..4dcdae7 --- /dev/null +++ b/extras/lis3dh/lis3dh.c @@ -0,0 +1,1239 @@ +/* + * Driver for LIS3DH 3-axes digital accelerometer connected to I2C or SPI. + * + * This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos) + * [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32 + * and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux + * based systems using a wrapper library for ESP8266 functions. + * + * --------------------------------------------------------------------------- + * + * The BSD License (3-clause license) + * + * Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder nor the names of its + * 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 THE 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. + */ + +#include +#include + +#include "lis3dh.h" + +#if defined(LIS3DH_DEBUG_LEVEL_2) +#define debug(s, f, ...) printf("%s %s: " s "\n", "LIS3DH", f, ## __VA_ARGS__) +#define debug_dev(s, f, d, ...) printf("%s %s: bus %d, addr %02x - " s "\n", "LIS3DH", f, d->bus, d->addr, ## __VA_ARGS__) +#else +#define debug(s, f, ...) +#define debug_dev(s, f, d, ...) +#endif + +#if defined(LIS3DH_DEBUG_LEVEL_1) || defined(LIS3DH_DEBUG_LEVEL_2) +#define error(s, f, ...) printf("%s %s: " s "\n", "LIS3DH", f, ## __VA_ARGS__) +#define error_dev(s, f, d, ...) printf("%s %s: bus %d, addr %02x - " s "\n", "LIS3DH", f, d->bus, d->addr, ## __VA_ARGS__) +#else +#define error(s, f, ...) +#define error_dev(s, f, d, ...) +#endif + +// register addresses +#define LIS3DH_REG_STATUS_AUX 0x07 +#define LIS3DH_REG_OUT_ADC1_L 0x08 +#define LIS3DH_REG_OUT_ADC1_H 0x09 +#define LIS3DH_REG_OUT_ADC2_L 0x0a +#define LIS3DH_REG_OUT_ADC2_H 0x0b +#define LIS3DH_REG_OUT_ADC3_L 0x0c +#define LIS3DH_REG_OUT_ADC3_H 0x0d +#define LIS3DH_REG_INT_COUNTER 0x0e +#define LIS3DH_REG_WHO_AM_I 0x0f +#define LIS3DH_REG_TEMP_CFG 0x1f +#define LIS3DH_REG_CTRL1 0x20 +#define LIS3DH_REG_CTRL2 0x21 +#define LIS3DH_REG_CTRL3 0x22 +#define LIS3DH_REG_CTRL4 0x23 +#define LIS3DH_REG_CTRL5 0x24 +#define LIS3DH_REG_CTRL6 0x25 +#define LIS3DH_REG_REFERENCE 0x26 +#define LIS3DH_REG_STATUS 0x27 +#define LIS3DH_REG_OUT_X_L 0x28 +#define LIS3DH_REG_OUT_X_H 0x29 +#define LIS3DH_REG_OUT_Y_L 0x2a +#define LIS3DH_REG_OUT_Y_H 0x2b +#define LIS3DH_REG_OUT_Z_L 0x2c +#define LIS3DH_REG_OUT_Z_H 0x2d +#define LIS3DH_REG_FIFO_CTRL 0x2e +#define LIS3DH_REG_FIFO_SRC 0x2f +#define LIS3DH_REG_INT1_CFG 0x30 +#define LIS3DH_REG_INT1_SRC 0x31 +#define LIS3DH_REG_INT1_THS 0x32 +#define LIS3DH_REG_INT1_DUR 0x33 +#define LIS3DH_REG_INT2_CFG 0x34 +#define LIS3DH_REG_INT2_SRC 0x35 +#define LIS3DH_REG_INT2_THS 0x36 +#define LIS3DH_REG_INT2_DUR 0x37 +#define LIS3DH_REG_CLICK_CFG 0x38 +#define LIS3DH_REG_CLICK_SRC 0x39 +#define LIS3DH_REG_CLICK_THS 0x3a +#define LIS3DH_REG_TIME_LIMIT 0x3b +#define LIS3DH_REG_TIME_LATENCY 0x3c +#define LIS3DH_REG_TIME_WINDOW 0x3d + +// register structure definitions +struct lis3dh_reg_status +{ + uint8_t XDA :1; // STATUS<0> X axis new data available + uint8_t YDA :1; // STATUS<1> Y axis new data available + uint8_t ZDA :1; // STATUS<2> Z axis new data available + uint8_t ZYXDA :1; // STATUS<3> X, Y and Z axis new data available + uint8_t XOR :1; // STATUS<4> X axis data overrun + uint8_t YOR :1; // STATUS<5> Y axis data overrun + uint8_t ZOR :1; // STATUS<6> Z axis data overrun + uint8_t ZYXOR :1; // STATUS<7> X, Y and Z axis data overrun +}; + +#define LIS3DH_ANY_DATA_READY 0x0f // LIS3DH_REG_STATUS<3:0> + +struct lis3dh_reg_ctrl1 +{ + uint8_t Xen :1; // CTRL1<0> X axis enable + uint8_t Yen :1; // CTRL1<1> Y axis enable + uint8_t Zen :1; // CTRL1<2> Z axis enable + uint8_t LPen :1; // CTRL1<3> Low power mode enable + uint8_t ODR :4; // CTRL1<7:4> Data rate selection +}; + +struct lis3dh_reg_ctrl2 +{ + uint8_t HPIS1 :1; // CTRL2<0> HPF enabled for AOI on INT2 + uint8_t HPIS2 :1; // CTRL2<1> HPF enabled for AOI on INT2 + uint8_t HPCLICK :1; // CTRL2<2> HPF enabled for CLICK + uint8_t FDS :1; // CTRL2<3> Filter data selection + uint8_t HPCF :2; // CTRL2<5:4> HPF cutoff frequency + uint8_t HPM :2; // CTRL2<7:6> HPF mode +}; + +struct lis3dh_reg_ctrl3 +{ + uint8_t unused :1; // CTRL3<0> unused + uint8_t I1_OVERRUN :1; // CTRL3<1> FIFO Overrun interrupt on INT1 + uint8_t I1_WTM1 :1; // CTRL3<2> FIFO Watermark interrupt on INT1 + uint8_t IT_DRDY2 :1; // CTRL3<3> DRDY2 (ZYXDA) interrupt on INT1 + uint8_t IT_DRDY1 :1; // CTRL3<4> DRDY1 (321DA) interrupt on INT1 + uint8_t I1_AOI2 :1; // CTRL3<5> AOI2 interrupt on INT1 + uint8_t I1_AOI1 :1; // CTRL3<6> AOI1 interrupt on INT1 + uint8_t I1_CLICK :1; // CTRL3<7> CLICK interrupt on INT1 +}; + +struct lis3dh_reg_ctrl4 +{ + uint8_t SIM :1; // CTRL4<0> SPI serial interface selection + uint8_t ST :2; // CTRL4<2:1> Self test enable + uint8_t HR :1; // CTRL4<3> High resolution output mode + uint8_t FS :2; // CTRL4<5:4> Full scale selection + uint8_t BLE :1; // CTRL4<6> Big/litle endian data selection + uint8_t BDU :1; // CTRL4<7> Block data update +}; + +struct lis3dh_reg_ctrl5 +{ + uint8_t D4D_INT2 :1; // CTRL5<0> 4D detection enabled on INT1 + uint8_t LIR_INT2 :1; // CTRL5<1> Latch interrupt request on INT1 + uint8_t D4D_INT1 :1; // CTRL5<2> 4D detection enabled on INT2 + uint8_t LIR_INT1 :1; // CTRL5<3> Latch interrupt request on INT1 + uint8_t unused :2; // CTRL5<5:4> unused + uint8_t FIFO_EN :1; // CTRL5<6> FIFO enabled + uint8_t BOOT :1; // CTRL5<7> Reboot memory content +}; + +struct lis3dh_reg_ctrl6 +{ + uint8_t unused1 :1; // CTRL6<0> unused + uint8_t H_LACTIVE:1; // CTRL6<1> Interrupt polarity + uint8_t unused2 :1; // CTRL6<2> unused + uint8_t I2_ACT :1; // CTRL6<3> ? + uint8_t I2_BOOT :1; // CTRL6<4> ? + uint8_t I2_AOI2 :1; // CTRL6<5> AOI2 interrupt on INT1 + uint8_t I2_AOI1 :1; // CTRL6<6> AOI1 interrupt on INT1 + uint8_t I2_CLICK :1; // CTRL6<7> CLICK interrupt on INT2 +}; + +struct lis3dh_reg_fifo_ctrl +{ + uint8_t FTH :5; // FIFO_CTRL<4:0> FIFO threshold + uint8_t TR :1; // FIFO_CTRL<5> Trigger selection INT1 / INT2 + uint8_t FM :2; // FIFO_CTRL<7:6> FIFO mode +}; + +struct lis3dh_reg_fifo_src +{ + uint8_t FFS :5; // FIFO_SRC<4:0> FIFO samples stored + uint8_t EMPTY :1; // FIFO_SRC<5> FIFO is empty + uint8_t OVRN_FIFO :1; // FIFO_SRC<6> FIFO buffer full + uint8_t WTM :1; // FIFO_SRC<7> FIFO content exceeds watermark +}; + +struct lis3dh_reg_intx_cfg +{ + uint8_t XLIE :1; // INTx_CFG<0> X axis below threshold enabled + uint8_t XHIE :1; // INTx_CFG<1> X axis above threshold enabled + uint8_t YLIE :1; // INTx_CFG<2> Y axis below threshold enabled + uint8_t YHIE :1; // INTx_CFG<3> Y axis above threshold enabled + uint8_t ZLIE :1; // INTx_CFG<4> Z axis below threshold enabled + uint8_t ZHIE :1; // INTx_CFG<5> Z axis above threshold enabled + uint8_t SIXD :1; // INTx_CFG<6> 6D/4D orientation detecetion enabled + uint8_t AOI :1; // INTx_CFG<7> AND/OR combination of interrupt events +}; + +struct lis3dh_reg_intx_src +{ + uint8_t XL :1; // INTx_SRC<0> X axis below threshold enabled + uint8_t XH :1; // INTx_SRC<1> X axis above threshold enabled + uint8_t YL :1; // INTx_SRC<2> Y axis below threshold enabled + uint8_t YH :1; // INTx_SRC<3> Y axis above threshold enabled + uint8_t ZL :1; // INTx_SRC<4> Z axis below threshold enabled + uint8_t ZH :1; // INTx_SRC<5> Z axis above threshold enabled + uint8_t IA :1; // INTx_SRC<6> Interrupt active + uint8_t unused:1; // INTx_SRC<7> unused +}; + + +struct lis3dh_reg_click_cfg +{ + uint8_t XS :1; // CLICK_CFG<0> X axis single click enabled + uint8_t XD :1; // CLICK_CFG<1> X axis double click enabled + uint8_t YS :1; // CLICK_CFG<2> Y axis single click enabled + uint8_t YD :1; // CLICK_CFG<3> Y axis double click enabled + uint8_t ZS :1; // CLICK_CFG<4> Z axis single click enabled + uint8_t ZD :1; // CLICK_CFG<5> Z axis double click enabled + uint8_t unused:2; // CLICK_CFG<7:6> unused +}; + + +/** Forward declaration of functions for internal use */ + +static bool lis3dh_reset (lis3dh_sensor_t* dev); +static bool lis3dh_is_available(lis3dh_sensor_t* dev); + +static bool lis3dh_i2c_read (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); +static bool lis3dh_i2c_write (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); +static bool lis3dh_spi_read (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); +static bool lis3dh_spi_write (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); + +#define msb_lsb_to_type(t,b,o) (t)(((t)b[o] << 8) | b[o+1]) +#define lsb_msb_to_type(t,b,o) (t)(((t)b[o+1] << 8) | b[o]) +#define lsb_to_type(t,b,o) (t)(b[o]) + +#define lis3dh_update_reg(dev,addr,type,elem,value) \ + { \ + struct type __reg; \ + if (!lis3dh_read_reg (dev, (addr), (uint8_t*)&__reg, 1)) \ + return false; \ + __reg.elem = (value); \ + if (!lis3dh_write_reg (dev, (addr), (uint8_t*)&__reg, 1)) \ + return false; \ + } + +lis3dh_sensor_t* lis3dh_init_sensor (uint8_t bus, uint8_t addr, uint8_t cs) +{ + lis3dh_sensor_t* dev; + + if ((dev = malloc (sizeof(lis3dh_sensor_t))) == NULL) + return NULL; + + // init sensor data structure + dev->bus = bus; + dev->addr = addr; + dev->cs = cs; + + dev->error_code = LIS3DH_OK; + dev->scale = lis3dh_scale_2g; + dev->fifo_mode = lis3dh_bypass; + dev->fifo_first = true; + + // if addr==0 then SPI is used and has to be initialized + if (!addr && !spi_device_init (bus, cs)) + { + error_dev ("Could not initialize SPI interface.", __FUNCTION__, dev); + free (dev); + return NULL; + } + + // check availability of the sensor + if (!lis3dh_is_available (dev)) + { + error_dev ("Sensor is not available.", __FUNCTION__, dev); + free (dev); + return NULL; + } + + // reset the sensor + if (!lis3dh_reset(dev)) + { + error_dev ("Could not reset the sensor device.", __FUNCTION__, dev); + free (dev); + return NULL; + } + + lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, FS, lis3dh_scale_2g); + lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, BDU, 1); + + return dev; +} + +bool lis3dh_set_mode (lis3dh_sensor_t* dev, + lis3dh_odr_mode_t odr, lis3dh_resolution_t res, + bool x, bool y, bool z) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + dev->res = res; + + struct lis3dh_reg_ctrl1 reg; + uint8_t old_odr; + + // read current register values + if (!lis3dh_read_reg (dev, LIS3DH_REG_CTRL1, (uint8_t*)®, 1)) + return false; + + old_odr = reg.ODR; + + // set mode + reg.Xen = x; + reg.Yen = y; + reg.Zen = z; + reg.ODR = odr; + reg.LPen = (res == lis3dh_low_power); + + lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, + HR, (res == lis3dh_high_res)); + + if (!lis3dh_write_reg (dev, LIS3DH_REG_CTRL1, (uint8_t*)®, 1)) + return false; + + // if sensor was in power down mode it takes at least 100 ms to start in another mode + if (old_odr == lis3dh_power_down && odr != lis3dh_power_down) + vTaskDelay (15); + + return false; +} + + +bool lis3dh_set_scale (lis3dh_sensor_t* dev, lis3dh_scale_t scale) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + dev->scale = scale; + + // read CTRL4 register and write scale + lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, FS, scale); + + return true; +} + + +bool lis3dh_set_fifo_mode (lis3dh_sensor_t* dev, lis3dh_fifo_mode_t mode, + uint8_t thresh, lis3dh_int_signals_t trigger) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + dev->fifo_mode = mode; + + // read CTRL5 register and write FIFO_EN flag + lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, FIFO_EN, mode != lis3dh_bypass); + + struct lis3dh_reg_fifo_ctrl fifo_ctrl = { + .FTH = thresh, + .TR = trigger, + .FM = mode, + }; + + // write FIFO_CTRL register + if (!lis3dh_write_reg (dev, LIS3DH_REG_FIFO_CTRL, (uint8_t*)&fifo_ctrl, 1)) + return false; + + return true; +} + + +bool lis3dh_new_data (lis3dh_sensor_t* dev) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + if (dev->fifo_mode == lis3dh_bypass) + { + struct lis3dh_reg_status status; + + if (!lis3dh_read_reg (dev, LIS3DH_REG_STATUS, (uint8_t*)&status, 1)) + { + error_dev ("Could not get sensor status", __FUNCTION__, dev); + return false; + } + return status.ZYXDA; + } + else + { + struct lis3dh_reg_fifo_src fifo_src; + + if (!lis3dh_read_reg (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) + { + error_dev ("Could not get fifo source register data", __FUNCTION__, dev); + return false; + } + return !fifo_src.EMPTY; + } +} + +/** + * Scaling factors for the conversion of raw sensor data to floating point g + * values. Scaling factors are from mechanical characteristics in datasheet. + * + * scale/sensitivity resolution + * +-1g 1 mg/digit + * +-2g 2 mg/digit + * +-4g 4 mg/digit + * +-16g 12 mg/digit + */ +const static double LIS3DH_SCALES[4] = { 0.001, 0.002, 0.004, 0.012 }; + +bool lis3dh_get_float_data (lis3dh_sensor_t* dev, lis3dh_float_data_t* data) +{ + if (!dev || !data) return false; + + lis3dh_raw_data_t raw; + + if (!lis3dh_get_raw_data (dev, &raw)) + return false; + + data->ax = LIS3DH_SCALES[dev->scale] * (raw.ax >> 4); + data->ay = LIS3DH_SCALES[dev->scale] * (raw.ay >> 4); + data->az = LIS3DH_SCALES[dev->scale] * (raw.az >> 4); + + return true; +} + + +uint8_t lis3dh_get_float_data_fifo (lis3dh_sensor_t* dev, lis3dh_float_data_fifo_t data) +{ + if (!dev || !data) return false; + + lis3dh_raw_data_fifo_t raw; + + uint8_t num = lis3dh_get_raw_data_fifo (dev, raw); + + for (int i = 0; i < num; i++) + { + data[i].ax = LIS3DH_SCALES[dev->scale] * (raw[i].ax >> 4); + data[i].ay = LIS3DH_SCALES[dev->scale] * (raw[i].ay >> 4); + data[i].az = LIS3DH_SCALES[dev->scale] * (raw[i].az >> 4); + } + return num; +} + + +bool lis3dh_get_raw_data (lis3dh_sensor_t* dev, lis3dh_raw_data_t* raw) +{ + if (!dev || !raw) return false; + + dev->error_code = LIS3DH_OK; + + // abort if not in bypass mode + if (dev->fifo_mode != lis3dh_bypass) + { + dev->error_code = LIS3DH_SENSOR_IN_BYPASS_MODE; + error_dev ("Sensor is in FIFO mode, use lis3dh_get_*_data_fifo to get data", + __FUNCTION__, dev); + return false; + } + + // read raw data sample + if (!lis3dh_read_reg (dev, LIS3DH_REG_OUT_X_L, (uint8_t*)raw, 6)) + { + error_dev ("Could not get raw data sample", __FUNCTION__, dev); + dev->error_code |= LIS3DH_GET_RAW_DATA_FAILED; + return false; + } + + return true; +} + + +uint8_t lis3dh_get_raw_data_fifo (lis3dh_sensor_t* dev, lis3dh_raw_data_fifo_t raw) +{ + if (!dev) return 0; + + dev->error_code = LIS3DH_OK; + + // in bypass mode, use lis3dh_get_raw_data to return one sample + if (dev->fifo_mode == lis3dh_bypass) + return lis3dh_get_raw_data (dev, raw) ? 1 : 0; + + struct lis3dh_reg_fifo_src fifo_src; + + // read FIFO state + if (!lis3dh_read_reg (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) + { + error_dev ("Could not get fifo source register data", __FUNCTION__, dev); + return 0; + } + + // if nothing is in the FIFO, just return with 0 + if (fifo_src.EMPTY) + return 0; + + uint8_t samples = fifo_src.FFS + (fifo_src.OVRN_FIFO ? 1 : 0); + + // read samples from FIFO + for (int i = 0; i < samples; i++) + if (!lis3dh_read_reg (dev, LIS3DH_REG_OUT_X_L, (uint8_t*)&raw[i], 6)) + { + error_dev ("Could not get raw data samples", __FUNCTION__, dev); + dev->error_code |= LIS3DH_GET_RAW_DATA_FIFO_FAILED; + return i; + } + + lis3dh_read_reg (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1); + + // if FFS is not 0 after all samples read, ODR is higher than fetching rate + if (fifo_src.FFS) + { + dev->error_code = LIS3DH_ODR_TOO_HIGH; + error_dev ("New samples stored in FIFO while reading, " + "output data rate (ODR) too high", __FUNCTION__, dev); + return 0; + } + + if (dev->fifo_mode == lis3dh_fifo && samples == 32) + { + // clean FIFO (see app note) + lis3dh_update_reg (dev, LIS3DH_REG_FIFO_CTRL, lis3dh_reg_fifo_ctrl, FM, lis3dh_bypass); + lis3dh_update_reg (dev, LIS3DH_REG_FIFO_CTRL, lis3dh_reg_fifo_ctrl, FM, lis3dh_fifo); + } + + return samples; +} + + +bool lis3dh_set_int_activity_config (lis3dh_sensor_t* dev, + lis3dh_int_signals_t signal, + lis3dh_int_activity_config_t* config) +{ + if (!dev || !config) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_intx_cfg intx_cfg; + + intx_cfg.XLIE = config->x_low_enabled; + intx_cfg.XHIE = config->x_high_enabled; + + intx_cfg.YLIE = config->y_low_enabled; + intx_cfg.YHIE = config->y_high_enabled; + + intx_cfg.ZLIE = config->z_low_enabled; + intx_cfg.ZHIE = config->z_high_enabled; + + bool d4d_int = false; + + switch (config->activity) + { + case lis3dh_wake_up : intx_cfg.AOI = 0; intx_cfg.SIXD = 0; break; + case lis3dh_free_fall : intx_cfg.AOI = 1; intx_cfg.SIXD = 0; break; + + case lis3dh_4d_movement : d4d_int = true; + case lis3dh_6d_movement : intx_cfg.AOI = 0; intx_cfg.SIXD = 1; break; + + case lis3dh_4d_position : d4d_int = true; + case lis3dh_6d_position : intx_cfg.AOI = 1; intx_cfg.SIXD = 1; break; + } + + uint8_t intx_cfg_addr = !signal ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; + uint8_t intx_ths_addr = !signal ? LIS3DH_REG_INT1_THS : LIS3DH_REG_INT2_THS; + uint8_t intx_dur_addr = !signal ? LIS3DH_REG_INT1_DUR : LIS3DH_REG_INT2_DUR; + + if (// write the thresholds to registers IG_THS_* + !lis3dh_write_reg (dev, intx_ths_addr, &config->threshold, 1) || + + // write duration configuration to IG_DURATION + !lis3dh_write_reg (dev, intx_dur_addr, &config->duration, 1) || + + // write INT1 configuration to IG_CFG + !lis3dh_write_reg (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1)) + { + error_dev ("Could not configure interrupt INT1", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_INT_FAILED; + return false; + } + + bool enable = intx_cfg.XLIE || intx_cfg.XHIE || + intx_cfg.YLIE || intx_cfg.YHIE || + intx_cfg.ZLIE || intx_cfg.ZHIE; + + if (!signal) + { + lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, LIR_INT1, config->latch); + lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, D4D_INT1, d4d_int); + // enable or disable I1_AOI + lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, lis3dh_reg_ctrl3, I1_AOI1, enable); + } + else + { + lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, LIR_INT2, config->latch); + lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, D4D_INT2, d4d_int); + // enable or disable I2_AOI + lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, lis3dh_reg_ctrl6, I2_AOI1, enable); + } + + return true; +} + + +bool lis3dh_get_int_activity_config (lis3dh_sensor_t* dev, + lis3dh_int_signals_t signal, + lis3dh_int_activity_config_t* config) +{ + if (!dev || !config) return false; + + dev->error_code = LIS3DH_OK; + + uint8_t intx_cfg_addr = !signal ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; + uint8_t intx_ths_addr = !signal ? LIS3DH_REG_INT1_THS : LIS3DH_REG_INT2_THS; + uint8_t intx_dur_addr = !signal ? LIS3DH_REG_INT1_DUR : LIS3DH_REG_INT2_DUR; + + struct lis3dh_reg_intx_cfg intx_cfg; + struct lis3dh_reg_ctrl5 ctrl5; + + if (!lis3dh_read_reg (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1) || + !lis3dh_read_reg (dev, intx_ths_addr, (uint8_t*)&config->threshold, 1) || + !lis3dh_read_reg (dev, intx_dur_addr, (uint8_t*)&config->duration, 1) || + !lis3dh_read_reg (dev, LIS3DH_REG_CTRL5, (uint8_t*)&ctrl5, 1)) + { + error_dev ("Could not read interrupt configuration from sensor", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_INT_FAILED; + return false; + } + + config->x_low_enabled = intx_cfg.XLIE; + config->x_high_enabled = intx_cfg.XHIE; + + config->y_low_enabled = intx_cfg.YLIE; + config->y_high_enabled = intx_cfg.YHIE; + + config->z_low_enabled = intx_cfg.ZLIE; + config->z_high_enabled = intx_cfg.ZHIE; + + bool d4d_int = false; + + if (!signal) + { + config->latch = ctrl5.LIR_INT1; + d4d_int = ctrl5.D4D_INT1; + } + else + { + config->latch = ctrl5.LIR_INT2; + d4d_int = ctrl5.D4D_INT2; + } + + if (intx_cfg.AOI) + { + if (intx_cfg.SIXD && d4d_int) + config->activity = lis3dh_4d_position; + else if (intx_cfg.SIXD && !d4d_int) + config->activity = lis3dh_6d_position; + else + config->activity = lis3dh_free_fall; + } + else + { + if (intx_cfg.SIXD && d4d_int) + config->activity = lis3dh_4d_movement; + else if (intx_cfg.SIXD && !d4d_int) + config->activity = lis3dh_6d_movement; + else + config->activity = lis3dh_wake_up; + } + + return true; +} + + +bool lis3dh_get_int_activity_source (lis3dh_sensor_t* dev, + lis3dh_int_activity_source_t* source, + lis3dh_int_signals_t signal) +{ + if (!dev || !source) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_intx_cfg intx_cfg; + struct lis3dh_reg_intx_src intx_src; + + uint8_t intx_cfg_addr = (!signal) ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; + uint8_t intx_src_addr = (!signal) ? LIS3DH_REG_INT1_SRC : LIS3DH_REG_INT2_SRC; + + if (!lis3dh_read_reg (dev, intx_src_addr, (uint8_t*)&intx_src, 1) || + !lis3dh_read_reg (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1)) + { + error_dev ("Could not read source of interrupt INT1/INT2 from sensor", __FUNCTION__, dev); + dev->error_code |= LIS3DH_INT_SOURCE_FAILED; + return false; + } + + source->active = intx_src.IA; + source->x_low = intx_src.XL & intx_cfg.XLIE; + source->x_high = intx_src.XH & intx_cfg.XHIE; + source->y_low = intx_src.YL & intx_cfg.YLIE; + source->y_high = intx_src.YH & intx_cfg.YHIE; + source->z_low = intx_src.ZL & intx_cfg.ZLIE; + source->z_high = intx_src.ZH & intx_cfg.ZHIE; + + return true; +} + + +bool lis3dh_enable_int_data (lis3dh_sensor_t* dev, + lis3dh_int_data_t type, bool value) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + switch (type) + { + case lis3dh_data_ready: lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, + lis3dh_reg_ctrl3, + IT_DRDY1, value); + break; + case lis3dh_fifo_watermark: lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, + lis3dh_reg_ctrl3, + I1_WTM1, value); + break; + case lis3dh_fifo_overrun: lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, + lis3dh_reg_ctrl3, + I1_OVERRUN, value); + break; + default: dev->error_code = LIS3DH_WRONG_INT_TYPE; + error_dev ("Wrong interrupt type", __FUNCTION__, dev); + return false; + } + + return true; +} + + +bool lis3dh_get_int_data_source (lis3dh_sensor_t* dev, + lis3dh_int_data_source_t* source) +{ + if (!dev || !source) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_ctrl3 ctrl3; + struct lis3dh_reg_status status; + struct lis3dh_reg_fifo_src fifo_src; + + if (!lis3dh_read_reg (dev, LIS3DH_REG_CTRL3 , (uint8_t*)&ctrl3 , 1) || + !lis3dh_read_reg (dev, LIS3DH_REG_STATUS , (uint8_t*)&status , 1) || + !lis3dh_read_reg (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) + { + error_dev ("Could not read source of interrupt INT2 from sensor", __FUNCTION__, dev); + dev->error_code |= LIS3DH_INT_SOURCE_FAILED; + return false; + } + + source->data_ready = status.ZYXDA & ctrl3.IT_DRDY1; + source->fifo_watermark = fifo_src.WTM & ctrl3.I1_WTM1; + source->fifo_overrun = fifo_src.OVRN_FIFO & ctrl3.I1_OVERRUN; + + return true; +} + + +bool lis3dh_set_int_click_config (lis3dh_sensor_t* dev, + lis3dh_int_signals_t signal, + lis3dh_int_click_config_t* config) +{ + if (!dev || !config) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_click_cfg click_cfg; + + click_cfg.XS = config->x_single; + click_cfg.XD = config->x_double; + + click_cfg.YS = config->y_single; + click_cfg.YD = config->y_double; + + click_cfg.ZS = config->z_single; + click_cfg.ZD = config->z_double; + + uint8_t click_ths = config->threshold | ((config->latch) ? 0x80 : 0x00); + + if (!lis3dh_write_reg (dev, LIS3DH_REG_CLICK_CFG , (uint8_t*)&click_cfg, 1) || + !lis3dh_write_reg (dev, LIS3DH_REG_CLICK_THS , (uint8_t*)&click_ths, 1) || + !lis3dh_write_reg (dev, LIS3DH_REG_TIME_LIMIT , (uint8_t*)&config->time_limit, 1) || + !lis3dh_write_reg (dev, LIS3DH_REG_TIME_LATENCY, (uint8_t*)&config->time_latency, 1) || + !lis3dh_write_reg (dev, LIS3DH_REG_TIME_WINDOW , (uint8_t*)&config->time_window, 1)) + { + error_dev ("Could not configure click detection interrupt", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_CLICK_FAILED; + return false; + } + + bool enable = click_cfg.XS || click_cfg.XD || + click_cfg.YS || click_cfg.YD || + click_cfg.ZS || click_cfg.ZD; + if (!signal) + { + lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, lis3dh_reg_ctrl3, I1_CLICK, enable); + } + else + { + lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, lis3dh_reg_ctrl6, I2_CLICK, enable); + } + + return true; +} + +bool lis3dh_get_int_click_config (lis3dh_sensor_t* dev, + lis3dh_int_signals_t signal, + lis3dh_int_click_config_t* config) +{ + if (!dev || !config) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_click_cfg click_cfg; + uint8_t click_ths; + + if (!lis3dh_read_reg (dev, LIS3DH_REG_CLICK_CFG , (uint8_t*)&click_cfg, 1) || + !lis3dh_read_reg (dev, LIS3DH_REG_CLICK_THS , (uint8_t*)&click_ths, 1) || + !lis3dh_read_reg (dev, LIS3DH_REG_TIME_LIMIT , (uint8_t*)&config->time_limit, 1) || + !lis3dh_read_reg (dev, LIS3DH_REG_TIME_LATENCY, (uint8_t*)&config->time_latency, 1) || + !lis3dh_read_reg (dev, LIS3DH_REG_TIME_WINDOW , (uint8_t*)&config->time_window, 1)) + { + error_dev ("Could not configure click detection interrupt", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_CLICK_FAILED; + return false; + } + + config->x_single = click_cfg.XS; + config->x_double = click_cfg.XD; + + config->y_single = click_cfg.YS; + config->y_double = click_cfg.YD; + + config->z_single = click_cfg.ZS; + config->z_double = click_cfg.ZD; + + config->threshold= click_ths & 0x7f; + config->latch = click_ths & 0x80; + + return true; +} + +bool lis3dh_get_int_click_source (lis3dh_sensor_t* dev, + lis3dh_int_click_source_t* source) +{ + if (!dev || !source) return false; + + dev->error_code = LIS3DH_OK; + + if (!lis3dh_read_reg (dev, LIS3DH_REG_CLICK_SRC, (uint8_t*)source, 1)) + { + error_dev ("Could not read source of click interrupt from sensor", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CLICK_SOURCE_FAILED; + return false; + } + + return true; +} + + + +bool lis3dh_config_int_signals (lis3dh_sensor_t* dev, lis3dh_int_signal_level_t level) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + lis3dh_update_reg (dev, LIS3DH_REG_CTRL6, lis3dh_reg_ctrl6, H_LACTIVE, level); + + return true; +} + + +bool lis3dh_config_hpf (lis3dh_sensor_t* dev, + lis3dh_hpf_mode_t mode, uint8_t cutoff, + bool data, bool click, bool int1, bool int2) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_ctrl2 reg; + + reg.HPM = mode; + reg.HPCF = cutoff; + reg.FDS = data; + reg.HPCLICK = click; + reg.HPIS1 = int1; + reg.HPIS2 = int2; + + if (!lis3dh_write_reg (dev, LIS3DH_REG_CTRL2, (uint8_t*)®, 1)) + { + error_dev ("Could not configure high pass filter", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_HPF_FAILED; + return false; + } + + return true; +} + + +bool lis3dh_set_hpf_ref (lis3dh_sensor_t* dev, int8_t ref) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + if (!lis3dh_write_reg (dev, LIS3DH_REG_REFERENCE, (uint8_t*)&ref, 1)) + { + error_dev ("Could not set high pass filter reference", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_HPF_FAILED; + return false; + } + + return true; +} + + +int8_t lis3dh_get_hpf_ref (lis3dh_sensor_t* dev) +{ + if (!dev) return 0; + + dev->error_code = LIS3DH_OK; + + int8_t ref; + + if (!lis3dh_read_reg (dev, LIS3DH_REG_REFERENCE, (uint8_t*)&ref, 1)) + { + error_dev ("Could not get high pass filter reference", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_HPF_FAILED; + return 0; + } + + return ref; +} + +int8_t lis3dh_enable_adc (lis3dh_sensor_t* dev, bool adc, bool tmp) +{ + if (!dev) return 0; + + dev->error_code = LIS3DH_OK; + + uint8_t reg = 0; + + reg |= (adc) ? 0x80 : 0; + reg |= (tmp) ? 0x40 : 0; + + return lis3dh_write_reg (dev, LIS3DH_REG_TEMP_CFG, (uint8_t*)®, 1); +} + + +bool lis3dh_get_adc (lis3dh_sensor_t* dev, + uint16_t* adc1, uint16_t* adc2, uint16_t* adc3) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + uint8_t data[6]; + uint8_t temp_cfg; + struct lis3dh_reg_ctrl1 ctrl1; + + if (!lis3dh_read_reg (dev, LIS3DH_REG_OUT_ADC1_L, data, 6) || + !lis3dh_read_reg (dev, LIS3DH_REG_CTRL1, (uint8_t*)&ctrl1, 1) || + !lis3dh_read_reg (dev, LIS3DH_REG_TEMP_CFG, &temp_cfg, 1)) + { + error_dev ("Could not get adc data", __FUNCTION__, dev); + dev->error_code |= LIS3DH_GET_ADC_DATA_FAILED; + return false; + } + + if (adc1) *adc1 = lsb_msb_to_type ( int16_t, data, 0) >> (ctrl1.LPen ? 8 : 6); + if (adc2) *adc2 = lsb_msb_to_type ( int16_t, data, 2) >> (ctrl1.LPen ? 8 : 6); + + // temperature is always 8 bit + if (adc3 && temp_cfg & 0x40) + *adc3 = (lsb_msb_to_type ( int16_t, data, 4) >> 8) + 25; + else if (adc3) + *adc3 = lsb_msb_to_type ( int16_t, data, 4) >> (ctrl1.LPen ? 8 : 6); + + return true; +} + + +/** Functions for internal use only */ + +/** + * @brief Check the chip ID to test whether sensor is available + */ +static bool lis3dh_is_available (lis3dh_sensor_t* dev) +{ + uint8_t chip_id; + + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + if (!lis3dh_read_reg (dev, LIS3DH_REG_WHO_AM_I, &chip_id, 1)) + return false; + + if (chip_id != LIS3DH_CHIP_ID) + { + error_dev ("Chip id %02x is wrong, should be %02x.", + __FUNCTION__, dev, chip_id, LIS3DH_CHIP_ID); + dev->error_code = LIS3DH_WRONG_CHIP_ID; + return false; + } + + return true; +} + +static bool lis3dh_reset (lis3dh_sensor_t* dev) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + uint8_t reg[8] = { 0 }; + + // initialize sensor completely including setting in power down mode + lis3dh_write_reg (dev, LIS3DH_REG_TEMP_CFG , reg, 8); + lis3dh_write_reg (dev, LIS3DH_REG_FIFO_CTRL, reg, 1); + lis3dh_write_reg (dev, LIS3DH_REG_INT1_CFG , reg, 1); + lis3dh_write_reg (dev, LIS3DH_REG_INT1_THS , reg, 2); + lis3dh_write_reg (dev, LIS3DH_REG_INT2_CFG , reg, 1); + lis3dh_write_reg (dev, LIS3DH_REG_INT2_THS , reg, 2); + lis3dh_write_reg (dev, LIS3DH_REG_CLICK_CFG, reg, 1); + lis3dh_write_reg (dev, LIS3DH_REG_CLICK_THS, reg, 4); + + return true; +} + + +bool lis3dh_read_reg(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + return (dev->addr) ? lis3dh_i2c_read (dev, reg, data, len) + : lis3dh_spi_read (dev, reg, data, len); +} + + +bool lis3dh_write_reg(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + return (dev->addr) ? lis3dh_i2c_write (dev, reg, data, len) + : lis3dh_spi_write (dev, reg, data, len); +} + + +#define LIS3DH_SPI_BUF_SIZE 64 // SPI register data buffer size of ESP866 + +#define LIS3DH_SPI_READ_FLAG 0x80 +#define LIS3DH_SPI_WRITE_FLAG 0x00 +#define LIS3DH_SPI_AUTO_INC_FLAG 0x40 + +static bool lis3dh_spi_read(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + if (len >= LIS3DH_SPI_BUF_SIZE) + { + dev->error_code |= LIS3DH_SPI_BUFFER_OVERFLOW; + error_dev ("Error on read from SPI slave on bus 1. Tried to transfer " + "more than %d byte in one read operation.", + __FUNCTION__, dev, LIS3DH_SPI_BUF_SIZE); + return false; + } + + uint8_t addr = (reg & 0x3f) | LIS3DH_SPI_READ_FLAG | LIS3DH_SPI_AUTO_INC_FLAG; + + static uint8_t mosi[LIS3DH_SPI_BUF_SIZE]; + static uint8_t miso[LIS3DH_SPI_BUF_SIZE]; + + memset (mosi, 0xff, LIS3DH_SPI_BUF_SIZE); + memset (miso, 0xff, LIS3DH_SPI_BUF_SIZE); + + mosi[0] = addr; + + if (!spi_transfer_pf (dev->bus, dev->cs, mosi, miso, len+1)) + { + error_dev ("Could not read data from SPI", __FUNCTION__, dev); + dev->error_code |= LIS3DH_SPI_READ_FAILED; + return false; + } + + // shift data one by left, first byte received while sending register address is invalid + for (int i=0; i < len; i++) + data[i] = miso[i+1]; + + #ifdef LIS3DH_DEBUG_LEVEL_2 + printf("LIS3DH %s: read the following bytes from reg %02x: ", __FUNCTION__, reg); + for (int i=0; i < len; i++) + printf("%02x ", data[i]); + printf("\n"); + #endif + + return true; +} + + +static bool lis3dh_spi_write(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + uint8_t addr = (reg & 0x3f) | LIS3DH_SPI_WRITE_FLAG | LIS3DH_SPI_AUTO_INC_FLAG; + + static uint8_t mosi[LIS3DH_SPI_BUF_SIZE]; + + if (len >= LIS3DH_SPI_BUF_SIZE) + { + dev->error_code |= LIS3DH_SPI_BUFFER_OVERFLOW; + error_dev ("Error on write to SPI slave on bus 1. Tried to transfer more" + "than %d byte in one write operation.", + __FUNCTION__, dev, LIS3DH_SPI_BUF_SIZE); + + return false; + } + + reg &= 0x7f; + + // first byte in output is the register address + mosi[0] = addr; + + // shift data one byte right, first byte in output is the register address + for (int i = 0; i < len; i++) + mosi[i+1] = data[i]; + + #ifdef LIS3DH_DEBUG_LEVEL_2 + printf("LIS3DH %s: Write the following bytes to reg %02x: ", __FUNCTION__, reg); + for (int i = 1; i < len+1; i++) + printf("%02x ", mosi[i]); + printf("\n"); + #endif + + if (!spi_transfer_pf (dev->bus, dev->cs, mosi, NULL, len+1)) + { + error_dev ("Could not write data to SPI.", __FUNCTION__, dev); + dev->error_code |= LIS3DH_SPI_WRITE_FAILED; + return false; + } + + return true; +} + + +#define I2C_AUTO_INCREMENT (0x80) + +static bool lis3dh_i2c_read(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + debug_dev ("Read %d byte from i2c slave register %02x.", __FUNCTION__, dev, len, reg); + + if (len > 1) + reg |= I2C_AUTO_INCREMENT; + + int result = i2c_slave_read(dev->bus, dev->addr, ®, data, len); + + if (result) + { + dev->error_code |= (result == -EBUSY) ? LIS3DH_I2C_BUSY : LIS3DH_I2C_READ_FAILED; + error_dev ("Error %d on read %d byte from I2C slave register %02x.", + __FUNCTION__, dev, result, len, reg); + return false; + } + +# ifdef LIS3DH_DEBUG_LEVEL_2 + printf("LIS3DH %s: Read following bytes: ", __FUNCTION__); + printf("%02x: ", reg & 0x7f); + for (int i=0; i < len; i++) + printf("%02x ", data[i]); + printf("\n"); +# endif + + return true; +} + + +static bool lis3dh_i2c_write(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + debug_dev ("Write %d byte to i2c slave register %02x.", __FUNCTION__, dev, len, reg); + + if (len > 1) + reg |= I2C_AUTO_INCREMENT; + + int result = i2c_slave_write(dev->bus, dev->addr, ®, data, len); + + if (result) + { + dev->error_code |= (result == -EBUSY) ? LIS3DH_I2C_BUSY : LIS3DH_I2C_WRITE_FAILED; + error_dev ("Error %d on write %d byte to i2c slave register %02x.", + __FUNCTION__, dev, result, len, reg); + return false; + } + +# ifdef LIS3DH_DEBUG_LEVEL_2 + printf("LIS3DH %s: Wrote the following bytes: ", __FUNCTION__); + printf("%02x: ", reg & 0x7f); + for (int i=0; i < len; i++) + printf("%02x ", data[i]); + printf("\n"); +# endif + + return true; +} diff --git a/extras/lis3dh/lis3dh.h b/extras/lis3dh/lis3dh.h new file mode 100644 index 0000000..29eae78 --- /dev/null +++ b/extras/lis3dh/lis3dh.h @@ -0,0 +1,462 @@ +/** + * Driver for LIS3DH 3-axes digital accelerometer connected to I2C or SPI. + * + * This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos) + * [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32 + * and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux + * based systems using a wrapper library for ESP8266 functions. + * + * --------------------------------------------------------------------------- + * + * The BSD License (3-clause license) + * + * Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder nor the names of its + * 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 THE 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. + */ + +#ifndef __LIS3DH_H__ +#define __LIS3DH_H__ + +// Uncomment one of the following defines to enable debug output +// #define LIS3DH_DEBUG_LEVEL_1 // only error messages +// #define LIS3DH_DEBUG_LEVEL_2 // debug and error messages + +// LIS3DH addresses (also used for LIS2DH, LIS2DH12 and LIS2DE12) +#define LIS3DH_I2C_ADDRESS_1 0x18 // SDO pin is low +#define LIS3DH_I2C_ADDRESS_2 0x19 // SDO pin is high + +// LIS3DE addresse (also used for LIS2DE) +#define LIS3DE_I2C_ADDRESS_1 0x28 // SDO pin is low +#define LIS3DE_I2C_ADDRESS_2 0x29 // SDO pin is high + +// LIS3DH chip id +#define LIS3DH_CHIP_ID 0x33 // LIS3DH_REG_WHO_AM_I<7:0> + +// Definition of error codes +#define LIS3DH_OK 0 +#define LIS3DH_NOK -1 + +#define LIS3DH_INT_ERROR_MASK 0x000f +#define LIS3DH_DRV_ERROR_MASK 0xfff0 + +// Error codes for I2C and SPI interfaces ORed with LIS3DH driver error codes +#define LIS3DH_I2C_READ_FAILED 1 +#define LIS3DH_I2C_WRITE_FAILED 2 +#define LIS3DH_I2C_BUSY 3 +#define LIS3DH_SPI_WRITE_FAILED 4 +#define LIS3DH_SPI_READ_FAILED 5 +#define LIS3DH_SPI_BUFFER_OVERFLOW 6 + +// LIS3DH driver error codes ORed with error codes for I2C and SPI interfaces +#define LIS3DH_WRONG_CHIP_ID ( 1 << 8) +#define LIS3DH_WRONG_BANDWIDTH ( 2 << 8) +#define LIS3DH_GET_RAW_DATA_FAILED ( 3 << 8) +#define LIS3DH_GET_RAW_DATA_FIFO_FAILED ( 4 << 8) +#define LIS3DH_WRONG_INT_TYPE ( 5 << 8) +#define LIS3DH_CONFIG_INT_SIGNALS_FAILED ( 6 << 8) +#define LIS3DH_CONFIG_INT_FAILED ( 7 << 8) +#define LIS3DH_INT_SOURCE_FAILED ( 8 << 8) +#define LIS3DH_CONFIG_HPF_FAILED ( 9 << 8) +#define LIS3DH_ENABLE_HPF_FAILED (10 << 8) +#define LIS3DH_CONFIG_CLICK_FAILED (11 << 8) +#define LIS3DH_CLICK_SOURCE_FAILED (12 << 8) +#define LIS3DH_GET_ADC_DATA_FAILED (13 << 8) +#define LIS3DH_SENSOR_IN_BYPASS_MODE (14 << 8) +#define LIS3DH_SENSOR_IN_FIFO_MODE (15 << 8) +#define LIS3DH_ODR_TOO_HIGH (16 << 8) + +#include "lis3dh_platform.h" +#include "lis3dh_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/** + * @brief Initialize the sensor + * + * Reset the sensor and switch to power down mode. All registers are reset to + * default values. FIFO is cleared. + * + * @param bus I2C or SPI bus at which LIS3DH sensor is connected + * @param addr I2C addr of the LIS3DH sensor, 0 for using SPI + * @param cs SPI CS GPIO, ignored for I2C + * @return pointer to sensor data structure, or NULL on error + */ +lis3dh_sensor_t* lis3dh_init_sensor (uint8_t bus, uint8_t addr, uint8_t cs); + + +/** + * @brief Set sensor mode + * + * @param dev pointer to the sensor device data structure + * @param odr sensor output data rate (ODR) + * @param res sensor resolution + * @param x true enable x-axis, false disable x-axis + * @param y true enable y-axis, false disable y-axis + * @param z true enable z-axis, false disable z-axis + * @return true on success, false on error + */ +bool lis3dh_set_mode (lis3dh_sensor_t* dev, + lis3dh_odr_mode_t odr, lis3dh_resolution_t res, + bool x, bool y, bool z); + + +/** + * @brief Set scale (full scale range) + * + * @param dev pointer to the sensor device data structure + * @param scale full range scale + * @return true on success, false on error + */ +bool lis3dh_set_scale (lis3dh_sensor_t* dev, lis3dh_scale_t scale); + + +/** + * @brief Set FIFO mode + * + * FIFO watermark can be used to generate an interrupt when FIFO content + * exceeds the value. It is ignored in bypass mode. + * + * + * @param dev pointer to the sensor device data structure + * @param mode FIFO mode + * @param thresh FIFO watermark (ignored in bypass mode) + * @param trigger interrupt signal used as trigger (only in Stream-to-FIFO) + * @return true on success, false on error + */ +bool lis3dh_set_fifo_mode (lis3dh_sensor_t* dev, lis3dh_fifo_mode_t mode, + uint8_t thresh, lis3dh_int_signals_t trigger); + + +/** + * @brief Test whether new data samples are available + * + * @param dev pointer to the sensor device data structure + * @return true on new data, otherwise false + */ +bool lis3dh_new_data (lis3dh_sensor_t* dev); + + +/** + * @brief Get one sample of sensor data as floating point values (unit 1g) + * + * Function works only in bypass mode and fails in FIFO modes. In FIFO modes, + * function *lis3dh_get_float_data_fifo* has to be used instead to get data. + * + * @param dev pointer to the sensor device data structure + * @param data pointer to float data structure filled with g values + * @return true on success, false on error + */ +bool lis3dh_get_float_data (lis3dh_sensor_t* dev, + lis3dh_float_data_t* data); + + +/** + * @brief Get all samples of sensor data stored in the FIFO (unit g) + * + * In bypass mode, it returns only one sensor data sample. + * + * @param dev pointer to the sensor device data structure + * @param data array of 32 float data structures filled with g values + * @return number of data sets read from fifo on success or 0 on error + */ +uint8_t lis3dh_get_float_data_fifo (lis3dh_sensor_t* dev, + lis3dh_float_data_fifo_t data); + + +/** + * @brief Get one sample of raw sensor data as 16 bit two's complements + * + * Function works only in bypass mode and fails in FIFO modes. In FIFO modes, + * function *lis3dh_get_raw_data_fifo* has to be used instead to get data. + * + * @param dev pointer to the sensor device data structure + * @param raw pointer to raw data structure filled with values + * @return true on success, false on error + */ +bool lis3dh_get_raw_data (lis3dh_sensor_t* dev, lis3dh_raw_data_t* raw); + + +/** + * @brief Get all samples of raw sensor data stored in the FIFO + * + * In bypass mode, it returns only one raw data sample. + * + * @param dev pointer to the sensor device data structure + * @param raw array of 32 raw data structures + * @return number of data sets read from fifo on success or 0 on error + */ +uint8_t lis3dh_get_raw_data_fifo (lis3dh_sensor_t* dev, + lis3dh_raw_data_fifo_t raw); + + +/** + * @brief Set configuration for activity (inertial) interrupt INT1/INT2 + * + * Set the configuration for interrupts that are generated when a certain + * acceleration is higher or lower than defined threshold and one of the + * following activities are recognized: wake-up, free fall or 6D/4D + * orientation detection + * + * @param dev pointer to the sensor device data structure + * @param signal specifies the interrupt signal used in function + * @param config configuration for the specified interrupt signal + * @return true on success, false on error + */ +bool lis3dh_set_int_activity_config (lis3dh_sensor_t* dev, + lis3dh_int_signals_t signal, + lis3dh_int_activity_config_t* config); + + +/** + * @brief Get cofiguration for activity (inertial) interrupt INT1/INT2 + * + * Get the configuration for interrupts that are generated when a certain + * acceleration is higher or lower than defined threshold and one of the + * following activities is recognized: wake-up, free fall or 6D/4D orientation + * detection + * + * @param dev pointer to the sensor device data structure + * @param signal specifies the interrupt signal used in function + * @param config configuration for the specified interrupt signal + * @return true on success, false on error + */ +bool lis3dh_get_int_activity_config (lis3dh_sensor_t* dev, + lis3dh_int_signals_t signal, + lis3dh_int_activity_config_t* config); + + +/** + * @brief Get the source of the activity (inertial) interrupt INT1/INT2 + * + * Returns a byte with flags that indicate the activity which triggered + * the interrupt signal (see INTx_SRC register in datasheet for details) + * + * @param dev pointer to the sensor device data structure + * @param source pointer to the interrupt source + * @param signal specifies the interrupt signal used in function + * @return true on success, false on error + */ +bool lis3dh_get_int_activity_source (lis3dh_sensor_t* dev, + lis3dh_int_activity_source_t* source, + lis3dh_int_signals_t signal); + + +/** + * @brief Enable/disable an data interrupt on signal INT1 + * + * Enables or diables interrupts that are generated either when data are + * ready to read or FIFO activities like overrun an watermark happen. + * + * @param dev pointer to the sensor device data structure + * @param type type of interrupt to be enabled/disabled + * @param value true to enable/false to disable the interrupt + * @return true on success, false on error + */ +bool lis3dh_enable_int_data (lis3dh_sensor_t* dev, + lis3dh_int_data_t type, bool value); + + +/** + * @brief Get the source of the data interrupt on signal INT1 + * + * @param dev pointer to the sensor device data structure + * @param source pointer to the interrupt source + * @return true on success, false on error + */ +bool lis3dh_get_int_data_source (lis3dh_sensor_t* dev, + lis3dh_int_data_source_t* source); + + +/** + * @brief Set configuration for click detection interrupt INT1/INT2 + * + * Set the configuration for interrupts that are generated when single or + * double clicks are detected. + * + * @param dev pointer to the sensor device data structure + * @param signal specifies the interrupt signal used in function + * @param config configuration for the specified interrupt signal + * @return true on success, false on error + */ +bool lis3dh_set_int_click_config (lis3dh_sensor_t* dev, + lis3dh_int_signals_t signal, + lis3dh_int_click_config_t* config); + +/** + * @brief Set configuration for click detection interrupt INT1/INT2 + * + * Set the configuration for interrupts that are generated when single or + * double clicks are detected. + * + * @param dev pointer to the sensor device data structure + * @param signal specifies the interrupt signal used in function + * @param config configuration for the specified interrupt signal + * @return true on success, false on error + */ +bool lis3dh_get_int_click_config (lis3dh_sensor_t* dev, + lis3dh_int_signals_t signal, + lis3dh_int_click_config_t* config); + + +/** + * @brief Get the source of the click detection interrupt INT1/INT2 + * + * Returns a byte with flags that indicate the activity which triggered + * the interrupt signal (see CLICK_SRC register in datasheet for details) + * + * @param dev pointer to the sensor device data structure + * @param signal specifies the interrupt signal used in function + * @param source pointer to the interrupt source + * @return true on success, false on error + */ +bool lis3dh_get_int_click_source (lis3dh_sensor_t* dev, + lis3dh_int_click_source_t* source); + + +/** + * @brief Set signal configuration for INT1 and INT2 signals + * + * @param dev pointer to the sensor device data structure + * @param level define interrupt signal as low or high active + * @return true on success, false on error + */ +bool lis3dh_config_int_signals (lis3dh_sensor_t* dev, + lis3dh_int_signal_level_t level); + + +/** + * @brief Config HPF (high pass filter) + * + * @param dev pointer to the sensor device data structure + * @param mode filter mode + * @param cutoff filter cutoff frequency (depends on ODR) [0 ... 3] + * @param data if true, use filtered data as sensor output + * @param click if true, use filtered data for CLICK function + * @param int1 if true, use filtered data for interrupt INT1 generation + * @param int2 if true, use filtered data for interrupt INT2 generation + * @return true on success, false on error + */ +bool lis3dh_config_hpf (lis3dh_sensor_t* dev, + lis3dh_hpf_mode_t mode, uint8_t cutoff, + bool data, bool click, bool int1, bool int2); + + +/** + * @brief Set HPF (high pass filter) reference + * + * Used to set the reference of HPF in reference mode *lis3dh_hpf_reference*. + * Used to reset the HPF in autoreset mode *lis3dh_hpf_autoreset*. + * Reference is given as two's complement. + * + * @param dev pointer to the sensor device data structure + * @param ref reference *lis3dh_hpf_reference* mode, otherwise ignored + * @return true on success, false on error + */ +bool lis3dh_set_hpf_ref (lis3dh_sensor_t* dev, int8_t ref); + + +/** + * @brief Get HPF (high pass filter) reference + * + * Used to reset the HPF in normal mode *lis3dh_hpf_normal*. + * + * @param dev pointer to the sensor device data structure + * @return HPF reference as two's complement + */ +int8_t lis3dh_get_hpf_ref (lis3dh_sensor_t* dev); + + +/** + * @brief Enable / disable ADC or temperatury sensor + * + * @param dev pointer to the sensor device data structure + * @param enable if true, ADC inputs are enabled + * @param temp if true, ADC input 3 is the output of temperature sensor + * @return true on success, false on error + */ +int8_t lis3dh_enable_adc (lis3dh_sensor_t* dev, bool enable, bool temp); + + +/** + * @brief Get ADC input or temperature + * + * @param dev pointer to the sensor device data structure + * @param adc1 ADC input 1 + * @param adc2 ADC input 2 + * @param adc3 ADC input 3 or temperature in degree if enabled + * @return true on success, false on error + */ +bool lis3dh_get_adc (lis3dh_sensor_t* dev, + uint16_t* adc1, uint16_t* adc2, uint16_t* adc3); + +// ---- Low level interface functions ----------------------------- + +/** + * @brief Direct write to register + * + * PLEASE NOTE: This function should only be used to do something special that + * is not covered by the high level interface AND if you exactly know what you + * do and what effects it might have. Please be aware that it might affect the + * high level interface. + * + * @param dev pointer to the sensor device data structure + * @param reg address of the first register to be changed + * @param data pointer to the data to be written to the register + * @param len number of bytes to be written to the register + * @return true on success, false on error + */ +bool lis3dh_write_reg (lis3dh_sensor_t* dev, + uint8_t reg, uint8_t *data, uint16_t len); + +/** + * @brief Direct read from register + * + * PLEASE NOTE: This function should only be used to do something special that + * is not covered by the high level interface AND if you exactly know what you + * do and what effects it might have. Please be aware that it might affect the + * high level interface. + * + * @param dev pointer to the sensor device data structure + * @param reg address of the first register to be read + * @param data pointer to the data to be read from the register + * @param len number of bytes to be read from the register + * @return true on success, false on error + */ +bool lis3dh_read_reg (lis3dh_sensor_t* dev, + uint8_t reg, uint8_t *data, uint16_t len); + +#ifdef __cplusplus +} +#endif /* End of CPP guard */ + +#endif /* __LIS3DH_H__ */ diff --git a/extras/lis3dh/lis3dh_platform.h b/extras/lis3dh/lis3dh_platform.h new file mode 100644 index 0000000..03fd6bc --- /dev/null +++ b/extras/lis3dh/lis3dh_platform.h @@ -0,0 +1,112 @@ +/** + * Driver for LIS3DH 3-axes digital accelerometer connected to I2C or SPI. + * + * This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos) + * [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32 + * and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux + * based systems using a wrapper library for ESP8266 functions. + * + * --------------------------------------------------------------------------- + * + * The BSD License (3-clause license) + * + * Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder nor the names of its + * 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 THE 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. + */ + +/** + * Platform file: platform specific definitions, includes and functions + */ + +#ifndef __LIS3DH_PLATFORM_H__ +#define __LIS3DH_PLATFORM_H__ + +#if !defined(ESP_OPEN_RTOS) +#define ESP_OPEN_RTOS 1 +#endif + +#ifdef ESP_OPEN_RTOS // ESP8266 + +// platform specific includes + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include "espressif/esp_common.h" +#include "espressif/sdk_private.h" + +#include "esp/uart.h" +#include "esp/spi.h" +#include "i2c/i2c.h" + +// platform specific definitions + +#define spi_semaphore_init() +#define spi_semaphore_take() +#define spi_semaphore_give() + +// platform specific SPI functions + +#define spi_bus_init(bus,sck,miso,mosi) // not needed on ESP8266 + +static const spi_settings_t bus_settings = { + .mode = SPI_MODE0, + .freq_divider = SPI_FREQ_DIV_1M, + .msb = true, + .minimal_pins = false, + .endianness = SPI_LITTLE_ENDIAN +}; + +inline static bool spi_device_init (uint8_t bus, uint8_t cs) +{ + gpio_enable(cs, GPIO_OUTPUT); + gpio_write (cs, true); + return true; +} + +inline static size_t spi_transfer_pf(uint8_t bus, uint8_t cs, const uint8_t *mosi, uint8_t *miso, uint16_t len) +{ + spi_settings_t old_settings; + + spi_get_settings(bus, &old_settings); + spi_set_settings(bus, &bus_settings); + gpio_write(cs, false); + + size_t transfered = spi_transfer (bus, (const void*)mosi, (void*)miso, len, SPI_8BIT); + + gpio_write(cs, true); + spi_set_settings(bus, &old_settings); + + return transfered; +} + +#endif // ESP_OPEN_RTOS + +#endif // __LIS3DH_PLATFORM_H__ diff --git a/extras/lis3dh/lis3dh_types.h b/extras/lis3dh/lis3dh_types.h new file mode 100644 index 0000000..868108a --- /dev/null +++ b/extras/lis3dh/lis3dh_types.h @@ -0,0 +1,336 @@ +/** + * Driver for LIS3DH 3-axes digital accelerometer connected to I2C or SPI. + * + * This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos) + * [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32 + * and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux + * based systems using a wrapper library for ESP8266 functions. + * + * --------------------------------------------------------------------------- + * + * The BSD License (3-clause license) + * + * Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder nor the names of its + * 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 Activity SHALL THE 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. + */ + +#ifndef __LIS3DH_TYPES_H__ +#define __LIS3DH_TYPES_H__ + +#include "stdint.h" +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/** + * @brief Output data rates (ODR), related to resolution modes + */ +typedef enum { + + lis3dh_power_down = 0, // power down mode + lis3dh_odr_1, // high resolution / normal / low power 1 Hz + lis3dh_odr_10, // high resolution / normal / low power 10 Hz + lis3dh_odr_25, // high resolution / normal / low power 25 Hz + lis3dh_odr_50, // high resolution / normal / low power 50 Hz + lis3dh_odr_100, // high resolution / normal / low power 100 Hz + lis3dh_odr_200, // high resolution / normal / low power 200 Hz + lis3dh_odr_400, // high resolution / normal / low power 400 Hz + lis3dh_odr_1600, // low power mode 1.6 kHz + lis3dh_odr_5000, // normal 1.25 kHz / low power 5 kHz + +} lis3dh_odr_mode_t; + +/** + * @brief Resolution modes, related to output data rates (ODR) + */ +typedef enum { + + lis3dh_low_power, // low power mode resolution ( 8 bit data) + lis3dh_normal, // normal mode resolution (10 bit data) + lis3dh_high_res // high resolution mode (12 bit data) + +} lis3dh_resolution_t; + +/** + * @brief Full scale measurement range + */ +typedef enum { + + lis3dh_scale_2g = 0, // default + lis3dh_scale_4g, + lis3dh_scale_8g, + lis3dh_scale_16g + +} lis3dh_scale_t; + + +/** + * @brief FIFO mode + */ +typedef enum { + + lis3dh_bypass = 0, // default + lis3dh_fifo = 1, + lis3dh_stream = 2, + lis3dh_trigger= 3 + +} lis3dh_fifo_mode_t; + + +/** + * @brief Interrupt signals + */ +typedef enum { + + lis3dh_int1_signal = 0, + lis3dh_int2_signal = 1 + +} lis3dh_int_signals_t; + + +/** + * @brief Activity interrupt configuration for INT1/INT2 signals + * + * Activity interrupts are: axes movement wake-up, free-fall, 6D/4D detection. + */ +typedef struct { + + enum { // activity type for the interrupt + + lis3dh_wake_up, // AOI = 0, 6D = 0 + lis3dh_free_fall, // AOI = 1, 6D = 0 + + lis3dh_6d_movement, // AOI = 0, 6D = 1, D4D = 0 + lis3dh_6d_position, // AOI = 1, 6D = 1, D4D = 0 + + lis3dh_4d_movement, // AOI = 0, 6D = 1, D4D = 1 + lis3dh_4d_position, // AOI = 1, 6D = 1, D4D = 1 + + } activity; + + uint8_t threshold; // threshold used for comparison for all axes + + bool x_low_enabled; // x lower than threshold interrupt enabled + bool x_high_enabled; // x higher than threshold interrupt enabled + + bool y_low_enabled; // y lower than threshold interrupt enabled + bool y_high_enabled; // y higher than threshold interrupt enabled + + bool z_low_enabled; // z lower than threshold interrupt enabled + bool z_high_enabled; // z higher than threshold interrupt enabled + + bool latch; // latch the interrupt when true until the + // interrupt source has been read + + uint8_t duration; // duration in 1/ODR an interrupt condition has + // to be given before the interrupt is generated +} lis3dh_int_activity_config_t; + + +/** + * @brief Activity interrupt source type for interrupt signals INT1/INT2 + */ +typedef struct { + + bool active:1; // true - one ore more activities occured + + bool x_low :1; // true - x low activity occured + bool x_high:1; // true - x high activity occured + + bool y_low :1; // true - z low activity occured + bool y_high:1; // true - z high activity occured + + bool z_low :1; // true - z low activity occured + bool z_high:1; // true - z high activity occured + +} lis3dh_int_activity_source_t; + + +/** + * @brief Data interrupt types for INT1 signal + */ +typedef enum { + + lis3dh_data_ready, // interrupt when data are ready to read + lis3dh_fifo_watermark, // interrupt when FIFO exceeds the FIFO threashold + lis3dh_fifo_overrun // interrupt when FIFO is completely filled + +} lis3dh_int_data_t; + + +/** + * @brief Data interrupt source type for INT1 signal + */ +typedef struct { + + bool data_ready; // true when data are ready to read + bool fifo_watermark; // true when FIFO exceeds the FIFO threashold + bool fifo_overrun; // true when FIFO is completely filled + +} lis3dh_int_data_source_t; + + +/** + * @brief Click interrupt configuration for interrupt signals INT1/INT2 + */ +typedef struct { + + bool x_single; // x-axis single tap interrupt enabled + bool x_double; // x-axis double tap interrupt enabled + + bool y_single; // y-axis single tap interrupt enabled + bool y_double; // y-axis double tap interrupt enabled + + bool z_single; // z-axis single tap interrupt enabled + bool z_double; // z-axis double tap interrupt enabled + + uint8_t threshold; // threshold used for comparison for all axes + + bool latch; // latch the interrupt when true until the + // interrupt source has been read + + uint8_t time_limit; // maximum time interval between the start and the + // end of a cick (accel increases and falls back) + uint8_t time_latency; // click detection is disabled for that time after + // a was click detected (in 1/ODR) + uint8_t time_window; // time interval in which the second click has to + // to be detected in double clicks (in 1/ODR) + +} lis3dh_int_click_config_t; + + +/** + * @brief Click interrupt configuration for interrupt signals INT1/INT2 + */ +typedef struct { + + bool x_click:1; // click detected in x direction + bool y_click:1; // click detected in y direction + bool z_click:1; // click detected in z direction + + bool sign :1; // click sign (0 - posisitive, 1 - negative) + + bool s_click:1; // single click detected + bool d_click:1; // double click detected + + bool active :1; // true - one ore more Activities occured + +} lis3dh_int_click_source_t; + + +/** + * @brief INT1, INT2 signal activity level + */ +typedef enum { + + lis3dh_high_active = 0, + lis3dh_low_active + +} lis3dh_int_signal_level_t; + + +/** + * @brief Raw data set as two complements + */ +typedef struct { + + int16_t ax; // acceleration on x axis + int16_t ay; // acceleration on y axis + int16_t az; // acceleration on z axis + +} lis3dh_raw_data_t; + + +/** + * @brief Raw data FIFO type + */ +typedef lis3dh_raw_data_t lis3dh_raw_data_fifo_t[32]; + + +/** + * @brief Floating point output value set in degree + */ +typedef struct { + + float ax; // acceleration on x axis + float ay; // acceleration on y axis + float az; // acceleration on z axis + +} lis3dh_float_data_t; + + +/** + * @brief Floating point output value FIFO type + */ +typedef lis3dh_float_data_t lis3dh_float_data_fifo_t[32]; + + +/** + * @brief HPF (high pass filter) modes + */ +typedef enum { + + lis3dh_hpf_normal = 0, // normal mode (reset by reading reference) + lis3dh_hpf_reference, // reference signal for filtering + lis3dh_hpf_normal_x, // normal mode + lis3dh_hpf_autoreset // autoreset on interrupt Activity + +} lis3dh_hpf_mode_t; + + +/** + * @brief LIS3DH sensor device data structure type + */ +typedef struct { + + int error_code; // error code of last operation + + uint8_t bus; // I2C = x, SPI = 1 + uint8_t addr; // I2C = slave address, SPI = 0 + + uint8_t cs; // ESP8266, ESP32: GPIO used as SPI CS + // __linux__: device index + + lis3dh_scale_t scale; // fill range scale (default 245 dps) + lis3dh_resolution_t res; // resolution used + lis3dh_fifo_mode_t fifo_mode; // FIFO operation mode (default bypass) + bool fifo_first; // first FIFO access + +} lis3dh_sensor_t; + + +#ifdef __cplusplus +} +#endif /* End of CPP guard */ + +#endif /* __LIS3DH_TYPES_H__ */ From 2ae145dcc02136027b5abdb9bcd0a988cb712890 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Sat, 23 Dec 2017 21:26:12 +0100 Subject: [PATCH 02/10] minor fix --- examples/lis3dh/lis3dh_example.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/lis3dh/lis3dh_example.c b/examples/lis3dh/lis3dh_example.c index b18eaab..ab68ee3 100644 --- a/examples/lis3dh/lis3dh_example.c +++ b/examples/lis3dh/lis3dh_example.c @@ -13,7 +13,6 @@ * | GPIO 14 (SCL) ----> SCL | * | GPIO 13 (SDA) <---> SDA | * | GPIO 5 <---- INT1 | - * | GPIO 4 <---- DRDY/INT2| * +-----------------+ +----------+ * * SPI @@ -26,14 +25,13 @@ * | GPIO 12 (MISO)<---- SDO | | GPIO 18 (MISO)<---- SDO | * | GPIO 2 (CS) ----> CS | | GPIO 19 (CS) ----> CS | * | GPIO 5 <---- INT1 | | GPIO 5 <---- INT1 | - * | GPIO 4 <---- DRDY/INT2| | GPIO 4 <---- DRDY/INT2| * +-----------------+ +---------+ +-----------------+ +----------+ */ /* -- use following constants to define the example mode ----------- */ // #define SPI_USED // if defined SPI is used, otherwise I2C - #define DATA_INT // data ready and FIFO status interrupts +// #define DATA_INT // data ready and FIFO status interrupts // #define CLICK_INT // click detection interrupt // #define ACTIVITY_INT // wake-up, free fall or 6D/4D orientation detection // #define FIFO_MODE // multiple sample read mode @@ -142,7 +140,7 @@ static QueueHandle_t gpio_evt_queue = NULL; void user_task_interrupt (void *pvParameters) { - uint32_t gpio_num; + uint8_t gpio_num; while (1) { From 561c7005315465abb39d0f89d400244a4f3a3dc8 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Sat, 30 Dec 2017 17:35:56 +0100 Subject: [PATCH 03/10] minor changes and typos --- examples/lis3dh/lis3dh_example.c | 101 +++++---- extras/lis3dh/README.md | 240 +++++++++++---------- extras/lis3dh/lis3dh.c | 347 +++++++++++++++++-------------- extras/lis3dh/lis3dh.h | 152 +++++++------- extras/lis3dh/lis3dh_types.h | 107 ++++++---- 5 files changed, 519 insertions(+), 428 deletions(-) diff --git a/examples/lis3dh/lis3dh_example.c b/examples/lis3dh/lis3dh_example.c index ab68ee3..079b1b7 100644 --- a/examples/lis3dh/lis3dh_example.c +++ b/examples/lis3dh/lis3dh_example.c @@ -30,13 +30,13 @@ /* -- use following constants to define the example mode ----------- */ -// #define SPI_USED // if defined SPI is used, otherwise I2C -// #define DATA_INT // data ready and FIFO status interrupts -// #define CLICK_INT // click detection interrupt -// #define ACTIVITY_INT // wake-up, free fall or 6D/4D orientation detection +// #define SPI_USED // SPI interface is used, otherwise I2C // #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 (wake-up, free fall or 6D/4D orientation) +// #define INT_CLICK // click detection interrupts used -#if defined(DATA_INT) || defined(ACTIVITY_INT) || defined(CLICK_INT) +#if defined(INT_DATA) || defined(INT_EVENT) || defined(INT_CLICK) #define INT_USED #endif @@ -146,23 +146,40 @@ void user_task_interrupt (void *pvParameters) { if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY)) { - lis3dh_int_activity_source_t activity_src; - lis3dh_int_data_source_t data_src; - lis3dh_int_click_source_t click_src; + lis3dh_int_data_source_t data_src = {}; + lis3dh_int_event_source_t event_src = {}; + lis3dh_int_click_source_t click_src = {}; - // get the source of the interrupt and reset INT signals - lis3dh_get_int_activity_source (sensor, &activity_src, lis3dh_int1_signal); - lis3dh_get_int_data_source (sensor, &data_src); - lis3dh_get_int_click_source (sensor, &click_src); + // get the source of the interrupt and reset *INTx* signals + #ifdef INT_DATA + lis3dh_get_int_data_source (sensor, &data_src); + #elif INT_EVENT + lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event1_gen); + #elif INT_CLICK + lis3dh_get_int_click_source (sensor, &click_src); + #endif - // in case of DRDY interrupt or activity interrupt read one data sample - if (data_src.data_ready || activity_src.active) + // in case of DRDY interrupt or inertial event interrupt read one data sample + if (data_src.data_ready) read_data (); // in case of FIFO interrupts read the whole FIFO else if (data_src.fifo_watermark || data_src.fifo_overrun) read_data (); + // in case of event interrupt + else if (event_src.active) + { + printf("%.3f LIS3DH ", (double)sdk_system_get_time()*1e-3); + if (event_src.x_low) printf("x is lower than threshold\n"); + if (event_src.y_low) printf("y is lower than threshold\n"); + if (event_src.z_low) printf("z is lower than threshold\n"); + if (event_src.x_high) printf("x is higher than threshold\n"); + if (event_src.y_high) printf("y is higher than threshold\n"); + if (event_src.z_high) printf("z is higher than threshold\n"); + } + + // in case of click detection interrupt else if (click_src.active) printf("%.3f LIS3DH %s\n", (double)sdk_system_get_time()*1e-3, click_src.s_click ? "single click" : "double click"); @@ -260,41 +277,42 @@ void user_init(void) // set polarity of INT signals if necessary // lis3dh_config_int_signals (sensor, lis3dh_high_active); - #ifdef DATA_INT + #ifdef INT_DATA // enable data interrupts on INT1 (data ready or FIFO status interrupts) // data ready and FIFO status interrupts must not be enabled at the same time #ifdef FIFO_MODE - lis3dh_enable_int_data (sensor, lis3dh_fifo_overrun, true); - lis3dh_enable_int_data (sensor, lis3dh_fifo_watermark, true); + lis3dh_enable_int (sensor, lis3dh_int_fifo_overrun , lis3dh_int1_signal, true); + lis3dh_enable_int (sensor, lis3dh_int_fifo_watermark, lis3dh_int1_signal, true); #else - lis3dh_enable_int_data (sensor, lis3dh_data_ready, true); + lis3dh_enable_int (sensor, lis3dh_int_data_ready, lis3dh_int1_signal, true); #endif // FIFO_MODE - #endif // DATA_INT + #endif // INT_DATA - #ifdef ACTIVITY_INT + #ifdef INT_EVENT // enable data interrupts on INT1 - lis3dh_int_activity_config_t act_config; + lis3dh_int_event_config_t event_config; - act_config.activity = lis3dh_wake_up; - // act_config.activity = lis3dh_free_fall; - // act_config.activity = lis3dh_6d_movement; - // act_config.activity = lis3dh_6d_position; - // act_config.activity = lis3dh_4d_movement; - // act_config.activity = lis3dh_4d_position; - act_config.threshold = 10; - act_config.x_low_enabled = false; - act_config.x_high_enabled = true; - act_config.y_low_enabled = false; - act_config.y_high_enabled = true; - act_config.z_low_enabled = false; - act_config.z_high_enabled = true; - act_config.duration = 0; - act_config.latch = true; + event_config.mode = lis3dh_wake_up; + // event_config.mode = lis3dh_free_fall; + // event_config.mode = lis3dh_6d_movement; + // event_config.mode = lis3dh_6d_position; + // event_config.mode = lis3dh_4d_movement; + // event_config.mode = lis3dh_4d_position; + event_config.threshold = 10; + event_config.x_low_enabled = false; + event_config.x_high_enabled = true; + event_config.y_low_enabled = false; + event_config.y_high_enabled = true; + event_config.z_low_enabled = false; + event_config.z_high_enabled = true; + event_config.duration = 0; + event_config.latch = true; - lis3dh_set_int_activity_config (sensor, lis3dh_int1_signal, &act_config); - #endif // ACTIVITY_INT + lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event1_gen); + lis3dh_enable_int (sensor, lis3dh_int_event1, lis3dh_int1_signal, true); + #endif // INT_EVENT - #ifdef CLICK_INT + #ifdef INT_CLICK // enable click interrupt on INT1 lis3dh_int_click_config_t click_config; @@ -310,8 +328,9 @@ void user_init(void) click_config.time_latency = 1; click_config.time_window = 3; - lis3dh_set_int_click_config (sensor, lis3dh_int1_signal, &click_config); - #endif // CLICK_INT + lis3dh_set_int_click_config (sensor, &click_config); + lis3dh_enable_int (sensor, lis3dh_int_click, lis3dh_int1_signal, true); + #endif // INT_CLICK #ifdef FIFO_MODE // clear FIFO and activate FIFO mode if needed diff --git a/extras/lis3dh/README.md b/extras/lis3dh/README.md index d689b42..a6af314 100644 --- a/extras/lis3dh/README.md +++ b/extras/lis3dh/README.md @@ -153,7 +153,7 @@ The high pass filter can independently apply to - the raw output data, - the data used for click detection, and -- the data used for interrupt generation like wake-up, free fall or 6D/4D orientation detection. +- the data used for inertial interrupt generation like wake-up, free fall or 6D/4D orientation detection. The mode and the cutoff frequency of the high pass filter can be configured using function ```lis3dh_config_hpf```. Following HPF modes are available: @@ -241,26 +241,26 @@ void user_task_periodic (void *pvParameters) The LIS3DH supports two dedicated interrupt signals **```INT1```** and **```INT2```** and three different types of interrupts: -- data ready and FIFO status interrupts, -- activity detection interrupts like wake-up, free fall, and 6D/4D orientation detection, and +- data interrupts (data ready and FIFO status), +- inertial event interrupts (axis movement, wake-up, free fall, and 6D/4D orientation detection), and - click detection interrupts. -While activity detection and click detection interrupts can be configured for both interrupt signals, data ready and FIFO status interrupts can be configured only for interrupt signal ```INT1```. +While inertial event interrupts and click detection interrupts can be configured for both interrupt signals, data ready and FIFO status interrupts can be configured only for interrupt signal ```INT1```. -#### Data ready and FIFO status interrupts +#### Data interrupts (data ready and FIFO status) Following sources can generate an interrupt on signal ```INT1```: Interrupt source | Driver symbol :-----------------|:------------- -Output data become ready to read | lis3dh_data_ready -FIFO content exceeds the watermark level | lis3dh_fifo_watermark -FIFO is completely filled | lis3dh_fifo_overrun +Output data become ready to read | lis3dh_int_data_ready +FIFO content exceeds the watermark level | lis3dh_int_fifo_watermark +FIFO is completely filled | lis3dh_int_fifo_overrun -Each of these interrupt sources can be enabled or disabled separately with function ```lis3dh_enable_int_data```. By default all interrupt sources are disabled. +Each of these interrupt sources can be enabled or disabled separately with function ```lis3dh_enable_int```. By default all interrupt sources are disabled. ``` -lis3dh_enable_int_data (sensor, lis3dh_data_ready, true); +lis3dh_enable_int (sensor, lis3dh_int_data_ready, lis3dh_int1_signal, true); ``` Whenever an interrupt is generated at interrupt signal ```INT1```, the function ```lis3dh_get_int_data_source``` can be used to determine the source of the interrupt. This function returns a data structure of type ```lis3dh_int_data_source_t``` that contain a boolean member for each source that can be tested for true. @@ -270,114 +270,118 @@ void int1_handler () { lis3dh_int_data_source_t data_src; - // get interrupt source of INT1 + // get the interrupt source of INT1 lis3dh_get_int_data_source (sensor, &data_src); - // if data ready interrupt, get the results and do something with them + // in case of data ready interrupt, get the results and do something with them if (data_src.data_ready) // ... read data // in case of FIFO interrupts read the whole FIFO else if (data_src.fifo_watermark || data_src.fifo_overrun) - // read FIFO data + // ... read FIFO data ... } ``` -#### Activity detection interrupts +#### Inertial event interrupts -Activity detection allows to generate interrupts whenever a configured condition occur. If activated, the acceleration of each axis is compared with a defined threshold to check whether it is below or above the threshold. The results of all activated comparisons are then combined OR or AND to generate the interrupt signal. +Inertial interrupt generators allow to generate interrupts when certain inertial event occures (event interrupts), that is, the acceleration of defined axes is higher or lower than a defined threshold. If activated, the acceleration of each axis is compared with a defined threshold to check whether it is below or above the threshold. The results of all activated comparisons are then combined OR or AND to generate the interrupt signal. The configuration of the threshold valid for all axes, the activated comparisons and the selected AND/OR combination allows to recognize special situations: - **Wake-up detection** refers the special condition that the acceleration measured along any axis is above the defined threshold (```lis3dh_wake_up```). - **Free fall detection** refers the special condition that the acceleration measured along all the axes goes to zero (```lis3dh_free_fall```). -- **6D/4D Orientation Detection** refers to the special condition that the measured acceleration along certain axes is above and along the other axes is below the threshold which indicates a particular orientation (```lis3dh_6d_movement```, ```lis3dh_6d_position```, ```lis3dh_4d_movement```, ```lis3dh_4d_position```). +- **6D/4D orientation detection** refers to the special condition that the measured acceleration along certain axes is above and along the other axes is below the threshold which indicates a particular orientation (```lis3dh_6d_movement```, ```lis3dh_6d_position```, ```lis3dh_4d_movement```, ```lis3dh_4d_position```). -Activity detection interrupts can be configured with the function ```lis3dh_get_int_activity_config```. This function requires as parameters the configuration of type ```lis3dh_int_activity_config_t``` and the interrupt signal to be used for activity detection interrupts. +Inertial event interrupts can be configured with the function ```lis3dh_get_int_event_config```. This function requires as parameters the configuration of type ```lis3dh_int_event_config_t``` and the interrupt generator to be used for inertial event interrupts. + +Inertial event interrupts have to be enabled or disabled using function ```lis3dh_enable_int```. The interrupt signal on which the interrupts are generated is given as parameter. For example, wake-up detection interrupt on signal ```INT1``` could be configured as following: ``` -lis3dh_int_activity_config_t act_config; +lis3dh_int_event_config_t event_config; -act_config.activity = lis3dh_wake_up; -act_config.threshold = 10; -act_config.x_low_enabled = false; -act_config.x_high_enabled = true; -act_config.y_low_enabled = false; -act_config.y_high_enabled = true; -act_config.z_low_enabled = false; -act_config.z_high_enabled = true; +event_config.mode = lis3dh_wake_up; +event_config.threshold = 10; +event_config.x_low_enabled = false; +event_config.x_high_enabled = true; +event_config.y_low_enabled = false; +event_config.y_high_enabled = true; +event_config.z_low_enabled = false; +event_config.z_high_enabled = true; -act_config.duration = 0; -act_config.latch = true; +event_config.duration = 0; +event_config.latch = true; -lis3dh_set_int_activity_config (sensor, lis3dh_int1_signal, &act_config); - ``` +lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event1_gen); +lis3dh_enable_int (sensor, lis3dh_int_event1, lis3dh_int1_signal, true); +``` -The parameter of type ```lis3dh_int_activity_config_t``` also configures +The parameter of type ```lis3dh_int_event_config_t``` also configures -- whether the interrupt signal should latched until the interrupt source is read, and -- which time in 1/ODR an interrupt condition has to be given before the interrupt is generated. +- whether the interrupt should be latched until the interrupt source is read, and +- which time given in 1/ODR an interrupt condition has to be given before the interrupt is generated. -As with data ready and FIFO status interrupts, function ```lis3dh_get_int_activity_source``` can be used to determine the source of an activity interrupt whenever it is generated. This function returns a data structure of type ```lis3dh_int_activity_source_t``` which contains a boolean member for each source that can be tested for true. +As with data ready and FIFO status interrupts, function ```lis3dh_get_int_event_source``` can be used to determine the source of an inertial event interrupt whenever it is generated. This function returns a data structure of type ```lis3dh_int_event_source_t``` which contains a boolean member for each source that can be tested for true. ``` void int1_handler () { - lis3dh_int_data_source_t data_src; - lis3dh_int_activity_source_t activity_src; + lis3dh_int_data_source_t data_src; + lis3dh_int_event_source_t event_src; - // get interrupt source of INT1 - lis3dh_get_int_data_source (sensor, &data_src); - lis3dh_get_int_activity_source (sensor, &activity_src, lis3dh_int1_signal); + // get the interrupt source of INT1 + lis3dh_get_int_data_source (sensor, &data_src); + lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event1_gen); - // if data ready interrupt, get the results and do something with them + // in case of data ready interrupt, get the results and do something with them if (data_src.data_ready) // ... read data // in case of FIFO interrupts read the whole FIFO else if (data_src.fifo_watermark || data_src.fifo_overrun) - // read FIFO data + // ... read FIFO data - // in case of activity interrupt - else if (activity_src.active) + // in case of inertial event interrupt + else if (event_src.active) // ... read data ... } ``` -**Please note** Activating all threshold comparisons and the OR combination (```lis3dh_wake_up```) is the most flexible way to deal with activity interrupts. Functions such as free fall detection and so on can then be realized by suitably combining the various interrupt sources by the user task. Following example realizes the free fall detection in user task. +**Please note** Activating all threshold comparisons and the OR combination (```lis3dh_wake_up```) is the most flexible way to deal with inertial event interrupts. Functions such as free fall detection and so on can then be realized by suitably combining the various interrupt sources by the user task. Following example realizes the free fall detection in user task. ``` -lis3dh_int_activity_config_t act_config; +lis3dh_int_event_config_t event_config; -act_config.activity = lis3dh_wake_up; -act_config.threshold = 10; -act_config.x_low_enabled = true; -act_config.x_high_enabled = true; -act_config.y_low_enabled = true; -act_config.y_high_enabled = true; -act_config.z_low_enabled = true; -act_config.z_high_enabled = true; +event_config.mode = lis3dh_wake_up; +event_config.threshold = 10; +event_config.x_low_enabled = true; +event_config.x_high_enabled = true; +event_config.y_low_enabled = true; +event_config.y_high_enabled = true; +event_config.z_low_enabled = true; +event_config.z_high_enabled = true; -act_config.duration = 0; -act_config.latch = true; +event_config.duration = 0; +event_config.latch = true; -lis3dh_set_int_activity_config (sensor, lis3dh_int1_signal, &act_config); +lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event1_gen); +lis3dh_enable_int (sensor, lis3dh_int_event1, lis3dh_int1_signal, true); ``` ``` void int1_handler () { - lis3dh_int_activity_source_t activity_src; + lis3dh_int_event_source_t event_src; - // get interrupt source of INT1 - lis3dh_get_int_activity_source (sensor, &activity_src, lis3dh_int1_signal); + // get the interrupt source of INT1 + lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int1_signal); // detect free fall (all accelerations are below the threshold) - if (activity_src.x_low && activity_src.y_low && activity_src.z_low) + if (event_src.x_low && event_src.y_low && event_src.z_low) ... ... } @@ -390,6 +394,8 @@ A sequence of acceleration values over time measured along certain axes can be u Click detection interrupts are configured with function ```lis3dh_set_int_click_config```. This function requires as parameters the configuration of type ```lis3dh_int_click_config_t``` and the interrupt signal to be used for click detection interrupts. +Inertial event interrupts have to be enabled or disabled using function ```lis3dh_enable_int```. The interrupt signal on which the interrupts are generated is given as parameter. + In following example, the single click detection for z-axis is enabled with a time limit of 1/ODR, a time latency of 1/ODR and a time window of 3/ODR. ``` @@ -407,7 +413,8 @@ click_config.time_limit = 1; click_config.time_latency = 1; click_config.time_window = 3; -lis3dh_set_int_click_config (sensor, lis3dh_int1_signal, &click_config); +lis3dh_set_int_click_config (sensor, &click_config); +lis3dh_enable_int (sensor, lis3dh_int_click, lis3dh_int1_signal, true); ``` As with other interrupts, the function ```lis3dh_get_int_click_source``` can be used to determine the source of the interrupt signal whenever it is generated. This function returns a data structure of type ```lis3dh_int_click_source_t``` that contains a boolean member for each source that can be tested for true. @@ -417,7 +424,7 @@ void int1_handler () { lis3dh_int_click_source_t click_src; - // get interrupt source of INT1 + // get the interrupt source of INT1 lis3dh_get_int_click_source (sensor, &click_src); // detect single click along z-axis @@ -690,15 +697,13 @@ lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); ## Full Example ``` -/* -- use following constants to define the example mode ----------- */ - -// #define SPI_USED // if defined SPI is used, otherwise I2C - #define DATA_INT // data ready and FIFO status interrupts -// #define CLICK_INT // click detection interrupt -// #define ACTIVITY_INT // wake-up, free fall or 6D/4D orientation detection +// #define SPI_USED // SPI interface is used, otherwise I2C // #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 (wake-up, free fall or 6D/4D orientation) +// #define INT_CLICK // click detection interrupts used -#if defined(DATA_INT) || defined(ACTIVITY_INT) || defined(CLICK_INT) +#if defined(INT_DATA) || defined(INT_EVENT) || defined(INT_CLICK) #define INT_USED #endif @@ -744,7 +749,7 @@ lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); #define INT1_PIN 5 #define INT2_PIN 4 -/* -- user tasks ---------------------------------------------- */ +/* -- user tasks --------------------------------------------------- */ static lis3dh_sensor_t* sensor; @@ -802,29 +807,46 @@ static QueueHandle_t gpio_evt_queue = NULL; void user_task_interrupt (void *pvParameters) { - uint32_t gpio_num; + uint8_t gpio_num; while (1) { if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY)) { - lis3dh_int_activity_source_t activity_src; - lis3dh_int_data_source_t data_src; - lis3dh_int_click_source_t click_src; + lis3dh_int_data_source_t data_src = {}; + lis3dh_int_event_source_t event_src = {}; + lis3dh_int_click_source_t click_src = {}; - // get the source of the interrupt and reset INT signals - lis3dh_get_int_activity_source (sensor, &activity_src, lis3dh_int1_signal); - lis3dh_get_int_data_source (sensor, &data_src); - lis3dh_get_int_click_source (sensor, &click_src); + // get the source of the interrupt and reset *INTx* signals + #ifdef INT_DATA + lis3dh_get_int_data_source (sensor, &data_src); + #elif INT_EVENT + lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event1_gen); + #elif INT_CLICK + lis3dh_get_int_click_source (sensor, &click_src); + #endif - // in case of DRDY interrupt or activity interrupt read one data sample - if (data_src.data_ready || activity_src.active) + // in case of DRDY interrupt or inertial event interrupt read one data sample + if (data_src.data_ready) read_data (); // in case of FIFO interrupts read the whole FIFO else if (data_src.fifo_watermark || data_src.fifo_overrun) read_data (); + // in case of event interrupt + else if (event_src.active) + { + printf("%.3f LIS3DH ", (double)sdk_system_get_time()*1e-3); + if (event_src.x_low) printf("x is lower than threshold\n"); + if (event_src.y_low) printf("y is lower than threshold\n"); + if (event_src.z_low) printf("z is lower than threshold\n"); + if (event_src.x_high) printf("x is higher than threshold\n"); + if (event_src.y_high) printf("y is higher than threshold\n"); + if (event_src.z_high) printf("z is higher than threshold\n"); + } + + // in case of click detection interrupt else if (click_src.active) printf("%.3f LIS3DH %s\n", (double)sdk_system_get_time()*1e-3, click_src.s_click ? "single click" : "double click"); @@ -862,7 +884,7 @@ void user_task_periodic(void *pvParameters) #endif // INT_USED -/* -- main program ---------------------------------------------- */ +/* -- main program ------------------------------------------------- */ void user_init(void) { @@ -922,41 +944,42 @@ void user_init(void) // set polarity of INT signals if necessary // lis3dh_config_int_signals (sensor, lis3dh_high_active); - #ifdef DATA_INT + #ifdef INT_DATA // enable data interrupts on INT1 (data ready or FIFO status interrupts) // data ready and FIFO status interrupts must not be enabled at the same time #ifdef FIFO_MODE - lis3dh_enable_int_data (sensor, lis3dh_fifo_overrun, true); - lis3dh_enable_int_data (sensor, lis3dh_fifo_watermark, true); + lis3dh_enable_int (sensor, lis3dh_int_fifo_overrun , lis3dh_int1_signal, true); + lis3dh_enable_int (sensor, lis3dh_int_fifo_watermark, lis3dh_int1_signal, true); #else - lis3dh_enable_int_data (sensor, lis3dh_data_ready, true); + lis3dh_enable_int (sensor, lis3dh_int_data_ready, lis3dh_int1_signal, true); #endif // FIFO_MODE - #endif // DATA_INT + #endif // INT_DATA - #ifdef ACTIVITY_INT + #ifdef INT_EVENT // enable data interrupts on INT1 - lis3dh_int_activity_config_t act_config; + lis3dh_int_event_config_t event_config; - act_config.activity = lis3dh_wake_up; - // act_config.activity = lis3dh_free_fall; - // act_config.activity = lis3dh_6d_movement; - // act_config.activity = lis3dh_6d_position; - // act_config.activity = lis3dh_4d_movement; - // act_config.activity = lis3dh_4d_position; - act_config.threshold = 10; - act_config.x_low_enabled = false; - act_config.x_high_enabled = true; - act_config.y_low_enabled = false; - act_config.y_high_enabled = true; - act_config.z_low_enabled = false; - act_config.z_high_enabled = true; - act_config.duration = 0; - act_config.latch = true; + event_config.mode = lis3dh_wake_up; + // event_config.mode = lis3dh_free_fall; + // event_config.mode = lis3dh_6d_movement; + // event_config.mode = lis3dh_6d_position; + // event_config.mode = lis3dh_4d_movement; + // event_config.mode = lis3dh_4d_position; + event_config.threshold = 10; + event_config.x_low_enabled = false; + event_config.x_high_enabled = true; + event_config.y_low_enabled = false; + event_config.y_high_enabled = true; + event_config.z_low_enabled = false; + event_config.z_high_enabled = true; + event_config.duration = 0; + event_config.latch = true; - lis3dh_set_int_activity_config (sensor, lis3dh_int1_signal, &act_config); - #endif // ACTIVITY_INT + lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event1_gen); + lis3dh_enable_int (sensor, lis3dh_int_event1, lis3dh_int1_signal, true); + #endif // INT_EVENT - #ifdef CLICK_INT + #ifdef INT_CLICK // enable click interrupt on INT1 lis3dh_int_click_config_t click_config; @@ -972,8 +995,9 @@ void user_init(void) click_config.time_latency = 1; click_config.time_window = 3; - lis3dh_set_int_click_config (sensor, lis3dh_int1_signal, &click_config); - #endif // CLICK_INT + lis3dh_set_int_click_config (sensor, &click_config); + lis3dh_enable_int (sensor, lis3dh_int_click, lis3dh_int1_signal, true); + #endif // INT_CLICK #ifdef FIFO_MODE // clear FIFO and activate FIFO mode if needed diff --git a/extras/lis3dh/lis3dh.c b/extras/lis3dh/lis3dh.c index 4dcdae7..7742024 100644 --- a/extras/lis3dh/lis3dh.c +++ b/extras/lis3dh/lis3dh.c @@ -258,10 +258,10 @@ static bool lis3dh_spi_write (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *d #define lis3dh_update_reg(dev,addr,type,elem,value) \ { \ struct type __reg; \ - if (!lis3dh_read_reg (dev, (addr), (uint8_t*)&__reg, 1)) \ + if (!lis3dh_reg_read (dev, (addr), (uint8_t*)&__reg, 1)) \ return false; \ __reg.elem = (value); \ - if (!lis3dh_write_reg (dev, (addr), (uint8_t*)&__reg, 1)) \ + if (!lis3dh_reg_write (dev, (addr), (uint8_t*)&__reg, 1)) \ return false; \ } @@ -325,7 +325,7 @@ bool lis3dh_set_mode (lis3dh_sensor_t* dev, uint8_t old_odr; // read current register values - if (!lis3dh_read_reg (dev, LIS3DH_REG_CTRL1, (uint8_t*)®, 1)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_CTRL1, (uint8_t*)®, 1)) return false; old_odr = reg.ODR; @@ -340,7 +340,7 @@ bool lis3dh_set_mode (lis3dh_sensor_t* dev, lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, HR, (res == lis3dh_high_res)); - if (!lis3dh_write_reg (dev, LIS3DH_REG_CTRL1, (uint8_t*)®, 1)) + if (!lis3dh_reg_write (dev, LIS3DH_REG_CTRL1, (uint8_t*)®, 1)) return false; // if sensor was in power down mode it takes at least 100 ms to start in another mode @@ -366,7 +366,7 @@ bool lis3dh_set_scale (lis3dh_sensor_t* dev, lis3dh_scale_t scale) bool lis3dh_set_fifo_mode (lis3dh_sensor_t* dev, lis3dh_fifo_mode_t mode, - uint8_t thresh, lis3dh_int_signals_t trigger) + uint8_t thresh, lis3dh_int_signal_t trigger) { if (!dev) return false; @@ -383,7 +383,7 @@ bool lis3dh_set_fifo_mode (lis3dh_sensor_t* dev, lis3dh_fifo_mode_t mode, }; // write FIFO_CTRL register - if (!lis3dh_write_reg (dev, LIS3DH_REG_FIFO_CTRL, (uint8_t*)&fifo_ctrl, 1)) + if (!lis3dh_reg_write (dev, LIS3DH_REG_FIFO_CTRL, (uint8_t*)&fifo_ctrl, 1)) return false; return true; @@ -400,7 +400,7 @@ bool lis3dh_new_data (lis3dh_sensor_t* dev) { struct lis3dh_reg_status status; - if (!lis3dh_read_reg (dev, LIS3DH_REG_STATUS, (uint8_t*)&status, 1)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_STATUS, (uint8_t*)&status, 1)) { error_dev ("Could not get sensor status", __FUNCTION__, dev); return false; @@ -411,7 +411,7 @@ bool lis3dh_new_data (lis3dh_sensor_t* dev) { struct lis3dh_reg_fifo_src fifo_src; - if (!lis3dh_read_reg (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) { error_dev ("Could not get fifo source register data", __FUNCTION__, dev); return false; @@ -483,7 +483,7 @@ bool lis3dh_get_raw_data (lis3dh_sensor_t* dev, lis3dh_raw_data_t* raw) } // read raw data sample - if (!lis3dh_read_reg (dev, LIS3DH_REG_OUT_X_L, (uint8_t*)raw, 6)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_OUT_X_L, (uint8_t*)raw, 6)) { error_dev ("Could not get raw data sample", __FUNCTION__, dev); dev->error_code |= LIS3DH_GET_RAW_DATA_FAILED; @@ -507,7 +507,7 @@ uint8_t lis3dh_get_raw_data_fifo (lis3dh_sensor_t* dev, lis3dh_raw_data_fifo_t r struct lis3dh_reg_fifo_src fifo_src; // read FIFO state - if (!lis3dh_read_reg (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) { error_dev ("Could not get fifo source register data", __FUNCTION__, dev); return 0; @@ -521,14 +521,14 @@ uint8_t lis3dh_get_raw_data_fifo (lis3dh_sensor_t* dev, lis3dh_raw_data_fifo_t r // read samples from FIFO for (int i = 0; i < samples; i++) - if (!lis3dh_read_reg (dev, LIS3DH_REG_OUT_X_L, (uint8_t*)&raw[i], 6)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_OUT_X_L, (uint8_t*)&raw[i], 6)) { error_dev ("Could not get raw data samples", __FUNCTION__, dev); dev->error_code |= LIS3DH_GET_RAW_DATA_FIFO_FAILED; return i; } - lis3dh_read_reg (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1); + lis3dh_reg_read (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1); // if FFS is not 0 after all samples read, ODR is higher than fetching rate if (fifo_src.FFS) @@ -550,9 +550,124 @@ uint8_t lis3dh_get_raw_data_fifo (lis3dh_sensor_t* dev, lis3dh_raw_data_fifo_t r } -bool lis3dh_set_int_activity_config (lis3dh_sensor_t* dev, - lis3dh_int_signals_t signal, - lis3dh_int_activity_config_t* config) +bool lis3dh_enable_int (lis3dh_sensor_t* dev, + lis3dh_int_type_t type, + lis3dh_int_signal_t signal, bool value) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_ctrl3 ctrl3; + struct lis3dh_reg_ctrl6 ctrl6; + + uint8_t* reg = NULL; + uint8_t addr; + + // determine the addr of the register to change + if (type == lis3dh_int_data_ready || + type == lis3dh_int_fifo_watermark || + type == lis3dh_int_fifo_overrun) + { + reg = (uint8_t*)&ctrl3; + addr = LIS3DH_REG_CTRL3; + } + else if (signal == lis3dh_int1_signal) + { + reg = (uint8_t*)&ctrl3; + addr = LIS3DH_REG_CTRL3; + } + else + { + reg = (uint8_t*)&ctrl6; + addr = LIS3DH_REG_CTRL6; + } + + // read the register + if (!lis3dh_reg_read (dev, addr, reg, 1)) + { + error_dev ("Could not read interrupt control registers", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_INT_FAILED; + return false; + } + + // change the register + switch (type) + { + case lis3dh_int_data_ready: ctrl3.IT_DRDY1 = value; + break; + + case lis3dh_int_fifo_watermark: ctrl3.I1_WTM1 = value; + break; + + case lis3dh_int_fifo_overrun: ctrl3.I1_OVERRUN = value; + break; + + case lis3dh_int_event1: if (signal == lis3dh_int1_signal) + ctrl3.I1_AOI1 = value; + else + ctrl6.I2_AOI1 = value; + break; + + case lis3dh_int_event2: if (signal == lis3dh_int1_signal) + ctrl3.I1_AOI2 = value; + else + ctrl6.I2_AOI2 = value; + break; + + case lis3dh_int_click: if (signal == lis3dh_int1_signal) + ctrl3.I1_CLICK = value; + else + ctrl6.I2_CLICK = value; + break; + + default: dev->error_code = LIS3DH_WRONG_INT_TYPE; + error_dev ("Wrong interrupt type", __FUNCTION__, dev); + return false; + } + + if (!lis3dh_reg_write (dev, addr, reg, 1)) + { + error_dev ("Could not enable/disable interrupt", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_INT_FAILED; + return false; + } + + return true; +} + + +bool lis3dh_get_int_data_source (lis3dh_sensor_t* dev, + lis3dh_int_data_source_t* source) +{ + if (!dev || !source) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_ctrl3 ctrl3; + struct lis3dh_reg_status status; + struct lis3dh_reg_fifo_src fifo_src; + + if (!lis3dh_reg_read (dev, LIS3DH_REG_CTRL3 , (uint8_t*)&ctrl3 , 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_STATUS , (uint8_t*)&status , 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) + { + error_dev ("Could not read source of interrupt INT2 from sensor", __FUNCTION__, dev); + dev->error_code |= LIS3DH_INT_SOURCE_FAILED; + return false; + } + + source->data_ready = status.ZYXDA & ctrl3.IT_DRDY1; + source->fifo_watermark = fifo_src.WTM & ctrl3.I1_WTM1; + source->fifo_overrun = fifo_src.OVRN_FIFO & ctrl3.I1_OVERRUN; + + return true; +} + + +bool lis3dh_set_int_event_config (lis3dh_sensor_t* dev, + lis3dh_int_event_config_t* config, + lis3dh_int_event_gen_t gen) { if (!dev || !config) return false; @@ -571,7 +686,7 @@ bool lis3dh_set_int_activity_config (lis3dh_sensor_t* dev, bool d4d_int = false; - switch (config->activity) + switch (config->mode) { case lis3dh_wake_up : intx_cfg.AOI = 0; intx_cfg.SIXD = 0; break; case lis3dh_free_fall : intx_cfg.AOI = 1; intx_cfg.SIXD = 0; break; @@ -583,66 +698,58 @@ bool lis3dh_set_int_activity_config (lis3dh_sensor_t* dev, case lis3dh_6d_position : intx_cfg.AOI = 1; intx_cfg.SIXD = 1; break; } - uint8_t intx_cfg_addr = !signal ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; - uint8_t intx_ths_addr = !signal ? LIS3DH_REG_INT1_THS : LIS3DH_REG_INT2_THS; - uint8_t intx_dur_addr = !signal ? LIS3DH_REG_INT1_DUR : LIS3DH_REG_INT2_DUR; + uint8_t intx_cfg_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; + uint8_t intx_ths_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_THS : LIS3DH_REG_INT2_THS; + uint8_t intx_dur_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_DUR : LIS3DH_REG_INT2_DUR; if (// write the thresholds to registers IG_THS_* - !lis3dh_write_reg (dev, intx_ths_addr, &config->threshold, 1) || + !lis3dh_reg_write (dev, intx_ths_addr, &config->threshold, 1) || // write duration configuration to IG_DURATION - !lis3dh_write_reg (dev, intx_dur_addr, &config->duration, 1) || + !lis3dh_reg_write (dev, intx_dur_addr, &config->duration, 1) || // write INT1 configuration to IG_CFG - !lis3dh_write_reg (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1)) + !lis3dh_reg_write (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1)) { error_dev ("Could not configure interrupt INT1", __FUNCTION__, dev); dev->error_code |= LIS3DH_CONFIG_INT_FAILED; return false; } - bool enable = intx_cfg.XLIE || intx_cfg.XHIE || - intx_cfg.YLIE || intx_cfg.YHIE || - intx_cfg.ZLIE || intx_cfg.ZHIE; - - if (!signal) + if (gen == lis3dh_int_event1_gen) { lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, LIR_INT1, config->latch); lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, D4D_INT1, d4d_int); - // enable or disable I1_AOI - lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, lis3dh_reg_ctrl3, I1_AOI1, enable); } else { lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, LIR_INT2, config->latch); lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, D4D_INT2, d4d_int); - // enable or disable I2_AOI - lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, lis3dh_reg_ctrl6, I2_AOI1, enable); } return true; } -bool lis3dh_get_int_activity_config (lis3dh_sensor_t* dev, - lis3dh_int_signals_t signal, - lis3dh_int_activity_config_t* config) +bool lis3dh_get_int_event_config (lis3dh_sensor_t* dev, + lis3dh_int_event_config_t* config, + lis3dh_int_event_gen_t gen) { if (!dev || !config) return false; dev->error_code = LIS3DH_OK; - uint8_t intx_cfg_addr = !signal ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; - uint8_t intx_ths_addr = !signal ? LIS3DH_REG_INT1_THS : LIS3DH_REG_INT2_THS; - uint8_t intx_dur_addr = !signal ? LIS3DH_REG_INT1_DUR : LIS3DH_REG_INT2_DUR; + uint8_t intx_cfg_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; + uint8_t intx_ths_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_THS : LIS3DH_REG_INT2_THS; + uint8_t intx_dur_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_DUR : LIS3DH_REG_INT2_DUR; struct lis3dh_reg_intx_cfg intx_cfg; struct lis3dh_reg_ctrl5 ctrl5; - if (!lis3dh_read_reg (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1) || - !lis3dh_read_reg (dev, intx_ths_addr, (uint8_t*)&config->threshold, 1) || - !lis3dh_read_reg (dev, intx_dur_addr, (uint8_t*)&config->duration, 1) || - !lis3dh_read_reg (dev, LIS3DH_REG_CTRL5, (uint8_t*)&ctrl5, 1)) + if (!lis3dh_reg_read (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1) || + !lis3dh_reg_read (dev, intx_ths_addr, (uint8_t*)&config->threshold, 1) || + !lis3dh_reg_read (dev, intx_dur_addr, (uint8_t*)&config->duration, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_CTRL5, (uint8_t*)&ctrl5, 1)) { error_dev ("Could not read interrupt configuration from sensor", __FUNCTION__, dev); dev->error_code |= LIS3DH_CONFIG_INT_FAILED; @@ -660,7 +767,7 @@ bool lis3dh_get_int_activity_config (lis3dh_sensor_t* dev, bool d4d_int = false; - if (!signal) + if (gen == lis3dh_int_event1_gen) { config->latch = ctrl5.LIR_INT1; d4d_int = ctrl5.D4D_INT1; @@ -674,29 +781,29 @@ bool lis3dh_get_int_activity_config (lis3dh_sensor_t* dev, if (intx_cfg.AOI) { if (intx_cfg.SIXD && d4d_int) - config->activity = lis3dh_4d_position; + config->mode = lis3dh_4d_position; else if (intx_cfg.SIXD && !d4d_int) - config->activity = lis3dh_6d_position; + config->mode = lis3dh_6d_position; else - config->activity = lis3dh_free_fall; + config->mode = lis3dh_free_fall; } else { if (intx_cfg.SIXD && d4d_int) - config->activity = lis3dh_4d_movement; + config->mode = lis3dh_4d_movement; else if (intx_cfg.SIXD && !d4d_int) - config->activity = lis3dh_6d_movement; + config->mode = lis3dh_6d_movement; else - config->activity = lis3dh_wake_up; + config->mode = lis3dh_wake_up; } return true; } -bool lis3dh_get_int_activity_source (lis3dh_sensor_t* dev, - lis3dh_int_activity_source_t* source, - lis3dh_int_signals_t signal) +bool lis3dh_get_int_event_source (lis3dh_sensor_t* dev, + lis3dh_int_event_source_t* source, + lis3dh_int_event_gen_t gen) { if (!dev || !source) return false; @@ -705,11 +812,11 @@ bool lis3dh_get_int_activity_source (lis3dh_sensor_t* dev, struct lis3dh_reg_intx_cfg intx_cfg; struct lis3dh_reg_intx_src intx_src; - uint8_t intx_cfg_addr = (!signal) ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; - uint8_t intx_src_addr = (!signal) ? LIS3DH_REG_INT1_SRC : LIS3DH_REG_INT2_SRC; + uint8_t intx_cfg_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; + uint8_t intx_src_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_SRC : LIS3DH_REG_INT2_SRC; - if (!lis3dh_read_reg (dev, intx_src_addr, (uint8_t*)&intx_src, 1) || - !lis3dh_read_reg (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1)) + if (!lis3dh_reg_read (dev, intx_src_addr, (uint8_t*)&intx_src, 1) || + !lis3dh_reg_read (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1)) { error_dev ("Could not read source of interrupt INT1/INT2 from sensor", __FUNCTION__, dev); dev->error_code |= LIS3DH_INT_SOURCE_FAILED; @@ -728,66 +835,7 @@ bool lis3dh_get_int_activity_source (lis3dh_sensor_t* dev, } -bool lis3dh_enable_int_data (lis3dh_sensor_t* dev, - lis3dh_int_data_t type, bool value) -{ - if (!dev) return false; - - dev->error_code = LIS3DH_OK; - - switch (type) - { - case lis3dh_data_ready: lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, - lis3dh_reg_ctrl3, - IT_DRDY1, value); - break; - case lis3dh_fifo_watermark: lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, - lis3dh_reg_ctrl3, - I1_WTM1, value); - break; - case lis3dh_fifo_overrun: lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, - lis3dh_reg_ctrl3, - I1_OVERRUN, value); - break; - default: dev->error_code = LIS3DH_WRONG_INT_TYPE; - error_dev ("Wrong interrupt type", __FUNCTION__, dev); - return false; - } - - return true; -} - - -bool lis3dh_get_int_data_source (lis3dh_sensor_t* dev, - lis3dh_int_data_source_t* source) -{ - if (!dev || !source) return false; - - dev->error_code = LIS3DH_OK; - - struct lis3dh_reg_ctrl3 ctrl3; - struct lis3dh_reg_status status; - struct lis3dh_reg_fifo_src fifo_src; - - if (!lis3dh_read_reg (dev, LIS3DH_REG_CTRL3 , (uint8_t*)&ctrl3 , 1) || - !lis3dh_read_reg (dev, LIS3DH_REG_STATUS , (uint8_t*)&status , 1) || - !lis3dh_read_reg (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) - { - error_dev ("Could not read source of interrupt INT2 from sensor", __FUNCTION__, dev); - dev->error_code |= LIS3DH_INT_SOURCE_FAILED; - return false; - } - - source->data_ready = status.ZYXDA & ctrl3.IT_DRDY1; - source->fifo_watermark = fifo_src.WTM & ctrl3.I1_WTM1; - source->fifo_overrun = fifo_src.OVRN_FIFO & ctrl3.I1_OVERRUN; - - return true; -} - - bool lis3dh_set_int_click_config (lis3dh_sensor_t* dev, - lis3dh_int_signals_t signal, lis3dh_int_click_config_t* config) { if (!dev || !config) return false; @@ -807,34 +855,21 @@ bool lis3dh_set_int_click_config (lis3dh_sensor_t* dev, uint8_t click_ths = config->threshold | ((config->latch) ? 0x80 : 0x00); - if (!lis3dh_write_reg (dev, LIS3DH_REG_CLICK_CFG , (uint8_t*)&click_cfg, 1) || - !lis3dh_write_reg (dev, LIS3DH_REG_CLICK_THS , (uint8_t*)&click_ths, 1) || - !lis3dh_write_reg (dev, LIS3DH_REG_TIME_LIMIT , (uint8_t*)&config->time_limit, 1) || - !lis3dh_write_reg (dev, LIS3DH_REG_TIME_LATENCY, (uint8_t*)&config->time_latency, 1) || - !lis3dh_write_reg (dev, LIS3DH_REG_TIME_WINDOW , (uint8_t*)&config->time_window, 1)) + if (!lis3dh_reg_write (dev, LIS3DH_REG_CLICK_CFG , (uint8_t*)&click_cfg, 1) || + !lis3dh_reg_write (dev, LIS3DH_REG_CLICK_THS , (uint8_t*)&click_ths, 1) || + !lis3dh_reg_write (dev, LIS3DH_REG_TIME_LIMIT , (uint8_t*)&config->time_limit, 1) || + !lis3dh_reg_write (dev, LIS3DH_REG_TIME_LATENCY, (uint8_t*)&config->time_latency, 1) || + !lis3dh_reg_write (dev, LIS3DH_REG_TIME_WINDOW , (uint8_t*)&config->time_window, 1)) { error_dev ("Could not configure click detection interrupt", __FUNCTION__, dev); dev->error_code |= LIS3DH_CONFIG_CLICK_FAILED; return false; } - bool enable = click_cfg.XS || click_cfg.XD || - click_cfg.YS || click_cfg.YD || - click_cfg.ZS || click_cfg.ZD; - if (!signal) - { - lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, lis3dh_reg_ctrl3, I1_CLICK, enable); - } - else - { - lis3dh_update_reg (dev, LIS3DH_REG_CTRL3, lis3dh_reg_ctrl6, I2_CLICK, enable); - } - return true; } bool lis3dh_get_int_click_config (lis3dh_sensor_t* dev, - lis3dh_int_signals_t signal, lis3dh_int_click_config_t* config) { if (!dev || !config) return false; @@ -844,11 +879,11 @@ bool lis3dh_get_int_click_config (lis3dh_sensor_t* dev, struct lis3dh_reg_click_cfg click_cfg; uint8_t click_ths; - if (!lis3dh_read_reg (dev, LIS3DH_REG_CLICK_CFG , (uint8_t*)&click_cfg, 1) || - !lis3dh_read_reg (dev, LIS3DH_REG_CLICK_THS , (uint8_t*)&click_ths, 1) || - !lis3dh_read_reg (dev, LIS3DH_REG_TIME_LIMIT , (uint8_t*)&config->time_limit, 1) || - !lis3dh_read_reg (dev, LIS3DH_REG_TIME_LATENCY, (uint8_t*)&config->time_latency, 1) || - !lis3dh_read_reg (dev, LIS3DH_REG_TIME_WINDOW , (uint8_t*)&config->time_window, 1)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_CLICK_CFG , (uint8_t*)&click_cfg, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_CLICK_THS , (uint8_t*)&click_ths, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_TIME_LIMIT , (uint8_t*)&config->time_limit, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_TIME_LATENCY, (uint8_t*)&config->time_latency, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_TIME_WINDOW , (uint8_t*)&config->time_window, 1)) { error_dev ("Could not configure click detection interrupt", __FUNCTION__, dev); dev->error_code |= LIS3DH_CONFIG_CLICK_FAILED; @@ -877,7 +912,7 @@ bool lis3dh_get_int_click_source (lis3dh_sensor_t* dev, dev->error_code = LIS3DH_OK; - if (!lis3dh_read_reg (dev, LIS3DH_REG_CLICK_SRC, (uint8_t*)source, 1)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_CLICK_SRC, (uint8_t*)source, 1)) { error_dev ("Could not read source of click interrupt from sensor", __FUNCTION__, dev); dev->error_code |= LIS3DH_CLICK_SOURCE_FAILED; @@ -918,7 +953,7 @@ bool lis3dh_config_hpf (lis3dh_sensor_t* dev, reg.HPIS1 = int1; reg.HPIS2 = int2; - if (!lis3dh_write_reg (dev, LIS3DH_REG_CTRL2, (uint8_t*)®, 1)) + if (!lis3dh_reg_write (dev, LIS3DH_REG_CTRL2, (uint8_t*)®, 1)) { error_dev ("Could not configure high pass filter", __FUNCTION__, dev); dev->error_code |= LIS3DH_CONFIG_HPF_FAILED; @@ -935,7 +970,7 @@ bool lis3dh_set_hpf_ref (lis3dh_sensor_t* dev, int8_t ref) dev->error_code = LIS3DH_OK; - if (!lis3dh_write_reg (dev, LIS3DH_REG_REFERENCE, (uint8_t*)&ref, 1)) + if (!lis3dh_reg_write (dev, LIS3DH_REG_REFERENCE, (uint8_t*)&ref, 1)) { error_dev ("Could not set high pass filter reference", __FUNCTION__, dev); dev->error_code |= LIS3DH_CONFIG_HPF_FAILED; @@ -954,7 +989,7 @@ int8_t lis3dh_get_hpf_ref (lis3dh_sensor_t* dev) int8_t ref; - if (!lis3dh_read_reg (dev, LIS3DH_REG_REFERENCE, (uint8_t*)&ref, 1)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_REFERENCE, (uint8_t*)&ref, 1)) { error_dev ("Could not get high pass filter reference", __FUNCTION__, dev); dev->error_code |= LIS3DH_CONFIG_HPF_FAILED; @@ -975,7 +1010,7 @@ int8_t lis3dh_enable_adc (lis3dh_sensor_t* dev, bool adc, bool tmp) reg |= (adc) ? 0x80 : 0; reg |= (tmp) ? 0x40 : 0; - return lis3dh_write_reg (dev, LIS3DH_REG_TEMP_CFG, (uint8_t*)®, 1); + return lis3dh_reg_write (dev, LIS3DH_REG_TEMP_CFG, (uint8_t*)®, 1); } @@ -990,9 +1025,9 @@ bool lis3dh_get_adc (lis3dh_sensor_t* dev, uint8_t temp_cfg; struct lis3dh_reg_ctrl1 ctrl1; - if (!lis3dh_read_reg (dev, LIS3DH_REG_OUT_ADC1_L, data, 6) || - !lis3dh_read_reg (dev, LIS3DH_REG_CTRL1, (uint8_t*)&ctrl1, 1) || - !lis3dh_read_reg (dev, LIS3DH_REG_TEMP_CFG, &temp_cfg, 1)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_OUT_ADC1_L, data, 6) || + !lis3dh_reg_read (dev, LIS3DH_REG_CTRL1, (uint8_t*)&ctrl1, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_TEMP_CFG, &temp_cfg, 1)) { error_dev ("Could not get adc data", __FUNCTION__, dev); dev->error_code |= LIS3DH_GET_ADC_DATA_FAILED; @@ -1025,7 +1060,7 @@ static bool lis3dh_is_available (lis3dh_sensor_t* dev) dev->error_code = LIS3DH_OK; - if (!lis3dh_read_reg (dev, LIS3DH_REG_WHO_AM_I, &chip_id, 1)) + if (!lis3dh_reg_read (dev, LIS3DH_REG_WHO_AM_I, &chip_id, 1)) return false; if (chip_id != LIS3DH_CHIP_ID) @@ -1048,20 +1083,20 @@ static bool lis3dh_reset (lis3dh_sensor_t* dev) uint8_t reg[8] = { 0 }; // initialize sensor completely including setting in power down mode - lis3dh_write_reg (dev, LIS3DH_REG_TEMP_CFG , reg, 8); - lis3dh_write_reg (dev, LIS3DH_REG_FIFO_CTRL, reg, 1); - lis3dh_write_reg (dev, LIS3DH_REG_INT1_CFG , reg, 1); - lis3dh_write_reg (dev, LIS3DH_REG_INT1_THS , reg, 2); - lis3dh_write_reg (dev, LIS3DH_REG_INT2_CFG , reg, 1); - lis3dh_write_reg (dev, LIS3DH_REG_INT2_THS , reg, 2); - lis3dh_write_reg (dev, LIS3DH_REG_CLICK_CFG, reg, 1); - lis3dh_write_reg (dev, LIS3DH_REG_CLICK_THS, reg, 4); + lis3dh_reg_write (dev, LIS3DH_REG_TEMP_CFG , reg, 8); + lis3dh_reg_write (dev, LIS3DH_REG_FIFO_CTRL, reg, 1); + lis3dh_reg_write (dev, LIS3DH_REG_INT1_CFG , reg, 1); + lis3dh_reg_write (dev, LIS3DH_REG_INT1_THS , reg, 2); + lis3dh_reg_write (dev, LIS3DH_REG_INT2_CFG , reg, 1); + lis3dh_reg_write (dev, LIS3DH_REG_INT2_THS , reg, 2); + lis3dh_reg_write (dev, LIS3DH_REG_CLICK_CFG, reg, 1); + lis3dh_reg_write (dev, LIS3DH_REG_CLICK_THS, reg, 4); return true; } -bool lis3dh_read_reg(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +bool lis3dh_reg_read(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) { if (!dev || !data) return false; @@ -1070,7 +1105,7 @@ bool lis3dh_read_reg(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t } -bool lis3dh_write_reg(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +bool lis3dh_reg_write(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) { if (!dev || !data) return false; diff --git a/extras/lis3dh/lis3dh.h b/extras/lis3dh/lis3dh.h index 29eae78..c1fd52e 100644 --- a/extras/lis3dh/lis3dh.h +++ b/extras/lis3dh/lis3dh.h @@ -154,7 +154,7 @@ bool lis3dh_set_scale (lis3dh_sensor_t* dev, lis3dh_scale_t scale); * @return true on success, false on error */ bool lis3dh_set_fifo_mode (lis3dh_sensor_t* dev, lis3dh_fifo_mode_t mode, - uint8_t thresh, lis3dh_int_signals_t trigger); + uint8_t thresh, lis3dh_int_signal_t trigger); /** @@ -167,7 +167,7 @@ bool lis3dh_new_data (lis3dh_sensor_t* dev); /** - * @brief Get one sample of sensor data as floating point values (unit 1g) + * @brief Get one sample of sensor data as floating point values (unit g) * * Function works only in bypass mode and fails in FIFO modes. In FIFO modes, * function *lis3dh_get_float_data_fifo* has to be used instead to get data. @@ -220,74 +220,21 @@ uint8_t lis3dh_get_raw_data_fifo (lis3dh_sensor_t* dev, /** - * @brief Set configuration for activity (inertial) interrupt INT1/INT2 + * @brief Enable / disable an interrupt on signal INT1 or INT2 * - * Set the configuration for interrupts that are generated when a certain - * acceleration is higher or lower than defined threshold and one of the - * following activities are recognized: wake-up, free fall or 6D/4D - * orientation detection - * - * @param dev pointer to the sensor device data structure - * @param signal specifies the interrupt signal used in function - * @param config configuration for the specified interrupt signal - * @return true on success, false on error + * @param dev pointer to the sensor device data structure + * @param type interrupt to be enabled or disabled + * @param signal interrupt signal that is activated for the interrupt + * @param value true to enable or false to disable the interrupt + * @return true on success, false on error */ -bool lis3dh_set_int_activity_config (lis3dh_sensor_t* dev, - lis3dh_int_signals_t signal, - lis3dh_int_activity_config_t* config); - - -/** - * @brief Get cofiguration for activity (inertial) interrupt INT1/INT2 - * - * Get the configuration for interrupts that are generated when a certain - * acceleration is higher or lower than defined threshold and one of the - * following activities is recognized: wake-up, free fall or 6D/4D orientation - * detection - * - * @param dev pointer to the sensor device data structure - * @param signal specifies the interrupt signal used in function - * @param config configuration for the specified interrupt signal - * @return true on success, false on error - */ -bool lis3dh_get_int_activity_config (lis3dh_sensor_t* dev, - lis3dh_int_signals_t signal, - lis3dh_int_activity_config_t* config); - - -/** - * @brief Get the source of the activity (inertial) interrupt INT1/INT2 - * - * Returns a byte with flags that indicate the activity which triggered - * the interrupt signal (see INTx_SRC register in datasheet for details) - * - * @param dev pointer to the sensor device data structure - * @param source pointer to the interrupt source - * @param signal specifies the interrupt signal used in function - * @return true on success, false on error - */ -bool lis3dh_get_int_activity_source (lis3dh_sensor_t* dev, - lis3dh_int_activity_source_t* source, - lis3dh_int_signals_t signal); - - -/** - * @brief Enable/disable an data interrupt on signal INT1 - * - * Enables or diables interrupts that are generated either when data are - * ready to read or FIFO activities like overrun an watermark happen. - * - * @param dev pointer to the sensor device data structure - * @param type type of interrupt to be enabled/disabled - * @param value true to enable/false to disable the interrupt - * @return true on success, false on error - */ -bool lis3dh_enable_int_data (lis3dh_sensor_t* dev, - lis3dh_int_data_t type, bool value); +bool lis3dh_enable_int (lis3dh_sensor_t* dev, + lis3dh_int_type_t type, + lis3dh_int_signal_t signal, bool value); /** - * @brief Get the source of the data interrupt on signal INT1 + * @brief Get the source of data ready and FIFO interrupts on INT1 * * @param dev pointer to the sensor device data structure * @param source pointer to the interrupt source @@ -298,44 +245,91 @@ bool lis3dh_get_int_data_source (lis3dh_sensor_t* dev, /** - * @brief Set configuration for click detection interrupt INT1/INT2 + * @brief Set the configuration of an inertial event interrupt generator + * + * Inertial interrupt generators produce interrupts when certain inertial event + * occures (event interrupts), that is, the acceleration of defined axes is + * higher or lower than a defined threshold and one of the following event is + * recognized: axis movement / wake up, free fall, 6D/4D orientation detection. + * + * @param dev pointer to the sensor device data structure + * @param config pointer to the interrupt generator configuration + * @param gen interrupt generator to which the function is applied + * @return true on success, false on error + */ +bool lis3dh_set_int_event_config (lis3dh_sensor_t* dev, + lis3dh_int_event_config_t* config, + lis3dh_int_event_gen_t gen); + + +/** + * @brief Get the configuration of an inertial event interrupt generator + * + * Inertial interrupt generators produce interrupts when certain inertial event + * occures (event interrupts), that is, the acceleration of defined axes is + * higher or lower than a defined threshold and one of the following event is + * recognized: axis movement / wake up, free fall, 6D/4D orientation detection. + * + * @param dev pointer to the sensor device data structure + * @param config pointer to the interrupt generator configuration + * @param gen interrupt generator to which the function is applied + * @return true on success, false on error + */ +bool lis3dh_get_int_event_config (lis3dh_sensor_t* dev, + lis3dh_int_event_config_t* config, + lis3dh_int_event_gen_t gen); + + +/** + * @brief Get the source of an inertial event interrupt INT1/INT2 + * + * Returns a byte with flags that indicate the event which triggered + * the interrupt signal (see INTx_SRC register in datasheet for details) + * + * @param dev pointer to the sensor device data structure + * @param source pointer to the interrupt source data structure + * @param gen interrupt generator to which the function is applied + * @return true on success, false on error + */ +bool lis3dh_get_int_event_source (lis3dh_sensor_t* dev, + lis3dh_int_event_source_t* source, + lis3dh_int_event_gen_t gen); + + +/** + * @brief Set the configuration of the click detection interrupt generator * * Set the configuration for interrupts that are generated when single or * double clicks are detected. * * @param dev pointer to the sensor device data structure - * @param signal specifies the interrupt signal used in function - * @param config configuration for the specified interrupt signal + * @param config pointer to the interrupt generator configuration * @return true on success, false on error */ bool lis3dh_set_int_click_config (lis3dh_sensor_t* dev, - lis3dh_int_signals_t signal, lis3dh_int_click_config_t* config); /** - * @brief Set configuration for click detection interrupt INT1/INT2 + * @brief Get the configuration of the click detection interrupt generator * * Set the configuration for interrupts that are generated when single or * double clicks are detected. * * @param dev pointer to the sensor device data structure - * @param signal specifies the interrupt signal used in function - * @param config configuration for the specified interrupt signal + * @param config pointer to the interrupt generator configuration * @return true on success, false on error */ bool lis3dh_get_int_click_config (lis3dh_sensor_t* dev, - lis3dh_int_signals_t signal, lis3dh_int_click_config_t* config); /** - * @brief Get the source of the click detection interrupt INT1/INT2 + * @brief Get the source of the click detection interrupt on signal INT1/INT2 * * Returns a byte with flags that indicate the activity which triggered * the interrupt signal (see CLICK_SRC register in datasheet for details) * * @param dev pointer to the sensor device data structure - * @param signal specifies the interrupt signal used in function * @param source pointer to the interrupt source * @return true on success, false on error */ @@ -395,9 +389,8 @@ bool lis3dh_set_hpf_ref (lis3dh_sensor_t* dev, int8_t ref); */ int8_t lis3dh_get_hpf_ref (lis3dh_sensor_t* dev); - /** - * @brief Enable / disable ADC or temperatury sensor + * @brief Enable / disable ADC or temperature sensor * * @param dev pointer to the sensor device data structure * @param enable if true, ADC inputs are enabled @@ -419,6 +412,7 @@ int8_t lis3dh_enable_adc (lis3dh_sensor_t* dev, bool enable, bool temp); bool lis3dh_get_adc (lis3dh_sensor_t* dev, uint16_t* adc1, uint16_t* adc2, uint16_t* adc3); + // ---- Low level interface functions ----------------------------- /** @@ -435,7 +429,7 @@ bool lis3dh_get_adc (lis3dh_sensor_t* dev, * @param len number of bytes to be written to the register * @return true on success, false on error */ -bool lis3dh_write_reg (lis3dh_sensor_t* dev, +bool lis3dh_reg_write (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); /** @@ -452,7 +446,7 @@ bool lis3dh_write_reg (lis3dh_sensor_t* dev, * @param len number of bytes to be read from the register * @return true on success, false on error */ -bool lis3dh_read_reg (lis3dh_sensor_t* dev, +bool lis3dh_reg_read (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); #ifdef __cplusplus diff --git a/extras/lis3dh/lis3dh_types.h b/extras/lis3dh/lis3dh_types.h index 868108a..b90b042 100644 --- a/extras/lis3dh/lis3dh_types.h +++ b/extras/lis3dh/lis3dh_types.h @@ -115,17 +115,59 @@ typedef enum { lis3dh_int1_signal = 0, lis3dh_int2_signal = 1 -} lis3dh_int_signals_t; +} lis3dh_int_signal_t; /** - * @brief Activity interrupt configuration for INT1/INT2 signals - * - * Activity interrupts are: axes movement wake-up, free-fall, 6D/4D detection. + * @brief Inertial event interrupt generators + */ +typedef enum { + + lis3dh_int_event1_gen = 0, + lis3dh_int_event2_gen = 1 + +} lis3dh_int_event_gen_t; + + +/** + * @brief Interrupt types for interrupt signals INT1/INT2 + */ +typedef enum { + + lis3dh_int_data_ready, // data ready for read interrupt (only INT1) + + lis3dh_int_fifo_watermark, // FIFO exceeds the threshold (only INT1) + lis3dh_int_fifo_overrun, // FIFO is completely filled (only INT1) + + lis3dh_int_event1, // inertial event interrupt 1 + lis3dh_int_event2, // inertial event interrupt 2 + + lis3dh_int_click // click detection interrupt + +} lis3dh_int_type_t; + + +/** + * @brief Data ready and FIFO status interrupt source for INT1 */ typedef struct { - enum { // activity type for the interrupt + bool data_ready; // true when acceleration data are ready to read + + bool fifo_watermark; // true when FIFO exceeds the FIFO threshold + bool fifo_overrun; // true when FIFO is completely filled + +} lis3dh_int_data_source_t; + + +/** + * @brief Inertial interrupt generator configuration for INT1/INT2 + * + * Inertial events are: wake-up, free-fall, 6D/4D detection. + */ +typedef struct { + + enum { // interrupt mode lis3dh_wake_up, // AOI = 0, 6D = 0 lis3dh_free_fall, // AOI = 1, 6D = 0 @@ -136,7 +178,7 @@ typedef struct { lis3dh_4d_movement, // AOI = 0, 6D = 1, D4D = 1 lis3dh_4d_position, // AOI = 1, 6D = 1, D4D = 1 - } activity; + } mode; uint8_t threshold; // threshold used for comparison for all axes @@ -154,50 +196,26 @@ typedef struct { uint8_t duration; // duration in 1/ODR an interrupt condition has // to be given before the interrupt is generated -} lis3dh_int_activity_config_t; +} lis3dh_int_event_config_t; /** - * @brief Activity interrupt source type for interrupt signals INT1/INT2 + * @brief Inertial event source type for interrupt generator INT1/INT2 */ typedef struct { - bool active:1; // true - one ore more activities occured + bool active:1; // true - one ore more events occured - bool x_low :1; // true - x low activity occured - bool x_high:1; // true - x high activity occured + bool x_low :1; // true - x lower than threshold event + bool x_high:1; // true - x higher than threshold event - bool y_low :1; // true - z low activity occured - bool y_high:1; // true - z high activity occured + bool y_low :1; // true - z lower than threshold event + bool y_high:1; // true - z higher than threshold event - bool z_low :1; // true - z low activity occured - bool z_high:1; // true - z high activity occured + bool z_low :1; // true - z lower than threshold event + bool z_high:1; // true - z higher than threshold event -} lis3dh_int_activity_source_t; - - -/** - * @brief Data interrupt types for INT1 signal - */ -typedef enum { - - lis3dh_data_ready, // interrupt when data are ready to read - lis3dh_fifo_watermark, // interrupt when FIFO exceeds the FIFO threashold - lis3dh_fifo_overrun // interrupt when FIFO is completely filled - -} lis3dh_int_data_t; - - -/** - * @brief Data interrupt source type for INT1 signal - */ -typedef struct { - - bool data_ready; // true when data are ready to read - bool fifo_watermark; // true when FIFO exceeds the FIFO threashold - bool fifo_overrun; // true when FIFO is completely filled - -} lis3dh_int_data_source_t; +} lis3dh_int_event_source_t; /** @@ -230,7 +248,7 @@ typedef struct { /** - * @brief Click interrupt configuration for interrupt signals INT1/INT2 + * @brief Click interrupt source for interrupt signals INT1/INT2 */ typedef struct { @@ -243,7 +261,7 @@ typedef struct { bool s_click:1; // single click detected bool d_click:1; // double click detected - bool active :1; // true - one ore more Activities occured + bool active :1; // true - one ore more event occured } lis3dh_int_click_source_t; @@ -278,7 +296,7 @@ typedef lis3dh_raw_data_t lis3dh_raw_data_fifo_t[32]; /** - * @brief Floating point output value set in degree + * @brief Floating point output value set in g */ typedef struct { @@ -321,8 +339,9 @@ typedef struct { uint8_t cs; // ESP8266, ESP32: GPIO used as SPI CS // __linux__: device index - lis3dh_scale_t scale; // fill range scale (default 245 dps) + lis3dh_scale_t scale; // full range scale (default 2 g) lis3dh_resolution_t res; // resolution used + lis3dh_fifo_mode_t fifo_mode; // FIFO operation mode (default bypass) bool fifo_first; // first FIFO access From c82713ffd3810135e8b95ab2ec0f33b500455f1a Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Sun, 31 Dec 2017 14:30:53 +0100 Subject: [PATCH 04/10] minor changes --- extras/lis3dh/README.md | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/extras/lis3dh/README.md b/extras/lis3dh/README.md index a6af314..c23d0ae 100644 --- a/extras/lis3dh/README.md +++ b/extras/lis3dh/README.md @@ -85,18 +85,14 @@ In this example, a LIS3DH sensor connected to I2C is initialized and set to high The sensor determines periodically the accelerations for2 all axes that are enabled for measurement 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 and are always left-aligned. The resolution depends on the selected operation mode and the selected full scale. For example, in low power mode with 8-bit resolution only the high byte is used. - - - -range and the resolution of these data depend the selected measurement mode and on the sensitivity of the sensor which is selected by the **full scale** value. The LIS3DH allows to select the following full scales: +Raw **output data** (**raw data**) are given as 16-bit signed integer values in 2’s complement representation and are always left-aligned. The resolution depends on the selected operation mode and the selected full scale. For example, in low power mode with 8-bit resolution only the high byte is used. LIS3DH allows to select the following full scales: Full Scale | Driver symbol | Resolution 12 bit
```lis3dh_high_res``` | Resolution 10 bit
```lis3dh_normal``` | Resolution 8 bit
```lis3dh_low_power``` -:--------------------- | -----------:|-----------:|:--------------- -```lis3dh_scale_2g``` | ±2 g | 1 mg | 4 mg | 16 mg -```lis3dh_scale_4g``` | ±4 g | 2 mg | 8 mg | 32 mg -```lis3dh_scale_8g``` | ±8 g | 4 mg | 16 mg | 64 mg -```lis3dh_scale_16g``` |±16 g | 12 mg | 48 mg | 192 mg +---------------------:|:-----------:|-----------:|---------------:|-----: + ±2 g | ```lis3dh_scale_2g``` | 1 mg | 4 mg | 16 mg + ±4 g | ```lis3dh_scale_4g``` | 2 mg | 8 mg | 32 mg + ±8 g | ```lis3dh_scale_8g``` | 4 mg | 16 mg | 64 mg +±16 g | ```lis3dh_scale_16g``` | 12 mg | 48 mg | 192 mg By default, a full scale of ±2 g is used. Function ```lis3dh_set_scale``` can be used to change it. @@ -143,11 +139,11 @@ void user_task_periodic(void *pvParameters) ``` **Please note:** -The functions ```lis3dh_get_float_data``` and ```lis3dh_get_raw_data``` always return the last available results. If these functions are called more often than measurements are performed, some measurement results are retrieved multiple times. If these functions are called too rarely, some measurement results will be lost. +Functions ```lis3dh_get_float_data``` and ```lis3dh_get_raw_data``` 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. ### High pass filtering -LIS3DH provides embedded high-pass filtering capability to improve measurement results. Please refer the [datasheet](http://www.st.com/resource/en/datasheet/lis3dh.pdf) or [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more details. +LIS3DH provides embedded high-pass filtering capabilities to improve measurement results. Please refer the [datasheet](http://www.st.com/resource/en/datasheet/lis3dh.pdf) or [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more details. The high pass filter can independently apply to @@ -458,12 +454,12 @@ ADC sampling rate is the same the output data rate (ODR). Results are given as l The LIS3DH is a very complex and flexible sensor with a lot of features. It can be used for a big 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 lis3dh_read_reg (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); -bool lis3dh_write_reg (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); +bool lis3dh_reg_read (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); +bool lis3dh_reg_write (lis3dh_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 the high level interface AND if you exactly know what you do and what it might affect. Please be aware that it might affect the high level interface. +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. ## Usage @@ -481,7 +477,7 @@ Following figure shows a possible hardware configuration for ESP8266 and ESP32 i | GPIO 14 (SCL) >-----> SCL | | GPIO 13 (SDA) <-----> SDA | | GPIO 5 <------ INT1 | - | GPIO 4 <------ DRDY/INT2| + | GPIO 4 <------ INT2 | +-----------------+ +----------+ ``` @@ -496,6 +492,7 @@ If SPI interface is used, configuration for ESP8266 and ESP32 could look like fo | GPIO 12 (MISO)<------ SDO | | GPIO 18 (MISO)<------ SDO | | GPIO 2 (CS) ------> CS | | GPIO 19 (CS) ------> CS | | GPIO 5 <------ INT1 | | GPIO 5 <------ INT1 | + | GPIO 4 <------ INT2 | | GPIO 5 <------ INT2 | +-----------------+ +----------+ +-----------------+ +----------+ ``` @@ -554,9 +551,7 @@ i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ); SPI interface has only to be initialized explicitly on ESP32 platform to declare the GPIOs that are used for SPI interface. ``` -#ifdef ESP_PLATFORM spi_bus_init (SPI_BUS, SPI_SCK_GPIO, SPI_MISO_GPIO, SPI_MOSI_GPIO); -#endif ``` Once the interfaces are initialized, function ```lis3dh_init_sensor``` has to be called for each LIS3DH 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. @@ -616,7 +611,7 @@ void user_task_periodic(void *pvParameters) xTaskCreate(user_task_periodic, "user_task_periodic", TASK_STACK_DEPTH, NULL, 2, NULL); ``` -The user task simply tests periodically with a higher rate than the output data rate (ODR) of the sensor whether new data are available. If new data are available, it fetches the data. +The user task simply tests periodically with a rate higher than the output data rate (ODR) of the sensor whether new data are available. If new data are available, it fetches the data. #### Interrupt user task @@ -697,6 +692,8 @@ lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); ## Full Example ``` +/* -- use following constants to define the example mode ----------- */ + // #define SPI_USED // SPI interface is used, otherwise I2C // #define FIFO_MODE // multiple sample read mode // #define INT_DATA // data interrupts used (data ready and FIFO status) From 11ca4da400eb0e3de62ecfb8d14146df00fe01c6 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Wed, 3 Jan 2018 11:59:26 +0100 Subject: [PATCH 05/10] minor changes --- extras/lis3dh/README.md | 95 +++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 46 deletions(-) diff --git a/extras/lis3dh/README.md b/extras/lis3dh/README.md index c23d0ae..bd59be6 100644 --- a/extras/lis3dh/README.md +++ b/extras/lis3dh/README.md @@ -1,6 +1,6 @@ # Driver for the LIS3DH 3-axes digital output accelerometer -The driver is for the usage with the ESP8266 and [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos). +The driver is for the usage with the ESP8266 and [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos). If you can't find it in folder [extras/lis3dh](https://github.com/SuperHouse/esp-open-rtos/tree/master/extras) of original repository, it is not yet merged. Please take a look to branch [lis3dh](https://github.com/gschorcht/esp-open-rtos/tree/lis3dh) of my fork in that case. It is also working with ESP32 and [ESP-IDF](https://github.com/espressif/esp-idf.git) using a wrapper component for ESP8266 functions, see folder ```components/esp8266_wrapper```, as well as Linux based systems using a wrapper library. @@ -76,14 +76,14 @@ if ((sensor = lis3dh_init_sensor (I2C_BUS, LIS3DH_I2C_ADDRESS_2, 0))) In this example, a LIS3DH sensor connected to I2C is initialized and set to high-resolution mode to start measurements for all three axes with an output data rate (ODR) of 10 Hz. **Please note:** -- Function ```lis3dh_init_sensor``` resets the sensor completely, switches it to the power down 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. +- ```lis3dh_init_sensor``` function resets the sensor completely, switches it to the power down 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 function ```lis3dh_set_mode```. In particular, the interrupt configuration should be performed before to avoid loosing the first interrupt and locking the system. ## Measurement results ### Output data format -The sensor determines periodically the accelerations for2 all axes that are enabled for measurement and produces output data with the selected output data rate (ODR). +The sensor determines periodically the accelerations for all axes that are enabled for measurement 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 and are always left-aligned. The resolution depends on the selected operation mode and the selected full scale. For example, in low power mode with 8-bit resolution only the high byte is used. LIS3DH allows to select the following full scales: @@ -155,9 +155,9 @@ The mode and the cutoff frequency of the high pass filter can be configured usin Driver symbol | HPF mode :--------------|:--------- -lis3dh_hpf_normal | Normal mode -lis3dh_hpf_reference | Reference mode -lis3dh_hpf_autoreset | Auto-reset on interrupt +```lis3dh_hpf_normal``` | Normal mode +```lis3dh_hpf_reference``` | Reference mode +```lis3dh_hpf_autoreset``` | Auto-reset on interrupt For each output data rate (ODR), 4 different HPF cutoff frequencies can be used. Furthermore, a number of boolean parameters indicate to which data the HPF is applied. @@ -177,10 +177,10 @@ In order to limit the rate at which the host processor has to fetch the data, th Driver symbol | FIFO mode --------------|------------------------- -lis3dh_bypass | Bypass mode (FIFO is not used) -lis3dh_fifo | FIFO mode -lis3dh_stream | Stream mode -lis3dh_stream_to_fifo | Stream-to-FIFO mode +```lis3dh_bypass``` | Bypass mode (FIFO is not used) +```lis3dh_fifo``` | FIFO mode +```lis3dh_stream``` | Stream mode +```lis3dh_stream_to_fifo``` | Stream-to-FIFO mode The FIFO mode can be set using function ```lis3dh_set_fifo_mode```. This function takes three parameters @@ -205,7 +205,7 @@ lis3dh_set_fifo_mode (sensor, lis3dh_stream, 10, lis3dh_int1_signal); To read data from the FIFO, simply use either - the function ```lis3dh_get_raw_data_fifo``` to all get raw output data stored in FIFO or -- the function ```lis3dh_get_float_data_fifo``` to get all data stored in FIFO and converted to real values in dps (degrees per second). +- the function ```lis3dh_get_float_data_fifo``` to get all data stored in FIFO and converted to real values in g. Both functions clear the FIFO and return the number of samples read from the FIFO. @@ -233,25 +233,25 @@ void user_task_periodic (void *pvParameters) } ``` -### Interrupts +## Interrupts The LIS3DH supports two dedicated interrupt signals **```INT1```** and **```INT2```** and three different types of interrupts: -- data interrupts (data ready and FIFO status), -- inertial event interrupts (axis movement, wake-up, free fall, and 6D/4D orientation detection), and -- click detection interrupts. +- **data** interrupts (data ready and FIFO status), +- **inertial event** interrupts (axis movement, wake-up, free fall, and 6D/4D orientation detection), and +- **click detection** interrupts. While inertial event interrupts and click detection interrupts can be configured for both interrupt signals, data ready and FIFO status interrupts can be configured only for interrupt signal ```INT1```. -#### Data interrupts (data ready and FIFO status) +### Data interrupts (data ready and FIFO status) Following sources can generate an interrupt on signal ```INT1```: Interrupt source | Driver symbol :-----------------|:------------- -Output data become ready to read | lis3dh_int_data_ready -FIFO content exceeds the watermark level | lis3dh_int_fifo_watermark -FIFO is completely filled | lis3dh_int_fifo_overrun +Output data become ready to read | ```lis3dh_int_data_ready``` +FIFO content exceeds the watermark level | ```lis3dh_int_fifo_watermark``` +FIFO is completely filled | ```lis3dh_int_fifo_overrun``` Each of these interrupt sources can be enabled or disabled separately with function ```lis3dh_enable_int```. By default all interrupt sources are disabled. @@ -266,21 +266,21 @@ void int1_handler () { lis3dh_int_data_source_t data_src; - // get the interrupt source of INT1 + // get the source of the interrupt on *INT1* signal lis3dh_get_int_data_source (sensor, &data_src); // in case of data ready interrupt, get the results and do something with them if (data_src.data_ready) - // ... read data + ... // read data // in case of FIFO interrupts read the whole FIFO else if (data_src.fifo_watermark || data_src.fifo_overrun) - // ... read FIFO data + ... // read FIFO data ... } ``` -#### Inertial event interrupts +### Inertial event interrupts Inertial interrupt generators allow to generate interrupts when certain inertial event occures (event interrupts), that is, the acceleration of defined axes is higher or lower than a defined threshold. If activated, the acceleration of each axis is compared with a defined threshold to check whether it is below or above the threshold. The results of all activated comparisons are then combined OR or AND to generate the interrupt signal. @@ -294,7 +294,7 @@ Inertial event interrupts can be configured with the function ```lis3dh_get_int_ Inertial event interrupts have to be enabled or disabled using function ```lis3dh_enable_int```. The interrupt signal on which the interrupts are generated is given as parameter. -For example, wake-up detection interrupt on signal ```INT1``` could be configured as following: +For example, axis movement detection interrupt generated by inertial interrupt generator 2 on signal ```INT1``` could be configured as following: ``` lis3dh_int_event_config_t event_config; @@ -311,7 +311,7 @@ event_config.z_high_enabled = true; event_config.duration = 0; event_config.latch = true; -lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event1_gen); +lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event2_gen); lis3dh_enable_int (sensor, lis3dh_int_event1, lis3dh_int1_signal, true); ``` @@ -328,25 +328,27 @@ void int1_handler () lis3dh_int_data_source_t data_src; lis3dh_int_event_source_t event_src; - // get the interrupt source of INT1 + // get the source of the interrupt on *INT1* signal lis3dh_get_int_data_source (sensor, &data_src); - lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event1_gen); + lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event2_gen); // in case of data ready interrupt, get the results and do something with them if (data_src.data_ready) - // ... read data + ... // read data // in case of FIFO interrupts read the whole FIFO else if (data_src.fifo_watermark || data_src.fifo_overrun) - // ... read FIFO data + ... // read FIFO data // in case of inertial event interrupt else if (event_src.active) - // ... read data + ... // read data ... } ``` +**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 satisfied. + **Please note** Activating all threshold comparisons and the OR combination (```lis3dh_wake_up```) is the most flexible way to deal with inertial event interrupts. Functions such as free fall detection and so on can then be realized by suitably combining the various interrupt sources by the user task. Following example realizes the free fall detection in user task. ``` @@ -364,7 +366,7 @@ event_config.z_high_enabled = true; event_config.duration = 0; event_config.latch = true; -lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event1_gen); +lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event2_gen); lis3dh_enable_int (sensor, lis3dh_int_event1, lis3dh_int1_signal, true); ``` @@ -373,24 +375,22 @@ void int1_handler () { lis3dh_int_event_source_t event_src; - // get the interrupt source of INT1 - lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int1_signal); + // get the source of the interrupt from interrupt generator 2 on *INT1* signal + lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event2_gen); - // detect free fall (all accelerations are below the threshold) + // test for free fall condition (all accelerations are below the threshold) if (event_src.x_low && event_src.y_low && event_src.z_low) - ... + ... // do something ... } ``` -#### Click detection interrupts +### Click detection interrupts A sequence of acceleration values over time measured along certain axes can be used to detect single and double clicks. Please refer the [datasheet](http://www.st.com/resource/en/datasheet/lis3dh.pdf) or [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more information. -Click detection interrupts are configured with function ```lis3dh_set_int_click_config```. This function requires as parameters the configuration of type ```lis3dh_int_click_config_t``` and the interrupt signal to be used for click detection interrupts. - -Inertial event interrupts have to be enabled or disabled using function ```lis3dh_enable_int```. The interrupt signal on which the interrupts are generated is given as parameter. +Click detection interrupts are configured using the ``` lis3dh_set_int_click_config``` function. This function requires the configuration of type ```lis3dh_int_click_config_t``` as parameter. The interrupt has to be activated or deactivated using the ```lis3dh_enable_int``` function with the interrupt signal on which the interrupts are generated as parameter. In following example, the single click detection for z-axis is enabled with a time limit of 1/ODR, a time latency of 1/ODR and a time window of 3/ODR. @@ -413,6 +413,8 @@ lis3dh_set_int_click_config (sensor, &click_config); lis3dh_enable_int (sensor, lis3dh_int_click, lis3dh_int1_signal, true); ``` +Please refer the [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more information about the configuration parameters. + As with other interrupts, the function ```lis3dh_get_int_click_source``` can be used to determine the source of the interrupt signal whenever it is generated. This function returns a data structure of type ```lis3dh_int_click_source_t``` that contains a boolean member for each source that can be tested for true. ``` @@ -420,28 +422,29 @@ void int1_handler () { lis3dh_int_click_source_t click_src; - // get the interrupt source of INT1 + // get the source of the interrupt on *INT1* signal lis3dh_get_int_click_source (sensor, &click_src); // detect single click along z-axis if (click_src.z_click && click_src.s_click) - ... + ... // 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 satisfied. -#### Interrupt signal properties +### Interrupt signal properties By default, interrupt signals are high active. Using function ```lis3dh_config_int_signals```, the level of the interrupt signal can be changed. Driver symbol | Meaning :-------------|:------- -lis3dh_high_active | Interrupt signal is high active (default) -lis3dh_low_active | Interrupt signal is low active +```lis3dh_high_active``` | Interrupt signal is high active (default) +```lis3dh_low_active``` | Interrupt signal is low active -### Analog inputs and temperature sensor +## Analog inputs and temperature sensor The LIS3DH sensor contains an auxiliary ADC with 3 separate dedicated inputs ADC1, ADC2, and ADC3. ADC3 can be connected to the internal temperatur sensor. The input range is 1200 ± 400 mV. The resolution of the A/D converter is 10 bit in normal and high-resolution mode, but only 8 bit in low-power mode. @@ -449,7 +452,7 @@ ADC inputs can be activated and deactivated (default) with function ```lis3dh_en ADC sampling rate is the same the output data rate (ODR). Results are given as left-aligned 16-bit signed integer values in 2’s complement. Function ```lis3dh_get_adc``` can be used to get the results. -### Low level functions +## Low level functions The LIS3DH is a very complex and flexible sensor with a lot of features. It can be used for a big 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. From b4b374cad0c3b3b32443171a796c5f859f02a931 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Thu, 21 Dec 2017 19:04:59 +0100 Subject: [PATCH 06/10] LIS3DH driver added --- examples/lis3dh/Makefile | 3 + examples/lis3dh/lis3dh_example.c | 355 +++++++++ extras/lis3dh/README.md | 1023 ++++++++++++++++++++++++ extras/lis3dh/component.mk | 10 + extras/lis3dh/lis3dh.c | 1274 ++++++++++++++++++++++++++++++ extras/lis3dh/lis3dh.h | 456 +++++++++++ extras/lis3dh/lis3dh_platform.h | 112 +++ extras/lis3dh/lis3dh_types.h | 355 +++++++++ 8 files changed, 3588 insertions(+) create mode 100644 examples/lis3dh/Makefile create mode 100644 examples/lis3dh/lis3dh_example.c create mode 100644 extras/lis3dh/README.md create mode 100644 extras/lis3dh/component.mk create mode 100644 extras/lis3dh/lis3dh.c create mode 100644 extras/lis3dh/lis3dh.h create mode 100644 extras/lis3dh/lis3dh_platform.h create mode 100644 extras/lis3dh/lis3dh_types.h diff --git a/examples/lis3dh/Makefile b/examples/lis3dh/Makefile new file mode 100644 index 0000000..99a893a --- /dev/null +++ b/examples/lis3dh/Makefile @@ -0,0 +1,3 @@ +PROGRAM=LIS3DH +EXTRA_COMPONENTS = extras/i2c extras/lis3dh +include ../../common.mk diff --git a/examples/lis3dh/lis3dh_example.c b/examples/lis3dh/lis3dh_example.c new file mode 100644 index 0000000..079b1b7 --- /dev/null +++ b/examples/lis3dh/lis3dh_example.c @@ -0,0 +1,355 @@ +/** + * Simple example with one sensor connected to I2C or SPI. It demonstrates the + * different approaches to fetch the data. Either one of the interrupt signals + * is used or new data are fetched periodically. + * + * Harware configuration: + * + * I2C + * + * +-----------------+ +----------+ + * | ESP8266 / ESP32 | | LIS3DH | + * | | | | + * | GPIO 14 (SCL) ----> SCL | + * | GPIO 13 (SDA) <---> SDA | + * | GPIO 5 <---- INT1 | + * +-----------------+ +----------+ + * + * SPI + * + * +-----------------+ +----------+ +-----------------+ +----------+ + * | ESP8266 | | LIS3DH | | ESP32 | | LIS3DH | + * | | | | | | | | + * | GPIO 14 (SCK) ----> SCK | | GPIO 16 (SCK) ----> SCK | + * | GPIO 13 (MOSI)----> SDI | | GPIO 17 (MOSI)----> SDI | + * | GPIO 12 (MISO)<---- SDO | | GPIO 18 (MISO)<---- SDO | + * | GPIO 2 (CS) ----> CS | | GPIO 19 (CS) ----> CS | + * | GPIO 5 <---- INT1 | | GPIO 5 <---- INT1 | + * +-----------------+ +---------+ +-----------------+ +----------+ + */ + +/* -- use following constants to define the example mode ----------- */ + +// #define SPI_USED // SPI interface is used, otherwise I2C +// #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 (wake-up, free fall or 6D/4D orientation) +// #define INT_CLICK // click detection interrupts used + +#if defined(INT_DATA) || defined(INT_EVENT) || defined(INT_CLICK) +#define INT_USED +#endif + +/* -- includes ----------------------------------------------------- */ + +#include "lis3dh.h" + +/** -- platform dependent definitions ------------------------------ */ + +#ifdef ESP_PLATFORM // ESP32 (ESP-IDF) + +// user task stack depth for ESP32 +#define TASK_STACK_DEPTH 2048 + +// SPI interface definitions for ESP32 +#define SPI_BUS HSPI_HOST +#define SPI_SCK_GPIO 16 +#define SPI_MOSI_GPIO 17 +#define SPI_MISO_GPIO 18 +#define SPI_CS_GPIO 19 + +#else // ESP8266 (esp-open-rtos) + +// user task stack depth for ESP8266 +#define TASK_STACK_DEPTH 256 + +// SPI interface definitions for ESP8266 +#define SPI_BUS 1 +#define SPI_SCK_GPIO 14 +#define SPI_MOSI_GPIO 13 +#define SPI_MISO_GPIO 12 +#define SPI_CS_GPIO 2 // GPIO 15, the default CS of SPI bus 1, can't be used + +#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_100K + +// interrupt GPIOs defintions for ESP8266 and ESP32 +#define INT1_PIN 5 +#define INT2_PIN 4 + +/* -- user tasks --------------------------------------------------- */ + +static lis3dh_sensor_t* sensor; + +/** + * Common function used to get sensor data. + */ +void read_data () +{ + #ifdef FIFO_MODE + + lis3dh_float_data_fifo_t fifo; + + if (lis3dh_new_data (sensor)) + { + uint8_t num = lis3dh_get_float_data_fifo (sensor, fifo); + + printf("%.3f LIS3DH 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 LIS3DH (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 + + lis3dh_float_data_t data; + + if (lis3dh_new_data (sensor) && + lis3dh_get_float_data (sensor, &data)) + // max. full scale is +-16 g and best resolution is 1 mg, i.e. 5 digits + printf("%.3f LIS3DH (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 +/** + * In this case, any of the possible interrupts on interrupt signal *INT1* is + * used to fetch the data. + * + * When interrupts are used, the user has to define interrupt handlers that + * either fetches the data directly or triggers a task which is waiting to + * fetch the data. In this example, the interrupt handler sends an event to + * a waiting task to trigger the data gathering. + */ + +static QueueHandle_t gpio_evt_queue = NULL; + +// User task that fetches the sensor values. + +void user_task_interrupt (void *pvParameters) +{ + uint8_t gpio_num; + + while (1) + { + if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY)) + { + lis3dh_int_data_source_t data_src = {}; + lis3dh_int_event_source_t event_src = {}; + lis3dh_int_click_source_t click_src = {}; + + // get the source of the interrupt and reset *INTx* signals + #ifdef INT_DATA + lis3dh_get_int_data_source (sensor, &data_src); + #elif INT_EVENT + lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event1_gen); + #elif INT_CLICK + lis3dh_get_int_click_source (sensor, &click_src); + #endif + + // in case of DRDY interrupt or inertial event interrupt read one data sample + if (data_src.data_ready) + read_data (); + + // in case of FIFO interrupts read the whole FIFO + else if (data_src.fifo_watermark || data_src.fifo_overrun) + read_data (); + + // in case of event interrupt + else if (event_src.active) + { + printf("%.3f LIS3DH ", (double)sdk_system_get_time()*1e-3); + if (event_src.x_low) printf("x is lower than threshold\n"); + if (event_src.y_low) printf("y is lower than threshold\n"); + if (event_src.z_low) printf("z is lower than threshold\n"); + if (event_src.x_high) printf("x is higher than threshold\n"); + if (event_src.y_high) printf("y is higher than threshold\n"); + if (event_src.z_high) printf("z is higher than threshold\n"); + } + + // in case of click detection interrupt + else if (click_src.active) + printf("%.3f LIS3DH %s\n", (double)sdk_system_get_time()*1e-3, + click_src.s_click ? "single click" : "double click"); + } + } +} + +// Interrupt handler which resumes user_task_interrupt on interrupt + +void IRAM int_signal_handler (uint8_t gpio) +{ + // send an event with GPIO to the interrupt user task + xQueueSendFromISR(gpio_evt_queue, &gpio, NULL); +} + +#else // !INT_USED + +/* + * In this example, user task fetches the sensor values every seconds. + */ + +void user_task_periodic(void *pvParameters) +{ + vTaskDelay (100/portTICK_PERIOD_MS); + + while (1) + { + // read sensor data + read_data (); + + // passive waiting until 1 second is over + vTaskDelay(100/portTICK_PERIOD_MS); + } +} + +#endif // INT_USED + +/* -- 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 = lis3dh_init_sensor (SPI_BUS, 0, SPI_CS_GPIO); + + #else + + // init all I2C bus interfaces at which LIS3DH sensors are connected + i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ); + + // init the sensor with slave address LIS3DH_I2C_ADDRESS_1 connected to I2C_BUS. + sensor = lis3dh_init_sensor (I2C_BUS, LIS3DH_I2C_ADDRESS_1, 0); + + #endif + + if (sensor) + { + // --- SYSTEM CONFIGURATION PART ---- + + #if !defined (INT_USED) + + // create a user task that fetches data from sensor periodically + xTaskCreate(user_task_periodic, "user_task_periodic", TASK_STACK_DEPTH, NULL, 2, NULL); + + #else // INT_USED + + // create a task that is triggered only in case of interrupts to fetch the data + xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL); + + // create event queue + 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_set_interrupt(INT1_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler); + + #endif // !defined(INT_USED) + + // -- SENSOR CONFIGURATION PART --- + + // Interrupt configuration has to be done before the sensor is set + // into measurement mode + + // set polarity of INT signals if necessary + // lis3dh_config_int_signals (sensor, lis3dh_high_active); + + #ifdef INT_DATA + // enable data interrupts on INT1 (data ready or FIFO status interrupts) + // data ready and FIFO status interrupts must not be enabled at the same time + #ifdef FIFO_MODE + lis3dh_enable_int (sensor, lis3dh_int_fifo_overrun , lis3dh_int1_signal, true); + lis3dh_enable_int (sensor, lis3dh_int_fifo_watermark, lis3dh_int1_signal, true); + #else + lis3dh_enable_int (sensor, lis3dh_int_data_ready, lis3dh_int1_signal, true); + #endif // FIFO_MODE + #endif // INT_DATA + + #ifdef INT_EVENT + // enable data interrupts on INT1 + lis3dh_int_event_config_t event_config; + + event_config.mode = lis3dh_wake_up; + // event_config.mode = lis3dh_free_fall; + // event_config.mode = lis3dh_6d_movement; + // event_config.mode = lis3dh_6d_position; + // event_config.mode = lis3dh_4d_movement; + // event_config.mode = lis3dh_4d_position; + event_config.threshold = 10; + event_config.x_low_enabled = false; + event_config.x_high_enabled = true; + event_config.y_low_enabled = false; + event_config.y_high_enabled = true; + event_config.z_low_enabled = false; + event_config.z_high_enabled = true; + event_config.duration = 0; + event_config.latch = true; + + lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event1_gen); + lis3dh_enable_int (sensor, lis3dh_int_event1, lis3dh_int1_signal, true); + #endif // INT_EVENT + + #ifdef INT_CLICK + // enable click interrupt on INT1 + lis3dh_int_click_config_t click_config; + + click_config.threshold = 10; + click_config.x_single = false; + click_config.x_double = false; + click_config.y_single = false; + click_config.y_double = false; + click_config.z_single = true; + click_config.z_double = false; + click_config.latch = true; + click_config.time_limit = 1; + click_config.time_latency = 1; + click_config.time_window = 3; + + lis3dh_set_int_click_config (sensor, &click_config); + lis3dh_enable_int (sensor, lis3dh_int_click, lis3dh_int1_signal, true); + #endif // INT_CLICK + + #ifdef FIFO_MODE + // clear FIFO and activate FIFO mode if needed + lis3dh_set_fifo_mode (sensor, lis3dh_bypass, 0, lis3dh_int1_signal); + lis3dh_set_fifo_mode (sensor, lis3dh_stream, 10, lis3dh_int1_signal); + #endif + + // configure HPF and reset the reference by dummy read + lis3dh_config_hpf (sensor, lis3dh_hpf_normal, 0, true, true, true, true); + lis3dh_get_hpf_ref (sensor); + + // enable ADC inputs and temperature sensor for ADC input 3 + lis3dh_enable_adc (sensor, true, true); + + // LAST STEP: Finally set scale and mode to start measurements + lis3dh_set_scale(sensor, lis3dh_scale_2g); + lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); + + // -- SENSOR CONFIGURATION PART --- + } +} + diff --git a/extras/lis3dh/README.md b/extras/lis3dh/README.md new file mode 100644 index 0000000..bd59be6 --- /dev/null +++ b/extras/lis3dh/README.md @@ -0,0 +1,1023 @@ +# Driver for the LIS3DH 3-axes digital output accelerometer + +The driver is for the usage with the ESP8266 and [esp-open-rtos](https://github.com/SuperHouse/esp-open-rtos). If you can't find it in folder [extras/lis3dh](https://github.com/SuperHouse/esp-open-rtos/tree/master/extras) of original repository, it is not yet merged. Please take a look to branch [lis3dh](https://github.com/gschorcht/esp-open-rtos/tree/lis3dh) of my fork in that case. + +It is also working with ESP32 and [ESP-IDF](https://github.com/espressif/esp-idf.git) 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 also be used with LIS3DE, LIS2DH, LIS2DH12, LIS2DE, and LIS2DE12 + +## About the sensor + +LIS3DH is a low-power high performance **3-axis accelerometer sensor** connected to **I2C** or **SPI** with a full scale of up to **±16 g**. It supports different measuring rates. + +**Main features** of the sensor are: + +- 4 selectable full scales of ±2 g, ±4 g, ±8 g, and ±16 g +- 9 measuring rates from 1 Hz to 5 kHz +- 16 bit accelerometer value data output +- 2 independent programmable interrupt generators for free-fall and motion detection +- integrated high-pass filters with 3 modes and 4 different cut off frequencies +- embedded temperature sensor +- embedded 32 levels of 16 bit data output FIFO +- 6D/4D orientation detection +- Free-fall detection +- Motion detection +- click/double click recognition +- I2C and SPI digital output interface + +## Sensor operation + +### Sensor modes + +LIS3DH provides different operating modes. + +- **Power Down mode** is configured automatically after power up boot sequence. In this mode, almost all internal blocks of the device are switched off. Register content is preserved, but there are no measurements performed. + +- **Normal mode** is the standard measurement mode. In this mode measurements are performed with a resolution of **10 bit** at the defined output data rate (**ODR**). + +- **Low-power mode** is the measurement mode with reduced power consumption. Measurements are performed with a resolution of only **8 bit** at the defined output data rate (**ODR**). + +- **High-resolution mode** is the measurement mode where measurements are performed with a resolution of 12 bit at the defined output data rate (**ODR**). Only output data rates (ODR) up to 400 Hz are available. + +Switching from any mode to any another mode with the exception of high-resolution mode takes only 1/ODR. Switching from any mode to the high-resolution mode takes 7/ODRs. + +### Output Data Rates + +In normal, low-power and high-resolution modes, measurements are performed at a defined output rate. Following output data rates (ODR) are supported in the different modes: + +Driver symbol | Normal mode
```lis3dh_normal``` | Low-power mode
```lis3dh_low_power``` | High-resolution mode
```lis3dh_high_res``` +:---------------------- |:------------:|:---------------:|:--------------------: +```lis3dh_power_down``` | Power down | Power down | Power down +```lis3dh_normal_1``` | 1 Hz | 1 Hz | 1 Hz +```lis3dh_normal_10``` | 10 Hz | 10 Hz | 10 Hz +```lis3dh_normal_25``` | 25 Hz | 25 Hz | 25 Hz +```lis3dh_normal_50``` | 50 Hz | 50 Hz | 50 Hz +```lis3dh_normal_100``` | 100 Hz | 100 Hz | 100 Hz +```lis3dh_normal_200``` | 200 Hz | 200 Hz | 200 Hz +```lis3dh_normal_400``` | 400 Hz | 400 Hz | 400 Hz +```lis3dh_normal_1600```| - | 1600 Hz | - +```lis3dh_normal_5000```| 1250 Hz | 5000 Hz | - + +The **easiest way to use the sensor** is simply to initialize it with function ```lis3dh_init_sensor``` and then set it to any measurement mode with function ```lis3dh_set_mode``` to start measurements with the given output data rate (ODR). + +``` +... +static lis3dh_sensor_t* sensor = 0; +... +if ((sensor = lis3dh_init_sensor (I2C_BUS, LIS3DH_I2C_ADDRESS_2, 0))) +{ + ... + lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true) + ... +} +... + +``` +In this example, a LIS3DH sensor connected to I2C is initialized and set to high-resolution mode to start measurements for all three axes with an output data rate (ODR) of 10 Hz. + +**Please note:** +- ```lis3dh_init_sensor``` function resets the sensor completely, switches it to the power down 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 function ```lis3dh_set_mode```. In particular, the interrupt configuration should be performed before to avoid loosing the first interrupt and locking the system. + +## Measurement results + +### Output data format + +The sensor determines periodically the accelerations for all axes that are enabled for measurement 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 and are always left-aligned. The resolution depends on the selected operation mode and the selected full scale. For example, in low power mode with 8-bit resolution only the high byte is used. LIS3DH allows to select the following full scales: + +Full Scale | Driver symbol | Resolution 12 bit
```lis3dh_high_res``` | Resolution 10 bit
```lis3dh_normal``` | Resolution 8 bit
```lis3dh_low_power``` +---------------------:|:-----------:|-----------:|---------------:|-----: + ±2 g | ```lis3dh_scale_2g``` | 1 mg | 4 mg | 16 mg + ±4 g | ```lis3dh_scale_4g``` | 2 mg | 8 mg | 32 mg + ±8 g | ```lis3dh_scale_8g``` | 4 mg | 16 mg | 64 mg +±16 g | ```lis3dh_scale_16g``` | 12 mg | 48 mg | 192 mg + +By default, a full scale of ±2 g is used. Function ```lis3dh_set_scale``` can be used to change it. + +``` +lis3dh_set_scale(sensor, lis3dh_scale_4g); +``` + +### Fetching output data + +To get the information whether new data are available, the user task can either use + +- the function ```lis3dh_new_data``` to check periodically whether new output data are available, or +- the data ready interrupt (DRDY) which is thrown as soon as new output data are available (see below). + +Last measurement results can then be fetched either + +- as raw data using function ```lis3dh_get_raw_data``` or +- as floating point values in g using function ```lis3dh_get_float_data```. + +It is recommended to use function ```lis3dh_get_float_data``` since it already converts measurement results to real values according to the selected full scale. + +``` +void user_task_periodic(void *pvParameters) +{ + lis3dh_float_data_t data; + + while (1) + { + // execute task every 10 ms + vTaskDelay (10/portTICK_PERIOD_MS); + ... + // test for new data + if (!lis3dh_new_data (sensor)) + continue; + + // fetch new data + if (lis3dh_get_float_data (sensor, &data)) + { + // do something with data + ... + } + } +} +``` + +**Please note:** +Functions ```lis3dh_get_float_data``` and ```lis3dh_get_raw_data``` 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. + +### High pass filtering + +LIS3DH provides embedded high-pass filtering capabilities to improve measurement results. Please refer the [datasheet](http://www.st.com/resource/en/datasheet/lis3dh.pdf) or [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more details. + +The high pass filter can independently apply to + +- the raw output data, +- the data used for click detection, and +- the data used for inertial interrupt generation like wake-up, free fall or 6D/4D orientation detection. + +The mode and the cutoff frequency of the high pass filter can be configured using function ```lis3dh_config_hpf```. Following HPF modes are available: + +Driver symbol | HPF mode +:--------------|:--------- +```lis3dh_hpf_normal``` | Normal mode +```lis3dh_hpf_reference``` | Reference mode +```lis3dh_hpf_autoreset``` | Auto-reset on interrupt + +For each output data rate (ODR), 4 different HPF cutoff frequencies can be used. Furthermore, a number of boolean parameters indicate to which data the HPF is applied. + +``` +... +// configure HPF +lis3dh_config_hpf (sensor, lis3dh_hpf_normal, 0, true, true, true, true); + +// reset the reference by dummy read +lis3dh_get_hpf_ref (sensor); +... +``` + +### FIFO + +In order to limit the rate at which the host processor has to fetch the data, the LIS3DH 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](http://www.st.com/resource/en/datasheet/lis3dh.pdf) or [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more details. + +Driver symbol | FIFO mode +--------------|------------------------- +```lis3dh_bypass``` | Bypass mode (FIFO is not used) +```lis3dh_fifo``` | FIFO mode +```lis3dh_stream``` | Stream mode +```lis3dh_stream_to_fifo``` | Stream-to-FIFO mode + +The FIFO mode can be set using function ```lis3dh_set_fifo_mode```. This function takes three parameters + +- the FIFO mode, +- a threshold value which defines a watermark level, and +- an interrupt source that is used in Stream-to-FIFO mode. + +The watermark level is used by the sensor to set a watermark flag and to generate optionally an interrupt when the FIFO content exceeds this level. They can be used to gather a minimum number of axes acceleration samples with the sensor before the data are fetched as a single read operation from the sensor. + +``` +... +// clear FIFO +lis3dh_set_fifo_mode (sensor, lis3dh_bypass, 0, lis3dh_int1_signal); + +// activate FIFO mode +lis3dh_set_fifo_mode (sensor, lis3dh_stream, 10, lis3dh_int1_signal); +... +``` + +**Please note**: To clear the FIFO at any time, set the FIFO mode to ```lis3dh_bypass``` and back to the desired FIFO mode. + +To read data from the FIFO, simply use either + +- the function ```lis3dh_get_raw_data_fifo``` to all get raw output data stored in FIFO or +- the function ```lis3dh_get_float_data_fifo``` to get all data stored in FIFO and converted to real values in g. + +Both functions clear the FIFO and return the number of samples read from the FIFO. + +``` +void user_task_periodic (void *pvParameters) +{ + lis3dh_float_data_fifo_t data; + + while (1) + { + // execute task every 500 ms + vTaskDelay (500/portTICK_PERIOD_MS); + ... + // test for new data + if (!lis3dh_new_data (sensor)) + continue; + + // fetch data from fifo + uint8_t num = lis3dh_get_float_data_fifo (sensor, data); + + for (int i = 0; i < num; i++) + { + // do something with data[i] ... + } +} +``` + +## Interrupts + +The LIS3DH supports two dedicated interrupt signals **```INT1```** and **```INT2```** and three different types of interrupts: + +- **data** interrupts (data ready and FIFO status), +- **inertial event** interrupts (axis movement, wake-up, free fall, and 6D/4D orientation detection), and +- **click detection** interrupts. + +While inertial event interrupts and click detection interrupts can be configured for both interrupt signals, data ready and FIFO status interrupts can be configured only for interrupt signal ```INT1```. + +### Data interrupts (data ready and FIFO status) + +Following sources can generate an interrupt on signal ```INT1```: + +Interrupt source | Driver symbol +:-----------------|:------------- +Output data become ready to read | ```lis3dh_int_data_ready``` +FIFO content exceeds the watermark level | ```lis3dh_int_fifo_watermark``` +FIFO is completely filled | ```lis3dh_int_fifo_overrun``` + +Each of these interrupt sources can be enabled or disabled separately with function ```lis3dh_enable_int```. By default all interrupt sources are disabled. + +``` +lis3dh_enable_int (sensor, lis3dh_int_data_ready, lis3dh_int1_signal, true); +``` + +Whenever an interrupt is generated at interrupt signal ```INT1```, the function ```lis3dh_get_int_data_source``` can be used to determine the source of the interrupt. This function returns a data structure of type ```lis3dh_int_data_source_t``` that contain a boolean member for each source that can be tested for true. + +``` +void int1_handler () +{ + lis3dh_int_data_source_t data_src; + + // get the source of the interrupt on *INT1* signal + lis3dh_get_int_data_source (sensor, &data_src); + + // in case of data ready interrupt, get the results and do something with them + if (data_src.data_ready) + ... // read data + + // in case of FIFO interrupts read the whole FIFO + else if (data_src.fifo_watermark || data_src.fifo_overrun) + ... // read FIFO data + ... +} +``` + +### Inertial event interrupts + +Inertial interrupt generators allow to generate interrupts when certain inertial event occures (event interrupts), that is, the acceleration of defined axes is higher or lower than a defined threshold. If activated, the acceleration of each axis is compared with a defined threshold to check whether it is below or above the threshold. The results of all activated comparisons are then combined OR or AND to generate the interrupt signal. + +The configuration of the threshold valid for all axes, the activated comparisons and the selected AND/OR combination allows to recognize special situations: + +- **Wake-up detection** refers the special condition that the acceleration measured along any axis is above the defined threshold (```lis3dh_wake_up```). +- **Free fall detection** refers the special condition that the acceleration measured along all the axes goes to zero (```lis3dh_free_fall```). +- **6D/4D orientation detection** refers to the special condition that the measured acceleration along certain axes is above and along the other axes is below the threshold which indicates a particular orientation (```lis3dh_6d_movement```, ```lis3dh_6d_position```, ```lis3dh_4d_movement```, ```lis3dh_4d_position```). + +Inertial event interrupts can be configured with the function ```lis3dh_get_int_event_config```. This function requires as parameters the configuration of type ```lis3dh_int_event_config_t``` and the interrupt generator to be used for inertial event interrupts. + +Inertial event interrupts have to be enabled or disabled using function ```lis3dh_enable_int```. The interrupt signal on which the interrupts are generated is given as parameter. + +For example, axis movement detection interrupt generated by inertial interrupt generator 2 on signal ```INT1``` could be configured as following: + +``` +lis3dh_int_event_config_t event_config; + +event_config.mode = lis3dh_wake_up; +event_config.threshold = 10; +event_config.x_low_enabled = false; +event_config.x_high_enabled = true; +event_config.y_low_enabled = false; +event_config.y_high_enabled = true; +event_config.z_low_enabled = false; +event_config.z_high_enabled = true; + +event_config.duration = 0; +event_config.latch = true; + +lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event2_gen); +lis3dh_enable_int (sensor, lis3dh_int_event1, lis3dh_int1_signal, true); +``` + +The parameter of type ```lis3dh_int_event_config_t``` also configures + +- whether the interrupt should be latched until the interrupt source is read, and +- which time given in 1/ODR an interrupt condition has to be given before the interrupt is generated. + +As with data ready and FIFO status interrupts, function ```lis3dh_get_int_event_source``` can be used to determine the source of an inertial event interrupt whenever it is generated. This function returns a data structure of type ```lis3dh_int_event_source_t``` which contains a boolean member for each source that can be tested for true. + +``` +void int1_handler () +{ + lis3dh_int_data_source_t data_src; + lis3dh_int_event_source_t event_src; + + // get the source of the interrupt on *INT1* signal + lis3dh_get_int_data_source (sensor, &data_src); + lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event2_gen); + + // in case of data ready interrupt, get the results and do something with them + if (data_src.data_ready) + ... // read data + + // in case of FIFO interrupts read the whole FIFO + else if (data_src.fifo_watermark || data_src.fifo_overrun) + ... // read FIFO data + + // in case of inertial event interrupt + else if (event_src.active) + ... // read data + ... +} +``` + +**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 satisfied. + +**Please note** Activating all threshold comparisons and the OR combination (```lis3dh_wake_up```) is the most flexible way to deal with inertial event interrupts. Functions such as free fall detection and so on can then be realized by suitably combining the various interrupt sources by the user task. Following example realizes the free fall detection in user task. + +``` +lis3dh_int_event_config_t event_config; + +event_config.mode = lis3dh_wake_up; +event_config.threshold = 10; +event_config.x_low_enabled = true; +event_config.x_high_enabled = true; +event_config.y_low_enabled = true; +event_config.y_high_enabled = true; +event_config.z_low_enabled = true; +event_config.z_high_enabled = true; + +event_config.duration = 0; +event_config.latch = true; + +lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event2_gen); +lis3dh_enable_int (sensor, lis3dh_int_event1, lis3dh_int1_signal, true); +``` + +``` +void int1_handler () +{ + lis3dh_int_event_source_t event_src; + + // get the source of the interrupt from interrupt generator 2 on *INT1* signal + lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event2_gen); + + // test for free fall condition (all accelerations are below the threshold) + if (event_src.x_low && event_src.y_low && event_src.z_low) + ... // do something + ... +} + +``` + +### Click detection interrupts + +A sequence of acceleration values over time measured along certain axes can be used to detect single and double clicks. Please refer the [datasheet](http://www.st.com/resource/en/datasheet/lis3dh.pdf) or [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more information. + +Click detection interrupts are configured using the ``` lis3dh_set_int_click_config``` function. This function requires the configuration of type ```lis3dh_int_click_config_t``` as parameter. The interrupt has to be activated or deactivated using the ```lis3dh_enable_int``` function with the interrupt signal on which the interrupts are generated as parameter. + +In following example, the single click detection for z-axis is enabled with a time limit of 1/ODR, a time latency of 1/ODR and a time window of 3/ODR. + +``` +lis3dh_int_click_config_t click_config; + +click_config.threshold = 10; +click_config.x_single = false; +click_config.x_double = false; +click_config.y_single = false; +click_config.y_double = false; +click_config.z_single = true; +click_config.z_double = false; +click_config.latch = true; +click_config.time_limit = 1; +click_config.time_latency = 1; +click_config.time_window = 3; + +lis3dh_set_int_click_config (sensor, &click_config); +lis3dh_enable_int (sensor, lis3dh_int_click, lis3dh_int1_signal, true); +``` + +Please refer the [application note](http://www.st.com/resource/en/application_note/cd00290365.pdf) for more information about the configuration parameters. + +As with other interrupts, the function ```lis3dh_get_int_click_source``` can be used to determine the source of the interrupt signal whenever it is generated. This function returns a data structure of type ```lis3dh_int_click_source_t``` that contains a boolean member for each source that can be tested for true. + +``` +void int1_handler () +{ + lis3dh_int_click_source_t click_src; + + // get the source of the interrupt on *INT1* signal + lis3dh_get_int_click_source (sensor, &click_src); + + // detect single click along z-axis + if (click_src.z_click && click_src.s_click) + ... // 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 satisfied. + +### Interrupt signal properties + +By default, interrupt signals are high active. Using function ```lis3dh_config_int_signals```, the level of the interrupt signal can be changed. + +Driver symbol | Meaning +:-------------|:------- +```lis3dh_high_active``` | Interrupt signal is high active (default) +```lis3dh_low_active``` | Interrupt signal is low active + + +## Analog inputs and temperature sensor + +The LIS3DH sensor contains an auxiliary ADC with 3 separate dedicated inputs ADC1, ADC2, and ADC3. ADC3 can be connected to the internal temperatur sensor. The input range is 1200 ± 400 mV. The resolution of the A/D converter is 10 bit in normal and high-resolution mode, but only 8 bit in low-power mode. + +ADC inputs can be activated and deactivated (default) with function ```lis3dh_enable_adc```. If parameter ```temp``` is true, ADC3 is connected to the internal temperature sensor and provides the temperature in degrees. + +ADC sampling rate is the same the output data rate (ODR). Results are given as left-aligned 16-bit signed integer values in 2’s complement. Function ```lis3dh_get_adc``` can be used to get the results. + +## Low level functions + +The LIS3DH is a very complex and flexible sensor with a lot of features. It can be used for a big 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 lis3dh_reg_read (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); +bool lis3dh_reg_write (lis3dh_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. + + +## Usage + +First, the hardware configuration has to be established. + +### Hardware configurations + +Following figure shows a possible hardware configuration for ESP8266 and ESP32 if I2C interface is used to connect the sensor. + +``` + +-----------------+ +----------+ + | ESP8266 / ESP32 | | LIS3DH | + | | | | + | GPIO 14 (SCL) >-----> SCL | + | GPIO 13 (SDA) <-----> SDA | + | GPIO 5 <------ INT1 | + | GPIO 4 <------ INT2 | + +-----------------+ +----------+ +``` + +If SPI interface is used, configuration for ESP8266 and ESP32 could look like following. + +``` + +-----------------+ +----------+ +-----------------+ +----------+ + | ESP8266 | | LIS3DH | | ESP32 | | LIS3DH | + | | | | | | | | + | GPIO 14 (SCK) ------> SCK | | GPIO 16 (SCK) ------> SCK | + | GPIO 13 (MOSI)------> SDI | | GPIO 17 (MOSI)------> SDI | + | GPIO 12 (MISO)<------ SDO | | GPIO 18 (MISO)<------ SDO | + | GPIO 2 (CS) ------> CS | | GPIO 19 (CS) ------> CS | + | GPIO 5 <------ INT1 | | GPIO 5 <------ INT1 | + | GPIO 4 <------ INT2 | | GPIO 5 <------ INT2 | + +-----------------+ +----------+ +-----------------+ +----------+ +``` + +### Communication interface settings + +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 + +// SPI interface definitions for ESP32 +#define SPI_BUS HSPI_HOST +#define SPI_SCK_GPIO 16 +#define SPI_MOSI_GPIO 17 +#define SPI_MISO_GPIO 18 +#define SPI_CS_GPIO 19 + +#else // ESP8266 (esp-open-rtos) + +// user task stack depth for ESP8266 +#define TASK_STACK_DEPTH 256 + +// SPI interface definitions for ESP8266 +#define SPI_BUS 1 +#define SPI_SCK_GPIO 14 +#define SPI_MOSI_GPIO 13 +#define SPI_MISO_GPIO 12 +#define SPI_CS_GPIO 2 // GPIO 15, the default CS of SPI bus 1, can't be used + +#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_100K + +// interrupt GPIOs defintions for ESP8266 and ESP32 +#define INT1_PIN 5 +#define INT2_PIN 4 +``` + +### Main program + +#### Initialization + +If I2C interfaces are used, they have to be initialized first. + +``` +i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ); +``` + +SPI interface has only to be initialized explicitly on ESP32 platform to declare the GPIOs that are used for SPI interface. + +``` +spi_bus_init (SPI_BUS, SPI_SCK_GPIO, SPI_MISO_GPIO, SPI_MOSI_GPIO); +``` + +Once the interfaces are initialized, function ```lis3dh_init_sensor``` has to be called for each LIS3DH 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 or SPI bus to which the sensor is connected. + +``` +static lis3dh_sensor_t* sensor; +``` + +For sensors connected to an I2C interface, a valid I2C slave address has to be defined as parameter *addr*. In that case parameter *cs* is ignored. + +``` +sensor = lis3dh_init_sensor (I2C_BUS, LIS3DH_I2C_ADDRESS_1, 0); + +``` + +If parameter *addr* is 0, the sensor is connected to a SPI bus. In that case, parameter *cs* defines the GPIO used as CS signal. + +``` +sensor = lis3dh_init_sensor (SPI_BUS, 0, SPI_CS_GPIO); + +``` + +The remaining of the program is independent on the communication interface. + +#### Periodic user task + +If initialization of the sensor was successful, the user task that uses the sensor has to be created. 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 a configured event happens. + +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) +{ + lis3dh_float_data_t data; + + while (1) + { + // execute task every 10 ms + vTaskDelay (10/portTICK_PERIOD_MS); + ... + // test for new data + if (!lis3dh_new_data (sensor)) + continue; + + // fetch new data + if (lis3dh_get_float_data (sensor, &data)) + { + // do something with data + ... + } + } +} +... +// 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 rate higher than the output data rate (ODR) of the sensor whether new data are available. If new data are available, it fetches the data. + +#### Interrupt user task + +A different approach is to use one of the **interrupts** INT1 or INT2. In this case, the user has to implement an interrupt handler that either fetches the data directly or triggers a task, that is waiting to fetch the data. + +``` +static QueueHandle_t gpio_evt_queue = NULL; + +// Interrupt handler which resumes sends an event to the waiting user_task_interrupt + +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 fetches the sensor values + +void user_task_interrupt (void *pvParameters) +{ + uint32_t gpio_num; + + while (1) + { + if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY)) + { + // test for new data + if (!lis3dh_new_data (sensor)) + continue; + + // fetch new data + if (lis3dh_get_float_data (sensor, &data)) + { + // do something with data + ... + } + } + } +} +... + +// create a task that is triggered only in case of interrupts to fetch the data + +xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL); +... +``` + +In this example, there is + +- a task that is fetching data when it receives an event, and +- an interrupt handler that generates the event on interrupt. + +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 LIS3DH sensor, see section **Interrupts** above. + +#### Configuring the sensor + +Optionally, you could wish to set some measurement parameters. For details see the sections above, the header file of the driver ```lis3dh.h```, and of course the data sheet of the sensor. + +#### Starting measurements + +As last step, the sensor mode has be set to start periodic measurement. The sensor mode can be changed anytime later. + +``` +... +// start periodic measurement with output data rate of 10 Hz +lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); +... +``` + +## Full Example + +``` +/* -- use following constants to define the example mode ----------- */ + +// #define SPI_USED // SPI interface is used, otherwise I2C +// #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 (wake-up, free fall or 6D/4D orientation) +// #define INT_CLICK // click detection interrupts used + +#if defined(INT_DATA) || defined(INT_EVENT) || defined(INT_CLICK) +#define INT_USED +#endif + +/* -- includes ----------------------------------------------------- */ + +#include "lis3dh.h" + +/** -- platform dependent definitions ------------------------------ */ + +#ifdef ESP_PLATFORM // ESP32 (ESP-IDF) + +// user task stack depth for ESP32 +#define TASK_STACK_DEPTH 2048 + +// SPI interface definitions for ESP32 +#define SPI_BUS HSPI_HOST +#define SPI_SCK_GPIO 16 +#define SPI_MOSI_GPIO 17 +#define SPI_MISO_GPIO 18 +#define SPI_CS_GPIO 19 + +#else // ESP8266 (esp-open-rtos) + +// user task stack depth for ESP8266 +#define TASK_STACK_DEPTH 256 + +// SPI interface definitions for ESP8266 +#define SPI_BUS 1 +#define SPI_SCK_GPIO 14 +#define SPI_MOSI_GPIO 13 +#define SPI_MISO_GPIO 12 +#define SPI_CS_GPIO 2 // GPIO 15, the default CS of SPI bus 1, can't be used + +#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_100K + +// interrupt GPIOs defintions for ESP8266 and ESP32 +#define INT1_PIN 5 +#define INT2_PIN 4 + +/* -- user tasks --------------------------------------------------- */ + +static lis3dh_sensor_t* sensor; + +/** + * Common function used to get sensor data. + */ +void read_data () +{ + #ifdef FIFO_MODE + + lis3dh_float_data_fifo_t fifo; + + if (lis3dh_new_data (sensor)) + { + uint8_t num = lis3dh_get_float_data_fifo (sensor, fifo); + + printf("%.3f LIS3DH 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 LIS3DH (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 + + lis3dh_float_data_t data; + + if (lis3dh_new_data (sensor) && + lis3dh_get_float_data (sensor, &data)) + // max. full scale is +-16 g and best resolution is 1 mg, i.e. 5 digits + printf("%.3f LIS3DH (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 +/** + * In this case, any of the possible interrupts on interrupt signal *INT1* is + * used to fetch the data. + * + * When interrupts are used, the user has to define interrupt handlers that + * either fetches the data directly or triggers a task which is waiting to + * fetch the data. In this example, the interrupt handler sends an event to + * a waiting task to trigger the data gathering. + */ + +static QueueHandle_t gpio_evt_queue = NULL; + +// User task that fetches the sensor values. + +void user_task_interrupt (void *pvParameters) +{ + uint8_t gpio_num; + + while (1) + { + if (xQueueReceive(gpio_evt_queue, &gpio_num, portMAX_DELAY)) + { + lis3dh_int_data_source_t data_src = {}; + lis3dh_int_event_source_t event_src = {}; + lis3dh_int_click_source_t click_src = {}; + + // get the source of the interrupt and reset *INTx* signals + #ifdef INT_DATA + lis3dh_get_int_data_source (sensor, &data_src); + #elif INT_EVENT + lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event1_gen); + #elif INT_CLICK + lis3dh_get_int_click_source (sensor, &click_src); + #endif + + // in case of DRDY interrupt or inertial event interrupt read one data sample + if (data_src.data_ready) + read_data (); + + // in case of FIFO interrupts read the whole FIFO + else if (data_src.fifo_watermark || data_src.fifo_overrun) + read_data (); + + // in case of event interrupt + else if (event_src.active) + { + printf("%.3f LIS3DH ", (double)sdk_system_get_time()*1e-3); + if (event_src.x_low) printf("x is lower than threshold\n"); + if (event_src.y_low) printf("y is lower than threshold\n"); + if (event_src.z_low) printf("z is lower than threshold\n"); + if (event_src.x_high) printf("x is higher than threshold\n"); + if (event_src.y_high) printf("y is higher than threshold\n"); + if (event_src.z_high) printf("z is higher than threshold\n"); + } + + // in case of click detection interrupt + else if (click_src.active) + printf("%.3f LIS3DH %s\n", (double)sdk_system_get_time()*1e-3, + click_src.s_click ? "single click" : "double click"); + } + } +} + +// Interrupt handler which resumes user_task_interrupt on interrupt + +void IRAM int_signal_handler (uint8_t gpio) +{ + // send an event with GPIO to the interrupt user task + xQueueSendFromISR(gpio_evt_queue, &gpio, NULL); +} + +#else // !INT_USED + +/* + * In this example, user task fetches the sensor values every seconds. + */ + +void user_task_periodic(void *pvParameters) +{ + vTaskDelay (100/portTICK_PERIOD_MS); + + while (1) + { + // read sensor data + read_data (); + + // passive waiting until 1 second is over + vTaskDelay(100/portTICK_PERIOD_MS); + } +} + +#endif // INT_USED + +/* -- 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 = lis3dh_init_sensor (SPI_BUS, 0, SPI_CS_GPIO); + + #else + + // init all I2C bus interfaces at which LIS3DH sensors are connected + i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ); + + // init the sensor with slave address LIS3DH_I2C_ADDRESS_1 connected to I2C_BUS. + sensor = lis3dh_init_sensor (I2C_BUS, LIS3DH_I2C_ADDRESS_1, 0); + + #endif + + if (sensor) + { + // --- SYSTEM CONFIGURATION PART ---- + + #if !defined (INT_USED) + + // create a user task that fetches data from sensor periodically + xTaskCreate(user_task_periodic, "user_task_periodic", TASK_STACK_DEPTH, NULL, 2, NULL); + + #else // INT_USED + + // create a task that is triggered only in case of interrupts to fetch the data + xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL); + + // create event queue + 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_set_interrupt(INT1_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler); + + #endif // !defined(INT_USED) + + // -- SENSOR CONFIGURATION PART --- + + // Interrupt configuration has to be done before the sensor is set + // into measurement mode + + // set polarity of INT signals if necessary + // lis3dh_config_int_signals (sensor, lis3dh_high_active); + + #ifdef INT_DATA + // enable data interrupts on INT1 (data ready or FIFO status interrupts) + // data ready and FIFO status interrupts must not be enabled at the same time + #ifdef FIFO_MODE + lis3dh_enable_int (sensor, lis3dh_int_fifo_overrun , lis3dh_int1_signal, true); + lis3dh_enable_int (sensor, lis3dh_int_fifo_watermark, lis3dh_int1_signal, true); + #else + lis3dh_enable_int (sensor, lis3dh_int_data_ready, lis3dh_int1_signal, true); + #endif // FIFO_MODE + #endif // INT_DATA + + #ifdef INT_EVENT + // enable data interrupts on INT1 + lis3dh_int_event_config_t event_config; + + event_config.mode = lis3dh_wake_up; + // event_config.mode = lis3dh_free_fall; + // event_config.mode = lis3dh_6d_movement; + // event_config.mode = lis3dh_6d_position; + // event_config.mode = lis3dh_4d_movement; + // event_config.mode = lis3dh_4d_position; + event_config.threshold = 10; + event_config.x_low_enabled = false; + event_config.x_high_enabled = true; + event_config.y_low_enabled = false; + event_config.y_high_enabled = true; + event_config.z_low_enabled = false; + event_config.z_high_enabled = true; + event_config.duration = 0; + event_config.latch = true; + + lis3dh_set_int_event_config (sensor, &event_config, lis3dh_int_event1_gen); + lis3dh_enable_int (sensor, lis3dh_int_event1, lis3dh_int1_signal, true); + #endif // INT_EVENT + + #ifdef INT_CLICK + // enable click interrupt on INT1 + lis3dh_int_click_config_t click_config; + + click_config.threshold = 10; + click_config.x_single = false; + click_config.x_double = false; + click_config.y_single = false; + click_config.y_double = false; + click_config.z_single = true; + click_config.z_double = false; + click_config.latch = true; + click_config.time_limit = 1; + click_config.time_latency = 1; + click_config.time_window = 3; + + lis3dh_set_int_click_config (sensor, &click_config); + lis3dh_enable_int (sensor, lis3dh_int_click, lis3dh_int1_signal, true); + #endif // INT_CLICK + + #ifdef FIFO_MODE + // clear FIFO and activate FIFO mode if needed + lis3dh_set_fifo_mode (sensor, lis3dh_bypass, 0, lis3dh_int1_signal); + lis3dh_set_fifo_mode (sensor, lis3dh_stream, 10, lis3dh_int1_signal); + #endif + + // configure HPF and reset the reference by dummy read + lis3dh_config_hpf (sensor, lis3dh_hpf_normal, 0, true, true, true, true); + lis3dh_get_hpf_ref (sensor); + + // enable ADC inputs and temperature sensor for ADC input 3 + lis3dh_enable_adc (sensor, true, true); + + // LAST STEP: Finally set scale and mode to start measurements + lis3dh_set_scale(sensor, lis3dh_scale_2g); + lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); + + // -- SENSOR CONFIGURATION PART --- + } +} + +``` diff --git a/extras/lis3dh/component.mk b/extras/lis3dh/component.mk new file mode 100644 index 0000000..0b732ad --- /dev/null +++ b/extras/lis3dh/component.mk @@ -0,0 +1,10 @@ +# Component makefile for extras/lis3dh + +# expected anyone using SHT3x driver includes it as 'lis3dh/lis3dh.h' +INC_DIRS += $(lis3dh_ROOT).. +INC_DIRS += $(lis3dh_ROOT) + +# args for passing into compile rule generation +lis3dh_SRC_DIR = $(lis3dh_ROOT) + +$(eval $(call component_compile_rules,lis3dh)) diff --git a/extras/lis3dh/lis3dh.c b/extras/lis3dh/lis3dh.c new file mode 100644 index 0000000..7742024 --- /dev/null +++ b/extras/lis3dh/lis3dh.c @@ -0,0 +1,1274 @@ +/* + * Driver for LIS3DH 3-axes digital accelerometer connected to I2C or SPI. + * + * This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos) + * [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32 + * and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux + * based systems using a wrapper library for ESP8266 functions. + * + * --------------------------------------------------------------------------- + * + * The BSD License (3-clause license) + * + * Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder nor the names of its + * 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 THE 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. + */ + +#include +#include + +#include "lis3dh.h" + +#if defined(LIS3DH_DEBUG_LEVEL_2) +#define debug(s, f, ...) printf("%s %s: " s "\n", "LIS3DH", f, ## __VA_ARGS__) +#define debug_dev(s, f, d, ...) printf("%s %s: bus %d, addr %02x - " s "\n", "LIS3DH", f, d->bus, d->addr, ## __VA_ARGS__) +#else +#define debug(s, f, ...) +#define debug_dev(s, f, d, ...) +#endif + +#if defined(LIS3DH_DEBUG_LEVEL_1) || defined(LIS3DH_DEBUG_LEVEL_2) +#define error(s, f, ...) printf("%s %s: " s "\n", "LIS3DH", f, ## __VA_ARGS__) +#define error_dev(s, f, d, ...) printf("%s %s: bus %d, addr %02x - " s "\n", "LIS3DH", f, d->bus, d->addr, ## __VA_ARGS__) +#else +#define error(s, f, ...) +#define error_dev(s, f, d, ...) +#endif + +// register addresses +#define LIS3DH_REG_STATUS_AUX 0x07 +#define LIS3DH_REG_OUT_ADC1_L 0x08 +#define LIS3DH_REG_OUT_ADC1_H 0x09 +#define LIS3DH_REG_OUT_ADC2_L 0x0a +#define LIS3DH_REG_OUT_ADC2_H 0x0b +#define LIS3DH_REG_OUT_ADC3_L 0x0c +#define LIS3DH_REG_OUT_ADC3_H 0x0d +#define LIS3DH_REG_INT_COUNTER 0x0e +#define LIS3DH_REG_WHO_AM_I 0x0f +#define LIS3DH_REG_TEMP_CFG 0x1f +#define LIS3DH_REG_CTRL1 0x20 +#define LIS3DH_REG_CTRL2 0x21 +#define LIS3DH_REG_CTRL3 0x22 +#define LIS3DH_REG_CTRL4 0x23 +#define LIS3DH_REG_CTRL5 0x24 +#define LIS3DH_REG_CTRL6 0x25 +#define LIS3DH_REG_REFERENCE 0x26 +#define LIS3DH_REG_STATUS 0x27 +#define LIS3DH_REG_OUT_X_L 0x28 +#define LIS3DH_REG_OUT_X_H 0x29 +#define LIS3DH_REG_OUT_Y_L 0x2a +#define LIS3DH_REG_OUT_Y_H 0x2b +#define LIS3DH_REG_OUT_Z_L 0x2c +#define LIS3DH_REG_OUT_Z_H 0x2d +#define LIS3DH_REG_FIFO_CTRL 0x2e +#define LIS3DH_REG_FIFO_SRC 0x2f +#define LIS3DH_REG_INT1_CFG 0x30 +#define LIS3DH_REG_INT1_SRC 0x31 +#define LIS3DH_REG_INT1_THS 0x32 +#define LIS3DH_REG_INT1_DUR 0x33 +#define LIS3DH_REG_INT2_CFG 0x34 +#define LIS3DH_REG_INT2_SRC 0x35 +#define LIS3DH_REG_INT2_THS 0x36 +#define LIS3DH_REG_INT2_DUR 0x37 +#define LIS3DH_REG_CLICK_CFG 0x38 +#define LIS3DH_REG_CLICK_SRC 0x39 +#define LIS3DH_REG_CLICK_THS 0x3a +#define LIS3DH_REG_TIME_LIMIT 0x3b +#define LIS3DH_REG_TIME_LATENCY 0x3c +#define LIS3DH_REG_TIME_WINDOW 0x3d + +// register structure definitions +struct lis3dh_reg_status +{ + uint8_t XDA :1; // STATUS<0> X axis new data available + uint8_t YDA :1; // STATUS<1> Y axis new data available + uint8_t ZDA :1; // STATUS<2> Z axis new data available + uint8_t ZYXDA :1; // STATUS<3> X, Y and Z axis new data available + uint8_t XOR :1; // STATUS<4> X axis data overrun + uint8_t YOR :1; // STATUS<5> Y axis data overrun + uint8_t ZOR :1; // STATUS<6> Z axis data overrun + uint8_t ZYXOR :1; // STATUS<7> X, Y and Z axis data overrun +}; + +#define LIS3DH_ANY_DATA_READY 0x0f // LIS3DH_REG_STATUS<3:0> + +struct lis3dh_reg_ctrl1 +{ + uint8_t Xen :1; // CTRL1<0> X axis enable + uint8_t Yen :1; // CTRL1<1> Y axis enable + uint8_t Zen :1; // CTRL1<2> Z axis enable + uint8_t LPen :1; // CTRL1<3> Low power mode enable + uint8_t ODR :4; // CTRL1<7:4> Data rate selection +}; + +struct lis3dh_reg_ctrl2 +{ + uint8_t HPIS1 :1; // CTRL2<0> HPF enabled for AOI on INT2 + uint8_t HPIS2 :1; // CTRL2<1> HPF enabled for AOI on INT2 + uint8_t HPCLICK :1; // CTRL2<2> HPF enabled for CLICK + uint8_t FDS :1; // CTRL2<3> Filter data selection + uint8_t HPCF :2; // CTRL2<5:4> HPF cutoff frequency + uint8_t HPM :2; // CTRL2<7:6> HPF mode +}; + +struct lis3dh_reg_ctrl3 +{ + uint8_t unused :1; // CTRL3<0> unused + uint8_t I1_OVERRUN :1; // CTRL3<1> FIFO Overrun interrupt on INT1 + uint8_t I1_WTM1 :1; // CTRL3<2> FIFO Watermark interrupt on INT1 + uint8_t IT_DRDY2 :1; // CTRL3<3> DRDY2 (ZYXDA) interrupt on INT1 + uint8_t IT_DRDY1 :1; // CTRL3<4> DRDY1 (321DA) interrupt on INT1 + uint8_t I1_AOI2 :1; // CTRL3<5> AOI2 interrupt on INT1 + uint8_t I1_AOI1 :1; // CTRL3<6> AOI1 interrupt on INT1 + uint8_t I1_CLICK :1; // CTRL3<7> CLICK interrupt on INT1 +}; + +struct lis3dh_reg_ctrl4 +{ + uint8_t SIM :1; // CTRL4<0> SPI serial interface selection + uint8_t ST :2; // CTRL4<2:1> Self test enable + uint8_t HR :1; // CTRL4<3> High resolution output mode + uint8_t FS :2; // CTRL4<5:4> Full scale selection + uint8_t BLE :1; // CTRL4<6> Big/litle endian data selection + uint8_t BDU :1; // CTRL4<7> Block data update +}; + +struct lis3dh_reg_ctrl5 +{ + uint8_t D4D_INT2 :1; // CTRL5<0> 4D detection enabled on INT1 + uint8_t LIR_INT2 :1; // CTRL5<1> Latch interrupt request on INT1 + uint8_t D4D_INT1 :1; // CTRL5<2> 4D detection enabled on INT2 + uint8_t LIR_INT1 :1; // CTRL5<3> Latch interrupt request on INT1 + uint8_t unused :2; // CTRL5<5:4> unused + uint8_t FIFO_EN :1; // CTRL5<6> FIFO enabled + uint8_t BOOT :1; // CTRL5<7> Reboot memory content +}; + +struct lis3dh_reg_ctrl6 +{ + uint8_t unused1 :1; // CTRL6<0> unused + uint8_t H_LACTIVE:1; // CTRL6<1> Interrupt polarity + uint8_t unused2 :1; // CTRL6<2> unused + uint8_t I2_ACT :1; // CTRL6<3> ? + uint8_t I2_BOOT :1; // CTRL6<4> ? + uint8_t I2_AOI2 :1; // CTRL6<5> AOI2 interrupt on INT1 + uint8_t I2_AOI1 :1; // CTRL6<6> AOI1 interrupt on INT1 + uint8_t I2_CLICK :1; // CTRL6<7> CLICK interrupt on INT2 +}; + +struct lis3dh_reg_fifo_ctrl +{ + uint8_t FTH :5; // FIFO_CTRL<4:0> FIFO threshold + uint8_t TR :1; // FIFO_CTRL<5> Trigger selection INT1 / INT2 + uint8_t FM :2; // FIFO_CTRL<7:6> FIFO mode +}; + +struct lis3dh_reg_fifo_src +{ + uint8_t FFS :5; // FIFO_SRC<4:0> FIFO samples stored + uint8_t EMPTY :1; // FIFO_SRC<5> FIFO is empty + uint8_t OVRN_FIFO :1; // FIFO_SRC<6> FIFO buffer full + uint8_t WTM :1; // FIFO_SRC<7> FIFO content exceeds watermark +}; + +struct lis3dh_reg_intx_cfg +{ + uint8_t XLIE :1; // INTx_CFG<0> X axis below threshold enabled + uint8_t XHIE :1; // INTx_CFG<1> X axis above threshold enabled + uint8_t YLIE :1; // INTx_CFG<2> Y axis below threshold enabled + uint8_t YHIE :1; // INTx_CFG<3> Y axis above threshold enabled + uint8_t ZLIE :1; // INTx_CFG<4> Z axis below threshold enabled + uint8_t ZHIE :1; // INTx_CFG<5> Z axis above threshold enabled + uint8_t SIXD :1; // INTx_CFG<6> 6D/4D orientation detecetion enabled + uint8_t AOI :1; // INTx_CFG<7> AND/OR combination of interrupt events +}; + +struct lis3dh_reg_intx_src +{ + uint8_t XL :1; // INTx_SRC<0> X axis below threshold enabled + uint8_t XH :1; // INTx_SRC<1> X axis above threshold enabled + uint8_t YL :1; // INTx_SRC<2> Y axis below threshold enabled + uint8_t YH :1; // INTx_SRC<3> Y axis above threshold enabled + uint8_t ZL :1; // INTx_SRC<4> Z axis below threshold enabled + uint8_t ZH :1; // INTx_SRC<5> Z axis above threshold enabled + uint8_t IA :1; // INTx_SRC<6> Interrupt active + uint8_t unused:1; // INTx_SRC<7> unused +}; + + +struct lis3dh_reg_click_cfg +{ + uint8_t XS :1; // CLICK_CFG<0> X axis single click enabled + uint8_t XD :1; // CLICK_CFG<1> X axis double click enabled + uint8_t YS :1; // CLICK_CFG<2> Y axis single click enabled + uint8_t YD :1; // CLICK_CFG<3> Y axis double click enabled + uint8_t ZS :1; // CLICK_CFG<4> Z axis single click enabled + uint8_t ZD :1; // CLICK_CFG<5> Z axis double click enabled + uint8_t unused:2; // CLICK_CFG<7:6> unused +}; + + +/** Forward declaration of functions for internal use */ + +static bool lis3dh_reset (lis3dh_sensor_t* dev); +static bool lis3dh_is_available(lis3dh_sensor_t* dev); + +static bool lis3dh_i2c_read (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); +static bool lis3dh_i2c_write (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); +static bool lis3dh_spi_read (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); +static bool lis3dh_spi_write (lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len); + +#define msb_lsb_to_type(t,b,o) (t)(((t)b[o] << 8) | b[o+1]) +#define lsb_msb_to_type(t,b,o) (t)(((t)b[o+1] << 8) | b[o]) +#define lsb_to_type(t,b,o) (t)(b[o]) + +#define lis3dh_update_reg(dev,addr,type,elem,value) \ + { \ + struct type __reg; \ + if (!lis3dh_reg_read (dev, (addr), (uint8_t*)&__reg, 1)) \ + return false; \ + __reg.elem = (value); \ + if (!lis3dh_reg_write (dev, (addr), (uint8_t*)&__reg, 1)) \ + return false; \ + } + +lis3dh_sensor_t* lis3dh_init_sensor (uint8_t bus, uint8_t addr, uint8_t cs) +{ + lis3dh_sensor_t* dev; + + if ((dev = malloc (sizeof(lis3dh_sensor_t))) == NULL) + return NULL; + + // init sensor data structure + dev->bus = bus; + dev->addr = addr; + dev->cs = cs; + + dev->error_code = LIS3DH_OK; + dev->scale = lis3dh_scale_2g; + dev->fifo_mode = lis3dh_bypass; + dev->fifo_first = true; + + // if addr==0 then SPI is used and has to be initialized + if (!addr && !spi_device_init (bus, cs)) + { + error_dev ("Could not initialize SPI interface.", __FUNCTION__, dev); + free (dev); + return NULL; + } + + // check availability of the sensor + if (!lis3dh_is_available (dev)) + { + error_dev ("Sensor is not available.", __FUNCTION__, dev); + free (dev); + return NULL; + } + + // reset the sensor + if (!lis3dh_reset(dev)) + { + error_dev ("Could not reset the sensor device.", __FUNCTION__, dev); + free (dev); + return NULL; + } + + lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, FS, lis3dh_scale_2g); + lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, BDU, 1); + + return dev; +} + +bool lis3dh_set_mode (lis3dh_sensor_t* dev, + lis3dh_odr_mode_t odr, lis3dh_resolution_t res, + bool x, bool y, bool z) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + dev->res = res; + + struct lis3dh_reg_ctrl1 reg; + uint8_t old_odr; + + // read current register values + if (!lis3dh_reg_read (dev, LIS3DH_REG_CTRL1, (uint8_t*)®, 1)) + return false; + + old_odr = reg.ODR; + + // set mode + reg.Xen = x; + reg.Yen = y; + reg.Zen = z; + reg.ODR = odr; + reg.LPen = (res == lis3dh_low_power); + + lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, + HR, (res == lis3dh_high_res)); + + if (!lis3dh_reg_write (dev, LIS3DH_REG_CTRL1, (uint8_t*)®, 1)) + return false; + + // if sensor was in power down mode it takes at least 100 ms to start in another mode + if (old_odr == lis3dh_power_down && odr != lis3dh_power_down) + vTaskDelay (15); + + return false; +} + + +bool lis3dh_set_scale (lis3dh_sensor_t* dev, lis3dh_scale_t scale) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + dev->scale = scale; + + // read CTRL4 register and write scale + lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, FS, scale); + + return true; +} + + +bool lis3dh_set_fifo_mode (lis3dh_sensor_t* dev, lis3dh_fifo_mode_t mode, + uint8_t thresh, lis3dh_int_signal_t trigger) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + dev->fifo_mode = mode; + + // read CTRL5 register and write FIFO_EN flag + lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, FIFO_EN, mode != lis3dh_bypass); + + struct lis3dh_reg_fifo_ctrl fifo_ctrl = { + .FTH = thresh, + .TR = trigger, + .FM = mode, + }; + + // write FIFO_CTRL register + if (!lis3dh_reg_write (dev, LIS3DH_REG_FIFO_CTRL, (uint8_t*)&fifo_ctrl, 1)) + return false; + + return true; +} + + +bool lis3dh_new_data (lis3dh_sensor_t* dev) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + if (dev->fifo_mode == lis3dh_bypass) + { + struct lis3dh_reg_status status; + + if (!lis3dh_reg_read (dev, LIS3DH_REG_STATUS, (uint8_t*)&status, 1)) + { + error_dev ("Could not get sensor status", __FUNCTION__, dev); + return false; + } + return status.ZYXDA; + } + else + { + struct lis3dh_reg_fifo_src fifo_src; + + if (!lis3dh_reg_read (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) + { + error_dev ("Could not get fifo source register data", __FUNCTION__, dev); + return false; + } + return !fifo_src.EMPTY; + } +} + +/** + * Scaling factors for the conversion of raw sensor data to floating point g + * values. Scaling factors are from mechanical characteristics in datasheet. + * + * scale/sensitivity resolution + * +-1g 1 mg/digit + * +-2g 2 mg/digit + * +-4g 4 mg/digit + * +-16g 12 mg/digit + */ +const static double LIS3DH_SCALES[4] = { 0.001, 0.002, 0.004, 0.012 }; + +bool lis3dh_get_float_data (lis3dh_sensor_t* dev, lis3dh_float_data_t* data) +{ + if (!dev || !data) return false; + + lis3dh_raw_data_t raw; + + if (!lis3dh_get_raw_data (dev, &raw)) + return false; + + data->ax = LIS3DH_SCALES[dev->scale] * (raw.ax >> 4); + data->ay = LIS3DH_SCALES[dev->scale] * (raw.ay >> 4); + data->az = LIS3DH_SCALES[dev->scale] * (raw.az >> 4); + + return true; +} + + +uint8_t lis3dh_get_float_data_fifo (lis3dh_sensor_t* dev, lis3dh_float_data_fifo_t data) +{ + if (!dev || !data) return false; + + lis3dh_raw_data_fifo_t raw; + + uint8_t num = lis3dh_get_raw_data_fifo (dev, raw); + + for (int i = 0; i < num; i++) + { + data[i].ax = LIS3DH_SCALES[dev->scale] * (raw[i].ax >> 4); + data[i].ay = LIS3DH_SCALES[dev->scale] * (raw[i].ay >> 4); + data[i].az = LIS3DH_SCALES[dev->scale] * (raw[i].az >> 4); + } + return num; +} + + +bool lis3dh_get_raw_data (lis3dh_sensor_t* dev, lis3dh_raw_data_t* raw) +{ + if (!dev || !raw) return false; + + dev->error_code = LIS3DH_OK; + + // abort if not in bypass mode + if (dev->fifo_mode != lis3dh_bypass) + { + dev->error_code = LIS3DH_SENSOR_IN_BYPASS_MODE; + error_dev ("Sensor is in FIFO mode, use lis3dh_get_*_data_fifo to get data", + __FUNCTION__, dev); + return false; + } + + // read raw data sample + if (!lis3dh_reg_read (dev, LIS3DH_REG_OUT_X_L, (uint8_t*)raw, 6)) + { + error_dev ("Could not get raw data sample", __FUNCTION__, dev); + dev->error_code |= LIS3DH_GET_RAW_DATA_FAILED; + return false; + } + + return true; +} + + +uint8_t lis3dh_get_raw_data_fifo (lis3dh_sensor_t* dev, lis3dh_raw_data_fifo_t raw) +{ + if (!dev) return 0; + + dev->error_code = LIS3DH_OK; + + // in bypass mode, use lis3dh_get_raw_data to return one sample + if (dev->fifo_mode == lis3dh_bypass) + return lis3dh_get_raw_data (dev, raw) ? 1 : 0; + + struct lis3dh_reg_fifo_src fifo_src; + + // read FIFO state + if (!lis3dh_reg_read (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) + { + error_dev ("Could not get fifo source register data", __FUNCTION__, dev); + return 0; + } + + // if nothing is in the FIFO, just return with 0 + if (fifo_src.EMPTY) + return 0; + + uint8_t samples = fifo_src.FFS + (fifo_src.OVRN_FIFO ? 1 : 0); + + // read samples from FIFO + for (int i = 0; i < samples; i++) + if (!lis3dh_reg_read (dev, LIS3DH_REG_OUT_X_L, (uint8_t*)&raw[i], 6)) + { + error_dev ("Could not get raw data samples", __FUNCTION__, dev); + dev->error_code |= LIS3DH_GET_RAW_DATA_FIFO_FAILED; + return i; + } + + lis3dh_reg_read (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1); + + // if FFS is not 0 after all samples read, ODR is higher than fetching rate + if (fifo_src.FFS) + { + dev->error_code = LIS3DH_ODR_TOO_HIGH; + error_dev ("New samples stored in FIFO while reading, " + "output data rate (ODR) too high", __FUNCTION__, dev); + return 0; + } + + if (dev->fifo_mode == lis3dh_fifo && samples == 32) + { + // clean FIFO (see app note) + lis3dh_update_reg (dev, LIS3DH_REG_FIFO_CTRL, lis3dh_reg_fifo_ctrl, FM, lis3dh_bypass); + lis3dh_update_reg (dev, LIS3DH_REG_FIFO_CTRL, lis3dh_reg_fifo_ctrl, FM, lis3dh_fifo); + } + + return samples; +} + + +bool lis3dh_enable_int (lis3dh_sensor_t* dev, + lis3dh_int_type_t type, + lis3dh_int_signal_t signal, bool value) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_ctrl3 ctrl3; + struct lis3dh_reg_ctrl6 ctrl6; + + uint8_t* reg = NULL; + uint8_t addr; + + // determine the addr of the register to change + if (type == lis3dh_int_data_ready || + type == lis3dh_int_fifo_watermark || + type == lis3dh_int_fifo_overrun) + { + reg = (uint8_t*)&ctrl3; + addr = LIS3DH_REG_CTRL3; + } + else if (signal == lis3dh_int1_signal) + { + reg = (uint8_t*)&ctrl3; + addr = LIS3DH_REG_CTRL3; + } + else + { + reg = (uint8_t*)&ctrl6; + addr = LIS3DH_REG_CTRL6; + } + + // read the register + if (!lis3dh_reg_read (dev, addr, reg, 1)) + { + error_dev ("Could not read interrupt control registers", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_INT_FAILED; + return false; + } + + // change the register + switch (type) + { + case lis3dh_int_data_ready: ctrl3.IT_DRDY1 = value; + break; + + case lis3dh_int_fifo_watermark: ctrl3.I1_WTM1 = value; + break; + + case lis3dh_int_fifo_overrun: ctrl3.I1_OVERRUN = value; + break; + + case lis3dh_int_event1: if (signal == lis3dh_int1_signal) + ctrl3.I1_AOI1 = value; + else + ctrl6.I2_AOI1 = value; + break; + + case lis3dh_int_event2: if (signal == lis3dh_int1_signal) + ctrl3.I1_AOI2 = value; + else + ctrl6.I2_AOI2 = value; + break; + + case lis3dh_int_click: if (signal == lis3dh_int1_signal) + ctrl3.I1_CLICK = value; + else + ctrl6.I2_CLICK = value; + break; + + default: dev->error_code = LIS3DH_WRONG_INT_TYPE; + error_dev ("Wrong interrupt type", __FUNCTION__, dev); + return false; + } + + if (!lis3dh_reg_write (dev, addr, reg, 1)) + { + error_dev ("Could not enable/disable interrupt", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_INT_FAILED; + return false; + } + + return true; +} + + +bool lis3dh_get_int_data_source (lis3dh_sensor_t* dev, + lis3dh_int_data_source_t* source) +{ + if (!dev || !source) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_ctrl3 ctrl3; + struct lis3dh_reg_status status; + struct lis3dh_reg_fifo_src fifo_src; + + if (!lis3dh_reg_read (dev, LIS3DH_REG_CTRL3 , (uint8_t*)&ctrl3 , 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_STATUS , (uint8_t*)&status , 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_FIFO_SRC, (uint8_t*)&fifo_src, 1)) + { + error_dev ("Could not read source of interrupt INT2 from sensor", __FUNCTION__, dev); + dev->error_code |= LIS3DH_INT_SOURCE_FAILED; + return false; + } + + source->data_ready = status.ZYXDA & ctrl3.IT_DRDY1; + source->fifo_watermark = fifo_src.WTM & ctrl3.I1_WTM1; + source->fifo_overrun = fifo_src.OVRN_FIFO & ctrl3.I1_OVERRUN; + + return true; +} + + +bool lis3dh_set_int_event_config (lis3dh_sensor_t* dev, + lis3dh_int_event_config_t* config, + lis3dh_int_event_gen_t gen) +{ + if (!dev || !config) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_intx_cfg intx_cfg; + + intx_cfg.XLIE = config->x_low_enabled; + intx_cfg.XHIE = config->x_high_enabled; + + intx_cfg.YLIE = config->y_low_enabled; + intx_cfg.YHIE = config->y_high_enabled; + + intx_cfg.ZLIE = config->z_low_enabled; + intx_cfg.ZHIE = config->z_high_enabled; + + bool d4d_int = false; + + switch (config->mode) + { + case lis3dh_wake_up : intx_cfg.AOI = 0; intx_cfg.SIXD = 0; break; + case lis3dh_free_fall : intx_cfg.AOI = 1; intx_cfg.SIXD = 0; break; + + case lis3dh_4d_movement : d4d_int = true; + case lis3dh_6d_movement : intx_cfg.AOI = 0; intx_cfg.SIXD = 1; break; + + case lis3dh_4d_position : d4d_int = true; + case lis3dh_6d_position : intx_cfg.AOI = 1; intx_cfg.SIXD = 1; break; + } + + uint8_t intx_cfg_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; + uint8_t intx_ths_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_THS : LIS3DH_REG_INT2_THS; + uint8_t intx_dur_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_DUR : LIS3DH_REG_INT2_DUR; + + if (// write the thresholds to registers IG_THS_* + !lis3dh_reg_write (dev, intx_ths_addr, &config->threshold, 1) || + + // write duration configuration to IG_DURATION + !lis3dh_reg_write (dev, intx_dur_addr, &config->duration, 1) || + + // write INT1 configuration to IG_CFG + !lis3dh_reg_write (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1)) + { + error_dev ("Could not configure interrupt INT1", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_INT_FAILED; + return false; + } + + if (gen == lis3dh_int_event1_gen) + { + lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, LIR_INT1, config->latch); + lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, D4D_INT1, d4d_int); + } + else + { + lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, LIR_INT2, config->latch); + lis3dh_update_reg (dev, LIS3DH_REG_CTRL5, lis3dh_reg_ctrl5, D4D_INT2, d4d_int); + } + + return true; +} + + +bool lis3dh_get_int_event_config (lis3dh_sensor_t* dev, + lis3dh_int_event_config_t* config, + lis3dh_int_event_gen_t gen) +{ + if (!dev || !config) return false; + + dev->error_code = LIS3DH_OK; + + uint8_t intx_cfg_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; + uint8_t intx_ths_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_THS : LIS3DH_REG_INT2_THS; + uint8_t intx_dur_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_DUR : LIS3DH_REG_INT2_DUR; + + struct lis3dh_reg_intx_cfg intx_cfg; + struct lis3dh_reg_ctrl5 ctrl5; + + if (!lis3dh_reg_read (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1) || + !lis3dh_reg_read (dev, intx_ths_addr, (uint8_t*)&config->threshold, 1) || + !lis3dh_reg_read (dev, intx_dur_addr, (uint8_t*)&config->duration, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_CTRL5, (uint8_t*)&ctrl5, 1)) + { + error_dev ("Could not read interrupt configuration from sensor", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_INT_FAILED; + return false; + } + + config->x_low_enabled = intx_cfg.XLIE; + config->x_high_enabled = intx_cfg.XHIE; + + config->y_low_enabled = intx_cfg.YLIE; + config->y_high_enabled = intx_cfg.YHIE; + + config->z_low_enabled = intx_cfg.ZLIE; + config->z_high_enabled = intx_cfg.ZHIE; + + bool d4d_int = false; + + if (gen == lis3dh_int_event1_gen) + { + config->latch = ctrl5.LIR_INT1; + d4d_int = ctrl5.D4D_INT1; + } + else + { + config->latch = ctrl5.LIR_INT2; + d4d_int = ctrl5.D4D_INT2; + } + + if (intx_cfg.AOI) + { + if (intx_cfg.SIXD && d4d_int) + config->mode = lis3dh_4d_position; + else if (intx_cfg.SIXD && !d4d_int) + config->mode = lis3dh_6d_position; + else + config->mode = lis3dh_free_fall; + } + else + { + if (intx_cfg.SIXD && d4d_int) + config->mode = lis3dh_4d_movement; + else if (intx_cfg.SIXD && !d4d_int) + config->mode = lis3dh_6d_movement; + else + config->mode = lis3dh_wake_up; + } + + return true; +} + + +bool lis3dh_get_int_event_source (lis3dh_sensor_t* dev, + lis3dh_int_event_source_t* source, + lis3dh_int_event_gen_t gen) +{ + if (!dev || !source) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_intx_cfg intx_cfg; + struct lis3dh_reg_intx_src intx_src; + + uint8_t intx_cfg_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_CFG : LIS3DH_REG_INT2_CFG; + uint8_t intx_src_addr = (gen == lis3dh_int_event1_gen) ? LIS3DH_REG_INT1_SRC : LIS3DH_REG_INT2_SRC; + + if (!lis3dh_reg_read (dev, intx_src_addr, (uint8_t*)&intx_src, 1) || + !lis3dh_reg_read (dev, intx_cfg_addr, (uint8_t*)&intx_cfg, 1)) + { + error_dev ("Could not read source of interrupt INT1/INT2 from sensor", __FUNCTION__, dev); + dev->error_code |= LIS3DH_INT_SOURCE_FAILED; + return false; + } + + source->active = intx_src.IA; + source->x_low = intx_src.XL & intx_cfg.XLIE; + source->x_high = intx_src.XH & intx_cfg.XHIE; + source->y_low = intx_src.YL & intx_cfg.YLIE; + source->y_high = intx_src.YH & intx_cfg.YHIE; + source->z_low = intx_src.ZL & intx_cfg.ZLIE; + source->z_high = intx_src.ZH & intx_cfg.ZHIE; + + return true; +} + + +bool lis3dh_set_int_click_config (lis3dh_sensor_t* dev, + lis3dh_int_click_config_t* config) +{ + if (!dev || !config) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_click_cfg click_cfg; + + click_cfg.XS = config->x_single; + click_cfg.XD = config->x_double; + + click_cfg.YS = config->y_single; + click_cfg.YD = config->y_double; + + click_cfg.ZS = config->z_single; + click_cfg.ZD = config->z_double; + + uint8_t click_ths = config->threshold | ((config->latch) ? 0x80 : 0x00); + + if (!lis3dh_reg_write (dev, LIS3DH_REG_CLICK_CFG , (uint8_t*)&click_cfg, 1) || + !lis3dh_reg_write (dev, LIS3DH_REG_CLICK_THS , (uint8_t*)&click_ths, 1) || + !lis3dh_reg_write (dev, LIS3DH_REG_TIME_LIMIT , (uint8_t*)&config->time_limit, 1) || + !lis3dh_reg_write (dev, LIS3DH_REG_TIME_LATENCY, (uint8_t*)&config->time_latency, 1) || + !lis3dh_reg_write (dev, LIS3DH_REG_TIME_WINDOW , (uint8_t*)&config->time_window, 1)) + { + error_dev ("Could not configure click detection interrupt", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_CLICK_FAILED; + return false; + } + + return true; +} + +bool lis3dh_get_int_click_config (lis3dh_sensor_t* dev, + lis3dh_int_click_config_t* config) +{ + if (!dev || !config) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_click_cfg click_cfg; + uint8_t click_ths; + + if (!lis3dh_reg_read (dev, LIS3DH_REG_CLICK_CFG , (uint8_t*)&click_cfg, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_CLICK_THS , (uint8_t*)&click_ths, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_TIME_LIMIT , (uint8_t*)&config->time_limit, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_TIME_LATENCY, (uint8_t*)&config->time_latency, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_TIME_WINDOW , (uint8_t*)&config->time_window, 1)) + { + error_dev ("Could not configure click detection interrupt", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_CLICK_FAILED; + return false; + } + + config->x_single = click_cfg.XS; + config->x_double = click_cfg.XD; + + config->y_single = click_cfg.YS; + config->y_double = click_cfg.YD; + + config->z_single = click_cfg.ZS; + config->z_double = click_cfg.ZD; + + config->threshold= click_ths & 0x7f; + config->latch = click_ths & 0x80; + + return true; +} + +bool lis3dh_get_int_click_source (lis3dh_sensor_t* dev, + lis3dh_int_click_source_t* source) +{ + if (!dev || !source) return false; + + dev->error_code = LIS3DH_OK; + + if (!lis3dh_reg_read (dev, LIS3DH_REG_CLICK_SRC, (uint8_t*)source, 1)) + { + error_dev ("Could not read source of click interrupt from sensor", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CLICK_SOURCE_FAILED; + return false; + } + + return true; +} + + + +bool lis3dh_config_int_signals (lis3dh_sensor_t* dev, lis3dh_int_signal_level_t level) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + lis3dh_update_reg (dev, LIS3DH_REG_CTRL6, lis3dh_reg_ctrl6, H_LACTIVE, level); + + return true; +} + + +bool lis3dh_config_hpf (lis3dh_sensor_t* dev, + lis3dh_hpf_mode_t mode, uint8_t cutoff, + bool data, bool click, bool int1, bool int2) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + struct lis3dh_reg_ctrl2 reg; + + reg.HPM = mode; + reg.HPCF = cutoff; + reg.FDS = data; + reg.HPCLICK = click; + reg.HPIS1 = int1; + reg.HPIS2 = int2; + + if (!lis3dh_reg_write (dev, LIS3DH_REG_CTRL2, (uint8_t*)®, 1)) + { + error_dev ("Could not configure high pass filter", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_HPF_FAILED; + return false; + } + + return true; +} + + +bool lis3dh_set_hpf_ref (lis3dh_sensor_t* dev, int8_t ref) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + if (!lis3dh_reg_write (dev, LIS3DH_REG_REFERENCE, (uint8_t*)&ref, 1)) + { + error_dev ("Could not set high pass filter reference", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_HPF_FAILED; + return false; + } + + return true; +} + + +int8_t lis3dh_get_hpf_ref (lis3dh_sensor_t* dev) +{ + if (!dev) return 0; + + dev->error_code = LIS3DH_OK; + + int8_t ref; + + if (!lis3dh_reg_read (dev, LIS3DH_REG_REFERENCE, (uint8_t*)&ref, 1)) + { + error_dev ("Could not get high pass filter reference", __FUNCTION__, dev); + dev->error_code |= LIS3DH_CONFIG_HPF_FAILED; + return 0; + } + + return ref; +} + +int8_t lis3dh_enable_adc (lis3dh_sensor_t* dev, bool adc, bool tmp) +{ + if (!dev) return 0; + + dev->error_code = LIS3DH_OK; + + uint8_t reg = 0; + + reg |= (adc) ? 0x80 : 0; + reg |= (tmp) ? 0x40 : 0; + + return lis3dh_reg_write (dev, LIS3DH_REG_TEMP_CFG, (uint8_t*)®, 1); +} + + +bool lis3dh_get_adc (lis3dh_sensor_t* dev, + uint16_t* adc1, uint16_t* adc2, uint16_t* adc3) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + uint8_t data[6]; + uint8_t temp_cfg; + struct lis3dh_reg_ctrl1 ctrl1; + + if (!lis3dh_reg_read (dev, LIS3DH_REG_OUT_ADC1_L, data, 6) || + !lis3dh_reg_read (dev, LIS3DH_REG_CTRL1, (uint8_t*)&ctrl1, 1) || + !lis3dh_reg_read (dev, LIS3DH_REG_TEMP_CFG, &temp_cfg, 1)) + { + error_dev ("Could not get adc data", __FUNCTION__, dev); + dev->error_code |= LIS3DH_GET_ADC_DATA_FAILED; + return false; + } + + if (adc1) *adc1 = lsb_msb_to_type ( int16_t, data, 0) >> (ctrl1.LPen ? 8 : 6); + if (adc2) *adc2 = lsb_msb_to_type ( int16_t, data, 2) >> (ctrl1.LPen ? 8 : 6); + + // temperature is always 8 bit + if (adc3 && temp_cfg & 0x40) + *adc3 = (lsb_msb_to_type ( int16_t, data, 4) >> 8) + 25; + else if (adc3) + *adc3 = lsb_msb_to_type ( int16_t, data, 4) >> (ctrl1.LPen ? 8 : 6); + + return true; +} + + +/** Functions for internal use only */ + +/** + * @brief Check the chip ID to test whether sensor is available + */ +static bool lis3dh_is_available (lis3dh_sensor_t* dev) +{ + uint8_t chip_id; + + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + if (!lis3dh_reg_read (dev, LIS3DH_REG_WHO_AM_I, &chip_id, 1)) + return false; + + if (chip_id != LIS3DH_CHIP_ID) + { + error_dev ("Chip id %02x is wrong, should be %02x.", + __FUNCTION__, dev, chip_id, LIS3DH_CHIP_ID); + dev->error_code = LIS3DH_WRONG_CHIP_ID; + return false; + } + + return true; +} + +static bool lis3dh_reset (lis3dh_sensor_t* dev) +{ + if (!dev) return false; + + dev->error_code = LIS3DH_OK; + + uint8_t reg[8] = { 0 }; + + // initialize sensor completely including setting in power down mode + lis3dh_reg_write (dev, LIS3DH_REG_TEMP_CFG , reg, 8); + lis3dh_reg_write (dev, LIS3DH_REG_FIFO_CTRL, reg, 1); + lis3dh_reg_write (dev, LIS3DH_REG_INT1_CFG , reg, 1); + lis3dh_reg_write (dev, LIS3DH_REG_INT1_THS , reg, 2); + lis3dh_reg_write (dev, LIS3DH_REG_INT2_CFG , reg, 1); + lis3dh_reg_write (dev, LIS3DH_REG_INT2_THS , reg, 2); + lis3dh_reg_write (dev, LIS3DH_REG_CLICK_CFG, reg, 1); + lis3dh_reg_write (dev, LIS3DH_REG_CLICK_THS, reg, 4); + + return true; +} + + +bool lis3dh_reg_read(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + return (dev->addr) ? lis3dh_i2c_read (dev, reg, data, len) + : lis3dh_spi_read (dev, reg, data, len); +} + + +bool lis3dh_reg_write(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + return (dev->addr) ? lis3dh_i2c_write (dev, reg, data, len) + : lis3dh_spi_write (dev, reg, data, len); +} + + +#define LIS3DH_SPI_BUF_SIZE 64 // SPI register data buffer size of ESP866 + +#define LIS3DH_SPI_READ_FLAG 0x80 +#define LIS3DH_SPI_WRITE_FLAG 0x00 +#define LIS3DH_SPI_AUTO_INC_FLAG 0x40 + +static bool lis3dh_spi_read(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + if (len >= LIS3DH_SPI_BUF_SIZE) + { + dev->error_code |= LIS3DH_SPI_BUFFER_OVERFLOW; + error_dev ("Error on read from SPI slave on bus 1. Tried to transfer " + "more than %d byte in one read operation.", + __FUNCTION__, dev, LIS3DH_SPI_BUF_SIZE); + return false; + } + + uint8_t addr = (reg & 0x3f) | LIS3DH_SPI_READ_FLAG | LIS3DH_SPI_AUTO_INC_FLAG; + + static uint8_t mosi[LIS3DH_SPI_BUF_SIZE]; + static uint8_t miso[LIS3DH_SPI_BUF_SIZE]; + + memset (mosi, 0xff, LIS3DH_SPI_BUF_SIZE); + memset (miso, 0xff, LIS3DH_SPI_BUF_SIZE); + + mosi[0] = addr; + + if (!spi_transfer_pf (dev->bus, dev->cs, mosi, miso, len+1)) + { + error_dev ("Could not read data from SPI", __FUNCTION__, dev); + dev->error_code |= LIS3DH_SPI_READ_FAILED; + return false; + } + + // shift data one by left, first byte received while sending register address is invalid + for (int i=0; i < len; i++) + data[i] = miso[i+1]; + + #ifdef LIS3DH_DEBUG_LEVEL_2 + printf("LIS3DH %s: read the following bytes from reg %02x: ", __FUNCTION__, reg); + for (int i=0; i < len; i++) + printf("%02x ", data[i]); + printf("\n"); + #endif + + return true; +} + + +static bool lis3dh_spi_write(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + uint8_t addr = (reg & 0x3f) | LIS3DH_SPI_WRITE_FLAG | LIS3DH_SPI_AUTO_INC_FLAG; + + static uint8_t mosi[LIS3DH_SPI_BUF_SIZE]; + + if (len >= LIS3DH_SPI_BUF_SIZE) + { + dev->error_code |= LIS3DH_SPI_BUFFER_OVERFLOW; + error_dev ("Error on write to SPI slave on bus 1. Tried to transfer more" + "than %d byte in one write operation.", + __FUNCTION__, dev, LIS3DH_SPI_BUF_SIZE); + + return false; + } + + reg &= 0x7f; + + // first byte in output is the register address + mosi[0] = addr; + + // shift data one byte right, first byte in output is the register address + for (int i = 0; i < len; i++) + mosi[i+1] = data[i]; + + #ifdef LIS3DH_DEBUG_LEVEL_2 + printf("LIS3DH %s: Write the following bytes to reg %02x: ", __FUNCTION__, reg); + for (int i = 1; i < len+1; i++) + printf("%02x ", mosi[i]); + printf("\n"); + #endif + + if (!spi_transfer_pf (dev->bus, dev->cs, mosi, NULL, len+1)) + { + error_dev ("Could not write data to SPI.", __FUNCTION__, dev); + dev->error_code |= LIS3DH_SPI_WRITE_FAILED; + return false; + } + + return true; +} + + +#define I2C_AUTO_INCREMENT (0x80) + +static bool lis3dh_i2c_read(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + debug_dev ("Read %d byte from i2c slave register %02x.", __FUNCTION__, dev, len, reg); + + if (len > 1) + reg |= I2C_AUTO_INCREMENT; + + int result = i2c_slave_read(dev->bus, dev->addr, ®, data, len); + + if (result) + { + dev->error_code |= (result == -EBUSY) ? LIS3DH_I2C_BUSY : LIS3DH_I2C_READ_FAILED; + error_dev ("Error %d on read %d byte from I2C slave register %02x.", + __FUNCTION__, dev, result, len, reg); + return false; + } + +# ifdef LIS3DH_DEBUG_LEVEL_2 + printf("LIS3DH %s: Read following bytes: ", __FUNCTION__); + printf("%02x: ", reg & 0x7f); + for (int i=0; i < len; i++) + printf("%02x ", data[i]); + printf("\n"); +# endif + + return true; +} + + +static bool lis3dh_i2c_write(lis3dh_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len) +{ + if (!dev || !data) return false; + + debug_dev ("Write %d byte to i2c slave register %02x.", __FUNCTION__, dev, len, reg); + + if (len > 1) + reg |= I2C_AUTO_INCREMENT; + + int result = i2c_slave_write(dev->bus, dev->addr, ®, data, len); + + if (result) + { + dev->error_code |= (result == -EBUSY) ? LIS3DH_I2C_BUSY : LIS3DH_I2C_WRITE_FAILED; + error_dev ("Error %d on write %d byte to i2c slave register %02x.", + __FUNCTION__, dev, result, len, reg); + return false; + } + +# ifdef LIS3DH_DEBUG_LEVEL_2 + printf("LIS3DH %s: Wrote the following bytes: ", __FUNCTION__); + printf("%02x: ", reg & 0x7f); + for (int i=0; i < len; i++) + printf("%02x ", data[i]); + printf("\n"); +# endif + + return true; +} diff --git a/extras/lis3dh/lis3dh.h b/extras/lis3dh/lis3dh.h new file mode 100644 index 0000000..c1fd52e --- /dev/null +++ b/extras/lis3dh/lis3dh.h @@ -0,0 +1,456 @@ +/** + * Driver for LIS3DH 3-axes digital accelerometer connected to I2C or SPI. + * + * This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos) + * [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32 + * and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux + * based systems using a wrapper library for ESP8266 functions. + * + * --------------------------------------------------------------------------- + * + * The BSD License (3-clause license) + * + * Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder nor the names of its + * 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 THE 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. + */ + +#ifndef __LIS3DH_H__ +#define __LIS3DH_H__ + +// Uncomment one of the following defines to enable debug output +// #define LIS3DH_DEBUG_LEVEL_1 // only error messages +// #define LIS3DH_DEBUG_LEVEL_2 // debug and error messages + +// LIS3DH addresses (also used for LIS2DH, LIS2DH12 and LIS2DE12) +#define LIS3DH_I2C_ADDRESS_1 0x18 // SDO pin is low +#define LIS3DH_I2C_ADDRESS_2 0x19 // SDO pin is high + +// LIS3DE addresse (also used for LIS2DE) +#define LIS3DE_I2C_ADDRESS_1 0x28 // SDO pin is low +#define LIS3DE_I2C_ADDRESS_2 0x29 // SDO pin is high + +// LIS3DH chip id +#define LIS3DH_CHIP_ID 0x33 // LIS3DH_REG_WHO_AM_I<7:0> + +// Definition of error codes +#define LIS3DH_OK 0 +#define LIS3DH_NOK -1 + +#define LIS3DH_INT_ERROR_MASK 0x000f +#define LIS3DH_DRV_ERROR_MASK 0xfff0 + +// Error codes for I2C and SPI interfaces ORed with LIS3DH driver error codes +#define LIS3DH_I2C_READ_FAILED 1 +#define LIS3DH_I2C_WRITE_FAILED 2 +#define LIS3DH_I2C_BUSY 3 +#define LIS3DH_SPI_WRITE_FAILED 4 +#define LIS3DH_SPI_READ_FAILED 5 +#define LIS3DH_SPI_BUFFER_OVERFLOW 6 + +// LIS3DH driver error codes ORed with error codes for I2C and SPI interfaces +#define LIS3DH_WRONG_CHIP_ID ( 1 << 8) +#define LIS3DH_WRONG_BANDWIDTH ( 2 << 8) +#define LIS3DH_GET_RAW_DATA_FAILED ( 3 << 8) +#define LIS3DH_GET_RAW_DATA_FIFO_FAILED ( 4 << 8) +#define LIS3DH_WRONG_INT_TYPE ( 5 << 8) +#define LIS3DH_CONFIG_INT_SIGNALS_FAILED ( 6 << 8) +#define LIS3DH_CONFIG_INT_FAILED ( 7 << 8) +#define LIS3DH_INT_SOURCE_FAILED ( 8 << 8) +#define LIS3DH_CONFIG_HPF_FAILED ( 9 << 8) +#define LIS3DH_ENABLE_HPF_FAILED (10 << 8) +#define LIS3DH_CONFIG_CLICK_FAILED (11 << 8) +#define LIS3DH_CLICK_SOURCE_FAILED (12 << 8) +#define LIS3DH_GET_ADC_DATA_FAILED (13 << 8) +#define LIS3DH_SENSOR_IN_BYPASS_MODE (14 << 8) +#define LIS3DH_SENSOR_IN_FIFO_MODE (15 << 8) +#define LIS3DH_ODR_TOO_HIGH (16 << 8) + +#include "lis3dh_platform.h" +#include "lis3dh_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/** + * @brief Initialize the sensor + * + * Reset the sensor and switch to power down mode. All registers are reset to + * default values. FIFO is cleared. + * + * @param bus I2C or SPI bus at which LIS3DH sensor is connected + * @param addr I2C addr of the LIS3DH sensor, 0 for using SPI + * @param cs SPI CS GPIO, ignored for I2C + * @return pointer to sensor data structure, or NULL on error + */ +lis3dh_sensor_t* lis3dh_init_sensor (uint8_t bus, uint8_t addr, uint8_t cs); + + +/** + * @brief Set sensor mode + * + * @param dev pointer to the sensor device data structure + * @param odr sensor output data rate (ODR) + * @param res sensor resolution + * @param x true enable x-axis, false disable x-axis + * @param y true enable y-axis, false disable y-axis + * @param z true enable z-axis, false disable z-axis + * @return true on success, false on error + */ +bool lis3dh_set_mode (lis3dh_sensor_t* dev, + lis3dh_odr_mode_t odr, lis3dh_resolution_t res, + bool x, bool y, bool z); + + +/** + * @brief Set scale (full scale range) + * + * @param dev pointer to the sensor device data structure + * @param scale full range scale + * @return true on success, false on error + */ +bool lis3dh_set_scale (lis3dh_sensor_t* dev, lis3dh_scale_t scale); + + +/** + * @brief Set FIFO mode + * + * FIFO watermark can be used to generate an interrupt when FIFO content + * exceeds the value. It is ignored in bypass mode. + * + * + * @param dev pointer to the sensor device data structure + * @param mode FIFO mode + * @param thresh FIFO watermark (ignored in bypass mode) + * @param trigger interrupt signal used as trigger (only in Stream-to-FIFO) + * @return true on success, false on error + */ +bool lis3dh_set_fifo_mode (lis3dh_sensor_t* dev, lis3dh_fifo_mode_t mode, + uint8_t thresh, lis3dh_int_signal_t trigger); + + +/** + * @brief Test whether new data samples are available + * + * @param dev pointer to the sensor device data structure + * @return true on new data, otherwise false + */ +bool lis3dh_new_data (lis3dh_sensor_t* dev); + + +/** + * @brief Get one sample of sensor data as floating point values (unit g) + * + * Function works only in bypass mode and fails in FIFO modes. In FIFO modes, + * function *lis3dh_get_float_data_fifo* has to be used instead to get data. + * + * @param dev pointer to the sensor device data structure + * @param data pointer to float data structure filled with g values + * @return true on success, false on error + */ +bool lis3dh_get_float_data (lis3dh_sensor_t* dev, + lis3dh_float_data_t* data); + + +/** + * @brief Get all samples of sensor data stored in the FIFO (unit g) + * + * In bypass mode, it returns only one sensor data sample. + * + * @param dev pointer to the sensor device data structure + * @param data array of 32 float data structures filled with g values + * @return number of data sets read from fifo on success or 0 on error + */ +uint8_t lis3dh_get_float_data_fifo (lis3dh_sensor_t* dev, + lis3dh_float_data_fifo_t data); + + +/** + * @brief Get one sample of raw sensor data as 16 bit two's complements + * + * Function works only in bypass mode and fails in FIFO modes. In FIFO modes, + * function *lis3dh_get_raw_data_fifo* has to be used instead to get data. + * + * @param dev pointer to the sensor device data structure + * @param raw pointer to raw data structure filled with values + * @return true on success, false on error + */ +bool lis3dh_get_raw_data (lis3dh_sensor_t* dev, lis3dh_raw_data_t* raw); + + +/** + * @brief Get all samples of raw sensor data stored in the FIFO + * + * In bypass mode, it returns only one raw data sample. + * + * @param dev pointer to the sensor device data structure + * @param raw array of 32 raw data structures + * @return number of data sets read from fifo on success or 0 on error + */ +uint8_t lis3dh_get_raw_data_fifo (lis3dh_sensor_t* dev, + lis3dh_raw_data_fifo_t raw); + + +/** + * @brief Enable / disable an interrupt on signal INT1 or INT2 + * + * @param dev pointer to the sensor device data structure + * @param type interrupt to be enabled or disabled + * @param signal interrupt signal that is activated for the interrupt + * @param value true to enable or false to disable the interrupt + * @return true on success, false on error + */ +bool lis3dh_enable_int (lis3dh_sensor_t* dev, + lis3dh_int_type_t type, + lis3dh_int_signal_t signal, bool value); + + +/** + * @brief Get the source of data ready and FIFO interrupts on INT1 + * + * @param dev pointer to the sensor device data structure + * @param source pointer to the interrupt source + * @return true on success, false on error + */ +bool lis3dh_get_int_data_source (lis3dh_sensor_t* dev, + lis3dh_int_data_source_t* source); + + +/** + * @brief Set the configuration of an inertial event interrupt generator + * + * Inertial interrupt generators produce interrupts when certain inertial event + * occures (event interrupts), that is, the acceleration of defined axes is + * higher or lower than a defined threshold and one of the following event is + * recognized: axis movement / wake up, free fall, 6D/4D orientation detection. + * + * @param dev pointer to the sensor device data structure + * @param config pointer to the interrupt generator configuration + * @param gen interrupt generator to which the function is applied + * @return true on success, false on error + */ +bool lis3dh_set_int_event_config (lis3dh_sensor_t* dev, + lis3dh_int_event_config_t* config, + lis3dh_int_event_gen_t gen); + + +/** + * @brief Get the configuration of an inertial event interrupt generator + * + * Inertial interrupt generators produce interrupts when certain inertial event + * occures (event interrupts), that is, the acceleration of defined axes is + * higher or lower than a defined threshold and one of the following event is + * recognized: axis movement / wake up, free fall, 6D/4D orientation detection. + * + * @param dev pointer to the sensor device data structure + * @param config pointer to the interrupt generator configuration + * @param gen interrupt generator to which the function is applied + * @return true on success, false on error + */ +bool lis3dh_get_int_event_config (lis3dh_sensor_t* dev, + lis3dh_int_event_config_t* config, + lis3dh_int_event_gen_t gen); + + +/** + * @brief Get the source of an inertial event interrupt INT1/INT2 + * + * Returns a byte with flags that indicate the event which triggered + * the interrupt signal (see INTx_SRC register in datasheet for details) + * + * @param dev pointer to the sensor device data structure + * @param source pointer to the interrupt source data structure + * @param gen interrupt generator to which the function is applied + * @return true on success, false on error + */ +bool lis3dh_get_int_event_source (lis3dh_sensor_t* dev, + lis3dh_int_event_source_t* source, + lis3dh_int_event_gen_t gen); + + +/** + * @brief Set the configuration of the click detection interrupt generator + * + * Set the configuration for interrupts that are generated when single or + * double clicks are detected. + * + * @param dev pointer to the sensor device data structure + * @param config pointer to the interrupt generator configuration + * @return true on success, false on error + */ +bool lis3dh_set_int_click_config (lis3dh_sensor_t* dev, + lis3dh_int_click_config_t* config); + +/** + * @brief Get the configuration of the click detection interrupt generator + * + * Set the configuration for interrupts that are generated when single or + * double clicks are detected. + * + * @param dev pointer to the sensor device data structure + * @param config pointer to the interrupt generator configuration + * @return true on success, false on error + */ +bool lis3dh_get_int_click_config (lis3dh_sensor_t* dev, + lis3dh_int_click_config_t* config); + + +/** + * @brief Get the source of the click detection interrupt on signal INT1/INT2 + * + * Returns a byte with flags that indicate the activity which triggered + * the interrupt signal (see CLICK_SRC register in datasheet for details) + * + * @param dev pointer to the sensor device data structure + * @param source pointer to the interrupt source + * @return true on success, false on error + */ +bool lis3dh_get_int_click_source (lis3dh_sensor_t* dev, + lis3dh_int_click_source_t* source); + + +/** + * @brief Set signal configuration for INT1 and INT2 signals + * + * @param dev pointer to the sensor device data structure + * @param level define interrupt signal as low or high active + * @return true on success, false on error + */ +bool lis3dh_config_int_signals (lis3dh_sensor_t* dev, + lis3dh_int_signal_level_t level); + + +/** + * @brief Config HPF (high pass filter) + * + * @param dev pointer to the sensor device data structure + * @param mode filter mode + * @param cutoff filter cutoff frequency (depends on ODR) [0 ... 3] + * @param data if true, use filtered data as sensor output + * @param click if true, use filtered data for CLICK function + * @param int1 if true, use filtered data for interrupt INT1 generation + * @param int2 if true, use filtered data for interrupt INT2 generation + * @return true on success, false on error + */ +bool lis3dh_config_hpf (lis3dh_sensor_t* dev, + lis3dh_hpf_mode_t mode, uint8_t cutoff, + bool data, bool click, bool int1, bool int2); + + +/** + * @brief Set HPF (high pass filter) reference + * + * Used to set the reference of HPF in reference mode *lis3dh_hpf_reference*. + * Used to reset the HPF in autoreset mode *lis3dh_hpf_autoreset*. + * Reference is given as two's complement. + * + * @param dev pointer to the sensor device data structure + * @param ref reference *lis3dh_hpf_reference* mode, otherwise ignored + * @return true on success, false on error + */ +bool lis3dh_set_hpf_ref (lis3dh_sensor_t* dev, int8_t ref); + + +/** + * @brief Get HPF (high pass filter) reference + * + * Used to reset the HPF in normal mode *lis3dh_hpf_normal*. + * + * @param dev pointer to the sensor device data structure + * @return HPF reference as two's complement + */ +int8_t lis3dh_get_hpf_ref (lis3dh_sensor_t* dev); + +/** + * @brief Enable / disable ADC or temperature sensor + * + * @param dev pointer to the sensor device data structure + * @param enable if true, ADC inputs are enabled + * @param temp if true, ADC input 3 is the output of temperature sensor + * @return true on success, false on error + */ +int8_t lis3dh_enable_adc (lis3dh_sensor_t* dev, bool enable, bool temp); + + +/** + * @brief Get ADC input or temperature + * + * @param dev pointer to the sensor device data structure + * @param adc1 ADC input 1 + * @param adc2 ADC input 2 + * @param adc3 ADC input 3 or temperature in degree if enabled + * @return true on success, false on error + */ +bool lis3dh_get_adc (lis3dh_sensor_t* dev, + uint16_t* adc1, uint16_t* adc2, uint16_t* adc3); + + +// ---- Low level interface functions ----------------------------- + +/** + * @brief Direct write to register + * + * PLEASE NOTE: This function should only be used to do something special that + * is not covered by the high level interface AND if you exactly know what you + * do and what effects it might have. Please be aware that it might affect the + * high level interface. + * + * @param dev pointer to the sensor device data structure + * @param reg address of the first register to be changed + * @param data pointer to the data to be written to the register + * @param len number of bytes to be written to the register + * @return true on success, false on error + */ +bool lis3dh_reg_write (lis3dh_sensor_t* dev, + uint8_t reg, uint8_t *data, uint16_t len); + +/** + * @brief Direct read from register + * + * PLEASE NOTE: This function should only be used to do something special that + * is not covered by the high level interface AND if you exactly know what you + * do and what effects it might have. Please be aware that it might affect the + * high level interface. + * + * @param dev pointer to the sensor device data structure + * @param reg address of the first register to be read + * @param data pointer to the data to be read from the register + * @param len number of bytes to be read from the register + * @return true on success, false on error + */ +bool lis3dh_reg_read (lis3dh_sensor_t* dev, + uint8_t reg, uint8_t *data, uint16_t len); + +#ifdef __cplusplus +} +#endif /* End of CPP guard */ + +#endif /* __LIS3DH_H__ */ diff --git a/extras/lis3dh/lis3dh_platform.h b/extras/lis3dh/lis3dh_platform.h new file mode 100644 index 0000000..03fd6bc --- /dev/null +++ b/extras/lis3dh/lis3dh_platform.h @@ -0,0 +1,112 @@ +/** + * Driver for LIS3DH 3-axes digital accelerometer connected to I2C or SPI. + * + * This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos) + * [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32 + * and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux + * based systems using a wrapper library for ESP8266 functions. + * + * --------------------------------------------------------------------------- + * + * The BSD License (3-clause license) + * + * Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder nor the names of its + * 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 THE 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. + */ + +/** + * Platform file: platform specific definitions, includes and functions + */ + +#ifndef __LIS3DH_PLATFORM_H__ +#define __LIS3DH_PLATFORM_H__ + +#if !defined(ESP_OPEN_RTOS) +#define ESP_OPEN_RTOS 1 +#endif + +#ifdef ESP_OPEN_RTOS // ESP8266 + +// platform specific includes + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#include "espressif/esp_common.h" +#include "espressif/sdk_private.h" + +#include "esp/uart.h" +#include "esp/spi.h" +#include "i2c/i2c.h" + +// platform specific definitions + +#define spi_semaphore_init() +#define spi_semaphore_take() +#define spi_semaphore_give() + +// platform specific SPI functions + +#define spi_bus_init(bus,sck,miso,mosi) // not needed on ESP8266 + +static const spi_settings_t bus_settings = { + .mode = SPI_MODE0, + .freq_divider = SPI_FREQ_DIV_1M, + .msb = true, + .minimal_pins = false, + .endianness = SPI_LITTLE_ENDIAN +}; + +inline static bool spi_device_init (uint8_t bus, uint8_t cs) +{ + gpio_enable(cs, GPIO_OUTPUT); + gpio_write (cs, true); + return true; +} + +inline static size_t spi_transfer_pf(uint8_t bus, uint8_t cs, const uint8_t *mosi, uint8_t *miso, uint16_t len) +{ + spi_settings_t old_settings; + + spi_get_settings(bus, &old_settings); + spi_set_settings(bus, &bus_settings); + gpio_write(cs, false); + + size_t transfered = spi_transfer (bus, (const void*)mosi, (void*)miso, len, SPI_8BIT); + + gpio_write(cs, true); + spi_set_settings(bus, &old_settings); + + return transfered; +} + +#endif // ESP_OPEN_RTOS + +#endif // __LIS3DH_PLATFORM_H__ diff --git a/extras/lis3dh/lis3dh_types.h b/extras/lis3dh/lis3dh_types.h new file mode 100644 index 0000000..b90b042 --- /dev/null +++ b/extras/lis3dh/lis3dh_types.h @@ -0,0 +1,355 @@ +/** + * Driver for LIS3DH 3-axes digital accelerometer connected to I2C or SPI. + * + * This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos) + * [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32 + * and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux + * based systems using a wrapper library for ESP8266 functions. + * + * --------------------------------------------------------------------------- + * + * The BSD License (3-clause license) + * + * Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder nor the names of its + * 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 Activity SHALL THE 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. + */ + +#ifndef __LIS3DH_TYPES_H__ +#define __LIS3DH_TYPES_H__ + +#include "stdint.h" +#include "stdbool.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + +/** + * @brief Output data rates (ODR), related to resolution modes + */ +typedef enum { + + lis3dh_power_down = 0, // power down mode + lis3dh_odr_1, // high resolution / normal / low power 1 Hz + lis3dh_odr_10, // high resolution / normal / low power 10 Hz + lis3dh_odr_25, // high resolution / normal / low power 25 Hz + lis3dh_odr_50, // high resolution / normal / low power 50 Hz + lis3dh_odr_100, // high resolution / normal / low power 100 Hz + lis3dh_odr_200, // high resolution / normal / low power 200 Hz + lis3dh_odr_400, // high resolution / normal / low power 400 Hz + lis3dh_odr_1600, // low power mode 1.6 kHz + lis3dh_odr_5000, // normal 1.25 kHz / low power 5 kHz + +} lis3dh_odr_mode_t; + +/** + * @brief Resolution modes, related to output data rates (ODR) + */ +typedef enum { + + lis3dh_low_power, // low power mode resolution ( 8 bit data) + lis3dh_normal, // normal mode resolution (10 bit data) + lis3dh_high_res // high resolution mode (12 bit data) + +} lis3dh_resolution_t; + +/** + * @brief Full scale measurement range + */ +typedef enum { + + lis3dh_scale_2g = 0, // default + lis3dh_scale_4g, + lis3dh_scale_8g, + lis3dh_scale_16g + +} lis3dh_scale_t; + + +/** + * @brief FIFO mode + */ +typedef enum { + + lis3dh_bypass = 0, // default + lis3dh_fifo = 1, + lis3dh_stream = 2, + lis3dh_trigger= 3 + +} lis3dh_fifo_mode_t; + + +/** + * @brief Interrupt signals + */ +typedef enum { + + lis3dh_int1_signal = 0, + lis3dh_int2_signal = 1 + +} lis3dh_int_signal_t; + + +/** + * @brief Inertial event interrupt generators + */ +typedef enum { + + lis3dh_int_event1_gen = 0, + lis3dh_int_event2_gen = 1 + +} lis3dh_int_event_gen_t; + + +/** + * @brief Interrupt types for interrupt signals INT1/INT2 + */ +typedef enum { + + lis3dh_int_data_ready, // data ready for read interrupt (only INT1) + + lis3dh_int_fifo_watermark, // FIFO exceeds the threshold (only INT1) + lis3dh_int_fifo_overrun, // FIFO is completely filled (only INT1) + + lis3dh_int_event1, // inertial event interrupt 1 + lis3dh_int_event2, // inertial event interrupt 2 + + lis3dh_int_click // click detection interrupt + +} lis3dh_int_type_t; + + +/** + * @brief Data ready and FIFO status interrupt source for INT1 + */ +typedef struct { + + bool data_ready; // true when acceleration data are ready to read + + bool fifo_watermark; // true when FIFO exceeds the FIFO threshold + bool fifo_overrun; // true when FIFO is completely filled + +} lis3dh_int_data_source_t; + + +/** + * @brief Inertial interrupt generator configuration for INT1/INT2 + * + * Inertial events are: wake-up, free-fall, 6D/4D detection. + */ +typedef struct { + + enum { // interrupt mode + + lis3dh_wake_up, // AOI = 0, 6D = 0 + lis3dh_free_fall, // AOI = 1, 6D = 0 + + lis3dh_6d_movement, // AOI = 0, 6D = 1, D4D = 0 + lis3dh_6d_position, // AOI = 1, 6D = 1, D4D = 0 + + lis3dh_4d_movement, // AOI = 0, 6D = 1, D4D = 1 + lis3dh_4d_position, // AOI = 1, 6D = 1, D4D = 1 + + } mode; + + uint8_t threshold; // threshold used for comparison for all axes + + bool x_low_enabled; // x lower than threshold interrupt enabled + bool x_high_enabled; // x higher than threshold interrupt enabled + + bool y_low_enabled; // y lower than threshold interrupt enabled + bool y_high_enabled; // y higher than threshold interrupt enabled + + bool z_low_enabled; // z lower than threshold interrupt enabled + bool z_high_enabled; // z higher than threshold interrupt enabled + + bool latch; // latch the interrupt when true until the + // interrupt source has been read + + uint8_t duration; // duration in 1/ODR an interrupt condition has + // to be given before the interrupt is generated +} lis3dh_int_event_config_t; + + +/** + * @brief Inertial event source type for interrupt generator INT1/INT2 + */ +typedef struct { + + bool active:1; // true - one ore more events occured + + bool x_low :1; // true - x lower than threshold event + bool x_high:1; // true - x higher than threshold event + + bool y_low :1; // true - z lower than threshold event + bool y_high:1; // true - z higher than threshold event + + bool z_low :1; // true - z lower than threshold event + bool z_high:1; // true - z higher than threshold event + +} lis3dh_int_event_source_t; + + +/** + * @brief Click interrupt configuration for interrupt signals INT1/INT2 + */ +typedef struct { + + bool x_single; // x-axis single tap interrupt enabled + bool x_double; // x-axis double tap interrupt enabled + + bool y_single; // y-axis single tap interrupt enabled + bool y_double; // y-axis double tap interrupt enabled + + bool z_single; // z-axis single tap interrupt enabled + bool z_double; // z-axis double tap interrupt enabled + + uint8_t threshold; // threshold used for comparison for all axes + + bool latch; // latch the interrupt when true until the + // interrupt source has been read + + uint8_t time_limit; // maximum time interval between the start and the + // end of a cick (accel increases and falls back) + uint8_t time_latency; // click detection is disabled for that time after + // a was click detected (in 1/ODR) + uint8_t time_window; // time interval in which the second click has to + // to be detected in double clicks (in 1/ODR) + +} lis3dh_int_click_config_t; + + +/** + * @brief Click interrupt source for interrupt signals INT1/INT2 + */ +typedef struct { + + bool x_click:1; // click detected in x direction + bool y_click:1; // click detected in y direction + bool z_click:1; // click detected in z direction + + bool sign :1; // click sign (0 - posisitive, 1 - negative) + + bool s_click:1; // single click detected + bool d_click:1; // double click detected + + bool active :1; // true - one ore more event occured + +} lis3dh_int_click_source_t; + + +/** + * @brief INT1, INT2 signal activity level + */ +typedef enum { + + lis3dh_high_active = 0, + lis3dh_low_active + +} lis3dh_int_signal_level_t; + + +/** + * @brief Raw data set as two complements + */ +typedef struct { + + int16_t ax; // acceleration on x axis + int16_t ay; // acceleration on y axis + int16_t az; // acceleration on z axis + +} lis3dh_raw_data_t; + + +/** + * @brief Raw data FIFO type + */ +typedef lis3dh_raw_data_t lis3dh_raw_data_fifo_t[32]; + + +/** + * @brief Floating point output value set in g + */ +typedef struct { + + float ax; // acceleration on x axis + float ay; // acceleration on y axis + float az; // acceleration on z axis + +} lis3dh_float_data_t; + + +/** + * @brief Floating point output value FIFO type + */ +typedef lis3dh_float_data_t lis3dh_float_data_fifo_t[32]; + + +/** + * @brief HPF (high pass filter) modes + */ +typedef enum { + + lis3dh_hpf_normal = 0, // normal mode (reset by reading reference) + lis3dh_hpf_reference, // reference signal for filtering + lis3dh_hpf_normal_x, // normal mode + lis3dh_hpf_autoreset // autoreset on interrupt Activity + +} lis3dh_hpf_mode_t; + + +/** + * @brief LIS3DH sensor device data structure type + */ +typedef struct { + + int error_code; // error code of last operation + + uint8_t bus; // I2C = x, SPI = 1 + uint8_t addr; // I2C = slave address, SPI = 0 + + uint8_t cs; // ESP8266, ESP32: GPIO used as SPI CS + // __linux__: device index + + lis3dh_scale_t scale; // full range scale (default 2 g) + lis3dh_resolution_t res; // resolution used + + lis3dh_fifo_mode_t fifo_mode; // FIFO operation mode (default bypass) + bool fifo_first; // first FIFO access + +} lis3dh_sensor_t; + + +#ifdef __cplusplus +} +#endif /* End of CPP guard */ + +#endif /* __LIS3DH_TYPES_H__ */ From 4e629af5cdab011897a2d61be70ce46e22a0ac99 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Thu, 4 Jan 2018 17:32:34 +0100 Subject: [PATCH 07/10] minor changes --- examples/lis3dh/lis3dh_example.c | 2 +- extras/lis3dh/README.md | 12 ++++++------ extras/lis3dh/lis3dh.c | 4 ++-- extras/lis3dh/lis3dh_types.h | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/examples/lis3dh/lis3dh_example.c b/examples/lis3dh/lis3dh_example.c index 079b1b7..bdd3bfd 100644 --- a/examples/lis3dh/lis3dh_example.c +++ b/examples/lis3dh/lis3dh_example.c @@ -346,7 +346,7 @@ void user_init(void) lis3dh_enable_adc (sensor, true, true); // LAST STEP: Finally set scale and mode to start measurements - lis3dh_set_scale(sensor, lis3dh_scale_2g); + lis3dh_set_scale(sensor, lis3dh_scale_2_g); lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); // -- SENSOR CONFIGURATION PART --- diff --git a/extras/lis3dh/README.md b/extras/lis3dh/README.md index bd59be6..c43edbf 100644 --- a/extras/lis3dh/README.md +++ b/extras/lis3dh/README.md @@ -89,15 +89,15 @@ Raw **output data** (**raw data**) are given as 16-bit signed integer values in Full Scale | Driver symbol | Resolution 12 bit
```lis3dh_high_res``` | Resolution 10 bit
```lis3dh_normal``` | Resolution 8 bit
```lis3dh_low_power``` ---------------------:|:-----------:|-----------:|---------------:|-----: - ±2 g | ```lis3dh_scale_2g``` | 1 mg | 4 mg | 16 mg - ±4 g | ```lis3dh_scale_4g``` | 2 mg | 8 mg | 32 mg - ±8 g | ```lis3dh_scale_8g``` | 4 mg | 16 mg | 64 mg -±16 g | ```lis3dh_scale_16g``` | 12 mg | 48 mg | 192 mg + ±2 g | ```lis3dh_scale_2_g``` | 1 mg | 4 mg | 16 mg + ±4 g | ```lis3dh_scale_4_g``` | 2 mg | 8 mg | 32 mg + ±8 g | ```lis3dh_scale_8_g``` | 4 mg | 16 mg | 64 mg +±16 g | ```lis3dh_scale_16_g``` | 12 mg | 48 mg | 192 mg By default, a full scale of ±2 g is used. Function ```lis3dh_set_scale``` can be used to change it. ``` -lis3dh_set_scale(sensor, lis3dh_scale_4g); +lis3dh_set_scale(sensor, lis3dh_scale_4_g); ``` ### Fetching output data @@ -1013,7 +1013,7 @@ void user_init(void) lis3dh_enable_adc (sensor, true, true); // LAST STEP: Finally set scale and mode to start measurements - lis3dh_set_scale(sensor, lis3dh_scale_2g); + lis3dh_set_scale(sensor, lis3dh_scale_2_g); lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); // -- SENSOR CONFIGURATION PART --- diff --git a/extras/lis3dh/lis3dh.c b/extras/lis3dh/lis3dh.c index 7742024..b9def12 100644 --- a/extras/lis3dh/lis3dh.c +++ b/extras/lis3dh/lis3dh.c @@ -278,7 +278,7 @@ lis3dh_sensor_t* lis3dh_init_sensor (uint8_t bus, uint8_t addr, uint8_t cs) dev->cs = cs; dev->error_code = LIS3DH_OK; - dev->scale = lis3dh_scale_2g; + dev->scale = lis3dh_scale_2_g; dev->fifo_mode = lis3dh_bypass; dev->fifo_first = true; @@ -306,7 +306,7 @@ lis3dh_sensor_t* lis3dh_init_sensor (uint8_t bus, uint8_t addr, uint8_t cs) return NULL; } - lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, FS, lis3dh_scale_2g); + lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, FS, lis3dh_scale_2_g); lis3dh_update_reg (dev, LIS3DH_REG_CTRL4, lis3dh_reg_ctrl4, BDU, 1); return dev; diff --git a/extras/lis3dh/lis3dh_types.h b/extras/lis3dh/lis3dh_types.h index b90b042..122075e 100644 --- a/extras/lis3dh/lis3dh_types.h +++ b/extras/lis3dh/lis3dh_types.h @@ -86,10 +86,10 @@ typedef enum { */ typedef enum { - lis3dh_scale_2g = 0, // default - lis3dh_scale_4g, - lis3dh_scale_8g, - lis3dh_scale_16g + lis3dh_scale_2_g = 0, // default + lis3dh_scale_4_g, + lis3dh_scale_8_g, + lis3dh_scale_16_g } lis3dh_scale_t; From a974b010abe970dea9db29110ae1b998f1c32a07 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 5 Jan 2018 13:55:12 +0100 Subject: [PATCH 08/10] inline function moved to .c file to avoid multiple definition --- extras/lis3dh/lis3dh_platform.c | 81 +++++++++++++++++++++++++++++++++ extras/lis3dh/lis3dh_platform.h | 34 ++------------ 2 files changed, 85 insertions(+), 30 deletions(-) create mode 100644 extras/lis3dh/lis3dh_platform.c diff --git a/extras/lis3dh/lis3dh_platform.c b/extras/lis3dh/lis3dh_platform.c new file mode 100644 index 0000000..17eabf8 --- /dev/null +++ b/extras/lis3dh/lis3dh_platform.c @@ -0,0 +1,81 @@ +/** + * Driver for LIS3DH 3-axes digital accelerometer connected to I2C or SPI. + * + * This driver is for the usage with the ESP8266 and FreeRTOS (esp-open-rtos) + * [https://github.com/SuperHouse/esp-open-rtos]. It is also working with ESP32 + * and ESP-IDF [https://github.com/espressif/esp-idf.git] as well as Linux + * based systems using a wrapper library for ESP8266 functions. + * + * --------------------------------------------------------------------------- + * + * The BSD License (3-clause license) + * + * Copyright (c) 2017 Gunar Schorcht (https://github.com/gschorcht) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder nor the names of its + * 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 THE 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. + */ + +/** + * Platform file: platform specific definitions, includes and functions + */ + +#include "lis3dh_platform.h" + +// platform specific SPI functions + +static const spi_settings_t bus_settings = { + .mode = SPI_MODE0, + .freq_divider = SPI_FREQ_DIV_1M, + .msb = true, + .minimal_pins = false, + .endianness = SPI_LITTLE_ENDIAN +}; + +bool spi_device_init (uint8_t bus, uint8_t cs) +{ + gpio_enable(cs, GPIO_OUTPUT); + gpio_write (cs, true); + return true; +} + +size_t spi_transfer_pf(uint8_t bus, uint8_t cs, const uint8_t *mosi, uint8_t *miso, uint16_t len) +{ + spi_settings_t old_settings; + + spi_get_settings(bus, &old_settings); + spi_set_settings(bus, &bus_settings); + gpio_write(cs, false); + + size_t transfered = spi_transfer (bus, (const void*)mosi, (void*)miso, len, SPI_8BIT); + + gpio_write(cs, true); + spi_set_settings(bus, &old_settings); + + return transfered; +} + diff --git a/extras/lis3dh/lis3dh_platform.h b/extras/lis3dh/lis3dh_platform.h index 03fd6bc..31d1d01 100644 --- a/extras/lis3dh/lis3dh_platform.h +++ b/extras/lis3dh/lis3dh_platform.h @@ -76,36 +76,10 @@ #define spi_bus_init(bus,sck,miso,mosi) // not needed on ESP8266 -static const spi_settings_t bus_settings = { - .mode = SPI_MODE0, - .freq_divider = SPI_FREQ_DIV_1M, - .msb = true, - .minimal_pins = false, - .endianness = SPI_LITTLE_ENDIAN -}; - -inline static bool spi_device_init (uint8_t bus, uint8_t cs) -{ - gpio_enable(cs, GPIO_OUTPUT); - gpio_write (cs, true); - return true; -} - -inline static size_t spi_transfer_pf(uint8_t bus, uint8_t cs, const uint8_t *mosi, uint8_t *miso, uint16_t len) -{ - spi_settings_t old_settings; - - spi_get_settings(bus, &old_settings); - spi_set_settings(bus, &bus_settings); - gpio_write(cs, false); - - size_t transfered = spi_transfer (bus, (const void*)mosi, (void*)miso, len, SPI_8BIT); - - gpio_write(cs, true); - spi_set_settings(bus, &old_settings); - - return transfered; -} +extern bool spi_device_init (uint8_t bus, uint8_t cs); +extern size_t spi_transfer_pf (uint8_t bus, uint8_t cs, + const uint8_t *mosi, uint8_t *miso, + uint16_t len); #endif // ESP_OPEN_RTOS From 2d52ad85b47d369fa2f590d99b07c4659faada45 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Fri, 5 Jan 2018 18:35:31 +0100 Subject: [PATCH 09/10] semaphore handling added to SPI interface --- examples/lis3dh/lis3dh_example.c | 6 ++++-- extras/lis3dh/lis3dh_platform.h | 6 +++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/examples/lis3dh/lis3dh_example.c b/examples/lis3dh/lis3dh_example.c index bdd3bfd..5057fcf 100644 --- a/examples/lis3dh/lis3dh_example.c +++ b/examples/lis3dh/lis3dh_example.c @@ -153,9 +153,11 @@ void user_task_interrupt (void *pvParameters) // get the source of the interrupt and reset *INTx* signals #ifdef INT_DATA lis3dh_get_int_data_source (sensor, &data_src); - #elif INT_EVENT + #endif + #ifdef INT_EVENT lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event1_gen); - #elif INT_CLICK + #endif + #ifdef INT_CLICK lis3dh_get_int_click_source (sensor, &click_src); #endif diff --git a/extras/lis3dh/lis3dh_platform.h b/extras/lis3dh/lis3dh_platform.h index 31d1d01..cf91541 100644 --- a/extras/lis3dh/lis3dh_platform.h +++ b/extras/lis3dh/lis3dh_platform.h @@ -68,9 +68,9 @@ // platform specific definitions -#define spi_semaphore_init() -#define spi_semaphore_take() -#define spi_semaphore_give() +#define spi_semaphore_init(d) +#define spi_semaphore_take(d) +#define spi_semaphore_give(d) // platform specific SPI functions From 6e1bfd2845871e7fa90e0bc29dc74d85ce45d8f3 Mon Sep 17 00:00:00 2001 From: Gunar Schorcht Date: Sat, 6 Jan 2018 15:39:19 +0100 Subject: [PATCH 10/10] minor changes --- examples/lis3dh/lis3dh_example.c | 44 +++++++++------- extras/lis3dh/README.md | 86 +++++++++++++++++++------------- extras/lis3dh/lis3dh_platform.h | 6 --- 3 files changed, 78 insertions(+), 58 deletions(-) diff --git a/examples/lis3dh/lis3dh_example.c b/examples/lis3dh/lis3dh_example.c index 5057fcf..29534ce 100644 --- a/examples/lis3dh/lis3dh_example.c +++ b/examples/lis3dh/lis3dh_example.c @@ -250,32 +250,25 @@ void user_init(void) if (sensor) { - // --- SYSTEM CONFIGURATION PART ---- + #ifdef INT_USED + + /** --- INTERRUPT CONFIGURATION PART ---- */ - #if !defined (INT_USED) + // Interrupt configuration has to be done before the sensor is set + // into measurement mode to avoid losing interrupts - // create a user task that fetches data from sensor periodically - xTaskCreate(user_task_periodic, "user_task_periodic", TASK_STACK_DEPTH, NULL, 2, NULL); - - #else // INT_USED - - // create a task that is triggered only in case of interrupts to fetch the data - xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL); - - // create event queue + // 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_set_interrupt(INT1_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler); - #endif // !defined(INT_USED) + #endif // INT_USED - // -- SENSOR CONFIGURATION PART --- + /** -- SENSOR CONFIGURATION PART --- */ - // Interrupt configuration has to be done before the sensor is set - // into measurement mode - // set polarity of INT signals if necessary // lis3dh_config_int_signals (sensor, lis3dh_high_active); @@ -351,7 +344,24 @@ void user_init(void) lis3dh_set_scale(sensor, lis3dh_scale_2_g); lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); - // -- SENSOR CONFIGURATION PART --- + /** -- 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 only in case of interrupts to fetch the data + xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL); + + #else // INT_USED + + // 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 LIS3DH sensor\n"); } diff --git a/extras/lis3dh/README.md b/extras/lis3dh/README.md index c43edbf..352ec95 100644 --- a/extras/lis3dh/README.md +++ b/extras/lis3dh/README.md @@ -581,9 +581,28 @@ sensor = lis3dh_init_sensor (SPI_BUS, 0, SPI_CS_GPIO); The remaining of the program is independent on the communication interface. +#### Configuring the sensor + +Optionally, you could wish to set some measurement parameters. For details see the sections above, the header file of the driver ```lis3dh.h```, and of course the data sheet of the sensor. + +#### Starting measurements + +As last step, the sensor mode has be set to start periodic measurement. The sensor mode can be changed anytime later. + +``` +... +// start periodic measurement with output data rate of 10 Hz +lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); +... +``` + #### Periodic user task -If initialization of the sensor was successful, the user task that uses the sensor has to be created. 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 a configured event happens. +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 a configured event happens. If new data are fetched **periodically** the implementation of the user task is quite simple and could look like following. @@ -677,21 +696,6 @@ gpio_set_interrupt(INT2_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler); Furthermore, the interrupts have to be enabled and configured in the LIS3DH sensor, see section **Interrupts** above. -#### Configuring the sensor - -Optionally, you could wish to set some measurement parameters. For details see the sections above, the header file of the driver ```lis3dh.h```, and of course the data sheet of the sensor. - -#### Starting measurements - -As last step, the sensor mode has be set to start periodic measurement. The sensor mode can be changed anytime later. - -``` -... -// start periodic measurement with output data rate of 10 Hz -lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); -... -``` - ## Full Example ``` @@ -820,9 +824,11 @@ void user_task_interrupt (void *pvParameters) // get the source of the interrupt and reset *INTx* signals #ifdef INT_DATA lis3dh_get_int_data_source (sensor, &data_src); - #elif INT_EVENT + #endif + #ifdef INT_EVENT lis3dh_get_int_event_source (sensor, &event_src, lis3dh_int_event1_gen); - #elif INT_CLICK + #endif + #ifdef INT_CLICK lis3dh_get_int_click_source (sensor, &click_src); #endif @@ -915,32 +921,25 @@ void user_init(void) if (sensor) { - // --- SYSTEM CONFIGURATION PART ---- + #ifdef INT_USED + + /** --- INTERRUPT CONFIGURATION PART ---- */ - #if !defined (INT_USED) + // Interrupt configuration has to be done before the sensor is set + // into measurement mode to avoid losing interrupts - // create a user task that fetches data from sensor periodically - xTaskCreate(user_task_periodic, "user_task_periodic", TASK_STACK_DEPTH, NULL, 2, NULL); - - #else // INT_USED - - // create a task that is triggered only in case of interrupts to fetch the data - xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL); - - // create event queue + // 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_set_interrupt(INT1_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler); - #endif // !defined(INT_USED) + #endif // INT_USED - // -- SENSOR CONFIGURATION PART --- + /** -- SENSOR CONFIGURATION PART --- */ - // Interrupt configuration has to be done before the sensor is set - // into measurement mode - // set polarity of INT signals if necessary // lis3dh_config_int_signals (sensor, lis3dh_high_active); @@ -1016,8 +1015,25 @@ void user_init(void) lis3dh_set_scale(sensor, lis3dh_scale_2_g); lis3dh_set_mode (sensor, lis3dh_odr_10, lis3dh_high_res, true, true, true); - // -- SENSOR CONFIGURATION PART --- + /** -- 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 only in case of interrupts to fetch the data + xTaskCreate(user_task_interrupt, "user_task_interrupt", TASK_STACK_DEPTH, NULL, 2, NULL); + + #else // INT_USED + + // 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 LIS3DH sensor\n"); } ``` diff --git a/extras/lis3dh/lis3dh_platform.h b/extras/lis3dh/lis3dh_platform.h index cf91541..0903533 100644 --- a/extras/lis3dh/lis3dh_platform.h +++ b/extras/lis3dh/lis3dh_platform.h @@ -66,12 +66,6 @@ #include "esp/spi.h" #include "i2c/i2c.h" -// platform specific definitions - -#define spi_semaphore_init(d) -#define spi_semaphore_take(d) -#define spi_semaphore_give(d) - // platform specific SPI functions #define spi_bus_init(bus,sck,miso,mosi) // not needed on ESP8266