Skip to content

Added a function to allow inverting the output from a GPIO pin #3039

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions cores/rp2040/Arduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ void analogWriteFreq(uint32_t freq);
void analogWriteRange(uint32_t range);
void analogWriteResolution(int res);

// GPIO RP2040-specific calls
void setPinInvert(pin_size_t ulPin, bool invert);

#ifdef __cplusplus
} // extern "C"
#endif
Expand Down
17 changes: 15 additions & 2 deletions cores/rp2040/wiring_digital.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,27 @@ extern void __clearADCPin(pin_size_t p);

static PinMode _pm[__GPIOCNT];

extern "C" void setPinInvert(pin_size_t ulPin, bool invert) __attribute__((weak, alias("__setPinInvert")));
extern "C" void __setPinInvert(pin_size_t ulPin, bool invert) {
if (ulPin >= __GPIOCNT) {
DEBUGCORE("ERROR: Illegal pin in setPinInvert (%d)\n", ulPin);
return;
}

if (invert)
gpio_set_outover(ulPin, GPIO_OVERRIDE_INVERT);
else
gpio_set_outover(ulPin, GPIO_OVERRIDE_NORMAL);
}

extern "C" void pinMode(pin_size_t ulPin, PinMode ulMode) __attribute__((weak, alias("__pinMode")));
extern "C" void __pinMode(pin_size_t ulPin, PinMode ulMode) {
if (ulPin >= __GPIOCNT) {
DEBUGCORE("ERROR: Illegal pin in pinMode (%d)\n", ulPin);
return;
}


gpio_set_function(ulPin, GPIO_FUNC_SIO);
switch (ulMode) {
case INPUT:
gpio_init(ulPin);
Expand Down Expand Up @@ -90,7 +104,6 @@ extern "C" void __digitalWrite(pin_size_t ulPin, PinStatus ulVal) {
DEBUGCORE("ERROR: Illegal pin in pinMode (%d)\n", ulPin);
return;
}
gpio_set_function(ulPin, GPIO_FUNC_SIO);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While adding this in pinMode seems fine, I think deleting here will break the case where you have someone else controlling the pin (i.e. tone which uses PIO) and then you stop that process and then digitalWrite. Let me whip up a simple test case...

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, this breaks normal functionality. Can you re-add the line?

Simple example blinks after analogWrite with master, remains PWN (connected to PIO) with this patch

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  analogWriteRange(100);
  analogWrite(LED_BUILTIN, 50);
  delay(1000);
  analogWrite(LED_BUILTIN, 70);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
  digitalWrite(LED_BUILTIN, HIGH);
}

void loop() {
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
  digitalWrite(LED_BUILTIN, HIGH);
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gpio_set_function clears the override register so I will need to save the state somehow. I'll get back....

if (_pm[ulPin] == INPUT_PULLDOWN) {
if (ulVal == LOW) {
gpio_set_dir(ulPin, false);
Expand Down
28 changes: 28 additions & 0 deletions docs/digital.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,34 @@ Output Modes (Pad Strength)
---------------------------
The Raspberry Pi Pico has the ability to set the current that a pin (actually the pad associated with it) is capable of supplying. The current can be set to values of 2mA, 4mA, 8mA and 12mA. By default, on a reset, the setting is 4mA. A `pinMode(x, OUTPUT)`, where `x` is the pin number, is also the default setting. 4 settings have been added for use with `pinMode`: `OUTPUT_2MA`, `OUTPUT_4MA`, which has the same behavior as `OUTPUT`, `OUTPUT_8MA` and `OUTPUT_12MA`.

GPIO Output Inversion
---------------------
The RP2040 and RP2350 microcontrollers support configurable output inversion on all GPIO pins.
This feature allows each GPIO to output either the logical value or its inverted equivalent,
without requiring changes to the underlying logic of your application.

This is especially useful when adapting existing libraries or hardware that use opposite logic levels.
By configuring inversion at the GPIO level, you can achieve compatibility without rewriting code.

Function
--------

.. code-block:: c

void setPinInvert(uint pin, bool invert);

**Parameters:**

- ``pin``: The GPIO number to configure.
- ``invert``:

- ``true`` — Enables output inversion (logical 1 becomes 0, and vice versa).
- ``false`` — Disables inversion, restoring default non-inverted output.

.. note::

All GPIO pins are configured as non-inverting outputs by default.

Tone/noTone
-----------
Simple square wave tone generation is possible for up to 8 channels using
Expand Down
37 changes: 37 additions & 0 deletions libraries/rp2040/examples/GPIOInvert/GPIOInvert.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@

/*
GPIOInvert

This example shows how to enable the pin output inversion mechanism for a GPIO pin.

This example code is in the public domain.
*/

int led = LED_BUILTIN; // the pin the LED is attached to

// the setup routine runs once when you press reset:
void setup() {
// declare pin to be an output:
pinMode(led, OUTPUT);
// Now enable the pin inversion function.
// This must be done after the pinMode command.
setPinInvert(led, true);

while(!Serial)
delay(10);

Serial.begin(115200);
}

// the loop routine runs over and over again forever:
void loop() {
// Turn the LED off
digitalWrite(led, HIGH);
Serial.println("LED off");
delay(2000);

// Turn the LED on
digitalWrite(led, LOW);
Serial.println("LED on");
delay(2000);
}
Loading