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