From Uzebox Wiki
Jump to navigation Jump to search


The UART interface provides a common communications protocol to allow the Uzebox to communicate with other devices. The physical interface is made of two lines on the expansion port, Rx(receive) and Tx(transmit), and a one byte buffer for each line. Software can write a byte to the Tx buffer and the internal circuitry of the '644 will handle transmitting it bit by bit out the Tx line. Likewise, the Rx circuitry will receive information bit by bit into the byte buffer. Many devices have some means to communicate using this protocol, from an Atari Jaguar to a Cisco network switch console port.

The physical pins Rx and Tx are 5v TTL logic levels. You can use a 3.3v logic high to reliably transmit to the Uzebox Rx pin, so long as the device is tolerant to the 5v that the Uzebox will send out the Tx pin to the receiving device Rx pin. As an example, the ESP8266 is connected successfully in this manner without additional circuitry.

Kernel Support

There is built in UART support in the Uzebox kernel. The functionality is provided as a set of API functions, and both Tx and Rx buffer capabilities. When using the Tx and/or Rx buffer, it frees extra cycles for your program to run. The actual buffering happens regularly during the HSYNC interrupt and the data can be used later when the program gets around to it instead of being locked into reading or writing to the UART hardware. This is required due to the nature of UART communication and the single byte hardware buffer, so that bits are not lost. It is important that you have a large enough buffer for the UART speed you are using as well as how quickly you will be able to read or write the buffer. The size of the transmit sets a maximum practical limit to how much data you can send per 1/60th second frame, the bigger the better. The receive buffer sets a limit to the amount of data you can receive per 1/60th second. Currently it is believed a maximum speed of 57600 is reliable with screen rendering on. To reliably use this data will require a 96 byte buffer at the minimum, since 57600/60 = 96 bytes per 1/60th second. If your program cannot use up the buffer quickly enough during free cycles you will lose data, so you may need to have more of a safety margin depending on your application. Transmit buffer works the same way in that to reach full Tx speed requires 96 bytes buffer, and for that buffer to be refilled every 1/60th second. Again to reach maximum performance you may need larger than minimum buffer size. Higher baud rates could be possible using custom code and turning scanline rendering off, but it's not particularly useful for games.

To use Rx and Tx buffers you must enable first them in your make file like this:


Keep in mind you must use the inline sound mixer for UART to work. In this example we have used 128+128=256 bytes of ram(4 ram tiles), but we have very nice large buffers for good throughput at maximum speed. You will notice that we disabled the 5th audio channel to gain HSYNC time for the buffering. You can keep the 5th channel, if you are willing to sacrifice the ram and disable the inline sound mixer with -DSOUND_MIXER=0. You may need to experiment a bit with both buffer size and speed to find the best solution for your program. Keep in mind that the buffers must be powers of two; 2,4,8,16,32,64,128,256,512,1024, or 2048 bytes are the possible sizes. Now that the buffers exist for the kernel to use, they can automatically be used during HSYNC and the main program just has to keep up to date with UART activity. Before the kernel can make that happen however, you need to setup the UART flags in memory to make the hardware match the settings you need. Some common settings are:


//UBRR0L=185;	//9600 bauds	960 bytes/s	16 bytes/field
//UBRR0L=92;	//19200 bauds	1920 bytes/s	32 bytes/field
//UBRR0L=92;	//38400 bauds	3840 bytes/s	64 bytes/field
UBRR0L=30;	//57600 bauds	5760 bytes/s	96 bytes/field

UCSR0A=(0<<U2X0); //double speed mode(reliable disabled? must double UBRR0L values IF enabled)
UCSR0C=(1<<UCSZ01)+(1<<UCSZ00)+(0<<USBS0); //8-bit frame, no parity, 1 stop bit
UCSR0B=(1<<RXEN0)+(1<<TXEN0); //Enable UART TX & RX

See the UzenetDemo in the kernel source code for an example program. These are the current API functions:

void UartGoBack(unsigned char count);//sets the read position back count number of bytes
u8 UartUnreadCount();//returns how many unread bytes are left in the Rx buffer
s16 UartReadChar();//returns the byte at the current read position in the Rx buffer, then increments the read position
s8 UartSendChar(u8 data);//put the byte in the Tx buffer at the current write position, then increments the write position
bool IsUartTxBufferEmpty();//indicates whether there is still unsent data left in the Tx buffer
bool IsUartTxBufferFull();//indicates whether more space is left in the Tx buffer
void InitUartTxBuffer();//do this before using the Tx buffer
void InitUartRxBuffer();//do this before using the Rx buffer


It is possible to use your Uzebox running Uzesynth (available in the Uzebox repo as MusicDemo) as a MIDI synth via UART, which is could be useful for anyone wanting to compose music for their Uzebox game.

Currently the Uzebox sound engine doesn't support RAM patches. The sound patch data is loaded from flash memory and so cannot be modified at runtime.


Make sure your USB serial adapter uses or is configured to use 5V, connect the ground to the ground (usually green) and the TX header on your Linux machine's serial adapter to the RX header on the Uzebox.

ttymidi isn't in the Debian repositories so I had to build it from source but it was as simple as typing make in its dir after cloning or downloading its source.

ttymidi seems to default to using 115200 baud which didn't work for me so you have to specify to use 57600 baud.

./ttymidi -b 57600 -v

After starting ttymidi, you can use qjackctl's graph connection window to connect the ALSA MIDI output of qtractor, Muse, Ardour, REAPER or your ALSA MIDI supporting app of choice into the MIDI input of ttymidi. You can do this on the command line by using aconnect.

The first four MIDI channels control channels 1-4 of the Uzebox's sound engine. For channel 3, the noise channel, the note number corresponds to the patch number defined in your file.