| /* ST Microelectronics I3G4250D gyro driver |
| * |
| * Copyright (c) 2021 Jonathan Hahn |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| * |
| * Datasheet: |
| * https://www.st.com/resource/en/datasheet/i3g4250d.pdf |
| */ |
| |
| #define DT_DRV_COMPAT st_i3g4250d |
| |
| #include <init.h> |
| #include <sys/__assert.h> |
| #include <sys/byteorder.h> |
| #include <drivers/sensor.h> |
| #include <logging/log.h> |
| |
| #include "i3g4250d.h" |
| |
| #define RAW_TO_MICRODEGREEPERSEC 8750 |
| |
| LOG_MODULE_REGISTER(i3g4250d, CONFIG_SENSOR_LOG_LEVEL); |
| |
| static int i3g4250d_sample_fetch(const struct device *dev, |
| enum sensor_channel chan) |
| { |
| struct i3g4250d_data *i3g4250d = dev->data; |
| int ret; |
| uint8_t reg; |
| int16_t buf[3] = { 0 }; |
| |
| if ((chan != SENSOR_CHAN_ALL) && (chan != SENSOR_CHAN_GYRO_XYZ)) { |
| return -ENOTSUP; |
| } |
| |
| ret = i3g4250d_flag_data_ready_get(i3g4250d->ctx, ®); |
| if (ret < 0 || reg != 1) { |
| return ret; |
| } |
| |
| ret = i3g4250d_angular_rate_raw_get(i3g4250d->ctx, buf); |
| if (ret < 0) { |
| LOG_ERR("Failed to fetch raw data sample!"); |
| return ret; |
| } |
| |
| memcpy(i3g4250d->angular_rate, buf, sizeof(i3g4250d->angular_rate)); |
| |
| return 0; |
| } |
| |
| static inline void i3g4250d_convert(struct sensor_value *val, int16_t raw_value) |
| { |
| val->val1 = (int16_t)(raw_value * RAW_TO_MICRODEGREEPERSEC / 1000000LL); |
| val->val2 = (int16_t)(raw_value * RAW_TO_MICRODEGREEPERSEC) % 1000000LL; |
| } |
| |
| static void i3g4250d_channel_convert(enum sensor_channel chan, |
| uint16_t *raw_xyz, |
| struct sensor_value *val) |
| { |
| uint8_t ofs_start, ofs_stop; |
| |
| switch (chan) { |
| case SENSOR_CHAN_GYRO_X: |
| ofs_start = ofs_stop = 0U; |
| break; |
| case SENSOR_CHAN_GYRO_Y: |
| ofs_start = ofs_stop = 1U; |
| break; |
| case SENSOR_CHAN_GYRO_Z: |
| ofs_start = ofs_stop = 2U; |
| break; |
| default: |
| ofs_start = 0U; |
| ofs_stop = 2U; |
| break; |
| } |
| |
| for (int i = ofs_start; i <= ofs_stop; i++) { |
| i3g4250d_convert(val++, raw_xyz[i]); |
| } |
| } |
| |
| static int i3g4250d_channel_get(const struct device *dev, |
| enum sensor_channel chan, |
| struct sensor_value *val) |
| { |
| struct i3g4250d_data *i3g4250d = dev->data; |
| |
| switch (chan) { |
| case SENSOR_CHAN_GYRO_X: |
| __fallthrough; |
| case SENSOR_CHAN_GYRO_Y: |
| __fallthrough; |
| case SENSOR_CHAN_GYRO_Z: |
| __fallthrough; |
| case SENSOR_CHAN_GYRO_XYZ: |
| i3g4250d_channel_convert(chan, i3g4250d->angular_rate, val); |
| return 0; |
| default: |
| return -ENOTSUP; |
| } |
| } |
| |
| static i3g4250d_dr_t gyr_odr_to_reg(const struct sensor_value *val) |
| { |
| double odr = sensor_value_to_double(val); |
| i3g4250d_dr_t reg = I3G4250D_ODR_OFF; |
| |
| if ((odr > 0.0) && (odr < 100.0)) { |
| reg = I3G4250D_ODR_SLEEP; |
| } else if ((odr >= 100.0) && (odr < 200.0)) { |
| reg = I3G4250D_ODR_100Hz; |
| } else if ((odr >= 200.0) && (odr < 400.0)) { |
| reg = I3G4250D_ODR_200Hz; |
| } else if ((odr >= 400.0) && (odr < 800.0)) { |
| reg = I3G4250D_ODR_400Hz; |
| } else if (odr >= 800.0) { |
| reg = I3G4250D_ODR_800Hz; |
| } |
| |
| return reg; |
| } |
| |
| static int i3g4250d_config_gyro(const struct device *dev, |
| enum sensor_attribute attr, |
| const struct sensor_value *val) |
| { |
| struct i3g4250d_data *i3g4250d = dev->data; |
| i3g4250d_dr_t dr_reg; |
| |
| switch (attr) { |
| case SENSOR_ATTR_SAMPLING_FREQUENCY: |
| dr_reg = gyr_odr_to_reg(val); |
| return i3g4250d_data_rate_set(i3g4250d->ctx, dr_reg); |
| default: |
| LOG_ERR("Gyro attribute not supported"); |
| break; |
| } |
| |
| return -ENOTSUP; |
| } |
| |
| static int i3g4250d_attr_set(const struct device *dev, enum sensor_channel chan, |
| enum sensor_attribute attr, |
| const struct sensor_value *val) |
| { |
| switch (chan) { |
| case SENSOR_CHAN_GYRO_XYZ: |
| return i3g4250d_config_gyro(dev, attr, val); |
| default: |
| LOG_ERR("attr_set() not supported on this channel %d.", chan); |
| break; |
| } |
| |
| return -ENOTSUP; |
| } |
| |
| static const struct sensor_driver_api i3g4250d_driver_api = { |
| .attr_set = i3g4250d_attr_set, |
| .sample_fetch = i3g4250d_sample_fetch, |
| .channel_get = i3g4250d_channel_get, |
| }; |
| |
| static int i3g4250d_init(const struct device *dev) |
| { |
| struct i3g4250d_data *i3g4250d = dev->data; |
| uint8_t wai; |
| int ret = 0; |
| |
| ret = i3g4250d_spi_init(dev); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| ret = i3g4250d_device_id_get(i3g4250d->ctx, &wai); |
| if (ret != 0) { |
| return ret; |
| } |
| |
| if (wai != I3G4250D_ID) { |
| LOG_ERR("Invalid chip ID: %02x", wai); |
| return -EIO; |
| } |
| |
| /* Configure filtering chain - Gyroscope - High Pass */ |
| ret = i3g4250d_filter_path_set(i3g4250d->ctx, I3G4250D_LPF1_HP_ON_OUT); |
| if (ret != 0) { |
| LOG_ERR("Failed setting filter path"); |
| return ret; |
| } |
| |
| ret = i3g4250d_hp_bandwidth_set(i3g4250d->ctx, I3G4250D_HP_LEVEL_3); |
| if (ret != 0) { |
| LOG_ERR("Failed setting high pass"); |
| return ret; |
| } |
| |
| /* Set Output data rate */ |
| ret = i3g4250d_data_rate_set(i3g4250d->ctx, I3G4250D_ODR_100Hz); |
| if (ret != 0) { |
| LOG_ERR("Failed setting data rate"); |
| return ret; |
| } |
| |
| return 0; |
| } |
| |
| #define I3G4250D_DEVICE_INIT(inst) \ |
| static struct i3g4250d_data i3g4250d_data_##inst; \ |
| static const struct i3g4250d_device_config i3g4250d_config_##inst = { \ |
| .spi = SPI_DT_SPEC_INST_GET(inst, \ |
| SPI_OP_MODE_MASTER | SPI_MODE_CPOL | \ |
| SPI_MODE_CPHA | SPI_WORD_SET(8) | SPI_LINES_SINGLE, \ |
| 0), \ |
| }; \ |
| DEVICE_DT_INST_DEFINE(inst, \ |
| i3g4250d_init, \ |
| NULL, \ |
| &i3g4250d_data_##inst, \ |
| &i3g4250d_config_##inst, \ |
| POST_KERNEL, \ |
| CONFIG_SENSOR_INIT_PRIORITY, \ |
| &i3g4250d_driver_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(I3G4250D_DEVICE_INIT) |