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.
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:
KERNEL_OPTIONS += -DUART=1 -DUART_RX_BUFFER_SIZE=128 -DUART_TX_BUFFER_SIZE=128 -DSOUND_CHANNEL_5_ENABLE=0 -DSOUND_MIXER=1
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:
UBRR0H=0; //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