Skip to content

Commit a9425cd

Browse files
committed
digitalio: Improve DigitalInOut constructor, add finaliser
Now it's possible to construct outputs or enable pulls immediately: ```py >>> d = digitalio.DigitalInOut(board.A0, pull=digitalio.Pull.UP) >>> d.pull digitalio.Pull.UP ``` ```py >>> d = digitalio.DigitalInOut(board.A0, value=True) >>> d.direction digitalio.Direction.OUTPUT >>> d.value True ``` ```py >>> d = digitalio.DigitalInOut(board.A0, value=False, drive_mode=digitalio.DriveMode.OPEN_DRAIN) >>> d.drive_mode digitalio.DriveMode.OPEN_DRAIN ``` The finaliser means that if a pin object is allowed to go out of scope, then it will be deinitialized automatically when GC runs. **this is a potential incompatibility**: some hypothetical code that intentionally leaked a pin might function differently, i.e. ```py digitalio.DigitalInOut(board.A0).switch_to_output(True) ``` as a way to stick a pin in output mode indefinitely will no longer work (but may still appear to work "for awhile" because gc doesn't run right away). I made this change in part because it simplifies error handling in the constructor: An incorrect argument would otherwise have left the pin stuck in allocated state until the interpreter was reset; with this change, it's available again though not until the GC runs so it would remain perplexing in interactive situations: ```py >>> d = digitalio.DigitalInOut(board.A0, pull=7) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: pull must be of type Pull or None, not int >>> d = digitalio.DigitalInOut(board.A0) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: A0 in use >>> gc.collect() >>> d = digitalio.DigitalInOut(board.A0) >>> d <DigitalInOut> ```
1 parent 3a0b97d commit a9425cd

File tree

1 file changed

+41
-8
lines changed

1 file changed

+41
-8
lines changed

shared-bindings/digitalio/DigitalInOut.c

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
#include "bindings/cyw43/__init__.h"
2727
#endif
2828

29+
static mp_obj_t digitalio_digitalinout_switch_to_output(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
30+
static mp_obj_t digitalio_digitalinout_switch_to_input(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args);
31+
2932
static void check_result(digitalinout_result_t result) {
3033
switch (result) {
3134
case DIGITALINOUT_OK:
@@ -58,22 +61,51 @@ MP_WEAK const mcu_pin_obj_t *common_hal_digitalio_validate_pin(mp_obj_t obj) {
5861
//| a pin, see the :py:class:`analogio.AnalogIn` and
5962
//| :py:class:`analogio.AnalogOut` classes."""
6063
//|
61-
//| def __init__(self, pin: microcontroller.Pin) -> None:
64+
//| def __init__(
65+
//| self,
66+
//| pin: microcontroller.Pin,
67+
//| *,
68+
//| pull: Pull | None = None,
69+
//| value: bool | None,
70+
//| drive_mode: DriveMode = DriveMode.PUSH_PULL
71+
//| ) -> None:
6272
//| """Create a new DigitalInOut object associated with the pin. Defaults to input
63-
//| with no pull. Use :py:meth:`switch_to_input` and
64-
//| :py:meth:`switch_to_output` to change the direction.
73+
//| with no pull.
74+
//|
75+
//| If `value` is specified then the initial direction is output and
76+
//| specified `drive_mode` is used.
6577
//|
66-
//| :param ~microcontroller.Pin pin: The pin to control"""
78+
//| :param ~microcontroller.Pin pin: The pin to control
79+
//| :param ~Pull pull: The pull resistor setting
80+
//| :param bool value: The initial value. If not `None`, sets pin to output mode.
81+
//| :param DriveMode drive_mode: The initial drive mode, if `value` is specified
82+
//|
83+
//| """
6784
//| ...
6885
static mp_obj_t digitalio_digitalinout_make_new(const mp_obj_type_t *type,
69-
size_t n_args, size_t n_kw, const mp_obj_t *args) {
70-
mp_arg_check_num(n_args, n_kw, 1, 1, false);
86+
size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
87+
enum { ARG_pin, ARG_pull, ARG_value, ARG_drive_mode };
88+
static const mp_arg_t allowed_args[] = {
89+
{ MP_QSTR_pin, MP_ARG_OBJ | MP_ARG_REQUIRED, {}},
90+
{ MP_QSTR_pull, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = MP_ROM_NONE } },
91+
{ MP_QSTR_value, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = MP_ROM_NONE } },
92+
{ MP_QSTR_drive_mode, MP_ARG_OBJ | MP_ARG_KW_ONLY, { .u_obj = MP_ROM_NONE } },
93+
};
94+
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
95+
mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
7196

72-
digitalio_digitalinout_obj_t *self = mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type);
97+
digitalio_digitalinout_obj_t *self = mp_obj_malloc_with_finaliser(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type);
7398

74-
const mcu_pin_obj_t *pin = common_hal_digitalio_validate_pin(args[0]);
99+
const mcu_pin_obj_t *pin = common_hal_digitalio_validate_pin(all_args[0]);
75100
common_hal_digitalio_digitalinout_construct(self, pin);
76101

102+
if (args[ARG_value].u_obj != MP_ROM_NONE) {
103+
mp_obj_t args1[] = { MP_OBJ_FROM_PTR(self), args[ARG_value].u_obj, args[ARG_drive_mode].u_obj};
104+
digitalio_digitalinout_switch_to_output(3, args1, NULL);
105+
} else if (args[ARG_pull].u_obj != MP_ROM_NONE) {
106+
mp_obj_t args1[] = { MP_OBJ_FROM_PTR(self), args[ARG_pull].u_obj};
107+
digitalio_digitalinout_switch_to_input(2, args1, NULL);
108+
}
77109
return MP_OBJ_FROM_PTR(self);
78110
}
79111

@@ -325,6 +357,7 @@ MP_PROPERTY_GETSET(digitalio_digitalio_pull_obj,
325357
static const mp_rom_map_elem_t digitalio_digitalinout_locals_dict_table[] = {
326358
// instance methods
327359
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&digitalio_digitalinout_deinit_obj) },
360+
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&digitalio_digitalinout_deinit_obj) },
328361
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) },
329362
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&digitalio_digitalinout_obj___exit___obj) },
330363
{ MP_ROM_QSTR(MP_QSTR_switch_to_output), MP_ROM_PTR(&digitalio_digitalinout_switch_to_output_obj) },

0 commit comments

Comments
 (0)