TL;DR

The Raspberry Pi Pico is an incredible little microcontroller. While it does not have a video output interface built-in, like it’s bigger Raspberry Pi Zero / 1 / 2 / 3 / 4 / 400 brethren (HDMI / double HDMI in their case), it is possible to add a video output to the Pico! (As VGA or DVI over an HDMI connector, read on for details)

We’ll explain why a video out is a very special feature for microcontrollers, and what types of video out you can build or purchase for your Pico. We’ll also show you some sample code – already precompiled for you, so you can download it, and talk about what you need to modify to make it run.

Our sister shop, buyzero.de, sells a variety of Pico accessories – including the DVI Sock, and the VGA carrier board for the Pico.

Pico DVI Sock
Pico VGA & Audio expansion Board (also supports microSD!)

Note: the Pico does not have a Linux operating system like the Raspberry Pi Zero W for example. So you’ll most likely have to dive a bit deeper into learning how to code & upload your applications to the Pico. If that’s not your thing, the Pi Zero W might be a better fit to start playing around 🙂

Pico Video output using the DVI sock – wild animations are possible. Do you recognize the face? Let us know in the comments

If you’ve succeeded doing your own video project with the Raspberry Pi Pico, let us know in the comments :-)!

Why is adding a Raspberry Pi Pico Video output a challenge?

The Raspberry Pi 400, for example, is based on the BCM2711 SoC. This system-on-a-chip has specialized hardware which takes care of the video output, perfectly preparing it in the format which is specified for the particular video interface(s), two HDMI ports in the case of the Pi 400. It also has a massive memory (of 4 GB), in which to store the video output data.

To understand this better we need to look at some fundamentals of computer video output:

Video display and transmission

We see the display on the screen “at once”. Our brain has massively parallel wiring, in which it ingests information from all available retina cells at once (cone cells and rod cells).

a cat with a butterfly, an example of what we want to show to our users.
Photo by Karina Vorozheeva on Unsplash

(* the mechanism of transmission into the brain integrates information across several retina cells, but the fact remains that a lot of information is transmitted in parallel. Look into bipolar cells if you’re interested to know more)

The brain, however has latencies – it is not able to resolve changes in visual stimuli faster than 13 ms. (which is about 1 in 75 frames per second).

For us this means, if we want to display a really smooth animation, we need to show about 60 different static pictures per second. Our brain will interpret these static pictures as smooth and life-like playback.

Of course, we want these pictures to be in color. We have three different photoreceptors for color, so the screen displays only these three different colors with different brightness. Again, our brain will synthesize the colors inbetween from this information it gets. (On a sidenote, purple does not really exist as it’s own wavelenght – it’s a mixture of red and blue).

You’ve probably heard about these colors before:

  • red
  • green
  • blue

Together, they are known as RGB. When you add all three of them, you get white. Adding red to green yields yellow, adding green to blue yields cyan, adding blue to red yields magenta.

So what we really want to output are:

  • three different colors,
  • each with a different brightness value
  • ideally about 60 different values per second (60 Hz)
  • a certain screen resolution – e.g. 640 x 480

Let’s calculate how much data this is per second:

  • 3 colors
  • x 8 bit color depth
  • x 60
  • x 640 x 480

= 442.368.000 bits per second = ca. 422 MBit / s

(Consider that for example the USB v1.1 interface on the Pico has about 10 Mbit/s – about 40 times less throughput!)

To output this information, ideally you would also want to hold this information as a bitmap in your RAM – one part of your application will update the image in the RAM, while another will take care of delivering the data in a format which a monitor can work with. This needs to be RAM, since we are operating at high speeds, and need to be able to read this reliably with small latencies – which Flash memory would not provide.

Let’s calculate how much RAM we need:

  • 640 x 480 (resolution)
  • x 3 (colors)
  • x 8 bits (color depth)

= 7372800 bits = 900 kBytes (note 1 Byte = 8 Bits)

While 900 kBytes will fit many times over into the RAM of a Raspberry Pi 400, the Pico only has 264KB RAM.

As you see, we will need to reduce some of the parameters (e.g. resolution, color depth, …) if we want to fit a bitmap into the RAM, or we need to come up with some clever ideas of how to make it work without storing everything!

Pico’s RP2040 PIO allows to output video data

Finally, while the Raspberry Pi 400 and other Raspberry Pi models have dedicated hardware circuitry to process all this information and output it in a reliable manner, the Pico doesn’t have special circuits dedicated just for video output.

But it has one trick up it’s sleeve! The RP2040 supports PIO (programmable IO). The PIO is dedicated to emulating different interfaces with precise timing, and it is very very powerful! It can be programmed to read from the RAM and output at high speeds.

We will be using the PIO to output video to some GPIO pins, and will use some additional circuitry (resistors) to bring the signal into the desired condition, depending on the video output interface we want to connect the Pico to.

Video output format history

Before getting into the meat of how you can add a video output to your Raspberry Pi Pico, let’s have a look at a little history of video output formats.

As discussed already, the human eye is not capable of detecting changes in images faster than about 13 ms. One of the first approaches to build computer monitors was therefore the CRT (cathode ray tube) monitor.

The CRT monitor uses several ray beams (2) to write the image on the screen line by line (4), (5) shows a closeup of the colorful phosphors being adressed individually by the different rays. CC BY-SA 3.0 Peo~commonswiki 

The CRT has three ray guns, which sweep pixel by pixel, line by line. (and then need some time to return to the starting point). The screen has colorful phosphors, which will continue to emit light for a while after the ray has passed through it. On the next sweep of the ray gun over this particular pixel the intesity of the ray might have changed, which we’ll see as a darker pixel. Our brain fuses the adjacent colorful phosphor pixels into one pixel, and is not able to notice the brightness changes between ray sweeps.

CRT closeup, showing the colorful phosphor dots. by: FreeImages.com / hubert jelen

This way the illusion of a moving image is created.

In effect they don’t need to have all the data present at the start of the image – but just the current pixel brigthness value. The intesity of the ray will be modified according to this. We can use an analogue signal for this – for example, increasing the voltage will increase the brightness.

We need three different wires for the different colors (to drive each ray gun individually), and we need to have a way to let the monitor know when a new line needs to be started, and when a new image has to be started (when all lines have been shown).

VGA (video graphics array)

VGA was designed with these CRT monitors in mind. It is still reasonably common as an input on monitors, even though it’s becoming increasingly obsolete as we shift to completely digital transmission (more on that later).

This is also the easiest video output standard to get working on the Pico.

Next to a specification (with default resolution modes), it specified a connector, the VGA connector:

VGA port; photo by Duncan Lithgow

It has 15 pins:

  1. RED (red video)
  2. GREEN (green video)
  3. BLUE (blue video)
  4. ID2/RES (reserved)
  5. GND (Ground HSync)
  6. RED_RTN (red return, analog ground for red)
  7. GREEN_RTN (Green return, analog ground for green)
  8. BLUE_RTN (Blue return, analog ground for blue)
  9. KEY/PWR (+5 V DC powers EDID EEPROM chip on some monitors)
  10. GND (Ground VSync, DDC)
  11. ID0/RES (reserved)
  12. ID1/SDA (I2C data since DDC2)
  13. HSync (Horizontal Sync)
  14. VSync (Vertical Sync)
  15. ID3/SCL (I2C clock since DDC2)

Note: VGA cables can support different resolutions, color depths and refresh frequencies, whereas the word “VGA” when referring to the resolution usually means 640 x 480.

As you see, there are three wires carrying the image data, one for each color. The signal is transmitted with a peak (maximum) voltage of 0.7 V. The signals transmitted for the colors are analog in nature – higher voltages increase the brightness, a voltage of 0 means the pixel is dark / off.

Making VGA output work on the Pico

This means that the Pico, with it’s 3.3V digital output has high enough voltages to be able to drive these RGB pins into the VGA cable (which expects voltages of 0 – 0.7V). We actually need to reduce the voltage by using resistors.

We can buld a simple DAC (digital analog converter) by combining several resistors and GPIO pins. Depending on the combination of GPIO pins which are active at any given moment, we have different voltage levels (= brightnesses):

A simple VGA DAC, picture taken from “Hardware design with the RP2040”

As you can see in the picture above, five GPIOs (0 -4) drive one channel (red in this case), which gives us a five-bit depth. The resistors are weighted 1:2:4:8:16, for example the least significant bit (LSB) of red has a 8.06K resistor.

When trying to build this circuit, you should go for 1 % tolerance resistors to get a good picture.

Please refer to “Hardware design with the RP2040” to understand how the resistor values were calculated. In short, if we drive all of them at once, we’ll have a voltage of 0.74 V which is OK for our purposes.

More specifically, what this reference design suggests is to support a commonly used 16-bit RGB data format (RGB-565), which uses 5 bits for red and blue, and 6 for green. We can reduce the actual physical output to the green to 5 GPIO pins as for the other colors, to save one pin.

In addition to that 2 more pins are needed for horizontal and vertical blanking timing (HSYNC and VSYNC)

This brings us to a total of 17 GPIO pins to drive a VGA output. Luckily, the Pico has 26 available GPIO pins, which allows us to drive the VGA output.

As mentioned previously, the Pico is also capable of driving these pins at the necessary frequencies and with precise timing, thanks to the RP2040 PIO feature (programmable I/O).

Raspberry Pi Pico VGA video output hardware

Raspberry Pi Pico VGA, SD Card & Audio Demo Board – picture taken from the Hardware Design with the RP2040 manual

Raspberry Pi has designed and open-sourced a carrier board for the Pico which shows off different features:

  • VGA output
  • buttons
  • microSD slot
  • audio outputs (analog PWM, digital I2S)

Purchase the Pico VGA / Audio / microSD Board

We (buyzero.de) are currently in the process of having this reference design board built and assembled for us. Contact us if you want to be notified, once the board can be purchased!

The Pico VGA Board is now available for purchase from us, limited initial stock!

In the meantime, Pimoroni has also created a version of this board, they call it the Pimoroni Pico VGA Demo Base.

Side note: each pin of the 15 rgb output pins will still need to be driven at around 17.58 Mbit/s, which still is an impressive – but much more manageable number!

Software required to drive the VGA output

Since we’re working on the Pico, there are no “graphics drivers” we can simply install. We need to write the code ourselves … or not 🙂

Luckily, the people who designed the hardware board for us already supply some code we can use, so that we’re able to focus on our project.

Example code you can use can be found in the pico-playground Repository:

In this repository, there is a simple movie player called popcorn (which plays movies in a custom format). A big buck bunny, 1.6 GB in size can be downloaded here. Note that these are raw disk images to be written to an SD card – this example assumes you have the VGA demo board, which has an SD slot. Instructions for converting movies are also given.

The code uses the pico_scanvideo library (pico/scanvideo.h) from the pico_extras repository. Also have a look at that repository for audio sample code!

The API outputs parallel RGB data and sync signal on pins for DPI VGA (using resistor DACs, as described above).

A neat point:

  • The default PIO scanline program accepts run-length-encoded data – this means that you can save RAM to generate flat color areas (I’m thinking games here!)

In general you would want to use the pico_scanvideo library instead of developing VGA output code from scratch 🙂

good coding practice

More demos

Check out the scanvideo directory of the pico-playground repository:

Using the VGA Board

You need to pass an additional parameter to CMake while compiling:

-DPICO_BOARD=vgaboard

TBD: Add step-by-step guides

DVI: Digital Visual Interface

Technology moves on. Life moves on. CRTs became more and more obsolete, superseded by more modern flat screens, which have digital interfaces. No moving beams, just pixels.

For a while, signals continued to be analog – but this is not very desirable, since we have to take something digital, transform it into something analog, and then transform it back into something digital again. The image will be less precise, and we have additional circuitry which we could dispense with.

Enter DVI. It cleverly offered an option to transmit analog signals as well, so simple DVI to VGA adapters / cables could be built. Of course, the graphics card would need to output both analog and digital data. But this helped to have the standard accepted and for it to become widespread.

We’re interested in the digital signals here (DVI-D), as we would like to bit-bang them from the Raspberry Pi Pico.

With DVI-D, the image data is transmitted in a serial way.

A “single link” (most basic) DVI connection consists of four so-called TMDS links (transition minimized differential signaling):

  • red
  • green
  • blue
  • pixel clock

Differential signalling is used to minimize interference (since

We have a total of 24 bits per pixel (8 bits x 3 colors), and encode the data using 8b10b encoding (8 bits are mapped to 10 bit symbols on the actual physical line to achieve DC balance amongst other things).

DVI treats the pixels similarly to VGA: everything is re-transmitted every time the image “starts” again, and the data is clocked precisely. This is like a telephone line, where the line is constantly in use when two people talk.

Note: in contrast to this, DisplayPort treats the data as packets – which has a number of advantages.

In contrast to the VGA example discussed above, since the data is sent digitally, instead of analog brightness values, this means much higher data volumes.

Luke Wren, an engineer with Raspberry Pi, believed that the RP2040 (at the heart of the Pico) would also be capable of driving DVI output, again using PIO.

The result is the PicoDVI repository & projects, and the Pico DVI Sock.

Luke Wren calculated that about 252 Mbps serial data has to be driven through the GPIO digital pads – differential serial, which is emulated with two single-ended pads.

dc_coupling.png
Source: Luke Wren’s repository (BSD-3 licensed)

Above you see the circuit used to drive the DVI output (using an HDMI connector, more on this below) – are simply several 270 Ohm resistors.

Luke Wren has even gone further and added dual DVI output to his PicoDVI layout by a plugin board:

two_displays.jpg
Luke Wren demonstrating output on two displays using his own design

HDMI: downwards compatible with DVI

HDMI is the next evolution of connectors (and in competition with DisplayPort). It is fully downwards compatible with the digital DVI signals – thus you can have simple, purely passive DVI / HDMI converters.

Pico DVI Sock

The Pico DVI Sock is an easy and low-cost solution to add digital video output to your Pi. It has been designed by Luke Wren (see description above). It is a DVI output with an HDMI connector. As HDMI is downward-compatible to DVI, you can use an HDMI cable to connect your Pico to HDMI displays:

soldering_04.jpg
Photo by Luke Wren, CC0-1.0 License

Programming the Pico DVI Sock

TL;DR

You can download our picodvi-test.zip and get started playing around with the .UF2 example compiles in it. It also includes a PDF which gives you a step-by-step guide.

Code examples

Luke Wren provides code examples in his repository. However, to use them with the Pico DVI Sock, you’ll need to set the correct config to use. We’ll show you how in this mini-tutorial.

Install Prerequisites

sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential

Clone PicoDVI repo from Luke Wren:

cd ~
mkdir pico
cd pico 
git clone https://github.com/raspberrypi/pico-sdk
cd pico-sdk
git submodule update --init
cd ~/pico
git clone https://github.com/Wren6991/PicoDVI.git

To use the code examples with the Pico DVI Sock, you’ll need to set the correct pin config to use. Add the following line to common_dvi_pin_configs.h just before the first #ifndef

#define DEFAULT_DVI_SERIAL_CONFIG pico_sock_cfg

Building the examples

cd PicoDVI/software/
mkdir build
cd build
export PICO_SDK_PATH=~/pico/pico-sdk
make -j$(nproc)

Install the examples on the Pico

The built examples will be in the software/build/apps folder.

Press and hold the BOOTSEL key on the Pico, then connect the board to your PC using the microUSB connector. Copy and paste the appropriate .uf2 file you want to try – e.g. sprite_bounce.uf2 – to the Pico.

The Pico will automatically restart, and you should be able to see output on the HDMI connector (remember it’s DVI really :-)).

Download

You can download our picodvi-test.zip and get started playing around with the .UF2 example compiles in it. It also includes a PDF which gives you a step-by-step guide. If the sprite_bounce.uf2 example doesn’t work for you, try another monitor – possibly not all monitors are compatible with this video output.

If you’ve come so far, let us know in the comments how it worked for you & what project ideas you’ve come up with!

Purchase a Pico DVI Sock

Our sister page, buyzero.de, stocks the Pico DVI Sock in two variants:

Sidenote: DBI & DSI displays

As a side note, the Raspberry Pi Pico Extras repository has placeholders for DBI (16 bit MIPI DBI displays – with data being passed in parallel) and DSI (MIPI serial displays), so we might see support for these displays in the future as well.

9 Comments

  1. […] Video output using the Raspberry Pi Pico (VGA & DVI) […]

  2. VanTa on March 20, 2021 at 8:27 am

    Any example of composite output with genlock?

    • PiCaptain on April 6, 2021 at 2:47 pm

      I think composite output should be possible, but have not seen anything in the wild about that, yet.

  3. Miroslav Nemecek on June 10, 2021 at 10:24 am

    As alternative to scanvideo, I’ve implemented another VGA/TV library for Raspberry Pico – ‘PicoVGA’, which I think is easier to use: http://www.breatharian.eu/hw/picovga/index_en.html

    • raspi berry on July 1, 2021 at 5:20 pm

      Thank you for sharing the info about your library!

  4. Ashesh Rai on June 10, 2021 at 5:39 pm

    Hey, can this HDMI be used as an INPUT and process the data.

    • raspi berry on July 1, 2021 at 5:20 pm

      the way it is programmed, and since the resistors act to limit the Raspberry Pi output signal – no.
      possibly you could use an adaption of the design, with something which boosts the signal level instead of lowering it / and of course adapted code for the outputs to work as inputs, to be able to process the video data inside the Pico.

  5. Lorencz on June 25, 2021 at 2:25 pm

    A few practical tips though. Thanks to silicon lottery, your particular unit may not overclock proportionally the gpu and the cpu part the same as well. So, if you don’t need faster graphics, don’t overclock its GPU. This increases your chance to overclock higher and have a stable system. Also lower power consumption and less supply current means better stability. Raspberry Pis with 4 CPU cores may hit a power delivery wall when loaded with all 4 cores active. The integrated PMIC (power management IC) may not supply the needed current for all of the cores, and turn itself off for a short period of time, thus rebooting the Pi. Cool the PMIC too. Don’t overvolt at max if you need all-core load (kernel compiles, heavy number crunching etc). Here we have a RPI4 which can run at 1850MHz with all cores active with over_voltage=2, but with higher over_voltage=3, it reboots when loaded. Higher over_voltage means also higher current and the PMIC circuitry has limited output current. The hotter the PMIC and its surrounding inductors, the less current it can supply. This is a limitation of the PMIC chip, not the BCM chipset. So, if you encounter occasional reboots under heavy load, it may be the overheating of the PMIC. If your Pi locks up, it won’t be this particular problem, but perhaps silicon lottery loss. Underclocking a GPU part may be an interesting way of increasing overclockability (more stable system), but AFAIK, older Raspberry Pis had L2 cache tied to the GPU, so underclocking it may slow the CPU too. I’ve documented how the PMIC of Raspberry Pi 4 heats – all other chips being cooled directly to a heatsink. The view is from an underside, i.e. you see the heated PCB. Lower left corner is the PMIC.

    • raspi berry on July 1, 2021 at 5:18 pm

      Thank you very much for these practical tips!

Leave a Comment