PIC micro drives VGA monitor

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
Hi, thought you might be interested in one of my little "holidays" projects. :)

It uses a PIC 18F452 which runs at 10 MIPS to directly drive a VGA computer monitor in 640x480 60Hz mode.

Actual video pixels are generated by the PIC's inbuilt serial SPI module and sent at 10 Megapixels / second, so it makes actual 251 pixels across and 480 pixels down.

It's not as good as some of the AVR video projects (because the PIC is 4x slower than AVR at this type of task) but it's proof of concept that a cheap PIC can create video at 10 Megapix/sec with some crude colour control etc thrown in for fun. I haven't provided source code because it's a mess of C and assembler chunks and still unfinished but I did provide .HEX code in case anyone want to try it on their own PIC.

The hardware only requires the PIC, xtal and 3 resistors to form the RGB video "DAC".



More details on the project can be found near the bottom of this page;

PIC drives VGA monitor project!
 

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
Hi Peter, I did say somewhere on my web page. I used the PIC internal hardware MSSP serial module, this sends bits out at 10 megabits per second (which gives 10 mega pixels per second).

So the firmware needs to load a byte into the MSSP module, and the module shifts the 8 bits out automatically at the processor speed (10MHz).
 

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
No, the MSSP module has a data out pin, that is the pin the shifter data comes out from.

It is all explained in the PIC datasheet under the "MSSP module" chapter. :)
 

peter_morley

Joined Mar 12, 2011
179
oh so we are talking only one bit per cycle. Sorry to keep asking you questions but one problem i have not been able to tackle is my problem of jitter. I seem to be getting little changes in frequency that cause the pixels to shift one pixel from left to right. These changes are very little but add up to almost a moving/pulsing picture. Any suggestions to troubleshooting this problem?
 

John P

Joined Oct 14, 2008
2,026
Roman, I'm curious about how you create color. The processor only has a single synchronous port output, and VGA uses 3 color inputs. Did you set up external logic to create the colors?

Or maybe you only use 2 colors. If you had all 3, you'd be "The_RGB".
 

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
To Peter_Morley; the sync is hard to get right. You probably need to use assembler and hard-code with the PIC timers to make sure the h-sync pulse happens on an exact interval.

John-P; Hi, my schematic is shown on the web page linked to in post #1. The video is generated in black/white pixels on pin RC5 (the MSSP port output pin) then there are 3 PIC output pins and 3 resistors, these are used as "colour killers" so they are either high impedance (which has no effect on that colour), or they are outputs pulled to ground (that kills that colour).

The 3 pins give 8 possible RGB colours, you can see all 8 colours on the text lines.

Of course the PIC is not fast enough to control RGB colour for each pixel, so it sets the colour at the start of the horiz line and then the colour is set for the whole horiz pixel line.

So you can call me "THE_RGB" but only on one line! ;)
 

peter_morley

Joined Mar 12, 2011
179
The RB- I have been using assembler from the start. What do you mean by hard code the PIC timers. I have implemented my code with timers with interrupts and counter iterations. Both are not giving jitter free results. My hsync code seems to be completely correct and i have checked and rechecked with no results. The thing that doesnt make sense to me is that some of the bars jitter while others do not. These are jitters in the 15-20Mhz range (rough guess compared to my pixel clock) which I can't actually implement because outputting colors takes 64MHz/8cycles = 8MHz pixel clock. Could this be from a jittering cpu from using the PLL?
 

peter_morley

Joined Mar 12, 2011
179
yes it does but it only has a one bit output per cycle so it isn't what im looking for. I feel my code is fine and there has to be another reason other than coding that is causing my problem.
 

MrChips

Joined Oct 2, 2009
30,824
The number of execution cycles is not always deterministic, especially if you are using other things such as timers, interrupts, UARTs, ADC etc.

I am doing SVGA from a MCU also and am generating pixels at 21MHz.
 

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
...
Could this be from a jittering cpu from using the PLL?
No, it is not the HSPLL. Your jitter will be in the order of 1 or 2 PIC instructions etc, many many times larger than any expected HSPLL phase error.

That is a very typical symptom that some lines jitter and some don't. It will be a beat frequency of the hsync as it beats to the timer or interrupt.

Exactly how are you generating the hsync pulse? If you do it in assembler in a single timer interrupt (with no other interrupts enabled) the int freq and int latency should remain stable, and if the pulse is hard coded in asm it should be as stable as the interrupt frequency.

If you post the small section of code for your hsync pulse in the interrupt we might be able to see the problem?
 

MrChips

Joined Oct 2, 2009
30,824
Interrupt latency is never stable. There is usually at least one clock cycle uncertainty for a single cycle per instruction machine.

One possible solution around this problem is to execute a WAIT instruction before your timer interrupt occurs (if your MCU supports this).
 
Last edited:

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
Sorry MrChips you are wrong. I have done timing applications for many years with PICs and the timer overflow interrupt latency is stable. I have even created test setups with markers on the CRO and tested which timer count corresponds to the first instruction executable in the interrupt.

The stability of the timer overflow interrupt is exactly what makes solid hsync possible on mine and everybody elses PIC 16F and 18F video generator projects.
 

MrChips

Joined Oct 2, 2009
30,824
Ok. Fine. I believe you. What I know is that when an interrupt occurs, the current instruction must be completed before the interrupt sequence begins. I suppose your timer is synchronized with the CPU SYSCLK.
 

peter_morley

Joined Mar 12, 2011
179
Don't pay too much attention to the comments because i have been changing the code so often the comments are not relevant anymore unfortunately so don't let them confuse you. Thanks for taking a look. This is only the hsync loop.
Rich (BB code):
displayloop										;Executes pixels in each line
		bsf		PORTB,0
		bsf		PORTA,7	
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop		
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop	
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop		
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
;rgb on phase 406 cycles

;first 88 cycles
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA	
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA

;second 100 cycles
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA	
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
	
;third 100 cycles
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA	
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA

;fourth 86 cycles
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA	
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
		movf	POSTINC0,0
		movwf	PORTA
	
		movlw	B'11000000'
		movwf	PORTA
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop

		clrf	FSR0L
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		bcf		PORTA,7

		decf	lowbytecount					;decrement the low byte skip if 0
		bz		setlowbyte
		goto	skipvtiming
setlowbyte
		movlw	LOWVCOUNT							;low byte count for v sync timing
		movwf	lowbytecount	
		decf	highbytecount					;decrement the high byte skip to v low status
		bz		sethighbyte
		goto	skipvtiming2
sethighbyte
		movlw	VLOW							
		movwf	PORTA
		movlw	HIGHVCOUNT							;highbyte count for v sync timing
		movwf	highbytecount
		goto	vlowenable

skipvtiming											;timing stall
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		nop
		goto	displayloop
skipvtiming2
		nop
		nop
		nop
		goto	displayloop
 

Markd77

Joined Sep 7, 2009
2,806
<ed> Ignore this bit, I didn't realise POSTINC0 was an FSR
Is there some code in the interrupt that is changing POSTINC0?
If not, pretty much the whole of that code could be replaced by a couple of delay loops.
If so post the interrupt code.

</ed>
If you need to code a short delay using nops, you should consider replacing some of them with "goto $+1" (takes 2 instruction cycles) or "call delay4cycles" (place the label "delay4cycles" immediately before any return statement in the same program memory page [takes 4 instruction cycles]).
 
Last edited:

Thread Starter

THE_RB

Joined Feb 11, 2008
5,438
Is there some code in the interrupt that is changing POSTINC0?
If not, pretty much the whole of that code could be replaced by a couple of delay loops.
That sounds logical... But a loop would mean minimum 3 cycles per output byte, and possibly 4 cycles as you need to check for the time to end the loop, where his code above gives exactly 2 cycles per output byte. That's probably 100% more hpixel resolution. :)

To Peter_Morley; where's your timer sync?? :eek:

It looks like you are just looping the hlines and trying to put some extra NOPs in there to compensate processing time. That is not a great way to do it.

There's plenty of time at the start of each hline to do a timer sync. :)
 
Top