Basics of Design CC Blog Research & Design Hub

It’s All HD Now

Written by Jeff Bachiochi

The Gameduino GDX3 Dazzler

HDMI has become the standard for transferring high-definition digital audio and video data on computers, monitors, and televisions. In this month’s article, Jeff describes how application designers can use Gameduino GDX3 Dazzler to output HD graphics, text, and audio to any Arduino-compatible HDMI TVs or other large displays.

  • How can application designers output HD graphics, text, and audio to an HDMI TV?

  • What are some uses of the Gameduino GDX3 Dazzler?

  • How does HDMI work?

  • HDMI

  • Gameduino GDX3

  • Dazzler

  • Bridgetek BT815 GPU

  • VGA 1024 x 768

  • Spartan-6 FPGA

  • EEPROM

  • Raspberry Pi Pico

  • Adafruit’s Feather M4

  • Express

  • PJRC Teensy 4.x

  • Arduino shield

  • MEGA

  • Wii controllers

You can’t avoid it. For years now you have not been able to buy a TV that isn’t high definition (also known as High Def or HD). Even our PCs have HD screens. That means that graphic cards have to keep up with the higher-resolution displays. Not that gamers have ever been left wanting. Many games rival streaming for picture quality, since a display is only as good as its content and the bandwidth you provide. So where does that leave the lowly micro that doesn’t need such high resolution output, but would still like to display content on a large screen?

Up to this point if you didn’t want to use your PC as a display screen through the serial port, you needed to provide some kind of video interface to a TV. NTSC (and PAL in Europe) were the formats that you had to produce to use your TV directly. Or, in early cases, you needed to actually broadcast on a channel that your TV could pick up. At the time, computer monitors were the only devices that had an input that avoided the whole modulated broadcast thing. But, even these composite signals were a conglomeration of sync pulses and analog (video) levels, and were a lot for our little micro to handle.

While the basic idea stays the same, resolutions have steadily increased from the original Monochrome Display Adapter (MDA, 720 × 350) and Color Graphics Adapter (CGA, 640 x 480) to EGA, VGA, SVGA, XGA, WXGA, SXGA, WSXGA, UXGA, and WUXGA (Widescreen Ultra Extended Graphics Array) with a display resolution of a whooping 1920 x 1200 pixels. You’ll note that at the time, these required high-end displays. Display output is video only, with a refresh rate of 60Hz.

As the display technology has moved from Cathode Ray Tubes (CRT) to LCDs and LEDs with actual individual pixels, and signal transmission has moved from analog to digital, a new standard has been developed.

WHAT IS HDMI?

High-Definition Multimedia Interface, or HDMI, is a digital audio, video, and control signal format. The HDTV is born with a minimum resolution of 1920 x 780 pixels and the ability to refresh at up to 240Hz. The communication medium has up to six Transition Minimized Differential Signaling (TMDS) data channels, the data clock, Consumer Electronics Control (CEC), I2C, power, ground, and hot-plug detect. Two types of cabling exist, types A and B, where type A is the most common. Table 1 shows the signals in each cable type.

Table 1
Of the two existing HDMI types, type A is the most common. Type B has additional data channels for higher throughput.
Table 1
Of the two existing HDMI types, type A is the most common. Type B has additional data channels for higher throughput.

You may be wondering about the number of data channels. If you think back to the CGA interface, you will remember that the video was actually sent as separate RGB signals. Here, RGB digital data is sent separately on differential channels. The TMDS data used as a link for the digital component video signals is shown in Figure 1. CEC is a one-wire bidirectional serial bus that is used to perform remote control functions. The I2C channel is used by the source to read the Enhanced Extended Display Identification Data E-(EDID) from the HDMI devices to learn what audio/video formats it can accept.

Figure 1
Twisted pair drivers and matching receivers keep the noise low and allow for a lengthy cable.
Figure 1
Twisted pair drivers and matching receivers keep the noise low and allow for a lengthy cable.

It is not the purpose of this article to go into the HDMI format any further. My point is just to show how quickly things have changed. My article back in 2009, “A World Without NTSC. Bridge the Gap Between NTSC and VGA,” (Circuit Cellar #226, May 2009)[1], showed how to create VGA output using Parallax’s Propeller 1, a microcontroller with eight 32-bit cores. HDMI speeds require some kind of dedicated hardware. So where does that leave us little guys who want to display output from our project on some bigger screen. With all these HDMI displays, it makes my mouth water just thinking of it.

GAMEDUINO

It began as a project by James Bowman to develop an Arduino shield for use with larger LCDs with integrated touchscreens. A Bridgetek BT815 GPU was used to give graphics and touch control for 4.3”, 5”, and 7” LCDs, along with audio control via an SPI interface. The target for all the BT GPUs is a parallel LCD, but Bowman used the BT815 to supply a VGA 1024 x 768 interface. The TermDriver (Figure 2) became a text-only Arduino shield [2]. While the VGA out now made using high res monitors possible, the real holy grail would be HDMI.

Figure 2
TermDriver listens on a serial line and emulates a terminal through the standard VGA interface. It gives embedded microcontrollers a real console, if you have a VGA monitor [2].
Figure 2
TermDriver listens on a serial line and emulates a terminal through the standard VGA interface. It gives embedded microcontrollers a real console, if you have a VGA monitor [2].

This wouldn’t be possible without some specialized hardware. Bowman stumbled upon an application note from Xilinx, “Implementing a TMDS Video Interface in the Spartan-6 FPGA” [3]. It would fit in a low-end Spartan-6 FPGA. So this became the basis of the PCB-based GD3X Dazzler module. The module contains the BT815 and a Spartan-6 FPGA, and a couple of serial EEPROMs, along with a few discreet parts and a HDMI connector. The module concept allows it to be used on several different interfaces, including the Raspberry Pi Pico, Adafruit’s Feather M4 Express, the PJRC Teensy 4.x, and my favorite—the Arduino shield.

All interfaces include an SD card slot and two Wii-compatible I2C controller ports. While the SD card slot is obviously useful for graphics and video files, it can be used for other purposes. The Wii interface is not so obvious. Remember, the original purpose of the BT815 was for an LCD with touch input. Well, touch isn’t an option for today’s HDMI displays. The Wii controllers can be used to give the user this feature and also allow multi-user input!

As an Arduino shield, the GD3X Dazzler supports the UNO footprint. Because SPI on the UNO uses D10-D13, using an extended Arduino, such as the MEGA, requires its SPI, which has moved away from those digital I/O, to be rewired. Since all Arduino I/O defaults to an input pin, the easiest approach is to just add wires from the MEGA’s SPI to the UNO’s SPI pins. Although you won’t be able to use those digital I/Os for anything else, the MEGA offers so many additional I/O that this will not be an issue.

THE SIMPLE FIRST STEP

To allow the GD3X Dazzler to be used with minimum code, you can enable the TEXT mode, in which the Dazzler only needs to connect to the Serial port Tx0/Rx0. By using the code in Listing 1, the Dazzler will boot into TEXT mode.

Listing 1
This application sets up the SPI interface to the Gameduino for Text Mode. Text Mode accepts serial print statements to provide HDMI output in a Serial Terminal Format.
# include <SPI.h>
byte row;
byte column;
byte attribute;
byte fore;
byte back;
/**************************************/
void setup()
{
	Serial.begin(115200);
 	SPI.begin();
 	pinMode(8, OUTPUT);	// GPU select
 	pinMode(9, OUTPUT);	// SD select
 	pinMode(10, OUTPUT);	// Dazzler select
 	digitalWrite(8, HIGH);	// GPU disabled
 	digitalWrite(9, HIGH);	// SD disabled
 	digitalWrite(10, HIGH);	// Dazzler disabled 
 	Serial.println(“Serial/SPI ready”);
 	digitalWrite(10, LOW);	// Dazzler enable
 	SPI.transfer(0x41);		// Boot from slot
 	SPI.transfer(0x01);		// slot 1
 	digitalWrite(10, HIGH);	// Dazzler disable
}
/**************************************/
void loop()
{
 	cursorPosition();		// cursor position
 	colors(); 			// display Hello world
 	Serial.print(“Hello world “);	// display Hello world
 	delay(100);
}
/**************************************///34x128
void cursorPosition()
{
 	row = random(1,35); 	// random row 1-34
 	column = random(0,10) * 12 + 1;	// random column 0-9 * 12 +1
	Serial.print(“\e[“ + String(row) + “;” + String(column) + “H”);	// esc position
}
/**************************************/
void colors()
{
 	attribute = random(0,2);	// normal/highlight 0-1
 	fore = random(30,38);	// random foreground color 30-37
 	back = random(40,48);	// random background color 40-47
 	Serial.print(“\e[“ + String(attribute) + “;”);	// esc command for
 	Serial.print(String(fore) + “;” + String(back) + “m”);	// color
}
/**************************************/
Listing 2
This simple application shows how easy it is to use the graphics mode to provide control over color and text size via the Graphic Commands.
# include <EEPROM.h>		// non volitile storage library
# include <SPI.h>			// SPI library
# include <GD2.h>			// Graphic Dazzler library
/**************************************/
void setup()
{
	GD.begin();		// enable Graphic Dazzler library
}
/**************************************/
void loop()
{
 	GD.ClearColorRGB(random(0,256),random(0,256),random(0,256));	// background color
 	GD.Clear();		// cls of all graphics
 	// 1280x720 HDMI resolution
 	for(int i=16; i<=31; i++) 	// do all internal fonts
 	{
 		GD.ColorRGB(random(0,256),random(0,256),random(0,256));	// foreground color (text)
 		String myArray = “Font Size: “ + String(i);			// myArray = text + number		
 		GD.cmd_text(640, (i-15) * 40, i, OPT_CENTER, myArray.c_str());	// display centered text
 	}
 	GD.swap();  // swap display memory
 	delay(1000);  // pause 1 second
}
/**************************************/
Listing 3

Here's a quick demo application to show off the Audio Instruments available from the Gamduino through the HDMI output.
/**************************************/
void playInstruments(byte s)
{
 	GD.Clear(); // cls of all graphics
 	// 1280x720 HDMI resolution
 	String myString = “This Instrument: “ + myInstrumentStrings[s] + “=” + String(myInstrumentValues[s]);
 	GD.cmd_text(640, 360, 31, OPT_CENTER, myString.c_str()); // HDMI display centered text
 	Serial.println(myString);			 // send it out the serial port for debugging
 	GD.swap();				// change the display	
 	for(byte n=60; n<66; n++)			// 6 note midi numbers from 60 to 66 (C4 to F#4)
 	{
 		GD.play(myInstrumentValues[s],n);
							// the note is played by passing the instrument value and midi note)
 		delay(500);			// let it play for ½ second
 	}
}
/**************************************/
Listing 4

Here this command uses an RGB value to set the screen background color and the GD.Clear() then clears any foreground text and graphics. The color is the standard 3 byte R, G, and B values. All of the Dazzler commands are available from the #include GD2.h library.
void ClearScreenAndHome()
{
	GD.ClearColorRGB(backgroundRGB);
				// set background color
 	GD.Clear();
				// clear all graphics (foreground items)
}
Listing 5
While a command is added to set the color of the text, the text command handles positioning. The first two parameters define the X and Y position of the text. The third parameter is the text size, followed by any adjustment to the X and/or Y string values to automatically do any requested centering. The last parameter is the actual text. This must be presented in an character array, thus the conversion used form String to array, myString.c_str().
void showLine()
{
 	GD.ColorRGB(foregroundRGB); 
					// foreground color (text/graphics)
 	GD.cmd_text(640, myRow * 45, 31, OPT_CENTER, myString.c_str()); // display centered text
}
Listing 6

This routine waits for a button to be pressed on the Host's Key Fob.
{
	myRow = 14;
	myString = “Host, Hit any key to continue.”;
 	foregroundRGB = Red;
 	showLine();
 	GD.swap();
 	//
 	waitForRF();
 	while(myID != teacherFob)
 	{
 		waitForRF();
 	}
 	GD.play(SWITCH);
}
Listing 7

Here's the code for the opening screen. You can see what is produced in figure 4.
void openingDisplay()
{
 	clearScreenAndHome();
 	//
 	myRow = 1;
 	myString = “RF Quizer”;
 	foregroundRGB = Green;
 	showLine();
 	//
 	myRow = 5;
 	myString = “from the bench #386”;
 	foregroundRGB = White;
 	showLine();
 	//
 	myRow = 6;
 	myString = “Circuit Cellar Magazine”;
 	foregroundRGB = Blue;
 	showLine();
	//
 	myRow = 10;
 	myString = “Getting things ready...”;
 	foregroundRGB = Yellow;
 	showLine();
 	//
 	anyKeyToContinue();
}

In the loop() function, you can see that simple serial commands can be used to display text on the HDMI-connected monitor/TV. While you have escape functions to control the screen (clear and cursor movement) and TEXT color control, you are essentially limited to two text sizes in two orientations. These are by character format:

\e[0m = 128 x 34 landscape
\e[1m = 182 x 48 landscape
\e[2m = 80 x 64 portrait
\e[3m = 102 x 85 portrait

So there are lots of characters, as shown in Figure 3, but no control of text size, so it it is difficult to read from a distance. Note the “boot from” and “slot” commands in the setup() function. There are eight slots, 0-7, presented upon power-up. Unless you send SPI commands to choose a slot from 1-7, the Dazzler boots into slot 0, the “Dazzler screen.” Slots 0 & 1 are fixed as “Dazzler screen” and “TEXT mode”. The other slots can be cued up with a Wii joystick by holding the Start + x + y keys from the Dazzler screen. The SD card and Wii controllers can be accessed from an Arduino sketch, but I will leave that up to you to discover if you are interested. We need to steer back toward my intention of using this as an HDMI display for last month’s project.

Figure 3
Text mode makes it simple to display text on a high-resolution screen, and the esc commands give some control over position and color. However, you have no real control over text size. I need big text that can easily be seen from a distance.
Figure 3
Text mode makes it simple to display text on a high-resolution screen, and the esc commands give some control over position and color. However, you have no real control over text size. I need big text that can easily be seen from a distance.
SERIAL PERIPHERAL INTERFACE (SPI)

Although using the Dazzler in TEXT mode is easy, it does have limitations, and to me the major limitation is text size. By using the GPU library (GD2.h) we have more control over the screen. The library has several fonts embedded, or we can provide a font files. Let me introduce a similar “Hello world” sketch that makes use of the GD2 library (Listing 2).

Here we are using the SPI and GD2 libraries. When we initialize the GD2 library, SPI activity cancels the Dazzler screen. Immediately, we set the background color to a random RGB color, clear the foreground of any pixels, and then loop to change the foreground to another random RGB color, and display some text of all of the 16 available internal fonts centered in separate rows. This does not change the display, but alters a page of display memory. The swap() function updates the display instantaneously by swapping memory pages . After pausing for 1 second, the loop() repeats, and we see a new background color with the same text displayed, but in new foreground colors.

Note here that the text being printed is expected to be in a character array, as opposed to a String, but you can use the “c_str” method, mystring.c_str(), to change it. Now we have better control of the font size. Even larger fonts can be had by supplying your own external font. The largest internal font can be sufficiently seen from a distance.

SOUND

The SPI mode offers an audio interface that will allow your applications to have a new dimension. Many prestored sounds can be played—Click, Switch, Cowbell, Notch, HiHat, Kickdrum, Pop, Clack, and Chack. Several instruments can be used to play tunes—Harp, Xylophone, Tuba, Glockenspiel, Organ, Trumpet, Piano, Chimes, Musicbox, and Bell. There are also Waves, Alarms, DTMF tones, and PIPs, but you’ll have to download this application from the website for the complete list, because it is quite long. Here is just the playInstruments() routine, which plays one instrument, depending on the byte passed. It also shows some text on the display for each instrument (Listing 3).

GRAPHICS AND DISPLAY

Those of you who have ever made any game graphics probably know that this can all get a bit overwhelming. While bitmaps, line drawing, scene composing, and “special effects” have their places, I will not go over all the features that are open to you. One of the most dazzling effects is the spinning cube, which has a different video being played on each face as it spins in space. I do not pretend to be a graphic artist, so I’ll leave that to those of you that may wish to explore the possibilities. You can read about all this in more detail by downloading the reference material, The Gameduino 2 Tutorial, Reference and Cookbook [4], and Gameduino 3X Dazzler manuals [5].

I originally wanted to use this board along with the project I presented last month, “From the Bench: Build Quiz Game Based on Key Fob,” (Circuit Cellar #385, August 2022) [6]. At the time, the only output available was a PC receiving serial output and casting its screen to a HDTV. As I previously mentioned, the serial output could be simply displayed using the Text Mode. However, with a fixed screen format of 34 rows of 128-character columns, the character size is extremely small and is not suitable for viewing from a distance.

So the best option is to use the SPI mode, where we have better control of the text size and have the added benefit of sound. You can get creative with some of the other effects available, but for now I’ll be happy with some fairly simple use of screen colors and a sound here and there.

Last month [6] I used serial print statements and esc commands to handle some basic screen formatting. To use the Dazzler’s HDMI output, we must redirect output to the Dazzler shield via the SPI bus. The esc commands are replaced with actual positioning commands, and we have the added benefit of controlling both background and foreground color. While I used separate variables to hold background and foreground colors, it is the same command that handles color. How a color is used depends on when it is used. The GD.ClearScreenAndHome() routine is simplified (Listing 4).

Here this command uses an RGB value to set the screen background color and the GD.Clear(), then clears any foreground text and graphics. The color is the standard 3-byte R, G, and B values. All of the Dazzler commands are available from the #include GD2.h library.

To display a screen using the Dazzler, you must prepare it and then display it using the GD.swap() function. This makes it seem instantaneous, since all drawing takes place before the swap. This means that if you want to add something to the present screen, you need to redraw the screen, add the new items, and then swap. To the user, it looks like something was added, but in reality, it’s a whole new screen. This takes a change in the thought process on how things are done.

Displaying some text at a certain location is actually a bit easier than the serial method, because that used a separate routine to pad the text so it would be centered on the screen. The actual text command here includes the ability to have the text centered horizontally, vertically, or both from within the text command (Listing 5).

While a command is added to set the color of the text, the text command handles positioning. The first two parameters define the X and Y position of the text. The third parameter is the text size, followed by any adjustment to the X and/or Y string values to automatically perform any requested centering. The last parameter is the actual text. This must be presented in a character array, hence, the conversion used form String to array, myString.c_str().

Separating items into routines makes screen control a bit easier. Commonly, I’ll want to hold a screen until the teacher, host, or emcee wants to move on. This routine can be used to add a text prompt and wait for the host to respond with a button click. Here I’ve added the sound of a mechanical switch being switched when the response is made (Listing 6).

The open screen can therefore be created from these functions and a few text lines (Listing 7).

The openingDipslay() routine will now display this info on an HDMI screen, as shown in Figure 4. Except for having to divide some routines into multiple functions, the basic skeleton of last month’s application remains the same. I had to modify the wiring to allow the Dazzler to take over the hardware SPI. I was not able to get my SD card to play well with the Dazzler’s use of the SPI. After a bit of searching, I was able to find a library to handle the SD card through a software SPI using other I/O pins. Since I had used every pin available through the UNO shield interface, I had to reassign three of the switch inputs to the secondary SPI port. I ended up needing only one switch to force the relearning of the five key fobs, so all is well. The Dazzler has pass through connectors, so this project is contained in a three-board stack.

If you have been referring to last month’s project [4], you may remember my use of characters to create four boxes that reflected the status of the four player key fobs. I have the ability to do real graphics, so I replaced these with circles that reflect status in two ways. First, the color of the circle initially is red, and changes to green when that player enters their answer through the A, B, C, or D button on their key fob. Second, the text on top of the circle indicates the player number, their chosen answer, or a ‘?’ if the answer was not received in time. Needless to say, the screen is constantly being redrawn here, as seconds tick off and any player answers a question. Figure 5 shows a game in process, in which the timer has expired and the correct answer is revealed.

Figure 4
Using SPI commands, you can have very large text and control of the background and foreground colors, plus graphics, audio, and picture files.
Figure 4
Using SPI commands, you can have very large text and control of the background and foreground colors, plus graphics, audio, and picture files.

Figure 5
In my application described in last month’s project [6], a Jeopardy-style, multiple-choice answer game, teams compete for the best scores. Teams answer by pressing a button, A, B, C, or D, on a wireless key fob. Questions and answers are presented randomly from a chosen text file on an SD card.
Figure 5
In my application described in last month’s project [6], a Jeopardy-style, multiple-choice answer game, teams compete for the best scores. Teams answer by pressing a button, A, B, C, or D, on a wireless key fob. Questions and answers are presented randomly from a chosen text file on an SD card.

CONCLUSION

While writing this article, I noticed that Brian Miller had an article in Circuit Cellar #382, “Building a Touchscreen Display” using the Bridgetek BT815 [7]. In his article, you can find out more about the BT815 and how it can be used with an LCD display. As for me, I’m happy as a clam to be able to finally interface with an HDMI device. Figure 6 shows the final three-board stack for this project. One note— I could not get some TVs and monitors to display the HDMI output from the Gameduino. After scoping around on the output, I found that by grounding the HDMI connector’s metal shield, the display would work fine. A short piece of wire from the frame to ground on the Gameduino took care of that.

Figure 6
The Gameduino shield has been added to the original circuit presented in my article last month [6], to allow the game to be played directly on any TV/Monitor with an HDMI input.

This project seemed to have a few more detours than many of the projects I’ve presented in the past. Does that mean:

  • A – I’m getting dumber by the day?
  • B – Technology is getting harder to work with?
  • C – Time is speeding up?
  • D – My midi-chlorian concentration is waning?

Answer: Possibly “All of the above.” The only thing I can be sure of is that there is too much to do, and so little time. 

RESOURCES
ECE4760 Final Project: Sound Localization Assisted GPS Navigation. https://people.ece.cornell.edu/land/courses/ece4760/FinalProjects/f2015/ath68_sjy33_vwh7/ath68_sjy33_vwh7/ath68_sjy33_vwh7/SoundNavigation/SoundNavigation.htm. Accessed 18 Feb. 2022.
ECE 4760 Final Project: Auditory Navigator. https://people.ece.cornell.edu/land/courses/ece4760/FinalProjects/s2010/gp244_nva2_mrk99/gp244_nva2_mrk99/index.html. Accessed 18 Feb. 2022.
Aguerrevere, D., Choudhury, M. & Barreto, A. (2004). Portable 3D Sound / Sonar Navigation System for Blind Individuals. LACCET.
Gunther, R., Kazman, R. & MacGregor, C. (2004). Using 3D sound as a navigational aid in virtual environments. Behaviour & IT. 23. 435-446. 10.1080/01449290410001723364.
Schoop, E., Smith, J., & Hartmann, B. (2018, April). Hindsight: enhancing spatial awareness by sonifying detected objects in real-time 360-degree video. In Proceedings of the 2018 CHI Conference on Human Factors in Computing Systems (pp. 1-12).
Head-Related Transfer Functions – The CIPIC Interface Laboratory Home Page. https://www.ece.ucdavis.edu/cipic/spatial-sound/tutorial/hrtf/. Accessed 18 Feb. 2022.
Zhou X. (1996). Virtual reality technique. Telecommunications Science. 12(7): 46-–.
PIC 32 Hardware Manual. https://people.ece.cornell.edu/land/courses/ece4760/PIC32/index_Ref_Man.html
PIC32 Peripheral Libraries. http://ww1.microchip.com/downloads/en/DeviceDoc/32bitPeripheralLibraryGuide.pdf

Code and Supporting Files

PUBLISHED IN CIRCUIT CELLAR MAGAZINE • SEPTEMBER 2022 #386 – Get a PDF of the issue

Keep up-to-date with our FREE Weekly Newsletter!

Don't miss out on upcoming issues of Circuit Cellar.


Note: We’ve made the Dec 2022 issue of Circuit Cellar available as a free sample issue. In it, you’ll find a rich variety of the kinds of articles and information that exemplify a typical issue of the current magazine.

Would you like to write for Circuit Cellar? We are always accepting articles/posts from the technical community. Get in touch with us and let's discuss your ideas.

— ADVERTISMENT—

Advertise Here

Sponsor this Article
 | Website

Jeff Bachiochi (pronounced BAH-key-AH-key) has been writing for Circuit Cellar since 1988. His background includes product design and manufacturing. You can reach him at: jeff.bachiochi@imaginethatnow.com or at: www.imaginethatnow.com.

Supporting Companies

Upcoming Events


Copyright © KCK Media Corp.
All Rights Reserved

Copyright © 2024 KCK Media Corp.

It’s All HD Now

by Jeff Bachiochi time to read: 16 min