| /* |
| * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #ifndef _HARDWARE_ADC_H_ |
| #define _HARDWARE_ADC_H_ |
| |
| #include "pico.h" |
| #include "hardware/structs/adc.h" |
| #include "hardware/gpio.h" |
| |
| /** \file hardware/adc.h |
| * \defgroup hardware_adc hardware_adc |
| * |
| * Analog to Digital Converter (ADC) API |
| * |
| * The RP2040 has an internal analogue-digital converter (ADC) with the following features: |
| * - SAR ADC |
| * - 500 kS/s (Using an independent 48MHz clock) |
| * - 12 bit (9.5 ENOB) |
| * - 5 input mux: |
| * - 4 inputs that are available on package pins shared with GPIO[29:26] |
| * - 1 input is dedicated to the internal temperature sensor |
| * - 4 element receive sample FIFO |
| * - Interrupt generation |
| * - DMA interface |
| * |
| * Although there is only one ADC you can specify the input to it using the adc_select_input() function. |
| * In round robin mode (adc_rrobin()) will use that input and move to the next one after a read. |
| * |
| * User ADC inputs are on 0-3 (GPIO 26-29), the temperature sensor is on input 4. |
| * |
| * Temperature sensor values can be approximated in centigrade as: |
| * |
| * T = 27 - (ADC_Voltage - 0.706)/0.001721 |
| * |
| * The FIFO, if used, can contain up to 4 entries. |
| * |
| * \subsection adc_example Example |
| * \addtogroup hardware_adc |
| * |
| * \include hello_adc.c |
| */ |
| |
| // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_ADC, Enable/disable assertions in the ADC module, type=bool, default=0, group=hardware_adc |
| #ifndef PARAM_ASSERTIONS_ENABLED_ADC |
| #define PARAM_ASSERTIONS_ENABLED_ADC 0 |
| #endif |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif |
| |
| /*! \brief Initialise the ADC HW |
| * \ingroup hardware_adc |
| * |
| */ |
| void adc_init(void); |
| |
| /*! \brief Initialise the gpio for use as an ADC pin |
| * \ingroup hardware_adc |
| * |
| * Prepare a GPIO for use with ADC, by disabling all digital functions. |
| * |
| * \param gpio The GPIO number to use. Allowable GPIO numbers are 26 to 29 inclusive. |
| */ |
| static inline void adc_gpio_init(uint gpio) { |
| invalid_params_if(ADC, gpio < 26 || gpio > 29); |
| // Select NULL function to make output driver hi-Z |
| gpio_set_function(gpio, GPIO_FUNC_NULL); |
| // Also disable digital pulls and digital receiver |
| gpio_disable_pulls(gpio); |
| gpio_set_input_enabled(gpio, false); |
| } |
| |
| /*! \brief ADC input select |
| * \ingroup hardware_adc |
| * |
| * Select an ADC input. 0...3 are GPIOs 26...29 respectively. |
| * Input 4 is the onboard temperature sensor. |
| * |
| * \param input Input to select. |
| */ |
| static inline void adc_select_input(uint input) { |
| invalid_params_if(ADC, input > 4); |
| hw_write_masked(&adc_hw->cs, input << ADC_CS_AINSEL_LSB, ADC_CS_AINSEL_BITS); |
| } |
| |
| /*! \brief Round Robin sampling selector |
| * \ingroup hardware_adc |
| * |
| * This function sets which inputs are to be run through in round robin mode. |
| * Value between 0 and 0x1f (bit 0 to bit 4 for GPIO 26 to 29 and temperature sensor input respectively) |
| * |
| * \param input_mask A bit pattern indicating which of the 5 inputs are to be sampled. Write a value of 0 to disable round robin sampling. |
| */ |
| static inline void adc_set_round_robin(uint input_mask) { |
| invalid_params_if(ADC, input_mask & ~ADC_CS_RROBIN_BITS); |
| hw_write_masked(&adc_hw->cs, input_mask << ADC_CS_RROBIN_LSB, ADC_CS_RROBIN_BITS); |
| } |
| |
| /*! \brief Enable the onboard temperature sensor |
| * \ingroup hardware_adc |
| * |
| * \param enable Set true to power on the onboard temperature sensor, false to power off. |
| * |
| */ |
| static inline void adc_set_temp_sensor_enabled(bool enable) { |
| if (enable) |
| hw_set_bits(&adc_hw->cs, ADC_CS_TS_EN_BITS); |
| else |
| hw_clear_bits(&adc_hw->cs, ADC_CS_TS_EN_BITS); |
| } |
| |
| /*! \brief Perform a single conversion |
| * \ingroup hardware_adc |
| * |
| * Performs an ADC conversion, waits for the result, and then returns it. |
| * |
| * \return Result of the conversion. |
| */ |
| static inline uint16_t adc_read(void) { |
| hw_set_bits(&adc_hw->cs, ADC_CS_START_ONCE_BITS); |
| |
| while (!(adc_hw->cs & ADC_CS_READY_BITS)) |
| tight_loop_contents(); |
| |
| return adc_hw->result; |
| } |
| |
| /*! \brief Enable or disable free-running sampling mode |
| * \ingroup hardware_adc |
| * |
| * \param run false to disable, true to enable free running conversion mode. |
| */ |
| static inline void adc_run(bool run) { |
| if (run) |
| hw_set_bits(&adc_hw->cs, ADC_CS_START_MANY_BITS); |
| else |
| hw_clear_bits(&adc_hw->cs, ADC_CS_START_MANY_BITS); |
| } |
| |
| /*! \brief Set the ADC Clock divisor |
| * \ingroup hardware_adc |
| * |
| * Period of samples will be (1 + div) cycles on average. Note it takes 96 cycles to perform a conversion, |
| * so any period less than that will be clamped to 96. |
| * |
| * \param clkdiv If non-zero, conversion will be started at intervals rather than back to back. |
| */ |
| static inline void adc_set_clkdiv(float clkdiv) { |
| invalid_params_if(ADC, clkdiv >= 1 << (ADC_DIV_INT_MSB - ADC_DIV_INT_LSB + 1)); |
| adc_hw->div = (uint32_t)(clkdiv * (float) (1 << ADC_DIV_INT_LSB)); |
| } |
| |
| /*! \brief Setup the ADC FIFO |
| * \ingroup hardware_adc |
| * |
| * FIFO is 4 samples long, if a conversion is completed and the FIFO is full the result is dropped. |
| * |
| * \param en Enables write each conversion result to the FIFO |
| * \param dreq_en Enable DMA requests when FIFO contains data |
| * \param dreq_thresh Threshold for DMA requests/FIFO IRQ if enabled. |
| * \param err_in_fifo If enabled, bit 15 of the FIFO contains error flag for each sample |
| * \param byte_shift Shift FIFO contents to be one byte in size (for byte DMA) - enables DMA to byte buffers. |
| */ |
| static inline void adc_fifo_setup(bool en, bool dreq_en, uint16_t dreq_thresh, bool err_in_fifo, bool byte_shift) { |
| hw_write_masked(&adc_hw->fcs, |
| (!!en << ADC_FCS_EN_LSB) | |
| (!!dreq_en << ADC_FCS_DREQ_EN_LSB) | |
| (dreq_thresh << ADC_FCS_THRESH_LSB) | |
| (!!err_in_fifo << ADC_FCS_ERR_LSB) | |
| (!!byte_shift << ADC_FCS_SHIFT_LSB), |
| ADC_FCS_EN_BITS | |
| ADC_FCS_DREQ_EN_BITS | |
| ADC_FCS_THRESH_BITS | |
| ADC_FCS_ERR_BITS | |
| ADC_FCS_SHIFT_BITS |
| ); |
| } |
| |
| /*! \brief Check FIFO empty state |
| * \ingroup hardware_adc |
| * |
| * \return Returns true if the fifo is empty |
| */ |
| static inline bool adc_fifo_is_empty(void) { |
| return !!(adc_hw->fcs & ADC_FCS_EMPTY_BITS); |
| } |
| |
| /*! \brief Get number of entries in the ADC FIFO |
| * \ingroup hardware_adc |
| * |
| * The ADC FIFO is 4 entries long. This function will return how many samples are currently present. |
| */ |
| static inline uint8_t adc_fifo_get_level(void) { |
| return (adc_hw->fcs & ADC_FCS_LEVEL_BITS) >> ADC_FCS_LEVEL_LSB; |
| } |
| |
| /*! \brief Get ADC result from FIFO |
| * \ingroup hardware_adc |
| * |
| * Pops the latest result from the ADC FIFO. |
| */ |
| static inline uint16_t adc_fifo_get(void) { |
| return adc_hw->fifo; |
| } |
| |
| /*! \brief Wait for the ADC FIFO to have data. |
| * \ingroup hardware_adc |
| * |
| * Blocks until data is present in the FIFO |
| */ |
| static inline uint16_t adc_fifo_get_blocking(void) { |
| while (adc_fifo_is_empty()) |
| tight_loop_contents(); |
| return adc_hw->fifo; |
| } |
| |
| /*! \brief Drain the ADC FIFO |
| * \ingroup hardware_adc |
| * |
| * Will wait for any conversion to complete then drain the FIFO discarding any results. |
| */ |
| static inline void adc_fifo_drain(void) { |
| // Potentially there is still a conversion in progress -- wait for this to complete before draining |
| while (!(adc_hw->cs & ADC_CS_READY_BITS)) |
| tight_loop_contents(); |
| while (!adc_fifo_is_empty()) |
| (void) adc_fifo_get(); |
| } |
| |
| /*! \brief Enable/Disable ADC interrupts. |
| * \ingroup hardware_adc |
| * |
| * \param enabled Set to true to enable the ADC interrupts, false to disable |
| */ |
| static inline void adc_irq_set_enabled(bool enabled) { |
| adc_hw->inte = !!enabled; |
| } |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif |