| VGA clock with USB interface |
|
|
|
| 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 :-( )
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.
(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 with 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.
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. 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.
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 > |
|---|