User Tools

Site Tools


uart

UART

The gamepad features 3 UART controllers.

Base address Desc.
0xF0004C00 UART0
0xF0004C40 UART1
0xF0004C80 UART2

The entire UART region is mirrored every 256 bytes over 0xF0004C00..0xF0004FFF.

Related IRQs:

IRQ Desc.
0x03 UART0
0x04 UART1
0x05 UART2

UART0 appears to be unused. UART1 is connected to the test points under the battery connector, and is used as a debug console by the diagnostics firmware. UART2 is used for IR comm.

Registers

Offset Desc.
0x00 Data output
0x04 Data input
0x08 IRQ enable
0x0C IRQ status
0x10 ??
0x14 Serial settings
0x18 ??
0x1C Status register
0x20 ??
0x24 Clock divider 1
0x28 Clock multiplier
0x2C Clock divider 2
0x30 Control register
0x34 RX FIFO level
0x38 TX FIFO level
0x3C ??

Base+0x00

Data output.

Data received can be read out from this register. If data is received while the FIFO is already full, the RX error IRQ is raised.

Base+0x04

Data input.

Data written to this register is queued in the transmit FIFO. If the FIFO is already full, the data is discarded (TODO: does it raise an error condition?).

Base+0x08

IRQ enable.

Bits Desc.
0 Enable RX IRQ (4)
1 Enable TX IRQ (2)
2 Enable RX error IRQ (6)

Base+0x0C

Bit 0-3 are the current IRQ status.

Value Desc.
0 ??
1 No pending IRQ
2 Ready to send
4 Data received
6 RX error
12 ??

IRQ 2 is fired when the TX FIFO becomes empty. It is also fired when starting the UART. To acknowledge it, reading this register is enough.

IRQ 4 is fired when a byte of data is received. To acknowledge it, read the data from the data output port.

IRQ 6 is fired when data reception fails. For example this happens if the RX line is pulled low (when polarity set to active high). This also happens on parity errors. The only known way to acknowledge this IRQ is to reset the UART entirely.

Unknown what IRQ 12 would be. Firmware code has a case for it, but I haven't observed it so far.

Base+0x14

Serial settings.

Bits Desc.
0-1 Data bits; 0=5 bits, 1=6 bits, 2=7 bits, 3=8 bits
2 Stop bits; 0=1 bit, 1=2 bits
3 Enable parity bit
4 Parity; 0=odd, 1=even
5 ??? (no effect observed)
6 ??? (causes TX line to be held low)

Base+0x1C

Status register.

Bits Desc.
0 RX FIFO not empty
1 RX FIFO overflow (received data while FIFO already full)
2 RX parity error
3 RX error, ? (FIFO overflow?)
4 RX line error (ie. line held low)
5 1 when ready to send, 0 when sending
6 1 when ready to send, 0 when sending
7 RX error

There doesn't seem to be error bits for TX FIFO overflow.

Base+0x24

Clock divider. This register is 9 bits wide.

Base+0x28

Clock multiplier. This register is 16 bits wide.

Base+0x2C

Clock divider. This register is 16 bits wide.

Base+0x30

Control register.

Bits Desc.
0 Enable UART
1 Data bit order; 0=LSb first, 1=MSb first
2 Line polarity; 0=active high, 1=active low

When bit 0 is cleared, most of the UART registers are disabled (read-only, zero).

Base+0x34

RX FIFO level.

Bits Desc.
0-3 RX state
8-12 RX FIFO level (occupied space, 0..16)

RX state goes through the following values when receiving a data byte:

  • 1 = receiving start bit
  • 6 = waiting for start bit to end?
  • 2 = receiving data bit
  • 7 = waiting for next data bit?
  • 3 = receiving parity bit
  • 8 = checking parity bit?
  • 9 = waiting for stop bits after parity bit?
  • 4 = receiving stop bits
  • 10 = storing byte into FIFO
  • 0 = idle

Base+0x38

TX FIFO level.

Bits Desc.
0-3 TX state
8-12 TX FIFO level (occupied space, 0..16)

TX state goes through the following values when sending a data byte:

  • 5 = fetching byte from FIFO
  • 1 = sending start bit
  • 2 = sending data bits
  • 3 = sending parity bit
  • 4 = sending stop bits
  • 0 = idle

Base+0x3C

Unknown, RX related.

Bits Desc.
0-3 ???
4 Aux. LED (for UART2) (0=off, 1=on)
8-14 ???

Default value 0x102.

Bit 0 seems to break clocking/timing logic? When it is set, data is still clocked out, but in a very fast and broken way (and completely ignores all clock settings).

Baud rate calculation

Three registers are used to determine the serial baud rate.

The formula is as follows:

baudrate = (inputclock / 16) * multiplier / divider1 / divider2

The input clock can be configured in the clock registers. Each UART gets its own clock register: 0xF0000040 for UART0, 0xF0000044 for UART1, and 0xF0000048 for UART2.

Here is a table of settings for common baud rates, given an input clock of 16 MHz:

Baud rate Divider 1 Multiplier Divider 2
4800 1 3 625
9600 1 6 625
19200 1 12 625
38400 1 24 625
57600 1 36 625
115200 1 72 625
230400 1 144 625
460800 1 288 625
921600 1 576 625

The diagnostics firmware contains code (at 0xAC0EE) to calculate the multiplier/divider register parameters for a given baud rate based on the current UART input clock. However, that function produces incorrect results for certain baud rates.

The diagnostics firmware uses a baud rate of 115200, for which the aforementioned function produces correct results.

The following C code can be used. It is based on the diagnostics firmware's code, but with the aforementioned bug fixed.

void calc_baud_rate(u32 clk, u32 baudrate, u32* mul, u32* div)
{
    baudrate <<= 4;
    u32 multiplier = baudrate;
    u32 divider = clk;

    for (;;)
    {
        multiplier = multiplier % divider;
        if (multiplier != 0)
        {
            // baudrate is not a multiple of the input clock

            divider = divider % multiplier;
            if (divider != 0) continue;     // input clock not a multiple of baudrate

            divider = clk / multiplier;
            multiplier = baudrate / multiplier;
            break;
        }
        else
        {
            // baudrate is a multiple of the input clock

            multiplier = baudrate / divider;
            divider = clk / divider;
            break;
        }
    }

    // adjust parameters to fit within 16-bit range
    while ((multiplier > 0xFFFF) || (divider > 0xFFFF))
    {
        multiplier >>= 1;
        divider >>= 1;
    }

    *mul = multiplier;
    *div = divider;
}

Output mul and div parameters can be applied to the multiplier and divider 2 registers, respectively. Divider 1 will be set to 1.

Using with DMA

Much like SPI, the UARTs can be used conjointly with DMA channels 0 or 1.

To send data using DMA, you simply start a write DMA transfer instead of manually writing data to the UART's data output port. You should rely on the TX IRQ (2) to detect transfer completion.

To receive data using DMA, you can start a read DMA transfer at any moment. The DMA transfer will complete when the requested amount of bytes has been received. Note that if a read DMA transfer is active on the UART, it will suppress the RX IRQ. Thus, you should rely on the DMA completion IRQ to detect transfer completion.

uart.txt · Last modified: 2025/05/10 11:55 by arisotura

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki