sensor: fxas21002: Add gyroscope driver
Adds sensor driver support for the NXP FXAS21002 3-axis gyroscope. Includes statically configurable range and output data rate, as well as the sensor data ready trigger. Datasheet: http://www.nxp.com/assets/documents/data/en/data-sheets/FXAS21002.pdf Jira: ZEP-1392 Change-Id: I84587c4d5e76863245e9d045c6abb10b21b2615a Signed-off-by: Maureen Helm <maureen.helm@nxp.com>
This commit is contained in:
parent
bb9c8df891
commit
f38ea1636a
7 changed files with 751 additions and 0 deletions
|
@ -56,6 +56,8 @@ source "drivers/sensor/bmi160/Kconfig"
|
||||||
|
|
||||||
source "drivers/sensor/dht/Kconfig"
|
source "drivers/sensor/dht/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/sensor/fxas21002/Kconfig"
|
||||||
|
|
||||||
source "drivers/sensor/fxos8700/Kconfig"
|
source "drivers/sensor/fxos8700/Kconfig"
|
||||||
|
|
||||||
source "drivers/sensor/hdc1008/Kconfig"
|
source "drivers/sensor/hdc1008/Kconfig"
|
||||||
|
|
|
@ -8,6 +8,7 @@ obj-$(CONFIG_BME280) += bme280/
|
||||||
obj-$(CONFIG_BMG160) += bmg160/
|
obj-$(CONFIG_BMG160) += bmg160/
|
||||||
obj-$(CONFIG_BMI160) += bmi160/
|
obj-$(CONFIG_BMI160) += bmi160/
|
||||||
obj-$(CONFIG_DHT) += dht/
|
obj-$(CONFIG_DHT) += dht/
|
||||||
|
obj-$(CONFIG_FXAS21002) += fxas21002/
|
||||||
obj-$(CONFIG_FXOS8700) += fxos8700/
|
obj-$(CONFIG_FXOS8700) += fxos8700/
|
||||||
obj-$(CONFIG_HDC1008) += hdc1008/
|
obj-$(CONFIG_HDC1008) += hdc1008/
|
||||||
obj-$(CONFIG_HMC5883L) += hmc5883l/
|
obj-$(CONFIG_HMC5883L) += hmc5883l/
|
||||||
|
|
116
drivers/sensor/fxas21002/Kconfig
Normal file
116
drivers/sensor/fxas21002/Kconfig
Normal file
|
@ -0,0 +1,116 @@
|
||||||
|
# Kconfig - FXAS21002 3-axis gyroscope
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017, NXP
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
menuconfig FXAS21002
|
||||||
|
bool "FXAS21002 gyroscope driver"
|
||||||
|
depends on SENSOR && I2C
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable driver for the FXAS21002 gyroscope
|
||||||
|
|
||||||
|
if FXAS21002
|
||||||
|
|
||||||
|
config FXAS21002_NAME
|
||||||
|
string "Device name"
|
||||||
|
default "FXAS21002"
|
||||||
|
|
||||||
|
config FXAS21002_I2C_NAME
|
||||||
|
string "I2C device name"
|
||||||
|
default I2C_0_NAME
|
||||||
|
|
||||||
|
config FXAS21002_I2C_ADDRESS
|
||||||
|
hex "I2C address"
|
||||||
|
range 0x20 0x21
|
||||||
|
default 0x20
|
||||||
|
help
|
||||||
|
The I2C slave address can be configured by the SA0 input pin. This
|
||||||
|
option should usually be set by the board defconfig.
|
||||||
|
|
||||||
|
config FXAS21002_WHOAMI
|
||||||
|
hex "WHOAMI value"
|
||||||
|
range 0x00 0xff
|
||||||
|
default 0xd7
|
||||||
|
help
|
||||||
|
The datasheet defines the value of the WHOAMI register, but some
|
||||||
|
pre-production devices can have a different value. It is unlikely you
|
||||||
|
should need to change this configuration option from the default.
|
||||||
|
|
||||||
|
config FXAS21002_RANGE
|
||||||
|
int "Full scale range"
|
||||||
|
range 0 3
|
||||||
|
default 0
|
||||||
|
help
|
||||||
|
Selects the full scale range
|
||||||
|
0: +/-2000 dps (62.5 mdps/LSB)
|
||||||
|
1: +/-1000 dps (31.25 mdps/LSB)
|
||||||
|
2: +/-500 dps (15.625 mdps/LSB)
|
||||||
|
3: +/-250 dps (7.8125 mdps/LSB)
|
||||||
|
|
||||||
|
config FXAS21002_DR
|
||||||
|
int "Output data rate"
|
||||||
|
range 0 7
|
||||||
|
default 3
|
||||||
|
help
|
||||||
|
Selects the output data rate
|
||||||
|
0: 800 Hz
|
||||||
|
1: 400 Hz
|
||||||
|
2: 200 Hz
|
||||||
|
3: 100 Hz
|
||||||
|
4: 50 Hz
|
||||||
|
5: 25 Hz
|
||||||
|
6: 12.5 Hz
|
||||||
|
7: 12.5 Hz
|
||||||
|
|
||||||
|
choice
|
||||||
|
prompt "Trigger mode"
|
||||||
|
default FXAS21002_TRIGGER_NONE
|
||||||
|
|
||||||
|
config FXAS21002_TRIGGER_NONE
|
||||||
|
bool "No trigger"
|
||||||
|
|
||||||
|
config FXAS21002_TRIGGER_GLOBAL_THREAD
|
||||||
|
bool "Use global thread"
|
||||||
|
select FXAS21002_TRIGGER
|
||||||
|
|
||||||
|
config FXAS21002_TRIGGER_OWN_THREAD
|
||||||
|
bool "Use own thread"
|
||||||
|
select FXAS21002_TRIGGER
|
||||||
|
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config FXAS21002_TRIGGER
|
||||||
|
bool
|
||||||
|
default n
|
||||||
|
|
||||||
|
if FXAS21002_TRIGGER
|
||||||
|
|
||||||
|
config FXAS21002_GPIO_NAME
|
||||||
|
string "GPIO device name"
|
||||||
|
|
||||||
|
config FXAS21002_GPIO_PIN
|
||||||
|
int "GPIO pin"
|
||||||
|
|
||||||
|
config FXAS21002_DRDY_INT1
|
||||||
|
bool "Data ready interrupt to INT1 pin"
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Say Y to route data ready interrupt to INT1 pin. Say N to route to
|
||||||
|
INT2 pin.
|
||||||
|
|
||||||
|
config FXAS21002_THREAD_PRIORITY
|
||||||
|
int "Own thread priority"
|
||||||
|
depends on FXAS21002_TRIGGER_OWN_THREAD
|
||||||
|
default 10
|
||||||
|
|
||||||
|
config FXAS21002_THREAD_STACK_SIZE
|
||||||
|
int "Own thread stack size"
|
||||||
|
depends on FXAS21002_TRIGGER_OWN_THREAD
|
||||||
|
default 1024
|
||||||
|
|
||||||
|
endif # FXAS21002_TRIGGER
|
||||||
|
|
||||||
|
endif # FXAS21002
|
9
drivers/sensor/fxas21002/Makefile
Normal file
9
drivers/sensor/fxas21002/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
# Makefile - FXAS21002 3-axis gyroscope
|
||||||
|
#
|
||||||
|
# Copyright (c) 2017, NXP
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
obj-$(CONFIG_FXAS21002) += fxas21002.o
|
||||||
|
obj-$(CONFIG_FXAS21002_TRIGGER) += fxas21002_trigger.o
|
301
drivers/sensor/fxas21002/fxas21002.c
Normal file
301
drivers/sensor/fxas21002/fxas21002.c
Normal file
|
@ -0,0 +1,301 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017, NXP
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fxas21002.h>
|
||||||
|
#include <misc/util.h>
|
||||||
|
#include <misc/__assert.h>
|
||||||
|
|
||||||
|
/* Sample period in microseconds, indexed by output data rate encoding (DR) */
|
||||||
|
static const uint32_t sample_period[] = {
|
||||||
|
1250, 2500, 5000, 10000, 20000, 40000, 80000, 80000
|
||||||
|
};
|
||||||
|
|
||||||
|
static int fxas21002_sample_fetch(struct device *dev, enum sensor_channel chan)
|
||||||
|
{
|
||||||
|
const struct fxas21002_config *config = dev->config->config_info;
|
||||||
|
struct fxas21002_data *data = dev->driver_data;
|
||||||
|
uint8_t buffer[FXAS21002_MAX_NUM_BYTES];
|
||||||
|
int16_t *raw;
|
||||||
|
int ret = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (chan != SENSOR_CHAN_ALL) {
|
||||||
|
SYS_LOG_ERR("Unsupported sensor channel");
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sem_take(&data->sem, K_FOREVER);
|
||||||
|
|
||||||
|
/* Read all the channels in one I2C transaction. */
|
||||||
|
if (i2c_burst_read(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_OUTXMSB, buffer, sizeof(buffer))) {
|
||||||
|
SYS_LOG_ERR("Could not fetch sample");
|
||||||
|
ret = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse the buffer into raw channel data (16-bit integers). To save
|
||||||
|
* RAM, store the data in raw format and wait to convert to the
|
||||||
|
* normalized sensor_value type until later.
|
||||||
|
*/
|
||||||
|
raw = &data->raw[0];
|
||||||
|
|
||||||
|
for (i = 0; i < sizeof(buffer); i += 2) {
|
||||||
|
*raw++ = (buffer[i] << 8) | (buffer[i+1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
k_sem_give(&data->sem);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fxas21002_convert(struct sensor_value *val, int16_t raw,
|
||||||
|
enum fxas21002_range range)
|
||||||
|
{
|
||||||
|
int32_t micro_rad;
|
||||||
|
|
||||||
|
/* Convert units to micro radians per second.*/
|
||||||
|
micro_rad = (raw * 62500) >> range;
|
||||||
|
|
||||||
|
val->val1 = micro_rad / 1000000;
|
||||||
|
val->val2 = micro_rad % 1000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fxas21002_channel_get(struct device *dev, enum sensor_channel chan,
|
||||||
|
struct sensor_value *val)
|
||||||
|
{
|
||||||
|
const struct fxas21002_config *config = dev->config->config_info;
|
||||||
|
struct fxas21002_data *data = dev->driver_data;
|
||||||
|
int start_channel;
|
||||||
|
int num_channels;
|
||||||
|
int16_t *raw;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
k_sem_take(&data->sem, K_FOREVER);
|
||||||
|
|
||||||
|
/* Start with an error return code by default, then clear it if we find
|
||||||
|
* a supported sensor channel.
|
||||||
|
*/
|
||||||
|
ret = -ENOTSUP;
|
||||||
|
|
||||||
|
/* Convert raw gyroscope data to the normalized sensor_value type. */
|
||||||
|
switch (chan) {
|
||||||
|
case SENSOR_CHAN_GYRO_X:
|
||||||
|
start_channel = FXAS21002_CHANNEL_GYRO_X;
|
||||||
|
num_channels = 1;
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GYRO_Y:
|
||||||
|
start_channel = FXAS21002_CHANNEL_GYRO_Y;
|
||||||
|
num_channels = 1;
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GYRO_Z:
|
||||||
|
start_channel = FXAS21002_CHANNEL_GYRO_Z;
|
||||||
|
num_channels = 1;
|
||||||
|
break;
|
||||||
|
case SENSOR_CHAN_GYRO_XYZ:
|
||||||
|
start_channel = FXAS21002_CHANNEL_GYRO_X;
|
||||||
|
num_channels = 3;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
start_channel = 0;
|
||||||
|
num_channels = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
raw = &data->raw[start_channel];
|
||||||
|
for (i = 0; i < num_channels; i++) {
|
||||||
|
fxas21002_convert(val++, *raw++, config->range);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_channels > 0) {
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
SYS_LOG_ERR("Unsupported sensor channel");
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sem_give(&data->sem);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fxas21002_get_power(struct device *dev, enum fxas21002_power *power)
|
||||||
|
{
|
||||||
|
const struct fxas21002_config *config = dev->config->config_info;
|
||||||
|
struct fxas21002_data *data = dev->driver_data;
|
||||||
|
uint8_t val = *power;
|
||||||
|
|
||||||
|
if (i2c_reg_read_byte(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_CTRLREG1,
|
||||||
|
&val)) {
|
||||||
|
SYS_LOG_ERR("Could not get power setting");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
val &= FXAS21002_CTRLREG1_POWER_MASK;
|
||||||
|
*power = val;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fxas21002_set_power(struct device *dev, enum fxas21002_power power)
|
||||||
|
{
|
||||||
|
const struct fxas21002_config *config = dev->config->config_info;
|
||||||
|
struct fxas21002_data *data = dev->driver_data;
|
||||||
|
|
||||||
|
return i2c_reg_update_byte(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_CTRLREG1,
|
||||||
|
FXAS21002_CTRLREG1_POWER_MASK,
|
||||||
|
power);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t fxas21002_get_transition_time(enum fxas21002_power start,
|
||||||
|
enum fxas21002_power end,
|
||||||
|
uint8_t dr)
|
||||||
|
{
|
||||||
|
uint32_t transition_time;
|
||||||
|
|
||||||
|
/* If not transitioning to active mode, then don't need to wait */
|
||||||
|
if (end != FXAS21002_POWER_ACTIVE) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, the transition time depends on which state we're
|
||||||
|
* transitioning from. These times are defined by the datasheet.
|
||||||
|
*/
|
||||||
|
transition_time = sample_period[dr];
|
||||||
|
|
||||||
|
if (start == FXAS21002_POWER_READY) {
|
||||||
|
transition_time += 5000;
|
||||||
|
} else {
|
||||||
|
transition_time += 60000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transition_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fxas21002_init(struct device *dev)
|
||||||
|
{
|
||||||
|
const struct fxas21002_config *config = dev->config->config_info;
|
||||||
|
struct fxas21002_data *data = dev->driver_data;
|
||||||
|
uint32_t transition_time;
|
||||||
|
uint8_t whoami;
|
||||||
|
uint8_t ctrlreg1;
|
||||||
|
|
||||||
|
/* Get the I2C device */
|
||||||
|
data->i2c = device_get_binding(config->i2c_name);
|
||||||
|
if (data->i2c == NULL) {
|
||||||
|
SYS_LOG_ERR("Could not find I2C device");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read the WHOAMI register to make sure we are talking to FXAS21002
|
||||||
|
* and not some other type of device that happens to have the same I2C
|
||||||
|
* address.
|
||||||
|
*/
|
||||||
|
if (i2c_reg_read_byte(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_WHOAMI, &whoami)) {
|
||||||
|
SYS_LOG_ERR("Could not get WHOAMI value");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (whoami != config->whoami) {
|
||||||
|
SYS_LOG_ERR("WHOAMI value received 0x%x, expected 0x%x",
|
||||||
|
whoami, config->whoami);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reset the sensor. Upon issuing a software reset command over the I2C
|
||||||
|
* interface, the sensor immediately resets and does not send any
|
||||||
|
* acknowledgment (ACK) of the written byte to the master. Therefore,
|
||||||
|
* do not check the return code of the I2C transaction.
|
||||||
|
*/
|
||||||
|
i2c_reg_write_byte(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_CTRLREG1, FXAS21002_CTRLREG1_RST_MASK);
|
||||||
|
|
||||||
|
/* Wait for the reset sequence to complete */
|
||||||
|
do {
|
||||||
|
if (i2c_reg_read_byte(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_CTRLREG1, &ctrlreg1)) {
|
||||||
|
SYS_LOG_ERR("Could not get ctrlreg1 value");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
} while (ctrlreg1 & FXAS21002_CTRLREG1_RST_MASK);
|
||||||
|
|
||||||
|
/* Set the full-scale range */
|
||||||
|
if (i2c_reg_update_byte(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_CTRLREG0,
|
||||||
|
FXAS21002_CTRLREG0_FS_MASK,
|
||||||
|
config->range)) {
|
||||||
|
SYS_LOG_ERR("Could not set range");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the output data rate */
|
||||||
|
if (i2c_reg_update_byte(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_CTRLREG1,
|
||||||
|
FXAS21002_CTRLREG1_DR_MASK,
|
||||||
|
config->dr << FXAS21002_CTRLREG1_DR_SHIFT)) {
|
||||||
|
SYS_LOG_ERR("Could not set output data rate");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if CONFIG_FXAS21002_TRIGGER
|
||||||
|
if (fxas21002_trigger_init(dev)) {
|
||||||
|
SYS_LOG_ERR("Could not initialize interrupts");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Set active */
|
||||||
|
if (fxas21002_set_power(dev, FXAS21002_POWER_ACTIVE)) {
|
||||||
|
SYS_LOG_ERR("Could not set active");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait the transition time from standby to active mode */
|
||||||
|
transition_time = fxas21002_get_transition_time(FXAS21002_POWER_STANDBY,
|
||||||
|
FXAS21002_POWER_ACTIVE,
|
||||||
|
config->dr);
|
||||||
|
k_busy_wait(transition_time);
|
||||||
|
|
||||||
|
|
||||||
|
k_sem_init(&data->sem, 0, UINT_MAX);
|
||||||
|
k_sem_give(&data->sem);
|
||||||
|
|
||||||
|
SYS_LOG_DBG("Init complete");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct sensor_driver_api fxas21002_driver_api = {
|
||||||
|
.sample_fetch = fxas21002_sample_fetch,
|
||||||
|
.channel_get = fxas21002_channel_get,
|
||||||
|
#if CONFIG_FXAS21002_TRIGGER
|
||||||
|
.trigger_set = fxas21002_trigger_set,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct fxas21002_config fxas21002_config = {
|
||||||
|
.i2c_name = CONFIG_FXAS21002_I2C_NAME,
|
||||||
|
.i2c_address = CONFIG_FXAS21002_I2C_ADDRESS,
|
||||||
|
.whoami = CONFIG_FXAS21002_WHOAMI,
|
||||||
|
.range = CONFIG_FXAS21002_RANGE,
|
||||||
|
.dr = CONFIG_FXAS21002_DR,
|
||||||
|
#ifdef CONFIG_FXAS21002_TRIGGER
|
||||||
|
.gpio_name = CONFIG_FXAS21002_GPIO_NAME,
|
||||||
|
.gpio_pin = CONFIG_FXAS21002_GPIO_PIN,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct fxas21002_data fxas21002_data;
|
||||||
|
|
||||||
|
DEVICE_AND_API_INIT(fxas21002, CONFIG_FXAS21002_NAME, fxas21002_init,
|
||||||
|
&fxas21002_data, &fxas21002_config,
|
||||||
|
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
|
||||||
|
&fxas21002_driver_api);
|
107
drivers/sensor/fxas21002/fxas21002.h
Normal file
107
drivers/sensor/fxas21002/fxas21002.h
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017, NXP
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sensor.h>
|
||||||
|
#include <i2c.h>
|
||||||
|
#include <gpio.h>
|
||||||
|
|
||||||
|
#define SYS_LOG_DOMAIN "FXAS21002"
|
||||||
|
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SENSOR_LEVEL
|
||||||
|
#include <logging/sys_log.h>
|
||||||
|
|
||||||
|
#define FXAS21002_REG_STATUS 0x00
|
||||||
|
#define FXAS21002_REG_OUTXMSB 0x01
|
||||||
|
#define FXAS21002_REG_INT_SOURCE 0x0b
|
||||||
|
#define FXAS21002_REG_WHOAMI 0x0c
|
||||||
|
#define FXAS21002_REG_CTRLREG0 0x0d
|
||||||
|
#define FXAS21002_REG_CTRLREG1 0x13
|
||||||
|
#define FXAS21002_REG_CTRLREG2 0x14
|
||||||
|
#define FXAS21002_REG_CTRLREG3 0x15
|
||||||
|
|
||||||
|
#define FXAS21002_INT_SOURCE_DRDY_MASK (1 << 0)
|
||||||
|
|
||||||
|
#define FXAS21002_CTRLREG0_FS_MASK (3 << 0)
|
||||||
|
|
||||||
|
#define FXAS21002_CTRLREG1_DR_SHIFT 2
|
||||||
|
|
||||||
|
#define FXAS21002_CTRLREG1_POWER_MASK (3 << 0)
|
||||||
|
#define FXAS21002_CTRLREG1_DR_MASK (7 << FXAS21002_CTRLREG1_DR_SHIFT)
|
||||||
|
#define FXAS21002_CTRLREG1_RST_MASK (1 << 6)
|
||||||
|
|
||||||
|
#define FXAS21002_CTRLREG2_CFG_EN_MASK (1 << 2)
|
||||||
|
#define FXAS21002_CTRLREG2_CFG_DRDY_MASK (1 << 3)
|
||||||
|
|
||||||
|
#define FXAS21002_MAX_NUM_CHANNELS 3
|
||||||
|
|
||||||
|
#define FXAS21002_BYTES_PER_CHANNEL 2
|
||||||
|
|
||||||
|
#define FXAS21002_MAX_NUM_BYTES (FXAS21002_BYTES_PER_CHANNEL * \
|
||||||
|
FXAS21002_MAX_NUM_CHANNELS)
|
||||||
|
|
||||||
|
enum fxas21002_power {
|
||||||
|
FXAS21002_POWER_STANDBY = 0,
|
||||||
|
FXAS21002_POWER_READY = 1,
|
||||||
|
FXAS21002_POWER_ACTIVE = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum fxas21002_range {
|
||||||
|
FXAS21002_RANGE_2000DPS = 0,
|
||||||
|
FXAS21002_RANGE_1000DPS,
|
||||||
|
FXAS21002_RANGE_500DPS,
|
||||||
|
FXAS21002_RANGE_250DPS,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum fxas21002_channel {
|
||||||
|
FXAS21002_CHANNEL_GYRO_X = 0,
|
||||||
|
FXAS21002_CHANNEL_GYRO_Y,
|
||||||
|
FXAS21002_CHANNEL_GYRO_Z,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fxas21002_config {
|
||||||
|
char *i2c_name;
|
||||||
|
#ifdef CONFIG_FXAS21002_TRIGGER
|
||||||
|
char *gpio_name;
|
||||||
|
uint8_t gpio_pin;
|
||||||
|
#endif
|
||||||
|
uint8_t i2c_address;
|
||||||
|
uint8_t whoami;
|
||||||
|
enum fxas21002_range range;
|
||||||
|
uint8_t dr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct fxas21002_data {
|
||||||
|
struct device *i2c;
|
||||||
|
struct k_sem sem;
|
||||||
|
#ifdef CONFIG_FXAS21002_TRIGGER
|
||||||
|
struct device *gpio;
|
||||||
|
uint8_t gpio_pin;
|
||||||
|
struct gpio_callback gpio_cb;
|
||||||
|
sensor_trigger_handler_t drdy_handler;
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_FXAS21002_TRIGGER_OWN_THREAD
|
||||||
|
char __stack thread_stack[CONFIG_FXAS21002_THREAD_STACK_SIZE];
|
||||||
|
struct k_sem trig_sem;
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_FXAS21002_TRIGGER_GLOBAL_THREAD
|
||||||
|
struct k_work work;
|
||||||
|
struct device *dev;
|
||||||
|
#endif
|
||||||
|
int16_t raw[FXAS21002_MAX_NUM_CHANNELS];
|
||||||
|
};
|
||||||
|
|
||||||
|
int fxas21002_get_power(struct device *dev, enum fxas21002_power *power);
|
||||||
|
int fxas21002_set_power(struct device *dev, enum fxas21002_power power);
|
||||||
|
|
||||||
|
uint32_t fxas21002_get_transition_time(enum fxas21002_power start,
|
||||||
|
enum fxas21002_power end,
|
||||||
|
uint8_t dr);
|
||||||
|
|
||||||
|
#if CONFIG_FXAS21002_TRIGGER
|
||||||
|
int fxas21002_trigger_init(struct device *dev);
|
||||||
|
int fxas21002_trigger_set(struct device *dev,
|
||||||
|
const struct sensor_trigger *trig,
|
||||||
|
sensor_trigger_handler_t handler);
|
||||||
|
#endif
|
215
drivers/sensor/fxas21002/fxas21002_trigger.c
Normal file
215
drivers/sensor/fxas21002/fxas21002_trigger.c
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2017, NXP
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <fxas21002.h>
|
||||||
|
|
||||||
|
static void fxas21002_gpio_callback(struct device *dev,
|
||||||
|
struct gpio_callback *cb,
|
||||||
|
uint32_t pin_mask)
|
||||||
|
{
|
||||||
|
struct fxas21002_data *data =
|
||||||
|
CONTAINER_OF(cb, struct fxas21002_data, gpio_cb);
|
||||||
|
|
||||||
|
if ((pin_mask & BIT(data->gpio_pin)) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_pin_disable_callback(dev, data->gpio_pin);
|
||||||
|
|
||||||
|
#if defined(CONFIG_FXAS21002_TRIGGER_OWN_THREAD)
|
||||||
|
k_sem_give(&data->trig_sem);
|
||||||
|
#elif defined(CONFIG_FXAS21002_TRIGGER_GLOBAL_THREAD)
|
||||||
|
k_work_submit(&data->work);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fxas21002_handle_drdy_int(struct device *dev)
|
||||||
|
{
|
||||||
|
struct fxas21002_data *data = dev->driver_data;
|
||||||
|
|
||||||
|
struct sensor_trigger drdy_trig = {
|
||||||
|
.type = SENSOR_TRIG_DATA_READY,
|
||||||
|
.chan = SENSOR_CHAN_ALL,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data->drdy_handler) {
|
||||||
|
data->drdy_handler(dev, &drdy_trig);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fxas21002_handle_int(void *arg)
|
||||||
|
{
|
||||||
|
struct device *dev = (struct device *)arg;
|
||||||
|
const struct fxas21002_config *config = dev->config->config_info;
|
||||||
|
struct fxas21002_data *data = dev->driver_data;
|
||||||
|
uint8_t int_source;
|
||||||
|
|
||||||
|
k_sem_take(&data->sem, K_FOREVER);
|
||||||
|
|
||||||
|
if (i2c_reg_read_byte(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_INT_SOURCE,
|
||||||
|
&int_source)) {
|
||||||
|
SYS_LOG_ERR("Could not read interrupt source");
|
||||||
|
int_source = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
k_sem_give(&data->sem);
|
||||||
|
|
||||||
|
if (int_source & FXAS21002_INT_SOURCE_DRDY_MASK) {
|
||||||
|
fxas21002_handle_drdy_int(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
gpio_pin_enable_callback(data->gpio, config->gpio_pin);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_FXAS21002_TRIGGER_OWN_THREAD
|
||||||
|
static void fxas21002_thread_main(void *arg1, void *unused1, void *unused2)
|
||||||
|
{
|
||||||
|
struct device *dev = (struct device *)arg1;
|
||||||
|
struct fxas21002_data *data = dev->driver_data;
|
||||||
|
|
||||||
|
ARG_UNUSED(unused1);
|
||||||
|
ARG_UNUSED(unused2);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
k_sem_take(&data->trig_sem, K_FOREVER);
|
||||||
|
fxas21002_handle_int(dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_FXAS21002_TRIGGER_GLOBAL_THREAD
|
||||||
|
static void fxas21002_work_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
struct fxas21002_data *data =
|
||||||
|
CONTAINER_OF(work, struct fxas21002_data, work);
|
||||||
|
|
||||||
|
fxas21002_handle_int(data->dev);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int fxas21002_trigger_set(struct device *dev,
|
||||||
|
const struct sensor_trigger *trig,
|
||||||
|
sensor_trigger_handler_t handler)
|
||||||
|
{
|
||||||
|
const struct fxas21002_config *config = dev->config->config_info;
|
||||||
|
struct fxas21002_data *data = dev->driver_data;
|
||||||
|
enum fxas21002_power power = FXAS21002_POWER_STANDBY;
|
||||||
|
uint32_t transition_time;
|
||||||
|
uint8_t mask;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
k_sem_take(&data->sem, K_FOREVER);
|
||||||
|
|
||||||
|
switch (trig->type) {
|
||||||
|
case SENSOR_TRIG_DATA_READY:
|
||||||
|
mask = FXAS21002_CTRLREG2_CFG_EN_MASK;
|
||||||
|
data->drdy_handler = handler;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SYS_LOG_ERR("Unsupported sensor trigger");
|
||||||
|
ret = -ENOTSUP;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The sensor must be in standby or ready mode when writing the
|
||||||
|
* configuration registers, therefore get the current power mode so we
|
||||||
|
* can restore it later.
|
||||||
|
*/
|
||||||
|
if (fxas21002_get_power(dev, &power)) {
|
||||||
|
SYS_LOG_ERR("Could not get power mode");
|
||||||
|
ret = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Put the sensor in ready mode */
|
||||||
|
if (fxas21002_set_power(dev, FXAS21002_POWER_READY)) {
|
||||||
|
SYS_LOG_ERR("Could not set ready mode");
|
||||||
|
ret = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure the sensor interrupt */
|
||||||
|
if (i2c_reg_update_byte(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_CTRLREG2,
|
||||||
|
mask,
|
||||||
|
handler ? mask : 0)) {
|
||||||
|
SYS_LOG_ERR("Could not configure interrupt");
|
||||||
|
ret = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore the previous power mode */
|
||||||
|
if (fxas21002_set_power(dev, power)) {
|
||||||
|
SYS_LOG_ERR("Could not restore power mode");
|
||||||
|
ret = -EIO;
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait the transition time from ready mode */
|
||||||
|
transition_time = fxas21002_get_transition_time(FXAS21002_POWER_READY,
|
||||||
|
power,
|
||||||
|
config->dr);
|
||||||
|
k_busy_wait(transition_time);
|
||||||
|
|
||||||
|
exit:
|
||||||
|
k_sem_give(&data->sem);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fxas21002_trigger_init(struct device *dev)
|
||||||
|
{
|
||||||
|
const struct fxas21002_config *config = dev->config->config_info;
|
||||||
|
struct fxas21002_data *data = dev->driver_data;
|
||||||
|
uint8_t ctrl_reg2;
|
||||||
|
|
||||||
|
#if defined(CONFIG_FXAS21002_TRIGGER_OWN_THREAD)
|
||||||
|
k_sem_init(&data->trig_sem, 0, UINT_MAX);
|
||||||
|
k_thread_spawn(data->thread_stack, CONFIG_FXAS21002_THREAD_STACK_SIZE,
|
||||||
|
fxas21002_thread_main, dev, 0, NULL,
|
||||||
|
K_PRIO_COOP(CONFIG_FXAS21002_THREAD_PRIORITY), 0, 0);
|
||||||
|
#elif defined(CONFIG_FXAS21002_TRIGGER_GLOBAL_THREAD)
|
||||||
|
data->work.handler = fxas21002_work_handler;
|
||||||
|
data->dev = dev;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Route the interrupts to INT1/INT2 pins */
|
||||||
|
ctrl_reg2 = 0;
|
||||||
|
#if CONFIG_FXAS21002_DRDY_INT1
|
||||||
|
ctrl_reg2 |= FXAS21002_CTRLREG2_CFG_DRDY_MASK;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (i2c_reg_write_byte(data->i2c, config->i2c_address,
|
||||||
|
FXAS21002_REG_CTRLREG2, ctrl_reg2)) {
|
||||||
|
SYS_LOG_ERR("Could not configure interrupt pin routing");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the GPIO device */
|
||||||
|
data->gpio = device_get_binding(config->gpio_name);
|
||||||
|
if (data->gpio == NULL) {
|
||||||
|
SYS_LOG_ERR("Could not find GPIO device");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
data->gpio_pin = config->gpio_pin;
|
||||||
|
|
||||||
|
gpio_pin_configure(data->gpio, config->gpio_pin,
|
||||||
|
GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
|
||||||
|
GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE);
|
||||||
|
|
||||||
|
gpio_init_callback(&data->gpio_cb, fxas21002_gpio_callback,
|
||||||
|
BIT(config->gpio_pin));
|
||||||
|
|
||||||
|
gpio_add_callback(data->gpio, &data->gpio_cb);
|
||||||
|
|
||||||
|
gpio_pin_enable_callback(data->gpio, config->gpio_pin);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue