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.
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.
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
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:
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:
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
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:
Furthermore, there may be differences in the various iterations or versions for different regions/countries of the Macintosh Classic. However, that’s just speculations.
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.
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.
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.
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.