From 8ae64967930f2bfe27a6487183d3e55bb03f60b9 Mon Sep 17 00:00:00 2001 From: Nikolay Dimitrov Date: Tue, 12 Aug 2025 05:06:40 +0300 Subject: [PATCH] rp2/machine_pin: Add support for setting pin drive strength and slew rate Currently when micropython uses GPIO pins as outputs, their actual configuration for pin drive strength and slew rate is the default one, which was set at power-on/reset (4mA, slow slew rate). There's no way to change these pin configurations in the micropython environment (other than low-level hacks with mem32[]). This patch adds the ability to explicitly configure rp2040/rp2350 output pins' drive strength and slew rate, if/when needed, like this: pin1 = Pin(1, Pin.OUT, drive=Pin.DRIVE_12, slew=Pin.SLEW_FAST) As the new drive/slew parameters are optional kwargs, all existing code and examples will still work as expected, like this: pin2 = Pin(2, Pin.OUT) All relevant documentation files are updated. Tested on rpi-pico and rpi-pico2 boards. Signed-off-by: Nikolay Dimitrov --- docs/library/machine.Pin.rst | 27 ++++++++++++------ docs/rp2/quickref.rst | 3 ++ ports/rp2/machine_pin.c | 55 ++++++++++++++++++++++++++++++++++-- 3 files changed, 75 insertions(+), 10 deletions(-) diff --git a/docs/library/machine.Pin.rst b/docs/library/machine.Pin.rst index 4aac0f4b94be2..7ccdd1c3c39e1 100644 --- a/docs/library/machine.Pin.rst +++ b/docs/library/machine.Pin.rst @@ -42,7 +42,7 @@ Usage Model:: Constructors ------------ -.. class:: Pin(id, mode=-1, pull=-1, *, value=None, drive=0, alt=-1) +.. class:: Pin(id, mode=-1, pull=-1, *, value=None, drive=-1, alt=-1, slew=-1) Access the pin peripheral (GPIO pin) associated with the given ``id``. If additional arguments are given in the constructor then they are used to initialise @@ -87,9 +87,10 @@ Constructors output pin value if given, otherwise the state of the pin peripheral remains unchanged. - - ``drive`` specifies the output power of the pin and can be one of: ``Pin.DRIVE_0``, - ``Pin.DRIVE_1``, etc., increasing in drive strength. The actual current driving - capabilities are port dependent. Not all ports implement this argument. + - ``drive`` specifies the drive strength of the pin (applicable only for output pins) + and can be one of: ``Pin.DRIVE_0``, ``Pin.DRIVE_1``, etc., increasing in drive strength. + The actual current driving capabilities are port dependent. Not all ports implement this + argument. - ``alt`` specifies an alternate function for the pin and the values it can take are port dependent. This argument is valid only for ``Pin.ALT`` and ``Pin.ALT_OPEN_DRAIN`` @@ -97,6 +98,10 @@ Constructors one pin alternate function is supported the this argument is not required. Not all ports implement this argument. + - ``slew`` specifies the slew rate of the pin (applicable only for output pins) and can + be one of: ``Pin.SLEW_SLOW``, ``Pin.SLEW_FAST``, etc. The actual slew rate capabilities + are port dependent. Not all ports implement this argument. + As specified above, the Pin class allows to set an alternate function for a particular pin, but it does not specify any further operations on such a pin. Pins configured in alternate-function mode are usually not used as GPIO but are instead driven by other @@ -108,7 +113,7 @@ Constructors Methods ------- -.. method:: Pin.init(mode=-1, pull=-1, *, value=None, drive=0, alt=-1) +.. method:: Pin.init(mode=-1, pull=-1, *, value=None, drive=-1, alt=-1, slew=-1) Re-initialise the pin using the given parameters. Only those arguments that are specified will be set. The rest of the pin peripheral state will remain @@ -270,9 +275,15 @@ not all constants are available on all ports. Pin.DRIVE_1 Pin.DRIVE_2 - Selects the pin drive strength. A port may define additional drive - constants with increasing number corresponding to increasing drive - strength. + Selects the output pin drive strength. A port may define additional or + entirely custom drive constants with increasing number corresponding to + increasing drive strength. + +.. data:: Pin.SLEW_SLOW + Pin.SLEW_FAST + + Selects the output pin slew rate. A port may define additional or entirely + custom drive constants with constant corresponding to specific slew rate. .. data:: Pin.IRQ_FALLING Pin.IRQ_RISING diff --git a/docs/rp2/quickref.rst b/docs/rp2/quickref.rst index ec31442990ff4..155175a304ae1 100644 --- a/docs/rp2/quickref.rst +++ b/docs/rp2/quickref.rst @@ -162,6 +162,9 @@ Use the :ref:`machine.Pin ` class:: p4 = Pin(4, Pin.IN, Pin.PULL_UP) # enable internal pull-up resistor p5 = Pin(5, Pin.OUT, value=1) # set pin high on creation + # create output pin on GPIO6 with 8mA drive strength and slow slew rate + p6 = Pin(6, Pin.OUT, drive=Pin.DRIVE_8, slew=Pin.SLEW_SLOW) + Programmable IO (PIO) --------------------- diff --git a/ports/rp2/machine_pin.c b/ports/rp2/machine_pin.c index 7a2de0c0bc690..09689bbc97f20 100644 --- a/ports/rp2/machine_pin.c +++ b/ports/rp2/machine_pin.c @@ -195,11 +195,26 @@ static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin machine_pin_obj_t *self = self_in; uint funcsel = gpio_get_function(self->id); qstr mode_qst; + bool is_output; + mp_uint_t drive; + mp_uint_t slew; + static const qstr drive2str[] = { + [GPIO_DRIVE_STRENGTH_2MA] = MP_QSTR_DRIVE_2, + [GPIO_DRIVE_STRENGTH_4MA] = MP_QSTR_DRIVE_4, + [GPIO_DRIVE_STRENGTH_8MA] = MP_QSTR_DRIVE_8, + [GPIO_DRIVE_STRENGTH_12MA] = MP_QSTR_DRIVE_12 + }; + static const qstr slew2str[] = { + [GPIO_SLEW_RATE_SLOW] = MP_QSTR_SLEW_SLOW, + [GPIO_SLEW_RATE_FAST] = MP_QSTR_SLEW_FAST, + }; + if (!is_ext_pin(self)) { + is_output = gpio_is_dir_out(self->id); if (funcsel == GPIO_FUNC_SIO) { if (GPIO_IS_OPEN_DRAIN(self->id)) { mode_qst = MP_QSTR_OPEN_DRAIN; - } else if (gpio_is_dir_out(self->id)) { + } else if (is_output) { mode_qst = MP_QSTR_OUT; } else { mode_qst = MP_QSTR_IN; @@ -220,6 +235,12 @@ static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin mp_printf(print, ", pull=%q", MP_QSTR_PULL_DOWN); } } + + if (is_output) { + drive = (mp_uint_t)gpio_get_drive_strength(self->id); + mp_printf(print, ", drive=%q", drive2str[drive]); + } + if (funcsel != GPIO_FUNC_SIO) { const machine_pin_af_obj_t *af = machine_pin_find_alt_by_index(self, funcsel); if (af == NULL) { @@ -228,6 +249,11 @@ static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin mp_printf(print, ", alt=%q", af->name); } } + + if (is_output) { + slew = gpio_get_slew_rate(self->id); + mp_printf(print, ", slew=%q", slew2str[slew]); + } } else { #if MICROPY_HW_PIN_EXT_COUNT mode_qst = (self->is_output) ? MP_QSTR_OUT : MP_QSTR_IN; @@ -238,13 +264,20 @@ static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin } enum { - ARG_mode, ARG_pull, ARG_value, ARG_alt + ARG_mode, + ARG_pull, + ARG_value, + ARG_drive, + ARG_alt, + ARG_slew, }; static const mp_arg_t allowed_args[] = { {MP_QSTR_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, {MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, {MP_QSTR_value, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE}}, + {MP_QSTR_drive, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1}}, {MP_QSTR_alt, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = GPIO_FUNC_SIO}}, + {MP_QSTR_slew, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1}}, }; static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -284,6 +317,18 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ gpio_put(self->id, value); } mp_hal_pin_output(self->id); + + // Configure drive strength + mp_uint_t drive = args[ARG_drive].u_int; + if (drive != -1) { + gpio_set_drive_strength(self->id, drive); + } + + // Configure slew rate + mp_uint_t slew = args[ARG_slew].u_int; + if (slew != -1) { + gpio_set_slew_rate(self->id, slew); + } } else if (mode == MACHINE_PIN_MODE_OPEN_DRAIN) { mp_hal_pin_open_drain_with_value(self->id, value == -1 ? 1 : value); } else { @@ -504,6 +549,12 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_ALT), MP_ROM_INT(MACHINE_PIN_MODE_ALT) }, { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULL_UP) }, { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULL_DOWN) }, + { MP_ROM_QSTR(MP_QSTR_DRIVE_2), MP_ROM_INT(GPIO_DRIVE_STRENGTH_2MA) }, + { MP_ROM_QSTR(MP_QSTR_DRIVE_4), MP_ROM_INT(GPIO_DRIVE_STRENGTH_4MA) }, + { MP_ROM_QSTR(MP_QSTR_DRIVE_8), MP_ROM_INT(GPIO_DRIVE_STRENGTH_8MA) }, + { MP_ROM_QSTR(MP_QSTR_DRIVE_12), MP_ROM_INT(GPIO_DRIVE_STRENGTH_12MA) }, + { MP_ROM_QSTR(MP_QSTR_SLEW_SLOW), MP_ROM_INT(GPIO_SLEW_RATE_SLOW) }, + { MP_ROM_QSTR(MP_QSTR_SLEW_FAST), MP_ROM_INT(GPIO_SLEW_RATE_FAST) }, { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_IRQ_EDGE_RISE) }, { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(GPIO_IRQ_EDGE_FALL) },