VGA clock with USB interface PDF Print E-mail
User Rating: / 16
PoorBest 
Written by Dario Greggio   
Friday, 21 July 2006

This piece of software generates a standard VGA signal, displaying in it a standard 6-digit date-time clock. The chosen color is green, but it can easily changed to red or blue (no gradients, unfortunately :-( )


Its use could be the creation of a big, luminous wall-clock, by using old VGA monitors ready to be dismissed. Or, brand new flat LCDs, if you feel like!

Please note all code is released AS-IS and should used at the users own risk, no guarantee is made by PICcoder.co.uk or the author with regards to the accuracy of the diagrams or code contained in this article. 

Discuss this article on the forums. (8 posts)

    
 
The biggest effort in designing this was the strict timing of VGA signalling; but, as we'll see later, one bigger trouble has been letting the peripheral behave as an USB-device, since Microchip stack does suffer a lot when heavy interrupt processing takes place in the meantime.


Basically, these are the needed sync signals for a VGA monitor, used in basic-VGA mode (640x480).

 

 Horizonal Timing
       
 Horizonal Dots  640  640  640   
 Vertical Scan Lines
350 400  480   
 Horiz. Sync Polarity POS POS  NEG   
 A (uS)
31.77 31.77  31.77  Scanline Time 
 B (uS)
3.77  3.77  3.77  Sync pulse length 
 C (uS)
1.89  1.89  1.89  Back porch 
 D (uS)
25.17  25.17  25.17  Active video time 
 E (uS)
0.94 0.94  0.94 Front porch

 

wave form 1


 Vertical Timing         
 Horizontal Dots
 640   640   640   
 Vertical Scan Lines
 350   400   480   
 Vert. Sync Polarity  NEG   POS   NEG   
 Vertical Frequency
 70Hz   70Hz   60Hz   
 O (mS)  14.27  14.27   16.68  Total fram time 
 P (mS)
 0.06  0.06  0.06  Synce Length
 Q (mS)
 1.88  1.08  1.02  Back porch
 R (mS)
 11.13  12.72  15.25  Active video time
 S (mS)
1.2   0.41  0.35  Frontporch

 

wave form 2

 

(Information source HP D1194A Super VGA Display & HP D1195A Erognomic Super VGA Display Installation Guide, Hewlett Packard)

(actually, a lower res might have been good as well, and easier to achieve with PIC: unfortunately, VGA monitor don't accept things like 320x240, and usually not even the old EGA (640x350); and, anyway, the real trouble is in the Horizontal 640)I took a breadboard vga3with a 18F2550 that we created for some general-purpose prototypes, and put on a crystal (20MHz, but 4MHz is okay, given the correct CONFIG bits), a USB cable, and some capacitors as needed for USB. Then, I took some pins from the Port B and connected a VGA female connector (you'll find the exact connections into the sources, IO_CFG.H): it's 6 wires, ground, R-G-B, Hsync and Vsync.

I wanted all the timing being generated with an interrupt from Timer0, or stability reasons.In a first draft, I tried to have an interrupt for every "pixel": in my mind, achieving 32x16 pixels would have been enough for my goal. Thus, as can be derived from the tables above, if a line lasts about 30uS, we have to present out a value from memory every 1uS circa.But, because of interrupt latency and basic variable saving, this was not feasible (consider that we are running at 48MHz).

So, I decided to have an interrupt every line: actually, for every scan-line, we have an interrupt at the beginning, which activates the HSync signal, then it schedules Timer0 to get another interrupt after about 3.5 uS. Now, the HSync signal is deactivated, and after a small delay (front porch, actually the delay is needed to prepare and calculate the address of video memory), we move on. Next interrupt will happen after 28uS, and this time will be spent outputting video data on R-G-B pins (that's why the whole thing is so time-consuming).

At the beginning of the line, we calculate a position in the array VideoArray (16x4 bytes, which makes 16x32 pixels b/w), based on the current scan-line (we'll talk about this later in the Vertical section).Then, 8 pixels are outputted using 4 instructions each (BTFSS ... BCF ... BTFSC ... BSF: this makes 83nS x 4, or 330nS, and DELAY_PIXEL macro is used to adjust the length of each pixel so as to fill a line).The array (altogether with some more variables) have been declared in ACCESS bank for speed reasons; assembler was required because C would have wasted some instructions, no matter how optimized (I use Microchip C18).

Anyway, the most of the program is still C language.

After 8 pixels, we switch to next byte (next position in array) and actually this "delay" is seen on the screen as a small stretch of every 8th pixel. One could pre-save the 4 bytes at the beginning of line, to avoid this. Maybe a future version. Actually, as can be seen in the DELAY_PIXEL macro, it looks like we have time enough to double the horizontal precision (i.e. 64 pixel): this could be another future improvement.


Back porch is achieved automatically at the end of the scan line, just waiting for the next interrupt.vga2


At the end of every line (or rather, at the beginning of it) we take care of vertical position and sync signals. A counter (split in 2 bytes, and handled as a pre-scaler of 2, for ease of calculation and speed, again) is incremented, and when it reaches 255 (i.e. 510) it causes the VSync to be activated, for the length of 1/256 (which is way inside the specs!). I decided to cut the number of lines to 256x2 for counting and timing reasons, so the resulting signal is not exactly 525 lines. Also this part could be improved, though the resulting image would not change.

Of course, on the vertical side we don't have timing limitations, and the number of vertical pixels could be brought to some more (32 or even 64, provided we have anough RAM!). At the moment, the vertical scan-line number is divided by 32 and indexed into the array to 16 different position: but this scaling can easily be changed, with no harm to the timing.

This completes the signal-generator section. One things that is worth noticing is that, keeping the same resolution, this software could be adapted to driving 4 monitor, spanning the image over them all so as to form a "widi-wall" clock VERY large!And note, besides, that currently a green image is presented, but using the #defines, a red or blue clock can be easily obtained.

The USB section is basic Microchip USB stack, with just some changes I got from somebody (I'm sorry, I don't remember his name :-( ) : I'm referring to HandleControlOutReport() and GetInputReport0() and GetFeatureReport0(), which are used to retrieve Output reports (and sending input too) using HIDP_SETREPORT (which I'm not using here, though).

Commands are retrieved from the PC using

     if(HIDRxReport(USBPCBuffI,HID_INT_OUT_EP_SIZE)) {

and, on the PC side, packets are sent with WriteFile.

Of course, the device is a basic HID device; the most important transaction is #25, which sets the clock (seconds, minutes, hours, day, month, year).

In the main loop, every second, a routine transforms the numbers in display images and "plots" them in VideoArray; as seen in old watches, the display shows the time when seconds go from 0 to 7, and the date when seconds go from 8 to 9.

So far, kind of trivial.The troubles arose when this "time-consuming" interrupt entered the scene.Apart from having difficulties in enumeration, the which thing I corrected using the approach already decribed on the forums, i.e. having some delay, at power on, before interrupts are activated (leaving then the time to get enumerated by PC), it gets worse since, even if correctly enumerated, the device won't talk nor receive commands, and WITH NO ERROR on the PC side!

I supposed it was because of slow response, on the PIC, to USB events which must be polled with no longer delay than 10-20 uSec (so it seems, I don't have exact reference data). Because of interrupts, the USBTasks function was being called every 100-150uSec.

vga1So I decided to perform some "mixed-mode" processing of USB events: I activated USB interrupt at Low-Priority (so as not to disturb the critical timings of VGA), and, when one of these happen, I stop VGA signalling (so as to make USBTasks run as fast as possible).Result: it's not perfect, as not EVERY transaction coming from PC is completed, and for each of them, the VGA signal stops for a moment. But, you can get your watch syncronized with US navy atomic clock ! (or an italian one, if you prefer)

I guess someone may correct my code and find a better solution; at least, I hope so! In fact, I thought that ONLY SOME USB interrupts needed to be taken into account, but could not understand well: I ended up with this 

if( (UIRbits.TRNIF && UIEbits.TRNIE)  (interrupt.c)

that is, the interrupt for "TRANSACTION ENDED" is the only one that gets special attention. But as it's not perfect, I suppose I should monitor other interrupt from USB; but, if I do monitor EVERY of them, I get interrupted at 1mS, which of course destroys VGA... (it has to be the IDLE interrupt)

This ends my description of this "experiment", which will probably be improved somewhere in the future (maybe for example, to add a Real Time Clock chip: now, apart from syncronization with the PC, the clock goes on in software, hardly precise!)


Feel free to contact me for whatever question!
 
Dario Greggio
 

 

Last Updated ( Thursday, 03 August 2006 )
 
Next >