drivers: sensor: Add icm40627 6-axis accelerometer driver
Initial driver for the icm40627 from Invensense/TDK, a 6-axis accelerometer with gyroscope and temperature sensing capabilities. Signed-off-by: Florijan Plohl <florijan.plohl@norik.com>
This commit is contained in:
parent
b377543628
commit
e0bd460de0
12 changed files with 1471 additions and 0 deletions
|
@ -2,6 +2,7 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
add_subdirectory_ifdef(CONFIG_ICM40627 icm40627)
|
||||
add_subdirectory_ifdef(CONFIG_ICM42605 icm42605)
|
||||
add_subdirectory_ifdef(CONFIG_ICM42688 icm42688)
|
||||
add_subdirectory_ifdef(CONFIG_ICM42X70 icm42x70)
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# zephyr-keep-sorted-start
|
||||
source "drivers/sensor/tdk/icm40627/Kconfig"
|
||||
source "drivers/sensor/tdk/icm42605/Kconfig"
|
||||
source "drivers/sensor/tdk/icm42688/Kconfig"
|
||||
source "drivers/sensor/tdk/icm42x70/Kconfig"
|
||||
|
|
10
drivers/sensor/tdk/icm40627/CMakeLists.txt
Normal file
10
drivers/sensor/tdk/icm40627/CMakeLists.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
zephyr_library()
|
||||
|
||||
zephyr_library_sources(
|
||||
icm40627.c
|
||||
icm40627_i2c.c
|
||||
)
|
||||
|
||||
zephyr_library_sources_ifdef(CONFIG_ICM40627_TRIGGER icm40627_trigger.c)
|
22
drivers/sensor/tdk/icm40627/Kconfig
Normal file
22
drivers/sensor/tdk/icm40627/Kconfig
Normal file
|
@ -0,0 +1,22 @@
|
|||
# ICM40627 Six-Axis Motion Tracking device configuration options
|
||||
#
|
||||
# Copyright (c) 2025 PHYTEC America LLC
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
menuconfig ICM40627
|
||||
bool "ICM40627 Six-Axis Motion Tracking Device"
|
||||
default y
|
||||
depends on DT_HAS_INVENSENSE_ICM40627_ENABLED
|
||||
select I2C
|
||||
help
|
||||
Enable driver for ICM40627 I2C-based six-axis motion tracking device.
|
||||
|
||||
if ICM40627
|
||||
|
||||
module = ICM40627
|
||||
thread_priority = 10
|
||||
thread_stack_size = 1024
|
||||
source "drivers/sensor/Kconfig.trigger_template"
|
||||
|
||||
endif # ICM40627
|
708
drivers/sensor/tdk/icm40627/icm40627.c
Normal file
708
drivers/sensor/tdk/icm40627/icm40627.c
Normal file
|
@ -0,0 +1,708 @@
|
|||
/*
|
||||
* Copyright (c) 2025 PHYTEC America LLC
|
||||
* Author: Florijan Plohl <florijan.plohl@norik.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#define DT_DRV_COMPAT invensense_icm40627
|
||||
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/sys/byteorder.h>
|
||||
#include "icm40627.h"
|
||||
#include "icm40627_reg.h"
|
||||
#include "icm40627_trigger.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_REGISTER(ICM40627, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
/*
|
||||
* Gyro FS to scaling factor mapping.
|
||||
* See datasheet section 3.1 for details
|
||||
*/
|
||||
static const uint16_t icm40627_gyro_sensitivity_x10[] = {
|
||||
[BIT_GYRO_UI_FS_2000] 164, [BIT_GYRO_UI_FS_1000] 328, [BIT_GYRO_UI_FS_500] 655,
|
||||
[BIT_GYRO_UI_FS_250] 1310, [BIT_GYRO_UI_FS_125] 2620, [BIT_GYRO_UI_FS_62] 5243,
|
||||
[BIT_GYRO_UI_FS_31] 10486, [BIT_GYRO_UI_FS_15] 20972,
|
||||
};
|
||||
|
||||
static int icm40627_set_accel_fs(const struct device *dev, uint16_t fs)
|
||||
{
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
struct icm40627_data *data = dev->data;
|
||||
struct sensor_value accel_fs_value;
|
||||
uint8_t temp, round_fs;
|
||||
|
||||
if ((fs > 16) || (fs < 2)) {
|
||||
LOG_ERR("Unsupported accel fs range");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (fs > 8) {
|
||||
temp = BIT_ACCEL_UI_FS_16;
|
||||
round_fs = 16;
|
||||
} else if (fs > 4) {
|
||||
temp = BIT_ACCEL_UI_FS_8;
|
||||
round_fs = 8;
|
||||
} else if (fs > 2) {
|
||||
temp = BIT_ACCEL_UI_FS_4;
|
||||
round_fs = 4;
|
||||
} else {
|
||||
temp = BIT_ACCEL_UI_FS_2;
|
||||
round_fs = 2;
|
||||
}
|
||||
|
||||
sensor_g_to_ms2(round_fs, &accel_fs_value);
|
||||
data->accel_fs = accel_fs_value.val1;
|
||||
data->accel_sensitivity_shift = MIN_ACCEL_SENS_SHIFT;
|
||||
|
||||
return cfg->bus_io->update(&cfg->bus, REG_ACCEL_CONFIG0, (uint8_t)MASK_ACCEL_UI_FS_SEL,
|
||||
temp);
|
||||
}
|
||||
|
||||
static int icm40627_set_gyro_fs(const struct device *dev, uint16_t fs)
|
||||
{
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
struct icm40627_data *data = dev->data;
|
||||
struct sensor_value gyro_fs_value;
|
||||
uint8_t temp;
|
||||
uint16_t round_fs;
|
||||
|
||||
if ((fs > 2000) || (fs < 15)) {
|
||||
LOG_ERR("Unsupported gyro fs range");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (fs > 1000) {
|
||||
temp = BIT_GYRO_UI_FS_2000;
|
||||
round_fs = 2000;
|
||||
} else if (fs > 500) {
|
||||
temp = BIT_GYRO_UI_FS_1000;
|
||||
round_fs = 1000;
|
||||
} else if (fs > 250) {
|
||||
temp = BIT_GYRO_UI_FS_500;
|
||||
round_fs = 500;
|
||||
} else if (fs > 125) {
|
||||
temp = BIT_GYRO_UI_FS_250;
|
||||
round_fs = 250;
|
||||
} else if (fs > 62) {
|
||||
temp = BIT_GYRO_UI_FS_125;
|
||||
round_fs = 125;
|
||||
} else if (fs > 31) {
|
||||
temp = BIT_GYRO_UI_FS_62;
|
||||
round_fs = 62;
|
||||
} else if (fs > 15) {
|
||||
temp = BIT_GYRO_UI_FS_31;
|
||||
round_fs = 31;
|
||||
} else {
|
||||
temp = BIT_GYRO_UI_FS_15;
|
||||
round_fs = 15;
|
||||
}
|
||||
|
||||
sensor_degrees_to_rad(round_fs, &gyro_fs_value);
|
||||
data->gyro_fs = gyro_fs_value.val1;
|
||||
data->gyro_sensitivity_x10 = icm40627_gyro_sensitivity_x10[temp];
|
||||
|
||||
return cfg->bus_io->update(&cfg->bus, REG_GYRO_CONFIG0, (uint8_t)MASK_GYRO_UI_FS_SEL, temp);
|
||||
}
|
||||
|
||||
static int icm40627_set_accel_odr(const struct device *dev, uint16_t rate)
|
||||
{
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
struct icm40627_data *data = dev->data;
|
||||
uint8_t temp;
|
||||
uint16_t round_rate;
|
||||
|
||||
if ((rate > 8000) || (rate < 1)) {
|
||||
LOG_ERR("Unsupported accel odr frequency");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (rate > 4000) {
|
||||
temp = BIT_ACCEL_ODR_8000;
|
||||
round_rate = 8000;
|
||||
} else if (rate > 2000) {
|
||||
temp = BIT_ACCEL_ODR_4000;
|
||||
round_rate = 4000;
|
||||
} else if (rate > 1000) {
|
||||
temp = BIT_ACCEL_ODR_2000;
|
||||
round_rate = 2000;
|
||||
} else if (rate > 500) {
|
||||
temp = BIT_ACCEL_ODR_1000;
|
||||
round_rate = 1000;
|
||||
} else if (rate > 200) {
|
||||
temp = BIT_ACCEL_ODR_500;
|
||||
round_rate = 500;
|
||||
} else if (rate > 100) {
|
||||
temp = BIT_ACCEL_ODR_200;
|
||||
round_rate = 200;
|
||||
} else if (rate > 50) {
|
||||
temp = BIT_ACCEL_ODR_100;
|
||||
round_rate = 100;
|
||||
} else if (rate > 25) {
|
||||
temp = BIT_ACCEL_ODR_50;
|
||||
round_rate = 50;
|
||||
} else if (rate > 12) {
|
||||
temp = BIT_ACCEL_ODR_25;
|
||||
round_rate = 25;
|
||||
} else if (rate > 6) {
|
||||
temp = BIT_ACCEL_ODR_12;
|
||||
round_rate = 12;
|
||||
} else if (rate > 3) {
|
||||
temp = BIT_ACCEL_ODR_6;
|
||||
round_rate = 6;
|
||||
} else if (rate > 1) {
|
||||
temp = BIT_ACCEL_ODR_3;
|
||||
round_rate = 3;
|
||||
} else {
|
||||
temp = BIT_ACCEL_ODR_1;
|
||||
round_rate = 1;
|
||||
}
|
||||
|
||||
data->accel_hz = round_rate;
|
||||
|
||||
return cfg->bus_io->update(&cfg->bus, REG_ACCEL_CONFIG0, (uint8_t)MASK_ACCEL_ODR, temp);
|
||||
}
|
||||
|
||||
static int icm40627_set_gyro_odr(const struct device *dev, uint16_t rate)
|
||||
{
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
struct icm40627_data *data = dev->data;
|
||||
uint8_t temp;
|
||||
uint16_t round_rate;
|
||||
|
||||
if ((rate > 8000) || (rate < 12)) {
|
||||
LOG_ERR("Unsupported gyro odr frequency");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (rate > 4000) {
|
||||
temp = BIT_GYRO_ODR_8000;
|
||||
round_rate = 8000;
|
||||
} else if (rate > 2000) {
|
||||
temp = BIT_GYRO_ODR_4000;
|
||||
round_rate = 4000;
|
||||
} else if (rate > 1000) {
|
||||
temp = BIT_GYRO_ODR_2000;
|
||||
round_rate = 2000;
|
||||
} else if (rate > 500) {
|
||||
temp = BIT_GYRO_ODR_1000;
|
||||
round_rate = 1000;
|
||||
} else if (rate > 200) {
|
||||
temp = BIT_GYRO_ODR_500;
|
||||
round_rate = 500;
|
||||
} else if (rate > 100) {
|
||||
temp = BIT_GYRO_ODR_200;
|
||||
round_rate = 200;
|
||||
} else if (rate > 50) {
|
||||
temp = BIT_GYRO_ODR_100;
|
||||
round_rate = 100;
|
||||
} else if (rate > 25) {
|
||||
temp = BIT_GYRO_ODR_50;
|
||||
round_rate = 50;
|
||||
} else if (rate > 12) {
|
||||
temp = BIT_GYRO_ODR_25;
|
||||
round_rate = 25;
|
||||
} else {
|
||||
temp = BIT_GYRO_ODR_12;
|
||||
round_rate = 12;
|
||||
}
|
||||
|
||||
data->gyro_hz = round_rate;
|
||||
|
||||
return cfg->bus_io->update(&cfg->bus, REG_GYRO_CONFIG0, (uint8_t)MASK_GYRO_ODR, temp);
|
||||
}
|
||||
|
||||
static int icm40627_sensor_init(const struct device *dev)
|
||||
{
|
||||
int res;
|
||||
uint8_t value;
|
||||
struct icm40627_data *data = dev->data;
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
|
||||
/* start up time for register read/write after POR is 1ms and supply ramp time is 3ms */
|
||||
k_msleep(3);
|
||||
|
||||
/* perform a soft reset to ensure a clean slate, reset bit will auto-clear */
|
||||
res = cfg->bus_io->write(&cfg->bus, REG_DEVICE_CONFIG, BIT_SOFT_RESET);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("write REG_SIGNAL_PATH_RESET failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
/* wait for soft reset to take effect */
|
||||
k_msleep(SOFT_RESET_TIME_MS);
|
||||
|
||||
/* always use internal RC oscillator */
|
||||
res = cfg->bus_io->write(&cfg->bus, REG_INTF_CONFIG1,
|
||||
(uint8_t)FIELD_PREP(MASK_CLKSEL, BIT_CLKSEL_INT_RC));
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("write REG_INTF_CONFIG1 failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
/* clear reset done int flag */
|
||||
res = cfg->bus_io->read(&cfg->bus, REG_INT_STATUS, &value, 1);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("read REG_INT_STATUS failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (FIELD_GET(BIT_STATUS_RESET_DONE_INT, value) != 1) {
|
||||
LOG_ERR("Unexpected RESET_DONE_INT value, %i", value);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
res = cfg->bus_io->read(&cfg->bus, REG_WHO_AM_I, &value, 1);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("read WHO_AM_I failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
if (value != WHO_AM_I_ICM40627) {
|
||||
LOG_ERR("Invalid WHO_AM_I value, was %i but expected %i", value, WHO_AM_I_ICM40627);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
LOG_DBG("device id: 0x%02X", value);
|
||||
|
||||
res = icm40627_set_accel_fs(dev, data->accel_fs);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("set accel fs failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
res = icm40627_set_accel_odr(dev, data->accel_hz);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("set accel odr failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
res = icm40627_set_gyro_fs(dev, data->gyro_fs);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("set gyro fs failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
res = icm40627_set_gyro_odr(dev, data->gyro_hz);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("set gyro odr failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icm40627_turn_on_sensor(const struct device *dev)
|
||||
{
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
uint8_t value;
|
||||
int res;
|
||||
|
||||
value = FIELD_PREP(MASK_ACCEL_MODE, BIT_ACCEL_MODE_LNM) |
|
||||
FIELD_PREP(MASK_GYRO_MODE, BIT_GYRO_MODE_LNM);
|
||||
|
||||
res = cfg->bus_io->update(&cfg->bus, REG_PWR_MGMT0,
|
||||
(uint8_t)(MASK_ACCEL_MODE | MASK_GYRO_MODE), value);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("write REG_PWR_MGMT0 failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Accelerometer sensor need at least 10ms startup time
|
||||
* Gyroscope sensor need at least 30ms startup time
|
||||
*/
|
||||
k_msleep(40);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void icm40627_convert_accel(struct sensor_value *val, int16_t raw_val,
|
||||
uint16_t sensitivity_shift)
|
||||
{
|
||||
/* see datasheet section 3.2 for details */
|
||||
int64_t conv_val = ((int64_t)raw_val * SENSOR_G) >> sensitivity_shift;
|
||||
|
||||
val->val1 = conv_val / 1000000LL;
|
||||
val->val2 = conv_val % 1000000LL;
|
||||
}
|
||||
|
||||
static void icm40627_convert_gyro(struct sensor_value *val, int16_t raw_val,
|
||||
uint16_t sensitivity_x10)
|
||||
{
|
||||
/* see datasheet section 3.1 for details */
|
||||
int64_t conv_val = ((int64_t)raw_val * SENSOR_PI * 10) / ((uint32_t)sensitivity_x10 * 180);
|
||||
|
||||
val->val1 = conv_val / 1000000LL;
|
||||
val->val2 = conv_val % 1000000LL;
|
||||
}
|
||||
|
||||
static inline void icm40627_convert_temp(struct sensor_value *val, int16_t raw_val)
|
||||
{
|
||||
/* see datasheet section 14.6 for details */
|
||||
val->val1 = (((int64_t)raw_val * 100) / 13248) + 25;
|
||||
val->val2 = ((((int64_t)raw_val * 100) % 13248) * 1000000) / 13248;
|
||||
|
||||
if (val->val2 < 0) {
|
||||
val->val1--;
|
||||
val->val2 += 1000000;
|
||||
} else if (val->val2 >= 1000000) {
|
||||
val->val1++;
|
||||
val->val2 -= 1000000;
|
||||
}
|
||||
}
|
||||
|
||||
static int icm40627_channel_get(const struct device *dev, enum sensor_channel chan,
|
||||
struct sensor_value *val)
|
||||
{
|
||||
int res = 0;
|
||||
const struct icm40627_data *data = dev->data;
|
||||
|
||||
icm40627_lock(dev);
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
icm40627_convert_accel(&val[0], data->accel_x, data->accel_sensitivity_shift);
|
||||
icm40627_convert_accel(&val[1], data->accel_y, data->accel_sensitivity_shift);
|
||||
icm40627_convert_accel(&val[2], data->accel_z, data->accel_sensitivity_shift);
|
||||
break;
|
||||
case SENSOR_CHAN_ACCEL_X:
|
||||
icm40627_convert_accel(val, data->accel_x, data->accel_sensitivity_shift);
|
||||
break;
|
||||
case SENSOR_CHAN_ACCEL_Y:
|
||||
icm40627_convert_accel(val, data->accel_y, data->accel_sensitivity_shift);
|
||||
break;
|
||||
case SENSOR_CHAN_ACCEL_Z:
|
||||
icm40627_convert_accel(val, data->accel_z, data->accel_sensitivity_shift);
|
||||
break;
|
||||
case SENSOR_CHAN_GYRO_XYZ:
|
||||
icm40627_convert_gyro(&val[0], data->gyro_x, data->gyro_sensitivity_x10);
|
||||
icm40627_convert_gyro(&val[1], data->gyro_y, data->gyro_sensitivity_x10);
|
||||
icm40627_convert_gyro(&val[2], data->gyro_z, data->gyro_sensitivity_x10);
|
||||
break;
|
||||
case SENSOR_CHAN_GYRO_X:
|
||||
icm40627_convert_gyro(val, data->gyro_x, data->gyro_sensitivity_x10);
|
||||
break;
|
||||
case SENSOR_CHAN_GYRO_Y:
|
||||
icm40627_convert_gyro(val, data->gyro_y, data->gyro_sensitivity_x10);
|
||||
break;
|
||||
case SENSOR_CHAN_GYRO_Z:
|
||||
icm40627_convert_gyro(val, data->gyro_z, data->gyro_sensitivity_x10);
|
||||
break;
|
||||
case SENSOR_CHAN_DIE_TEMP:
|
||||
icm40627_convert_temp(val, data->temp);
|
||||
break;
|
||||
default:
|
||||
res = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
icm40627_unlock(dev);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int icm40627_sample_fetch_accel(const struct device *dev)
|
||||
{
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
struct icm40627_data *data = dev->data;
|
||||
uint8_t buffer[ACCEL_DATA_SIZE];
|
||||
|
||||
int res = cfg->bus_io->read(&cfg->bus, REG_ACCEL_DATA_X1, buffer, ACCEL_DATA_SIZE);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("read accel data failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
data->accel_x = (int16_t)sys_get_be16(&buffer[0]);
|
||||
data->accel_y = (int16_t)sys_get_be16(&buffer[2]);
|
||||
data->accel_z = (int16_t)sys_get_be16(&buffer[4]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icm40627_sample_fetch_gyro(const struct device *dev)
|
||||
{
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
struct icm40627_data *data = dev->data;
|
||||
uint8_t buffer[GYRO_DATA_SIZE];
|
||||
|
||||
int res = cfg->bus_io->read(&cfg->bus, REG_GYRO_DATA_X1, buffer, GYRO_DATA_SIZE);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("read gyro data failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
data->gyro_x = (int16_t)sys_get_be16(&buffer[0]);
|
||||
data->gyro_y = (int16_t)sys_get_be16(&buffer[2]);
|
||||
data->gyro_z = (int16_t)sys_get_be16(&buffer[4]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icm40627_sample_fetch_temp(const struct device *dev)
|
||||
{
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
struct icm40627_data *data = dev->data;
|
||||
uint8_t buffer[TEMP_DATA_SIZE];
|
||||
|
||||
int res = cfg->bus_io->read(&cfg->bus, REG_TEMP_DATA1, buffer, TEMP_DATA_SIZE);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("read temp data failed");
|
||||
return res;
|
||||
}
|
||||
|
||||
data->temp = (int16_t)sys_get_be16(&buffer[0]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int icm40627_sample_fetch(const struct device *dev, enum sensor_channel chan)
|
||||
{
|
||||
uint8_t status;
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
|
||||
icm40627_lock(dev);
|
||||
|
||||
int res = cfg->bus_io->read(&cfg->bus, REG_INT_STATUS, &status, 1);
|
||||
|
||||
if (res) {
|
||||
LOG_ERR("read INT_STATUS failed");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!FIELD_GET(BIT_INT_STATUS_DATA_RDY_INT, status)) {
|
||||
res = -EBUSY;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_ALL:
|
||||
res = icm40627_sample_fetch_accel(dev);
|
||||
if (res) {
|
||||
break;
|
||||
}
|
||||
res = icm40627_sample_fetch_gyro(dev);
|
||||
if (res) {
|
||||
break;
|
||||
}
|
||||
res = icm40627_sample_fetch_temp(dev);
|
||||
break;
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
case SENSOR_CHAN_ACCEL_X:
|
||||
case SENSOR_CHAN_ACCEL_Y:
|
||||
case SENSOR_CHAN_ACCEL_Z:
|
||||
res = icm40627_sample_fetch_accel(dev);
|
||||
break;
|
||||
case SENSOR_CHAN_GYRO_XYZ:
|
||||
case SENSOR_CHAN_GYRO_X:
|
||||
case SENSOR_CHAN_GYRO_Y:
|
||||
case SENSOR_CHAN_GYRO_Z:
|
||||
res = icm40627_sample_fetch_gyro(dev);
|
||||
break;
|
||||
case SENSOR_CHAN_DIE_TEMP:
|
||||
res = icm40627_sample_fetch_temp(dev);
|
||||
break;
|
||||
default:
|
||||
res = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
icm40627_unlock(dev);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int icm40627_attr_set(const struct device *dev, enum sensor_channel chan,
|
||||
enum sensor_attribute attr, const struct sensor_value *val)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
__ASSERT_NO_MSG(val != NULL);
|
||||
|
||||
icm40627_lock(dev);
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_ACCEL_X:
|
||||
case SENSOR_CHAN_ACCEL_Y:
|
||||
case SENSOR_CHAN_ACCEL_Z:
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) {
|
||||
res = icm40627_set_accel_odr(dev, val->val1);
|
||||
} else if (attr == SENSOR_ATTR_FULL_SCALE) {
|
||||
res = icm40627_set_accel_fs(dev, sensor_ms2_to_g(val));
|
||||
} else {
|
||||
LOG_ERR("Unsupported accel attribute");
|
||||
res = -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GYRO_X:
|
||||
case SENSOR_CHAN_GYRO_Y:
|
||||
case SENSOR_CHAN_GYRO_Z:
|
||||
case SENSOR_CHAN_GYRO_XYZ:
|
||||
if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) {
|
||||
res = icm40627_set_gyro_odr(dev, val->val1);
|
||||
} else if (attr == SENSOR_ATTR_FULL_SCALE) {
|
||||
res = icm40627_set_gyro_fs(dev, sensor_rad_to_degrees(val));
|
||||
} else {
|
||||
LOG_ERR("Unsupported gyro attribute");
|
||||
res = -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERR("Unsupported channel");
|
||||
res = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
icm40627_unlock(dev);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int icm40627_attr_get(const struct device *dev, enum sensor_channel chan,
|
||||
enum sensor_attribute attr, struct sensor_value *val)
|
||||
{
|
||||
const struct icm40627_data *data = dev->data;
|
||||
int res = 0;
|
||||
|
||||
__ASSERT_NO_MSG(val != NULL);
|
||||
|
||||
icm40627_lock(dev);
|
||||
|
||||
val->val2 = 0;
|
||||
|
||||
switch (chan) {
|
||||
case SENSOR_CHAN_ACCEL_X:
|
||||
case SENSOR_CHAN_ACCEL_Y:
|
||||
case SENSOR_CHAN_ACCEL_Z:
|
||||
case SENSOR_CHAN_ACCEL_XYZ:
|
||||
if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) {
|
||||
val->val1 = data->accel_hz;
|
||||
} else if (attr == SENSOR_ATTR_FULL_SCALE) {
|
||||
val->val1 = data->accel_fs;
|
||||
} else {
|
||||
LOG_ERR("Unsupported accel attribute");
|
||||
res = -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
|
||||
case SENSOR_CHAN_GYRO_X:
|
||||
case SENSOR_CHAN_GYRO_Y:
|
||||
case SENSOR_CHAN_GYRO_Z:
|
||||
case SENSOR_CHAN_GYRO_XYZ:
|
||||
if (attr == SENSOR_ATTR_SAMPLING_FREQUENCY) {
|
||||
val->val1 = data->gyro_hz;
|
||||
} else if (attr == SENSOR_ATTR_FULL_SCALE) {
|
||||
val->val1 = data->gyro_fs;
|
||||
} else {
|
||||
LOG_ERR("Unsupported gyro attribute");
|
||||
res = -ENOTSUP;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
LOG_ERR("Unsupported channel");
|
||||
res = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
icm40627_unlock(dev);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline int icm40627_bus_check(const struct device *dev)
|
||||
{
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
|
||||
return cfg->bus_io->check(&cfg->bus);
|
||||
}
|
||||
|
||||
static int icm40627_init(const struct device *dev)
|
||||
{
|
||||
if (icm40627_bus_check(dev) < 0) {
|
||||
LOG_ERR("Bus is not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (icm40627_sensor_init(dev)) {
|
||||
LOG_ERR("Could not initialize sensor");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ICM40627_TRIGGER
|
||||
if (icm40627_trigger_init(dev)) {
|
||||
LOG_ERR("Failed to initialize interrupts.");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (icm40627_trigger_enable_interrupt(dev)) {
|
||||
LOG_ERR("Failed to enable interrupts");
|
||||
return -EIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
int res = icm40627_turn_on_sensor(dev);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_ICM40627_TRIGGER
|
||||
|
||||
void icm40627_lock(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
}
|
||||
|
||||
void icm40627_unlock(const struct device *dev)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static DEVICE_API(sensor, icm40627_driver_api) = {
|
||||
#ifdef CONFIG_ICM40627_TRIGGER
|
||||
.trigger_set = icm40627_trigger_set,
|
||||
#endif
|
||||
.sample_fetch = icm40627_sample_fetch,
|
||||
.channel_get = icm40627_channel_get,
|
||||
.attr_set = icm40627_attr_set,
|
||||
.attr_get = icm40627_attr_get,
|
||||
};
|
||||
|
||||
#define ICM40627_INIT(inst) \
|
||||
static struct icm40627_data icm40627_data_##inst = { \
|
||||
.accel_hz = DT_INST_PROP(inst, accel_hz), \
|
||||
.accel_fs = DT_INST_PROP(inst, accel_fs), \
|
||||
.gyro_hz = DT_INST_PROP(inst, gyro_hz), \
|
||||
.gyro_fs = DT_INST_PROP(inst, gyro_fs), \
|
||||
}; \
|
||||
\
|
||||
static const struct icm40627_config icm40627_cfg_##inst = { \
|
||||
.bus.i2c = I2C_DT_SPEC_INST_GET(inst), \
|
||||
.bus_io = &icm40627_bus_io_i2c, \
|
||||
.gpio_int = GPIO_DT_SPEC_INST_GET_OR(inst, int_gpios, {0}), \
|
||||
}; \
|
||||
\
|
||||
SENSOR_DEVICE_DT_INST_DEFINE(inst, icm40627_init, NULL, &icm40627_data_##inst, \
|
||||
&icm40627_cfg_##inst, POST_KERNEL, \
|
||||
CONFIG_SENSOR_INIT_PRIORITY, &icm40627_driver_api);
|
||||
|
||||
DT_INST_FOREACH_STATUS_OKAY(ICM40627_INIT)
|
81
drivers/sensor/tdk/icm40627/icm40627.h
Normal file
81
drivers/sensor/tdk/icm40627/icm40627.h
Normal file
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright (c) 2025 PHYTEC America LLC
|
||||
* Author: Florijan Plohl <florijan.plohl@norik.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_ICM40627_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_ICM40627_H_
|
||||
|
||||
#include <zephyr/drivers/gpio.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/kernel.h>
|
||||
|
||||
#define ICM40627_BUS_SPI DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(invensense_icm40627, spi)
|
||||
#define ICM40627_BUS_I2C DT_HAS_COMPAT_ON_BUS_STATUS_OKAY(invensense_icm40627, i2c)
|
||||
|
||||
union icm40627_bus {
|
||||
#if ICM40627_BUS_I2C
|
||||
struct i2c_dt_spec i2c;
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef int (*icm40627_bus_check_fn)(const union icm40627_bus *bus);
|
||||
typedef int (*icm40627_reg_read_fn)(const union icm40627_bus *bus, uint16_t reg, uint8_t *data,
|
||||
size_t size);
|
||||
typedef int (*icm40627_reg_write_fn)(const union icm40627_bus *bus, uint16_t reg, uint8_t data);
|
||||
|
||||
typedef int (*icm40627_reg_update_fn)(const union icm40627_bus *bus, uint16_t reg, uint8_t mask,
|
||||
uint8_t data);
|
||||
|
||||
struct icm40627_bus_io {
|
||||
icm40627_bus_check_fn check;
|
||||
icm40627_reg_read_fn read;
|
||||
icm40627_reg_write_fn write;
|
||||
icm40627_reg_update_fn update;
|
||||
};
|
||||
|
||||
#if ICM40627_BUS_I2C
|
||||
extern const struct icm40627_bus_io icm40627_bus_io_i2c;
|
||||
#endif
|
||||
|
||||
struct icm40627_data {
|
||||
int16_t accel_x;
|
||||
int16_t accel_y;
|
||||
int16_t accel_z;
|
||||
uint16_t accel_sensitivity_shift;
|
||||
uint16_t accel_hz;
|
||||
uint16_t accel_fs;
|
||||
int16_t gyro_x;
|
||||
int16_t gyro_y;
|
||||
int16_t gyro_z;
|
||||
uint16_t gyro_sensitivity_x10;
|
||||
uint16_t gyro_hz;
|
||||
uint16_t gyro_fs;
|
||||
int16_t temp;
|
||||
#ifdef CONFIG_ICM40627_TRIGGER
|
||||
const struct device *dev;
|
||||
struct gpio_callback gpio_cb;
|
||||
sensor_trigger_handler_t data_ready_handler;
|
||||
const struct sensor_trigger *data_ready_trigger;
|
||||
struct k_mutex mutex;
|
||||
#endif
|
||||
#ifdef CONFIG_ICM40627_TRIGGER_OWN_THREAD
|
||||
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_ICM40627_THREAD_STACK_SIZE);
|
||||
struct k_thread thread;
|
||||
struct k_sem gpio_sem;
|
||||
#endif
|
||||
#ifdef CONFIG_ICM40627_TRIGGER_GLOBAL_THREAD
|
||||
struct k_work work;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct icm40627_config {
|
||||
union icm40627_bus bus;
|
||||
const struct icm40627_bus_io *bus_io;
|
||||
struct gpio_dt_spec gpio_int;
|
||||
};
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_ICM40627_H_ */
|
96
drivers/sensor/tdk/icm40627/icm40627_i2c.c
Normal file
96
drivers/sensor/tdk/icm40627/icm40627_i2c.c
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2025 PHYTEC America LLC
|
||||
* Author: Florijan Plohl <florijan.plohl@norik.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/*
|
||||
* Bus-specific functionality for ICM40627 accessed via I2C.
|
||||
*/
|
||||
|
||||
#include "icm40627.h"
|
||||
#include "icm40627_reg.h"
|
||||
|
||||
#if ICM40627_BUS_I2C
|
||||
static inline int icm40627_bus_check_i2c(const union icm40627_bus *bus)
|
||||
{
|
||||
return i2c_is_ready_dt(&bus->i2c) ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
static inline int i2c_read_bank(const union icm40627_bus *bus, uint8_t reg, uint8_t bank,
|
||||
uint8_t *buf, size_t len)
|
||||
{
|
||||
int res = i2c_reg_write_byte_dt(&bus->i2c, REG_REG_BANK_SEL, bank);
|
||||
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
uint8_t addr = reg + i;
|
||||
|
||||
res = i2c_reg_read_byte_dt(&bus->i2c, addr, &buf[i]);
|
||||
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int icm40627_reg_read_i2c(const union icm40627_bus *bus, uint16_t reg, uint8_t *data,
|
||||
size_t len)
|
||||
{
|
||||
int res = 0;
|
||||
uint8_t bank = FIELD_GET(REG_BANK_MASK, reg);
|
||||
uint8_t address = FIELD_GET(REG_ADDRESS_MASK, reg);
|
||||
|
||||
res = i2c_read_bank(bus, address, bank, data, len);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline int i2c_write_bank(const union icm40627_bus *bus, uint16_t reg, uint8_t bank,
|
||||
uint8_t buf)
|
||||
{
|
||||
int res = i2c_reg_write_byte_dt(&bus->i2c, REG_REG_BANK_SEL, bank);
|
||||
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
res = i2c_reg_write_byte_dt(&bus->i2c, reg, buf);
|
||||
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int icm40627_reg_write_i2c(const union icm40627_bus *bus, uint16_t reg, uint8_t data)
|
||||
{
|
||||
int res = 0;
|
||||
uint8_t bank = FIELD_GET(REG_BANK_MASK, reg);
|
||||
uint8_t address = FIELD_GET(REG_ADDRESS_MASK, reg);
|
||||
|
||||
res = i2c_write_bank(bus, address, bank, data);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static inline int icm40627_reg_update_i2c(const union icm40627_bus *bus, uint16_t reg, uint8_t mask,
|
||||
uint8_t val)
|
||||
{
|
||||
return i2c_reg_update_byte_dt(&bus->i2c, reg, mask, val);
|
||||
}
|
||||
|
||||
const struct icm40627_bus_io icm40627_bus_io_i2c = {
|
||||
.check = icm40627_bus_check_i2c,
|
||||
.read = icm40627_reg_read_i2c,
|
||||
.write = icm40627_reg_write_i2c,
|
||||
.update = icm40627_reg_update_i2c,
|
||||
};
|
||||
#endif /* ICM40627_BUS_I2C */
|
238
drivers/sensor/tdk/icm40627/icm40627_reg.h
Normal file
238
drivers/sensor/tdk/icm40627/icm40627_reg.h
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright (c) 2025 PHYTEC America LLC
|
||||
* Author: Florijan Plohl <florijan.plohl@norik.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_ICM40627_REG_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_ICM40627_REG_H_
|
||||
|
||||
#include <zephyr/sys/util.h>
|
||||
|
||||
/* Helper macros for addressing registers in BANKs 1-4 */
|
||||
#define REG_ADDRESS_MASK GENMASK(7, 0)
|
||||
#define REG_BANK_MASK GENMASK(15, 8)
|
||||
|
||||
/* BANK 0 */
|
||||
#define REG_DEVICE_CONFIG 0x11
|
||||
#define REG_DRIVE_CONFIG 0x13
|
||||
#define REG_INT_CONFIG 0x14
|
||||
#define REG_FIFO_CONFIG 0x16
|
||||
#define REG_TEMP_DATA1 0x1D
|
||||
#define REG_ACCEL_DATA_X1 0x1F
|
||||
#define REG_GYRO_DATA_X1 0x25
|
||||
#define REG_TMST_FSYNCH 0x2B
|
||||
#define REG_INT_STATUS 0x2D
|
||||
#define REG_FIFO_COUNTH 0x2E
|
||||
#define REG_FIFO_COUNTL 0x2F
|
||||
#define REG_FIFO_DATA 0x30
|
||||
#define REG_APEX_DATA0 0x31
|
||||
#define REG_APEX_DATA1 0x32
|
||||
#define REG_APEX_DATA2 0x33
|
||||
#define REG_APEX_DATA3 0x34
|
||||
#define REG_APEX_DATA4 0x35
|
||||
#define REG_APEX_DATA5 0x36
|
||||
#define REG_INT_STATUS2 0x37
|
||||
#define REG_INT_STATUS3 0x38
|
||||
#define REG_SIGNAL_PATH_RESET 0x4B
|
||||
#define REG_INTF_CONFIG0 0x4C
|
||||
#define REG_INTF_CONFIG1 0x4D
|
||||
#define REG_PWR_MGMT0 0x4E
|
||||
#define REG_GYRO_CONFIG0 0x4F
|
||||
#define REG_ACCEL_CONFIG0 0x50
|
||||
#define REG_GYRO_CONFIG1 0x51
|
||||
#define REG_ACCEL_GYRO_CONFIG0 0x52
|
||||
#define REG_ACCEL_CONFIG1 0x53
|
||||
#define REG_TMST_CONFIG 0x54
|
||||
#define REG_APEX_CONFIG0 0x56
|
||||
#define REG_SMD_CONFIG 0x57
|
||||
#define REG_FIFO_CONFIG1 0x5F
|
||||
#define REG_FIFO_CONFIG2 0x60
|
||||
#define REG_FSYNC_CONFIG 0x62
|
||||
#define REG_INT_CONFIG0 0x63
|
||||
#define REG_INT_CONFIG1 0x64
|
||||
#define REG_INT_SOURCE0 0x65
|
||||
#define REG_INT_SOURCE1 0x66
|
||||
#define REG_INT_SOURCE2 0x67
|
||||
#define REG_INT_SOURCE3 0x68
|
||||
#define REG_INT_SOURCE4 0x69
|
||||
#define REG_INT_SOURCE5 0x6A
|
||||
#define REG_FIFO_LOST_PKT0 0x6C
|
||||
#define REG_SELF_TEST_CONFIG 0x70
|
||||
#define REG_WHO_AM_I 0x75
|
||||
#define REG_REG_BANK_SEL 0x76
|
||||
|
||||
/* BANK 1 */
|
||||
#define REG_GYRO_CONFIG_STATIC2_B1 0x0B
|
||||
#define REG_GYRO_CONFIG_STATIC3_B1 0x0C
|
||||
#define REG_GYRO_CONFIG_STATIC4_B1 0x0D
|
||||
#define REG_GYRO_CONFIG_STATIC5_B1 0x0E
|
||||
#define REG_XG_ST_DATA_B1 0x5F
|
||||
#define REG_YG_ST_DATA_B1 0x60
|
||||
#define REG_ZG_ST_DATA_B1 0x61
|
||||
#define REG_TMST_VAL0_B1 0x62
|
||||
#define REG_INTF_CONFIG4_B1 0x7A
|
||||
#define REG_INTF_CONFIG5_B1 0x7B
|
||||
#define REG_INTF_CONFIG6_B1 0x7C
|
||||
|
||||
/* BANK 2 */
|
||||
#define REG_ACCEL_CONFIG_STATIC2_B2 0x03
|
||||
#define REG_ACCEL_CONFIG_STATIC3_B2 0x04
|
||||
#define REG_ACCEL_CONFIG_STATIC4_B2 0x05
|
||||
#define REG_ACCEL_CONFIG_STATIC0_B2 0x39
|
||||
#define REG_XA_ST_DATA_B2 0x3B
|
||||
#define REG_YA_ST_DATA_B2 0x3C
|
||||
#define REG_ZA_ST_DATA_B2 0x3D
|
||||
|
||||
/* BANK 4 */
|
||||
#define REG_APEX_CONFIG1_B4 0x40
|
||||
#define REG_APEX_CONFIG2_B4 0x41
|
||||
#define REG_APEX_CONFIG3_B4 0x42
|
||||
#define REG_APEX_CONFIG4_B4 0x43
|
||||
#define REG_APEX_CONFIG5_B4 0x44
|
||||
#define REG_APEX_CONFIG6_B4 0x45
|
||||
#define REG_APEX_CONFIG7_B4 0x46
|
||||
#define REG_APEX_CONFIG8_B4 0x47
|
||||
#define REG_APEX_CONFIG9_B4 0x48
|
||||
#define REG_ACCEL_WOM_X_THR_B4 0x4A
|
||||
#define REG_ACCEL_WOM_Y_THR_B4 0x4B
|
||||
#define REG_ACCEL_WOM_Z_THR_B4 0x4C
|
||||
#define REG_INT_SOURCE6_B4 0x4D
|
||||
#define REG_INT_SOURCE7_B4 0x4E
|
||||
#define REG_INT_SOURCE8_B4 0x4F
|
||||
#define REG_INT_SOURCE9_B4 0x50
|
||||
#define REG_INT_SOURCE10_B4 0x51
|
||||
#define REG_OFFSET_USER_0_B4 0x77
|
||||
#define REG_OFFSET_USER_1_B4 0x78
|
||||
#define REG_OFFSET_USER_2_B4 0x79
|
||||
#define REG_OFFSET_USER_3_B4 0x7A
|
||||
#define REG_OFFSET_USER_4_B4 0x7B
|
||||
#define REG_OFFSET_USER_5_B4 0x7C
|
||||
#define REG_OFFSET_USER_6_B4 0x7D
|
||||
#define REG_OFFSET_USER_7_B4 0x7E
|
||||
#define REG_OFFSET_USER_8_B4 0x7F
|
||||
|
||||
/* Bank0 REG_SIGNAL_PATH_RESET */
|
||||
#define BIT_FIFO_FLUSH BIT(2)
|
||||
#define BIT_SOFT_RESET BIT(0)
|
||||
|
||||
/* Bank0 REG_INST_STATUS */
|
||||
#define BIT_STATUS_RESET_DONE_INT BIT(4)
|
||||
|
||||
/* Bank0 REG_INT_CONFIG */
|
||||
#define BIT_INT1_POLARITY BIT(0)
|
||||
#define BIT_INT1_DRIVE_CIRCUIT BIT(1)
|
||||
#define BIT_INT1_MODE BIT(2)
|
||||
#define BIT_INT2_POLARITY BIT(3)
|
||||
#define BIT_INT2_DRIVE_CIRCUIT BIT(4)
|
||||
#define BIT_INT2_MODE BIT(5)
|
||||
|
||||
/* Bank0 REG_PWR_MGMT_0 */
|
||||
#define MASK_ACCEL_MODE GENMASK(1, 0)
|
||||
#define BIT_ACCEL_MODE_OFF 0x00
|
||||
#define BIT_ACCEL_MODE_LPM 0x02
|
||||
#define BIT_ACCEL_MODE_LNM 0x03
|
||||
#define MASK_GYRO_MODE GENMASK(3, 2)
|
||||
#define BIT_GYRO_MODE_OFF 0x00
|
||||
#define BIT_GYRO_MODE_STBY 0x01
|
||||
#define BIT_GYRO_MODE_LNM 0x03
|
||||
#define BIT_IDLE BIT(4)
|
||||
#define BIT_ACCEL_LP_CLK_SEL BIT(7)
|
||||
|
||||
/* Bank0 REG_INT_SOURCE0 */
|
||||
#define BIT_INT_AGC_RDY_INT1_EN BIT(0)
|
||||
#define BIT_INT_FIFO_FULL_INT1_EN BIT(1)
|
||||
#define BIT_INT_FIFO_THS_INT1_EN BIT(2)
|
||||
#define BIT_INT_DRDY_INT1_EN BIT(3)
|
||||
#define BIT_INT_RESET_DONE_INT1_EN BIT(4)
|
||||
#define BIT_INT_PLL_RDY_INT1_EN BIT(5)
|
||||
#define BIT_INT_FSYNC_INT1_EN BIT(6)
|
||||
#define BIT_INT_ST_INT1_EN BIT(7)
|
||||
|
||||
/* Bank0 REG_INT_STATUS_DATA_RDY_INT */
|
||||
#define BIT_INT_STATUS_DATA_RDY_INT BIT(3)
|
||||
|
||||
/* Bank9 REG_INTF_CONFIG1 */
|
||||
#define BIT_ACCELL_LP_CLK_SEL BIT(3)
|
||||
#define MASK_CLKSEL GENMASK(1, 0)
|
||||
#define BIT_CLKSEL_INT_RC 0x00
|
||||
#define BIT_CLKSEL_PLL_OR_RC 0x01
|
||||
#define BIT_CLKSEL_DISABLE 0x11
|
||||
|
||||
/* Bank0 REG_INT_STATUS */
|
||||
#define BIT_INT_STATUS_AGC_RDY BIT(0)
|
||||
#define BIT_INT_STATUS_FIFO_FULL BIT(1)
|
||||
#define BIT_INT_STATUS_FIFO_THS BIT(2)
|
||||
#define BIT_INT_STATUS_RESET_DONE BIT(4)
|
||||
#define BIT_INT_STATUS_PLL_RDY BIT(5)
|
||||
#define BIT_INT_STATUS_FSYNC BIT(6)
|
||||
#define BIT_INT_STATUS_ST BIT(7)
|
||||
|
||||
/* Bank0 REG_INT_STATUS2 */
|
||||
#define BIT_INT_STATUS_WOM_Z BIT(0)
|
||||
#define BIT_INT_STATUS_WOM_Y BIT(1)
|
||||
#define BIT_INT_STATUS_WOM_X BIT(2)
|
||||
#define BIT_INT_STATUS_SMD BIT(3)
|
||||
|
||||
/* Bank0 REG_INT_STATUS3 */
|
||||
#define BIT_INT_STATUS_LOWG_DET BIT(1)
|
||||
#define BIT_INT_STATUS_FF_DET BIT(2)
|
||||
#define BIT_INT_STATUS_TILT_DET BIT(3)
|
||||
#define BIT_INT_STATUS_STEP_CNT_OVFL BIT(4)
|
||||
#define BIT_INT_STATUS_STEP_DET BIT(5)
|
||||
|
||||
/* Bank0 REG_ACCEL_CONFIG0 */
|
||||
#define MASK_ACCEL_UI_FS_SEL GENMASK(6, 5)
|
||||
#define BIT_ACCEL_UI_FS_16 0x00
|
||||
#define BIT_ACCEL_UI_FS_8 0x01
|
||||
#define BIT_ACCEL_UI_FS_4 0x02
|
||||
#define BIT_ACCEL_UI_FS_2 0x03
|
||||
#define MASK_ACCEL_ODR GENMASK(3, 0)
|
||||
#define BIT_ACCEL_ODR_8000 0x03
|
||||
#define BIT_ACCEL_ODR_4000 0x04
|
||||
#define BIT_ACCEL_ODR_2000 0x05
|
||||
#define BIT_ACCEL_ODR_1000 0x06
|
||||
#define BIT_ACCEL_ODR_200 0x07
|
||||
#define BIT_ACCEL_ODR_100 0x08
|
||||
#define BIT_ACCEL_ODR_50 0x09
|
||||
#define BIT_ACCEL_ODR_25 0x0A
|
||||
#define BIT_ACCEL_ODR_12 0x0B
|
||||
#define BIT_ACCEL_ODR_6 0x0C
|
||||
#define BIT_ACCEL_ODR_3 0x0D
|
||||
#define BIT_ACCEL_ODR_1 0x0E
|
||||
#define BIT_ACCEL_ODR_500 0x0F
|
||||
|
||||
/* Bank0 REG_GYRO_CONFIG0 */
|
||||
#define MASK_GYRO_UI_FS_SEL GENMASK(6, 5)
|
||||
#define BIT_GYRO_UI_FS_2000 0x00
|
||||
#define BIT_GYRO_UI_FS_1000 0x01
|
||||
#define BIT_GYRO_UI_FS_500 0x02
|
||||
#define BIT_GYRO_UI_FS_250 0x03
|
||||
#define BIT_GYRO_UI_FS_125 0x04
|
||||
#define BIT_GYRO_UI_FS_62 0x05
|
||||
#define BIT_GYRO_UI_FS_31 0x06
|
||||
#define BIT_GYRO_UI_FS_15 0x07
|
||||
#define MASK_GYRO_ODR GENMASK(3, 0)
|
||||
#define BIT_GYRO_ODR_8000 0x03
|
||||
#define BIT_GYRO_ODR_4000 0x04
|
||||
#define BIT_GYRO_ODR_2000 0x05
|
||||
#define BIT_GYRO_ODR_1000 0x06
|
||||
#define BIT_GYRO_ODR_200 0x07
|
||||
#define BIT_GYRO_ODR_100 0x08
|
||||
#define BIT_GYRO_ODR_50 0x09
|
||||
#define BIT_GYRO_ODR_25 0x0A
|
||||
#define BIT_GYRO_ODR_12 0x0B
|
||||
#define BIT_GYRO_ODR_500 0x0F
|
||||
|
||||
/* misc. defines */
|
||||
#define WHO_AM_I_ICM40627 0x4E
|
||||
#define MIN_ACCEL_SENS_SHIFT 10
|
||||
#define ACCEL_DATA_SIZE 6
|
||||
#define GYRO_DATA_SIZE 6
|
||||
#define TEMP_DATA_SIZE 2
|
||||
#define MCLK_POLL_INTERVAL_US 250
|
||||
#define MCLK_POLL_ATTEMPTS 100
|
||||
#define SOFT_RESET_TIME_MS 2
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_ICM40627_REG_H_ */
|
174
drivers/sensor/tdk/icm40627/icm40627_trigger.c
Normal file
174
drivers/sensor/tdk/icm40627/icm40627_trigger.c
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
* Copyright (c) 2025 PHYTEC America LLC
|
||||
* Author: Florijan Plohl <florijan.plohl@norik.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/sensor.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include "icm40627.h"
|
||||
#include "icm40627_reg.h"
|
||||
#include "icm40627_trigger.h"
|
||||
|
||||
#include <zephyr/logging/log.h>
|
||||
LOG_MODULE_DECLARE(ICM40627, CONFIG_SENSOR_LOG_LEVEL);
|
||||
|
||||
static void icm40627_gpio_callback(const struct device *dev, struct gpio_callback *cb,
|
||||
uint32_t pins)
|
||||
{
|
||||
ARG_UNUSED(dev);
|
||||
ARG_UNUSED(pins);
|
||||
|
||||
struct icm40627_data *data = CONTAINER_OF(cb, struct icm40627_data, gpio_cb);
|
||||
|
||||
#if defined(CONFIG_ICM40627_TRIGGER_OWN_THREAD)
|
||||
k_sem_give(&data->gpio_sem);
|
||||
#elif defined(CONFIG_ICM40627_TRIGGER_GLOBAL_THREAD)
|
||||
k_work_submit(&data->work);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void icm40627_thread_cb(const struct device *dev)
|
||||
{
|
||||
struct icm40627_data *data = dev->data;
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
|
||||
icm40627_lock(dev);
|
||||
gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE);
|
||||
|
||||
if (data->data_ready_handler) {
|
||||
data->data_ready_handler(dev, data->data_ready_trigger);
|
||||
}
|
||||
|
||||
gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
icm40627_unlock(dev);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_ICM40627_TRIGGER_OWN_THREAD)
|
||||
|
||||
static void icm40627_thread(void *p1, void *p2, void *p3)
|
||||
{
|
||||
ARG_UNUSED(p2);
|
||||
ARG_UNUSED(p3);
|
||||
|
||||
struct icm40627_data *data = p1;
|
||||
|
||||
while (1) {
|
||||
k_sem_take(&data->gpio_sem, K_FOREVER);
|
||||
icm40627_thread_cb(data->dev);
|
||||
}
|
||||
}
|
||||
|
||||
#elif defined(CONFIG_ICM40627_TRIGGER_GLOBAL_THREAD)
|
||||
|
||||
static void icm40627_work_handler(struct k_work *work)
|
||||
{
|
||||
struct icm40627_data *data = CONTAINER_OF(work, struct icm40627_data, work);
|
||||
|
||||
icm40627_thread_cb(data->dev);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
int icm40627_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler)
|
||||
{
|
||||
int res = 0;
|
||||
struct icm40627_data *data = dev->data;
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
|
||||
if (!handler) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
icm40627_lock(dev);
|
||||
gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_DISABLE);
|
||||
|
||||
switch (trig->type) {
|
||||
case SENSOR_TRIG_DATA_READY:
|
||||
data->data_ready_handler = handler;
|
||||
data->data_ready_trigger = trig;
|
||||
break;
|
||||
default:
|
||||
res = -ENOTSUP;
|
||||
break;
|
||||
}
|
||||
|
||||
gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
icm40627_unlock(dev);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
int icm40627_trigger_init(const struct device *dev)
|
||||
{
|
||||
struct icm40627_data *data = dev->data;
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
int res = 0;
|
||||
|
||||
if (!cfg->gpio_int.port) {
|
||||
LOG_ERR("trigger enabled but no interrupt gpio supplied");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!gpio_is_ready_dt(&cfg->gpio_int)) {
|
||||
LOG_ERR("gpio_int gpio not ready");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
data->dev = dev;
|
||||
gpio_pin_configure_dt(&cfg->gpio_int, GPIO_INPUT);
|
||||
gpio_init_callback(&data->gpio_cb, icm40627_gpio_callback, BIT(cfg->gpio_int.pin));
|
||||
res = gpio_add_callback(cfg->gpio_int.port, &data->gpio_cb);
|
||||
|
||||
if (res < 0) {
|
||||
LOG_ERR("Failed to set gpio callback");
|
||||
return res;
|
||||
}
|
||||
|
||||
k_mutex_init(&data->mutex);
|
||||
|
||||
#if defined(CONFIG_ICM40627_TRIGGER_OWN_THREAD)
|
||||
k_sem_init(&data->gpio_sem, 0, K_SEM_MAX_LIMIT);
|
||||
k_thread_create(&data->thread, data->thread_stack, CONFIG_ICM40627_THREAD_STACK_SIZE,
|
||||
icm40627_thread, data, NULL, NULL,
|
||||
K_PRIO_COOP(CONFIG_ICM40627_THREAD_PRIORITY), 0, K_NO_WAIT);
|
||||
#elif defined(CONFIG_ICM40627_TRIGGER_GLOBAL_THREAD)
|
||||
data->work.handler = icm40627_work_handler;
|
||||
#endif
|
||||
|
||||
return gpio_pin_interrupt_configure_dt(&cfg->gpio_int, GPIO_INT_EDGE_TO_ACTIVE);
|
||||
}
|
||||
|
||||
int icm40627_trigger_enable_interrupt(const struct device *dev)
|
||||
{
|
||||
int res;
|
||||
const struct icm40627_config *cfg = dev->config;
|
||||
|
||||
/* pulse-mode (auto clearing), push-pull and active-high */
|
||||
res = cfg->bus_io->write(&cfg->bus, REG_INT_CONFIG,
|
||||
BIT_INT1_DRIVE_CIRCUIT | BIT_INT1_POLARITY);
|
||||
|
||||
if (res) {
|
||||
return res;
|
||||
}
|
||||
|
||||
/* enable data ready interrupt on INT1 pin */
|
||||
return cfg->bus_io->write(&cfg->bus, REG_INT_SOURCE0, BIT_INT_DRDY_INT1_EN);
|
||||
}
|
||||
|
||||
void icm40627_lock(const struct device *dev)
|
||||
{
|
||||
struct icm40627_data *data = dev->data;
|
||||
|
||||
k_mutex_lock(&data->mutex, K_FOREVER);
|
||||
}
|
||||
|
||||
void icm40627_unlock(const struct device *dev)
|
||||
{
|
||||
struct icm40627_data *data = dev->data;
|
||||
|
||||
k_mutex_unlock(&data->mutex);
|
||||
}
|
47
drivers/sensor/tdk/icm40627/icm40627_trigger.h
Normal file
47
drivers/sensor/tdk/icm40627/icm40627_trigger.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2025 PHYTEC America LLC
|
||||
* Author: Florijan Plohl <florijan.plohl@norik.com>
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef ZEPHYR_DRIVERS_SENSOR_ICM40627_TRIGGER_H_
|
||||
#define ZEPHYR_DRIVERS_SENSOR_ICM40627_TRIGGER_H_
|
||||
|
||||
#include <zephyr/device.h>
|
||||
|
||||
/** implement the trigger_set sensor api function */
|
||||
int icm40627_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
|
||||
sensor_trigger_handler_t handler);
|
||||
|
||||
/**
|
||||
* @brief initialize the icm40627 trigger system
|
||||
*
|
||||
* @param dev icm40627 device pointer
|
||||
* @return int 0 on success, negative error code otherwise
|
||||
*/
|
||||
int icm40627_trigger_init(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief enable the trigger gpio interrupt
|
||||
*
|
||||
* @param dev icm40627 device pointer
|
||||
* @return int 0 on success, negative error code otherwise
|
||||
*/
|
||||
int icm40627_trigger_enable_interrupt(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief lock access to the icm40627 device driver
|
||||
*
|
||||
* @param dev icm40627 device pointer
|
||||
*/
|
||||
void icm40627_lock(const struct device *dev);
|
||||
|
||||
/**
|
||||
* @brief lock access to the icm40627 device driver
|
||||
*
|
||||
* @param dev icm40627 device pointer
|
||||
*/
|
||||
void icm40627_unlock(const struct device *dev);
|
||||
|
||||
#endif /* ZEPHYR_DRIVERS_SENSOR_ICM40627_TRIGGER_H_ */
|
8
dts/bindings/sensor/invensense,icm40627-i2c.yaml
Normal file
8
dts/bindings/sensor/invensense,icm40627-i2c.yaml
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Copyright (c) 2025 PHYTEC America LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: ICM-40627 motion tracking device
|
||||
|
||||
compatible: "invensense,icm40627"
|
||||
|
||||
include: [i2c-device.yaml, "invensense,icm40627.yaml"]
|
85
dts/bindings/sensor/invensense,icm40627.yaml
Normal file
85
dts/bindings/sensor/invensense,icm40627.yaml
Normal file
|
@ -0,0 +1,85 @@
|
|||
# Copyright (c) 2025 PHYTEC America LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: ICM-40627 motion tracking device
|
||||
|
||||
include: [sensor-device.yaml]
|
||||
|
||||
properties:
|
||||
int-gpios:
|
||||
type: phandle-array
|
||||
description: |
|
||||
The INT signal default configuration is active-high. The
|
||||
property value should ensure the flags properly describe the
|
||||
signal that is presented to the driver.
|
||||
|
||||
accel-hz:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Default frequency of accelerometer. (Unit - Hz)
|
||||
Maps to ACCEL_ODR field in ACCEL_CONFIG0 setting
|
||||
Power-on reset value is 1000.
|
||||
enum:
|
||||
- 1
|
||||
- 3
|
||||
- 6
|
||||
- 12
|
||||
- 25
|
||||
- 50
|
||||
- 100
|
||||
- 200
|
||||
- 500
|
||||
- 1000
|
||||
- 2000
|
||||
- 4000
|
||||
- 8000
|
||||
|
||||
gyro-hz:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Default frequency of gyroscope. (Unit - Hz)
|
||||
Maps to GYRO_ODR field in GYRO_CONFIG0 setting
|
||||
Power-on reset value is 1000.
|
||||
enum:
|
||||
- 12
|
||||
- 25
|
||||
- 50
|
||||
- 100
|
||||
- 200
|
||||
- 500
|
||||
- 1000
|
||||
- 2000
|
||||
- 4000
|
||||
- 8000
|
||||
|
||||
accel-fs:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Default full scale of accelerometer. (Unit - g)
|
||||
Maps to ACCEL_FS_SEL field in ACCEL_CONFIG0 setting
|
||||
Power-on reset value is 16
|
||||
enum:
|
||||
- 16
|
||||
- 8
|
||||
- 4
|
||||
- 2
|
||||
|
||||
gyro-fs:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Default full scale of gyroscope. (Unit - DPS)
|
||||
Maps to GYRO_FS_SEL field in GYRO_CONFIG0 setting
|
||||
Power-on reset value is 2000
|
||||
enum:
|
||||
- 2000
|
||||
- 1000
|
||||
- 500
|
||||
- 250
|
||||
- 125
|
||||
- 62
|
||||
- 31
|
||||
- 15
|
Loading…
Add table
Add a link
Reference in a new issue