1 /*
2 * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #ifndef _HARDWARE_ADC_H_
8 #define _HARDWARE_ADC_H_
9
10 #include "pico.h"
11 #include "hardware/structs/adc.h"
12 #include "hardware/gpio.h"
13
14 /** \file hardware/adc.h
15 * \defgroup hardware_adc hardware_adc
16 *
17 * Analog to Digital Converter (ADC) API
18 *
19 * The RP2040 has an internal analogue-digital converter (ADC) with the following features:
20 * - SAR ADC
21 * - 500 kS/s (Using an independent 48MHz clock)
22 * - 12 bit (9.5 ENOB)
23 * - 5 input mux:
24 * - 4 inputs that are available on package pins shared with GPIO[29:26]
25 * - 1 input is dedicated to the internal temperature sensor
26 * - 4 element receive sample FIFO
27 * - Interrupt generation
28 * - DMA interface
29 *
30 * Although there is only one ADC you can specify the input to it using the adc_select_input() function.
31 * In round robin mode (adc_rrobin()) will use that input and move to the next one after a read.
32 *
33 * User ADC inputs are on 0-3 (GPIO 26-29), the temperature sensor is on input 4.
34 *
35 * Temperature sensor values can be approximated in centigrade as:
36 *
37 * T = 27 - (ADC_Voltage - 0.706)/0.001721
38 *
39 * The FIFO, if used, can contain up to 4 entries.
40 *
41 * \subsection adc_example Example
42 * \addtogroup hardware_adc
43 *
44 * \include hello_adc.c
45 */
46
47 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_ADC, Enable/disable assertions in the ADC module, type=bool, default=0, group=hardware_adc
48 #ifndef PARAM_ASSERTIONS_ENABLED_ADC
49 #define PARAM_ASSERTIONS_ENABLED_ADC 0
50 #endif
51
52 #ifdef __cplusplus
53 extern "C" {
54 #endif
55
56 /*! \brief Initialise the ADC HW
57 * \ingroup hardware_adc
58 *
59 */
60 void adc_init(void);
61
62 /*! \brief Initialise the gpio for use as an ADC pin
63 * \ingroup hardware_adc
64 *
65 * Prepare a GPIO for use with ADC, by disabling all digital functions.
66 *
67 * \param gpio The GPIO number to use. Allowable GPIO numbers are 26 to 29 inclusive.
68 */
adc_gpio_init(uint gpio)69 static inline void adc_gpio_init(uint gpio) {
70 invalid_params_if(ADC, gpio < 26 || gpio > 29);
71 // Select NULL function to make output driver hi-Z
72 gpio_set_function(gpio, GPIO_FUNC_NULL);
73 // Also disable digital pulls and digital receiver
74 gpio_disable_pulls(gpio);
75 gpio_set_input_enabled(gpio, false);
76 }
77
78 /*! \brief ADC input select
79 * \ingroup hardware_adc
80 *
81 * Select an ADC input. 0...3 are GPIOs 26...29 respectively.
82 * Input 4 is the onboard temperature sensor.
83 *
84 * \param input Input to select.
85 */
adc_select_input(uint input)86 static inline void adc_select_input(uint input) {
87 invalid_params_if(ADC, input > 4);
88 hw_write_masked(&adc_hw->cs, input << ADC_CS_AINSEL_LSB, ADC_CS_AINSEL_BITS);
89 }
90
91 /*! \brief Round Robin sampling selector
92 * \ingroup hardware_adc
93 *
94 * This function sets which inputs are to be run through in round robin mode.
95 * Value between 0 and 0x1f (bit 0 to bit 4 for GPIO 26 to 29 and temperature sensor input respectively)
96 *
97 * \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.
98 */
adc_set_round_robin(uint input_mask)99 static inline void adc_set_round_robin(uint input_mask) {
100 invalid_params_if(ADC, input_mask & ~ADC_CS_RROBIN_BITS);
101 hw_write_masked(&adc_hw->cs, input_mask << ADC_CS_RROBIN_LSB, ADC_CS_RROBIN_BITS);
102 }
103
104 /*! \brief Enable the onboard temperature sensor
105 * \ingroup hardware_adc
106 *
107 * \param enable Set true to power on the onboard temperature sensor, false to power off.
108 *
109 */
adc_set_temp_sensor_enabled(bool enable)110 static inline void adc_set_temp_sensor_enabled(bool enable) {
111 if (enable)
112 hw_set_bits(&adc_hw->cs, ADC_CS_TS_EN_BITS);
113 else
114 hw_clear_bits(&adc_hw->cs, ADC_CS_TS_EN_BITS);
115 }
116
117 /*! \brief Perform a single conversion
118 * \ingroup hardware_adc
119 *
120 * Performs an ADC conversion, waits for the result, and then returns it.
121 *
122 * \return Result of the conversion.
123 */
adc_read(void)124 static inline uint16_t adc_read(void) {
125 hw_set_bits(&adc_hw->cs, ADC_CS_START_ONCE_BITS);
126
127 while (!(adc_hw->cs & ADC_CS_READY_BITS))
128 tight_loop_contents();
129
130 return adc_hw->result;
131 }
132
133 /*! \brief Enable or disable free-running sampling mode
134 * \ingroup hardware_adc
135 *
136 * \param run false to disable, true to enable free running conversion mode.
137 */
adc_run(bool run)138 static inline void adc_run(bool run) {
139 if (run)
140 hw_set_bits(&adc_hw->cs, ADC_CS_START_MANY_BITS);
141 else
142 hw_clear_bits(&adc_hw->cs, ADC_CS_START_MANY_BITS);
143 }
144
145 /*! \brief Set the ADC Clock divisor
146 * \ingroup hardware_adc
147 *
148 * Period of samples will be (1 + div) cycles on average. Note it takes 96 cycles to perform a conversion,
149 * so any period less than that will be clamped to 96.
150 *
151 * \param clkdiv If non-zero, conversion will be started at intervals rather than back to back.
152 */
adc_set_clkdiv(float clkdiv)153 static inline void adc_set_clkdiv(float clkdiv) {
154 invalid_params_if(ADC, clkdiv >= 1 << (ADC_DIV_INT_MSB - ADC_DIV_INT_LSB + 1));
155 adc_hw->div = (uint32_t)(clkdiv * (float) (1 << ADC_DIV_INT_LSB));
156 }
157
158 /*! \brief Setup the ADC FIFO
159 * \ingroup hardware_adc
160 *
161 * FIFO is 4 samples long, if a conversion is completed and the FIFO is full the result is dropped.
162 *
163 * \param en Enables write each conversion result to the FIFO
164 * \param dreq_en Enable DMA requests when FIFO contains data
165 * \param dreq_thresh Threshold for DMA requests/FIFO IRQ if enabled.
166 * \param err_in_fifo If enabled, bit 15 of the FIFO contains error flag for each sample
167 * \param byte_shift Shift FIFO contents to be one byte in size (for byte DMA) - enables DMA to byte buffers.
168 */
adc_fifo_setup(bool en,bool dreq_en,uint16_t dreq_thresh,bool err_in_fifo,bool byte_shift)169 static inline void adc_fifo_setup(bool en, bool dreq_en, uint16_t dreq_thresh, bool err_in_fifo, bool byte_shift) {
170 hw_write_masked(&adc_hw->fcs,
171 (!!en << ADC_FCS_EN_LSB) |
172 (!!dreq_en << ADC_FCS_DREQ_EN_LSB) |
173 (dreq_thresh << ADC_FCS_THRESH_LSB) |
174 (!!err_in_fifo << ADC_FCS_ERR_LSB) |
175 (!!byte_shift << ADC_FCS_SHIFT_LSB),
176 ADC_FCS_EN_BITS |
177 ADC_FCS_DREQ_EN_BITS |
178 ADC_FCS_THRESH_BITS |
179 ADC_FCS_ERR_BITS |
180 ADC_FCS_SHIFT_BITS
181 );
182 }
183
184 /*! \brief Check FIFO empty state
185 * \ingroup hardware_adc
186 *
187 * \return Returns true if the fifo is empty
188 */
adc_fifo_is_empty(void)189 static inline bool adc_fifo_is_empty(void) {
190 return !!(adc_hw->fcs & ADC_FCS_EMPTY_BITS);
191 }
192
193 /*! \brief Get number of entries in the ADC FIFO
194 * \ingroup hardware_adc
195 *
196 * The ADC FIFO is 4 entries long. This function will return how many samples are currently present.
197 */
adc_fifo_get_level(void)198 static inline uint8_t adc_fifo_get_level(void) {
199 return (adc_hw->fcs & ADC_FCS_LEVEL_BITS) >> ADC_FCS_LEVEL_LSB;
200 }
201
202 /*! \brief Get ADC result from FIFO
203 * \ingroup hardware_adc
204 *
205 * Pops the latest result from the ADC FIFO.
206 */
adc_fifo_get(void)207 static inline uint16_t adc_fifo_get(void) {
208 return adc_hw->fifo;
209 }
210
211 /*! \brief Wait for the ADC FIFO to have data.
212 * \ingroup hardware_adc
213 *
214 * Blocks until data is present in the FIFO
215 */
adc_fifo_get_blocking(void)216 static inline uint16_t adc_fifo_get_blocking(void) {
217 while (adc_fifo_is_empty())
218 tight_loop_contents();
219 return adc_hw->fifo;
220 }
221
222 /*! \brief Drain the ADC FIFO
223 * \ingroup hardware_adc
224 *
225 * Will wait for any conversion to complete then drain the FIFO discarding any results.
226 */
adc_fifo_drain(void)227 static inline void adc_fifo_drain(void) {
228 // Potentially there is still a conversion in progress -- wait for this to complete before draining
229 while (!(adc_hw->cs & ADC_CS_READY_BITS))
230 tight_loop_contents();
231 while (!adc_fifo_is_empty())
232 (void) adc_fifo_get();
233 }
234
235 /*! \brief Enable/Disable ADC interrupts.
236 * \ingroup hardware_adc
237 *
238 * \param enabled Set to true to enable the ADC interrupts, false to disable
239 */
adc_irq_set_enabled(bool enabled)240 static inline void adc_irq_set_enabled(bool enabled) {
241 adc_hw->inte = !!enabled;
242 }
243
244 #ifdef __cplusplus
245 }
246 #endif
247
248 #endif
249