Sorry, I thought this thread was about VGA output on UNO.
I'll get my coat.
Sorry, I thought this thread was about VGA output on UNO.
I'll get my coat.
i wan to change the font size, can u tell me which part of the code that i need to modify it?
@Nick Gammon
I found this when I was looking up if anyone had done VGA natively on the Arduino (ie, no shield that might include additional stuff). I liked your approach but thought it could be interesting to get a bitmapped version that used less resources (you use two timers and the main loop D: ).
So I thought I'd try my hand at it and came up with this. It only uses Timer2 and by doing everything in the Timer2 ISR, it saves the loop function for things like games or what not (mine is bitmapped). My resolution is terrible, 40x30, but this is primarily due to memory constraints as I wanted a multiple of 640x480 that would fit in the Arduino UNO's RAM. I havent looked into making the "screen" bigger but it is likely possible. I did expand it to your 2-bit per channel color as well, here it is:
/*
* VGA Uno
* A basic system for VGA output for the Arduino UNO R3 board
*
* Michael Rosen
* mrrosen
* 10-18-2014
*
*/
#define SCREEN_H 30
#define SCREEN_W 40
#define IN_DRAW 0
#define IN_VSYNC_PULSE 1
#define IN_VSYNC_FP 2
#define nop __asm__("nop \n")
/* Define VGA Constants */
#define VSYNC_FP_LINE 10
#define VSYNC_SP_LINE 2
#define VSYNC_BP_LINE 33
#define VSYNC_VA_LINE 480
#define VSYNC_TL_LINE 525
#define HSYNC_FP_PXL 16
#define HSYNC_SP_PXL 96
#define HSYNC_BP_PXL 48
#define HSYNC_VA_PXL 640
#define HSYNC_TL_PXL 800
/* Define VGA vars */
int lineCounter;
char showRow;
char displayState;
char screen[SCREEN_H][SCREEN_W];
/* Define the counter OVF ISR:
* Responsible smoothing out timing
*/
ISR(TIMER2_OVF_vect) {
/* All we do is enable interrupts and go to sleep */
sei();
__asm__("sleep \n");
}
/* Define the counter COMPB ISR:
* Responsible for generating Vsync on PORTD[4]
* Draw the screen out PORTD[7:5,2:0]
* Counts the number of lines
*/
ISR(TIMER2_COMPB_vect) {
register char screenCounter;
register char* screenPtr;
/* Use the previous mode to do the correct thing now */
switch (displayState) {
case IN_DRAW:
/* Initialize the screen pointer and counter */
screenPtr = &(screen[(lineCounter - VSYNC_SP_LINE - VSYNC_BP_LINE) >> 4][0]);
screenCounter = SCREEN_W;
/* Put the colors to the screen via PORTD */
while (screenCounter--) {
PORTD = *(screenPtr++);
nop;
}
/* Wait a tiny bit to stretch the last pixel and black the rest */
nop; nop; nop;
PORTD = 0;
break;
case IN_VSYNC_PULSE:
/* Vsync pulse */
PORTD = _BV(PORTD4);
break;
case IN_VSYNC_FP:
/* Blank time */
PORTD = 0;
/* If going to maximum, reset lineCounter */
if (lineCounter == (VSYNC_TL_LINE - 1)) {
lineCounter = -1;
}
break;
}
/* Maintain a count of the number of lines, so we can know where we are in the Vsync */
lineCounter++;
showRow = !showRow;
/* Using lineCounter, see what part of the Vsync cycle it is. If its during the pulse,
* send the pulse on PORTD[4]. If its during the drawing region, draw the line. Otherwise,
* reset lineCounter if at max. All done through the displayState and the above switch-case */
if (lineCounter < VSYNC_SP_LINE) {
displayState = IN_VSYNC_PULSE;
} else if (showRow && (lineCounter >= (VSYNC_SP_LINE + VSYNC_BP_LINE)) &&
(lineCounter < (VSYNC_TL_LINE - VSYNC_FP_LINE))) {
displayState = IN_DRAW;
} else {
displayState = IN_VSYNC_FP;
}
}
/* Setup function */
void setup(void) {
/* Ensure no interrupts during the setup (to be safe) */
cli();
pinMode(13, OUTPUT);
digitalWrite(13, LOW);
/* Use PORTD for all signals:
* PD0 = R
* PD1 = G
* PD2 = B
* PD3 = Hsync
* PD4 = Vsync
*/
PORTD = 0;
DDRD = _BV(DDD0) | _BV(DDD1) | _BV(DDD2) | _BV(DDD3) | _BV(DDD4) | _BV(DDD5) | _BV(DDD6) | _BV(DDD7);
/* Use Timer2 for Hsync pulses and VGA timing, so set that up */
TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
TCCR2B = _BV(WGM22) | _BV(CS21);
OCR2A = 63;
OCR2B = 7;
TIMSK2 = _BV(TOIE2) | _BV(OCIE2B);
TIFR2 = _BV(TOV2) | _BV(OCF2B);
/* Allow IDLE sleep mode */
SMCR = _BV(SE);
/* Setup VGA-related vars */
lineCounter = 0;
showRow = 0;
displayState = IN_VSYNC_PULSE;
memset(screen, 0, sizeof(screen));
/* Done now, turn interrupts back on and leave */
sei();
return;
}
inline char colorGen(int i, int j) {
int val;
char r, g, b;
char portColor;
b = ((char) ((i + j) & 0x3));
r = ((char) (((i + j) >> 2) & 0x3));
g = ((char) (((i + j) >> 4) & 0x3));
val = r | (g << 2) | (b << 4);
color |= val & 0x01;
color |= val & 0x02;
color |= val & 0x04;
color |= (val << 2) & 0x20;
color |= (val << 2) & 0x40;
color |= (val << 2) & 0x80;
return color;
}
/* Loop function */
void loop(void) {
/* FILL IN THIS WILL COOL CODE */
/* For now, RAINBOW */
for (int i = 0; i < SCREEN_H; i++) {
for (int j = 0; j < SCREEN_W; j++) {
screen[i][j] = colorGen(i, j);
}
}
}
I'll admit the Timer2 COMPB ISR is weird, but thats because I thought I had a timing issue in it for the longest time. At least in its current form, it isnt terribly slow to start drawing. Also, it is interleaved to meet certain timing.
All you need to due is set up screen to have the correct value, where bit 4 is 0. The way I had it hooked up, D0-7 where BrightRed (D0), BrightGreen (D1), BrightBlue (D2), Hsync (D3), Vsync (D4), DarkRed (D5), DarkGreen (D6), DarkBlue (D7), but other than the rearrangement, the connections matched your 2-bit color circuit.
Basically, with this code, you can fill in loop with whatever code you need and by writing to screen, you get color. Heres PONG with my 1-bit color code (no score display though ):
[Pong was too big so I attached it]
If you want to know more, just let me know
PONG.ino (8.44 KB)
Just a thought. The AtMega328 is 20MHz chip. We (Arduino) run it at 16MHz.VGA (video in general) being timing/speed constrained why not squeeze that extra 25% or so with a 20MHz crystal (probably won't even need change the caps) and fuse update so the I/O timing remains correct?
Early boards used less capable chips, like Atmega8, which only operated at 16 MHz, so the code was developed around that, and carries forward to today.
Also, 16 clock cycles at 16 MHz = 1uS, so a 4 bit counter rolling over can make a 1uS time tick. Maybe just a coincidence, maybe a feature that was easy to take advantage of in the smaller/older uC.
Go check it out if really curious:
http://www.atmel.com/Images/Atmel-2486-8-bit-AVR-microcontroller-ATmega8_L_datasheet.pdf
Presented below is a solution in assembly for a 20MHz ATMega 328, for those curious. Should compile via AVRA or AVR Studio, I think. Works well for me, anyhow..
The way that I approached this was a little different – use a timer to catch HSYNC pulses and precalculate the value of PORTD (which includes both VSYNC and HSYNC pins on it) for the next pulse after.
Could be made even more efficient by doing the precalc for the next pulse in the ~60-70 cycles it takes to keep the HSYNC pin down.
.include "m328pdef.inc"
.org 0x0000
rjmp RESET
.org INT0addr ; External Interrupt 0 (pin PD2)
rjmp intExt0
.org OC1Aaddr ; Timer/Counter1 Compare Match A
rjmp intTim1
.def ITERATOR = r18
.def INITDATA = r19
.def PORTOUT = r20
.def YPOSH = r25
.def YPOSL = r24
.equ HSYNC = 0
.equ VSYNC = 1
.equ VERTLINES = 525 ; 525 for industry standard 640 x 480
.equ VERTBP = 523 ; 523 for industry standard
RESET:
cli
sbi PORTD, 1
; the idea in this version is to only catch the hsync once every
; 20/25.175 * 800 = 635 clocks
; and then to do a manual output using nop to time the sync pulse
; this will tie up at least 73 clocks per cycle.
; but much better than the alternative!
ldi PORTOUT, (1 << HSYNC | 1 << VSYNC)
out DDRD, PORTOUT
out PORTD, PORTOUT
; set up a 16 bit counter for y lines to 0
ldi YPOSH, HIGH(VERTLINES)
ldi YPOSL, LOW(VERTLINES)
; set up INT0 interrupt..
; falling edges should trigger counter reset..
ldi INITDATA, (1 << ISC01)
;sts EICRA, INITDATA
ldi INITDATA, (1 << INT0)
;out EIMSK, INITDATA
; SET UP 16 BIT Timer
; ATTEMPT SYNC PULSE EVERY 635 CLOCKS
; Set OCR1A to 0x0276
ldi INITDATA, HIGH(0x0276)
sts OCR1AH, INITDATA
ldi INITDATA, LOW(0x0276)
sts OCR1AL, INITDATA
ldi INITDATA, 0
; Clear timer counter (start from 0)
sts TCNT1H, INITDATA ; clear TCNT1
sts TCNT1L, INITDATA ; clear TCNT1
; enable CTC mode and no prescaler for clock source
ori INITDATA, (1 << WGM12 | 1 << CS10)
sts TCCR1B, INITDATA
ldi INITDATA, 0
ori INITDATA, (1 << OCIE1A)
sts TIMSK1, INITDATA
sei
main:
rjmp main
intExt0:
ldi INITDATA, 0
; Clear timer counter (start from 0)
sts TCNT1H, INITDATA ; clear TCNT1
sts TCNT1L, INITDATA ; clear TCNT1
cbi EIMSK, INT0
sbi DDRC, 5
sbi PORTC, 5
reti
intTim1:
; this needs to have been calculated after the last sync
; so that we hit the critical timing portions
out PORTD, PORTOUT
ldi ITERATOR, 22
hsyncpulsedelay:
dec ITERATOR
brne hsyncpulsedelay
cbi PORTD, HSYNC ; hsync down
; first, decrement YPOS.
sbiw YPOSL, 1
; now, will the next vsync be the end of a line?
; if so, flip VSYNC ON, and reset the counter.
breq resetCounter
; if not, will the next vsync be the end of the sync pulse?
cpi YPOSH, HIGH(VERTBP)
brne doneinterrupt
cpi YPOSL, LOW(VERTBP)
brne doneinterrupt
; if we got this far, we need to flip VSYNC OFF
ldi PORTOUT, (0 << VSYNC)
rjmp doneinterrupt
resetCounter:
ldi PORTOUT, (1 << VSYNC)
ldi YPOSL, LOW(VERTLINES)
ldi YPOSH, HIGH(VERTLINES)
doneinterrupt:
ori PORTOUT, (1 << HSYNC)
reti
Ive been working on VGA output from an ATTiny85 (8k rom, 512bytes ram, 8 pins), I am running mine at 32MHz and have got some nice effects out of it (not doing text, just video effects)
I have several modes I can run, the latest mode is fully mappable 40*40 pixels with 12 simultaneous colours on screen
I have lots more work to do before I am totally happy with it, but it has taken some serious time working out how I can totally exploit the ram, of which I only use 200-216 bytes for the actual screen map
Regards
Hello Nick Gammon i read over your form on the VGA monitor and i must say you did a awesome job man. i tried the Tv out one and loved it but I'm more into using a monitor on some of my future projects and you did it you are the best man one day when i get a chance i will try it out what I'm just looking for is just to display text from a serial monitor chat and instead of having it going to the Serial monitor it would be great to have it to go a LCD monitor. and you did it Thank you. Would it be hard to display the text from the chat instead of the of the Serial monitor?
Hello I tried the from the first sketch on the first comment #0 From Nick Gammon and that didn't work. But i did try Nick Gammon other sketch with the Signwave from Comment #16That did work. i see these moving Pixels green blue red all differensizes scrolling up looks awesome But can someone please help me out i took a video from my cellphone on what happens when i try that first Sketch http://www.codehunter.info/20150207_003151.mp4 to show what happens can someone please help me out? Thank you.
Ohh i looked at the sketch it has some I2C stuff in it what is that for?
LosTZealoT:
So I thought I'd try my hand at it and came up with this. It only uses Timer2 and by doing everything in the Timer2 ISR, it saves the loop function for things like games or what not (mine is bitmapped).
I apologize for not commenting on this, but honestly the forum is a real pain. For a few months we simply did not get notifications of new posts to threads, and thus I didn't see your reply.
I'll try your sketch out tomorrow once I wrestle my monitor out from wherever-it-is.
josephchrzempiec:
Hello Nick Gammon i read over your form on the VGA monitor and i must say you did a awesome job man. i tried the Tv out one and loved it ...
Did my sketch from that post work, ever? I tried modifying it once to "improve" it and it stopped working. I suspect that it is even more timing-critical than I thought.
instead of having it going to the Serial monitor it would be great to have it to go a LCD monitor. and you did it
I doubt it because of the timing issues, but it could be worth trying.
josephchrzempiec:
Ohh i looked at the sketch it has some I2C stuff in it what is that for?
The I2C stuff was the only way I could reliably communicate from a second board to the one doing the drawing. You could have the second board get data from serial, to be displayed by the TV board.
Hello Nick So you made some changes to it and didn't work out needed a lot of processing that i can understand for a second board. All I'm trying to do is Display some text on the Monitor it's self. No colors or images or lines of any kind that's all. If a second board is needed to process that using i2c that is okay as well. i did a project with sending some info from one board to another using i1c But never did one to display on VGA not sure to even know where to start on it sense this is your baby what should i do to display text on the VGA?
What do you mean? The sketch on my page displays text so I don't understand what you are asking me.
Hello nick when I uploaded y our sketch I get some red lines on the vga. At in my early comment I posted a video link I took from my cellphone I can not get no text to display.
The video doesn't show much, it seems to get get cut off.
Do you mean you used the code from Gammon Forum : Electronics : Microprocessors : Arduino Uno output to VGA monitor? Which one?
Could be a wiring issue.
Hey Nick the wiring is correct as in your wiring http://www.gammon.com.au/images/Arduino/VGA_Output_11.png I downloaded your VGA out zip http://gammon.com.au/Arduino/VGA_output.zip and the TimerHelp zip once i did that i tired the VGA_output sketch when i uploaded it i get a whole bunch of red/black thin lines that's all but if i try your Signwave Sketch Turn your Uno into a VGA output device! - #17 by nickgammon - Displays - Arduino Forum and that works i can see the scrolling pizels graphics or whatever you call then that works.
Here Nick this is a sample image from your zip file of the VGA_output zip file
and This is from your Signwave Sketch
Nick i set up a second arduino i went to your blog page Gammon Forum : Electronics : Microprocessors : Arduino Uno output to VGA monitor i did both sketches one for the VGA part and one for the Text part using the I2C and still nothing still them Red/Black Thin lines what am i doing wrong i know the wiring is correct can you help me out please?