Control a CRT with the Raspberry Pi DPI

I managed to send video signals to a Mac Classic’s internal CRT monitor from a BeagleBone Black back in 2016, and it seems like this is a topic that a lot of people are still interested in. A recent discussion gave me the idea to try and do the same thing with a Raspberry Pi, and I wanted to document the experiment in this article.

Introduction

Important update: As of right now, the Macintosh Classic CRT related parts of this article are only valid if you use a Raspberry Pi 4! Older versions won’t work. However, the DPI interface is available on all other 40-pin Raspberry Pi models, and the rest of the article remains valid for those.

Please also note that this is an approach that worked for my Mac Classic, not an official guide or a universal method that’ll work for all of you. Please also see the comments for fixes that other readers have implemented!

If you remember the original series and this article about exact timings on the Pi, I somewhere concluded that it simply wasn’t possible. At least not in the same way it is on the BBB. While the sync signals could be generated precisely on the Raspberry Pi, it was not capable of sending the pixel data to the monitor fast enough in the full resolution.

While I still think that’s true, I didn’t think about the possibility to use the Pi’s display hardware to do the job for me, like a commenter on the original series pointed out. So the main goal of this article is to configure the Raspberry Pi in a way that allows the framebuffer to be rendered on the Macintosh Classic’s monochrome CRT display.

The Display Parallel Interface (DPI)

You might have seen some Raspberry Pi display HATs that only connect via the GPIO interface. Those use the DPI mode of the 40-Pin GPIO Raspberry Pi, which is one of the alternate functions of the GPIO.

If selected, the GPIO pinout of the Pi changes:

Figure 1: The GPIO pins in DPI mode (Source)

This configuration allows parallel RGB displays to be attached to the Raspberry Pi GPIO. Several color modes are available:

  • RGB24 (8 bits per color)
  • RGB666 (6 bits per color)
  • RGB565 (5 bits red, 6 green, and 5 blue)

However, this also means that most of the GPIO pins can’t be utilized for other tasks while the Pi operates in the DPI mode.

This interface is controlled by the GPU firmware and can be configured with special config.txt parameters. Furthermore, you’ll also have to load and enable the correct Linux Device Tree overlay, just like with the original BBB version.

Enabling the ALT2 (DPI) Mode of the GPIO

As mentioned, the mode is enabled by loading the correct Linux Device Tree overlay. But first, you’ll have to disable I2C and SPI, because those will conflict with some of the video pins. To do that, edit the config.txt file:

sudo nano /boot/config.txt

In that file, comment out the following two lines:

dtparam=i2c_arm=on
dtparam=spi=on

They should now look like this:

# dtparam=i2c_arm=on
# dtparam=spi=on

Once that’s done, put the GPIO in the Alt2 mode by loading the DTO:

# 24-Bit mode
dtoverlay=dpi24
# 18-Bit mode
# dtoverlay=dpi18

Note that both DTOs are already installed by default. They are located in the following folder:

/boot/overlays

That’s all you need to do to enable the DPI mode! Let’s take a look at how you can configure the graphics card to output the right sync signals.

Configuring the video hardware

As mentioned earlier, the DPI mode can be configured by placing special attributes in the config.txt file. I wrote this small Java application that’ll allow you to quickly enter all the necessary information. It will then generate the attributes for you, and you only need to add them to the config.txt file:

Figure 2: The RPi DPI-Calculator

This tool is universal and can also be used to create the configuration properties for other displays. The various fields and parameters are explained on the app’s download page. I used the following two attributes for the Macintosh Classic CRT:

dpi_output_format=0x76017
dpi_timings=512 0 14 178 0 342 0 0 4 24 0 0 0 60 0 15667200 1

Just add them to the end of the config.txt file.

Configure the framebuffer and setup a custom video mode

You can either use a pre-configured timing mode, or define a custom one. In this case, no standard video-mode could be used to interface the display. Therefore, I had to define a custom video mode, which can be done by setting the following two flags in the config.txt file:

dpi_group=2
dpi_mode=87

This will make sure that the dpi_timings parameter, described above, is used by the driver when the Raspberry Pi boots up.

As the last step, the framebuffer has to be configured. I used the following settings for the Mac Classic CRT:

overscan_left=0
overscan_right=0
overscan_top=0
overscan_bottom=0
framebuffer_width=512
framebuffer_height=342
enable_dpi_lcd=1
display_default_lcd=1

The last two lines will make sure that the video signals get generated and that the DPI is used to output the contents of the frame buffer.

The overscan values can be used to center the image if it should be off-center. However, mine was fine right away, so I didn’t use those values.

The completed config.txt file

These are all the lines I added to the config.txt file for the Macintosh Classic CRT:

dtoverlay=dpi24
overscan_left=0
overscan_right=0
overscan_top=0
overscan_bottom=0
framebuffer_width=512
framebuffer_height=342
enable_dpi_lcd=1
display_default_lcd=1
dpi_group=2
dpi_mode=87
dpi_output_format=0x76017
dpi_timings=512 0 14 178 0 342 0 0 4 24 0 0 0 60 0 15667200 1

Hooking everything up

Connect the HSYNC line of the Raspberry Pi (Physical Pin #5) and the VSYNC line of the Pi (Physical Pin #3) to the HSYNC and VSYNC lines of the display. Unfortunately, my display’s synchronization signals use a voltage of 0V and 5V for the digital LOW and HIGH state. However, the Raspberry Pi only outputs 3.3V. Therefore, I also needed to add a logic level converter to the two sync signals. Don’t forget to connect a ground wire of the display to a GND pin on the Pi (See fig. 4).

Next, connect the color lines of the Raspberry Pi to the display. This step varies, depending on your configuration and display. The following GPIO pins can be used to transmit the color information:

Figure 3: color data lines in various color modes

I used mode 7, which means that there are 8-bits per color. If you, however, use mode 3, for example, the red data-bits get transmitted on the GPIO pins 24 to 20, green pixel information gets sent over the pins 17 to 12, and blue over GPIO 8 to 4. This configuration allows you to use some GPIO pins for other tasks.

The Mac Classic’s display is a one-bit monochrome display, so I used a single color line to connect the data-line of the screen. That’s a very quick and dirty solution, and I’ll properly hook the screen up in another article. For now, this is all I needed to do:

Figure 4: The connections I had to make to control the Macintosh Classic CRT
(View full size image)

This method was enough to verify whether the signal generation works properly.

Luckily, it did almost right away. Here are a few quick pictures I took:

As you can see both, the console, as well as the desktop, can be displayed perfectly fine. However, as mentioned before, the colors are completely off, which is fine for now. I’ll fix that later.

Problems and solutions

Compatibility
As some commenters have brought up, older Raspberry Pi Models can’t produce a pixel clock frequency that suits the Macintosh Classic CRT. Therefore, the Macintosh Classic related sections of this article only hold for the Raspberry Pi 4. With it, you can achieve a close enough output:

Figure 5

Furthermore, there may be differences in the various iterations or versions for different regions/countries of the Macintosh Classic. However, that’s just speculations.

Overscan
If you remember the BBB series, one problem was, that the Macintosh Classic’s CRT display circuitry expects the HSYNC signal to become HIGH (and thus inactive) exactly 110 pixels after the video data had already started:

Figure 6: The HSYNC signal that the CRT expects

However, the Pi can only generate an HSYNC signal that gets HIGH just before the data gets transmitted. Or, in other words, the data gets transmitted just after the HSYNC is done, which is the way to go for HDMI and VGA.

My first workaround for this problem was to increase the number of pixels per line by 110, use a left-overscan of -110 pixels, and then make the HSYNC pulse 110 dots longer. This worked reasonably well. One of the pictures from the slideshow above shows this workaround. However, this way, every line took longer than it should, which caused refreshing artifacts and flicker.

You might have noticed that my finished configuration from above doesn’t contain this workaround. It turned out, that the display simply ignored the incorrect portion of the HSYNC pulse and happily rendered the data anyway. So in the end, it was much less of a problem than I had first thought.

Troubleshooting
Another thing, that I noticed, is that when you leave the Mac Classic CRT’s data line floating (not connected to anything), it displays all pixels at full brightness (blank white screen) as long as the HSYNC and VSYNC lines are correct. This is good for troubleshooting. If you don’t see this happening (or if the display produces a clearly audible buzzing or humming sound) try swapping the HSYNC and VSYNC lines.

Voltages
Make sure that you supply the correct voltages! The synchronization lines on the Macintosh Classic CRT use 5V and the data line uses 3.3V. Utilize a logic level converter (or any similar technique) to overcome this issue.

Display is still blank after troubleshooting
If the Mac Classic CRT displays a full white screen with the data pin floating but it doesn’t display anything with it connected, try to use a different color output of the Raspberry Pi.

Supported Raspberry Pi Versions
The DPI interface is available on all 40-pin Raspberry Pi versions (including the Zero). The instructions for the Macintosh Classic CRT, mentioned in this article, will currently only work on a Raspberry Pi 4.

Sources

[Figure 1] Screenshot from pinout.xyz
[Figure 3] Official Raspberry Pi Hardware documentation
[Figure 4] Raspberry Pi 3 Vector Graphic
[Figure 6] Mac Classic II Developer Notes

Raspberry Pi video options (overview)

Summary

The old BBB based project was a lot of fun to create. However, it’s extremely outdated nowadays as well as inconvenient to use. Furthermore, I never managed to display the contents of the framebuffer with that method.

That’s where this mini-project comes in. While the BBB is practically dead, the Raspberry Pi is more than alive. Luckily, it’s very easy to set up the DPI mode and configure it to work with almost any display, even 30-year-old CRTs. This method allows me to render the desktop and the console output without any complicated programs and hardware modifications.

49 thoughts on “Control a CRT with the Raspberry Pi DPI

  1. I like your work.
    But both configuration does not work.
    Yours does not display, Adrian’s work sort of, but not centered.
    I have edited config.txt.
    Only I need is:

    dtoverlay=vga666
    enable_dpi_lcd=1
    display_default_lcd=1

    dpi_group=2
    dpi_mode=87

    dpi_timings=512 1 14 178 0 342 1 0 4 24 0 0 0 60 0 15667200 1

    And this works!

    It is sort of mixed work from your and Adrians.

    Notice dpi_timings is yours, but it is 512 0….342 0, I set it to 512 1….342 1.

    P.s. it is running from Pi4.

    Liked by 1 person

      1. I have to admit, at first try somehow I destroyed 3B+….
        I don’t know what went wrong, I use SN74AHCT125N level convertor.
        Btw, here is picture of my result:
        https://ibb.co/j6qNDx6

        And now we have to find solution add other color into 1 signal.
        Maybe add small diode or small resistor in parallel. Like VGA666 adaptor.

        Liked by 1 person

        1. I have small progess of crt.

          I find config.txt setting you can add dithering.

          Info you can find at: https://retropie.org.uk/forum/topic/28071/piboy-dmg-18bit-color-banding

          I have only connected video line on pin 10 (green7) and 12 (red4).

          For color test I used 265 color test pattern.
          https://askubuntu.com/questions/821157/print-a-256-color-test-pattern-in-the-terminal

          This is before I set dither.
          https://ibb.co/pjgfV3m

          Add this line to end.
          hvs_set_dither=0x3110
          And this is what you get.
          https://ibb.co/qkNQyDC

          I haven’t tried all pins and dither settings, just lucky find this and random guess settings and pins.

          Liked by 1 person

          1. Hello!

            Thank you a lot for sharing this information! Fantastic progress. I’m super glad that my article helped you and you got the CRT working so well! I’ve kinda gone away from using just the Raspberry Pi for this project. Instead, I’m working on a universal solution that I hope will work for many more devices (two new updates are coming this Wednesday and next week).

            Either way, thanks again for sharing the dithering config. I knew that there was a way to enable it, but I couldn’t find a proper link back in 2016 when I first started using the Mac Classic CRT.

            Like

Leave your two cents, comment here!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

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