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.
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:
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:
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:
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:
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:
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
This step is fairly simple. Just connect the HSYNC line of the Raspberry Pi (GPIO 5) and the VSYNC line of the Pi (GPIO 3) to the HSYNC and VSYNC lines of the display. Don’t forget to connect a ground wire of the display to a GND pin on the Pi.
Then, connect the color lines of the Raspberry Pi to your display. This step varies, depending on your configuration and display. The following GPIO pins can be used to transmit the color information:
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 simply 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. However, 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
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:
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.
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.