sensor: lis2dh: Add support for lis2dh accelerometer
* lis2dh 3 axis accelerometer support on SPI and I2C bus * data ready and anymotion type of trigger support Tested on Dynastream module D52QD2M4IA-A using SPI Origin: based on Zephyr lis3dh driver Change-Id: I2c4e9418b87f09c957bba8f73522bd78830bc809 Signed-off-by: Roger Lendenmann <roger.lendenmann@intel.com>
This commit is contained in:
parent
b484c1eeed
commit
0bc4a88bc7
7 changed files with 1366 additions and 0 deletions
|
@ -70,6 +70,8 @@ source "drivers/sensor/hts221/Kconfig"
|
|||
|
||||
source "drivers/sensor/isl29035/Kconfig"
|
||||
|
||||
source "drivers/sensor/lis2dh/Kconfig"
|
||||
|
||||
source "drivers/sensor/lis3dh/Kconfig"
|
||||
|
||||
source "drivers/sensor/lis3mdl/Kconfig"
|
||||
|
|
|
@ -15,6 +15,7 @@ obj-$(CONFIG_HMC5883L) += hmc5883l/
|
|||
obj-$(CONFIG_HP206C) += hp206c/
|
||||
obj-$(CONFIG_HTS221) += hts221/
|
||||
obj-$(CONFIG_ISL29035) += isl29035/
|
||||
obj-$(CONFIG_LIS2DH) += lis2dh/
|
||||
obj-$(CONFIG_LIS3DH) += lis3dh/
|
||||
obj-$(CONFIG_LIS3MDL) += lis3mdl/
|
||||
obj-$(CONFIG_LPS25HB) += lps25hb/
|
||||
|
|
241
drivers/sensor/lis2dh/Kconfig
Normal file
241
drivers/sensor/lis2dh/Kconfig
Normal file
|
@ -0,0 +1,241 @@
|
|||
# Kconfig - LIS2DH Three Axis Accelerometer configuration options
|
||||
|
||||
#
|
||||
# Copyright (c) 2017 Intel Corporation
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
menuconfig LIS2DH
|
||||
bool
|
||||
prompt "LIS2DH Three Axis Accelerometer"
|
||||
depends on SENSOR && (I2C || SPI)
|
||||
default n
|
||||
help
|
||||
Enable driver for LIS2DH SPI/I2C-based triaxial accelerometer sensor.
|
||||
|
||||
config LIS2DH_NAME
|
||||
string "Driver name"
|
||||
default "LIS2DH"
|
||||
depends on LIS2DH
|
||||
help
|
||||
Device name with which the LIS2DH sensor is identified.
|
||||
|
||||
choice
|
||||
prompt "Sensor Bus Type"
|
||||
depends on LIS2DH
|
||||
default LIS2DH_BUS_SPI
|
||||
help
|
||||
Specify bus type, SPI versus I2C, to which the sensor is attached.
|
||||
|
||||
config LIS2DH_BUS_SPI
|
||||
bool "SPI bus"
|
||||
depends on SPI
|
||||
help
|
||||
Use a SPI master to communicate with accelerometer (SPI slave)
|
||||
|
||||
config LIS2DH_BUS_I2C
|
||||
bool "I2C bus"
|
||||
depends on I2C
|
||||
help
|
||||
Use an I2C master to communicate with accelerometer (I2C slave)
|
||||
|
||||
endchoice
|
||||
|
||||
config LIS2DH_SPI_MASTER_DEV_NAME
|
||||
string "SPI master device name"
|
||||
depends on LIS2DH && LIS2DH_BUS_SPI
|
||||
default "SPI_0"
|
||||
help
|
||||
Specify the device name of the SPI master device to which LIS2DH is
|
||||
connected.
|
||||
|
||||
config LIS2DH_SPI_SS_1
|
||||
int "LIS2DH SPI slave 1 select number"
|
||||
depends on LIS2DH && LIS2DH_BUS_SPI
|
||||
range 1 4
|
||||
default 1
|
||||
help
|
||||
SPI slave select line to use to talk to LIS2DH sensor.
|
||||
|
||||
config LIS2DH_SPI_FREQUENCY
|
||||
int "SPI clock frequency"
|
||||
depends on LIS2DH && LIS2DH_BUS_SPI
|
||||
range 125000 10000000
|
||||
default 4000000
|
||||
help
|
||||
SPI clock frequency to use. Make sure the SPI master hardware supports
|
||||
this frequency.
|
||||
|
||||
config LIS2DH_I2C_ADDR
|
||||
hex "LIS2DH I2C address"
|
||||
depends on LIS2DH && LIS2DH_BUS_I2C
|
||||
default 0x18
|
||||
help
|
||||
I2C address of the LIS2DH sensor.
|
||||
|
||||
0x18: Choose this option if the SDO pin is pulled to GND.
|
||||
0x19: Choose this option if the SDO pin is pulled to VDDIO.
|
||||
|
||||
config LIS2DH_I2C_MASTER_DEV_NAME
|
||||
string
|
||||
prompt "I2C master where LIS2DH is connected"
|
||||
depends on LIS2DH && LIS2DH_BUS_I2C
|
||||
default "I2C_0"
|
||||
help
|
||||
Specify the device name of the I2C master device to which LIS2DH is
|
||||
connected.
|
||||
|
||||
choice
|
||||
prompt "Trigger mode"
|
||||
depends on LIS2DH
|
||||
default LIS2DH_TRIGGER_GLOBAL_THREAD
|
||||
help
|
||||
Specify the type of triggering to be used by the driver.
|
||||
|
||||
config LIS2DH_TRIGGER_NONE
|
||||
bool "No trigger"
|
||||
|
||||
config LIS2DH_TRIGGER_GLOBAL_THREAD
|
||||
bool "Use global thread"
|
||||
depends on GPIO
|
||||
select LIS2DH_TRIGGER
|
||||
|
||||
config LIS2DH_TRIGGER_OWN_THREAD
|
||||
bool "Use own thread"
|
||||
depends on GPIO
|
||||
select LIS2DH_TRIGGER
|
||||
|
||||
endchoice
|
||||
|
||||
config LIS2DH_TRIGGER
|
||||
bool
|
||||
depends on LIS2DH
|
||||
|
||||
config LIS2DH_GPIO_DEV_NAME
|
||||
string "GPIO device"
|
||||
default "GPIO_0"
|
||||
depends on LIS2DH && LIS2DH_TRIGGER
|
||||
help
|
||||
The device name of the GPIO device to which the LIS2DH interrupt pins
|
||||
are connected.
|
||||
|
||||
config LIS2DH_INT1_GPIO_PIN
|
||||
int "Interrupt 1 GPIO pin number"
|
||||
range 0 254
|
||||
default 25
|
||||
depends on LIS2DH && LIS2DH_TRIGGER
|
||||
help
|
||||
The number of the GPIO on which the interrupt 1 signal from the LIS2DH
|
||||
chip will be received.
|
||||
|
||||
config LIS2DH_INT2_GPIO_PIN
|
||||
int "Interrupt 2 GPIO pin number"
|
||||
range 0 254
|
||||
default 26
|
||||
depends on LIS2DH && LIS2DH_TRIGGER
|
||||
help
|
||||
The number of the GPIO on which the interrupt 2 signal from the LIS2DH
|
||||
chip will be received.
|
||||
|
||||
config LIS2DH_THREAD_PRIORITY
|
||||
int "Thread priority"
|
||||
depends on LIS2DH && LIS2DH_TRIGGER_OWN_THREAD
|
||||
default 10
|
||||
help
|
||||
Priority of thread used by the driver to handle interrupts.
|
||||
|
||||
config LIS2DH_THREAD_STACK_SIZE
|
||||
int "Thread stack size"
|
||||
depends on LIS2DH && LIS2DH_TRIGGER_OWN_THREAD
|
||||
default 1024
|
||||
help
|
||||
Stack size of thread used by the driver to handle interrupts.
|
||||
|
||||
choice
|
||||
prompt "Acceleration measurement range"
|
||||
depends on LIS2DH
|
||||
default LIS2DH_ACCEL_RANGE_RUNTIME
|
||||
help
|
||||
Initial measurement full scale range for acceleration values.
|
||||
|
||||
config LIS2DH_ACCEL_RANGE_RUNTIME
|
||||
bool "Set at runtime"
|
||||
|
||||
config LIS2DH_ACCEL_RANGE_2G
|
||||
bool "+/-2g"
|
||||
|
||||
config LIS2DH_ACCEL_RANGE_4G
|
||||
bool "+/-4g"
|
||||
|
||||
config LIS2DH_ACCEL_RANGE_8G
|
||||
bool "+/-8g"
|
||||
|
||||
config LIS2DH_ACCEL_RANGE_16G
|
||||
bool "+/-16g"
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Power mode"
|
||||
depends on LIS2DH
|
||||
default LIS2DH_POWER_MODE_NORMAL
|
||||
help
|
||||
Choose between normal or low power operation mode for chip at init.
|
||||
|
||||
config LIS2DH_POWER_MODE_NORMAL
|
||||
bool "normal"
|
||||
|
||||
config LIS2DH_POWER_MODE_LOW
|
||||
bool "low"
|
||||
|
||||
endchoice
|
||||
|
||||
choice
|
||||
prompt "Output data rate frequency"
|
||||
depends on LIS2DH
|
||||
default LIS2DH_ODR_RUNTIME
|
||||
help
|
||||
Initial data rate frequency of acceleration data at initialisation.
|
||||
Supported values:
|
||||
1Hz, 10Hz, 25Hz, 50Hz, 100Hz, 200Hz, 400Hz in all power modes
|
||||
1620Hz, 5376Hz in low power mode only
|
||||
1344Hz in normal power mode
|
||||
|
||||
config LIS2DH_ODR_RUNTIME
|
||||
bool "Set at runtime"
|
||||
|
||||
config LIS2DH_ODR_1
|
||||
bool "1Hz"
|
||||
|
||||
config LIS2DH_ODR_2
|
||||
bool "10Hz"
|
||||
|
||||
config LIS2DH_ODR_3
|
||||
bool "25Hz"
|
||||
|
||||
config LIS2DH_ODR_4
|
||||
bool "50Hz"
|
||||
|
||||
config LIS2DH_ODR_5
|
||||
bool "100Hz"
|
||||
|
||||
config LIS2DH_ODR_6
|
||||
bool "200Hz"
|
||||
|
||||
config LIS2DH_ODR_7
|
||||
bool "400Hz"
|
||||
|
||||
config LIS2DH_ODR_8
|
||||
bool "1.6KHz"
|
||||
depends on LIS2DH_POWER_MODE_LOW
|
||||
|
||||
config LIS2DH_ODR_9_NORMAL
|
||||
bool "1.25KHz"
|
||||
depends on LIS2DH_POWER_MODE_NORMAL
|
||||
|
||||
config LIS2DH_ODR_9_LOW
|
||||
bool "5KHz"
|
||||
depends on LIS2DH_POWER_MODE_LOW
|
||||
|
||||
endchoice
|
2
drivers/sensor/lis2dh/Makefile
Normal file
2
drivers/sensor/lis2dh/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
obj-$(CONFIG_LIS2DH) += lis2dh.o
|
||||
obj-$(CONFIG_LIS2DH_TRIGGER) += lis2dh_trigger.o
|
346
drivers/sensor/lis2dh/lis2dh.c
Normal file
346
drivers/sensor/lis2dh/lis2dh.c
Normal file
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "lis2dh.h"
|
||||
|
||||
#include <init.h>
|
||||
#include <misc/byteorder.h>
|
||||
#include <misc/__assert.h>
|
||||
|
||||
#if defined(CONFIG_LIS2DH_TRIGGER) || defined(CONFIG_LIS2DH_ACCEL_RANGE_RUNTIME)
|
||||
int lis2dh_reg_field_update(struct device *bus, u8_t reg_addr,
|
||||
u8_t pos, u8_t mask, u8_t val)
|
||||
{
|
||||
int status;
|
||||
u8_t old_val;
|
||||
|
||||
/* just to remove gcc warning */
|
||||
old_val = 0;
|
||||
|
||||
status = lis2dh_reg_read_byte(bus, reg_addr, &old_val);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return lis2dh_reg_write_byte(bus, reg_addr,
|
||||
(old_val & ~mask) | ((val << pos) & mask));
|
||||
}
|
||||
#endif
|
||||
|
||||
static void lis2dh_convert(s16_t raw_val, u16_t scale,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
s32_t converted_val;
|
||||
|
||||
/*
|
||||
* maximum converted value we can get is: max(raw_val) * max(scale)
|
||||
* max(raw_val) = +/- 2^15
|
||||
* max(scale) = 4785
|
||||
* max(converted_val) = 156794880 which is less than 2^31
|
||||
*/
|
||||
converted_val = raw_val * scale;
|
||||
val->val1 = converted_val / 1000000;
|
||||
val->val2 = converted_val % 1000000;
|
||||
|
||||
/* normalize val to make sure val->val2 is positive */
|
||||
if (val->val2 < 0) {
|
||||
val->val1 -= 1;
|
||||
val->val2 += 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
static int lis2dh_channel_get(struct device *dev,
|
||||
enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
int ofs_start;
|
||||
int ofs_end;
|
||||
int i;
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_ACCEL_X:
|
||||
ofs_start = ofs_end = 0;
|
||||
break;
|
||||
case SENSOR_CHAN_ACCEL_Y:
|
||||
ofs_start = ofs_end = 1;
|
||||
break;
|
||||
case SENSOR_CHAN_ACCEL_Z:
|
||||
ofs_start = ofs_end = 2;
|
||||
break;
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
ofs_start = 0;
|
||||
ofs_end = 2;
|
||||
break;
|
||||
default:
|
||||
return -ENOTSUP;
|
||||
}
|
||||
for (i = ofs_start; i <= ofs_end; i++, val++) {
|
||||
lis2dh_convert(lis2dh->sample.xyz[i], lis2dh->scale, val);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lis2dh_sample_fetch(struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
size_t i;
|
||||
int status;
|
||||
|
||||
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
|
||||
chan == SENSOR_CHAN_ACCEL_XYZ);
|
||||
|
||||
/*
|
||||
* since status and all accel data register addresses are consecutive,
|
||||
* a burst read can be used to read all the samples
|
||||
*/
|
||||
status = lis2dh_burst_read(lis2dh->bus, LIS2DH_REG_STATUS,
|
||||
lis2dh->sample.raw,
|
||||
sizeof(lis2dh->sample.raw));
|
||||
if (status < 0) {
|
||||
SYS_LOG_WRN("Could not read accel axis data");
|
||||
return status;
|
||||
}
|
||||
|
||||
for (i = 0; i < (3 * sizeof(s16_t)); i += sizeof(s16_t)) {
|
||||
s16_t *sample =
|
||||
(s16_t *)&lis2dh->sample.raw[LIS2DH_DATA_OFS + 1 + i];
|
||||
|
||||
*sample = sys_le16_to_cpu(*sample);
|
||||
}
|
||||
|
||||
SYS_LOG_INF("status=0x%x x=%d y=%d z=%d", lis2dh->sample.status,
|
||||
lis2dh->sample.xyz[0], lis2dh->sample.xyz[1],
|
||||
lis2dh->sample.xyz[2]);
|
||||
|
||||
if (lis2dh->sample.status & LIS2DH_STATUS_OVR_MASK) {
|
||||
return -EBADMSG;
|
||||
} else if (lis2dh->sample.status & LIS2DH_STATUS_DRDY_MASK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENODATA;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LIS2DH_ODR_RUNTIME
|
||||
/* 1620 & 5376 are low power only */
|
||||
static const u16_t lis2dh_odr_map[] = {0, 1, 10, 25, 50, 100, 200, 400, 1620,
|
||||
1344, 5376};
|
||||
|
||||
static int lis2dh_freq_to_odr_val(u16_t freq)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
/* An ODR of 0 Hz is not allowed */
|
||||
if (freq == 0) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(lis2dh_odr_map); i++) {
|
||||
if (freq == lis2dh_odr_map[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int lis2dh_acc_odr_set(struct device *dev, u16_t freq)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
int odr;
|
||||
int status;
|
||||
u8_t value;
|
||||
|
||||
odr = lis2dh_freq_to_odr_val(freq);
|
||||
if (odr < 0) {
|
||||
return odr;
|
||||
}
|
||||
|
||||
status = lis2dh_reg_read_byte(lis2dh->bus, LIS2DH_REG_CTRL1, &value);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* some odr values cannot be set in certain power modes */
|
||||
if ((value & LIS2DH_LP_EN_BIT) == 0 && odr == LIS2DH_ODR_8) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
/* adjust odr index for LP enabled mode, see table above */
|
||||
if ((value & LIS2DH_LP_EN_BIT) == 1 && (odr == LIS2DH_ODR_9 + 1)) {
|
||||
odr--;
|
||||
}
|
||||
|
||||
return lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_CTRL1,
|
||||
(value & ~LIS2DH_ODR_MASK) |
|
||||
LIS2DH_ODR_RATE(odr));
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LIS2DH_ACCEL_RANGE_RUNTIME
|
||||
static const union {
|
||||
u32_t word_le32;
|
||||
u8_t fs_values[4];
|
||||
} lis2dh_acc_range_map = { .fs_values = {2, 4, 8, 16} };
|
||||
|
||||
static int lis2dh_range_to_reg_val(u16_t range)
|
||||
{
|
||||
int i;
|
||||
u32_t range_map;
|
||||
|
||||
range_map = sys_le32_to_cpu(lis2dh_acc_range_map.word_le32);
|
||||
|
||||
for (i = 0; range_map; i++, range_map >>= 1) {
|
||||
if (range == (range_map & 0xff)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int lis2dh_acc_range_set(struct device *dev, s32_t range)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
int fs;
|
||||
|
||||
fs = lis2dh_range_to_reg_val(range);
|
||||
if (fs < 0) {
|
||||
return fs;
|
||||
}
|
||||
|
||||
lis2dh->scale = LIS2DH_ACCEL_SCALE(range);
|
||||
|
||||
return lis2dh_reg_field_update(lis2dh->bus, LIS2DH_REG_CTRL4,
|
||||
LIS2DH_FS_SHIFT,
|
||||
LIS2DH_FS_MASK,
|
||||
fs);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int lis2dh_acc_config(struct device *dev, enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
switch (attr) {
|
||||
#ifdef CONFIG_LIS2DH_ACCEL_RANGE_RUNTIME
|
||||
case SENSOR_ATTR_FULL_SCALE:
|
||||
return lis2dh_acc_range_set(dev, sensor_ms2_to_g(val));
|
||||
#endif
|
||||
#ifdef CONFIG_LIS2DH_ODR_RUNTIME
|
||||
case SENSOR_ATTR_SAMPLING_FREQUENCY:
|
||||
return lis2dh_acc_odr_set(dev, val->val1);
|
||||
#endif
|
||||
#if defined(CONFIG_LIS2DH_TRIGGER)
|
||||
case SENSOR_ATTR_SLOPE_TH:
|
||||
case SENSOR_ATTR_SLOPE_DUR:
|
||||
return lis2dh_acc_slope_config(dev, attr, val);
|
||||
#endif
|
||||
default:
|
||||
SYS_LOG_DBG("Accel attribute not supported.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lis2dh_attr_set(struct device *dev, enum sensor_channel chan,
|
||||
enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_ACCEL_X:
|
||||
case SENSOR_CHAN_ACCEL_Y:
|
||||
case SENSOR_CHAN_ACCEL_Z:
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
return lis2dh_acc_config(dev, chan, attr, val);
|
||||
default:
|
||||
SYS_LOG_WRN("attr_set() not supported on this channel.");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct sensor_driver_api lis2dh_driver_api = {
|
||||
.attr_set = lis2dh_attr_set,
|
||||
#if CONFIG_LIS2DH_TRIGGER
|
||||
.trigger_set = lis2dh_trigger_set,
|
||||
#endif
|
||||
.sample_fetch = lis2dh_sample_fetch,
|
||||
.channel_get = lis2dh_channel_get,
|
||||
};
|
||||
|
||||
int lis2dh_init(struct device *dev)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
int status;
|
||||
u8_t raw[LIS2DH_DATA_OFS + 6];
|
||||
|
||||
lis2dh->bus = device_get_binding(LIS2DH_BUS_DEV_NAME);
|
||||
if (lis2dh->bus == NULL) {
|
||||
SYS_LOG_ERR("Could not get pointer to %s device",
|
||||
LIS2DH_BUS_DEV_NAME);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* configure bus, e.g. spi clock and format */
|
||||
status = lis2dh_bus_configure(lis2dh->bus);
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("Failed to configure bus (spi, i2c)");
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Initialize control register ctrl1 to ctrl 6 to default boot values
|
||||
* to avoid warm start/reset issues as the accelerometer has no reset
|
||||
* pin. Register values are retained if power is not removed.
|
||||
* Default values see LIS2DH documentation page 30, chapter 6.
|
||||
*/
|
||||
memset(raw, 0, sizeof(raw));
|
||||
raw[LIS2DH_DATA_OFS] = LIS2DH_ACCEL_EN_BITS;
|
||||
|
||||
status = lis2dh_burst_write(lis2dh->bus, LIS2DH_REG_CTRL1, raw,
|
||||
sizeof(raw));
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("Failed to reset ctrl registers.");
|
||||
return status;
|
||||
}
|
||||
|
||||
/* set full scale range and store it for later conversion */
|
||||
lis2dh->scale = LIS2DH_ACCEL_SCALE(1 << (LIS2DH_FS_IDX + 1));
|
||||
status = lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_CTRL4,
|
||||
LIS2DH_FS_BITS);
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("Failed to set full scale ctrl register.");
|
||||
return status;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LIS2DH_TRIGGER
|
||||
status = lis2dh_init_interrupt(dev);
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("Failed to initialize interrupts.");
|
||||
return status;
|
||||
}
|
||||
#endif
|
||||
|
||||
dev->driver_api = &lis2dh_driver_api;
|
||||
|
||||
SYS_LOG_INF("bus=%s fs=%d, odr=0x%x lp_en=0x%x scale=%d",
|
||||
LIS2DH_BUS_DEV_NAME, 1 << (LIS2DH_FS_IDX + 1),
|
||||
LIS2DH_ODR_IDX, (u8_t)LIS2DH_LP_EN_BIT, lis2dh->scale);
|
||||
|
||||
/* enable accel measurements and set power mode and data rate */
|
||||
return lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_CTRL1,
|
||||
LIS2DH_ACCEL_EN_BITS | LIS2DH_LP_EN_BIT |
|
||||
LIS2DH_ODR_BITS);
|
||||
}
|
||||
|
||||
static struct lis2dh_data lis2dh_driver;
|
||||
|
||||
DEVICE_INIT(lis2dh, CONFIG_LIS2DH_NAME, lis2dh_init, &lis2dh_driver,
|
||||
NULL, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY);
|
341
drivers/sensor/lis2dh/lis2dh.h
Normal file
341
drivers/sensor/lis2dh/lis2dh.h
Normal file
|
@ -0,0 +1,341 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __SENSOR_LIS2DH_H__
|
||||
#define __SENSOR_LIS2DH_H__
|
||||
|
||||
#include <kernel.h>
|
||||
#include <device.h>
|
||||
#include <misc/util.h>
|
||||
#include <stdint.h>
|
||||
#include <gpio.h>
|
||||
#include <sensor.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(CONFIG_LIS2DH_BUS_SPI)
|
||||
#include <spi.h>
|
||||
|
||||
#define LIS2DH_BUS_ADDRESS CONFIG_LIS2DH_SPI_SS_1
|
||||
|
||||
#define LIS2DH_SPI_READ_BIT BIT(7)
|
||||
#define LIS2DH_SPI_AUTOINC_ADDR BIT(6)
|
||||
#define LIS2DH_SPI_ADDR_MASK BIT_MASK(6)
|
||||
|
||||
/* LIS2DH supports only SPI mode 0, word size 8 bits, MSB first */
|
||||
#define LIS2DH_SPI_CFG 0x80
|
||||
|
||||
#define LIS2DH_BUS_DEV_NAME CONFIG_LIS2DH_SPI_MASTER_DEV_NAME
|
||||
|
||||
#elif defined(CONFIG_LIS2DH_BUS_I2C)
|
||||
#include <i2c.h>
|
||||
|
||||
#define LIS2DH_BUS_ADDRESS CONFIG_LIS2DH_I2C_ADDR
|
||||
|
||||
#define LIS2DH_BUS_DEV_NAME CONFIG_LIS2DH_I2C_MASTER_DEV_NAME
|
||||
|
||||
#endif
|
||||
|
||||
#define LIS2DH_AUTOINCREMENT_ADDR BIT(7)
|
||||
|
||||
#define LIS2DH_REG_CTRL1 0x20
|
||||
#define LIS2DH_ACCEL_XYZ_SHIFT 0
|
||||
#define LIS2DH_ACCEL_X_EN_BIT BIT(0)
|
||||
#define LIS2DH_ACCEL_Y_EN_BIT BIT(1)
|
||||
#define LIS2DH_ACCEL_Z_EN_BIT BIT(2)
|
||||
#define LIS2DH_ACCEL_EN_BITS (LIS2DH_ACCEL_X_EN_BIT | \
|
||||
LIS2DH_ACCEL_Y_EN_BIT | \
|
||||
LIS2DH_ACCEL_Z_EN_BIT)
|
||||
#define LIS2DH_ACCEL_XYZ_MASK BIT_MASK(3)
|
||||
|
||||
#if defined(CONFIG_LIS2DH_POWER_MODE_LOW)
|
||||
#define LIS2DH_LP_EN_BIT BIT(3)
|
||||
#elif defined(CONFIG_LIS2DH_POWER_MODE_NORMAL)
|
||||
#define LIS2DH_LP_EN_BIT 0
|
||||
#endif
|
||||
|
||||
#define LIS2DH_ODR_1 1
|
||||
#define LIS2DH_ODR_2 2
|
||||
#define LIS2DH_ODR_3 3
|
||||
#define LIS2DH_ODR_4 4
|
||||
#define LIS2DH_ODR_5 5
|
||||
#define LIS2DH_ODR_6 6
|
||||
#define LIS2DH_ODR_7 7
|
||||
#define LIS2DH_ODR_8 8
|
||||
#define LIS2DH_ODR_9 9
|
||||
|
||||
#if defined(CONFIG_LIS2DH_ODR_1)
|
||||
#define LIS2DH_ODR_IDX LIS2DH_ODR_1
|
||||
#elif defined(CONFIG_LIS2DH_ODR_2)
|
||||
#define LIS2DH_ODR_IDX LIS2DH_ODR_2
|
||||
#elif defined(CONFIG_LIS2DH_ODR_3)
|
||||
#define LIS2DH_ODR_IDX LIS2DH_ODR_3
|
||||
#elif defined(CONFIG_LIS2DH_ODR_4) || defined(CONFIG_LIS2DH_ODR_RUNTIME)
|
||||
#define LIS2DH_ODR_IDX LIS2DH_ODR_4
|
||||
#elif defined(CONFIG_LIS2DH_ODR_5)
|
||||
#define LIS2DH_ODR_IDX LIS2DH_ODR_5
|
||||
#elif defined(CONFIG_LIS2DH_ODR_6)
|
||||
#define LIS2DH_ODR_IDX LIS2DH_ODR_6
|
||||
#elif defined(CONFIG_LIS2DH_ODR_7)
|
||||
#define LIS2DH_ODR_IDX LIS2DH_ODR_7
|
||||
#elif defined(CONFIG_LIS2DH_ODR_8)
|
||||
#define LIS2DH_ODR_IDX LIS2DH_ODR_8
|
||||
#elif defined(CONFIG_LIS2DH_ODR_9_NORMAL) || defined(CONFIG_LIS2DH_ODR_9_LOW)
|
||||
#define LIS2DH_ODR_IDX LIS2DH_ODR_9
|
||||
#endif
|
||||
|
||||
#define LIS2DH_ODR_SHIFT 4
|
||||
#define LIS2DH_ODR_RATE(r) ((r) << LIS2DH_ODR_SHIFT)
|
||||
#define LIS2DH_ODR_BITS (LIS2DH_ODR_RATE(LIS2DH_ODR_IDX))
|
||||
#define LIS2DH_ODR_MASK (BIT_MASK(4) << LIS2DH_ODR_SHIFT)
|
||||
|
||||
#define LIS2DH_REG_CTRL2 0x21
|
||||
#define LIS2DH_HPIS2_EN_BIT BIT(1)
|
||||
#define LIS2DH_FDS_EN_BIT BIT(3)
|
||||
|
||||
#define LIS2DH_REG_CTRL3 0x22
|
||||
#define LIS2DH_EN_DRDY1_INT1_SHIFT 4
|
||||
#define LIS2DH_EN_DRDY1_INT1 BIT(LIS2DH_EN_DRDY1_INT1_SHIFT)
|
||||
|
||||
#define LIS2DH_REG_CTRL4 0x23
|
||||
#define LIS2DH_FS_SHIFT 4
|
||||
#define LIS2DH_FS_MASK (BIT_MASK(2) << LIS2DH_FS_SHIFT)
|
||||
|
||||
#if defined(CONFIG_LIS2DH_ACCEL_RANGE_2G) ||\
|
||||
defined(CONFIG_LIS2DH_ACCEL_RANGE_RUNTIME)
|
||||
#define LIS2DH_FS_IDX 0
|
||||
#elif defined(CONFIG_LIS2DH_ACCEL_RANGE_4G)
|
||||
#define LIS2DH_FS_IDX 1
|
||||
#elif defined(CONFIG_LIS2DH_ACCEL_RANGE_8G)
|
||||
#define LIS2DH_FS_IDX 2
|
||||
#elif defined(CONFIG_LIS2DH_ACCEL_RANGE_16G)
|
||||
#define LIS2DH_FS_IDX 3
|
||||
#endif
|
||||
|
||||
#define LIS2DH_FS_SELECT(fs) ((fs) << LIS2DH_FS_SHIFT)
|
||||
#define LIS2DH_FS_BITS (LIS2DH_FS_SELECT(LIS2DH_FS_IDX))
|
||||
#define LIS2DH_ACCEL_SCALE(range_g) ((SENSOR_G * 2 * (range_g)) / 65636LL)
|
||||
|
||||
#define LIS2DH_REG_CTRL5 0x24
|
||||
#define LIS2DH_LIR_INT2_SHIFT 1
|
||||
#define LIS2DH_EN_LIR_INT2 BIT(LIS2DH_LIR_INT2_SHIFT)
|
||||
|
||||
#define LIS2DH_REG_CTRL6 0x25
|
||||
#define LIS2DH_EN_INT2_INT2_SHIFT 5
|
||||
#define LIS2DH_EN_INT2_INT2 BIT(LIS2DH_EN_INT2_INT2_SHIFT)
|
||||
|
||||
#define LIS2DH_REG_REFERENCE 0x26
|
||||
|
||||
#define LIS2DH_REG_STATUS 0x27
|
||||
#define LIS2DH_STATUS_ZYZ_OVR BIT(7)
|
||||
#define LIS2DH_STATUS_Z_OVR BIT(6)
|
||||
#define LIS2DH_STATUS_Y_OVR BIT(5)
|
||||
#define LIS2DH_STATUS_X_OVR BIT(4)
|
||||
#define LIS2DH_STATUS_OVR_MASK (BIT_MASK(4) << 4)
|
||||
#define LIS2DH_STATUS_ZYX_DRDY BIT(3)
|
||||
#define LIS2DH_STATUS_Z_DRDY BIT(2)
|
||||
#define LIS2DH_STATUS_Y_DRDY BIT(1)
|
||||
#define LIS2DH_STATUS_X_DRDY BIT(0)
|
||||
#define LIS2DH_STATUS_DRDY_MASK BIT_MASK(4)
|
||||
|
||||
#define LIS2DH_REG_ACCEL_X_LSB 0x28
|
||||
#define LIS2DH_REG_ACCEL_Y_LSB 0x2A
|
||||
#define LIS2DH_REG_ACCEL_Z_LSB 0x2C
|
||||
#define LIS2DH_REG_ACCEL_X_MSB 0x29
|
||||
#define LIS2DH_REG_ACCEL_Y_MSB 0x2B
|
||||
#define LIS2DH_REG_ACCEL_Z_MSB 0x2D
|
||||
|
||||
#define LIS2DH_REG_INT1_CFG 0x30
|
||||
#define LIS2DH_REG_INT2_CFG 0x34
|
||||
#define LIS2DH_AOI_CFG BIT(7)
|
||||
#define LIS2DH_INT_CFG_ZHIE_ZUPE BIT(5)
|
||||
#define LIS2DH_INT_CFG_ZLIE_ZDOWNE BIT(4)
|
||||
#define LIS2DH_INT_CFG_YHIE_YUPE BIT(3)
|
||||
#define LIS2DH_INT_CFG_YLIE_YDOWNE BIT(2)
|
||||
#define LIS2DH_INT_CFG_XHIE_XUPE BIT(1)
|
||||
#define LIS2DH_INT_CFG_XLIE_XDOWNE BIT(0)
|
||||
|
||||
#define LIS2DH_REG_INT2_SRC 0x35
|
||||
|
||||
#define LIS2DH_REG_INT2_THS 0x36
|
||||
|
||||
#define LIS2DH_REG_INT2_DUR 0x37
|
||||
|
||||
/* sample buffer size includes status register */
|
||||
#if defined(CONFIG_LIS2DH_BUS_SPI)
|
||||
#define LIS2DH_BUF_SZ 8
|
||||
#define LIS2DH_DATA_OFS 1
|
||||
#else
|
||||
#define LIS2DH_BUF_SZ 7
|
||||
#define LIS2DH_DATA_OFS 0
|
||||
#endif
|
||||
|
||||
union lis2dh_sample {
|
||||
u8_t raw[LIS2DH_BUF_SZ];
|
||||
struct {
|
||||
#if defined(CONFIG_LIS2DH_BUS_SPI)
|
||||
u8_t dummy;
|
||||
#endif
|
||||
u8_t status;
|
||||
s16_t xyz[3];
|
||||
} __packed;
|
||||
};
|
||||
|
||||
struct lis2dh_data {
|
||||
struct device *bus;
|
||||
|
||||
union lis2dh_sample sample;
|
||||
/* current scaling factor, in micro m/s^2 / lsb */
|
||||
u16_t scale;
|
||||
|
||||
#ifdef CONFIG_LIS2DH_TRIGGER
|
||||
struct device *gpio;
|
||||
struct gpio_callback gpio_int1_cb;
|
||||
struct gpio_callback gpio_int2_cb;
|
||||
|
||||
sensor_trigger_handler_t handler_drdy;
|
||||
sensor_trigger_handler_t handler_anymotion;
|
||||
atomic_t trig_flags;
|
||||
enum sensor_channel chan_drdy;
|
||||
|
||||
#if defined(CONFIG_LIS2DH_TRIGGER_OWN_THREAD)
|
||||
char __stack thread_stack[CONFIG_LIS2DH_THREAD_STACK_SIZE];
|
||||
struct k_sem gpio_sem;
|
||||
#elif defined(CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD)
|
||||
struct k_work work;
|
||||
struct device *dev;
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_LIS2DH_TRIGGER */
|
||||
};
|
||||
|
||||
#define SYS_LOG_DOMAIN "lis2dh"
|
||||
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SENSOR_LEVEL
|
||||
#include <logging/sys_log.h>
|
||||
|
||||
static inline int lis2dh_burst_read(struct device *bus, u8_t start_addr,
|
||||
u8_t *buf, u8_t num_bytes)
|
||||
{
|
||||
#if defined(CONFIG_LIS2DH_BUS_SPI)
|
||||
int status;
|
||||
u8_t tx_buf = LIS2DH_SPI_READ_BIT | LIS2DH_SPI_AUTOINC_ADDR |
|
||||
start_addr;
|
||||
|
||||
status = spi_slave_select(bus, LIS2DH_BUS_ADDRESS);
|
||||
status = spi_transceive(bus, &tx_buf, 1, buf, num_bytes);
|
||||
|
||||
SYS_LOG_DBG("tx=0x%x num=0x%x, status=%d", tx_buf,
|
||||
num_bytes, status);
|
||||
|
||||
return status;
|
||||
#elif defined(CONFIG_LIS2DH_BUS_I2C)
|
||||
return i2c_burst_read(bus, LIS2DH_BUS_ADDRESS, start_addr, buf,
|
||||
num_bytes);
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int lis2dh_reg_read_byte(struct device *bus, u8_t reg_addr,
|
||||
u8_t *value)
|
||||
{
|
||||
#if defined(CONFIG_LIS2DH_BUS_SPI)
|
||||
int status;
|
||||
u8_t tx_buf = LIS2DH_SPI_READ_BIT | reg_addr;
|
||||
u8_t rx_buf[2];
|
||||
|
||||
status = spi_slave_select(bus, LIS2DH_BUS_ADDRESS);
|
||||
status = spi_transceive(bus, &tx_buf, 1, rx_buf, 2);
|
||||
|
||||
if (status == 0) {
|
||||
*value = rx_buf[1];
|
||||
}
|
||||
|
||||
SYS_LOG_DBG("tx=0x%x rx1=0x%x", tx_buf, rx_buf[1]);
|
||||
|
||||
return status;
|
||||
#elif defined(CONFIG_LIS2DH_BUS_I2C)
|
||||
return i2c_reg_read_byte(bus, LIS2DH_BUS_ADDRESS, reg_addr, value);
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int lis2dh_burst_write(struct device *bus, u8_t start_addr,
|
||||
u8_t *buf, u8_t num_bytes)
|
||||
{
|
||||
#if defined(CONFIG_LIS2DH_BUS_SPI)
|
||||
int status;
|
||||
u8_t dummy;
|
||||
|
||||
buf[0] = LIS2DH_SPI_AUTOINC_ADDR | start_addr;
|
||||
|
||||
status = spi_slave_select(bus, LIS2DH_BUS_ADDRESS);
|
||||
status = spi_transceive(bus, buf, num_bytes, &dummy, 0);
|
||||
|
||||
SYS_LOG_DBG("tx=0x%x num=0x%x, status=%d", buf[0],
|
||||
num_bytes, status);
|
||||
|
||||
return status;
|
||||
#elif defined(CONFIG_LIS2DH_BUS_I2C)
|
||||
return i2c_burst_write(bus, LIS2DH_BUS_ADDRESS, start_addr, buf,
|
||||
num_bytes);
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int lis2dh_reg_write_byte(struct device *bus, u8_t reg_addr,
|
||||
u8_t value)
|
||||
{
|
||||
#if defined(CONFIG_LIS2DH_BUS_SPI)
|
||||
u8_t tx_buf[2] = { reg_addr & LIS2DH_SPI_ADDR_MASK, value };
|
||||
u8_t dummy;
|
||||
|
||||
spi_slave_select(bus, LIS2DH_BUS_ADDRESS);
|
||||
|
||||
SYS_LOG_DBG("tx0=0x%x tx1=0x%x", tx_buf[0], tx_buf[1]);
|
||||
|
||||
return spi_transceive(bus, tx_buf, 2, &dummy, 0);
|
||||
#elif defined(CONFIG_LIS2DH_BUS_I2C)
|
||||
u8_t tx_buf[2] = {reg_addr, value};
|
||||
|
||||
return i2c_write(bus, tx_buf, sizeof(tx_buf), LIS2DH_BUS_ADDRESS);
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int lis2dh_bus_configure(struct device *bus)
|
||||
{
|
||||
#if defined(CONFIG_LIS2DH_BUS_SPI)
|
||||
struct spi_config config = {
|
||||
.config = LIS2DH_SPI_CFG,
|
||||
.max_sys_freq = CONFIG_LIS2DH_SPI_FREQUENCY,
|
||||
};
|
||||
|
||||
return spi_configure(bus, &config);
|
||||
#elif defined(CONFIG_LIS2DH_BUS_I2C)
|
||||
return 0;
|
||||
#else
|
||||
return -ENODEV;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LIS2DH_TRIGGER
|
||||
int lis2dh_trigger_set(struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler);
|
||||
|
||||
int lis2dh_init_interrupt(struct device *dev);
|
||||
|
||||
int lis2dh_reg_field_update(struct device *bus, u8_t reg_addr,
|
||||
u8_t pos, u8_t mask, u8_t val);
|
||||
|
||||
int lis2dh_acc_slope_config(struct device *dev, enum sensor_attribute attr,
|
||||
const struct sensor_value *val);
|
||||
#endif
|
||||
|
||||
#endif /* __SENSOR_LIS2DH__ */
|
433
drivers/sensor/lis2dh/lis2dh_trigger.c
Normal file
433
drivers/sensor/lis2dh/lis2dh_trigger.c
Normal file
|
@ -0,0 +1,433 @@
|
|||
/*
|
||||
* Copyright (c) 2017 Intel Corporation
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "lis2dh.h"
|
||||
|
||||
#include <misc/util.h>
|
||||
#include <kernel.h>
|
||||
|
||||
#define START_TRIG_INT1 BIT(0)
|
||||
#define START_TRIG_INT2 BIT(1)
|
||||
#define TRIGGED_INT1 BIT(4)
|
||||
#define TRIGGED_INT2 BIT(5)
|
||||
|
||||
static int lis2dh_trigger_drdy_set(struct device *dev, enum sensor_channel chan,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
int status;
|
||||
|
||||
gpio_pin_disable_callback(lis2dh->gpio, CONFIG_LIS2DH_INT1_GPIO_PIN);
|
||||
|
||||
/* cancel potentially pending trigger */
|
||||
atomic_clear_bit(&lis2dh->trig_flags, TRIGGED_INT1);
|
||||
|
||||
status = lis2dh_reg_field_update(lis2dh->bus, LIS2DH_REG_CTRL3,
|
||||
LIS2DH_EN_DRDY1_INT1_SHIFT,
|
||||
LIS2DH_EN_DRDY1_INT1, 0);
|
||||
|
||||
lis2dh->handler_drdy = handler;
|
||||
if ((handler == NULL) || (status < 0)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
lis2dh->chan_drdy = chan;
|
||||
|
||||
/* serialize start of int1 in thread to synchronize output sampling
|
||||
* and first interrupt. this avoids concurrent bus context access.
|
||||
*/
|
||||
atomic_set_bit(&lis2dh->trig_flags, START_TRIG_INT1);
|
||||
#if defined(CONFIG_LIS2DH_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&lis2dh->gpio_sem);
|
||||
#elif defined(CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&lis2dh->work);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lis2dh_start_trigger_int1(const struct lis2dh_data *lis2dh)
|
||||
{
|
||||
int status;
|
||||
u8_t raw[LIS2DH_BUF_SZ];
|
||||
u8_t ctrl1 = 0;
|
||||
|
||||
/* power down temporarly to align interrupt & data output sampling */
|
||||
status = lis2dh_reg_read_byte(lis2dh->bus, LIS2DH_REG_CTRL1, &ctrl1);
|
||||
if (unlikely(status < 0)) {
|
||||
return status;
|
||||
}
|
||||
status = lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_CTRL1,
|
||||
ctrl1 & ~LIS2DH_ODR_MASK);
|
||||
if (unlikely(status < 0)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
SYS_LOG_DBG("ctrl1=0x%x @tick=%u", ctrl1, k_cycle_get_32());
|
||||
|
||||
/* empty output data */
|
||||
status = lis2dh_burst_read(lis2dh->bus, LIS2DH_REG_STATUS, raw,
|
||||
sizeof(raw));
|
||||
if (unlikely(status < 0)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
gpio_pin_enable_callback(lis2dh->gpio, CONFIG_LIS2DH_INT1_GPIO_PIN);
|
||||
|
||||
/* re-enable output sampling */
|
||||
status = lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_CTRL1, ctrl1);
|
||||
if (unlikely(status < 0)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return lis2dh_reg_field_update(lis2dh->bus, LIS2DH_REG_CTRL3,
|
||||
LIS2DH_EN_DRDY1_INT1_SHIFT,
|
||||
LIS2DH_EN_DRDY1_INT1, 1);
|
||||
}
|
||||
|
||||
#define LIS2DH_ANYM_CFG (LIS2DH_INT_CFG_ZHIE_ZUPE | LIS2DH_INT_CFG_YHIE_YUPE |\
|
||||
LIS2DH_INT_CFG_XHIE_XUPE)
|
||||
|
||||
static int lis2dh_trigger_anym_set(struct device *dev,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
int status;
|
||||
u8_t reg_val;
|
||||
|
||||
gpio_pin_disable_callback(lis2dh->gpio, CONFIG_LIS2DH_INT2_GPIO_PIN);
|
||||
|
||||
/* cancel potentially pending trigger */
|
||||
atomic_clear_bit(&lis2dh->trig_flags, TRIGGED_INT2);
|
||||
|
||||
/* disable all interrupt 2 events */
|
||||
status = lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_INT2_CFG, 0);
|
||||
|
||||
/* make sure any pending interrupt is cleared */
|
||||
status = lis2dh_reg_read_byte(lis2dh->bus, LIS2DH_REG_INT2_SRC,
|
||||
®_val);
|
||||
|
||||
lis2dh->handler_anymotion = handler;
|
||||
if ((handler == NULL) || (status < 0)) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* serialize start of int2 in thread to synchronize output sampling
|
||||
* and first interrupt. this avoids concurrent bus context access.
|
||||
*/
|
||||
atomic_set_bit(&lis2dh->trig_flags, START_TRIG_INT2);
|
||||
#if defined(CONFIG_LIS2DH_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&lis2dh->gpio_sem);
|
||||
#elif defined(CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&lis2dh->work);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lis2dh_start_trigger_int2(const struct lis2dh_data *lis2dh)
|
||||
{
|
||||
int status;
|
||||
|
||||
status = gpio_pin_enable_callback(lis2dh->gpio,
|
||||
CONFIG_LIS2DH_INT2_GPIO_PIN);
|
||||
if (unlikely(status < 0)) {
|
||||
SYS_LOG_ERR("enable callback failed err=%d", status);
|
||||
}
|
||||
|
||||
return lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_INT2_CFG,
|
||||
LIS2DH_ANYM_CFG);
|
||||
}
|
||||
|
||||
int lis2dh_trigger_set(struct device *dev,
|
||||
const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
if (trig->type == SENSOR_TRIG_DATA_READY &&
|
||||
trig->chan == SENSOR_CHAN_ACCEL_XYZ) {
|
||||
return lis2dh_trigger_drdy_set(dev, trig->chan, handler);
|
||||
} else if (trig->type == SENSOR_TRIG_DELTA) {
|
||||
return lis2dh_trigger_anym_set(dev, handler);
|
||||
}
|
||||
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
int lis2dh_acc_slope_config(struct device *dev, enum sensor_attribute attr,
|
||||
const struct sensor_value *val)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
int status;
|
||||
|
||||
if (attr == SENSOR_ATTR_SLOPE_TH) {
|
||||
u8_t range_g, reg_val;
|
||||
u32_t slope_th_ums2;
|
||||
|
||||
status = lis2dh_reg_read_byte(lis2dh->bus, LIS2DH_REG_CTRL4,
|
||||
®_val);
|
||||
if (status < 0) {
|
||||
return status;
|
||||
}
|
||||
|
||||
/* fs reg value is in the range 0 (2g) - 3 (16g) */
|
||||
range_g = 2 * (1 << ((LIS2DH_FS_MASK & reg_val)
|
||||
>> LIS2DH_FS_SHIFT));
|
||||
|
||||
slope_th_ums2 = val->val1 * 1000000 + val->val2;
|
||||
|
||||
/* make sure the provided threshold does not exceed range */
|
||||
if ((slope_th_ums2 - 1) > (range_g * SENSOR_G)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* 7 bit full range value */
|
||||
reg_val = 128 / range_g * (slope_th_ums2 - 1) / SENSOR_G;
|
||||
|
||||
SYS_LOG_INF("int2_ths=0x%x range_g=%d ums2=%u", reg_val,
|
||||
range_g, slope_th_ums2 - 1);
|
||||
|
||||
status = lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_INT2_THS,
|
||||
reg_val);
|
||||
} else { /* SENSOR_ATTR_SLOPE_DUR */
|
||||
/*
|
||||
* slope duration is measured in number of samples:
|
||||
* N/ODR where N is the register value
|
||||
*/
|
||||
if (val->val1 < 0 || val->val1 > 127) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
SYS_LOG_INF("int2_dur=0x%x", val->val1);
|
||||
|
||||
status = lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_INT2_DUR,
|
||||
val->val1);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static void lis2dh_gpio_int1_callback(struct device *dev,
|
||||
struct gpio_callback *cb, u32_t pins)
|
||||
{
|
||||
struct lis2dh_data *lis2dh =
|
||||
CONTAINER_OF(cb, struct lis2dh_data, gpio_int1_cb);
|
||||
|
||||
ARG_UNUSED(pins);
|
||||
|
||||
atomic_set_bit(&lis2dh->trig_flags, TRIGGED_INT1);
|
||||
|
||||
#if defined(CONFIG_LIS2DH_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&lis2dh->gpio_sem);
|
||||
#elif defined(CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&lis2dh->work);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void lis2dh_gpio_int2_callback(struct device *dev,
|
||||
struct gpio_callback *cb, u32_t pins)
|
||||
{
|
||||
struct lis2dh_data *lis2dh =
|
||||
CONTAINER_OF(cb, struct lis2dh_data, gpio_int2_cb);
|
||||
|
||||
ARG_UNUSED(pins);
|
||||
|
||||
atomic_set_bit(&lis2dh->trig_flags, TRIGGED_INT2);
|
||||
|
||||
#if defined(CONFIG_LIS2DH_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&lis2dh->gpio_sem);
|
||||
#elif defined(CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&lis2dh->work);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void lis2dh_thread_cb(void *arg)
|
||||
{
|
||||
struct device *dev = arg;
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
|
||||
if (unlikely(atomic_test_and_clear_bit(&lis2dh->trig_flags,
|
||||
START_TRIG_INT1))) {
|
||||
int status = lis2dh_start_trigger_int1(lis2dh);
|
||||
|
||||
if (unlikely(status < 0)) {
|
||||
SYS_LOG_ERR("lis2dh_start_trigger_int1: %d", status);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (unlikely(atomic_test_and_clear_bit(&lis2dh->trig_flags,
|
||||
START_TRIG_INT2))) {
|
||||
int status = lis2dh_start_trigger_int2(lis2dh);
|
||||
|
||||
if (unlikely(status < 0)) {
|
||||
SYS_LOG_ERR("lis2dh_start_trigger_int2: %d", status);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (atomic_test_and_clear_bit(&lis2dh->trig_flags,
|
||||
TRIGGED_INT1)) {
|
||||
struct sensor_trigger drdy_trigger = {
|
||||
.type = SENSOR_TRIG_DATA_READY,
|
||||
.chan = lis2dh->chan_drdy,
|
||||
};
|
||||
|
||||
if (likely(lis2dh->handler_drdy != NULL)) {
|
||||
lis2dh->handler_drdy(dev, &drdy_trigger);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (atomic_test_and_clear_bit(&lis2dh->trig_flags,
|
||||
TRIGGED_INT2)) {
|
||||
struct sensor_trigger anym_trigger = {
|
||||
.type = SENSOR_TRIG_DELTA,
|
||||
.chan = lis2dh->chan_drdy,
|
||||
};
|
||||
u8_t reg_val;
|
||||
|
||||
/* clear interrupt 2 to de-assert int2 line */
|
||||
lis2dh_reg_read_byte(lis2dh->bus, LIS2DH_REG_INT2_SRC,
|
||||
®_val);
|
||||
|
||||
if (likely(lis2dh->handler_anymotion != NULL)) {
|
||||
lis2dh->handler_anymotion(dev, &anym_trigger);
|
||||
}
|
||||
|
||||
SYS_LOG_DBG("@tick=%u int2_src=0x%x", k_cycle_get_32(),
|
||||
reg_val);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_LIS2DH_TRIGGER_OWN_THREAD
|
||||
static void lis2dh_thread(void *arg1, void *unused2, void *unused3)
|
||||
{
|
||||
struct device *dev = arg1;
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
|
||||
ARG_UNUSED(unused2);
|
||||
ARG_UNUSED(unused3);
|
||||
|
||||
while (1) {
|
||||
k_sem_take(&lis2dh->gpio_sem, K_FOREVER);
|
||||
lis2dh_thread_cb(dev);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD
|
||||
static void lis2dh_work_cb(struct k_work *work)
|
||||
{
|
||||
struct lis2dh_data *lis2dh =
|
||||
CONTAINER_OF(work, struct lis2dh_data, work);
|
||||
|
||||
lis2dh_thread_cb(lis2dh->dev);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define LIS2DH_INT1_CFG (GPIO_DIR_IN | GPIO_INT |\
|
||||
GPIO_INT_EDGE | GPIO_INT_ACTIVE_HIGH)
|
||||
|
||||
#define LIS2DH_INT2_CFG (GPIO_DIR_IN | GPIO_INT |\
|
||||
GPIO_INT_EDGE | GPIO_INT_ACTIVE_HIGH)
|
||||
|
||||
int lis2dh_init_interrupt(struct device *dev)
|
||||
{
|
||||
struct lis2dh_data *lis2dh = dev->driver_data;
|
||||
int status;
|
||||
u8_t raw[LIS2DH_DATA_OFS + 2];
|
||||
|
||||
/* setup data ready gpio interrupt */
|
||||
lis2dh->gpio = device_get_binding(CONFIG_LIS2DH_GPIO_DEV_NAME);
|
||||
if (lis2dh->gpio == NULL) {
|
||||
SYS_LOG_ERR("Cannot get pointer to %s device",
|
||||
CONFIG_LIS2DH_GPIO_DEV_NAME);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* data ready int1 gpio configuration */
|
||||
status = gpio_pin_configure(lis2dh->gpio, CONFIG_LIS2DH_INT1_GPIO_PIN,
|
||||
LIS2DH_INT1_CFG);
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("Could not configure gpio %d",
|
||||
CONFIG_LIS2DH_INT1_GPIO_PIN);
|
||||
return status;
|
||||
}
|
||||
|
||||
gpio_init_callback(&lis2dh->gpio_int1_cb,
|
||||
lis2dh_gpio_int1_callback,
|
||||
BIT(CONFIG_LIS2DH_INT1_GPIO_PIN));
|
||||
|
||||
status = gpio_add_callback(lis2dh->gpio, &lis2dh->gpio_int1_cb);
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("Could not add gpio int1 callback");
|
||||
return status;
|
||||
}
|
||||
|
||||
/* any motion int2 gpio configuration */
|
||||
status = gpio_pin_configure(lis2dh->gpio, CONFIG_LIS2DH_INT2_GPIO_PIN,
|
||||
LIS2DH_INT2_CFG);
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("Could not configure gpio %d",
|
||||
CONFIG_LIS2DH_INT2_GPIO_PIN);
|
||||
return status;
|
||||
}
|
||||
|
||||
gpio_init_callback(&lis2dh->gpio_int2_cb,
|
||||
lis2dh_gpio_int2_callback,
|
||||
BIT(CONFIG_LIS2DH_INT2_GPIO_PIN));
|
||||
|
||||
/* callback is going to be enabled by trigger setting function */
|
||||
status = gpio_add_callback(lis2dh->gpio, &lis2dh->gpio_int2_cb);
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("Could not add gpio int2 callback (%d)", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_LIS2DH_TRIGGER_OWN_THREAD)
|
||||
k_sem_init(&lis2dh->gpio_sem, 0, UINT_MAX);
|
||||
|
||||
k_thread_spawn(lis2dh->thread_stack, CONFIG_LIS2DH_THREAD_STACK_SIZE,
|
||||
(k_thread_entry_t)lis2dh_thread, dev, NULL, NULL,
|
||||
K_PRIO_COOP(CONFIG_LIS2DH_THREAD_PRIORITY), 0, 0);
|
||||
#elif defined(CONFIG_LIS2DH_TRIGGER_GLOBAL_THREAD)
|
||||
lis2dh->work.handler = lis2dh_work_cb;
|
||||
lis2dh->dev = dev;
|
||||
#endif
|
||||
/* disable interrupt 2 in case of warm (re)boot */
|
||||
status = lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_INT2_CFG, 0);
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("Interrupt 2 disable reg write failed (%d)",
|
||||
status);
|
||||
return status;
|
||||
}
|
||||
|
||||
memset(raw, 0, sizeof(raw));
|
||||
status = lis2dh_burst_write(lis2dh->bus, LIS2DH_REG_INT2_THS, raw,
|
||||
sizeof(raw));
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("Burst write to INT2 THS failed (%d)", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
/* latch int2 line interrupt */
|
||||
status = lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_CTRL5,
|
||||
LIS2DH_EN_LIR_INT2);
|
||||
if (status < 0) {
|
||||
SYS_LOG_ERR("INT2 latch enable reg write failed (%d)", status);
|
||||
return status;
|
||||
}
|
||||
|
||||
SYS_LOG_INF("int1 on pin=%d cfg=0x%x, int2 on pin=%d cfg=0x%x",
|
||||
CONFIG_LIS2DH_INT1_GPIO_PIN, LIS2DH_INT1_CFG,
|
||||
CONFIG_LIS2DH_INT2_GPIO_PIN, LIS2DH_INT2_CFG);
|
||||
|
||||
/* enable interrupt 2 on int2 line */
|
||||
return lis2dh_reg_write_byte(lis2dh->bus, LIS2DH_REG_CTRL6,
|
||||
LIS2DH_EN_INT2_INT2);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue