1 /*
2  * SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #pragma once
8 
9 #if __cpp_exceptions
10 
11 #include "esp_exception.hpp"
12 #include "system_cxx.hpp"
13 
14 namespace idf {
15 
16 /**
17  * @brief Exception thrown for errors in the GPIO C++ API.
18  */
19 struct GPIOException : public ESPException {
20     /**
21      * @param error The IDF error representing the error class of the error to throw.
22      */
23     GPIOException(esp_err_t error);
24 };
25 
26 /**
27  * Check if the numeric pin number is valid on the current hardware.
28  */
29 esp_err_t check_gpio_pin_num(uint32_t pin_num) noexcept;
30 
31 /**
32  * Check if the numeric value of a drive strength is valid on the current hardware.
33  */
34 esp_err_t check_gpio_drive_strength(uint32_t strength) noexcept;
35 
36 /**
37  * This is a "Strong Value Type" class for GPIO. The GPIO pin number is checked during construction according to
38  * the hardware capabilities. This means that any GPIONumBase object is guaranteed to contain a valid GPIO number.
39  * See also the template class \c StrongValue.
40  */
41 template<typename GPIONumFinalType>
42 class GPIONumBase final : public StrongValueComparable<uint32_t> {
43 public:
44     /**
45      * @brief Create a numerical pin number representation and make sure it's correct.
46      *
47      * @throw GPIOException if the number does not reflect a valid GPIO number on the current hardware.
48      */
GPIONumBase(uint32_t pin)49     GPIONumBase(uint32_t pin) : StrongValueComparable<uint32_t>(pin)
50     {
51         esp_err_t pin_check_result = check_gpio_pin_num(pin);
52         if (pin_check_result != ESP_OK) {
53             throw GPIOException(pin_check_result);
54         }
55     }
56 
57     using StrongValueComparable<uint32_t>::operator==;
58     using StrongValueComparable<uint32_t>::operator!=;
59 
60     /**
61      * Retrieves the valid numerical representation of the GPIO number.
62      */
get_num() const63     uint32_t get_num() const { return get_value(); };
64 };
65 
66 /**
67  * This is a TAG type whose sole purpose is to create a distinct type from GPIONumBase.
68  */
69 class GPIONumType;
70 
71 /**
72  * A GPIO number type used for general GPIOs, in contrast to specific GPIO pins like e.g. SPI_SCLK.
73  */
74 using GPIONum = GPIONumBase<class GPIONumType>;
75 
76 /**
77  * Level of an input GPIO.
78  */
79 enum class GPIOLevel {
80     HIGH,
81     LOW
82 };
83 
84 /**
85  * Represents a valid pull up configuration for GPIOs.
86  * It is supposed to resemble an enum type, hence it has static creation methods and a private constructor.
87  * This class is a "Strong Value Type", see also the template class \c StrongValue for more properties.
88  */
89 class GPIOPullMode final : public StrongValueComparable<uint32_t> {
90 private:
91     /**
92      * Constructor is private since it should only be accessed by the static creation methods.
93      *
94      * @param pull_mode A valid numerical respresentation of the pull up configuration. Must be valid!
95      */
GPIOPullMode(uint32_t pull_mode)96     GPIOPullMode(uint32_t pull_mode) : StrongValueComparable<uint32_t>(pull_mode) { }
97 
98 public:
99     /**
100      * Create a representation of a floating pin configuration.
101      * For more information, check the driver and HAL files.
102      */
103     static GPIOPullMode FLOATING();
104 
105     /**
106      * Create a representation of a pullup configuration.
107      * For more information, check the driver and HAL files.
108      */
109     static GPIOPullMode PULLUP();
110 
111     /**
112      * Create a representation of a pulldown configuration.
113      * For more information, check the driver and HAL files.
114      */
115     static GPIOPullMode PULLDOWN();
116 
117     using StrongValueComparable<uint32_t>::operator==;
118     using StrongValueComparable<uint32_t>::operator!=;
119 
120     /**
121      * Retrieves the valid numerical representation of the pull mode.
122      */
get_pull_mode() const123     uint32_t get_pull_mode() const { return get_value(); };
124 };
125 
126 /**
127  * @brief Represents a valid wakup interrupt type for GPIO inputs.
128  *
129  * This class is a "Strong Value Type", see also the template class \c StrongValue for more properties.
130  * It is supposed to resemble an enum type, hence it has static creation methods and a private constructor.
131  * For a detailed mapping of interrupt types to numeric values, please refer to the driver types and implementation.
132  */
133 class GPIOWakeupIntrType final: public StrongValueComparable<uint32_t> {
134 private:
135     /**
136      * Constructor is private since it should only be accessed by the static creation methods.
137      *
138      * @param pull_mode A valid numerical respresentation of a possible interrupt level to wake up. Must be valid!
139      */
GPIOWakeupIntrType(uint32_t interrupt_level)140     GPIOWakeupIntrType(uint32_t interrupt_level) : StrongValueComparable<uint32_t>(interrupt_level) { }
141 
142 public:
143     static GPIOWakeupIntrType LOW_LEVEL();
144     static GPIOWakeupIntrType HIGH_LEVEL();
145 
146     /**
147      * Retrieves the valid numerical representation of the pull mode.
148      */
get_level() const149     uint32_t get_level() const noexcept { return get_value(); };
150 };
151 
152 /**
153  * Class representing a valid drive strength for GPIO outputs.
154  * This class is a "Strong Value Type", see also the template class \c StrongValue for more properties.
155  * For a detailed mapping for values to drive strengths, please refer to the datasheet of the chip you are using.
156  * E.g. for ESP32, the values in general are the following:
157  *  - WEAK:             5mA
158  *  - STRONGER:        10mA
159  *  - DEFAULT/MEDIUM:  20mA
160  *  - STRONGEST:       40mA
161  */
162 class GPIODriveStrength final : public StrongValueComparable<uint32_t> {
163 public:
164     /**
165      * @brief Create a drive strength representation and checks its validity.
166      *
167      * After construction, this class should have a guaranteed valid strength representation.
168      *
169      * @param strength the numeric value mapping for a particular strength. For possible ranges, look at the
170      *                  static creation functions below.
171      * @throws GPIOException if the supplied number is out of the hardware capable range.
172      */
GPIODriveStrength(uint32_t strength)173     GPIODriveStrength(uint32_t strength) : StrongValueComparable<uint32_t>(strength)
174     {
175         esp_err_t strength_check_result = check_gpio_drive_strength(strength);
176         if (strength_check_result != ESP_OK) {
177             throw GPIOException(strength_check_result);
178         }
179     }
180 
181     /**
182      * Create a representation of the default drive strength.
183      * For more information, check the datasheet and driver and HAL files.
184      */
185     static GPIODriveStrength DEFAULT();
186 
187     /**
188      * Create a representation of the weak drive strength.
189      * For more information, check the datasheet and driver and HAL files.
190      */
191     static GPIODriveStrength WEAK();
192 
193     /**
194      * Create a representation of the less weak drive strength.
195      * For more information, check the datasheet and driver and HAL files.
196      */
197     static GPIODriveStrength LESS_WEAK();
198 
199     /**
200      * Create a representation of the medium drive strength.
201      * For more information, check the datasheet and driver and HAL files.
202      */
203     static GPIODriveStrength MEDIUM();
204 
205     /**
206      * Create a representation of the strong drive strength.
207      */
208     static GPIODriveStrength STRONGEST();
209 
210     using StrongValueComparable<uint32_t>::operator==;
211     using StrongValueComparable<uint32_t>::operator!=;
212 
213     /**
214      * Retrieves the valid numerical representation of the drive strength.
215      */
get_strength() const216     uint32_t get_strength() const { return get_value(); };
217 
218 };
219 
220 /**
221  * @brief Implementations commonly used functionality for all GPIO configurations.
222  *
223  * Some functionality is only for specific configurations (set and get drive strength) but is necessary here
224  * to avoid complicating the inheritance hierarchy of the GPIO classes.
225  * Child classes implementing any GPIO configuration (output, input, etc.) are meant to intherit from this class
226  * and possibly make some of the functionality publicly available.
227  */
228 class GPIOBase {
229 protected:
230     /**
231      * @brief Construct a GPIO.
232      *
233      * This constructor will only reset the GPIO but leaves the actual configuration (input, output, etc.) to
234      * the sub class.
235      *
236      * @param num GPIO pin number of the GPIO to be configured.
237      *
238      * @throws GPIOException
239      *              - if the underlying driver function fails
240      */
241     GPIOBase(GPIONum num);
242 
243     /**
244      * @brief Enable gpio pad hold function.
245      *
246      * The gpio pad hold function works in both input and output modes, but must be output-capable gpios.
247      * If pad hold enabled:
248      *   in output mode: the output level of the pad will be force locked and can not be changed.
249      *   in input mode: the input value read will not change, regardless the changes of input signal.
250      *
251      * @throws GPIOException if the underlying driver function fails.
252      */
253     void hold_en();
254 
255     /**
256      * @brief Disable gpio pad hold function.
257      *
258      * @throws GPIOException if the underlying driver function fails.
259      */
260     void hold_dis();
261 
262     /**
263      * @brief Configure the drive strength of the GPIO.
264      *
265      * @param strength The drive strength. Refer to \c GPIODriveStrength for more details.
266      *
267      * @throws GPIOException if the underlying driver function fails.
268      */
269     void set_drive_strength(GPIODriveStrength strength);
270 
271     /**
272      * @brief Return the current drive strength of the GPIO.
273      *
274      * @return The currently configured drive strength. Refer to \c GPIODriveStrength for more details.
275      *
276      * @throws GPIOException if the underlying driver function fails.
277      */
278     GPIODriveStrength get_drive_strength();
279 
280     /**
281      * @brief The number of the configured GPIO pin.
282      */
283     GPIONum gpio_num;
284 };
285 
286 /**
287  * @brief This class represents a GPIO which is configured as output.
288  */
289 class GPIO_Output : public GPIOBase {
290 public:
291     /**
292      * @brief Construct and configure a GPIO as output.
293      *
294      * @param num GPIO pin number of the GPIO to be configured.
295      *
296      * @throws GPIOException
297      *              - if the underlying driver function fails
298      */
299     GPIO_Output(GPIONum num);
300 
301     /**
302      * @brief Set GPIO to high level.
303      *
304      * @throws GPIOException if the underlying driver function fails.
305      */
306     void set_high();
307 
308     /**
309      * @brief Set GPIO to low level.
310      *
311      * @throws GPIOException if the underlying driver function fails.
312      */
313     void set_low();
314 
315     using GPIOBase::set_drive_strength;
316     using GPIOBase::get_drive_strength;
317 };
318 
319 /**
320  * @brief This class represents a GPIO which is configured as input.
321  */
322 class GPIOInput : public GPIOBase {
323 public:
324     /**
325      * @brief Construct and configure a GPIO as input.
326      *
327      * @param num GPIO pin number of the GPIO to be configured.
328      *
329      * @throws GPIOException
330      *              - if the underlying driver function fails
331      */
332     GPIOInput(GPIONum num);
333 
334     /**
335      * @brief Read the current level of the GPIO.
336      *
337      * @return The GPIO current level of the GPIO.
338      */
339     GPIOLevel get_level() const noexcept;
340 
341     /**
342      * @brief Configure the internal pull-up and pull-down restors.
343      *
344      * @param mode The pull-up/pull-down configuration see \c GPIOPullMode.
345      *
346      * @throws GPIOException if the underlying driver function fails.
347      */
348     void set_pull_mode(GPIOPullMode mode);
349 
350     /**
351      * @brief Configure the pin as wake up pin.
352      *
353      * @throws GPIOException if the underlying driver function fails.
354      */
355     void wakeup_enable(GPIOWakeupIntrType interrupt_type);
356 
357     /**
358      * @brief Disable wake up functionality for this pin if it was enabled before.
359      *
360      * @throws GPIOException if the underlying driver function fails.
361      */
362     void wakeup_disable();
363 };
364 
365 /**
366  * @brief This class represents a GPIO which is configured as open drain output and input at the same time.
367  *
368  * This class facilitates bit-banging for single wire protocols.
369  */
370 class GPIO_OpenDrain : public GPIOInput {
371 public:
372     /**
373      * @brief Construct and configure a GPIO as open drain output as well as input.
374      *
375      * @param num GPIO pin number of the GPIO to be configured.
376      *
377      * @throws GPIOException
378      *              - if the underlying driver function fails
379      */
380     GPIO_OpenDrain(GPIONum num);
381 
382     /**
383      * @brief Set GPIO to floating level.
384      *
385      * @throws GPIOException if the underlying driver function fails.
386      */
387     void set_floating();
388 
389     /**
390      * @brief Set GPIO to low level.
391      *
392      * @throws GPIOException if the underlying driver function fails.
393      */
394     void set_low();
395 
396     using GPIOBase::set_drive_strength;
397     using GPIOBase::get_drive_strength;
398 };
399 
400 }
401 
402 #endif
403