Javier Albinarrate - LU8AJA

  • Increase font size
  • Default font size
  • Decrease font size

PIC Text VideoCard, TV Output


I wanted a project to work with in my free time, which would allow me to learn assembler for PIC micros. I also had a spare 5inch small TV, B&W.

So I got into this crazy idea of building a video card, which could be used in my other projects. There are many of similar projects arround, but none of them had the features I was looking for. So I started one from a scracth.

If you know of PIC and video, then you know that current 16bit PICs don't have much horsepower, specially for such a timing stringent task as generating a complicated video signal. This means, forget about colors, and forget about a reasonable definition.




Choosing a PIC


From the bunch of PICs I have arround in my box, I choose the fastest one, which had enough RAM and was otherwise feature less (I am not going to spend a 4550 or a 2550 with USB for an app which does not have USB!).

I ended up selecting the PIC18F2331, with 8KB Flash, 768 bytes RAM, 40pins, SPI, 40MHz, 10MIPS, and no other fancy peripherials.

This meant a 0.1 us cycle time.

Generating the line efficiently

The PAL video signal, has a 64 us line period, which means that during a line you have 640 instruction cycles. To try to output as many pixels as possible in that time, so I had to avoid jumps, branches, etc so I used macros to generate the line.

Also I just rotated the whole port (an idea taken from other projects), wasting pins, but gaining speed.

The macro basically does this:

Output6bitsContinued: macro
    NOP               ;  1 TIC
    NOP               ;  1 TIC
   RRNCF OutputPort   ; 1 TIC Bit 1
    NOP               ;  1 TIC
    NOP               ;  1 TIC
   RRNCF OutputPort   ; 1 TIC Bit 2
    MOVF POSTINC0 , 0 ;  1 TIC Read Buffer into W, then increment pointer
    NOP               ;  1 TIC
   RRNCF OutputPort   ; 1 TIC Bit 3
    MOVWF TBLPTRL     ;  1 TIC ptr to the current char so we can get its pixel line
    NOP               ;  1 TIC
   RRNCF OutputPort   ; 1 TIC Bit 4
    TBLRD*            ;  2 TIC Read the bitstream line into TABLAT !!! 2TIC!!!
   RRNCF OutputPort   ; 1 TIC Bit 5
    MOVF TABLAT, 0    ;  1 TIC line -> W
    NOP               ;  1 TIC
   MOVWF OutputPort   ; 1 TIC Bit 0 (Output new char)

This means that for every pixel, you spend 3 cycles. The visible PAL line has 56us. 56us / 0.1us = 560 cycles. Each pixel is 3 cycles. so each line can have a theoretically 186 pixels.

Note: You can achieve all this with 2 cycles per pixel, leading to a better horizontal resolution, but as unlike all other instructions, TBLRD* takes 2 cycles, this would involve that there is an anomaly pixel column in the bit 5 (the letters have those pixels 50%  longer). At first I implemented it like that , and it took me quite some time to realize why it was hapening, yet if you want more resolution, this can be done, it is not such a big deal. But in the end I decided to have a lower resolution (RAM was a problem too), but equal pixel widths.

Horizontal Resolution

My characters are 5 x 10 pixels, plus margins means 6 x 11. This leads me to a maximum of 31 columns.

Now, if you leave some margins to the sides, the actual numer is smaller. The next problem is RAM. You really don't have much RAM, and you need to handle several variables and a UART/SPI buffer (after all you need to get the bytes in from outside!).

Then vertical resolution is easier. Because you determine it from the ratio to the horizontal resolution and not because of any timming limit like we did in horizontal. So basically, I use 2 horizontal lines for each pixel in each frame. As you should know, the video is composed of 2 interlaced frames, so this means that there are 4 lines per pixel.This leads to a roughly square pixel. Taking out invisible lines and vertical margins, I ended up using of the 506 of the 625 lines.

So I ended up with a text grid composed of 18 columns x 23 rows. It is not that much, and it looks good.

Video signal

Having all that more or less specified, I started the painfull task of building the video signal. It was crucial to have an oscilloscope for this.

I am not going to explain everything right now, because that is already explained elsewhere. Some usefull stuff:

I may add more content on this later...

Painting the character and using the font table

Basically we have a screen buffer in RAM, which has the ASCII characters of our screen, row by row. Then we have the Font table in ROM. It is a lookup table, specially constructed, which holds character by character (NOT line by line), all the 11 lines of the character matrix.  So knowing the current ASCII character, and the current line in the character, we can obtain the corresponding 6 pixels to be shown.

The characters are 6x11 pixels. We waster 2 pixels, and we store 11 lines of 8 bits (1 byte) in ROM, for all of the 256 ASCII characters.

So each ASCII will take 11 bytes in the ROM table. 11 x 256 = 2816 bytes.

These 11 lines are numbered from 1 to 11 :)

The odd thing of this is how the table was built, because what might seem natural to do, that is to put all the lines of the A character, then all the lines of B character and so on... is extremely unefficient when you have to read it, because you have to first position the pointer at the start of the ASCII character, and then add the line offset, so can end p in the right byte of the lookup table. And you have to do all this in the extremely stringent output6bit macro.

Instead, I rearranged the lookup table completely, in a very unnatural way, but once that was done, I can achieve what I want by a single instruction.

At the begining of each line in the video signal, during the H sync pulse, I increment in 1, the TBLPTRH

      INCF TBLPTRH     ; Increment the table pointer by 256

This value will not change during the whole video line.

Then with every new character, I simply put the ASCII value in TBLPTRL like this:

      MOVWF TBLPTRL ;Set the pointer to the current character so we can get its line pixels

And that's it! 1 single cycle in the output6bit macro, positions the pointer to the right place of the font table.

The font table then looks like this:

Expressed like this: ASCII(Charline)

  •                   -- ASCII/Line --------------------------------------------
  • 256 bytes line 1  00/01 01/01 02/01 03/01 ... A/01 B/01 C/01 D/01 ... 255/01
  • 256 bytes line 2  00/02 01/02 02/02 03/02 ... A/02 B/02 C/02 D/02 ... 255/02
  • ....
  • 256 bytes line 11 00/11 01/11 02/11 03/11 ... A/11 B/11 C/11 D/11 ... 255/11

To write such table, with Photoshop, I made a picture with all the characters of the monospaced font I wanted to use, one next to the other. Then I wrote a simple program that reads the BMP, and writes the table accordingly. Even the fact that the table looks complicated, writting such table was the simplest part of the whole project. 

Painting the screen

Yet to be written....

Communications protocol

Yet to be written 

Terminal commands

Yet to be written

A few photos 

Yet to be resized and posted... 

To be continued ...


Last Updated on Sunday, 08 March 2009 00:49  

Google Translate

English French German Italian Portuguese Spanish