|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14101
Lua rocks!
|
 |
« Reply #16 on: April 24, 2012, 05:10:17 am » |
I modified my sketch to output a sine wave: It could be faster if I pre-calculated the sine figures and stored them in flash memory. /* VGA colour video generation - Sine wave generation Author: Nick Gammon Date: 22nd April 2012 Version: 1.0 Version 1.0: initial release
Connections: D3 : Horizontal Sync (68 ohms in series) --> Pin 13 on DB15 socket D4 : Red pixel output (470 ohms in series) --> Pin 1 on DB15 socket D5 : Green pixel output (470 ohms in series) --> Pin 2 on DB15 socket D6 : Blue pixel output (470 ohms in series) --> Pin 3 on DB15 socket D10 : Vertical Sync (68 ohms in series) --> Pin 14 on DB15 socket Gnd : --> Pins 5, 6, 7, 8, 10 on DB15 socket
Note: As written, this sketch has 34 bytes of free SRAM memory. PERMISSION TO DISTRIBUTE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. LIMITATION OF LIABILITY The software is provided "as is", without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in an action of contract, tort or otherwise, arising from, out of or in connection with the software or the use or other dealings in the software.
*/
#include <TimerHelpers.h> #include <avr/pgmspace.h> #include <avr/sleep.h>
const byte hSyncPin = 3; // <------- HSYNC
const byte redPin = 4; // <------- Red pixel data const byte greenPin = 5; // <------- Green pixel data const byte bluePin = 6; // <------- Blue pixel data
const byte vSyncPin = 10; // <------- VSYNC
const int horizontalBytes = 50; // 480 pixels wide const int verticalPixels = 480; // 480 pixels high
// Timer 1 - Vertical sync
// output OC1B pin 16 (D10) <------- VSYNC
// Period: 16.64 mS (60 Hz) // 1/60 * 1e6 = 16666.66 uS // Pulse for 64 uS (2 x HSync width of 32 uS) // Sync pulse: 2 lines // Back porch: 33 lines // Active video: 480 lines // Front porch: 10 lines // Total: 525 lines
// Timer 2 - Horizontal sync
// output OC2B pin 5 (D3) <------- HSYNC
// Period: 32 uS (31.25 kHz) // (1/60) / 525 * 1e6 = 31.74 uS // Pulse for 4 uS (96 times 39.68 nS) // Sync pulse: 96 pixels // Back porch: 48 pixels // Active video: 640 pixels // Front porch: 16 pixels // Total: 800 pixels
// Pixel time = ((1/60) / 525 * 1e9) / 800 = 39.68 nS // frequency = 1 / (((1/60) / 525 * 1e6) / 800) = 25.2 MHz
// However in practice, it we can only pump out pixels at 375 nS each because it // takes 6 clock cycles to read one in from RAM and send it out the port.
const int verticalLines = verticalPixels / 16; const int horizontalPixels = horizontalBytes * 8;
const byte verticalBackPorchLines = 35; // includes sync pulse? const int verticalFrontPorchLines = 525 - verticalBackPorchLines;
volatile int vLine; volatile int messageLine; volatile int backPorchLinesToGo; volatile byte newFrame;
#define nop asm volatile ("nop\n\t")
// bitmap - gets sent to PORTD // For D4/D5/D6 bits need to be shifted left 4 bits // ie. 00BGR0000
char message [verticalLines] [horizontalBytes];
// ISR: Vsync pulse ISR (TIMER1_OVF_vect) { vLine = 0; messageLine = 0; backPorchLinesToGo = verticalBackPorchLines; newFrame = true; } // end of TIMER1_OVF_vect // ISR: Hsync pulse ... this interrupt merely wakes us up ISR (TIMER2_OVF_vect) { backPorchLinesToGo--; } // end of TIMER2_OVF_vect
void setup() { // initial bitmap ... change to suit for (int y = 0; y < verticalLines; y++) for (int x = 0; x < horizontalBytes; x++) message [y] [x] = (7) << 4; // disable Timer 0 TIMSK0 = 0; // no interrupts on Timer 0 OCR0A = 0; // and turn it off OCR0B = 0; // Timer 1 - vertical sync pulses pinMode (vSyncPin, OUTPUT); Timer1::setMode (15, Timer1::PRESCALE_1024, Timer1::CLEAR_B_ON_COMPARE); OCR1A = 259; // 16666 / 64 uS = 260 (less one) OCR1B = 0; // 64 / 64 uS = 1 (less one) TIFR1 = _BV (TOV1); // clear overflow flag TIMSK1 = _BV (TOIE1); // interrupt on overflow on timer 1
// Timer 2 - horizontal sync pulses pinMode (hSyncPin, OUTPUT); Timer2::setMode (7, Timer2::PRESCALE_8, Timer2::CLEAR_B_ON_COMPARE); OCR2A = 63; // 32 / 0.5 uS = 64 (less one) OCR2B = 7; // 4 / 0.5 uS = 8 (less one) TIFR2 = _BV (TOV2); // clear overflow flag TIMSK2 = _BV (TOIE2); // interrupt on overflow on timer 2 // prepare to sleep between horizontal sync pulses set_sleep_mode (SLEEP_MODE_IDLE); // pins for outputting the colour information pinMode (redPin, OUTPUT); pinMode (greenPin, OUTPUT); pinMode (bluePin, OUTPUT); } // end of setup
// draw a single scan line boolean doOneScanLine () { // after vsync we do the back porch if (backPorchLinesToGo > 0) { backPorchLinesToGo--; return false; } // end still doing back porch // if all lines done, do the front porch if (vLine == verticalPixels) return newFrame; // pre-load pointer for speed register char * messagePtr = & (message [messageLine] [0] );
delayMicroseconds (1); // how many pixels to send register byte i = horizontalBytes;
// blit pixel data to screen while (i--) PORTD = * messagePtr++;
// stretch final pixel nop; nop; nop; PORTD = 0; // back to black // finished this line vLine++;
// every 16 pixels it is time to move to a new line in our text if ((vLine & 0xF) == 0) messageLine++; return false; } // end of doOneScanLine
float radians = 0; const float pi = 3.1415926; const float radiansIncrement = (pi / 2.0) / (horizontalBytes / 2); byte x; boolean Up = true; byte colour = 0; boolean Calc = true;
void advanceLine () { if (Calc) { x = sin (radians) * horizontalBytes; if (Up) { radians += radiansIncrement; if (radians >= pi / 2) Up = false; } else { radians -= radiansIncrement; if (radians <= 0) { Up = true; radians = 0; colour++; } } Calc = false; } else { memmove (& message [0] [0], & message [1] [0], sizeof message - horizontalBytes); memset (&message [verticalLines - 1] [0], (colour + 1) << 4, horizontalBytes); memset (&message [verticalLines - 1] [0], colour << 4, x); Calc = true; }
newFrame = false; } void loop() { // loop to avoid overhead of function call while (true) { // sleep to ensure we start up in a predictable way sleep_mode (); if (doOneScanLine ()) advanceLine (); } // end of while } // end of loop
|
|
|
|
« Last Edit: April 24, 2012, 05:43:46 am by Nick Gammon »
|
Logged
|
|
|
|
|
Offline
Sr. Member
Karma: 0
Posts: 302
|
 |
« Reply #17 on: April 24, 2012, 09:46:36 am » |
I know this is just a proof of concept more than anything, but wouldnt there be a way of centring the display, despite the limitations u already mentioned ?!? Sorry for my "doh!!!"ness on the subject !
|
|
|
|
|
Logged
|
10 LET Loop=Infinite 20 GO TO 10
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14101
Lua rocks!
|
 |
« Reply #18 on: April 24, 2012, 03:43:52 pm » |
Sure. But my particular monitor is "smart" enough to left-justify on the start of the visible area. Initially when I tested it was more centered. I strongly suspect that the monitor firmware tries to detect where the visible signal starts (because different VGA cards might be out by a few nanonseconds one way or another) and then remembers something like "oh this card has a back porch of 2.1 uS, so we'll start drawing after that each time".
Judging by the logic analyzer output the signal is fairly correctly centered.
The monitor has a "horizontal position" menu setting. If I play with that I can center it. Ditto for vertical.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14101
Lua rocks!
|
 |
« Reply #19 on: April 24, 2012, 07:29:53 pm » |
This YouTube video shows the text-based output in action. You can see how quickly the text draws, and it zooms in to show the clarity of the letters. You can also see commands from the second Uno to clear the screen, clear a line, do cursor addressing, and scrolling:
|
|
|
|
|
Logged
|
|
|
|
|
nr Bundaberg, Australia
Offline
Tesla Member
Karma: 75
Posts: 6975
Scattered showers my arse -- Noah, 2348BC.
|
 |
« Reply #20 on: April 24, 2012, 08:31:04 pm » |
I saw a method of doing this the other day that used the SPI shift register to spit the bits out, that took a large load of the uC as you just have to handle V and H-sync. Only mono of course.
______ Rob
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14101
Lua rocks!
|
 |
« Reply #21 on: April 24, 2012, 08:49:47 pm » |
Was that my method? I am using SPI and the output is mono. But the processor still has to work pretty hard (well it would get bored otherwise wouldn't it?). You have 32.746 uS to draw a line. The line consists of 20 characters (160 pixels). To spit them out using SPI means sending 20 bytes. The highest clock rate you can output with SPI is the processor clock rate / 2, which is once every 125 nS. So you must take: 125 * 20 * 8 = 20000 nS (20 uS). So that leaves 12 uS over. It will take about 3 to enter and leave the ISR, which is required for synchronization. A couple more to load up the register with the correct line information. So really, not much spare time.
|
|
|
|
|
Logged
|
|
|
|
|
nr Bundaberg, Australia
Offline
Tesla Member
Karma: 75
Posts: 6975
Scattered showers my arse -- Noah, 2348BC.
|
 |
« Reply #22 on: April 24, 2012, 08:59:36 pm » |
May have been, I guess you know about it then  ______ Rob
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14101
Lua rocks!
|
 |
« Reply #23 on: April 25, 2012, 04:45:16 pm » |
From another thread: Your video project looks really nice. Do you have an easy suggestion if I just want larger letters or numbers on the screen?
Or maybe just a single large number?
For something large I would probably go with the "colour" version which basically draws a large bitmap: 60 x 30 pixels. Then store the patterns for whatever you want to display in progmem. The original text version has the bitmaps for every letter in it (one pit per pixel). You could expand that out to one byte per pixel. Then a letter or number would be 8 x 8 pixels, so you could fit 7 across and about 3 down. That also lets you choose the colour of each letter.
|
|
|
|
|
Logged
|
|
|
|
|
Hamme, Belgium
Offline
Sr. Member
Karma: 3
Posts: 383
|
 |
« Reply #24 on: April 26, 2012, 09:47:21 am » |
You can also see commands from the second Uno to clear the screen, clear a line, do cursor addressing, and scrolling: Can you do text scrolling? just sending a text to the output atmega and make it scroll over the screen (left to right)? Could you show a video on that, like in the video I showed you before? Thanks.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14101
Lua rocks!
|
 |
« Reply #25 on: April 26, 2012, 05:05:55 pm » |
I haven't built in a "scroll sideways" interface option at this stage. Personally I wouldn't because of the artifacts you get when updating the screen buffer, so "smoothly scrolling sideways text" probably isn't going to work ... unless you can solve the timing issues. You could do it to an extent by doing something like: - Carriage-return (take you to column 1)
- Clear to end of line
- Cursor position to desired column
- Draw desired text
I suggest you make one up and try it out - the parts are only about $5 (depending on how much the connector costs). I think it would be perfect for something like a home security system / weather monitoring where you want to have more text than you can fit onto an LCD, and don't mind a bit of flicker when it updates.
|
|
|
|
|
Logged
|
|
|
|
|
Hamme, Belgium
Offline
Sr. Member
Karma: 3
Posts: 383
|
 |
« Reply #26 on: April 27, 2012, 02:39:34 am » |
You could do it to an extent by doing something like:
Carriage-return (take you to column 1) Clear to end of line Cursor position to desired column Draw desired text wouldn't that make it scroll per character? I was thinking a per pixel scroll. But I guess you're system is only able to position in rows and columns and not in pixels. So actually yours is a great system for text output aka LCD character displays I suggest you make one up and try it out - the parts are only about $5 you are right, I should. Let's see if I can clip some things.
|
|
|
|
|
Logged
|
|
|
|
|
Global Moderator
Melbourne, Australia
Offline
Shannon Member
Karma: 226
Posts: 14101
Lua rocks!
|
 |
« Reply #27 on: April 27, 2012, 03:06:57 am » |
wouldn't that make it scroll per character? I was thinking a per pixel scroll.
That's right. But to store individual pixels is going to take a lot of memory. As it is, each character is looked-up from a font array on the fly. This isn't supposed to be as fancy as a sideways scrolling shooter. Not with a $5 chip. The intention was to show that you could display 20 x 30 characters just using a Uno, and not having to spend $30 on a graphics board. And quite possibly more if you wanted bitmapped graphics.
|
|
|
|
|
Logged
|
|
|
|
|
Hamme, Belgium
Offline
Sr. Member
Karma: 3
Posts: 383
|
 |
« Reply #28 on: April 27, 2012, 03:39:40 am » |
The intention was to show that you could display 20 x 30 characters just using a Uno, and not having to spend $30 on a graphics board. And quite possibly more if you wanted bitmapped graphics. yeah, sorry about that. But the thing is, I have been with the video-out library in my head the whole time. So I was thinking, you where making the same thing but for vga-out (and maybe it wouldn't be a bad idea, to do so) any way good work. You could say, if you want something like the video-out library, why don't you try to make it yourself? Well it's over my head.  I know how to use it, but not how to make it.
|
|
|
|
|
Logged
|
|
|
|
|
Offline
God Member
Karma: 4
Posts: 873
|
 |
« Reply #29 on: May 01, 2012, 04:54:44 pm » |
hi i've got a CGA crt tube with his electronc controlling board, the whole has been pulled out from a very strange machine, there is a lable who is making me understand the machine was called "Compaq trasportanble PC", and the label is telling the Truth: you can't call "notebook" a machine of more than 8Kg of weight. So heavy you want to leave it on your desk anyway, once you have pulled out the crt you realize that it is very easy, it's so light, and it works at 12V: that's wonderful ! I'd really like to have a video boart to display things, but the sync is not vga, it's cga, sync are different, also, the crt is able to display just things with just 1 bit of color. could you help me into adapting your vga-project into cga-project ? any tricks & tips for me  ? regards
|
|
|
|
|
Logged
|
|
|
|
|
|