![]() ![]() INFO - FAQ - CC2-Forum - CCPro-Forum |
|
![]() ' --------------------------------------------------------------------------- ' Berechnung und Darstellung der Mandelbrot-Menge ("Apfelmännchen") ' von Dietmar Harlos am 1. Juli 2006 bis 3. Dezember 2012 ' Version vom 22. August 2019: Darstellung auf dem OLED-I2C-Grafikdisplay ' --------------------------------------------------------------------------- ' Dieses Beispielprogramm zeigt, wie sich auf den OM-Controllern die ' sogenannte Mandelbrot-Menge berechnen und darstellen laesst. ' Die Mandelbrot-Menge ist ein fraktales (selbstaehnliches) Gebilde und wird ' durch die sehr einfache Berechnungsvorschrift "z=z^2+c" erzeugt. Besonders ' beim Hineinzoomen zeigen sich sehr interessante Welten. ' Siehe "http://de.wikipedia.org/wiki/Mandelbrot-Menge". ' In diesem Programm wird die Mandelbrot-Menge mittels 8-Bit-Fixkommazahlen ' in Assembler berechnet. Die Grafik wird auf dem OLED-I2C-Grafikdisplay ' ausgegeben. Bedingt durch die Auflösung des Displays ist die Darstellung ' horizonal gestreckt. ' Das diesem Code zugrundeliegende Original-Programm befindet sich im ZIP- ' Archiv des OCBASIC-Compilers im Verzeichnis OM. Dort wird die Grafik auf ' der Seriellen Schnittstelle ausgegeben. ' Dieses Programm belegt nur 612 Byte FLASH-Speicher und 13 RAM-Bytes! ' --- Definitionen für das System ------------------------------------------- INCLUDE "omax.def" ' Definitionen für die Open-Maxi ' --- Definitionen des Anwenders -------------------------------------------- DEFINE OLED_I2C_ADDR &h78 ' Konstanten für OLED-I2C-Display DEFINE OLED_DISP_OFF &hAE DEFINE OLED_DISP_ON &hAF DEFINE DISPLAY_WIDTH 128 DEFINE DISPLAY_HEIGHT 64 DEFINE DISPLAYSIZE 1024 DIM a,b,c,data,x,y BYTE ' einige Variablen definieren DIM IMc BYTE ' Deklarierung der Mandelbrot-Variablen DIM REc BYTE DIM iter BYTE DIM buffer,buffm BYTE ' Buffer für Ausgabe auf Display ' --- Das Hauptprogramm ----------------------------------------------------- End2Host=ON PrintSpc=ON PRINT "OLED-I2C-Display: Mandel" i2c_init ' I2C-Bus initialisieren oled_init ' OLED-I2C-Display initialisieren FOR x=0 TO 127 REc=-126+(x*39) SHR 5 ' Realteil; (30--126)/128*32 = 39 buffer=0 : buffm=1 ' Buffer für 8 Punkte FOR y=0 TO 63 IMc=-72+(y*72) SHR 5 ' Imaginärteil; (72--72)/64*32 = 72 mandel ' einen Punkt der Mandelbrotmenge berechnen IF iter>=11 THEN buffer=buffer OR buffm ' Punkt im Buffer setzen ENDIF buffm=buffm SHL 1 ' Maske eine Stelle nach links (mal 2) IF buffm=0 THEN ' 8 Punkte berechnet? oled_gotoxy x,y oled_data buffer ' dann Buffer auf Display ausgeben buffer=0 : buffm=1 END IF NEXT y NEXT x END ' zurueck in den Hostmodus ' --------------------------------------------------------------------------- DEFINE REz BYTE ' Deklarierung der Assembler-Variablen DEFINE IMz BYTE DEFINE REc_dz FREERAM2 DEFINE IMc_dz FREERAM3 DEFINE REzQ FREERAM4 DEFINE IMzQ FREERAM5 ' --------------------------------------------------------------------------- ' Berechnung eines Punktes der Mandelbrotmenge ' von Dietmar Harlos am 1. Juli 2006 ' die Anzahl der benoetigten Iterationen wird in iter zurueckgeliefert PROC mandel INLASM ! lda REc '-128..32 ' RE(c) muss in der Aufloesung reduziert ! asra ' werden, damit es in den folgenden Berech- ! adc #0 ' nungen nicht zu einem Ueberlauf kommt. ! sta REc_dz '-64..16 ' Dabei wird gerundet. ! sta REz ! lda IMc '-72..72 ' IM(c) ebenso reduzieren. Die Zahlen sind ! asra ' jetzt mit dem Faktor 32 skaliert, was ! adc #0 ' dem Linksshiften um 5 Stellen entspricht. ! sta IMc_dz '-36..36 ' -64..16 -> -64/32..16/32 -> -2.0..0.5 ! sta IMz ! clr iter ' Anzahl der Iterationen festhalten #mandel_1 ! lda REz '-64..63 ' RE(z)^2 berechnen und festhalten, da es ! tax ' unten noch einmal benoetigt wird. ! bsr imul ! asrx ! rora ! adc #0 ! sta REzQ '0..128 ! lda IMz '-64..63 ' IM(z)^2 berechnen und festhalten, da es ! tax ' unten noch einmal benoetigt wird. ! bsr imul ! asrx ! rora ! adc #0 ! sta IMzQ '0..128 ! add REzQ ' Abfrage RE(z)^2+IM(z)^2 >= 128 (= 4.0) ! blt mandel_2 '(N XOR V)=1 ' Schleife beenden, wenn Betrag von z >= 2.0 ! inc iter ' Iterationszaehler um eins inkrementieren ! lda #14 ' und auf maximale Anzahl der Iterationen ! cbeq iter,mandel_2 ' ueberpruefen ! lda REz '-64..63 ' IM(z) = 2*RE(z)*IM(z) + IM(c) ! ldx IMz '-64..63 ! bsr imul '-4032..4096 -> x:a= $ff:04..$01:$00 ! adc IMc_dz '-36..36 -/+ 1 ! bcc mandel_3 ! incx ' Die Multiplikation mit 2 liefert ein #mandel_3 ' 16-Bit-Ergebnis, deshalb muss auch die ! brclr #7,IMc_dz,mandel_4 ' Addition in 16-Bit durchgefuehrt werden. ! decx #mandel_4 ! sta IMz ! pushx ' Schleife verlassen, falls das 16-Bit- ! poph ' Ergebnis zu gross oder zu klein ist. ! tax ' Wenn IM(z) z.B. -65, dann waere IM(z)^2 ! cmphx #63 ' gleich 132. Die Schleife wuerde also nach- ! bgt mandel_2 ' folgend beim "blt mandel_2" verlassen. ! cmphx #-64 ! blt mandel_2 ! lda REzQ '0...126 ' RE(z) = RE(z)^2-IM(z)^2 + RE(c) ! sub IMzQ '0...126 -> -126..126 ! add REc_dz '-64..15 ! tax ' Schleife verlassen, falls bei der Addition ! tpa ' ein Ueberlauf im 2-er-Komplement auftrat ! tsta ! bmi mandel_2 'V - Overflow Flag - A7 & M7 & ~R7 | ~A7 & ~M7 & R7 ! stx REz ! cmpx #63 ' Schleife verlassen, falls das Ergebnis ! bgt mandel_2 ' zu gross oder zu klein ist. Begruendung ! cmpx #-64 ' siehe oben. ! bge mandel_1 #mandel_2 ! rts ' Ruecksprung nach BASIC ' Vorzeichenbehaftete Multiplikation im mit 32 skalierten 8-Bit-Fixkommaformat ' von Dietmar Harlos am 1. Juli 2006 #imul ! clr OSTEMP ! tsta ' Zum Verstaendnis, Multiplikation 1.75 mal 1.5: ! bpl imul_1 ' Die Zahlen sind skaliert: 1.75*32=56, 1.5*32=48 ! inc OSTEMP ' Multiplikation liefert: 1.75*1.5*32*32=2688 ! nega ' Zur Anpassung an die Skalierung wird durch 32 #imul_1 ' geteilt, also 5-mal nach rechts geshiftet. ! tstx ' Das Ergebnis 2688/32=84 ist somit 84/32=2.625. ! bpl imul_2 ! inc OSTEMP ! negx ' Maximaler Rueckgabewert (Ergebnis): #imul_2 ' -64*-64= 4096/2^4= 256 -> x:a = $01:$00 ! mul ' -64* 63=-4032/2^4=-252 -> x:a = $ff:$04 #imul_4 ! brclr #0,OSTEMP,imul_3 ! comx ! nega ! bne imul_3 ! incx #imul_3 ! asrx ! rora ' Eigentlich sind 5 Schiebe- und Rotierbefehle ! asrx ' notwendig, aber im Hauptprogramm muss an ! rora ' einer Stelle das Ergebnis der Multiplikation ! asrx ' mit zwei multipliziert werden. Deshalb wird ! rora ' das fuenfte Schieben inklusive Runden bei ! asrx ' Bedarf im Hauptprogramm durchgefuehrt. ! rora ! rts END PROC ' --------------------------------------------------------------------------- ' Subroutinen für das OLED-I2C-Display ' --------------------------------------------------------------------------- PROCEDURE oled_init PAUSE 2 'etwas warten i2c_start IF i2c_write(OLED_I2C_ADDR)=0 THEN PRINT "OLED-I2C-Display reagiert nicht!" END END IF i2c_write &h00 '0x00 for command, 0x40 for data FOR a=0 TO 26 i2c_write LOOKTABBYTE(init_sequence,a) NEXT a i2c_stop oled_command(OLED_DISP_ON) RETURN END PROCEDURE PROCEDURE oled_command(c) i2c_start i2c_write OLED_I2C_ADDR i2c_write &h00 '0x00 for command, 0x40 for data i2c_write c i2c_stop RETURN END PROCEDURE PROCEDURE oled_data(data) i2c_start i2c_write OLED_I2C_ADDR i2c_write &h40 '0x00 for command, 0x40 for data i2c_write data i2c_stop RETURN END PROCEDURE ' x von 0 bis 127, y von 0 bis 63 (in 8-er Schritten) PROCEDURE oled_gotoxy(x,y) i2c_start i2c_write OLED_I2C_ADDR i2c_write &h00 '0x00 for command, 0x40 for data i2c_write &hb0+(y SHR 3) i2c_write &h21 i2c_write x i2c_write &h7f i2c_stop RETURN END PROCEDURE ' --------------------------------------------------------------------------- ' Tabellen ' --------------------------------------------------------------------------- ' Initialization Sequence für OLED-I2C-Display ' nach lcd_text.zip TABLE init_sequence BYTE OLED_DISP_OFF, ' Display OFF (sleep mode) &h20, &h00, ' Set Memory Addressing Mode ' 00=Horizontal Addressing Mode; 01=Vertical Addressing Mode; ' 10=Page Addressing Mode (RESET); 11=Invalid &hB0, ' Set Page Start Address for Page Addressing Mode, 0-7 &hC8, ' Set COM Output Scan Direction &h00, ' --set low column address &h10, ' --set high column address &h40, ' --set start line address &h81, &h3F ' Set contrast control register &hA1, ' Set Segment Re-map. A0=address mapped; A1=address 127 mapped. &hA6, ' Set display mode. A6=Normal; A7=Inverse &hA8, 63 ' DISPLAY_HEIGHT-1 ' Set multiplex ratio(1 to 64) &hA4, ' Output RAM to Display ' &hA4=Output follows RAM content; &hA5,Output ignores RAM content &hD3, &h00 ' Set display offset. 00 = no offset &hD5, ' --set display clock divide ratio/oscillator frequency &hF0, ' --set divide ratio &hD9, &h22 ' Set pre-charge period &hDA, &h12 ' Set com pins hardware configuration &hDB, ' --set vcomh &h20, ' &h20,0.77xVcc &h8D, &h14 ' Set DC-DC enable END TABLE ' --------------------------------------------------------------------------- ' Die I2C-Routinen einbinden ' --------------------------------------------------------------------------- INCLUDE "omax_i2c.pro" ' --------------------------------------------------------------------------- ' Die Firmware-Routinen einbinden ' --------------------------------------------------------------------------- INCLUDE "om_fw.pro" ' --- Programmende --------------------------------------------------------- Meine Homepage: http://ccintern.dharlos.de |
Antwort schreiben |