Does digitalRead() on an output pin return the last state it was set to?

Toggling a pin. Would this work reliably?

digitalWrite(outPin,!digitalRead(outPin));

-jim lee

1 Like

I don't know, I'd just go for Direct Port Manipulation: write a 1 to the Input Port to toggle the pin. For example, toggling D2:

PIND = PIND | 0b00000100;

Can toggle an output at up to 2 MHz doing that. while (1){ PIND = PIND | 0b00000100; } Course, not much else will be happening.

Way faster then doing a digitalRead and then a digitalWrite.

Yes, reading an output pin returns the state of the pin (the last thing that was written to the pin).

Thanks!

-jim lee

CrossRoads: I don't know, I'd just go for Direct Port Manipulation: write a 1 to the Input Port to toggle the pin. For example, toggling D2:

PIND = PIND | 0b00000100;

This may toggle (respective zero) way more bits than you want, because the bits toggle when written with a one.

So the correct way for a fast toggle would be

PIND = 0b00000100;

https://forum.arduino.cc/index.php?topic=118155.15

18.2.2. Toggling the Pin Writing a '1' to PINxn toggles the value of PORTxn, independent on the value of DDRxn. The SBI instruction can be used to toggle one single bit in a port.

This works on AVR but not necessarily on every architecture. I don't own one but it was reported it doesn't work on the Arduino Zero: https://groups.google.com/a/arduino.cc/d/msg/developers/81p19Lkf-zM/FjNezwZWBgAJ so I guess it depends on what you mean by "reliably".

My point was that PIND = PIND | 0b00000100; does not work on AVRs.

Whandall: My point was that PIND = PIND | 0b00000100; does not work on AVRs.

Was that reply directed to me? My reply was regarding using digitalRead() to determine the state of an output pin.

digitalRead on an output pin DOES NOT WORK CORRECTLY on an ESP8266
see http://forum.arduino.cc/index.php?topic=41954.0 for a solution.
The code int digitalReadOutputPin(uint8_t pin) { … } works perfectly on a WEMOS D1 mini.

I seem to remember something about ESP8266 was mentioned in the Arduino Developers mailing list discussion I linked above. A little while ago when this topic came up again, I did some tests with the various boards I have on hand and I didn't find it to not work on any of them. I know I did it with AVR and ESP8266 and probably ESP32 but I can't remember whether I had a SAMD board to test with at that time.

I'd be interested to hear more details on it not working on the ESP8266. Might you provide a demonstration sketch?

Even if it does work, I will still always just store the pin state in a variable.

I wonder why I asked that to begin with?

-jim lee

This is probably nit picking, but since digitalRead does not return a bool but an int, the correct syntax for inverting the state of a digital pin would be:

digitalWrite(pin, digitalRead(pin) ^ 1);

PaulS: Yes, reading an output pin returns the state of the pin (the last thing that was written to the pin).

Not actually true (if using digitalRead())

Reading a pin determines if its voltage is HIGH or LOW at the time of the call. If you short-circuit an OUTPUT pin to GND, it will read LOW whatever you try digitalWriting to it... (this can and will also damage the pin).

It is possible on ATmega processors to read the relevant bit in the PORTx register to see what was last written to it.

Thus (PORTD & 4) will return whether pin 2 was last written high. Note that digitalRead uses the input register, here (PIND & 4) to see what the pin's voltage currently is (modulo some very small delay due to the input synchronization flipflops.)

The is all explained in the datasheet. (Section 13.2 in ATmega48/88/168/328 datasheet)

Danois90: This is probably nit picking, but since digitalRead does not return a bool but an int, the correct syntax for inverting the state of a digital pin would be:

digitalWrite(pin, digitalRead(pin) ^ 1);

digitalRead() is not specified to return 0 / 1, or 0 / non-zero, or true/false, but rather LOW or HIGH. https://www.arduino.cc/reference/en/language/functions/digital-io/digitalread/ The specific values of LOW and HIGH are never specified nor guaranteed to be any particular value.

To assume that the values of LOW and HIGH are 0/1 or 0/non-zero, true/false, is abusing the API. While currently all the cores I've seen do happen to define them as 0 and 1 it is not guaranteed. So if nit picking and being totally pedantic you must assume that the values returned are LOW and HIGH and you must not assume any particular value for LOW and HIGH and then always pass the values LOW and HIGH appropriately.

i.e.

digitalWrite(pin, digitalRead(pin) == HIGH ? LOW : HIGH);

--- bill

MarkT:
It is possible on ATmega processors to read the relevant bit in the PORTx register to see what
was last written to it.

pd2x.png

Figure-1: 1-bit structure of Port-D Register

To know exactly what was written at DPin-2 of Fig-1, we must execute this instruction bool x1 = PORTD2; or bool x1 = bitRead(PORTD, 2) (this is what has been said in the above quote) and not this instruction bool x2 = digtalRead(2);. Why?

Assume (as an academic/experimental interest) that the DPin-2 of Fig-1 is driving the base of Q1. Let us execute digitalWrite(2, HIGH) instruction to make the the transistor (Q1) ON. After a while, we would like to change the state of Q1 (to bring Q1 into OFF state). The state cannot be changed by executing this instruction digitalWrite(2, !digitalRead(2)); because, digitalRead(2) will always return Logic Low (VBEQ1 = 0.7V). To bring Q1 into OFF state, we must execute this instruction digitalWrite(2, !PORTD2) or digitalWrite(2, !bitRead(PORTD, 2)) as PORTD2 retains the undisturbed copy of the logic value that was written on DPin-2.

pd2x.png

It looks like HIGH and LOW will soon be of type PinStatus in all cores:

typedef enum {
  LOW     = 0,
  HIGH    = 1,
  CHANGE  = 2,
  FALLING = 3,
  RISING  = 4,
} PinStatus;

After that change, the suggested codes:

digitalWrite(outPin,!digitalRead(outPin));

or

digitalWrite(pin, digitalRead(pin) ^ 1);

will no longer compile on any hardware package that doesn’t use the -fpermissive flag (e.g. all the popular MCUdude cores). On hardware packages that do use the -fpermissive flag, it will cause a warning.

If you aren’t aware, ArduinoCore-API (code name “Project Chainsaw”) breaks all the non-architecture specific code out of the core library into a separate repository, where it will be shared by all hardware packages. This is already in use by the Arduino Uno WiFi Rev2’s Arduino megaAVR Boards hardware package and will be used by all the official Arduino hardware packages (and hopefully all the 3rd party packages as well) in the future.

Although I completely agree with bperrybap, I am concerned about the impact this change (and a similar one for pin mode) will have on existing code. I know it breaks the popular Keypad library and I’d assume that’s not the only one. Anyone have thoughts on that?

In fact, the first code doesn't compile even with -fpermissive.

error: cannot convert 'bool' to 'PinStatus' for argument '2' to 'void digitalWrite(pin_size_t, PinStatus)

Stupid.. Changing the data type from "int" to "uint8_t" would had made sense. Introducing a new data type may break a lot of libraries and thus makes no sense.

If this is going to be changed, it would be a good idea to bring it up ASAP before any more cores start using ArduninoCore-API. I think there was good intention behind the change, but we may just be too far down the other path to switch now. I have been meaning to give some more thorough consideration to the implications of this change before I make my decision, but it certainly seems like it will be a disaster from where I’m sitting now.

GolamMostafa, You are totally in the weeds. :astonished: (hint: go look at what PORTD2 really is. It is 2)

--- bill