| /** |
| * @file |
| * @brief ADC public API header file. |
| */ |
| |
| /* |
| * Copyright (c) 2018 Nordic Semiconductor ASA |
| * Copyright (c) 2015 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #ifndef ZEPHYR_INCLUDE_DRIVERS_ADC_H_ |
| #define ZEPHYR_INCLUDE_DRIVERS_ADC_H_ |
| |
| #include <zephyr/device.h> |
| #include <zephyr/dt-bindings/adc/adc.h> |
| #include <zephyr/kernel.h> |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /** |
| * @brief ADC driver APIs |
| * @defgroup adc_interface ADC driver APIs |
| * @since 1.0 |
| * @version 1.0.0 |
| * @ingroup io_interfaces |
| * @{ |
| */ |
| |
| /** @brief ADC channel gain factors. */ |
| enum adc_gain { |
| ADC_GAIN_1_6, /**< x 1/6. */ |
| ADC_GAIN_1_5, /**< x 1/5. */ |
| ADC_GAIN_1_4, /**< x 1/4. */ |
| ADC_GAIN_1_3, /**< x 1/3. */ |
| ADC_GAIN_2_5, /**< x 2/5. */ |
| ADC_GAIN_1_2, /**< x 1/2. */ |
| ADC_GAIN_2_3, /**< x 2/3. */ |
| ADC_GAIN_4_5, /**< x 4/5. */ |
| ADC_GAIN_1, /**< x 1. */ |
| ADC_GAIN_2, /**< x 2. */ |
| ADC_GAIN_3, /**< x 3. */ |
| ADC_GAIN_4, /**< x 4. */ |
| ADC_GAIN_6, /**< x 6. */ |
| ADC_GAIN_8, /**< x 8. */ |
| ADC_GAIN_12, /**< x 12. */ |
| ADC_GAIN_16, /**< x 16. */ |
| ADC_GAIN_24, /**< x 24. */ |
| ADC_GAIN_32, /**< x 32. */ |
| ADC_GAIN_64, /**< x 64. */ |
| ADC_GAIN_128, /**< x 128. */ |
| }; |
| |
| /** |
| * @brief Invert the application of gain to a measurement value. |
| * |
| * For example, if the gain passed in is ADC_GAIN_1_6 and the |
| * referenced value is 10, the value after the function returns is 60. |
| * |
| * @param gain the gain used to amplify the input signal. |
| * |
| * @param value a pointer to a value that initially has the effect of |
| * the applied gain but has that effect removed when this function |
| * successfully returns. If the gain cannot be reversed the value |
| * remains unchanged. |
| * |
| * @retval 0 if the gain was successfully reversed |
| * @retval -EINVAL if the gain could not be interpreted |
| */ |
| int adc_gain_invert(enum adc_gain gain, |
| int32_t *value); |
| |
| /** @brief ADC references. */ |
| enum adc_reference { |
| ADC_REF_VDD_1, /**< VDD. */ |
| ADC_REF_VDD_1_2, /**< VDD/2. */ |
| ADC_REF_VDD_1_3, /**< VDD/3. */ |
| ADC_REF_VDD_1_4, /**< VDD/4. */ |
| ADC_REF_INTERNAL, /**< Internal. */ |
| ADC_REF_EXTERNAL0, /**< External, input 0. */ |
| ADC_REF_EXTERNAL1, /**< External, input 1. */ |
| }; |
| |
| /** |
| * @brief Structure for specifying the configuration of an ADC channel. |
| */ |
| struct adc_channel_cfg { |
| /** Gain selection. */ |
| enum adc_gain gain; |
| |
| /** Reference selection. */ |
| enum adc_reference reference; |
| |
| /** |
| * Acquisition time. |
| * Use the ADC_ACQ_TIME macro to compose the value for this field or |
| * pass ADC_ACQ_TIME_DEFAULT to use the default setting for a given |
| * hardware (e.g. when the hardware does not allow to configure the |
| * acquisition time). |
| * Particular drivers do not necessarily support all the possible units. |
| * Value range is 0-16383 for a given unit. |
| */ |
| uint16_t acquisition_time; |
| |
| /** |
| * Channel identifier. |
| * This value primarily identifies the channel within the ADC API - when |
| * a read request is done, the corresponding bit in the "channels" field |
| * of the "adc_sequence" structure must be set to include this channel |
| * in the sampling. |
| * For hardware that does not allow selection of analog inputs for given |
| * channels, but rather have dedicated ones, this value also selects the |
| * physical ADC input to be used in the sampling. Otherwise, when it is |
| * needed to explicitly select an analog input for the channel, or two |
| * inputs when the channel is a differential one, the selection is done |
| * in "input_positive" and "input_negative" fields. |
| * Particular drivers indicate which one of the above two cases they |
| * support by selecting or not a special hidden Kconfig option named |
| * ADC_CONFIGURABLE_INPUTS. If this option is not selected, the macro |
| * CONFIG_ADC_CONFIGURABLE_INPUTS is not defined and consequently the |
| * mentioned two fields are not present in this structure. |
| * While this API allows identifiers from range 0-31, particular drivers |
| * may support only a limited number of channel identifiers (dependent |
| * on the underlying hardware capabilities or configured via a dedicated |
| * Kconfig option). |
| */ |
| uint8_t channel_id : 5; |
| |
| /** Channel type: single-ended or differential. */ |
| uint8_t differential : 1; |
| |
| #ifdef CONFIG_ADC_CONFIGURABLE_INPUTS |
| /** |
| * Positive ADC input. |
| * This is a driver dependent value that identifies an ADC input to be |
| * associated with the channel. |
| */ |
| uint8_t input_positive; |
| |
| /** |
| * Negative ADC input (used only for differential channels). |
| * This is a driver dependent value that identifies an ADC input to be |
| * associated with the channel. |
| */ |
| uint8_t input_negative; |
| #endif /* CONFIG_ADC_CONFIGURABLE_INPUTS */ |
| |
| #ifdef CONFIG_ADC_CONFIGURABLE_EXCITATION_CURRENT_SOURCE_PIN |
| uint8_t current_source_pin_set : 1; |
| /** |
| * Output pin for the current sources. |
| * This is only available if the driver enables this feature |
| * via the hidden configuration option ADC_CONFIGURABLE_EXCITATION_CURRENT_SOURCE_PIN. |
| * The meaning itself is then defined by the driver itself. |
| */ |
| uint8_t current_source_pin[2]; |
| #endif /* CONFIG_ADC_CONFIGURABLE_EXCITATION_CURRENT_SOURCE_PIN */ |
| |
| #ifdef CONFIG_ADC_CONFIGURABLE_VBIAS_PIN |
| /** |
| * Output pins for the bias voltage. |
| * This is only available if the driver enables this feature |
| * via the hidden configuration option ADC_CONFIGURABLE_VBIAS_PIN. |
| * The field is interpreted as a bitmask, where each bit represents |
| * one of the input pins. The actual mapping to the physical pins |
| * depends on the driver itself. |
| */ |
| uint32_t vbias_pins; |
| #endif /* CONFIG_ADC_CONFIGURABLE_VBIAS_PIN */ |
| }; |
| |
| /** |
| * @brief Get ADC channel configuration from a given devicetree node. |
| * |
| * This returns a static initializer for a <tt>struct adc_channel_cfg</tt> |
| * filled with data from a given devicetree node. |
| * |
| * Example devicetree fragment: |
| * |
| * @code{.dts} |
| * &adc { |
| * #address-cells = <1>; |
| * #size-cells = <0>; |
| * |
| * channel@0 { |
| * reg = <0>; |
| * zephyr,gain = "ADC_GAIN_1_6"; |
| * zephyr,reference = "ADC_REF_INTERNAL"; |
| * zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20)>; |
| * zephyr,input-positive = <NRF_SAADC_AIN6>; |
| * zephyr,input-negative = <NRF_SAADC_AIN7>; |
| * }; |
| * |
| * channel@1 { |
| * reg = <1>; |
| * zephyr,gain = "ADC_GAIN_1_6"; |
| * zephyr,reference = "ADC_REF_INTERNAL"; |
| * zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>; |
| * zephyr,input-positive = <NRF_SAADC_AIN0>; |
| * }; |
| * }; |
| * @endcode |
| * |
| * Example usage: |
| * |
| * @code{.c} |
| * static const struct adc_channel_cfg ch0_cfg_dt = |
| * ADC_CHANNEL_CFG_DT(DT_CHILD(DT_NODELABEL(adc), channel_0)); |
| * static const struct adc_channel_cfg ch1_cfg_dt = |
| * ADC_CHANNEL_CFG_DT(DT_CHILD(DT_NODELABEL(adc), channel_1)); |
| * |
| * // Initializes 'ch0_cfg_dt' to: |
| * // { |
| * // .channel_id = 0, |
| * // .gain = ADC_GAIN_1_6, |
| * // .reference = ADC_REF_INTERNAL, |
| * // .acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20), |
| * // .differential = true, |
| * // .input_positive = NRF_SAADC_AIN6, |
| * // .input-negative = NRF_SAADC_AIN7, |
| * // } |
| * // and 'ch1_cfg_dt' to: |
| * // { |
| * // .channel_id = 1, |
| * // .gain = ADC_GAIN_1_6, |
| * // .reference = ADC_REF_INTERNAL, |
| * // .acquisition_time = ADC_ACQ_TIME_DEFAULT, |
| * // .input_positive = NRF_SAADC_AIN0, |
| * // } |
| * @endcode |
| * |
| * @param node_id Devicetree node identifier. |
| * |
| * @return Static initializer for an adc_channel_cfg structure. |
| */ |
| #define ADC_CHANNEL_CFG_DT(node_id) { \ |
| .gain = DT_STRING_TOKEN(node_id, zephyr_gain), \ |
| .reference = DT_STRING_TOKEN(node_id, zephyr_reference), \ |
| .acquisition_time = DT_PROP(node_id, zephyr_acquisition_time), \ |
| .channel_id = DT_REG_ADDR(node_id), \ |
| IF_ENABLED(CONFIG_ADC_CONFIGURABLE_INPUTS, \ |
| (.differential = DT_NODE_HAS_PROP(node_id, zephyr_input_negative), \ |
| .input_positive = DT_PROP_OR(node_id, zephyr_input_positive, 0), \ |
| .input_negative = DT_PROP_OR(node_id, zephyr_input_negative, 0),)) \ |
| IF_ENABLED(DT_PROP(node_id, zephyr_differential), \ |
| (.differential = true,)) \ |
| IF_ENABLED(CONFIG_ADC_CONFIGURABLE_EXCITATION_CURRENT_SOURCE_PIN, \ |
| (.current_source_pin_set = DT_NODE_HAS_PROP(node_id, zephyr_current_source_pin), \ |
| .current_source_pin = DT_PROP_OR(node_id, zephyr_current_source_pin, {0}),)) \ |
| IF_ENABLED(CONFIG_ADC_CONFIGURABLE_VBIAS_PIN, \ |
| (.vbias_pins = DT_PROP_OR(node_id, zephyr_vbias_pins, 0),)) \ |
| } |
| |
| /** |
| * @brief Container for ADC channel information specified in devicetree. |
| * |
| * @see ADC_DT_SPEC_GET_BY_IDX |
| * @see ADC_DT_SPEC_GET |
| */ |
| struct adc_dt_spec { |
| /** |
| * Pointer to the device structure for the ADC driver instance |
| * used by this io-channel. |
| */ |
| const struct device *dev; |
| |
| /** ADC channel identifier used by this io-channel. */ |
| uint8_t channel_id; |
| |
| /** |
| * Flag indicating whether configuration of the associated ADC channel |
| * is provided as a child node of the corresponding ADC controller in |
| * devicetree. |
| */ |
| bool channel_cfg_dt_node_exists; |
| |
| /** |
| * Configuration of the associated ADC channel specified in devicetree. |
| * This field is valid only when @a channel_cfg_dt_node_exists is set |
| * to @a true. |
| */ |
| struct adc_channel_cfg channel_cfg; |
| |
| /** |
| * Voltage of the reference selected for the channel or 0 if this |
| * value is not provided in devicetree. |
| * This field is valid only when @a channel_cfg_dt_node_exists is set |
| * to @a true. |
| */ |
| uint16_t vref_mv; |
| |
| /** |
| * ADC resolution to be used for that channel. |
| * This field is valid only when @a channel_cfg_dt_node_exists is set |
| * to @a true. |
| */ |
| uint8_t resolution; |
| |
| /** |
| * Oversampling setting to be used for that channel. |
| * This field is valid only when @a channel_cfg_dt_node_exists is set |
| * to @a true. |
| */ |
| uint8_t oversampling; |
| }; |
| |
| /** @cond INTERNAL_HIDDEN */ |
| |
| #define ADC_DT_SPEC_STRUCT(ctlr, input) { \ |
| .dev = DEVICE_DT_GET(ctlr), \ |
| .channel_id = input, \ |
| ADC_CHANNEL_CFG_FROM_DT_NODE(\ |
| ADC_CHANNEL_DT_NODE(ctlr, input)) \ |
| } |
| |
| #define ADC_CHANNEL_DT_NODE(ctlr, input) \ |
| DT_FOREACH_CHILD_VARGS(ctlr, ADC_FOREACH_INPUT, input) |
| |
| #define ADC_FOREACH_INPUT(node, input) \ |
| IF_ENABLED(IS_EQ(DT_REG_ADDR(node), input), (node)) |
| |
| #define ADC_CHANNEL_CFG_FROM_DT_NODE(node_id) \ |
| IF_ENABLED(DT_NODE_EXISTS(node_id), \ |
| (.channel_cfg_dt_node_exists = true, \ |
| .channel_cfg = ADC_CHANNEL_CFG_DT(node_id), \ |
| .vref_mv = DT_PROP_OR(node_id, zephyr_vref_mv, 0), \ |
| .resolution = DT_PROP_OR(node_id, zephyr_resolution, 0), \ |
| .oversampling = DT_PROP_OR(node_id, zephyr_oversampling, 0),)) |
| |
| /** @endcond */ |
| |
| /** |
| * @brief Get ADC io-channel information from devicetree by name. |
| * |
| * This returns a static initializer for an @p adc_dt_spec structure |
| * given a devicetree node and a channel name. The node must have |
| * the "io-channels" property defined. |
| * |
| * Example devicetree fragment: |
| * |
| * @code{.dts} |
| * / { |
| * zephyr,user { |
| * io-channels = <&adc0 1>, <&adc0 3>; |
| * io-channel-names = "A0", "A1"; |
| * }; |
| * }; |
| * |
| * &adc0 { |
| * #address-cells = <1>; |
| * #size-cells = <0>; |
| * |
| * channel@3 { |
| * reg = <3>; |
| * zephyr,gain = "ADC_GAIN_1_5"; |
| * zephyr,reference = "ADC_REF_VDD_1_4"; |
| * zephyr,vref-mv = <750>; |
| * zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>; |
| * zephyr,resolution = <12>; |
| * zephyr,oversampling = <4>; |
| * }; |
| * }; |
| * @endcode |
| * |
| * Example usage: |
| * |
| * @code{.c} |
| * static const struct adc_dt_spec adc_chan0 = |
| * ADC_DT_SPEC_GET_BY_NAME(DT_PATH(zephyr_user), a0); |
| * static const struct adc_dt_spec adc_chan1 = |
| * ADC_DT_SPEC_GET_BY_NAME(DT_PATH(zephyr_user), a1); |
| * |
| * // Initializes 'adc_chan0' to: |
| * // { |
| * // .dev = DEVICE_DT_GET(DT_NODELABEL(adc0)), |
| * // .channel_id = 1, |
| * // } |
| * // and 'adc_chan1' to: |
| * // { |
| * // .dev = DEVICE_DT_GET(DT_NODELABEL(adc0)), |
| * // .channel_id = 3, |
| * // .channel_cfg_dt_node_exists = true, |
| * // .channel_cfg = { |
| * // .channel_id = 3, |
| * // .gain = ADC_GAIN_1_5, |
| * // .reference = ADC_REF_VDD_1_4, |
| * // .acquisition_time = ADC_ACQ_TIME_DEFAULT, |
| * // }, |
| * // .vref_mv = 750, |
| * // .resolution = 12, |
| * // .oversampling = 4, |
| * // } |
| * @endcode |
| * |
| * @param node_id Devicetree node identifier. |
| * @param name Channel name. |
| * |
| * @return Static initializer for an adc_dt_spec structure. |
| */ |
| #define ADC_DT_SPEC_GET_BY_NAME(node_id, name) \ |
| ADC_DT_SPEC_STRUCT(DT_IO_CHANNELS_CTLR_BY_NAME(node_id, name), \ |
| DT_IO_CHANNELS_INPUT_BY_NAME(node_id, name)) |
| |
| /** @brief Get ADC io-channel information from a DT_DRV_COMPAT devicetree |
| * instance by name. |
| * |
| * @see ADC_DT_SPEC_GET_BY_NAME() |
| * |
| * @param inst DT_DRV_COMPAT instance number |
| * @param name Channel name. |
| * |
| * @return Static initializer for an adc_dt_spec structure. |
| */ |
| #define ADC_DT_SPEC_INST_GET_BY_NAME(inst, name) \ |
| ADC_DT_SPEC_GET_BY_NAME(DT_DRV_INST(inst), name) |
| |
| /** |
| * @brief Get ADC io-channel information from devicetree. |
| * |
| * This returns a static initializer for an @p adc_dt_spec structure |
| * given a devicetree node and a channel index. The node must have |
| * the "io-channels" property defined. |
| * |
| * Example devicetree fragment: |
| * |
| * @code{.dts} |
| * / { |
| * zephyr,user { |
| * io-channels = <&adc0 1>, <&adc0 3>; |
| * }; |
| * }; |
| * |
| * &adc0 { |
| * #address-cells = <1>; |
| * #size-cells = <0>; |
| * |
| * channel@3 { |
| * reg = <3>; |
| * zephyr,gain = "ADC_GAIN_1_5"; |
| * zephyr,reference = "ADC_REF_VDD_1_4"; |
| * zephyr,vref-mv = <750>; |
| * zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>; |
| * zephyr,resolution = <12>; |
| * zephyr,oversampling = <4>; |
| * }; |
| * }; |
| * @endcode |
| * |
| * Example usage: |
| * |
| * @code{.c} |
| * static const struct adc_dt_spec adc_chan0 = |
| * ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0); |
| * static const struct adc_dt_spec adc_chan1 = |
| * ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 1); |
| * |
| * // Initializes 'adc_chan0' to: |
| * // { |
| * // .dev = DEVICE_DT_GET(DT_NODELABEL(adc0)), |
| * // .channel_id = 1, |
| * // } |
| * // and 'adc_chan1' to: |
| * // { |
| * // .dev = DEVICE_DT_GET(DT_NODELABEL(adc0)), |
| * // .channel_id = 3, |
| * // .channel_cfg_dt_node_exists = true, |
| * // .channel_cfg = { |
| * // .channel_id = 3, |
| * // .gain = ADC_GAIN_1_5, |
| * // .reference = ADC_REF_VDD_1_4, |
| * // .acquisition_time = ADC_ACQ_TIME_DEFAULT, |
| * // }, |
| * // .vref_mv = 750, |
| * // .resolution = 12, |
| * // .oversampling = 4, |
| * // } |
| * @endcode |
| * |
| * @see ADC_DT_SPEC_GET() |
| * |
| * @param node_id Devicetree node identifier. |
| * @param idx Channel index. |
| * |
| * @return Static initializer for an adc_dt_spec structure. |
| */ |
| #define ADC_DT_SPEC_GET_BY_IDX(node_id, idx) \ |
| ADC_DT_SPEC_STRUCT(DT_IO_CHANNELS_CTLR_BY_IDX(node_id, idx), \ |
| DT_IO_CHANNELS_INPUT_BY_IDX(node_id, idx)) |
| |
| /** @brief Get ADC io-channel information from a DT_DRV_COMPAT devicetree |
| * instance. |
| * |
| * @see ADC_DT_SPEC_GET_BY_IDX() |
| * |
| * @param inst DT_DRV_COMPAT instance number |
| * @param idx Channel index. |
| * |
| * @return Static initializer for an adc_dt_spec structure. |
| */ |
| #define ADC_DT_SPEC_INST_GET_BY_IDX(inst, idx) \ |
| ADC_DT_SPEC_GET_BY_IDX(DT_DRV_INST(inst), idx) |
| |
| /** |
| * @brief Equivalent to ADC_DT_SPEC_GET_BY_IDX(node_id, 0). |
| * |
| * @see ADC_DT_SPEC_GET_BY_IDX() |
| * |
| * @param node_id Devicetree node identifier. |
| * |
| * @return Static initializer for an adc_dt_spec structure. |
| */ |
| #define ADC_DT_SPEC_GET(node_id) ADC_DT_SPEC_GET_BY_IDX(node_id, 0) |
| |
| /** @brief Equivalent to ADC_DT_SPEC_INST_GET_BY_IDX(inst, 0). |
| * |
| * @see ADC_DT_SPEC_GET() |
| * |
| * @param inst DT_DRV_COMPAT instance number |
| * |
| * @return Static initializer for an adc_dt_spec structure. |
| */ |
| #define ADC_DT_SPEC_INST_GET(inst) ADC_DT_SPEC_GET(DT_DRV_INST(inst)) |
| |
| /* Forward declaration of the adc_sequence structure. */ |
| struct adc_sequence; |
| |
| /** |
| * @brief Action to be performed after a sampling is done. |
| */ |
| enum adc_action { |
| /** The sequence should be continued normally. */ |
| ADC_ACTION_CONTINUE = 0, |
| |
| /** |
| * The sampling should be repeated. New samples or sample should be |
| * read from the ADC and written in the same place as the recent ones. |
| */ |
| ADC_ACTION_REPEAT, |
| |
| /** The sequence should be finished immediately. */ |
| ADC_ACTION_FINISH, |
| }; |
| |
| /** |
| * @brief Type definition of the optional callback function to be called after |
| * a requested sampling is done. |
| * |
| * @param dev Pointer to the device structure for the driver |
| * instance. |
| * @param sequence Pointer to the sequence structure that triggered |
| * the sampling. This parameter points to a copy of |
| * the structure that was supplied to the call that |
| * started the sampling sequence, thus it cannot be |
| * used with the CONTAINER_OF() macro to retrieve |
| * some other data associated with the sequence. |
| * Instead, the adc_sequence_options::user_data field |
| * should be used for such purpose. |
| * |
| * @param sampling_index Index (0-65535) of the sampling done. |
| * |
| * @returns Action to be performed by the driver. See @ref adc_action. |
| */ |
| typedef enum adc_action (*adc_sequence_callback)(const struct device *dev, |
| const struct adc_sequence *sequence, |
| uint16_t sampling_index); |
| |
| /** |
| * @brief Structure defining additional options for an ADC sampling sequence. |
| */ |
| struct adc_sequence_options { |
| /** |
| * Interval between consecutive samplings (in microseconds), 0 means |
| * sample as fast as possible, without involving any timer. |
| * The accuracy of this interval is dependent on the implementation of |
| * a given driver. The default routine that handles the intervals uses |
| * a kernel timer for this purpose, thus, it has the accuracy of the |
| * kernel's system clock. Particular drivers may use some dedicated |
| * hardware timers and achieve a better precision. |
| */ |
| uint32_t interval_us; |
| |
| /** |
| * Callback function to be called after each sampling is done. |
| * Optional - set to NULL if it is not needed. |
| */ |
| adc_sequence_callback callback; |
| |
| /** |
| * Pointer to user data. It can be used to associate the sequence |
| * with any other data that is needed in the callback function. |
| */ |
| void *user_data; |
| |
| /** |
| * Number of extra samplings to perform (the total number of samplings |
| * is 1 + extra_samplings). |
| */ |
| uint16_t extra_samplings; |
| }; |
| |
| /** |
| * @brief Structure defining an ADC sampling sequence. |
| */ |
| struct adc_sequence { |
| /** |
| * Pointer to a structure defining additional options for the sequence. |
| * If NULL, the sequence consists of a single sampling. |
| */ |
| const struct adc_sequence_options *options; |
| |
| /** |
| * Bit-mask indicating the channels to be included in each sampling |
| * of this sequence. |
| * All selected channels must be configured with adc_channel_setup() |
| * before they are used in a sequence. |
| * The least significant bit corresponds to channel 0. |
| */ |
| uint32_t channels; |
| |
| /** |
| * Pointer to a buffer where the samples are to be written. Samples |
| * from subsequent samplings are written sequentially in the buffer. |
| * The number of samples written for each sampling is determined by |
| * the number of channels selected in the "channels" field. |
| * The values written to the buffer represent a sample from each |
| * selected channel starting from the one with the lowest ID. |
| * The buffer must be of an appropriate size, taking into account |
| * the number of selected channels and the ADC resolution used, |
| * as well as the number of samplings contained in the sequence. |
| */ |
| void *buffer; |
| |
| /** |
| * Specifies the actual size of the buffer pointed by the "buffer" |
| * field (in bytes). The driver must ensure that samples are not |
| * written beyond the limit and it must return an error if the buffer |
| * turns out to be not large enough to hold all the requested samples. |
| */ |
| size_t buffer_size; |
| |
| /** |
| * ADC resolution. |
| * For single-ended channels the sample values are from range: |
| * 0 .. 2^resolution - 1, |
| * for differential ones: |
| * - 2^(resolution-1) .. 2^(resolution-1) - 1. |
| */ |
| uint8_t resolution; |
| |
| /** |
| * Oversampling setting. |
| * Each sample is averaged from 2^oversampling conversion results. |
| * This feature may be unsupported by a given ADC hardware, or in |
| * a specific mode (e.g. when sampling multiple channels). |
| */ |
| uint8_t oversampling; |
| |
| /** |
| * Perform calibration before the reading is taken if requested. |
| * |
| * The impact of channel configuration on the calibration |
| * process is specific to the underlying hardware. ADC |
| * implementations that do not support calibration should |
| * ignore this flag. |
| */ |
| bool calibrate; |
| }; |
| |
| |
| /** |
| * @brief Type definition of ADC API function for configuring a channel. |
| * See adc_channel_setup() for argument descriptions. |
| */ |
| typedef int (*adc_api_channel_setup)(const struct device *dev, |
| const struct adc_channel_cfg *channel_cfg); |
| |
| /** |
| * @brief Type definition of ADC API function for setting a read request. |
| * See adc_read() for argument descriptions. |
| */ |
| typedef int (*adc_api_read)(const struct device *dev, |
| const struct adc_sequence *sequence); |
| |
| /** |
| * @brief Type definition of ADC API function for setting an asynchronous |
| * read request. |
| * See adc_read_async() for argument descriptions. |
| */ |
| typedef int (*adc_api_read_async)(const struct device *dev, |
| const struct adc_sequence *sequence, |
| struct k_poll_signal *async); |
| |
| /** |
| * @brief ADC driver API |
| * |
| * This is the mandatory API any ADC driver needs to expose. |
| */ |
| __subsystem struct adc_driver_api { |
| adc_api_channel_setup channel_setup; |
| adc_api_read read; |
| #ifdef CONFIG_ADC_ASYNC |
| adc_api_read_async read_async; |
| #endif |
| uint16_t ref_internal; /* mV */ |
| }; |
| |
| /** |
| * @brief Configure an ADC channel. |
| * |
| * It is required to call this function and configure each channel before it is |
| * selected for a read request. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param channel_cfg Channel configuration. |
| * |
| * @retval 0 On success. |
| * @retval -EINVAL If a parameter with an invalid value has been provided. |
| */ |
| __syscall int adc_channel_setup(const struct device *dev, |
| const struct adc_channel_cfg *channel_cfg); |
| |
| static inline int z_impl_adc_channel_setup(const struct device *dev, |
| const struct adc_channel_cfg *channel_cfg) |
| { |
| const struct adc_driver_api *api = |
| (const struct adc_driver_api *)dev->api; |
| |
| return api->channel_setup(dev, channel_cfg); |
| } |
| |
| /** |
| * @brief Configure an ADC channel from a struct adc_dt_spec. |
| * |
| * @param spec ADC specification from Devicetree. |
| * |
| * @return A value from adc_channel_setup() or -ENOTSUP if information from |
| * Devicetree is not valid. |
| * @see adc_channel_setup() |
| */ |
| static inline int adc_channel_setup_dt(const struct adc_dt_spec *spec) |
| { |
| if (!spec->channel_cfg_dt_node_exists) { |
| return -ENOTSUP; |
| } |
| |
| return adc_channel_setup(spec->dev, &spec->channel_cfg); |
| } |
| |
| /** |
| * @brief Set a read request. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param sequence Structure specifying requested sequence of samplings. |
| * |
| * If invoked from user mode, any sequence struct options for callback must |
| * be NULL. |
| * |
| * @retval 0 On success. |
| * @retval -EINVAL If a parameter with an invalid value has been provided. |
| * @retval -ENOMEM If the provided buffer is to small to hold the results |
| * of all requested samplings. |
| * @retval -ENOTSUP If the requested mode of operation is not supported. |
| * @retval -EBUSY If another sampling was triggered while the previous one |
| * was still in progress. This may occur only when samplings |
| * are done with intervals, and it indicates that the selected |
| * interval was too small. All requested samples are written |
| * in the buffer, but at least some of them were taken with |
| * an extra delay compared to what was scheduled. |
| */ |
| __syscall int adc_read(const struct device *dev, |
| const struct adc_sequence *sequence); |
| |
| static inline int z_impl_adc_read(const struct device *dev, |
| const struct adc_sequence *sequence) |
| { |
| const struct adc_driver_api *api = |
| (const struct adc_driver_api *)dev->api; |
| |
| return api->read(dev, sequence); |
| } |
| |
| /** |
| * @brief Set a read request from a struct adc_dt_spec. |
| * |
| * @param spec ADC specification from Devicetree. |
| * @param sequence Structure specifying requested sequence of samplings. |
| * |
| * @return A value from adc_read(). |
| * @see adc_read() |
| */ |
| static inline int adc_read_dt(const struct adc_dt_spec *spec, |
| const struct adc_sequence *sequence) |
| { |
| return adc_read(spec->dev, sequence); |
| } |
| |
| /** |
| * @brief Set an asynchronous read request. |
| * |
| * @note This function is available only if @kconfig{CONFIG_ADC_ASYNC} |
| * is selected. |
| * |
| * If invoked from user mode, any sequence struct options for callback must |
| * be NULL. |
| * |
| * @param dev Pointer to the device structure for the driver instance. |
| * @param sequence Structure specifying requested sequence of samplings. |
| * @param async Pointer to a valid and ready to be signaled struct |
| * k_poll_signal. (Note: if NULL this function will not notify |
| * the end of the transaction, and whether it went successfully |
| * or not). |
| * |
| * @returns 0 on success, negative error code otherwise. |
| * See adc_read() for a list of possible error codes. |
| * |
| */ |
| __syscall int adc_read_async(const struct device *dev, |
| const struct adc_sequence *sequence, |
| struct k_poll_signal *async); |
| |
| |
| #ifdef CONFIG_ADC_ASYNC |
| static inline int z_impl_adc_read_async(const struct device *dev, |
| const struct adc_sequence *sequence, |
| struct k_poll_signal *async) |
| { |
| const struct adc_driver_api *api = |
| (const struct adc_driver_api *)dev->api; |
| |
| return api->read_async(dev, sequence, async); |
| } |
| #endif /* CONFIG_ADC_ASYNC */ |
| |
| /** |
| * @brief Get the internal reference voltage. |
| * |
| * Returns the voltage corresponding to @ref ADC_REF_INTERNAL, |
| * measured in millivolts. |
| * |
| * @return a positive value is the reference voltage value. Returns |
| * zero if reference voltage information is not available. |
| */ |
| static inline uint16_t adc_ref_internal(const struct device *dev) |
| { |
| const struct adc_driver_api *api = |
| (const struct adc_driver_api *)dev->api; |
| |
| return api->ref_internal; |
| } |
| |
| /** |
| * @brief Convert a raw ADC value to millivolts. |
| * |
| * This function performs the necessary conversion to transform a raw |
| * ADC measurement to a voltage in millivolts. |
| * |
| * @param ref_mv the reference voltage used for the measurement, in |
| * millivolts. This may be from adc_ref_internal() or a known |
| * external reference. |
| * |
| * @param gain the ADC gain configuration used to sample the input |
| * |
| * @param resolution the number of bits in the absolute value of the |
| * sample. For differential sampling this needs to be one less than the |
| * resolution in struct adc_sequence. |
| * |
| * @param valp pointer to the raw measurement value on input, and the |
| * corresponding millivolt value on successful conversion. If |
| * conversion fails the stored value is left unchanged. |
| * |
| * @retval 0 on successful conversion |
| * @retval -EINVAL if the gain is not reversible |
| */ |
| static inline int adc_raw_to_millivolts(int32_t ref_mv, |
| enum adc_gain gain, |
| uint8_t resolution, |
| int32_t *valp) |
| { |
| int32_t adc_mv = *valp * ref_mv; |
| int ret = adc_gain_invert(gain, &adc_mv); |
| |
| if (ret == 0) { |
| *valp = (adc_mv >> resolution); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * @brief Convert a raw ADC value to millivolts using information stored |
| * in a struct adc_dt_spec. |
| * |
| * @param[in] spec ADC specification from Devicetree. |
| * @param[in,out] valp Pointer to the raw measurement value on input, and the |
| * corresponding millivolt value on successful conversion. If conversion fails |
| * the stored value is left unchanged. |
| * |
| * @return A value from adc_raw_to_millivolts() or -ENOTSUP if information from |
| * Devicetree is not valid. |
| * @see adc_raw_to_millivolts() |
| */ |
| static inline int adc_raw_to_millivolts_dt(const struct adc_dt_spec *spec, |
| int32_t *valp) |
| { |
| int32_t vref_mv; |
| uint8_t resolution; |
| |
| if (!spec->channel_cfg_dt_node_exists) { |
| return -ENOTSUP; |
| } |
| |
| if (spec->channel_cfg.reference == ADC_REF_INTERNAL) { |
| vref_mv = (int32_t)adc_ref_internal(spec->dev); |
| } else { |
| vref_mv = spec->vref_mv; |
| } |
| |
| resolution = spec->resolution; |
| |
| /* |
| * For differential channels, one bit less needs to be specified |
| * for resolution to achieve correct conversion. |
| */ |
| if (spec->channel_cfg.differential) { |
| resolution -= 1U; |
| } |
| |
| return adc_raw_to_millivolts(vref_mv, spec->channel_cfg.gain, |
| resolution, valp); |
| } |
| |
| /** |
| * @brief Initialize a struct adc_sequence from information stored in |
| * struct adc_dt_spec. |
| * |
| * Note that this function only initializes the following fields: |
| * |
| * - @ref adc_sequence.channels |
| * - @ref adc_sequence.resolution |
| * - @ref adc_sequence.oversampling |
| * |
| * Other fields should be initialized by the caller. |
| * |
| * @param[in] spec ADC specification from Devicetree. |
| * @param[out] seq Sequence to initialize. |
| * |
| * @retval 0 On success |
| * @retval -ENOTSUP If @p spec does not have valid channel configuration |
| */ |
| static inline int adc_sequence_init_dt(const struct adc_dt_spec *spec, |
| struct adc_sequence *seq) |
| { |
| if (!spec->channel_cfg_dt_node_exists) { |
| return -ENOTSUP; |
| } |
| |
| seq->channels = BIT(spec->channel_id); |
| seq->resolution = spec->resolution; |
| seq->oversampling = spec->oversampling; |
| |
| return 0; |
| } |
| |
| /** |
| * @brief Validate that the ADC device is ready. |
| * |
| * @param spec ADC specification from devicetree |
| * |
| * @retval true if the ADC device is ready for use and false otherwise. |
| */ |
| static inline bool adc_is_ready_dt(const struct adc_dt_spec *spec) |
| { |
| return device_is_ready(spec->dev); |
| } |
| |
| /** |
| * @} |
| */ |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #include <zephyr/syscalls/adc.h> |
| |
| #endif /* ZEPHYR_INCLUDE_DRIVERS_ADC_H_ */ |