Skip to main content

DVI Output

The Soldered NULA Max RP2350 board features built-in DVI output capability, enabling it to display images, graphics, and animations directly on any DVI-compatible display. This is achieved through the PicoDVI library, which uses the RP2350’s PIO (Programmable I/O) system to bit-bang a DVI/DVI signal entirely in software—no dedicated video hardware required. The implementation supports framebuffer-based rendering, allowing smooth animation and static image output in various color modes. Despite being a software-driven approach, PicoDVI efficiently leverages the RP2350’s dual-core performance and fast I/O to deliver surprisingly fluid DVI visuals, making the NULA Max ideal for compact embedded projects, UI displays, and creative graphics demos.

Showcase of the DVI capabilities of the RP2350 board
Showcase of a screensaver on the NULA Max board

We have made a fork of the original PicoDVI library, which makes it and the examples plug-and-play with our NULA Max RP2350 board. The library can be found below:

PicoDVI - Soldered Electronics fork

A fork of the original PicoDVI library specifically tailored to work with the Soldered NULA Max RP2350 board

Download the library as a .zip file and then, in the Arduino IDE, go to Sketch->Include Library->Add .ZIP Library.

The library implements the Adafruit GFX library, which greatly simplifies the drawing process.

Supported resolutions and framerates

The PicoDVI library supports a range of resolutions and framerates for your specific needs; they are defined in the following table:

ResolutionFramerate
320x24060fps
400x24030fps
400x24060fps
640x48030fps
640x48060fps
800x48030fps
800x48060fps
640x24060fps
800x24030fps
800x24060fps

It is also possible to output color in 1-bit, 8-bit, and 16-bit modes.

⚠️
While all of these resolutions are supported, the biggest bottleneck here is the RAM size of the RP2350. Some resolutions may only be available in 1-bit mode. When running an example from our library, the onboard WSLED will blink red if there is insufficient RAM to run it.

Simple example

In the following example, we will draw a line, a circle, and a triangle on the screen in 800x480 resolution at 30fps.

// Simple 1-bit Adafruit_GFX-compatible framebuffer for PicoDVI.

#include <PicoDVI.h>
#include <Adafruit_NeoPixel.h>

// Here's how a 640x480 1-bit (black, white) framebuffer is declared.
// Second argument ('false' here) means NO double-buffering; all drawing
// operations are shown as they occur. Third argument is a hardware
// configuration — examples are written for Soldered NULA RP2350
DVIGFX1 display(DVI_RES_800x480p30, false, soldered_nula_rp2350_dvi_cfg);

// Configure WSLED parameters
Adafruit_NeoPixel pixels(1, 26); // WSLED object

void setup() { // Runs once on startup
// Initialize the onboard NeoPixel RGB, used for debugging
pixels.begin();

// Try to initialize the framebuffer for the video output
if (!display.begin()) {
// Blink LED red infinitely - something's wrong
while (true)
{
pixels.setPixelColor(0, pixels.Color(0x20, 0, 0)); // Set the color to red
pixels.show();
delay(400);
pixels.clear();
pixels.show();
delay(400);
}
}
// Draw a circle with the center at (400,240), a radius of 200px, and the color white (1)
display.drawCircle(400, 240, 200, 1);

// Draw a triangle with vertices at (200,240), (400,40), (600,240) and the color white (1)
display.drawTriangle(200, 240, 400, 40, 600, 240, 1);

// Draw a line from (200,440) to (600,440) with the color white (1)
display.drawLine(200, 440, 600, 440, 1);
}

void loop() {

}

display.begin()

Initializes the framebuffer for writing to the display

Returns value: Boolean value, true if the buffer initialization was successful, false if not

display.drawCircle()

Draws a circle outline on the display.

Returns value: none

Function parameters:

TypeNameDescription
intxThe x-coordinate of the circle center.
intyThe y-coordinate of the circle center.
intradiusThe radius of the circle.
uint8_tcolorThe color of the circle outline.

display.drawTriangle()

Draws a triangle outline on the display.

Returns value: none

Function parameters:

TypeNameDescription
intx0The x-coordinate of the first vertex.
inty0The y-coordinate of the first vertex.
intx1The x-coordinate of the second vertex.
inty1The y-coordinate of the second vertex.
intx2The x-coordinate of the third vertex.
inty2The y-coordinate of the third vertex.
uint8_tcolorThe color of the triangle outline.

display.drawLine()

Draws a straight line between two points on the display.

Returns value: none

Function parameters:

TypeNameDescription
intx0The x-coordinate of the starting point.
inty0The y-coordinate of the starting point.
intx1The x-coordinate of the ending point.
inty1The y-coordinate of the ending point.
uint8_tcolorThe color of the line.
DVI output
Output on an DVI monitor

Text Example

The following example shows how to draw text on the screen.

// 1-bit (black, white) text mode for PicoDVI.

#include <PicoDVI.h>
#include <Adafruit_NeoPixel.h>
// Here's how an 80x30 character display is declared. The first argument,
// resolution, is the full display pixel count; character cells are 8x8 pixels,
// yielding the 80x30 result. The 640x240 resolution uses "tall" pixels, which is
// very reminiscent of the classic IBM VGA mode. The second argument is a hardware
// configuration — examples are written for Soldered NULA RP2350.
DVItext1 display(DVI_RES_640x240p60, soldered_nula_rp2350_dvi_cfg);

// Configure WSLED parameters
Adafruit_NeoPixel pixels(1, 26); // WSLED object

void setup() { // Runs once on startup
// Initialize the onboard NeoPixel RGB, used for debugging
pixels.begin();

// Try to initialize the display framebuffer
if (!display.begin()) {
// Blink LED red infinitely - something's wrong
while (true)
{
pixels.setPixelColor(0, pixels.Color(0x20, 0, 0)); // Set the color to red
pixels.show();
delay(400);
pixels.clear();
pixels.show();
delay(400);
}
}

// Write to the first line and then move to the next line
display.println("Hello World!");
// Continue writing on the next line
display.print("This is a simple example of drawing some text to the display :D");
}

void loop() {
}

display.print(const char* _c)

Prints text at the previously set cursor position. This is the standard Arduino print function used in many native Arduino objects and libraries.

Returns value: size_t, number of bytes printed.

Function parameters:

TypeNameDescription
const char *_cThe C-style string to print on the display.

display.print(const char* _c)

Prints text at the previously set cursor position. This is the standard Arduino print function used in many native Arduino objects and libraries.

Returns value: size_t, number of bytes printed.

Function parameters:

TypeNameDescription
const char *_cThe C-style string to print on the display.
DVI output
Output on an DVI monitor

Animation example

The following is an animation showing the speed at which the display can refresh.

DVI output
Output on an DVI monitor
ℹ️
If you are experiencing horizontal red lines, in the Arduino IDE, go to Tools->Optimise->Optimise Even More (-O3)

Code is available at the link below:

Double buffer example

An example showing multi-colored balls jumping on the screen