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
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);
|
Loading…
Add table
Add a link
Reference in a new issue