Modified Bob Stone's code for ILI9341 QVGA TFT's without touch capability. Navigation is now done with rotary encoders - two for position, & one setting the maxiterations.
Dependencies: SPI_TFT_ILI9341 TFT_fonts mbed
Fork of Mandelbrot by
main.cpp
00001 /* Mandelbrot for mbed - April 2013 00002 * Uses Peter Drescher's library for Mikroelektronika's TFT-Proto screen, an inexpensive SPI-driven QVGA touch panel 00003 * Library: http://mbed.org/cookbook/SPI-driven-QVGA-TFT 00004 * Manufacturer's page for the screen: http://www.mikroe.com/add-on-boards/display/tft-proto/ 00005 * UK distributor: http://www.mcustore.com/tft-proto-board.html 00006 * Partly adapted from Kamil Ondrousek's code for another screen: http://mbed.org/users/JLS/code/Mandelbrot-2/ 00007 */ 00008 00009 00010 /* Adaptation for displays without touch screen from Bob Stone's https://developer.mbed.org/users/RorschachUK/code/Mandelbrot/ 00011 * Instead, using cheap rotary encoders with integral button. 00012 * Two encoders navigate the complex plane Z, a third changes the maxiterations value. 00013 * Status (Z, zoom, maxiters) is shown on bottom of screen. 00014 * 00015 * Max-iters can be changed on the fly so that its effect on detail & colour rendering can be evaluated quickly. 00016 * Drawing can be interrupted with any of the buttons to allow re-centering or re-zooming without having to wait for a full redraw. 00017 * 00018 * After the rendering is completed, or interrupted, these controls are available: 00019 * Turning either of the position encoders will move a small cursor around the complex plane. 00020 * Turning the third encoder will adjust maxiters. 00021 * The buttons atop the Zr&Zi encoders set new zoom out/in levels & initiate drawing at that new zoom. 00022 * The button atop the maxiters encoder initiates a re-draw at the current zoom. 00023 * State rendering/idle is indicated by a '+'/' ' in the bottom right corner. 00024 * 00025 * David Lowe Jan 2015 00026 */ 00027 00028 #include "mbed.h" 00029 #include "SPI_TFT_ILI9341.h" 00030 #include "Encoder.h" 00031 #include "Arial10x10.h" 00032 00033 #define WIDTH 320 00034 #define HEIGHT 240 00035 #define ORIENTATION 3 00036 SPI_TFT_ILI9341 tt(SPI_MOSI, SPI_MISO, SPI_SCK, D9, D6, D7,"TFT"); // mosi, miso, sclk, cs, reset, dc 00037 00038 // Wire all encoder commons to 3V3, 00039 // encoder A&B outputs to A0 A1, D4 D5, D10 PB7 00040 TIM_Encoder_InitTypeDef encoder2, encoder3, encoder4; 00041 TIM_HandleTypeDef timer2, timer3, timer4; 00042 00043 //Wire one side of button to 3V3 (same as encoder common connections) 00044 InterruptIn zoominbutton(D3); //@ encoder3: real (x) axis, button -> redraw zoom out 00045 InterruptIn zoomoutbutton(D2); //@ encoder2: imaginary (y) axis, button -> redraw zoom in 00046 InterruptIn zoomsamebutton(USER_BUTTON); //@ encoder4: iterations, button -> redraw zoom unchanged 00047 00048 //Setup base Mandelbrot 00049 double centrex = -0.5; 00050 double centrey = -0.0; 00051 double zoom=0.5; 00052 int32_t log2zoom=-1; 00053 00054 #define MAXITERS 8192 00055 #define MINITERS 64 00056 #define maxiters (MINITERS+TIM4->CNT) 00057 uint16_t lastiters=MINITERS; 00058 00059 enum nextaction_t { 00060 idle=0, 00061 zoomin, 00062 zoomout, 00063 zoomsame, 00064 interrupted 00065 }; 00066 00067 nextaction_t buttonpressed; 00068 00069 void GotZoomIn(void) 00070 { 00071 buttonpressed=zoomin; 00072 } 00073 00074 void GotZoomOut(void) 00075 { 00076 buttonpressed=zoomout; 00077 } 00078 00079 void GotZoomSame(void) 00080 { 00081 buttonpressed=zoomsame; 00082 } 00083 00084 void UpdateStats(void) 00085 { 00086 //invert orientation & set cursor to top left (our bottom right at usual orientation) 00087 tt.set_orientation(ORIENTATION^2); 00088 tt.locate(0,0); 00089 //indicate "rendering active" & erase a few chars at line end 00090 tt.printf("+ "); 00091 00092 //write current infos to bottom of screen: real, imag, log2(zoom), maxiters 00093 tt.set_orientation(ORIENTATION); 00094 tt.locate(0,240-10); 00095 tt.printf("%1.16f%+1.16fi %d %d", centrex, -centrey, log2zoom, maxiters); 00096 } 00097 00098 void DrawMandelbrot(void) 00099 { 00100 double xmin = centrex - 1.0/zoom; 00101 double xmax = centrex + 1.0/zoom; 00102 double ymin = centrey - HEIGHT / (WIDTH * zoom); 00103 double ymax = centrey + HEIGHT / (WIDTH *zoom); 00104 00105 double dx = (xmax - xmin) / WIDTH; 00106 double dy = (ymax - ymin) / HEIGHT; 00107 00108 double y = ymin; 00109 00110 double c; 00111 unsigned int cr, cg, cb; 00112 00113 for (int j = 0; j < HEIGHT; j++) { 00114 double x = xmin; 00115 00116 for (int i = 0; i < WIDTH; i++) { 00117 double a = x; 00118 double b = y; 00119 int n = 0; 00120 00121 if (maxiters!=lastiters) { //update stats if user changes maxiters on-the-fly 00122 UpdateStats(); 00123 lastiters=maxiters; 00124 } 00125 00126 while (n < maxiters) { 00127 double aa = a * a; 00128 double bb = b * b; 00129 double twoab = 2.0 * a * b; 00130 00131 a = aa - bb + x; 00132 b = twoab + y; 00133 00134 if(aa + bb > 16.0) { 00135 break; 00136 } 00137 n++; 00138 } 00139 00140 if (n == maxiters) { 00141 //It's in the set - Black 00142 tt.pixel(i,j,Black); 00143 } else { 00144 //Not in the set - pick a colour 00145 c = 3.0 * (maxiters-n)/maxiters; 00146 cr = ((c < 1.0) ? 255 * ( 1.0 - c) : (c > 2.0) ? 255 * (c - 2.0) : 0); 00147 cg = ((c < 1.0) ? 255 * c : (c > 2.0) ? 0 : 255 * (2.0 - c)); 00148 cb = ((c < 1.0) ? 0 : (c > 2.0) ? 255 * (3.0 - c) : 255 * (c - 1.0)); 00149 tt.pixel(i,j, RGB(cr,cg,cb)); 00150 } 00151 x += dx; 00152 if (buttonpressed) return; 00153 } 00154 y += dy; 00155 } 00156 } 00157 00158 void PixelBrot(int i, int j) 00159 { 00160 double xmin = centrex - 1.0/zoom; 00161 double xmax = centrex + 1.0/zoom; 00162 double ymin = centrey - HEIGHT / (WIDTH * zoom); 00163 double ymax = centrey + HEIGHT / (WIDTH *zoom); 00164 00165 double dx = (xmax - xmin) / WIDTH; 00166 double dy = (ymax - ymin) / HEIGHT; 00167 00168 double x=xmin+dx*i; 00169 double y=ymin+dy*j; 00170 00171 double a = x; 00172 double b = y; 00173 int n = 0; 00174 double c; 00175 unsigned int cr, cg, cb; 00176 00177 while (n < maxiters) { 00178 double aa = a * a; 00179 double bb = b * b; 00180 double twoab = 2.0 * a * b; 00181 00182 a = aa - bb + x; 00183 b = twoab + y; 00184 00185 if(aa + bb > 16.0) { 00186 break; 00187 } 00188 n++; 00189 } 00190 00191 if (n == maxiters) { 00192 //It's in the set - Black 00193 tt.pixel(i,j,Black); 00194 } else { 00195 //Not in the set - pick a colour 00196 c = 3.0 * (maxiters-n)/maxiters; 00197 cr = ((c < 1.0) ? 255 * ( 1.0 - c) : (c > 2.0) ? 255 * (c - 2.0) : 0); 00198 cg = ((c < 1.0) ? 255 * c : (c > 2.0) ? 0 : 255 * (2.0 - c)); 00199 cb = ((c < 1.0) ? 0 : (c > 2.0) ? 255 * (3.0 - c) : 255 * (c - 1.0)); 00200 tt.pixel(i,j, RGB(cr,cg,cb)); 00201 } 00202 00203 } 00204 00205 void EraseCursor(int x, int y) 00206 { 00207 int x1,x2,y1,y2; 00208 00209 x1=x-3; 00210 if (x1<0) x1=0; 00211 if (x1>WIDTH-1) x1=WIDTH-1; 00212 x2=x+3; 00213 if (x2<0) x2=0; 00214 if (x2>WIDTH-1) x2=WIDTH-1; 00215 00216 y1=y-3; 00217 if (y1<0) y1=0; 00218 if (y1>HEIGHT-1) y1=HEIGHT-1; 00219 y2=y+3; 00220 if (y2<0) y2=0; 00221 if (y2>HEIGHT-1) y2=HEIGHT-1; 00222 00223 if (x<0) x=0; 00224 if (x>WIDTH-1) x=WIDTH-1; 00225 00226 if (y<0) y=0; 00227 if (y>HEIGHT-1) y=HEIGHT-1; 00228 00229 //Overwite crosshair 00230 int i; 00231 for(i=x1; i<=x2; i++) PixelBrot(i,y); 00232 for(i=y1; i<=y2; i++) PixelBrot(x,i); 00233 } 00234 00235 void DrawCursor(int x, int y) 00236 { 00237 int x1,x2,y1,y2; 00238 00239 x1=x-3; 00240 if (x1<0) x1=0; 00241 if (x1>WIDTH-1) x1=WIDTH-1; 00242 x2=x+3; 00243 if (x2<0) x2=0; 00244 if (x2>WIDTH-1) x2=WIDTH-1; 00245 00246 y1=y-3; 00247 if (y1<0) y1=0; 00248 if (y1>HEIGHT-1) y1=HEIGHT-1; 00249 y2=y+3; 00250 if (y2<0) y2=0; 00251 if (y2>HEIGHT-1) y2=HEIGHT-1; 00252 00253 if (x<0) x=0; 00254 if (x>WIDTH-1) x=WIDTH-1; 00255 00256 if (y<0) y=0; 00257 if (y>HEIGHT-1) y=HEIGHT-1; 00258 00259 //Draw crosshair 00260 tt.line(x,y1,x,y2,White); 00261 tt.line(x1,y,x2,y,White); 00262 } 00263 00264 void InitEncoders(void) 00265 { 00266 //TIM1 is used by mbed for SPI clocking 00267 00268 //A0 A1, common to 3V3 00269 //counting on both B-input only, 2 ticks per cycle, full 32-bit count 00270 EncoderInit(encoder2, timer2, TIM2, 0xffffffff, TIM_ENCODERMODE_TI2); 00271 00272 //D4 D5, common to 3V3 00273 //counting on B-input only, 2 ticks per cycle, full 16-bit count 00274 EncoderInit(encoder3, timer3, TIM3, 0xffff, TIM_ENCODERMODE_TI2); 00275 00276 //D10 PB7, common to 3V3 00277 //counting on both A&B edges, 4 ticks per cycle, max count used to limit maxiters to range (MINITERS..MAXITERS) 00278 EncoderInit(encoder4, timer4, TIM4, MAXITERS-MINITERS+3, TIM_ENCODERMODE_TI12); 00279 00280 //TIM5 is used by mbed for us_ticker 00281 } 00282 00283 int main() 00284 { 00285 //Setup screen 00286 tt.background(Black); // set background to black 00287 tt.foreground(White); // set chars to white 00288 tt.cls(); // clear the screen 00289 tt.set_font((unsigned char*)Arial10x10); 00290 00291 InitEncoders(); 00292 00293 zoominbutton.rise(&GotZoomIn); 00294 zoominbutton.mode(PullDown); 00295 zoomoutbutton.rise(&GotZoomOut); 00296 zoomoutbutton.mode(PullDown); 00297 zoomsamebutton.rise(&GotZoomSame); 00298 zoomsamebutton.mode(PullDown); 00299 00300 while(true) { 00301 00302 //log status @ PC serial port 00303 printf("%1.18f%+1.18fi, %+d, %d\n\r", centrex, -centrey, log2zoom, maxiters); 00304 00305 wait(0.3); //debounce 00306 buttonpressed=idle; 00307 00308 UpdateStats(); 00309 00310 //Draw the thing, can be interrupted by a button press 00311 tt.set_orientation(ORIENTATION); 00312 DrawMandelbrot(); 00313 00314 wait(0.3); //debounce 00315 //flag that we should update status bar, but not commence new render 00316 if (buttonpressed) buttonpressed=interrupted; 00317 00318 //throw away any user-twiddling of real & imaginary encoders made while rendering 00319 TIM2->CNT=0; 00320 TIM3->CNT=0; 00321 00322 int16_t shiftx, shifty; 00323 int16_t lastshiftx=0, lastshifty=0; 00324 00325 while(true) { // loop until a redraw button is pressed, then go draw something 00326 00327 while(true) { //loop forever until real or imag or iters dials have moved, or any of the redraw buttons pressed 00328 shiftx=(int16_t)TIM2->CNT/2; 00329 shifty=(int16_t)TIM3->CNT/2; 00330 if (buttonpressed) break; 00331 if (shiftx!=lastshiftx || shifty!=lastshifty || maxiters!=lastiters) break; 00332 } 00333 00334 if(maxiters==lastiters) { //we're here cos cursor moved (speeds up redraw when twiddling maxiter knob 00335 EraseCursor(WIDTH/2+lastshiftx, HEIGHT/2+lastshifty); 00336 DrawCursor(WIDTH/2+shiftx, HEIGHT/2+shifty); 00337 } 00338 00339 lastshiftx=shiftx; 00340 lastshifty=shifty; 00341 lastiters=maxiters; 00342 00343 if(buttonpressed==interrupted) buttonpressed=idle; //draw was halted, update status & wait for new controls 00344 if(buttonpressed) break; //go redraw whole screen with updated centre/zoom/maxiters 00345 00346 //erase a few chars at line end 00347 tt.set_orientation(ORIENTATION^2); 00348 tt.locate(0,0); 00349 tt.printf(" "); 00350 00351 //write ammended infos to bottom of screen: real, imag, log2(zoom), maxiters 00352 tt.set_orientation(ORIENTATION); 00353 tt.locate(0,240-10); 00354 tt.printf("%1.16f%+1.16fi %d %d",centrex+((double)(shiftx*2))/(WIDTH*zoom), 00355 -(centrey+((double)(shifty*2))/(WIDTH*zoom)), 00356 buttonpressed==1 ? log2zoom+2 : (buttonpressed==2 ? log2zoom-2 : log2zoom), 00357 maxiters); 00358 } 00359 00360 centrex += ((double)(shiftx*2))/(WIDTH*zoom); 00361 centrey += ((double)(shifty*2))/(WIDTH*zoom); 00362 00363 if(buttonpressed==zoomin) { 00364 zoom*=4.0; 00365 log2zoom+=2; 00366 } 00367 if(buttonpressed==zoomout) { 00368 zoom/=4.0; 00369 log2zoom-=2; 00370 } 00371 //buttonpressed==zoomsame, redraw with zoom unchanged 00372 } 00373 }
Generated on Tue Jul 12 2022 23:08:42 by
1.7.2
