Raspberry Pi serial and the importance of pullups

Another day, another lesson learned.

My Zolatron 64 6502-based 8-bit computer uses serial for its main I/O. One day I may add video output and keyboard input. But for the time being, I’m happy to talk to it via a terminal from another machine.

So far, that other machine has been my electronics workbench computer – a LattePanda Alpha. And I’ve been making the connection via a USB cable and an Adafruit FTDI Friend. That device takes care of voltage levels and, it seems, other useful stuff.

But the time has come to give the Zolatron its own, dedicated computer. And the tool of choice for this is a Raspberry Pi Zero 2 W. It’s small and burns very little juice, but its wifi connection means I can SSH to it from anywhere. That in turn means I can use the Zolatron from the comfort of my sofa (always a plus). As it’s effectively a go-between for the Zolatron and whatever machine I’m connecting from, I’ve the Pi the hostname ‘imp’. Hands up if you get the reference.

Built-in UART

I don’t need to use the FTDI Friend for this – I can just hook up the Raspberry Pi’s UART direct from the GPIO pins – something I’ve done many times. But there is a catch. The Zolatron is a 5V device and the RPi uses 3.3V on its GPIO pins, and they are not 5V tolerant. The Zolatron will put out 5V on its TX line, which needs to be shifted down to 3.3V for the Pi’s RX pin. It doesn’t matter that the Pi is replying with 3.3V on its TX line – that’s high enough for the Zolatron to understand.

And I was already prepared for this. Some time ago I made a simple voltage divider board that uses pairs of resistors to reduce the 5V signals on the TX and RTS pins on one side to 3.3V for the RX and CTS pins on the other. Very simple.

Except that it wasn’t working.

Time to probe.

Nothing to see here

When the Zolatron boots, it sends out some text via serial, including a prompt. This wasn’t showing up at the Raspberry Pi end.

I measured voltage levels on the level shifter board. TX on the RPi side was high (3.3V) when idle, which is good, while the RX line was 0V, which is bad.

I realised that my serial board did not have pullups on signal lines. I bodged on a couple of 10KΩ resistors to keep TX and RX on the serial board pulled high. But when I connected everything again, the Pi’s RX line (TX on the Zolatron) was still at 0V. I tried disconnecting the Pi to check that it wasn’t holding the line low from some reason. It wasn’t.

It had to be my level shifter board causing the problem. But all that’s on that are four resistors.

Oh, wait.

Let’s look at the problem signal from that board’s point of view. This is what it’s doing.

The signal is set at 5V by the TX pin. On my level shifter board, R1 was 180Ω and R2 270Ω. This meant that at the junction between the two resistors, the signal level is at: R1 / R2 x 5V. With the resistors mentioned, my abacus tells me this is 3.3V.

And it works just fine. As a level shifter.

Alas, there is also a secondary effect of this little circuit. Look what happens between RX and GND. This signal effectively has a 270Ω pulldown resistor.

That’s a strong pulldown, easily overwhelming the 10KΩ pullup on the serial board. (The larger the resistor value, the weaker the pulling power it has.)


I replaced the two resistors with 22KΩ and 33KΩ units, which also produce 3.3V at the junction but with a much, much weaker pulldown effect. And lo! All is working as hoped.

In case you’re interested, here’s the level shifter board layout, with values amended to incorporate the lesson I just learned.

I’ve already ordered the PCBs for the second iteration of the serial board from JLCPCB. Luckily, I incorporated optional pullups for RX and TX and pulldown for CTS on the board (they can be selected with DIP switches). Obviously I’d had a premonition.


Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.