Dependencies:   mbed

Committer:
robyounger
Date:
Sun Nov 15 17:24:38 2009 +0000
Revision:
0:f81b117f7df3

        

Who changed what in which revision?

UserRevisionLine numberNew contents of line
robyounger 0:f81b117f7df3 1 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 2 // Software generation of a grayscale composite TV signal //
robyounger 0:f81b117f7df3 3 // Puts a 105x128 grayscale fractal zoom onscreen (slow!) //
robyounger 0:f81b117f7df3 4 // //
robyounger 0:f81b117f7df3 5 // Hacked together, (ab)uses the LPC1768 DAC output (p18) //
robyounger 0:f81b117f7df3 6 // with some shifty looking timing sensitive code //
robyounger 0:f81b117f7df3 7 // //
robyounger 0:f81b117f7df3 8 // //
robyounger 0:f81b117f7df3 9 // Rob Younger 26th Oct 2009, (tweaked 15th Nov 2009) //
robyounger 0:f81b117f7df3 10 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 11
robyounger 0:f81b117f7df3 12 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 13 // fire demo routine based on http://demo-effects.sourceforge.net/
robyounger 0:f81b117f7df3 14 // Copyright (C) 2003 W.P. van Paassen - peter@paassen.tmfweb.nl
robyounger 0:f81b117f7df3 15 // which I guess means this version is GPL.
robyounger 0:f81b117f7df3 16 //
robyounger 0:f81b117f7df3 17 // note it uses a secondary buffer for the fire, which is
robyounger 0:f81b117f7df3 18 // copied into the main buffer, innefficient but quick!
robyounger 0:f81b117f7df3 19 // limits on available resolution due to inneficient mem use
robyounger 0:f81b117f7df3 20 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 21
robyounger 0:f81b117f7df3 22 // Generating video like it's 1982!
robyounger 0:f81b117f7df3 23
robyounger 0:f81b117f7df3 24 // Warning : this is *very* hacky code - just proof of concept!
robyounger 0:f81b117f7df3 25 // This might blow up your mbed or your TV.
robyounger 0:f81b117f7df3 26 // I claim no responsibility for anything :-)
robyounger 0:f81b117f7df3 27
robyounger 0:f81b117f7df3 28 // Start with a 180 Ohm resistor in series with the DAC output
robyounger 0:f81b117f7df3 29 // before connecting to a composite AV input,
robyounger 0:f81b117f7df3 30 // DAC is about 0-3.3v output, Composite in 1v p-p, with a 75 Ohm termination, so 180 Ohms is about right.
robyounger 0:f81b117f7df3 31
robyounger 0:f81b117f7df3 32 // but it also worked without any resistor for me! Start with a higher value if you aren't sure.
robyounger 0:f81b117f7df3 33 // More likely to burn out your mbed or TV with low/no resistor - Use at your own risk!
robyounger 0:f81b117f7df3 34
robyounger 0:f81b117f7df3 35
robyounger 0:f81b117f7df3 36 // HOW THIS WORKS:
robyounger 0:f81b117f7df3 37
robyounger 0:f81b117f7df3 38 // The DAC output is written as fast as possible to software generate a composite signal
robyounger 0:f81b117f7df3 39 // dac.write_u16() seems to take about 0.5 us: I worked this timing out using a big loop of
robyounger 0:f81b117f7df3 40 // dac.write_u16(0);
robyounger 0:f81b117f7df3 41 // ....
robyounger 0:f81b117f7df3 42 // dac.write_u16(0);
robyounger 0:f81b117f7df3 43 // dac.write_u16(0xFFFF);
robyounger 0:f81b117f7df3 44 // ....
robyounger 0:f81b117f7df3 45 // dac.write_u16(0xFFFF);
robyounger 0:f81b117f7df3 46 // Until I got a frequency I could measure on a multimeter.
robyounger 0:f81b117f7df3 47 //
robyounger 0:f81b117f7df3 48 // At full speed gives us about 1MHz max frequency -
robyounger 0:f81b117f7df3 49 // I don't have an oscilloscope to see how well this actually works, probably totally out of spec!
robyounger 0:f81b117f7df3 50 //
robyounger 0:f81b117f7df3 51 // The software just runs loads of these to generate the composite signal as fast as possible!
robyounger 0:f81b117f7df3 52 //
robyounger 0:f81b117f7df3 53 // Since a TV output is generated continuously this would use 100% CPU time.
robyounger 0:f81b117f7df3 54 //
robyounger 0:f81b117f7df3 55 // Clever to-the-metal code would do things like use the horizontal and vertical blanking
robyounger 0:f81b117f7df3 56 // intervals to do any required calculation. This isn't clever to-the-metal code!
robyounger 0:f81b117f7df3 57 // Instead, I just don't draw the bottom few percent of the TV picture, and use this free time to run code.
robyounger 0:f81b117f7df3 58 // This may well cause your TV to loose sync, but it works for me - I did say it was a hack!
robyounger 0:f81b117f7df3 59 //
robyounger 0:f81b117f7df3 60 // Driving the display takes 90%, main code gets 10% to play with at the end of each frame.
robyounger 0:f81b117f7df3 61 // Tweak these percentages up and down, but loose too many lines and the tv is much more likely to
robyounger 0:f81b117f7df3 62 // drop the signal, equally as you hit 100% CPU the frame calls might start to overlap and it all goes a bit wrong!
robyounger 0:f81b117f7df3 63 //
robyounger 0:f81b117f7df3 64 // This code actually starts with the end of previous frame signalling first, then all the setup, then the actual picture.
robyounger 0:f81b117f7df3 65 //
robyounger 0:f81b117f7df3 66 // 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)
robyounger 0:f81b117f7df3 67 // This makes it easy to have a main routing that can operate normally, without you having to worry (too much) about the timing involved.
robyounger 0:f81b117f7df3 68 // The picture elements of the signal are created by dumping a global frame buffer over to the DAC:
robyounger 0:f81b117f7df3 69 // unsigned short int framebuffer[HEIGHT][WIDTH];
robyounger 0:f81b117f7df3 70 // The values in this framebuffer are the actual composite signal, NOT just shades of gray!
robyounger 0:f81b117f7df3 71 // In other words, only write values between 0x56DB (black) and 0xFFFF (bright white).
robyounger 0:f81b117f7df3 72 // For this reason, it's important to initialize the buffer to all 0x56DB or above
robyounger 0:f81b117f7df3 73
robyounger 0:f81b117f7df3 74 // Yes - there's probably a much better way to do this - but you don't want to slow down the DAC writes at all.
robyounger 0:f81b117f7df3 75 // 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...
robyounger 0:f81b117f7df3 76
robyounger 0:f81b117f7df3 77 // The frame buffer is 105 pixels wide - this is just because 105 dac writes take up the time required for a horizontal tv line.
robyounger 0:f81b117f7df3 78 // height is more arbitrary, as we draw every scan line - but I double or quadruple scan to get squarish pixels!
robyounger 0:f81b117f7df3 79 // Use a modulo value in the picture write line to repeat the picture for small framebuffers.
robyounger 0:f81b117f7df3 80
robyounger 0:f81b117f7df3 81 // This program has a couple of demo routines. One draws a fractal, and the other just writes random values to the framebuffer
robyounger 0:f81b117f7df3 82
robyounger 0:f81b117f7df3 83 // A future enhancement could be to have two small framebuffers 105x64 and do double buffering? Needs to all be in fast memory though.
robyounger 0:f81b117f7df3 84 // The code could definitely do with some tuning as the sync delays are all a bit off...
robyounger 0:f81b117f7df3 85
robyounger 0:f81b117f7df3 86 //////////////////////////////////////////////////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 87
robyounger 0:f81b117f7df3 88
robyounger 0:f81b117f7df3 89 #include "mbed.h"
robyounger 0:f81b117f7df3 90
robyounger 0:f81b117f7df3 91 //Framebuffer size
robyounger 0:f81b117f7df3 92 #define WIDTH 105
robyounger 0:f81b117f7df3 93 #define HEIGHT 86
robyounger 0:f81b117f7df3 94
robyounger 0:f81b117f7df3 95 //TV signal generation controlling:
robyounger 0:f81b117f7df3 96 #define LINES 256 //Visible lines drawn to screen
robyounger 0:f81b117f7df3 97 #define SCAN 3 //Number of scanlines per pixel (vertical)
robyounger 0:f81b117f7df3 98 #define DRAWWIDTH 105 //Pixels per line
robyounger 0:f81b117f7df3 99
robyounger 0:f81b117f7df3 100 // 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.
robyounger 0:f81b117f7df3 101 // SCAN: controls double scan (e.g. 128 pixels to 256 lines)
robyounger 0:f81b117f7df3 102 // DRAWWIDTH: number of pixels to attempt to draw in a line (should be =< framebuffer WIDTH). Very timing critical - expect different values to break
robyounger 0:f81b117f7df3 103
robyounger 0:f81b117f7df3 104 // Composite signal values for DAC output. These should really be scaled for 1v peak-to-peak
robyounger 0:f81b117f7df3 105 #define IRE_m40 0x0000 //0volts
robyounger 0:f81b117f7df3 106 #define IRE_0 0x4920 //Baseline
robyounger 0:f81b117f7df3 107 #define IRE_7p5 0x56DB //Black
robyounger 0:f81b117f7df3 108 #define IRE_100 0xFFFF //White
robyounger 0:f81b117f7df3 109 // DAC is 10bit, but i'm using write_u16 to write to the DAC.
robyounger 0:f81b117f7df3 110 // IRE is a definition:
robyounger 0:f81b117f7df3 111 // the levels are -40 (0volts), 0 (baseline below black), 7.5 (Black), 100 (White).
robyounger 0:f81b117f7df3 112 // IRE -40 is 0v, 100 is 1v, so scale accordingly!
robyounger 0:f81b117f7df3 113
robyounger 0:f81b117f7df3 114 AnalogOut dac(p18); // Video out pin
robyounger 0:f81b117f7df3 115 Ticker timer; // Timer for calling the frame
robyounger 0:f81b117f7df3 116
robyounger 0:f81b117f7df3 117 DigitalOut led1(LED1);//Some status lights...
robyounger 0:f81b117f7df3 118 DigitalOut led2(LED2);
robyounger 0:f81b117f7df3 119 DigitalOut led3(LED3);
robyounger 0:f81b117f7df3 120 DigitalOut led4(LED4);
robyounger 0:f81b117f7df3 121
robyounger 0:f81b117f7df3 122 // Framebuffer actually has video signal levels in it - not just grayscale data
robyounger 0:f81b117f7df3 123 // This means it must be initialised to at least all black IRE_7p5 before it's used.
robyounger 0:f81b117f7df3 124 // zero values will likely kill the output and TV will loose sync.
robyounger 0:f81b117f7df3 125 unsigned short int framebuffer[HEIGHT][WIDTH];
robyounger 0:f81b117f7df3 126
robyounger 0:f81b117f7df3 127
robyounger 0:f81b117f7df3 128
robyounger 0:f81b117f7df3 129 /////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 130 //Software composite signal generation (very timing specific)
robyounger 0:f81b117f7df3 131 /////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 132
robyounger 0:f81b117f7df3 133 void createframe() {
robyounger 0:f81b117f7df3 134
robyounger 0:f81b117f7df3 135 // Procedure to create a output frame to a tv - needs to run on a very regular sync (e.g. 50Hz or 60Hz)
robyounger 0:f81b117f7df3 136 // Using the DAC to create this output, which seems to happily run at 2MHz update
robyounger 0:f81b117f7df3 137 // dac.write_u16 seems to take almost spot on 0.5us, so I'm using multiples of this to create a signal.
robyounger 0:f81b117f7df3 138
robyounger 0:f81b117f7df3 139 // 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!
robyounger 0:f81b117f7df3 140
robyounger 0:f81b117f7df3 141 // Someone with an oscilloscope can tweak this to get the delays more up to standard!
robyounger 0:f81b117f7df3 142
robyounger 0:f81b117f7df3 143 // TV signal specs
robyounger 0:f81b117f7df3 144
robyounger 0:f81b117f7df3 145 // For 50Hz PAL, each line takes up 64us, and there are 625 lines, but split into two fields of about 312 lines.
robyounger 0:f81b117f7df3 146 // I'm treating both fields exactly the same, so we have a 312(ish) lines at 50Hz.
robyounger 0:f81b117f7df3 147 // NTSC is actually very similar but with slightly different timings/counts. (525 lines at 60Hz).
robyounger 0:f81b117f7df3 148
robyounger 0:f81b117f7df3 149 // Some info found through google:
robyounger 0:f81b117f7df3 150
robyounger 0:f81b117f7df3 151 //525line (NTSC) - required timing in us for a line
robyounger 0:f81b117f7df3 152 //NAME LENGTH LEVEL
robyounger 0:f81b117f7df3 153 //Front porch 1.5 IRE_0
robyounger 0:f81b117f7df3 154 //Sync Tip 4.7 IRE_m40
robyounger 0:f81b117f7df3 155 //Breezeway 0.6 IRE_0
robyounger 0:f81b117f7df3 156 //Color Burst 2.5 IRE_0
robyounger 0:f81b117f7df3 157 //Back Porch 1.6 IRE_0
robyounger 0:f81b117f7df3 158 //Active Video 52.6 IRE_7p5 - IRE100
robyounger 0:f81b117f7df3 159
robyounger 0:f81b117f7df3 160 //Total line time = 63.5us ( * half of 525 lines * 60Hz)
robyounger 0:f81b117f7df3 161
robyounger 0:f81b117f7df3 162 //625line (PAL) - required timing in us for a line
robyounger 0:f81b117f7df3 163 //NAME LENGTH LEVEL
robyounger 0:f81b117f7df3 164 //Front porch 1.65 IRE_0
robyounger 0:f81b117f7df3 165 //Sync Tip 4.7 IRE_m40
robyounger 0:f81b117f7df3 166 //Breezeway 0.9 IRE_0
robyounger 0:f81b117f7df3 167 //Color Burst 2.25 IRE_0
robyounger 0:f81b117f7df3 168 //Back Porch 2.55 IRE_0
robyounger 0:f81b117f7df3 169 //Active Video 51.95 IRE_7p5 - IRE100
robyounger 0:f81b117f7df3 170
robyounger 0:f81b117f7df3 171 //Total line time = 64us ( * half of 625 lines * 50Hz)
robyounger 0:f81b117f7df3 172
robyounger 0:f81b117f7df3 173 // There actually seem to be a lot of variations on this, but they all seem roughly the same.
robyounger 0:f81b117f7df3 174
robyounger 0:f81b117f7df3 175 // Colour needs a precision ~4MHz carrier signal applied over the 'color burst' and active video
robyounger 0:f81b117f7df3 176 // with precise phase and amplitude control (sounds like a lot of work!)
robyounger 0:f81b117f7df3 177
robyounger 0:f81b117f7df3 178 // So for colour, Use svideo, VGA, or use 3 of these signals to generate an RGB scart signal?
robyounger 0:f81b117f7df3 179
robyounger 0:f81b117f7df3 180 //The basic frame format is
robyounger 0:f81b117f7df3 181 //1) few lines of special start pulses,
robyounger 0:f81b117f7df3 182 //2) some off screen lines, which had things like teletext/close captions
robyounger 0:f81b117f7df3 183 //3) the tv picture bit you see,
robyounger 0:f81b117f7df3 184 //4) some special pulses to say end of screen, go back to the top.
robyounger 0:f81b117f7df3 185 // Then straight back to 1 for the next frame.
robyounger 0:f81b117f7df3 186
robyounger 0:f81b117f7df3 187 // To get the timing right - I do this:
robyounger 0:f81b117f7df3 188 //4) some special pulses to say end of screen, go back to the top.
robyounger 0:f81b117f7df3 189 //1) few lines of special start pulses,
robyounger 0:f81b117f7df3 190 //2) some off screen lines, which had things like teletext/close captions
robyounger 0:f81b117f7df3 191 //3) the tv picture bit you see,
robyounger 0:f81b117f7df3 192
robyounger 0:f81b117f7df3 193 // You can get away dropping the last few lines of 3)
robyounger 0:f81b117f7df3 194 // I use this to drop back to the main program to run as normal.
robyounger 0:f81b117f7df3 195
robyounger 0:f81b117f7df3 196 // Ideally you'd use the few cycles between each line to do stuff, but that's going to be hard to get timing right.
robyounger 0:f81b117f7df3 197
robyounger 0:f81b117f7df3 198
robyounger 0:f81b117f7df3 199 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 200 //Start of Frame
robyounger 0:f81b117f7df3 201 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 202
robyounger 0:f81b117f7df3 203 //Each dac.write is ~0.5us, so multiply up to create the timings.
robyounger 0:f81b117f7df3 204
robyounger 0:f81b117f7df3 205 // (This is a mix of PAL and NTSC - hack as appropriate)
robyounger 0:f81b117f7df3 206
robyounger 0:f81b117f7df3 207 // There are 21 lines per field in a vertical blanking period
robyounger 0:f81b117f7df3 208 // the last 4 lines of a field indicate are just before flyback
robyounger 0:f81b117f7df3 209 // then there are 5 blank lines for flyback itself...
robyounger 0:f81b117f7df3 210
robyounger 0:f81b117f7df3 211 //END OF A FRAME + FLYBACK + START OF NEW FRAME signalling (9 lines)
robyounger 0:f81b117f7df3 212 for (int i = 0; i < 6; i++) { //6 equalizing pulses (time = 6 half lines)
robyounger 0:f81b117f7df3 213 dac.write_u16(IRE_m40); //2.4us
robyounger 0:f81b117f7df3 214 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 215 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 216 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 217 // dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 218 dac.write_u16(IRE_0); //29.4us
robyounger 0:f81b117f7df3 219 wait_us(28);
robyounger 0:f81b117f7df3 220 }
robyounger 0:f81b117f7df3 221 for (int i = 0; i < 6; i++) {// 6 serrated vertical pulses (time = 6 half lines)
robyounger 0:f81b117f7df3 222 dac.write_u16(IRE_0); //2.4us
robyounger 0:f81b117f7df3 223 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 224 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 225 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 226 // dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 227 dac.write_u16(IRE_m40); //29.4us
robyounger 0:f81b117f7df3 228 wait_us(28);
robyounger 0:f81b117f7df3 229 }
robyounger 0:f81b117f7df3 230 for (int i = 0; i < 6; i++) { // 6 equalizing pulses (time = 6 half lines)
robyounger 0:f81b117f7df3 231 dac.write_u16(IRE_m40); //2.4us
robyounger 0:f81b117f7df3 232 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 233 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 234 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 235 // dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 236 dac.write_u16(IRE_0); //29.4us
robyounger 0:f81b117f7df3 237 wait_us(28);
robyounger 0:f81b117f7df3 238 }
robyounger 0:f81b117f7df3 239
robyounger 0:f81b117f7df3 240
robyounger 0:f81b117f7df3 241 // The lines just above the top of the picture used for setup/teletext/closed captions etc.
robyounger 0:f81b117f7df3 242 // about 17 lines for PAL, 12 for NTSC?
robyounger 0:f81b117f7df3 243 for (int i = 0; i < 17; i++) {
robyounger 0:f81b117f7df3 244 //10.9us (NTSC) or 12.5us (PAL) for horizontal blanking interval
robyounger 0:f81b117f7df3 245 dac.write_u16(IRE_0); //Front porch 1.6us
robyounger 0:f81b117f7df3 246 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 247 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 248 dac.write_u16(IRE_m40); //Sync Tip 4.7us
robyounger 0:f81b117f7df3 249 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 250 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 251 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 252 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 253 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 254 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 255 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 256 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 257 // dac.write_u16(IRE_m40); //extra for PAL timing
robyounger 0:f81b117f7df3 258 dac.write_u16(IRE_0); //Breezeway 0.5us
robyounger 0:f81b117f7df3 259 dac.write_u16(IRE_0); //ColorBurst 2.5us
robyounger 0:f81b117f7df3 260 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 261 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 262 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 263 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 264 dac.write_u16(IRE_m40); //Back Porch 1.6us (2.55us in PAL)
robyounger 0:f81b117f7df3 265 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 266 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 267 dac.write_u16(IRE_m40); //extra for PAL timing
robyounger 0:f81b117f7df3 268 dac.write_u16(IRE_m40); //extra for PAL timing
robyounger 0:f81b117f7df3 269
robyounger 0:f81b117f7df3 270 // for (int j = 0; j < DRAWWIDTH; j++) {
robyounger 0:f81b117f7df3 271 // dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 272 // } //next pixel
robyounger 0:f81b117f7df3 273
robyounger 0:f81b117f7df3 274
robyounger 0:f81b117f7df3 275 //Then that video signal for 52.6us (52 for PAL)
robyounger 0:f81b117f7df3 276 dac.write_u16(IRE_0); //Video signal for 52.6us
robyounger 0:f81b117f7df3 277 wait_us(51); // replaces another 104 dac.write_u16(IRE_0)
robyounger 0:f81b117f7df3 278 }
robyounger 0:f81b117f7df3 279
robyounger 0:f81b117f7df3 280
robyounger 0:f81b117f7df3 281 //Draw the actual visible lines on screen: exactly same header as previous, but followed by real video data.
robyounger 0:f81b117f7df3 282 // intentionally dropping the last few lines to throw some time to main()
robyounger 0:f81b117f7df3 283 // otherwise this loop would use 100% of CPU.
robyounger 0:f81b117f7df3 284 for (int i = 0; i < LINES; i++) {
robyounger 0:f81b117f7df3 285 //10.9us (NTSC) or 12.5us (PAL) for horizontal blanking interval
robyounger 0:f81b117f7df3 286 dac.write_u16(IRE_0); //Front porch 1.6us
robyounger 0:f81b117f7df3 287 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 288 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 289 dac.write_u16(IRE_m40); //Sync Tip 4.7us
robyounger 0:f81b117f7df3 290 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 291 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 292 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 293 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 294 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 295 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 296 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 297 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 298 // dac.write_u16(IRE_m40); //extra for PAL timing
robyounger 0:f81b117f7df3 299 dac.write_u16(IRE_0); //Breezeway 0.5us
robyounger 0:f81b117f7df3 300 dac.write_u16(IRE_0); //ColorBurst 2.5us
robyounger 0:f81b117f7df3 301 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 302 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 303 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 304 dac.write_u16(IRE_0);
robyounger 0:f81b117f7df3 305 dac.write_u16(IRE_m40); //Back Porch 1.6us (2.55us in PAL)
robyounger 0:f81b117f7df3 306 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 307 dac.write_u16(IRE_m40);
robyounger 0:f81b117f7df3 308 dac.write_u16(IRE_m40); //extra for PAL timing
robyounger 0:f81b117f7df3 309 dac.write_u16(IRE_m40); //extra for PAL timing
robyounger 0:f81b117f7df3 310
robyounger 0:f81b117f7df3 311 //Then that video signal for 52.6us (52 for PAL):
robyounger 0:f81b117f7df3 312
robyounger 0:f81b117f7df3 313 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 314 //Write out the video data
robyounger 0:f81b117f7df3 315 //(very timing sensitive as must last ~53us, no more or less)
robyounger 0:f81b117f7df3 316 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 317
robyounger 0:f81b117f7df3 318 // Examples:
robyounger 0:f81b117f7df3 319
robyounger 0:f81b117f7df3 320 //1) draw random shade per line
robyounger 0:f81b117f7df3 321 // dac.write_u16(rand() % 40000 + IRE_7p5); //Video signal for 52.6us
robyounger 0:f81b117f7df3 322 // wait_us(52);
robyounger 0:f81b117f7df3 323
robyounger 0:f81b117f7df3 324 //2) draw black
robyounger 0:f81b117f7df3 325 // dac.write_u16(IRE_7p5);
robyounger 0:f81b117f7df3 326 // wait_us(51);
robyounger 0:f81b117f7df3 327
robyounger 0:f81b117f7df3 328 //3) draw white
robyounger 0:f81b117f7df3 329 // dac.write_u16(IRE_100);
robyounger 0:f81b117f7df3 330 // wait_us(51);
robyounger 0:f81b117f7df3 331
robyounger 0:f81b117f7df3 332 //4) draw framebuffer
robyounger 0:f81b117f7df3 333
robyounger 0:f81b117f7df3 334 // Code here is very timing critical.
robyounger 0:f81b117f7df3 335
robyounger 0:f81b117f7df3 336 // loop count is instruction dependent, if you add some code here, it will need be a different width
robyounger 0:f81b117f7df3 337 // We have ~52.5us and a a dac write takes 0.5us so 104/105px seems correct.
robyounger 0:f81b117f7df3 338 // Trial+error shows ~100-110 pixels to be OKish on a particular old TV.
robyounger 0:f81b117f7df3 339
robyounger 0:f81b117f7df3 340 int k =(i/ SCAN ); //double scan the framebuffer, particularly convenient for 128 vertical px but 256 line resolution..
robyounger 0:f81b117f7df3 341
robyounger 0:f81b117f7df3 342 // The modulo is only needed if screen output size is bigger than framebuffer (wrapping occurs).
robyounger 0:f81b117f7df3 343 // Stick to powers of 2 for modulo wrapping (or likely too slow).
robyounger 0:f81b117f7df3 344 for (int j = 0; j < DRAWWIDTH; j++) {
robyounger 0:f81b117f7df3 345 dac.write_u16( framebuffer[k%128][j%128] ); //modulo used to wrap framebuffer. Keep to power of 2 =< framebuffer sizes.
robyounger 0:f81b117f7df3 346 } //next pixel
robyounger 0:f81b117f7df3 347
robyounger 0:f81b117f7df3 348 } //next line loop
robyounger 0:f81b117f7df3 349
robyounger 0:f81b117f7df3 350 //Default back to black when we don't bother drawing the last few lines of a frame!
robyounger 0:f81b117f7df3 351 dac.write_u16(IRE_7p5);
robyounger 0:f81b117f7df3 352 } //End of createframe routine
robyounger 0:f81b117f7df3 353
robyounger 0:f81b117f7df3 354
robyounger 0:f81b117f7df3 355
robyounger 0:f81b117f7df3 356 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 357 // randomfill the framebuffer //
robyounger 0:f81b117f7df3 358 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 359 void randomfill () {
robyounger 0:f81b117f7df3 360 for (int j = 0; j < HEIGHT; j++) {
robyounger 0:f81b117f7df3 361 for (int i = 0; i < WIDTH; i++) {
robyounger 0:f81b117f7df3 362 framebuffer[j][i] = rand();
robyounger 0:f81b117f7df3 363 if (framebuffer[j][i] < IRE_7p5 ) {
robyounger 0:f81b117f7df3 364 framebuffer[j][i] = IRE_7p5;
robyounger 0:f81b117f7df3 365 }
robyounger 0:f81b117f7df3 366 }
robyounger 0:f81b117f7df3 367 }
robyounger 0:f81b117f7df3 368 }
robyounger 0:f81b117f7df3 369
robyounger 0:f81b117f7df3 370 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 371 // blank the framebuffer //
robyounger 0:f81b117f7df3 372 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 373 void blankfill () {
robyounger 0:f81b117f7df3 374 for (int j = 0; j < HEIGHT; j++) {
robyounger 0:f81b117f7df3 375 for (int i = 0; i < WIDTH; i++) {
robyounger 0:f81b117f7df3 376 framebuffer[j][i] = IRE_7p5;
robyounger 0:f81b117f7df3 377 //framebuffer[j][i] = IRE_100;
robyounger 0:f81b117f7df3 378 }
robyounger 0:f81b117f7df3 379 }
robyounger 0:f81b117f7df3 380 }
robyounger 0:f81b117f7df3 381
robyounger 0:f81b117f7df3 382 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 383 // zooming mandelbot fractal in the framebuffer //
robyounger 0:f81b117f7df3 384 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 385
robyounger 0:f81b117f7df3 386 void mandelbrot () {
robyounger 0:f81b117f7df3 387 //Mandelbrot escape time algorithm (doubles+iteration=slow)
robyounger 0:f81b117f7df3 388 //Taken from wikipedia pseudocode,
robyounger 0:f81b117f7df3 389 //tweaked by using the speeded up version that google found on geocities
robyounger 0:f81b117f7df3 390 //(oops - Geocities has shut down in the 3 weeks since I wrote this! first time I've used it in years!)
robyounger 0:f81b117f7df3 391 //http://www.geocities.com/CapeCanaveral/5003/Mandel.txt
robyounger 0:f81b117f7df3 392 //then put in a loop to zoom in on a intersting co-ords point i saw elsewhere...
robyounger 0:f81b117f7df3 393 double zoom;
robyounger 0:f81b117f7df3 394 for (int z = 0; z < 200; z++) {//2^50 is quite a lot of zoom - thats why you need precision!
robyounger 0:f81b117f7df3 395 zoom= pow((double)1.2,z);
robyounger 0:f81b117f7df3 396
robyounger 0:f81b117f7df3 397 led1=0;
robyounger 0:f81b117f7df3 398 led2=0;
robyounger 0:f81b117f7df3 399 led3=0;
robyounger 0:f81b117f7df3 400 led4=0;
robyounger 0:f81b117f7df3 401
robyounger 0:f81b117f7df3 402 double x,y;
robyounger 0:f81b117f7df3 403 double x0,y0;
robyounger 0:f81b117f7df3 404 // double xtemp;
robyounger 0:f81b117f7df3 405 double xsq;
robyounger 0:f81b117f7df3 406 double ysq;
robyounger 0:f81b117f7df3 407
robyounger 0:f81b117f7df3 408 unsigned short int iteration = 0;
robyounger 0:f81b117f7df3 409 unsigned short int max_iteration = (z*2)+20; //arbitrary scaling so there are more interation allowed as you zoom
robyounger 0:f81b117f7df3 410 for (int j = 0; j < HEIGHT; j++) {
robyounger 0:f81b117f7df3 411 //little status hack as as drawing fractals (particularly with doubles on only 10% of a cpu is slow!)
robyounger 0:f81b117f7df3 412 if (j== (( HEIGHT /4)-1)) {
robyounger 0:f81b117f7df3 413 led1=1;
robyounger 0:f81b117f7df3 414 } else if (j==(( HEIGHT /2)-1)) {
robyounger 0:f81b117f7df3 415 led2=1;
robyounger 0:f81b117f7df3 416 } else if (j==(3*( HEIGHT /4)-1)) {
robyounger 0:f81b117f7df3 417 led3=1;
robyounger 0:f81b117f7df3 418 } else if (j==( HEIGHT -1)) {
robyounger 0:f81b117f7df3 419 led4=1;
robyounger 0:f81b117f7df3 420 }
robyounger 0:f81b117f7df3 421 //end of little status hack
robyounger 0:f81b117f7df3 422
robyounger 0:f81b117f7df3 423
robyounger 0:f81b117f7df3 424 for (int i = 0; i < WIDTH; i++) {
robyounger 0:f81b117f7df3 425 // x0=(((float) i) -32.0)/32.0;//redefine 0to63 as -1to+1 mandelbrot window
robyounger 0:f81b117f7df3 426 // y0=(((float) j) -32.0)/32.0;//redefine 0to63 as -1to+1 mandelbrot window
robyounger 0:f81b117f7df3 427 //-1.865725138512217656771 moves center point to something interesting
robyounger 0:f81b117f7df3 428 x0=((((double) i) - ( WIDTH /2)) /zoom)-1.865725138512217656771;//redefine 0to63 as -1to+1 mandelbrot window
robyounger 0:f81b117f7df3 429 y0=(((double) j) - ( HEIGHT /2)) /zoom;//redefine 0to63 as -1to+1 mandelbrot window
robyounger 0:f81b117f7df3 430 iteration = 0;
robyounger 0:f81b117f7df3 431
robyounger 0:f81b117f7df3 432 //Standard version of mandelbrot loop based on wikipedia pseudocode
robyounger 0:f81b117f7df3 433 // x=0;
robyounger 0:f81b117f7df3 434 // y=0;
robyounger 0:f81b117f7df3 435 // while ( ((x*x + y*y) <= (2*2)) && (iteration < max_iteration) ) {
robyounger 0:f81b117f7df3 436 // xtemp = x*x - y*y + x0;
robyounger 0:f81b117f7df3 437 // y = 2*x*y + y0;
robyounger 0:f81b117f7df3 438 // x = xtemp;
robyounger 0:f81b117f7df3 439 // iteration++;
robyounger 0:f81b117f7df3 440 // }
robyounger 0:f81b117f7df3 441
robyounger 0:f81b117f7df3 442 //Speedy version of main mandelbrot loop (algorithm from geocities page)
robyounger 0:f81b117f7df3 443 x=x0+x0*x0-y0*y0;
robyounger 0:f81b117f7df3 444 y=y0+x0*y0+x0*y0;
robyounger 0:f81b117f7df3 445 for (iteration=0;iteration<max_iteration && (ysq=y*y)+(xsq=x*x)<4;iteration++,y=y0+x*y+x*y,x=x0-ysq+xsq) ;
robyounger 0:f81b117f7df3 446
robyounger 0:f81b117f7df3 447 //Iteration count determines color (clamp max iteration to zero, and normalize for black to white)
robyounger 0:f81b117f7df3 448 framebuffer[j][i] = (( iteration == max_iteration ) ? (IRE_7p5) : (IRE_7p5 + ((iteration%20)*2000)) );
robyounger 0:f81b117f7df3 449 }
robyounger 0:f81b117f7df3 450 }
robyounger 0:f81b117f7df3 451 }//zoom loop
robyounger 0:f81b117f7df3 452 }
robyounger 0:f81b117f7df3 453
robyounger 0:f81b117f7df3 454 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 455 // fire the framebuffer //
robyounger 0:f81b117f7df3 456 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 457 // based on demo at http://demo-effects.sourceforge.net/
robyounger 0:f81b117f7df3 458 // refer back to original code for how it should be done :-)
robyounger 0:f81b117f7df3 459 void fire () {
robyounger 0:f81b117f7df3 460 static unsigned char fire[WIDTH * HEIGHT];
robyounger 0:f81b117f7df3 461 int i,j,index,temp;
robyounger 0:f81b117f7df3 462
robyounger 0:f81b117f7df3 463 /* draw random bottom line in fire array*/
robyounger 0:f81b117f7df3 464
robyounger 0:f81b117f7df3 465 j = WIDTH * (HEIGHT- 1);
robyounger 0:f81b117f7df3 466 for (i = 0; i < WIDTH - 1; i++) {
robyounger 0:f81b117f7df3 467 int random = 1 + (int)(16.0 * (rand()/(RAND_MAX+1.0)));
robyounger 0:f81b117f7df3 468 if (random > 8) /* the lower the value, the intenser the fire, compensate a lower value with a higher decay value*/
robyounger 0:f81b117f7df3 469 fire[j + i] = 255; /*maximum heat*/
robyounger 0:f81b117f7df3 470 else
robyounger 0:f81b117f7df3 471 fire[j + i] = 0;
robyounger 0:f81b117f7df3 472 }
robyounger 0:f81b117f7df3 473
robyounger 0:f81b117f7df3 474 /* move fire upwards, start at bottom*/
robyounger 0:f81b117f7df3 475
robyounger 0:f81b117f7df3 476 for (index = 0; index < HEIGHT-4 ; ++index) {
robyounger 0:f81b117f7df3 477 for (i = 0; i < WIDTH - 1; ++i) {
robyounger 0:f81b117f7df3 478 if (i == 0) { /* at the left border*/
robyounger 0:f81b117f7df3 479 temp = fire[j];
robyounger 0:f81b117f7df3 480 temp += fire[j + 1];
robyounger 0:f81b117f7df3 481 temp += fire[j - WIDTH];
robyounger 0:f81b117f7df3 482 temp /=3;
robyounger 0:f81b117f7df3 483 } else if (i == WIDTH - 1) { /* at the right border*/
robyounger 0:f81b117f7df3 484 temp = fire[j + i];
robyounger 0:f81b117f7df3 485 temp += fire[j - WIDTH + i];
robyounger 0:f81b117f7df3 486 temp += fire[j + i - 1];
robyounger 0:f81b117f7df3 487 temp /= 3;
robyounger 0:f81b117f7df3 488 } else {
robyounger 0:f81b117f7df3 489 temp = fire[j + i];
robyounger 0:f81b117f7df3 490 temp += fire[j + i + 1];
robyounger 0:f81b117f7df3 491 temp += fire[j + i - 1];
robyounger 0:f81b117f7df3 492 temp += fire[j - WIDTH + i];
robyounger 0:f81b117f7df3 493 temp >>= 2;
robyounger 0:f81b117f7df3 494 }
robyounger 0:f81b117f7df3 495 if (temp > 1)
robyounger 0:f81b117f7df3 496 temp -= 1; /* decay */
robyounger 0:f81b117f7df3 497
robyounger 0:f81b117f7df3 498 fire[j - WIDTH + i] = temp;
robyounger 0:f81b117f7df3 499 }
robyounger 0:f81b117f7df3 500 j -= WIDTH;
robyounger 0:f81b117f7df3 501 }
robyounger 0:f81b117f7df3 502
robyounger 0:f81b117f7df3 503 //dirty hack to convert the fire data to the framebuffer. better graduation of shades would look better!
robyounger 0:f81b117f7df3 504 for (int j = HEIGHT-3 ; j >= 0; --j) {
robyounger 0:f81b117f7df3 505 for (int i = WIDTH-1; i >= -1 ; --i) {
robyounger 0:f81b117f7df3 506 framebuffer[j][i] = ((fire[j * WIDTH + i])*150)+IRE_7p5;
robyounger 0:f81b117f7df3 507 }
robyounger 0:f81b117f7df3 508 }
robyounger 0:f81b117f7df3 509
robyounger 0:f81b117f7df3 510
robyounger 0:f81b117f7df3 511 }
robyounger 0:f81b117f7df3 512
robyounger 0:f81b117f7df3 513
robyounger 0:f81b117f7df3 514 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 515 // main() showing use framebuffer //
robyounger 0:f81b117f7df3 516 // Puts a grayscale pic in it //
robyounger 0:f81b117f7df3 517 ////////////////////////////////////////////////////////////
robyounger 0:f81b117f7df3 518
robyounger 0:f81b117f7df3 519 int main() {
robyounger 0:f81b117f7df3 520
robyounger 0:f81b117f7df3 521 blankfill(); //set framebuffer to blank values
robyounger 0:f81b117f7df3 522
robyounger 0:f81b117f7df3 523 timer.attach_us(&createframe,20000);//attach the display (at 50Hz)
robyounger 0:f81b117f7df3 524
robyounger 0:f81b117f7df3 525 // int attached=0; //attach frame
robyounger 0:f81b117f7df3 526 // //If you had a lot of setup in a main game loop, you could do something like this:
robyounger 0:f81b117f7df3 527 // if (attached==0) {
robyounger 0:f81b117f7df3 528 // timer.attach_us(&createframe,20000);
robyounger 0:f81b117f7df3 529 // attached=1;
robyounger 0:f81b117f7df3 530 // }
robyounger 0:f81b117f7df3 531
robyounger 0:f81b117f7df3 532 //Program loop
robyounger 0:f81b117f7df3 533 while (1) {
robyounger 0:f81b117f7df3 534 // Add you own demo code here. Expect it to get regularly interupted by the screen draw call!
robyounger 0:f81b117f7df3 535 // very simple code can run at full fps.
robyounger 0:f81b117f7df3 536 //Example - change HEIGHT to 64 and SCAN to 4 and use randomfill...
robyounger 0:f81b117f7df3 537 //randomfill(); //random pixel fill
robyounger 0:f81b117f7df3 538
robyounger 0:f81b117f7df3 539 //Example - change HEIGHT to 86 and SCAN to 3 and use fire;
robyounger 0:f81b117f7df3 540 fire();
robyounger 0:f81b117f7df3 541
robyounger 0:f81b117f7df3 542 //Example - change HEIGHT to 128 and SCAN to 2 and use mandelbrot...
robyounger 0:f81b117f7df3 543 //mandelbrot(); //mandelbrot procedure is a 200 loop zoom so takes ages - and each scene redraw takes a few seconds!
robyounger 0:f81b117f7df3 544 } //while
robyounger 0:f81b117f7df3 545
robyounger 0:f81b117f7df3 546 } //main
robyounger 0:f81b117f7df3 547
robyounger 0:f81b117f7df3 548