Dependencies:   mbed

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers composite.cpp Source File

composite.cpp

00001 ////////////////////////////////////////////////////////////
00002 // Software generation of a grayscale composite TV signal //
00003 // Puts a 105x128 grayscale fractal zoom onscreen (slow!) //
00004 //                                                        //
00005 // Hacked together, (ab)uses the LPC1768 DAC output (p18) //
00006 // with some shifty looking timing sensitive code         //
00007 //                                                        //
00008 //                                                        //
00009 // Rob Younger 26th Oct 2009, (tweaked 15th Nov 2009)     //
00010 ////////////////////////////////////////////////////////////
00011 
00012 ////////////////////////////////////////////////////////////
00013 // fire demo routine based on http://demo-effects.sourceforge.net/ 
00014 // Copyright (C) 2003 W.P. van Paassen - peter@paassen.tmfweb.nl
00015 // which I guess means this version is GPL.
00016 //
00017 // note it uses a secondary buffer for the fire, which is 
00018 // copied into the main buffer, innefficient but quick!
00019 // limits on available resolution due to inneficient mem use
00020 ////////////////////////////////////////////////////////////
00021 
00022 // Generating video like it's 1982!
00023 
00024 // Warning : this is *very* hacky code - just proof of concept!
00025 // This might blow up your mbed or your TV.
00026 // I claim no responsibility for anything :-)
00027 
00028 // Start with a 180 Ohm resistor in series with the DAC output
00029 // before connecting to a composite AV input,
00030 // DAC is about 0-3.3v output, Composite in 1v p-p, with a 75 Ohm termination, so 180 Ohms is about right.
00031 
00032 // but it also worked without any resistor for me! Start with a higher value if you aren't sure.
00033 // More likely to burn out your mbed or TV with low/no resistor - Use at your own risk!
00034 
00035 
00036 // HOW THIS WORKS:
00037 
00038 // The DAC output is written as fast as possible to software generate a composite signal
00039 // dac.write_u16() seems to take about 0.5 us: I worked this timing out using a big loop of
00040 //        dac.write_u16(0);
00041 //        ....
00042 //        dac.write_u16(0);
00043 //        dac.write_u16(0xFFFF);
00044 //        ....
00045 //        dac.write_u16(0xFFFF);
00046 // Until I got a frequency I could measure on a multimeter.
00047 //
00048 // At full speed gives us about 1MHz max frequency -
00049 // I don't have an oscilloscope to see how well this actually works, probably totally out of spec!
00050 //
00051 // The software just runs loads of these to generate the composite signal as fast as possible!
00052 //
00053 // Since a TV output is generated continuously this would use 100% CPU time.
00054 //
00055 // Clever to-the-metal code would do things like use the horizontal and vertical blanking
00056 // intervals to do any required calculation. This isn't clever to-the-metal code!
00057 // Instead, I just don't draw the bottom few percent of the TV picture, and use this free time to run code.
00058 // This may well cause your TV to loose sync, but it works for me - I did say it was a hack!
00059 //
00060 // Driving the display takes 90%, main code gets 10% to play with at the end of each frame.
00061 // Tweak these percentages up and down, but loose too many lines and the tv is much more likely to
00062 // drop the signal, equally as you hit 100% CPU the frame calls might start to overlap and it all goes a bit wrong!
00063 //
00064 // This code actually starts with the end of previous frame signalling first, then all the setup, then the actual picture.
00065 //
00066 // It's coded up as a routine that draws a whole frame (field), which is called from main on a timer interrupt (at 50Hz for PAL)
00067 // This makes it easy to have a main routing that can operate normally, without you having to worry (too much) about the timing involved.
00068 // The picture elements of the signal are created by dumping a global frame buffer over to the DAC:
00069 // unsigned short int framebuffer[HEIGHT][WIDTH];
00070 // The values in this framebuffer are the actual composite signal, NOT just shades of gray!
00071 // In other words, only write values between 0x56DB (black) and 0xFFFF (bright white).
00072 // For this reason, it's important to initialize the buffer to all 0x56DB or above
00073 
00074 // Yes - there's probably a much better way to do this - but you don't want to slow down the DAC writes at all.
00075 // Adding checks or shifting the value from a normal range might be to slow - over to the real programmers to work out how to do this...
00076 
00077 // The frame buffer is 105 pixels wide - this is just because 105 dac writes take up the time required for a horizontal tv line.
00078 // height is more arbitrary, as we draw every scan line - but I double or quadruple scan to get squarish pixels!
00079 // Use a modulo value in the picture write line to repeat the picture for small framebuffers.
00080 
00081 // This program has a couple of demo routines. One draws a fractal, and the other just writes random values to the framebuffer
00082 
00083 // A future enhancement could be to have two small framebuffers 105x64 and do double buffering? Needs to all be in fast memory though.
00084 // The code could definitely do with some tuning as the sync delays are all a bit off...
00085 
00086 //////////////////////////////////////////////////////////////////////////////////////////////////////////
00087 
00088 
00089 #include "mbed.h"
00090 
00091 //Framebuffer size
00092 #define WIDTH 105
00093 #define HEIGHT 86
00094 
00095 //TV signal generation controlling:
00096 #define LINES 256 //Visible lines drawn to screen
00097 #define SCAN 3 //Number of scanlines per pixel (vertical)
00098 #define DRAWWIDTH 105 //Pixels per line
00099 
00100 // LINES: theoretically up to 286 for PAL, 241 for NTSC). 285 seems to be about 100% CPU on PAL. Smaller values means I stop drawing the signal early.
00101 // SCAN: controls double scan (e.g. 128 pixels to 256 lines)
00102 // DRAWWIDTH: number of pixels to attempt to draw in a line (should be =< framebuffer WIDTH). Very timing critical - expect different values to break
00103 
00104 // Composite signal values for DAC output. These should really be scaled for 1v peak-to-peak
00105 #define IRE_m40 0x0000 //0volts
00106 #define IRE_0   0x4920 //Baseline
00107 #define IRE_7p5 0x56DB //Black
00108 #define IRE_100 0xFFFF //White
00109 // DAC is 10bit, but i'm using write_u16 to write to the DAC.
00110 // IRE is a definition:
00111 // the levels are -40 (0volts), 0 (baseline below black), 7.5 (Black), 100 (White).
00112 // IRE -40 is 0v, 100 is 1v, so scale accordingly!
00113 
00114 AnalogOut dac(p18); // Video out pin
00115 Ticker timer;  // Timer for calling the frame
00116 
00117 DigitalOut led1(LED1);//Some status lights...
00118 DigitalOut led2(LED2);
00119 DigitalOut led3(LED3);
00120 DigitalOut led4(LED4);
00121 
00122 // Framebuffer actually has video signal levels in it - not just grayscale data
00123 // This means it must be initialised to at least all black IRE_7p5 before it's used.
00124 // zero values will likely kill the output and TV will loose sync.
00125 unsigned short int framebuffer[HEIGHT][WIDTH];
00126 
00127 
00128 
00129 /////////////////////////////////////////////////////////////
00130 //Software composite signal generation (very timing specific)
00131 /////////////////////////////////////////////////////////////
00132 
00133 void createframe() {
00134 
00135 // Procedure to create a output frame to a tv - needs to run on a very regular sync (e.g. 50Hz or 60Hz)
00136 // Using the DAC to create this output, which seems to happily run at 2MHz update
00137 // dac.write_u16 seems to take almost spot on 0.5us, so I'm using multiples of this to create a signal.
00138 
00139 // Could maybe be done with timing precision through multiple digital outputs and a resistor ladder to create an external DAC, but this didn't need any external components!
00140 
00141 // Someone with an oscilloscope can tweak this to get the delays more up to standard!
00142 
00143 // TV signal specs
00144 
00145 // For 50Hz PAL, each line takes up 64us, and there are 625 lines, but split into two fields of about 312 lines.
00146 // I'm treating both fields exactly the same, so we have a 312(ish) lines  at 50Hz.
00147 // NTSC is actually very similar but with slightly different timings/counts. (525 lines at 60Hz).
00148 
00149 // Some info found through google:
00150 
00151 //525line     (NTSC) - required timing in us for a line
00152 //NAME       LENGTH LEVEL
00153 //Front porch   1.5 IRE_0
00154 //Sync Tip      4.7 IRE_m40
00155 //Breezeway     0.6 IRE_0
00156 //Color Burst   2.5 IRE_0
00157 //Back Porch    1.6 IRE_0
00158 //Active Video 52.6 IRE_7p5 - IRE100
00159 
00160 //Total line time = 63.5us ( * half of 525 lines * 60Hz)
00161 
00162 //625line     (PAL) - required timing in us for a line
00163 //NAME       LENGTH LEVEL
00164 //Front porch   1.65 IRE_0
00165 //Sync Tip      4.7  IRE_m40
00166 //Breezeway     0.9  IRE_0
00167 //Color Burst   2.25 IRE_0
00168 //Back Porch    2.55 IRE_0
00169 //Active Video 51.95 IRE_7p5 - IRE100
00170 
00171 //Total line time = 64us ( * half of 625 lines * 50Hz)
00172 
00173 // There actually seem to be a lot of variations on this, but they all seem roughly the same.
00174 
00175 // Colour needs a precision ~4MHz carrier signal applied over the 'color burst' and active video
00176 // with precise phase and amplitude control (sounds like a lot of work!)
00177 
00178 // So for colour, Use svideo, VGA, or use 3 of these signals to generate an RGB scart signal?
00179 
00180 //The basic frame format is
00181 //1) few lines of special start pulses,
00182 //2) some off screen lines, which had things like teletext/close captions
00183 //3) the tv picture bit you see,
00184 //4) some special pulses to say end of screen, go back to the top.
00185 // Then straight back to 1 for the next frame.
00186 
00187 // To get the timing right - I do this:
00188 //4) some special pulses to say end of screen, go back to the top.
00189 //1) few lines of special start pulses,
00190 //2) some off screen lines, which had things like teletext/close captions
00191 //3) the tv picture bit you see,
00192 
00193 // You can get away dropping the last few lines of 3)
00194 // I use this to drop back to the main program to run as normal.
00195 
00196 // Ideally you'd use the few cycles between each line to do stuff, but that's going to be hard to get timing right.
00197 
00198 
00199 ////////////////////////////////////////////////////////////
00200 //Start of Frame
00201 ////////////////////////////////////////////////////////////
00202 
00203 //Each dac.write is ~0.5us, so multiply up to create the timings.
00204 
00205 // (This is a mix of PAL and NTSC - hack as appropriate)
00206 
00207 // There are 21 lines per field in a vertical blanking period
00208 // the last 4 lines of a field indicate are just before flyback
00209 // then there are 5 blank lines for flyback itself...
00210 
00211 //END OF A FRAME + FLYBACK + START OF NEW FRAME signalling (9 lines)
00212     for (int i = 0; i < 6; i++) { //6 equalizing pulses (time = 6 half lines)
00213         dac.write_u16(IRE_m40); //2.4us
00214         dac.write_u16(IRE_m40);
00215         dac.write_u16(IRE_m40);
00216         dac.write_u16(IRE_m40);
00217 //        dac.write_u16(IRE_m40);
00218         dac.write_u16(IRE_0); //29.4us
00219         wait_us(28);
00220     }
00221     for (int i = 0; i < 6; i++) {// 6 serrated vertical pulses (time = 6 half lines)
00222         dac.write_u16(IRE_0);  //2.4us
00223         dac.write_u16(IRE_0);
00224         dac.write_u16(IRE_0);
00225         dac.write_u16(IRE_0);
00226 //        dac.write_u16(IRE_0);
00227         dac.write_u16(IRE_m40); //29.4us
00228         wait_us(28);
00229     }
00230     for (int i = 0; i < 6; i++) { // 6 equalizing pulses (time = 6 half lines)
00231         dac.write_u16(IRE_m40); //2.4us
00232         dac.write_u16(IRE_m40);
00233         dac.write_u16(IRE_m40);
00234         dac.write_u16(IRE_m40);
00235 //        dac.write_u16(IRE_m40);
00236         dac.write_u16(IRE_0); //29.4us
00237         wait_us(28);
00238     }
00239 
00240 
00241 // The lines just above the top of the picture used for setup/teletext/closed captions etc.
00242 // about 17 lines for PAL, 12 for NTSC?
00243     for (int i = 0; i < 17; i++) {
00244         //10.9us (NTSC) or 12.5us (PAL) for horizontal blanking interval
00245         dac.write_u16(IRE_0); //Front porch 1.6us
00246         dac.write_u16(IRE_0);
00247         dac.write_u16(IRE_0);
00248         dac.write_u16(IRE_m40); //Sync Tip    4.7us
00249         dac.write_u16(IRE_m40);
00250         dac.write_u16(IRE_m40);
00251         dac.write_u16(IRE_m40);
00252         dac.write_u16(IRE_m40);
00253         dac.write_u16(IRE_m40);
00254         dac.write_u16(IRE_m40);
00255         dac.write_u16(IRE_m40);
00256         dac.write_u16(IRE_m40);
00257 //        dac.write_u16(IRE_m40); //extra for PAL timing
00258         dac.write_u16(IRE_0); //Breezeway   0.5us
00259         dac.write_u16(IRE_0); //ColorBurst  2.5us
00260         dac.write_u16(IRE_0);
00261         dac.write_u16(IRE_0);
00262         dac.write_u16(IRE_0);
00263         dac.write_u16(IRE_0);
00264         dac.write_u16(IRE_m40); //Back Porch  1.6us (2.55us in PAL)
00265         dac.write_u16(IRE_m40);
00266         dac.write_u16(IRE_m40);
00267         dac.write_u16(IRE_m40); //extra for PAL timing
00268         dac.write_u16(IRE_m40); //extra for PAL timing
00269 
00270 //        for (int j = 0; j < DRAWWIDTH; j++) {
00271 //            dac.write_u16(IRE_0);
00272 //        }  //next pixel
00273 
00274 
00275         //Then that video signal for 52.6us (52 for PAL)
00276         dac.write_u16(IRE_0); //Video signal for 52.6us
00277         wait_us(51); // replaces another 104 dac.write_u16(IRE_0)
00278     }
00279 
00280 
00281 //Draw the actual visible lines on screen: exactly same header as previous, but followed by real video data.
00282     // intentionally dropping the last few lines to throw some time to main()
00283     // otherwise this loop would use 100% of CPU.
00284     for (int i = 0; i < LINES; i++) {
00285         //10.9us (NTSC) or 12.5us (PAL) for horizontal blanking interval
00286         dac.write_u16(IRE_0); //Front porch 1.6us
00287         dac.write_u16(IRE_0);
00288         dac.write_u16(IRE_0);
00289         dac.write_u16(IRE_m40); //Sync Tip    4.7us
00290         dac.write_u16(IRE_m40);
00291         dac.write_u16(IRE_m40);
00292         dac.write_u16(IRE_m40);
00293         dac.write_u16(IRE_m40);
00294         dac.write_u16(IRE_m40);
00295         dac.write_u16(IRE_m40);
00296         dac.write_u16(IRE_m40);
00297         dac.write_u16(IRE_m40);
00298 //       dac.write_u16(IRE_m40); //extra for PAL timing
00299         dac.write_u16(IRE_0); //Breezeway   0.5us
00300         dac.write_u16(IRE_0); //ColorBurst  2.5us
00301         dac.write_u16(IRE_0);
00302         dac.write_u16(IRE_0);
00303         dac.write_u16(IRE_0);
00304         dac.write_u16(IRE_0);
00305         dac.write_u16(IRE_m40); //Back Porch  1.6us (2.55us in PAL)
00306         dac.write_u16(IRE_m40);
00307         dac.write_u16(IRE_m40);
00308         dac.write_u16(IRE_m40); //extra for PAL timing
00309         dac.write_u16(IRE_m40); //extra for PAL timing
00310 
00311         //Then that video signal for 52.6us (52 for PAL):
00312 
00313         ////////////////////////////////////////////////////////////
00314         //Write out the video data
00315         //(very timing sensitive as must last ~53us, no more or less)
00316         ////////////////////////////////////////////////////////////
00317         
00318         // Examples:
00319         
00320         //1) draw random shade per line
00321         // dac.write_u16(rand() % 40000 + IRE_7p5);    //Video signal for 52.6us
00322         // wait_us(52);
00323         
00324         //2) draw black
00325         // dac.write_u16(IRE_7p5);
00326         // wait_us(51);
00327         
00328         //3) draw white
00329         // dac.write_u16(IRE_100);
00330         // wait_us(51);
00331         
00332         //4) draw framebuffer
00333         
00334         // Code here is very timing critical.
00335 
00336         // loop count is instruction dependent, if you add some code here, it will need be a different width
00337         // We have ~52.5us and a a dac write takes 0.5us so 104/105px seems correct.
00338         // Trial+error shows ~100-110 pixels to be OKish on a particular old TV.
00339 
00340         int k =(i/ SCAN ); //double scan the framebuffer, particularly convenient for 128 vertical px but 256 line resolution..
00341 
00342         // The modulo is only needed if screen output size is bigger than framebuffer (wrapping occurs).
00343         // Stick to powers of 2 for modulo wrapping (or likely too slow).
00344         for (int j = 0; j < DRAWWIDTH; j++) {
00345             dac.write_u16( framebuffer[k%128][j%128] ); //modulo used to wrap framebuffer. Keep to power of 2 =< framebuffer sizes.
00346         }  //next pixel
00347 
00348     } //next line loop
00349     
00350      //Default back to black when we don't bother drawing the last few lines of a frame!
00351     dac.write_u16(IRE_7p5); 
00352 }  //End of createframe routine
00353 
00354 
00355 
00356 ////////////////////////////////////////////////////////////
00357 // randomfill the framebuffer                             //
00358 ////////////////////////////////////////////////////////////
00359 void randomfill () {
00360     for (int j = 0; j < HEIGHT; j++) {
00361         for (int i = 0; i < WIDTH; i++) {
00362             framebuffer[j][i] = rand();
00363             if (framebuffer[j][i] < IRE_7p5 ) {
00364                 framebuffer[j][i] = IRE_7p5;
00365             }
00366         }
00367     }
00368 }
00369 
00370 ////////////////////////////////////////////////////////////
00371 // blank the framebuffer                                  //
00372 ////////////////////////////////////////////////////////////
00373 void blankfill () {
00374     for (int j = 0; j < HEIGHT; j++) {
00375         for (int i = 0; i < WIDTH; i++) {
00376             framebuffer[j][i] = IRE_7p5;
00377             //framebuffer[j][i] = IRE_100;
00378         }
00379     }
00380 }
00381 
00382 ////////////////////////////////////////////////////////////
00383 // zooming mandelbot fractal in the framebuffer           //
00384 ////////////////////////////////////////////////////////////
00385 
00386 void mandelbrot () {
00387 //Mandelbrot escape time algorithm (doubles+iteration=slow)
00388 //Taken from wikipedia pseudocode,
00389 //tweaked by using the speeded up version that google found on geocities
00390 //(oops - Geocities has shut down in the 3 weeks since I wrote this! first time I've used it in years!)
00391 //http://www.geocities.com/CapeCanaveral/5003/Mandel.txt
00392 //then put in a loop to zoom in on a intersting co-ords point i saw elsewhere...
00393     double zoom;
00394     for (int z = 0; z < 200; z++) {//2^50 is quite a lot of zoom - thats why you need precision!
00395         zoom= pow((double)1.2,z);
00396 
00397         led1=0;
00398         led2=0;
00399         led3=0;
00400         led4=0;
00401 
00402         double x,y;
00403         double x0,y0;
00404         //            double xtemp;
00405         double xsq;
00406         double ysq;
00407 
00408         unsigned short int iteration = 0;
00409         unsigned short int max_iteration = (z*2)+20; //arbitrary scaling so there are more interation allowed as you zoom
00410         for (int j = 0; j < HEIGHT; j++) {
00411             //little status hack as as drawing fractals (particularly with doubles on only 10% of a cpu is slow!)
00412             if (j== (( HEIGHT /4)-1)) {
00413                 led1=1;
00414             } else if (j==(( HEIGHT /2)-1)) {
00415                 led2=1;
00416             } else if (j==(3*( HEIGHT /4)-1)) {
00417                 led3=1;
00418             } else if (j==( HEIGHT -1)) {
00419                 led4=1;
00420             }
00421             //end of little status hack
00422 
00423 
00424             for (int i = 0; i < WIDTH; i++) {
00425                 //            x0=(((float) i) -32.0)/32.0;//redefine 0to63 as -1to+1 mandelbrot window
00426                 //            y0=(((float) j) -32.0)/32.0;//redefine 0to63 as -1to+1 mandelbrot window
00427                 //-1.865725138512217656771 moves center point to something interesting
00428                 x0=((((double) i) - ( WIDTH /2)) /zoom)-1.865725138512217656771;//redefine 0to63 as -1to+1 mandelbrot window
00429                 y0=(((double) j) - ( HEIGHT /2)) /zoom;//redefine 0to63 as -1to+1 mandelbrot window
00430                 iteration = 0;
00431 
00432                 //Standard version of mandelbrot loop based on wikipedia pseudocode
00433                 //                    x=0;
00434                 //                    y=0;
00435                 //                    while ( ((x*x + y*y) <= (2*2))  &&  (iteration < max_iteration) ) {
00436                 //                        xtemp = x*x - y*y + x0;
00437                 //                        y = 2*x*y + y0;
00438                 //                        x = xtemp;
00439                 //                        iteration++;
00440                 //                    }
00441 
00442                 //Speedy version of main mandelbrot loop (algorithm from geocities page)
00443                 x=x0+x0*x0-y0*y0;
00444                 y=y0+x0*y0+x0*y0;
00445                 for (iteration=0;iteration<max_iteration && (ysq=y*y)+(xsq=x*x)<4;iteration++,y=y0+x*y+x*y,x=x0-ysq+xsq) ;
00446 
00447                 //Iteration count determines color (clamp max iteration to zero, and normalize for black to white)
00448                 framebuffer[j][i] = (( iteration == max_iteration ) ? (IRE_7p5) : (IRE_7p5 + ((iteration%20)*2000)) );
00449             }
00450         }
00451     }//zoom loop
00452 }
00453 
00454 ////////////////////////////////////////////////////////////
00455 // fire the framebuffer                                   //
00456 ////////////////////////////////////////////////////////////
00457 // based on demo at http://demo-effects.sourceforge.net/
00458 // refer back to original code for how it should be done :-)
00459 void fire () {
00460     static unsigned char fire[WIDTH * HEIGHT];
00461     int i,j,index,temp;
00462 
00463     /* draw random bottom line in fire array*/
00464 
00465     j = WIDTH * (HEIGHT- 1);
00466     for (i = 0; i < WIDTH - 1; i++) {
00467         int random = 1 + (int)(16.0 * (rand()/(RAND_MAX+1.0)));
00468         if (random > 8) /* the lower the value, the intenser the fire, compensate a lower value with a higher decay value*/
00469             fire[j + i] = 255; /*maximum heat*/
00470         else
00471             fire[j + i] = 0;
00472     }
00473 
00474     /* move fire upwards, start at bottom*/
00475 
00476     for (index = 0; index < HEIGHT-4 ; ++index) {
00477         for (i = 0; i < WIDTH - 1; ++i) {
00478             if (i == 0) { /* at the left border*/
00479                 temp = fire[j];
00480                 temp += fire[j + 1];
00481                 temp += fire[j - WIDTH];
00482                 temp /=3;
00483             } else if (i == WIDTH - 1) { /* at the right border*/
00484                 temp = fire[j + i];
00485                 temp += fire[j - WIDTH + i];
00486                 temp += fire[j + i - 1];
00487                 temp /= 3;
00488             } else {
00489                 temp = fire[j + i];
00490                 temp += fire[j + i + 1];
00491                 temp += fire[j + i - 1];
00492                 temp += fire[j - WIDTH + i];
00493                 temp >>= 2;
00494             }
00495             if (temp > 1)
00496                 temp -= 1; /* decay */
00497 
00498             fire[j - WIDTH + i] = temp;
00499         }
00500         j -= WIDTH;
00501     }
00502 
00503 //dirty hack to convert the fire data to the framebuffer. better graduation of shades would look better!
00504     for (int j = HEIGHT-3 ; j >= 0; --j) {
00505         for (int i = WIDTH-1; i >= -1 ; --i) {
00506             framebuffer[j][i] = ((fire[j * WIDTH + i])*150)+IRE_7p5;
00507         }
00508     }
00509 
00510 
00511 }
00512 
00513 
00514 ////////////////////////////////////////////////////////////
00515 // main() showing use framebuffer                         //
00516 // Puts a grayscale pic in it                             //
00517 ////////////////////////////////////////////////////////////
00518 
00519 int main() {
00520 
00521     blankfill(); //set framebuffer to blank values
00522 
00523     timer.attach_us(&createframe,20000);//attach the display (at 50Hz)
00524 
00525     // int attached=0; //attach frame
00526     // //If you had a lot of setup in a main game loop, you could do something like this:
00527     // if (attached==0) {
00528     //     timer.attach_us(&createframe,20000);
00529     //     attached=1;
00530     // }
00531 
00532     //Program loop
00533     while (1) {
00534         // Add you own demo code here. Expect it to get regularly interupted by the screen draw call!
00535         // very simple code can run at full fps.
00536         //Example - change HEIGHT to 64 and SCAN to 4 and use randomfill...
00537         //randomfill();  //random pixel fill
00538 
00539         //Example - change HEIGHT to 86 and SCAN to 3 and use fire;    
00540         fire();
00541 
00542         //Example - change HEIGHT to 128 and SCAN to 2 and use mandelbrot...         
00543         //mandelbrot(); //mandelbrot procedure is a 200 loop zoom so takes ages - and each scene redraw takes a few seconds!
00544     } //while
00545     
00546 } //main
00547 
00548