VGA Output Example (Timed DMA Transfers)

This example demonstrates the generation of an analogue VGA signal from a PIC32 microcontroller using general output pins. Unlike the vga and vga-pmp examples, it employs a regular interrupt condition to schedule single-byte (single-pixel) DMA transfers instead of a single whole-line transfer.

The principal advantage of this method over the whole-line transfer method is its production of pixels with consistent widths. The principal disadvantage is the significant loss of horizontal resolution due to the latencies involved in propagating interrupt conditions to the DMA controller and thereby initiating each transfer.

Employing a peripheral clock that has half the frequency of the system clock should ensure the stability of the picture, since the lower frequency may make transfers easier to schedule. The peripheral clock should provide a more forgiving deadline for each transfer, permitting late transfers to complete on time.

Meanwhile, matching the system and peripheral clock frequencies appears to leave the scheduling of transfers open to uncertainty, with transfers being more readily delayed by other activity in the system, and with instability of the picture being the result.

Unlike the vga example, but in common with the vga-dual example, this example employs two DMA channels for pixel data which are interleaved to investigate a potential remedy for the wide pixel effect. This seems to preserve consistent pixel widths only with a transfer cell size of 1: other cell sizes suffer from the wide pixel problem. Despite not offering the greater throughput of larger cell sizes, merely employing dual channels increases throughput for a cell size of 1, making the technique worth using.

In contrast to the vga and vga-pmp examples, a special DMA channel is employed to initiate the pixel transfer process without actually transferring any pixel data itself. The channel arrangement is as follows:

Transfer Initiator DMA Channel Transfer Activity
Timer2 DMA1 zerodata -> PORTB
Timer3 DMA0 linedata -> PORTB
Timer3 DMA2 linedata -> PORTB
Timer3 DMA3 zerodata -> PORTB

The real purpose of this channel (DMA1) is to capture the Timer2 interrupt condition and to enable the following channels (DMA0, DMA2) through channel chaining. Having been enabled, DMA0 and DMA2 are then able to conduct transfers at a tempo dictated by Timer3. Finally, DMA3 acts as the "reset" or "zero" channel to ensure that the pixel level is set to black at the end of each display line.

In principle, other initiating conditions can be used instead of Timer3, which is configured to produce such conditions as frequently as possible:

Hardware Details

The pin usage of this solution is documented below.

PIC32MX270F256B-50I/SP Pin Assignments

MCLR#          1  \/  28
HSYNC/OC1/RA0  2      27
VSYNC/OC2/RA1  3      26 RB15/U1TX
       D0/RB0  4      25 RB14
       D1/RB1  5      24 RB13/U1RX
       D2/RB2  6      23
       D3/RB3  7      22 RB11/PGEC2
               8      21 RB10/PGEC3
          RA2  9      20
          RA3 10      19
       D4/RB4 11      18 RB9
              12      17 RB8
              13      16 RB7/D7
       D5/RB5 14      15

Note that RB6 is not available on pin 15 on this device (it is needed for VBUS unlike the MX170 variant).

UART Connections

UART1 is exposed by the RB13 and RB15 pins.

Data Signal Routing

For one bit of intensity, two bits per colour channel:

D7 -> 2200R -> I

I -> diode -> R
I -> diode -> G
I -> diode -> B

D6 (not connected)

D5 -> 470R -> R
D4 -> 1000R -> R
D3 -> 470R -> G
D2 -> 1000R -> G
D1 -> 470R -> B
D0 -> 1000R -> B

HSYNC -> HS
VSYNC -> VS