/** * @file sensor.h * * @brief Public APIs for the sensor driver. */ /* * Copyright (c) 2016 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef __SENSOR_H__ #define __SENSOR_H__ /** * @brief Sensor Interface * @defgroup sensor_interface Sensor Interface * @ingroup io_interfaces * @{ */ #ifdef __cplusplus extern "C" { #endif #include #include #include /** @brief Sensor value types. */ enum sensor_value_type { /** val1 contains an integer value, val2 is unused. */ SENSOR_VALUE_TYPE_INT, /** * val1 contains an integer value, val2 is the fractional value. * To obtain the final value, use the formula: val1 + val2 * * 10^(-6). */ SENSOR_VALUE_TYPE_INT_PLUS_MICRO, /** * @brief val1 contains a Q16.16 representation, val2 is * unused. */ SENSOR_VALUE_TYPE_Q16_16, /** @brief dval contains a floating point value. */ SENSOR_VALUE_TYPE_DOUBLE, }; /** * @brief Representation of a sensor readout value. * * The meaning of the fields is dictated by the type field. */ struct sensor_value { enum sensor_value_type type; union { struct { int32_t val1; int32_t val2; }; double dval; }; }; /** * @brief Sensor channels. */ enum sensor_channel { /** Acceleration on the X axis, in m/s^2. */ SENSOR_CHAN_ACCEL_X, /** Acceleration on the Y axis, in m/s^2. */ SENSOR_CHAN_ACCEL_Y, /** Acceleration on the Z axis, in m/s^2. */ SENSOR_CHAN_ACCEL_Z, /** Acceleration on any axis. */ SENSOR_CHAN_ACCEL_ANY, /** Angular velocity around the X axis, in radians/s. */ SENSOR_CHAN_GYRO_X, /** Angular velocity around the Y axis, in radians/s. */ SENSOR_CHAN_GYRO_Y, /** Angular velocity around the Z axis, in radians/s. */ SENSOR_CHAN_GYRO_Z, /** Angular velocity on any axis. */ SENSOR_CHAN_GYRO_ANY, /** Magnetic field on the X axis, in Gauss. */ SENSOR_CHAN_MAGN_X, /** Magnetic field on the Y axis, in Gauss. */ SENSOR_CHAN_MAGN_Y, /** Magnetic field on the Z axis, in Gauss. */ SENSOR_CHAN_MAGN_Z, /** Magnetic field on any axis. */ SENSOR_CHAN_MAGN_ANY, /** Temperature in degrees Celsius. */ SENSOR_CHAN_TEMP, /** Pressure in kilopascal. */ SENSOR_CHAN_PRESS, /** * Proximity. Adimensional. A value of 1 indicates that an * object is close. */ SENSOR_CHAN_PROX, /** Humidity, in milli percent. */ SENSOR_CHAN_HUMIDITY, /** Illuminance in visible spectrum, in lux. */ SENSOR_CHAN_LIGHT, /** Illuminance in infra-red spectrum, in lux. */ SENSOR_CHAN_IR, /** Altitude, in meters */ SENSOR_CHAN_ALTITUDE, /** All channels. */ SENSOR_CHAN_ALL, }; /** * @brief Sensor trigger types. */ enum sensor_trigger_type { /** * Timer-based trigger, useful when the sensor does not have an * interrupt line. */ SENSOR_TRIG_TIMER, /** Trigger fires whenever new data is ready. */ SENSOR_TRIG_DATA_READY, /** * Trigger fires when the selected channel varies significantly. * This includes any-motion detection when the channel is * acceleration or gyro. If detection is based on slope between * successive channel readings, the slope threshold is configured * via the @ref SENSOR_ATTR_SLOPE_TH and @ref SENSOR_ATTR_SLOPE_DUR * attributes. */ SENSOR_TRIG_DELTA, /** Trigger fires when a near/far event is detected. */ SENSOR_TRIG_NEAR_FAR, /** * Trigger fires when channel reading transitions configured * thresholds. The thresholds are configured via the @ref * SENSOR_ATTR_LOWER_THRESH and @ref SENSOR_ATTR_UPPER_THRESH * attributes. */ SENSOR_TRIG_THRESHOLD, }; /** * @brief Sensor trigger spec. */ struct sensor_trigger { /** Trigger type. */ enum sensor_trigger_type type; /** Channel the trigger is set on. */ enum sensor_channel chan; }; /** * @brief Sensor attribute types. */ enum sensor_attribute { /** * Sensor sampling frequency, i.e. how many times a second the * sensor takes a measurement. */ SENSOR_ATTR_SAMPLING_FREQUENCY, /** Lower threshold for trigger. */ SENSOR_ATTR_LOWER_THRESH, /** Upper threshold for trigger. */ SENSOR_ATTR_UPPER_THRESH, /** Threshold for any-motion (slope) trigger. */ SENSOR_ATTR_SLOPE_TH, /** * Duration for which the slope values needs to be * outside the threshold for the trigger to fire. */ SENSOR_ATTR_SLOPE_DUR, /** Oversampling factor */ SENSOR_ATTR_OVERSAMPLING, /** Sensor range, in SI units. */ SENSOR_ATTR_FULL_SCALE, /** * The sensor value returned will be altered by the amount indicated by * offset: final_value = sensor_value + offset. */ SENSOR_ATTR_OFFSET, /** * Calibration target. This will be used by the internal chip's * algorithms to calibrate itself on a certain axis, or all of them. */ SENSOR_ATTR_CALIB_TARGET, }; /** * @typedef sensor_trigger_handler_t * @brief Callback API upon firing of a trigger * * @param "struct device *dev" Pointer to the sensor device * @param "struct sensor_trigger *trigger" The trigger */ typedef void (*sensor_trigger_handler_t)(struct device *dev, struct sensor_trigger *trigger); /** * @typedef sensor_attr_set_t * @brief Callback API upon setting a sensor's attributes * * See sensor_attr_set() for argument description */ typedef int (*sensor_attr_set_t)(struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val); /** * @typedef sensor_trigger_set_t * @brief Callback API for setting a sensor's trigger and handler * * See sensor_trigger_set() for argument description */ typedef int (*sensor_trigger_set_t)(struct device *dev, const struct sensor_trigger *trig, sensor_trigger_handler_t handler); /** * @typedef sensor_sample_fetch_t * @brief Callback API for fetching data from a sensor * * See sensor_sample_fetch() for argument descriptor */ typedef int (*sensor_sample_fetch_t)(struct device *dev, enum sensor_channel chan); /** * @typedef sensor_channel_get_t * @brief Callback API for getting a reading from a sensor * * See sensor_channel_get() for argument descriptor */ typedef int (*sensor_channel_get_t)(struct device *dev, enum sensor_channel chan, struct sensor_value *val); struct sensor_driver_api { sensor_attr_set_t attr_set; sensor_trigger_set_t trigger_set; sensor_sample_fetch_t sample_fetch; sensor_channel_get_t channel_get; }; /** * @brief Set an attribute for a sensor * * @param dev Pointer to the sensor device * @param chan The channel the attribute belongs to, if any. Some * attributes may only be set for all channels of a device, depending on * device capabilities. * @param attr The attribute to set * @param val The value to set the attribute to * * @return 0 if successful, negative errno code if failure. */ static inline int sensor_attr_set(struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { const struct sensor_driver_api *api = dev->driver_api; if (!api->attr_set) { return -ENOTSUP; } return api->attr_set(dev, chan, attr, val); } /** * @brief Activate a sensor's trigger and set the trigger handler * * The handler will be called from a fiber, so I2C or SPI operations are * safe. However, the fiber's stack is limited and defined by the * driver. It is currently up to the caller to ensure that the handler * does not overflow the stack. * * @param dev Pointer to the sensor device * @param trig The trigger to activate * @param handler The function that should be called when the trigger * fires * * @return 0 if successful, negative errno code if failure. */ static inline int sensor_trigger_set(struct device *dev, struct sensor_trigger *trig, sensor_trigger_handler_t handler) { const struct sensor_driver_api *api = dev->driver_api; if (!api->trigger_set) { return -ENOTSUP; } return api->trigger_set(dev, trig, handler); } /** * @brief Fetch a sample from the sensor and store it in an internal * driver buffer * * Read all of a sensor's active channels and, if necessary, perform any * additional operations necessary to make the values useful. The user * may then get individual channel values by calling @ref * sensor_channel_get. * * Since the function communicates with the sensor device, it is unsafe * to call it in an ISR if the device is connected via I2C or SPI. * * @param dev Pointer to the sensor device * * @return 0 if successful, negative errno code if failure. */ static inline int sensor_sample_fetch(struct device *dev) { const struct sensor_driver_api *api = dev->driver_api; return api->sample_fetch(dev, SENSOR_CHAN_ALL); } /** * @brief Fetch a sample from the sensor and store it in an internal * driver buffer * * Read and compute compensation for one type of sensor data (magnetometer, * accelerometer, etc). The user may then get individual channel values by * calling @ref sensor_channel_get. * * This is mostly implemented by multi function devices enabling reading at * different sampling rates. * * Since the function communicates with the sensor device, it is unsafe * to call it in an ISR if the device is connected via I2C or SPI. * * @param dev Pointer to the sensor device * @param type The channel that needs updated * * @return 0 if successful, negative errno code if failure. */ static inline int sensor_sample_fetch_chan(struct device *dev, enum sensor_channel type) { const struct sensor_driver_api *api = dev->driver_api; return api->sample_fetch(dev, type); } /** * @brief Get a reading from a sensor device * * Return a useful value for a particular channel, from the driver's * internal data. Before calling this function, a sample must be * obtained by calling @ref sensor_sample_fetch or * @ref sensor_sample_fetch_chan. It is guaranteed that two subsequent * calls of this function for the same channels will yield the same * value, if @ref sensor_sample_fetch or @ref sensor_sample_fetch_chan * has not been called in the meantime. * * For vectorial data samples you can request all axes in just one call * by passing the specific channel with _ANY suffix. The sample will be * returned at val[0], val[1] and val[2] (X, Y and Z in that order). * * @param dev Pointer to the sensor device * @param chan The channel to read * @param val Where to store the value * * @return 0 if successful, negative errno code if failure. */ static inline int sensor_channel_get(struct device *dev, enum sensor_channel chan, struct sensor_value *val) { const struct sensor_driver_api *api = dev->driver_api; return api->channel_get(dev, chan, val); } /** * @brief The value of gravitational constant in micro m/s^2. */ #define SENSOR_G 9806650LL /** * @brief The value of constant PI in micros. */ #define SENSOR_PI 3141592LL /** * @brief Helper function to convert acceleration from m/s^2 to Gs * * @param ms2 A pointer to a sensor_value struct holding the acceleration, * in m/s^2. * * @return The converted value, in Gs. */ static inline int32_t sensor_ms2_to_g(const struct sensor_value *ms2) { int64_t micro_ms2 = ms2->val1 * 1000000LL + ms2->val2; if (micro_ms2 > 0) { return (micro_ms2 + SENSOR_G / 2) / SENSOR_G; } else { return (micro_ms2 - SENSOR_G / 2) / SENSOR_G; } } /** * @brief Helper function to convert acceleration from Gs to m/s^2 * * @param g The G value to be converted. * @param ms2 A pointer to a sensor_value struct, where the result is stored. */ static inline void sensor_g_to_ms2(int32_t g, struct sensor_value *ms2) { ms2->type = SENSOR_VALUE_TYPE_INT_PLUS_MICRO; ms2->val1 = ((int64_t)g * SENSOR_G) / 1000000LL; ms2->val2 = ((int64_t)g * SENSOR_G) % 1000000LL; } /** * @brief Helper function for converting radians to degrees. * * @param rad A pointer to a sensor_value struct, holding the value in radians. * * @return The converted value, in degrees. */ static inline int32_t sensor_rad_to_degrees(const struct sensor_value *rad) { int64_t micro_rad_s = rad->val1 * 1000000LL + rad->val2; if (micro_rad_s > 0) { return (micro_rad_s * 180LL + SENSOR_PI / 2) / SENSOR_PI; } else { return (micro_rad_s * 180LL - SENSOR_PI / 2) / SENSOR_PI; } } /** * @brief Helper function for converting degrees to radians. * * @param d The value (in degrees) to be converted. * @param rad A pointer to a sensor_value struct, where the result is stored. */ static inline void sensor_degrees_to_rad(int32_t d, struct sensor_value *rad) { rad->type = SENSOR_VALUE_TYPE_INT_PLUS_MICRO; rad->val1 = ((int64_t)d * SENSOR_PI / 180LL) / 1000000LL; rad->val2 = ((int64_t)d * SENSOR_PI / 180LL) % 1000000LL; } /** * @brief configuration parameters for sensor triggers. */ enum sensor_trigger_mode { /** Do not use triggering. */ SENSOR_TRIG_MODE_NONE, /** * Driver should start a workqueue specifically for this * device. See @ref sensor_trig_or_wq_config for instruction on * how to specify the parameters of the workqueue. */ SENSOR_TRIG_MODE_OWN_WQ, /** Use the system workqueue. */ SENSOR_TRIG_MODE_GLOBAL_WQ, }; /** * @brief configuration parameters for sensor triggers. */ struct sensor_trigger_config { /** * This is always set to NULL when using a @ref * sensor_trigger_config. See the comment in @ref * sensor_trig_or_wq_config. */ void *always_null; enum sensor_trigger_mode mode; }; /** * @brief Structure used for sensor trigger configuration. * * If fiber_config.stack is non-NULL, the driver should start its * own fiber based on @ref fiber_config. Otherwise, use * sensor_interface::sensor_trigger_mode to decide if and how to use * triggering. */ union sensor_trig_or_wq_config { struct fiber_config fiber_config; struct sensor_trigger_config trig_config; }; #define SENSOR_DECLARE_TRIG_CONFIG \ union sensor_trig_or_wq_config trig_or_wq_config #define SENSOR_TRIG_WQ_OWN(_stack, _prio) \ .trig_or_wq_config = { \ .fiber_config = { \ .stack = (_stack), \ .stack_size = sizeof(_stack), \ .prio = (_prio), \ } \ } #define SENSOR_TRIG_WQ_GLOBAL \ .trig_or_wq_config = { \ .trig_config = { \ .always_null = NULL, \ .mode = SENSOR_TRIG_MODE_GLOBAL_WQ, \ } \ } #define SENSOR_TRIG_NONE \ .trig_or_wq_config = { \ .trig_config = { \ .always_null = NULL, \ .mode = SENSOR_TRIG_MODE_NONE, \ } \ } #define SENSOR_GET_TRIG_MODE(_conf) \ (!(_conf)->trig_or_wq_config.fiber_config.stack \ ? SENSOR_TRIG_MODE_OWN_WQ : \ (_conf)->trig_or_wq_config.trig_config.mode) #define SENSOR_GET_WQ_CONFIG(_conf) \ ((_conf)->trig_or_wq_config.fiber_config) #ifdef __cplusplus } #endif /** * @} */ #endif /* __SENSOR_H__ */