Does switching pinMode from INPUT to OUTPUT produce reliable output LOW?

Well in order to be safe, that is what I suggested but it seems like he prefers his tricky method of doing it.

It sounds like you are trying to use the Pro Mini output as an open-drain? If so, it should work. I have some code that does that to interface to a Mitutoyo Digimatic digital dial indicator.

	// trigger request open-drain
  	pinMode(req_pin, INPUT);
	delay(5); 
    
    // Request pulled LOW
  	pinMode(req_pin, OUTPUT);
    digitalWrite(req_pin, LOW);

When switching a pin to INPUT on an AVR, the pin voltage will depend on any previous WRITE to the pin:

  • If the pin was previously written HIGH, the internal pullup (to +5V) will be enabled.
  • If the pin was previously written LOW, the pin will go high-impedance (INPUT mode.)

This is a hardware feature of the AVR, not library sneakiness. Other processor families may or may not try to reproduce this behavior in the software.

(Whether it is safe to do this to interface to 3.3V logic is a separate question.)

Also, I don't see any code in the bootloader(s) that would set PORTC (A0-A1/etc) or PORTB (D8-D13) to outputs (nor "input pullup"), so your observation that they go briefly high is ... mysterious.

I have undrstood as follows from the above: (It requires experimental verification from my side.)

(At power up, a DPin is input without pull-up enabled.)

I make the DPin as output by executing pinMode(DPin, OUTPUT).

I assert HIGH on the DPin by executing digitalWrite(DPin, HIGH);

I make the DPin as input by executing pinMode(DPin, INPUT).

Result: The internalpull-up is enabled. If I measure voltage on the DPin, I would see 5V (open circuit voltage) for UNOR3.

Correct. This is the result of the double use of the Output Register in that series of AVR.

EDIT2:
pinMode(pin, INPUT) will switch off the pull up after at least 2 clockcycles. This is the result of wiring_digital.c within Arduino IDE.
As the previous state was HIGH damage to OP's 3.3V MCU will already be done.

At first glance, direct connection between AVR I/O pin and ESP GPIO pin should work, provided you can guarantee the AVR I/O pin never goes HIGH.
But you also need to consider what might happen if one of them is powered up and the other is not.

I'd go for a transistor to set up an open collector or open drain configuration, the safe option.
If you need to use the connection in both directions you'll need a voltage translator.

Yes, that's right. I would switch between tristate and output low, with the two devices connected directly together. So the output is either off or low. That's what a device with a real open drain output would do, and it can be used as a form of voltage translation if there's a pullup resistor to provide the high voltage. But since the 328P does actually have transistors to the pin from the high side, I would have to be sure nothing would ever activate them - nothing in the flashing process, or booting process, or something in some library, or in my code.

No, that's wrong. When you set the pinMode to INPUT, the IDE automatically sets the output pin low. It has to do that because the output value is what controls the pullup resistor, and if it were high, you would have INPUT_PULLUP. But remember that this is only for AVR. The fancier processors may do things completely differently.

Yes, I am puzzled by that too. The Pro Mini I've been working with is an 8MHz clone from 2016. I've always assumed is has the bootloader specified in boards.txt, which is ATmegaBOOT_168_atmega328_pro_8MHz.hex.

But I don't actually know that. And the high pulses only occur when powered from USB. They don't happen if I power directly to the Vcc pin. So it's something involving the USB negotiation. I first thought it was just bringing all the PortB pins high, but that wouldn't explain why it happens on A0 and A1 too, which are PortC. But then A0 and A1 are also D14 and D15.

I've just received a new Pro Mini which has a 328PB processor on it. It provides the signature of a 328P, and it may have a newer bootloader. And it does not produce the spikes. When I have time I'm going to set up my Arduino-as-ISP, and use AVRdudess to extract the bootloaders from both parts. Maybe I'll be able to identify them. Meanwhile, it's a mystery.

Please, see #23 @westfw.

If "switching a pin to INPUT" means dealing directly with the registers, then the above is correct. But if you use the Arduino pinMode() instruction to make it an INPUT, then the output bit will be set to low automatically. If you make it INPUT_PULLUP, the output bit will be set to high automatically. The IDE code is in post #7.

The DPin is configured to work as input with internal pull-up enabled. So, the open-circuit (no input device is connected to assert LOW or HIGH) voltage of the DPin would be measured HIGH (Vcc) which comes from Vcc rail through the pull-up.

I've confirmed that the old 8MHz Pro Mini that I've been using has the bootloader specified in boards.txt of my IDE v1.8.8. It's ATmegaBOOT_168_atmega328_pro_8MHz.hex.

The new Pro Mini is the 16MHz version, with a 328PB pretending to be a 328P. Its bootloader is the one shown in boards.txt for the 16MHz Pro Mini, which is ATmegaBOOT_168_atmega328.hex. That's the same as the "old bootloader" for the Nano.

The old Pro Mini has the high-voltage spikes during powerup from USB, but the new one does not. And my Nano with the old bootloader does not. So there must be a bigger difference between the 16MHz bootloader and the 8MHz bootloader than just the processor speed.

If you know the purpose of the "bootloader program", then why are you relating it with voltage spike and processor speed?

Huh. I went and checked the actual code, and I was wrong and Sherman is correct. pinMode(p, INPUT); and pinMode(p, INPUT_PULLUP); now set both the direction and port bits, and so the pin will go high-impedance immediately on setting the mode to INPUT (and to HIGH via internal pullup when setting INPUT_PULLUP.)

This was apparently changed sometime between IDE v1.0 and v1.0.5 ("shortly" after INPUT_PULLUP was added.) (shows what happens if you don't pay enough attention to changes!)

That'll make it tricky to use trying to implement an "open collector" bus with a 3.3V external pullup, if you wanted to stick to the Arduino APIs.

From pinMode in wiring_digital.c:


  if (mode == INPUT) { 
    //
    *reg &= ~bit;  // clear bit in DDRx to make pin input
    *out &= ~bit;  // clear bit in PORTx to clear pull up

In this case I only consider the transition from Output to Hi-Z.
If Output was LOW, the second line has no effect, pull up was already off.
If Output was HIGH, the first line switches the pin from a strong +5V to a weak +5V, the second line switches the pin to Hi-Z.
They did think about it:
If they had reversed the order, coming from Output HIGH the now first line would switch the pin from a strong +5V to a strong GND before switching to Hi-Z.