L3GD20H 3-axes gyroscope driver (#545)
This commit is contained in:
parent
a4de9dd4f1
commit
37230b2de6
9 changed files with 3302 additions and 0 deletions
3
examples/l3gd20h/Makefile
Normal file
3
examples/l3gd20h/Makefile
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
PROGRAM=L3GD20H
|
||||||
|
EXTRA_COMPONENTS = extras/i2c extras/l3gd20h
|
||||||
|
include ../../common.mk
|
334
examples/l3gd20h/l3gd20h_example.c
Normal file
334
examples/l3gd20h/l3gd20h_example.c
Normal file
|
@ -0,0 +1,334 @@
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* for axis movement wake up *INT1* and data ready interrupt *INT2* is used
|
||||||
|
* or the new data are fetched periodically.
|
||||||
|
*
|
||||||
|
* Harware configuration:
|
||||||
|
*
|
||||||
|
* I2C
|
||||||
|
*
|
||||||
|
* +-----------------+ +----------+
|
||||||
|
* | ESP8266 / ESP32 | | L3GD20H |
|
||||||
|
* | | | |
|
||||||
|
* | GPIO 14 (SCL) ----> SCL |
|
||||||
|
* | GPIO 13 (SDA) <---> SDA |
|
||||||
|
* | GPIO 5 <---- INT1 |
|
||||||
|
* | GPIO 4 <---- DRDY/INT2|
|
||||||
|
* +-----------------+ +----------+
|
||||||
|
*
|
||||||
|
* SPI
|
||||||
|
*
|
||||||
|
* +---------------+ +----------+ +---------------+ +----------+
|
||||||
|
* | ESP8266 | | L3GD20H | | ESP32 | | L3GD20H |
|
||||||
|
* | | | | | | | |
|
||||||
|
* | 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 FIFO_MODE // multiple sample read mode
|
||||||
|
// #define INT_DATA // data interrupts used (data ready and FIFO status)
|
||||||
|
// #define INT_EVENT // event interrupts used (axis movement and wake up)
|
||||||
|
|
||||||
|
#if defined(INT_EVENT) || defined(INT_DATA)
|
||||||
|
#define INT_USED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* -- includes -------------------------------------------------- */
|
||||||
|
|
||||||
|
#include "l3gd20h.h"
|
||||||
|
|
||||||
|
/* -- platform dependent definitions ---------------------------- */
|
||||||
|
|
||||||
|
#ifdef ESP_PLATFORM // ESP32 (ESP-IDF)
|
||||||
|
|
||||||
|
// user task stack depth
|
||||||
|
#define TASK_STACK_DEPTH 2048
|
||||||
|
|
||||||
|
// define SPI interface for L3GD20H sensors
|
||||||
|
#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
|
||||||
|
#define TASK_STACK_DEPTH 256
|
||||||
|
|
||||||
|
// define SPI interface for L3GD20H sensors
|
||||||
|
#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
|
||||||
|
|
||||||
|
// define I2C interfaces for L3GD20H sensors
|
||||||
|
#define I2C_BUS 0
|
||||||
|
#define I2C_SCL_PIN 14
|
||||||
|
#define I2C_SDA_PIN 13
|
||||||
|
#define I2C_FREQ I2C_FREQ_100K
|
||||||
|
|
||||||
|
// define GPIOs for interrupt
|
||||||
|
#define INT1_PIN 5
|
||||||
|
#define INT2_PIN 4
|
||||||
|
|
||||||
|
/* -- user tasks ---------------------------------------------- */
|
||||||
|
|
||||||
|
static l3gd20h_sensor_t* sensor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common function used to get sensor data.
|
||||||
|
*/
|
||||||
|
void read_data (void)
|
||||||
|
{
|
||||||
|
#ifdef FIFO_MODE
|
||||||
|
|
||||||
|
l3gd20h_float_data_fifo_t data;
|
||||||
|
|
||||||
|
if (l3gd20h_new_data (sensor))
|
||||||
|
{
|
||||||
|
uint8_t num = l3gd20h_get_float_data_fifo (sensor, data);
|
||||||
|
printf("%.3f L3GD20H num=%d\n", (double)sdk_system_get_time()*1e-3, num);
|
||||||
|
for (int i = 0; i < num; i++)
|
||||||
|
// max. full scale is +-2000 dps and best sensitivity is 1 mdps, i.e. 7 digits
|
||||||
|
printf("%.3f L3GD20H (xyz)[dps]: %+9.3f %+9.3f %+9.3f\n",
|
||||||
|
(double)sdk_system_get_time()*1e-3, data[i].x, data[i].y, data[i].z);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !FIFO_MODE
|
||||||
|
|
||||||
|
l3gd20h_float_data_t data;
|
||||||
|
|
||||||
|
if (l3gd20h_new_data (sensor) &&
|
||||||
|
l3gd20h_get_float_data (sensor, &data))
|
||||||
|
// max. full scale is +-2000 dps and best sensitivity is 1 mdps, i.e. 7 digits
|
||||||
|
printf("%.3f L3GD20H (xyz)[dps]: %+9.3f %+9.3f %+9.3f\n",
|
||||||
|
(double)sdk_system_get_time()*1e-3, data.x, data.y, data.z);
|
||||||
|
|
||||||
|
#endif // FIFO_MODE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef INT_USED
|
||||||
|
/**
|
||||||
|
* In this case, axes movement wake up interrupt *INT1* and/or data ready
|
||||||
|
* interrupt *INT2* are used. While data ready interrupt *INT2* is generated
|
||||||
|
* every time new data are available or the FIFO status changes, the axes
|
||||||
|
* movement wake up interrupt *INT1* is triggered when output data across
|
||||||
|
* defined thresholds.
|
||||||
|
*
|
||||||
|
* 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))
|
||||||
|
{
|
||||||
|
if (gpio_num == INT1_PIN)
|
||||||
|
{
|
||||||
|
l3gd20h_int_event_source_t source;
|
||||||
|
|
||||||
|
// get the source of INT1 reset INT1 signal
|
||||||
|
l3gd20h_get_int_event_source (sensor, &source);
|
||||||
|
|
||||||
|
// in case of data ready interrupt, get the results and do something with them
|
||||||
|
if (source.active)
|
||||||
|
read_data ();
|
||||||
|
}
|
||||||
|
else if (gpio_num == INT2_PIN)
|
||||||
|
{
|
||||||
|
l3gd20h_int_data_source_t source;
|
||||||
|
|
||||||
|
// get the source of INT2
|
||||||
|
l3gd20h_get_int_data_source (sensor, &source);
|
||||||
|
|
||||||
|
// if data ready interrupt, get the results and do something with them
|
||||||
|
read_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !INT_USED
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In this case, no interrupts are used and the user task fetches the sensor
|
||||||
|
* values periodically 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 = l3gd20h_init_sensor (SPI_BUS, 0, SPI_CS_GPIO);
|
||||||
|
|
||||||
|
#else // I2C
|
||||||
|
|
||||||
|
// init all I2C bus interfaces at which L3GD20H sensors are connected
|
||||||
|
i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ);
|
||||||
|
|
||||||
|
// init the sensor with slave address L3GD20H_I2C_ADDRESS_2 connected to I2C_BUS.
|
||||||
|
sensor = l3gd20h_init_sensor (I2C_BUS, L3GD20H_I2C_ADDRESS_2, 0);
|
||||||
|
|
||||||
|
#endif // SPI_USED
|
||||||
|
|
||||||
|
if (sensor)
|
||||||
|
{
|
||||||
|
#ifdef INT_USED
|
||||||
|
|
||||||
|
/** --- INTERRUPT CONFIGURATION PART ---- */
|
||||||
|
|
||||||
|
// Interrupt configuration has to be done before the sensor is set
|
||||||
|
// into measurement mode to avoid losing interrupts
|
||||||
|
|
||||||
|
// create an event queue to send interrupt events from interrupt
|
||||||
|
// handler to the interrupt task
|
||||||
|
gpio_evt_queue = xQueueCreate(10, sizeof(uint8_t));
|
||||||
|
|
||||||
|
// configure interupt pins for *INT1* and *INT2* signals and set the
|
||||||
|
// interrupt handler
|
||||||
|
gpio_enable(INT1_PIN, GPIO_INPUT);
|
||||||
|
gpio_enable(INT2_PIN, GPIO_INPUT);
|
||||||
|
gpio_set_interrupt(INT1_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler);
|
||||||
|
gpio_set_interrupt(INT2_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler);
|
||||||
|
|
||||||
|
#endif // INT_USED
|
||||||
|
|
||||||
|
/** -- SENSOR CONFIGURATION PART --- */
|
||||||
|
|
||||||
|
// set type and polarity of INT signals if necessary
|
||||||
|
// l3gd20h_config_int_signals (dev, l3gd20h_push_pull, l3gd20h_high_active);
|
||||||
|
|
||||||
|
#ifdef INT_EVENT
|
||||||
|
// enable event interrupts (axis movement and wake up)
|
||||||
|
l3gd20h_int_event_config_t int_cfg;
|
||||||
|
|
||||||
|
l3gd20h_get_int_event_config (sensor, &int_cfg);
|
||||||
|
|
||||||
|
int_cfg.x_high_enabled = true;
|
||||||
|
int_cfg.y_high_enabled = true;
|
||||||
|
int_cfg.z_high_enabled = true;
|
||||||
|
int_cfg.x_low_enabled = false;
|
||||||
|
int_cfg.y_low_enabled = false;
|
||||||
|
int_cfg.z_low_enabled = false;
|
||||||
|
int_cfg.x_threshold = 1000;
|
||||||
|
int_cfg.y_threshold = 1000;
|
||||||
|
int_cfg.z_threshold = 1000;
|
||||||
|
|
||||||
|
int_cfg.filter = l3gd20h_hpf_only;
|
||||||
|
int_cfg.and_or = false;
|
||||||
|
int_cfg.duration = 0;
|
||||||
|
int_cfg.latch = true;
|
||||||
|
|
||||||
|
l3gd20h_set_int_event_config (sensor, &int_cfg);
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_event, true);
|
||||||
|
|
||||||
|
#endif // INT_EVENT
|
||||||
|
|
||||||
|
#ifdef INT_DATA
|
||||||
|
// enable data ready (DRDY) and FIFO interrupt signal *INT2*
|
||||||
|
// NOTE: DRDY and FIFO interrupts must not be enabled at the same time
|
||||||
|
#ifdef FIFO_MODE
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_fifo_overrun, true);
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_fifo_threshold, true);
|
||||||
|
#else
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_data_ready, true);
|
||||||
|
#endif
|
||||||
|
#endif // INT_DATA
|
||||||
|
|
||||||
|
#ifdef FIFO_MODE
|
||||||
|
// clear FIFO and activate FIFO mode if needed
|
||||||
|
l3gd20h_set_fifo_mode (sensor, l3gd20h_bypass, 0);
|
||||||
|
l3gd20h_set_fifo_mode (sensor, l3gd20h_stream, 10);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// select LPF/HPF, configure HPF and reset the reference by dummy read
|
||||||
|
l3gd20h_select_output_filter (sensor, l3gd20h_hpf_only);
|
||||||
|
l3gd20h_config_hpf (sensor, l3gd20h_hpf_normal, 0);
|
||||||
|
l3gd20h_get_hpf_ref (sensor);
|
||||||
|
|
||||||
|
// LAST STEP: Finally set scale and sensor mode to start measurements
|
||||||
|
l3gd20h_set_scale(sensor, l3gd20h_scale_245_dps);
|
||||||
|
l3gd20h_set_mode (sensor, l3gd20h_normal_odr_12_5, 3, true, true, true);
|
||||||
|
|
||||||
|
/** -- 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 L3GD20H sensor\n");
|
||||||
|
}
|
||||||
|
|
983
extras/l3gd20h/README.md
Normal file
983
extras/l3gd20h/README.md
Normal file
|
@ -0,0 +1,983 @@
|
||||||
|
# Driver for the L3GD20H 3-axes digital output gyroscope
|
||||||
|
|
||||||
|
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/l3gd20h](https://github.com/SuperHouse/esp-open-rtos/tree/master/extras) of original repository, it is not yet merged. Please take a look to branch [l3gd20h](https://github.com/gschorcht/esp-open-rtos/tree/l3gd20h) 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 L3GD20 and L3G4200D.
|
||||||
|
|
||||||
|
## About the sensor
|
||||||
|
|
||||||
|
L3GD20H is a low-power **3-axis angular rate sensor** connected to **I2C** or **SPI** with a full scale of up to **2000 dps**. It supports different measuring rates with a user selectable bandwidth.
|
||||||
|
|
||||||
|
**Main features** of the sensor are:
|
||||||
|
|
||||||
|
- 3 selectable full scales of ±245, ±500, and ±2000 dps
|
||||||
|
- 7 measuring rates from 12.5 Hz to 800 Hz with 4 bandwidths
|
||||||
|
- 16 bit angular rate value data output
|
||||||
|
- 8 bit temperature data output
|
||||||
|
- 2 dedicated interrupt signals for data and event interrupts
|
||||||
|
- integrated high-pass filters with 3 modes and 10 different cut off frequencies
|
||||||
|
- embedded temperature sensor
|
||||||
|
- embedded 32 levels of 16 bit data output FIFO
|
||||||
|
- I2C and SPI digital output interface
|
||||||
|
- embedded power-down and sleep mode with fast turn-on and wake-up
|
||||||
|
|
||||||
|
## Sensor operation
|
||||||
|
|
||||||
|
### Sensor modes
|
||||||
|
|
||||||
|
L3GD20H provides different operating modes.
|
||||||
|
|
||||||
|
- **Power Down mode** is configured automatically after power up boot sequence. In this mode, all gyros are switched off. Therefore, it takes up to 100 ms to switch to another mode.
|
||||||
|
|
||||||
|
- **Normal mode** is the normal measurement mode. All gyros are switched on and at least one axis is enabled for measurements. Measurements are performed at a defined output data rate (**ODR**).
|
||||||
|
|
||||||
|
- **Sleep mode** is the normal mode when no axis is enabled for measurement. In this modes, all gyros are kept switched on. Therefore, it only takes 1/ODR to switch to normal mode if low pass filtering is disabled or 6/ODR if low pass filtering is enabled.
|
||||||
|
|
||||||
|
### Output Data Rates
|
||||||
|
|
||||||
|
In normal mode, measurements are performed at a defined output rate with a user selectable bandwidth. Following output data rates (ODR) are supported.
|
||||||
|
|
||||||
|
Mode | Output Data Rate (ODR) | Driver symbol
|
||||||
|
:---- |:----------------------:|:------------------
|
||||||
|
Power down | - | ```l3gd20h_power_down```
|
||||||
|
Normal mode | 12.5 Hz | ```l3gd20h_normal_12_5```
|
||||||
|
Normal mode | 25 Hz | ```l3gd20h_normal_25```
|
||||||
|
Normal mode | 50 Hz | ```l3gd20h_normal_50```
|
||||||
|
Normal mode | 100 Hz | ```l3gd20h_normal_100```
|
||||||
|
Normal mode | 200 Hz | ```l3gd20h_normal_200```
|
||||||
|
Normal mode | 400 Hz | ```l3gd20h_normal_400```
|
||||||
|
Normal mode | 800 Hz | ```l3gd20h_normal_800```
|
||||||
|
|
||||||
|
Output data rates (ODR) of less than 100 Hz are called Low Data Rates.
|
||||||
|
|
||||||
|
For each ODR, one of the four bandwidths 0...3 can be selected that defines the cutoff frequency (please refer datasheet) of an embedded low pass filter for the measurement results.
|
||||||
|
|
||||||
|
The **easiest way to use the sensor** is simply to initialize it with function ```l3gd20h_init_sensor``` and then set it to the normal mode with function ```l3gd20h_set_mode``` to start measurements with the given output data rate (ODR). The bandwidth of the embedded low-pass filter and the axes x, y and z that are activated for measurements are also given as parameters.
|
||||||
|
|
||||||
|
```
|
||||||
|
...
|
||||||
|
static l3gd20h_sensor_t* sensor = 0;
|
||||||
|
...
|
||||||
|
if ((sensor = l3gd20h_init_sensor (I2C_BUS, L3GD20H_I2C_ADDRESS_2, 0)))
|
||||||
|
{
|
||||||
|
...
|
||||||
|
l3gd20h_set_mode (sensor, l3gd20h_normal_odr_200, 3, true, true, true);
|
||||||
|
...
|
||||||
|
}
|
||||||
|
...
|
||||||
|
|
||||||
|
```
|
||||||
|
In this example, a L3GD20H sensor connected to I2C is initialized and set to normal mode to start measurements for all three axes with an output data rate (ODR) of 200 Hz and bandwidth 3 (please refer datasheet).
|
||||||
|
|
||||||
|
**Please note:**
|
||||||
|
- Function ```l3gd20h_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 ```l3gd20h_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
|
||||||
|
|
||||||
|
In normal mode, sensor determines periodically the angular rate for all axes that are enabled for measurement and produces output data with the selected output data rate (ODR).
|
||||||
|
|
||||||
|
Function ```l3gd20h_new_data``` or the data ready interrupt (see below) can be used to determine when new data are available.
|
||||||
|
|
||||||
|
Raw **output data** (**raw data**) are given as 16-bit signed integer values in 2’s complement representation. The range and the resolution of these data depend on the sensitivity of the sensor which is selected by the **full scale** value. The L3GD20H allows to select the following full scales:
|
||||||
|
|
||||||
|
Full Scale | Resolution | Driver symbol
|
||||||
|
-----------:|-----------:|:---------------
|
||||||
|
±245 dps | 2 mdps | ```l3gd20h_scale_245_dps```
|
||||||
|
±500 dps | 4 mdps | ```l3gd20h_scale_500_dps```
|
||||||
|
±2000 dps | 16 mdps | ```l3gd20h_scale_2000_dps```
|
||||||
|
|
||||||
|
By default, a full scale of ±245 dps is used. Function ```l3gd20h_set_scale``` can be used to change it.
|
||||||
|
|
||||||
|
```
|
||||||
|
l3gd20h_set_scale(sensor, l3gd20h_scale_500_dps);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fetching output data
|
||||||
|
|
||||||
|
To get the information whether new data are available, the user task can either use
|
||||||
|
|
||||||
|
- the function ```l3gd20h_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 ```l3gd20h_get_raw_data``` or
|
||||||
|
- as floating point values in dps (degrees per second) using function ```l3gd20h_get_float_data```.
|
||||||
|
|
||||||
|
It is recommended to use function ```l3gd20h_get_float_data``` since it already converts measurement results to real values according to the selected full scale.
|
||||||
|
|
||||||
|
```
|
||||||
|
void user_task_periodic(void *pvParameters)
|
||||||
|
{
|
||||||
|
l3gd20h_float_data_t data;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
// execute task every 10 ms
|
||||||
|
vTaskDelay (10/portTICK_PERIOD_MS);
|
||||||
|
...
|
||||||
|
// test for new data
|
||||||
|
if (!l3gd20h_new_data (sensor))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// fetch new data
|
||||||
|
if (l3gd20h_get_float_data (sensor, &data))
|
||||||
|
{
|
||||||
|
// do something with data
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Please note:**
|
||||||
|
The functions ```l3gd20h_get_float_data``` and ```l3gd20h_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.
|
||||||
|
|
||||||
|
### Filters
|
||||||
|
|
||||||
|
L3GD20H provides embedded low-pass as well as high-pass filtering capabilities to improve measurement results.
|
||||||
|
It is possible to independently apply the filters on the output data and/or on the data used for event interrupt generation (selective axis movement and wake up, see below) separately. Please refer the [datasheet](http://www.st.com/resource/en/datasheet/l3gd20.pdf) or [application note](http://www.st.com/resource/en/application_note/dm00119036.pdf) for more details.
|
||||||
|
|
||||||
|
The filters applied to the output data are selected with function ```l3gd20h_select_output_filter```. Following selections are possible:
|
||||||
|
|
||||||
|
Driver symbol | Low pass filter (LPF2) used | High pass filter (HPF) used
|
||||||
|
:--------------|:-----------------------------:|:---------------------:
|
||||||
|
```l3gd20h_no_filter``` | - | -
|
||||||
|
```l3gd20h_hpf_only``` | x | -
|
||||||
|
```l3gd20h_lpf2_only``` | - | x
|
||||||
|
```l3gd20h_hpf_and_lpf2``` | x | x
|
||||||
|
|
||||||
|
These filters can also be applied to data used for event interrupt generation (selective axis movement and wake up). The filter mode is defined by member ```filter``` in the settings of interrupt generator configuration, see function ```l3gd20h_set_int_event_config```.
|
||||||
|
|
||||||
|
While the cutoff frequency of the low pass filter (LPF2) is fixed and depends only on the output data rate (ODR), the mode and the cutoff frequency of the high pass filter can be configured using function ```l3gd20h_config_hpf```. Following HPF modes are available:
|
||||||
|
|
||||||
|
Driver symbol | HPF mode
|
||||||
|
:--------------|:---------
|
||||||
|
```l3gd20h_hpf_normal``` | Normal mode
|
||||||
|
```l3gd20h_hpf_reference``` | Reference mode
|
||||||
|
```l3gd20h_hpf_autoreset``` | Auto-reset on interrupt
|
||||||
|
|
||||||
|
For each output data rate (ODR), 10 different HPF cutoff frequencies can be used.
|
||||||
|
|
||||||
|
```
|
||||||
|
...
|
||||||
|
// select LPF/HPF
|
||||||
|
l3gd20h_select_output_filter (sensor, l3gd20h_hpf_only);
|
||||||
|
|
||||||
|
// configure HPF in normal mode with cutoff frequency 0
|
||||||
|
l3gd20h_config_hpf (sensor, l3gd20h_hpf_normal, 2);
|
||||||
|
|
||||||
|
// reset the reference by a dummy read
|
||||||
|
l3gd20h_get_hpf_ref (sensor);
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Please note:** Since same filters are used for the output data as well as the data used for event interrupt generation (selective axes movement / wake up), the configuration of the filters always affects both data.
|
||||||
|
|
||||||
|
|
||||||
|
### FIFO
|
||||||
|
|
||||||
|
In order to limit the rate at which the host processor has to fetch the data, the L3GD20H embeds a first-in first-out buffer (FIFO). This is in particular helpful at high output data rates. The FIFO buffer can work in seven different modes and is able to store up to 32 angular rate samples. Please refer the [datasheet](http://www.st.com/resource/en/datasheet/l3gd20.pdf) or [application note](http://www.st.com/resource/en/application_note/dm00119036.pdf) for more details.
|
||||||
|
|
||||||
|
Driver symbol | FIFO mode
|
||||||
|
--------------|-------------------------
|
||||||
|
```l3gd20h_bypass``` | Bypass mode (FIFO is not used)
|
||||||
|
```l3gd20h_fifo``` | FIFO mode
|
||||||
|
```l3gd20h_stream``` | Stream mode
|
||||||
|
```l3gd20h_stream_to_fifo``` | Stream-to-FIFO mode
|
||||||
|
```l3gd20h_bypass_to_stream``` | Bypass-to-Stream mode
|
||||||
|
```l3gd20h_dynamic_stream``` | Dynamic Stream mode
|
||||||
|
```l3gd20h_bypass_to_fifo``` | Bypass to FIFO mode
|
||||||
|
|
||||||
|
The FIFO mode can be set using function ```l3gd20h_set_fifo_mode```. This function takes two parameters, the FIFO mode and a threshold value which defines a watermark level. When the FIFO content exceeds this level, a watermark flag is set and an interrupt can be generated. They can be used to gather a minimum number of axes angular rate samples with the sensor before the data are fetched as a single read operation from the sensor.
|
||||||
|
|
||||||
|
```
|
||||||
|
...
|
||||||
|
// clear FIFO
|
||||||
|
l3gd20h_set_fifo_mode (sensor, l3gd20h_bypass, 0);
|
||||||
|
|
||||||
|
// activate FIFO mode
|
||||||
|
l3gd20h_set_fifo_mode (sensor, l3gd20h_stream, 10);
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Please note**: To clear the FIFO at any time, set the FIFO mode to ```l3gd20h_bypass``` and back to the desired FIFO mode.
|
||||||
|
|
||||||
|
To read data from the FIFO, simply use either
|
||||||
|
|
||||||
|
- the function ```l3gd20h_get_raw_data_fifo``` to all get raw output data stored in FIFO or
|
||||||
|
- the function ```l3gd20h_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)
|
||||||
|
{
|
||||||
|
l3gd20h_float_data_fifo_t data;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
// execute task every 500 ms
|
||||||
|
vTaskDelay (500/portTICK_PERIOD_MS);
|
||||||
|
...
|
||||||
|
// test for new data
|
||||||
|
if (!l3gd20h_new_data (sensor))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// fetch data from fifo
|
||||||
|
uint8_t num = l3gd20h_get_float_data_fifo (sensor, data);
|
||||||
|
|
||||||
|
for (int i = 0; i < num; i++)
|
||||||
|
{
|
||||||
|
// do something with data[i] ...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Interrupts
|
||||||
|
|
||||||
|
The L3GD20H allows to activate interrupts on two dedicated interrupt signals
|
||||||
|
|
||||||
|
- for data interrupts (data ready and FIFO status) on signal **```DRDY/INT2```**, and
|
||||||
|
- for event interrupts (axis movement and wake up) on signal **```INT1```**.
|
||||||
|
|
||||||
|
### Data interrupts (data ready and FIFO status) on signal **```DRDY/INT2```**
|
||||||
|
|
||||||
|
Interrupts on signal ```DRDY/INT2``` can be generated by following sources:
|
||||||
|
|
||||||
|
Interrupt source | Driver symbol
|
||||||
|
:-----------------|:-------------
|
||||||
|
Output data become ready to read | ```l3gd20h_int_data_ready```
|
||||||
|
FIFO content exceeds the watermark level | ```l3gd20h_int_fifo_threshold```
|
||||||
|
FIFO is completely filled | ```l3gd20h_int_fifo_overrun```
|
||||||
|
FIFO becomes empty | ```l3gd20h_int_fifo_empty```
|
||||||
|
|
||||||
|
Each of these interrupt sources can be enabled or disabled separately with function ```l3gd20h_enable_int```. By default all interrupt sources are disabled.
|
||||||
|
|
||||||
|
```
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_data_ready, true);
|
||||||
|
```
|
||||||
|
|
||||||
|
Whenever the interrupt signal ```DRDY/INT2``` is generated, function ```l3gd20h_get_int_data_source``` can be used to determine the source of the interrupt signal. This function returns a data structure of type ```l3gd20h_int_data_source_t``` that contains a boolean member for each source that can be tested for true.
|
||||||
|
|
||||||
|
```
|
||||||
|
void int2_handler ()
|
||||||
|
{
|
||||||
|
l3gd20h_int_data_source_t source;
|
||||||
|
|
||||||
|
// get the interrupt source of INT2
|
||||||
|
l3gd20h_get_int_data_source (sensor, &source);
|
||||||
|
|
||||||
|
// in case of data ready interrupt, get the results and do something with them
|
||||||
|
if (source.data_ready)
|
||||||
|
{
|
||||||
|
l3gd20h_get_float_data (sensor, &data)
|
||||||
|
|
||||||
|
// do something with data
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Event interrupts (Axes movement and wake up) on signal **```INT1```**
|
||||||
|
|
||||||
|
This interrupt signal allows to recognize independent rotations of the x, y and z axes. For this purpose, a separate threshold can be defined for each axis. If activated, the angular rate of each axis is compared with its threshold to check whether it is below or above the threshold. The results of all activated comparisons are combined OR or AND to generate the interrupt signal.
|
||||||
|
|
||||||
|
The configuration of the thresholds, the activated comparisons and selected AND/OR combination allows to recognize special situations like selective axis movement (SA) or axes movement wakeup (WU).
|
||||||
|
|
||||||
|
- **Selective axis movement recognition (SA)** means that only one axis is rotating. This is the case if the angular rate of selected axis is above its threshold AND angular rates of all other axes are below their thresholds.
|
||||||
|
|
||||||
|
- **Axis movement wake up (WU)** means that the angular rate of any axis is above its threshold (OR).
|
||||||
|
|
||||||
|
To configure event interrupts, the function ```l3gd20h_set_int_event_config``` has to be used with a parameter of structured data type ```l3gd20h_int_event_config_t``` which contains the configuration. For example, selective axis movement recognition (SA) for the z-axis could be configured as following:
|
||||||
|
|
||||||
|
```
|
||||||
|
l3gd20h_int_event_config_t int_cfg;
|
||||||
|
|
||||||
|
// thresholds
|
||||||
|
int_cfg.x_threshold = 100;
|
||||||
|
int_cfg.y_threshold = 100;
|
||||||
|
int_cfg.z_threshold = 1000;
|
||||||
|
|
||||||
|
// x axis below threshold
|
||||||
|
int_cfg.x_low_enabled = false;
|
||||||
|
int_cfg.x_high_enabled = true;
|
||||||
|
|
||||||
|
// y axis below threshold
|
||||||
|
int_cfg.y_low_enabled = true;
|
||||||
|
int_cfg.y_high_enabled = false;
|
||||||
|
|
||||||
|
// z axis below threshold
|
||||||
|
int_cfg.z_low_enabled = false;
|
||||||
|
int_cfg.z_high_enabled = true;
|
||||||
|
|
||||||
|
// AND combination of all conditions
|
||||||
|
int_cfg.and_or = true;
|
||||||
|
|
||||||
|
// further parameters
|
||||||
|
int_cfg.filter = l3gd20h_hpf_only;
|
||||||
|
int_cfg.latch = true;
|
||||||
|
int_cfg.duration = 0;
|
||||||
|
int_cfg.wait = false;
|
||||||
|
|
||||||
|
// set the configuration and enable the interrupt
|
||||||
|
l3gd20h_set_int_cfg (sensor, &int_cfg);
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_event, true);
|
||||||
|
```
|
||||||
|
|
||||||
|
Furthermore, with this data structure it is also configured
|
||||||
|
|
||||||
|
- whether the interrupt signal should latched until the interrupt source is read,
|
||||||
|
- which filters are applied to data used for interrupt generation,
|
||||||
|
- which time in 1/ODR an interrupt condition has to be given before the interrupt is generated, and
|
||||||
|
- whether this time is also used when interrupt condition in no longer given before interrupt signal is reset.
|
||||||
|
|
||||||
|
Function ```l3gd20h_enable_int``` is used to enable or disable the event interrupt generation.
|
||||||
|
|
||||||
|
As with data ready and FIFO interrupts, function ```l3gd20h_get_int1_source``` can be used to determine the source of the interrupt signal whenever it is generated. This function returns a data structure of type ```l3gd20h_int1_source_t``` that contain a boolean member for each source that can be tested for true.
|
||||||
|
|
||||||
|
```
|
||||||
|
void int1_handler ()
|
||||||
|
{
|
||||||
|
l3gd20h_int_event_source_t source;
|
||||||
|
|
||||||
|
// get the source of INT1 reset INT1 signal
|
||||||
|
l3gd20h_get_int_event_source (sensor, &source);
|
||||||
|
|
||||||
|
// if all conditions where true interrupt
|
||||||
|
if (source.active)
|
||||||
|
{
|
||||||
|
l3gd20h_get_float_data (sensor, &data)
|
||||||
|
|
||||||
|
// do something with 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 is the most flexible way, functions like selective axis movement can then be realized combining the different interrupt sources. Following example realizes also the selective axis movement recognition (SA) for the z-axis.
|
||||||
|
|
||||||
|
```
|
||||||
|
l3gd20h_int_event_config_t int_cfg;
|
||||||
|
|
||||||
|
// thresholds
|
||||||
|
int_cfg.x_threshold = 100;
|
||||||
|
int_cfg.y_threshold = 100;
|
||||||
|
int_cfg.z_threshold = 100;
|
||||||
|
|
||||||
|
// x axis
|
||||||
|
int_cfg.x_low_enabled = true;
|
||||||
|
int_cfg.x_high_enabled = true;
|
||||||
|
|
||||||
|
// y axis
|
||||||
|
int_cfg.y_low_enabled = true;
|
||||||
|
int_cfg.y_high_enabled = true;
|
||||||
|
|
||||||
|
// z axis
|
||||||
|
int_cfg.z_low_enabled = true;
|
||||||
|
int_cfg.z_high_enabled = true;
|
||||||
|
|
||||||
|
// OR combination of all conditions
|
||||||
|
int_cfg.and_or = false;
|
||||||
|
...
|
||||||
|
// set the configuration and enable the interrupt
|
||||||
|
l3gd20h_set_int_cfg (sensor, &int_cfg);
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_event, true);
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
void int1_handler ()
|
||||||
|
{
|
||||||
|
l3gd20h_int1_source_t source;
|
||||||
|
|
||||||
|
// get the interrupt source of INT1
|
||||||
|
l3gd20h_get_int1_source (sensor, &source);
|
||||||
|
|
||||||
|
// if all conditions where true interrupt
|
||||||
|
if (source.y_low && source.y_low && source.z_high)
|
||||||
|
{
|
||||||
|
// selective axis movement of z-axis
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Interrupt signal properties
|
||||||
|
|
||||||
|
By default, interrupt signals are high active. Using function ```l3gd20h_config_int_signals```, the level of the interrupt signal and the type of the interrupt outputs can be changed.
|
||||||
|
|
||||||
|
Driver symbol | Meaning
|
||||||
|
:-------------|:-------
|
||||||
|
```l3gd20h_high_active``` | Interrupt signal is high active (default)
|
||||||
|
```l3gd20h_low_active``` | Interrupt signal is low active
|
||||||
|
|
||||||
|
Driver symbol | Meaning
|
||||||
|
:-------------|:-------
|
||||||
|
```l3gd20h_push_pull``` | Interrupt output is pushed/pulled
|
||||||
|
```l3gd20h_open_drain``` | Interrupt output is open-drain
|
||||||
|
|
||||||
|
## Temperature sensor
|
||||||
|
|
||||||
|
The L3GD20H contains a temperature sensor. Function ```l3gd20h_get_temperature``` can be used to get the temperature. The temperature is given as 8-bit signed integer values in 2’s complement.
|
||||||
|
|
||||||
|
## Low level functions
|
||||||
|
|
||||||
|
The L3GD20H 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 l3gd20h_reg_read (l3gd20h_sensor_t* dev, uint8_t reg, uint8_t *data, uint16_t len);
|
||||||
|
bool l3gd20h_reg_write (l3gd20h_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 | | L3GD20H |
|
||||||
|
| | | |
|
||||||
|
| 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 | | L3GD20H | | ESP32 | | L3GD20H |
|
||||||
|
| | | | | | | |
|
||||||
|
| 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|
|
||||||
|
+-----------------+ +----------+ +-----------------+ +----------+
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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 ```l3gd20h_init_sensor``` has to be called for each L3GD20H 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 l3gd20h_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 = l3gd20h_init_sensor (I2C_BUS, L3GD20H_I2C_ADDRESS_2, 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 = l3gd20h_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 ```l3gd20h.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 12.5 Hz
|
||||||
|
l3gd20h_set_mode (sensor, l3gd20h_normal_odr_12_5, 3, true, true, true);
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Periodic user task
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
```
|
||||||
|
void user_task_periodic(void *pvParameters)
|
||||||
|
{
|
||||||
|
l3gd20h_float_data_t data;
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
// execute task every 10 ms
|
||||||
|
vTaskDelay (10/portTICK_PERIOD_MS);
|
||||||
|
...
|
||||||
|
// test for new data
|
||||||
|
if (!l3gd20h_new_data (sensor))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// fetch new data
|
||||||
|
if (l3gd20h_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.
|
||||||
|
|
||||||
|
- **```DRDY/INT2```** is triggered when new data become available or the FIFO queue status changes.
|
||||||
|
- **```INT1```** is triggered when configured axis movements are recognized.
|
||||||
|
|
||||||
|
In both cases, 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 (!l3gd20h_new_data (sensor))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// fetch new data
|
||||||
|
if (l3gd20h_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 L3GD20H sensor, see section **Interrupts** above.
|
||||||
|
|
||||||
|
## Full Example
|
||||||
|
|
||||||
|
```
|
||||||
|
/* -- use following constants to define the example mode ----------- */
|
||||||
|
|
||||||
|
// #define SPI_USED // if defined SPI 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 // event interrupts used (axis movement and wake up)
|
||||||
|
|
||||||
|
#if defined(INT_EVENT) || defined(INT_DATA)
|
||||||
|
#define INT_USED
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* -- includes -------------------------------------------------- */
|
||||||
|
|
||||||
|
#include "l3gd20h.h"
|
||||||
|
|
||||||
|
/* -- platform dependent definitions ---------------------------- */
|
||||||
|
|
||||||
|
#ifdef ESP_PLATFORM // ESP32 (ESP-IDF)
|
||||||
|
|
||||||
|
// user task stack depth
|
||||||
|
#define TASK_STACK_DEPTH 2048
|
||||||
|
|
||||||
|
// define SPI interface for L3GD20H sensors
|
||||||
|
#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
|
||||||
|
#define TASK_STACK_DEPTH 256
|
||||||
|
|
||||||
|
// define SPI interface for L3GD20H sensors
|
||||||
|
#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
|
||||||
|
|
||||||
|
// define I2C interfaces for L3GD20H sensors
|
||||||
|
#define I2C_BUS 0
|
||||||
|
#define I2C_SCL_PIN 14
|
||||||
|
#define I2C_SDA_PIN 13
|
||||||
|
#define I2C_FREQ I2C_FREQ_100K
|
||||||
|
|
||||||
|
// define GPIOs for interrupt
|
||||||
|
#define INT1_PIN 5
|
||||||
|
#define INT2_PIN 4
|
||||||
|
|
||||||
|
/* -- user tasks ---------------------------------------------- */
|
||||||
|
|
||||||
|
static l3gd20h_sensor_t* sensor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common function used to get sensor data.
|
||||||
|
*/
|
||||||
|
void read_data (void)
|
||||||
|
{
|
||||||
|
#ifdef FIFO_MODE
|
||||||
|
|
||||||
|
l3gd20h_float_data_fifo_t data;
|
||||||
|
|
||||||
|
if (l3gd20h_new_data (sensor))
|
||||||
|
{
|
||||||
|
uint8_t num = l3gd20h_get_float_data_fifo (sensor, data);
|
||||||
|
printf("%.3f L3GD20H num=%d\n", (double)sdk_system_get_time()*1e-3, num);
|
||||||
|
for (int i = 0; i < num; i++)
|
||||||
|
// max. full scale is +-2000 dps and best sensitivity is 1 mdps, i.e. 7 digits
|
||||||
|
printf("%.3f L3GD20H (xyz)[dps]: %+9.3f %+9.3f %+9.3f\n",
|
||||||
|
(double)sdk_system_get_time()*1e-3, data[i].x, data[i].y, data[i].z);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !FIFO_MODE
|
||||||
|
|
||||||
|
l3gd20h_float_data_t data;
|
||||||
|
|
||||||
|
if (l3gd20h_new_data (sensor) &&
|
||||||
|
l3gd20h_get_float_data (sensor, &data))
|
||||||
|
// max. full scale is +-2000 dps and best sensitivity is 1 mdps, i.e. 7 digits
|
||||||
|
printf("%.3f L3GD20H (xyz)[dps]: %+9.3f %+9.3f %+9.3f\n",
|
||||||
|
(double)sdk_system_get_time()*1e-3, data.x, data.y, data.z);
|
||||||
|
|
||||||
|
#endif // FIFO_MODE
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef INT_USED
|
||||||
|
/**
|
||||||
|
* In this case, axes movement wake up interrupt *INT1* and/or data ready
|
||||||
|
* interrupt *INT2* are used. While data ready interrupt *INT2* is generated
|
||||||
|
* every time new data are available or the FIFO status changes, the axes
|
||||||
|
* movement wake up interrupt *INT1* is triggered when output data across
|
||||||
|
* defined thresholds.
|
||||||
|
*
|
||||||
|
* 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))
|
||||||
|
{
|
||||||
|
if (gpio_num == INT1_PIN)
|
||||||
|
{
|
||||||
|
l3gd20h_int_event_source_t source;
|
||||||
|
|
||||||
|
// get the source of INT1 reset INT1 signal
|
||||||
|
l3gd20h_get_int_event_source (sensor, &source);
|
||||||
|
|
||||||
|
// in case of data ready interrupt, get the results and do something with them
|
||||||
|
if (source.active)
|
||||||
|
read_data ();
|
||||||
|
}
|
||||||
|
else if (gpio_num == INT2_PIN)
|
||||||
|
{
|
||||||
|
l3gd20h_int_data_source_t source;
|
||||||
|
|
||||||
|
// get the source of INT2
|
||||||
|
l3gd20h_get_int_data_source (sensor, &source);
|
||||||
|
|
||||||
|
// if data ready interrupt, get the results and do something with them
|
||||||
|
read_data();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // !INT_USED
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In this case, no interrupts are used and the user task fetches the sensor
|
||||||
|
* values periodically 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 = l3gd20h_init_sensor (SPI_BUS, 0, SPI_CS_GPIO);
|
||||||
|
|
||||||
|
#else // I2C
|
||||||
|
|
||||||
|
// init all I2C bus interfaces at which L3GD20H sensors are connected
|
||||||
|
i2c_init (I2C_BUS, I2C_SCL_PIN, I2C_SDA_PIN, I2C_FREQ);
|
||||||
|
|
||||||
|
// init the sensor with slave address L3GD20H_I2C_ADDRESS_2 connected to I2C_BUS.
|
||||||
|
sensor = l3gd20h_init_sensor (I2C_BUS, L3GD20H_I2C_ADDRESS_2, 0);
|
||||||
|
|
||||||
|
#endif // SPI_USED
|
||||||
|
|
||||||
|
if (sensor)
|
||||||
|
{
|
||||||
|
#ifdef INT_USED
|
||||||
|
|
||||||
|
/** --- INTERRUPT CONFIGURATION PART ---- */
|
||||||
|
|
||||||
|
// Interrupt configuration has to be done before the sensor is set
|
||||||
|
// into measurement mode to avoid losing interrupts
|
||||||
|
|
||||||
|
// create an event queue to send interrupt events from interrupt
|
||||||
|
// handler to the interrupt task
|
||||||
|
gpio_evt_queue = xQueueCreate(10, sizeof(uint8_t));
|
||||||
|
|
||||||
|
// configure interupt pins for *INT1* and *INT2* signals and set the
|
||||||
|
// interrupt handler
|
||||||
|
gpio_enable(INT1_PIN, GPIO_INPUT);
|
||||||
|
gpio_enable(INT2_PIN, GPIO_INPUT);
|
||||||
|
gpio_set_interrupt(INT1_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler);
|
||||||
|
gpio_set_interrupt(INT2_PIN, GPIO_INTTYPE_EDGE_POS, int_signal_handler);
|
||||||
|
|
||||||
|
#endif // INT_USED
|
||||||
|
|
||||||
|
/** -- SENSOR CONFIGURATION PART --- */
|
||||||
|
|
||||||
|
// set type and polarity of INT signals if necessary
|
||||||
|
// l3gd20h_config_int_signals (dev, l3gd20h_push_pull, l3gd20h_high_active);
|
||||||
|
|
||||||
|
#ifdef INT_EVENT
|
||||||
|
// enable event interrupts (axis movement and wake up)
|
||||||
|
l3gd20h_int_event_config_t int_cfg;
|
||||||
|
|
||||||
|
l3gd20h_get_int_event_config (sensor, &int_cfg);
|
||||||
|
|
||||||
|
int_cfg.x_high_enabled = true;
|
||||||
|
int_cfg.y_high_enabled = true;
|
||||||
|
int_cfg.z_high_enabled = true;
|
||||||
|
int_cfg.x_low_enabled = false;
|
||||||
|
int_cfg.y_low_enabled = false;
|
||||||
|
int_cfg.z_low_enabled = false;
|
||||||
|
int_cfg.x_threshold = 1000;
|
||||||
|
int_cfg.y_threshold = 1000;
|
||||||
|
int_cfg.z_threshold = 1000;
|
||||||
|
|
||||||
|
int_cfg.filter = l3gd20h_hpf_only;
|
||||||
|
int_cfg.and_or = false;
|
||||||
|
int_cfg.duration = 0;
|
||||||
|
int_cfg.latch = true;
|
||||||
|
|
||||||
|
l3gd20h_set_int_event_config (sensor, &int_cfg);
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_event, true);
|
||||||
|
|
||||||
|
#endif // INT_EVENT
|
||||||
|
|
||||||
|
#ifdef INT_DATA
|
||||||
|
// enable data ready (DRDY) and FIFO interrupt signal *INT2*
|
||||||
|
// NOTE: DRDY and FIFO interrupts must not be enabled at the same time
|
||||||
|
#ifdef FIFO_MODE
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_fifo_overrun, true);
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_fifo_threshold, true);
|
||||||
|
#else
|
||||||
|
l3gd20h_enable_int (sensor, l3gd20h_int_data_ready, true);
|
||||||
|
#endif
|
||||||
|
#endif // INT_DATA
|
||||||
|
|
||||||
|
#ifdef FIFO_MODE
|
||||||
|
// clear FIFO and activate FIFO mode if needed
|
||||||
|
l3gd20h_set_fifo_mode (sensor, l3gd20h_bypass, 0);
|
||||||
|
l3gd20h_set_fifo_mode (sensor, l3gd20h_stream, 10);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// select LPF/HPF, configure HPF and reset the reference by dummy read
|
||||||
|
l3gd20h_select_output_filter (sensor, l3gd20h_hpf_only);
|
||||||
|
l3gd20h_config_hpf (sensor, l3gd20h_hpf_normal, 0);
|
||||||
|
l3gd20h_get_hpf_ref (sensor);
|
||||||
|
|
||||||
|
// LAST STEP: Finally set scale and sensor mode to start measurements
|
||||||
|
l3gd20h_set_scale(sensor, l3gd20h_scale_245_dps);
|
||||||
|
l3gd20h_set_mode (sensor, l3gd20h_normal_odr_12_5, 3, true, true, true);
|
||||||
|
|
||||||
|
/** -- 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 L3GD20H sensor\n");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
10
extras/l3gd20h/component.mk
Normal file
10
extras/l3gd20h/component.mk
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# Component makefile for extras/l3gd20h
|
||||||
|
|
||||||
|
# expected anyone using L3GD20H driver includes it as 'l3gd20h/l3gd20h.h'
|
||||||
|
INC_DIRS += $(l3gd20h_ROOT)..
|
||||||
|
INC_DIRS += $(l3gd20h_ROOT)
|
||||||
|
|
||||||
|
# args for passing into compile rule generation
|
||||||
|
l3gd20h_SRC_DIR = $(l3gd20h_ROOT)
|
||||||
|
|
||||||
|
$(eval $(call component_compile_rules,l3gd20h))
|
1091
extras/l3gd20h/l3gd20h.c
Normal file
1091
extras/l3gd20h/l3gd20h.c
Normal file
File diff suppressed because it is too large
Load diff
408
extras/l3gd20h/l3gd20h.h
Normal file
408
extras/l3gd20h/l3gd20h.h
Normal file
|
@ -0,0 +1,408 @@
|
||||||
|
/**
|
||||||
|
* Driver for L3GD20H 3-axes digital output gyroscope connected to I2C or SPI.
|
||||||
|
* It can also be used with L3GD20 and L3G4200D.
|
||||||
|
*
|
||||||
|
* 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 __L3GD20H_H__
|
||||||
|
#define __L3GD20H_H__
|
||||||
|
|
||||||
|
// Uncomment one of the following defines to enable debug output
|
||||||
|
// #define L3GD20H_DEBUG_LEVEL_1 // only error messages
|
||||||
|
// #define L3GD20H_DEBUG_LEVEL_2 // debug and error messages
|
||||||
|
|
||||||
|
// L3GD20H addresses
|
||||||
|
#define L3GD20H_I2C_ADDRESS_1 0x6a // SDO pin is low
|
||||||
|
#define L3GD20H_I2C_ADDRESS_2 0x6b // SDO pin is high
|
||||||
|
|
||||||
|
// L3GD20 addresses
|
||||||
|
#define L3GD20_I2C_ADDRESS_1 0x6a // SDO pin is low
|
||||||
|
#define L3GD20_I2C_ADDRESS_2 0x6b // SDO pin is high
|
||||||
|
|
||||||
|
// L3G4200D addresses
|
||||||
|
#define L3G4200D_I2C_ADDRESS_1 0x68 // SDO pin is low
|
||||||
|
#define L3G4200D_I2C_ADDRESS_2 0x69 // SDO pin is high
|
||||||
|
|
||||||
|
// L3GD20H chip id
|
||||||
|
#define L3GD20H_CHIP_ID 0xd7 // L3GD20H_REG_WHO_AM_I<7:0>
|
||||||
|
|
||||||
|
// L3GD20 chip id
|
||||||
|
#define L3GD20_CHIP_ID 0xd4 // L3GD20H_REG_WHO_AM_I<7:0>
|
||||||
|
|
||||||
|
// L3G4200D chip id
|
||||||
|
#define L3G4200D_CHIP_ID 0xd3 // L3GD20H_REG_WHO_AM_I<7:0>
|
||||||
|
|
||||||
|
// Definition of error codes
|
||||||
|
#define L3GD20H_OK 0
|
||||||
|
#define L3GD20H_NOK -1
|
||||||
|
|
||||||
|
#define L3GD20H_INT_ERROR_MASK 0x000f
|
||||||
|
#define L3GD20H_DRV_ERROR_MASK 0xfff0
|
||||||
|
|
||||||
|
// Error codes for I2C and SPI interfaces ORed with L3GD20H driver error codes
|
||||||
|
#define L3GD20H_I2C_READ_FAILED 1
|
||||||
|
#define L3GD20H_I2C_WRITE_FAILED 2
|
||||||
|
#define L3GD20H_I2C_BUSY 3
|
||||||
|
#define L3GD20H_SPI_WRITE_FAILED 4
|
||||||
|
#define L3GD20H_SPI_READ_FAILED 5
|
||||||
|
#define L3GD20H_SPI_BUFFER_OVERFLOW 6
|
||||||
|
#define L3GD20H_SPI_SET_PAGE_FAILED 7
|
||||||
|
|
||||||
|
// L3GD20H driver error codes ORed with error codes for I2C and SPI interfaces
|
||||||
|
#define L3GD20H_WRONG_CHIP_ID ( 1 << 8)
|
||||||
|
#define L3GD20H_WRONG_BANDWIDTH ( 2 << 8)
|
||||||
|
#define L3GD20H_GET_RAW_DATA_FAILED ( 3 << 8)
|
||||||
|
#define L3GD20H_GET_RAW_DATA_FIFO_FAILED ( 4 << 8)
|
||||||
|
#define L3GD20H_WRONG_INT_TYPE ( 5 << 8)
|
||||||
|
#define L3GD20H_CONFIG_INT_SIGNALS_FAILED ( 6 << 8)
|
||||||
|
#define L3GD20H_CONFIG_INT1_FAILED ( 7 << 8)
|
||||||
|
#define L3GD20H_CONFIG_INT2_FAILED ( 8 << 8)
|
||||||
|
#define L3GD20H_INT1_SOURCE_FAILED ( 9 << 8)
|
||||||
|
#define L3GD20H_INT2_SOURCE_FAILED (10 << 8)
|
||||||
|
#define L3GD20H_SEL_OUT_FILTER_FAILED (11 << 8)
|
||||||
|
#define L3GD20H_CONFIG_HPF_FAILED (12 << 8)
|
||||||
|
#define L3GD20H_ENABLE_HPF_FAILED (13 << 8)
|
||||||
|
#define L3GD20H_SENSOR_IN_BYPASS_MODE (14 << 8)
|
||||||
|
#define L3GD20H_SENSOR_IN_FIFO_MODE (15 << 8)
|
||||||
|
#define L3GD20H_ODR_TOO_HIGH (16 << 8)
|
||||||
|
#define L3GD20H_ODR_NOT_AVAILABLE (17 << 8)
|
||||||
|
#define L3GD20H_FIFO_MODE_NOT_AVAILABLE (18 << 8)
|
||||||
|
|
||||||
|
#include "l3gd20h_platform.h"
|
||||||
|
#include "l3gd20h_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 L3GD20H sensor is connected
|
||||||
|
* @param addr I2C addr of the L3GD20H sensor, 0 for using SPI
|
||||||
|
* @param cs SPI CS GPIO, ignored for I2C
|
||||||
|
* @return pointer to sensor data structure, or NULL on error
|
||||||
|
*/
|
||||||
|
l3gd20h_sensor_t* l3gd20h_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 mode sensor mode with certain output data rate
|
||||||
|
* @param bw bandwidth
|
||||||
|
* @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 l3gd20h_set_mode (l3gd20h_sensor_t* dev, l3gd20h_mode_t mode, uint8_t bw,
|
||||||
|
bool x, bool y, bool z);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set scale (full range range)
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @param scale range setting
|
||||||
|
* @return true on success, false on error
|
||||||
|
*/
|
||||||
|
bool l3gd20h_set_scale (l3gd20h_sensor_t* dev, l3gd20h_scale_t sens);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set FIFO mode
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @param mode FIFO mode
|
||||||
|
* @param thresh FIFO watermark (ignored in bypass mode)
|
||||||
|
* @return true on success, false on error
|
||||||
|
*/
|
||||||
|
bool l3gd20h_set_fifo_mode (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_fifo_mode_t mode, uint8_t thresh);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Filter selection for raw data output values
|
||||||
|
*
|
||||||
|
* High pass filter (HPF) is configured with function *l3gd20h_config_hpf*. If
|
||||||
|
* HPF is selected, it is enabled implicitly.
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @param filter selected filters for output values
|
||||||
|
* @return true on success, false on error
|
||||||
|
*/
|
||||||
|
bool l3gd20h_select_output_filter (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_filter_t filter);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Test whether new sets of data are available
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @return true on new data, otherwise false
|
||||||
|
*/
|
||||||
|
bool l3gd20h_new_data (l3gd20h_sensor_t* dev);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get one sample of floating point sensor data (unit degree)
|
||||||
|
*
|
||||||
|
* Function works only in bypass mode and fails in FIFO modes. In FIFO modes,
|
||||||
|
* function *l3gd20h_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 values
|
||||||
|
* @return true on success, false on error
|
||||||
|
*/
|
||||||
|
bool l3gd20h_get_float_data (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_float_data_t* data);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get all samples of sensor data stored in the FIFO (unit dps)
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
* @return number of data sets read from fifo on success or 0 on error
|
||||||
|
*/
|
||||||
|
uint8_t l3gd20h_get_float_data_fifo (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_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 *l3gd20h_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 l3gd20h_get_raw_data (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_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 l3gd20h_get_raw_data_fifo (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_raw_data_fifo_t raw);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Enable / disable data or event interrupts on signal INT1/INT2
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @param type type of the interrupt to be enabled/disabled
|
||||||
|
* @param value true to enable or false to disable the interrupt
|
||||||
|
* @return true on success, false on error
|
||||||
|
*/
|
||||||
|
bool l3gd20h_enable_int (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_int_types_t type, bool value);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the configuration of the event interrupt generator
|
||||||
|
*
|
||||||
|
* The event interrupt generator produces interrupts (axis movement and wake up)
|
||||||
|
* on signal INT1 whenever the angular rate of one or more axes becomes higher
|
||||||
|
* or lower than defined thresholds.
|
||||||
|
*
|
||||||
|
* @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 l3gd20h_set_int_event_config (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_int_event_config_t* config);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the configuration of the event interrupt generator
|
||||||
|
*
|
||||||
|
* The event interrupt generator produces interrupts (axis movement and wake up)
|
||||||
|
* on signal INT1 whenever the angular rate of one or more axes becomes higher
|
||||||
|
* or lower than defined thresholds.
|
||||||
|
*
|
||||||
|
* @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 l3gd20h_get_int_event_config (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_int_event_config_t* config);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the source of an event interrupt (axis movement and wake up)
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @param type pointer to the interrupt source
|
||||||
|
* @return true on success, false on error
|
||||||
|
*/
|
||||||
|
bool l3gd20h_get_int_event_source (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_int_event_source_t* source);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the source of a data interrupt (data ready or FIFO status)
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @param source pointer to the interrupt source
|
||||||
|
* @return true on success, false on error
|
||||||
|
*/
|
||||||
|
bool l3gd20h_get_int_data_source (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_int_data_source_t* source);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set signal configuration for INT1 and INT2 signals
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @param type define interrupt signal as pushed/pulled or open drain
|
||||||
|
* @return true on success, false on error
|
||||||
|
*/
|
||||||
|
bool l3gd20h_config_int_signals (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_signal_type_t type,
|
||||||
|
l3gd20h_signal_level_t level);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Config HPF (high pass filter)
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @param mode high pass filter mode
|
||||||
|
* @param cutoff cutoff frequency (depends on output data rate) [0 ... 15]
|
||||||
|
* @return true on success, false on error
|
||||||
|
*/
|
||||||
|
bool l3gd20h_config_hpf (l3gd20h_sensor_t* dev,
|
||||||
|
l3gd20h_hpf_mode_t mode, uint8_t cutoff);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set HPF (high pass filter) reference
|
||||||
|
*
|
||||||
|
* Used to set the reference of HPF in reference mode *l3gd20h_hpf_reference*.
|
||||||
|
* Used to reset the HPF in autoreset mode *l3gd20h_hpf_autoreset*.
|
||||||
|
* Reference is given as two's complement.
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @param ref reference *l3gd20h_hpf_reference* mode, otherwise ignored
|
||||||
|
* @return true on success, false on error
|
||||||
|
*/
|
||||||
|
bool l3gd20h_set_hpf_ref (l3gd20h_sensor_t* dev, int8_t ref);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get HPF (high pass filter) reference
|
||||||
|
*
|
||||||
|
* Used to reset the HPF in normal mode *l3gd20h_hpf_normal*.
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @return HPF reference as two's complement
|
||||||
|
*/
|
||||||
|
int8_t l3gd20h_get_hpf_ref (l3gd20h_sensor_t* dev);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get temperature
|
||||||
|
*
|
||||||
|
* @param dev pointer to the sensor device data structure
|
||||||
|
* @return temperature in degree as two's complement
|
||||||
|
*/
|
||||||
|
int8_t l3gd20h_get_temperature (l3gd20h_sensor_t* dev);
|
||||||
|
|
||||||
|
|
||||||
|
// ---- 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 l3gd20h_reg_write (l3gd20h_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 l3gd20h_reg_read (l3gd20h_sensor_t* dev,
|
||||||
|
uint8_t reg, uint8_t *data, uint16_t len);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* End of CPP guard */
|
||||||
|
|
||||||
|
#endif /* __L3GD20H_H__ */
|
82
extras/l3gd20h/l3gd20h_platform.c
Normal file
82
extras/l3gd20h/l3gd20h_platform.c
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
/**
|
||||||
|
* Driver for L3GD20H 3-axes digital output gyroscope connected to I2C or SPI.
|
||||||
|
* It can also be used with L3GD20 and L3G4200D.
|
||||||
|
*
|
||||||
|
* 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 "l3gd20h_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;
|
||||||
|
}
|
||||||
|
|
81
extras/l3gd20h/l3gd20h_platform.h
Normal file
81
extras/l3gd20h/l3gd20h_platform.h
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
/**
|
||||||
|
* Driver for L3GD20H 3-axes digital output gyroscope connected to I2C or SPI.
|
||||||
|
* It can also be used with L3GD20 and L3G4200D.
|
||||||
|
*
|
||||||
|
* 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 __L3GD20H_PLATFORM_H__
|
||||||
|
#define __L3GD20H_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 SPI functions
|
||||||
|
|
||||||
|
#define spi_bus_init(bus,sck,miso,mosi) // not needed on ESP8266
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
#endif // __L3GD20H_PLATFORM_H__
|
310
extras/l3gd20h/l3gd20h_types.h
Normal file
310
extras/l3gd20h/l3gd20h_types.h
Normal file
|
@ -0,0 +1,310 @@
|
||||||
|
/**
|
||||||
|
* Driver for L3GD20H 3-axes digital output gyroscope connected to I2C or SPI.
|
||||||
|
* It can also be used with L3GD20 and L3G4200D.
|
||||||
|
*
|
||||||
|
* 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 __L3GD20H_TYPES_H__
|
||||||
|
#define __L3GD20H_TYPES_H__
|
||||||
|
|
||||||
|
#include "stdint.h"
|
||||||
|
#include "stdbool.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C"
|
||||||
|
{
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Output data rates (ODR)
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
l3gd20h_power_down = 0, // power down mode
|
||||||
|
l3gd20h_normal_odr_12_5, // normal mode with low output data rate 12.5 Hz
|
||||||
|
l3gd20h_normal_odr_25, // normal mode with low output data rate 25 Hz
|
||||||
|
l3gd20h_normal_odr_50, // normal mode with low output data rate 50 Hz
|
||||||
|
l3gd20h_normal_odr_100, // normal mode with high output data rate 100 Hz
|
||||||
|
l3gd20h_normal_odr_200, // normal mode with high output data rate 200 Hz
|
||||||
|
l3gd20h_normal_odr_400, // normal mode with high output data rate 400 Hz
|
||||||
|
l3gd20h_normal_odr_800, // normal mode with high output data rate 800 Hz
|
||||||
|
|
||||||
|
} l3gd20h_mode_t;
|
||||||
|
|
||||||
|
#define l3gd20_normal_odr_95 l3gd20h_normal_odr_100
|
||||||
|
#define l3gd20_normal_odr_190 l3gd20h_normal_odr_200
|
||||||
|
#define l3gd20_normal_odr_380 l3gd20h_normal_odr_400
|
||||||
|
#define l3gd20_normal_odr_760 l3gd20h_normal_odr_800
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sensitivity level
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
l3gd20h_scale_245_dps = 0, // default
|
||||||
|
l3gd20h_scale_500_dps,
|
||||||
|
l3gd20h_scale_2000_dps
|
||||||
|
|
||||||
|
} l3gd20h_scale_t;
|
||||||
|
|
||||||
|
#define l3gd20_scale_250_dps l3gd20h_scale_245_dps
|
||||||
|
#define l3gd20_scale_500_dps l3gd20h_scale_500_dps
|
||||||
|
#define l3gd20_scale_2000_dps l3gd20h_scale_2000_dps
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief FIFO mode
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
l3gd20h_bypass = 0, // default
|
||||||
|
l3gd20h_fifo = 1,
|
||||||
|
l3gd20h_stream = 2,
|
||||||
|
l3gd20h_stream_to_fifo = 3,
|
||||||
|
l3gd20h_bypass_to_stream = 5,
|
||||||
|
l3gd20h_dynamic_stream = 6,
|
||||||
|
l3gd20h_bypass_to_fifo = 7
|
||||||
|
|
||||||
|
} l3gd20h_fifo_mode_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief High pass filter (HPF) and low pass filter 2 (LPF2) modes
|
||||||
|
*/
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
l3gd20h_no_filter = 0, // HPF not used, LPF2 not used
|
||||||
|
l3gd20h_hpf_only, // HPF used, LPF2 not used
|
||||||
|
l3gd20h_lpf2_only, // HPF not used, LPF2 used
|
||||||
|
l3gd20h_hpf_and_lpf2 // HPF used, LPF2 used
|
||||||
|
|
||||||
|
} l3gd20h_filter_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Interrupt types
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
|
||||||
|
l3gd20h_int_data_ready, // data are ready to read (INT2)
|
||||||
|
|
||||||
|
l3gd20h_int_fifo_threshold, // FIFO filling exceds FTH level (INT2)
|
||||||
|
l3gd20h_int_fifo_overrun, // FIFO is completely filled (INT2)
|
||||||
|
l3gd20h_int_fifo_empty, // FIFO becomes empty (INT2)
|
||||||
|
|
||||||
|
l3gd20h_int_event // angular rate of one or more axes becomes
|
||||||
|
// lower or higher than threshold (INT1)
|
||||||
|
} l3gd20h_int_types_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Event interrupt generator configuration (axis movement and wake up)
|
||||||
|
*
|
||||||
|
* memset to 0 to disable all interrupt conditions (default)
|
||||||
|
*/
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
bool x_low_enabled; // x lower than threshold interrupt enabled
|
||||||
|
bool x_high_enabled; // x higher than threshold interrupt enabled
|
||||||
|
uint16_t x_threshold; // x threshold value
|
||||||
|
|
||||||
|
bool y_low_enabled; // y lower than threshold interrupt enabled
|
||||||
|
bool y_high_enabled; // y higher than threshold interrupt enabled
|
||||||
|
uint16_t y_threshold; // y threshold value
|
||||||
|
|
||||||
|
bool z_low_enabled; // z lower than threshold interrupt enabled
|
||||||
|
bool z_high_enabled; // z higher than threshold interrupt enabled
|
||||||
|
uint16_t z_threshold; // z threshold value
|
||||||
|
|
||||||
|
l3gd20h_filter_t filter; // HPF and LPF2 mode used for threshold comparison
|
||||||
|
|
||||||
|
bool and_or; // interrupt combination true - AND, false - OR
|
||||||
|
// AND - all enabled axes passed the treshold
|
||||||
|
// OR - at least one axes passed the threshold
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
bool wait; // when true, duration is also used when interrupt
|
||||||
|
// condition in no longer given before interrupt
|
||||||
|
// signal is reset
|
||||||
|
|
||||||
|
bool counter_mode; // DCRM is not documented and not used therefore
|
||||||
|
|
||||||
|
} l3gd20h_int_event_config_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Event interrupt source (axis movement and wake up)
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
|
||||||
|
bool x_low :1; // true - x is lower event occured
|
||||||
|
bool x_high:1; // true - x is higher event occured
|
||||||
|
|
||||||
|
bool y_low :1; // true - z is lower event occured
|
||||||
|
bool y_high:1; // true - z is higher event occured
|
||||||
|
|
||||||
|
bool z_low :1; // true - z is lower event occured
|
||||||
|
bool z_high:1; // true - z is higher event occured
|
||||||
|
|
||||||
|
bool active:1; // true - one ore more have been generated
|
||||||
|
|
||||||
|
} l3gd20h_int_event_source_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Data interrupt source type (data ready and FIFO status)
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
|
||||||
|
bool data_ready; // true when data are ready to read
|
||||||
|
|
||||||
|
bool fifo_threshold; // true when FIFO filling >= FTH level
|
||||||
|
bool fifo_overrun; // true when FIFO is completely filled
|
||||||
|
bool fifo_empty; // true when FIFO is empty
|
||||||
|
|
||||||
|
} l3gd20h_int_data_source_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief INT1, INT2 signal activity level
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
|
||||||
|
l3gd20h_high_active = 0,
|
||||||
|
l3gd20h_low_active
|
||||||
|
|
||||||
|
} l3gd20h_signal_level_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief INT1, INT2 signal type
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
|
||||||
|
l3gd20h_push_pull = 0,
|
||||||
|
l3gd20h_open_drain
|
||||||
|
|
||||||
|
} l3gd20h_signal_type_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Raw data set as two complements
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
|
||||||
|
int16_t x;
|
||||||
|
int16_t y;
|
||||||
|
int16_t z;
|
||||||
|
|
||||||
|
} l3gd20h_raw_data_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Raw data FIFO type
|
||||||
|
*/
|
||||||
|
typedef l3gd20h_raw_data_t l3gd20h_raw_data_fifo_t[32];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Floating point output value set in degree
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
|
||||||
|
} l3gd20h_float_data_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Floating point output value FIFO type
|
||||||
|
*/
|
||||||
|
typedef l3gd20h_float_data_t l3gd20h_float_data_fifo_t[32];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief HPF (high pass filter) modes
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
|
||||||
|
l3gd20h_hpf_normal = 0,
|
||||||
|
l3gd20h_hpf_reference,
|
||||||
|
l3gd20h_hpf_normal_x,
|
||||||
|
l3gd20h_hpf_autoreset
|
||||||
|
|
||||||
|
} l3gd20h_hpf_mode_t;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief L3GD20H sensor device data structure type
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
|
||||||
|
int error_code; // contains the 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
|
||||||
|
|
||||||
|
l3gd20h_scale_t scale; // fill range scale (default 245 dps)
|
||||||
|
l3gd20h_fifo_mode_t fifo_mode; // FIFO operation mode (default bypass)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
l3gd20h,
|
||||||
|
l3gd20,
|
||||||
|
l3g4200d
|
||||||
|
} mode;
|
||||||
|
|
||||||
|
} l3gd20h_sensor_t;
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif /* End of CPP guard */
|
||||||
|
|
||||||
|
#endif /* __L3GD20H_TYPES_H__ */
|
Loading…
Reference in a new issue