Unterschiede

Hier werden die Unterschiede zwischen zwei Versionen angezeigt.

Link zu dieser Vergleichsansicht

grafik-display_am_stm32f4 [2017/04/05 14:14]
huwaldt
grafik-display_am_stm32f4 [2019/02/04 14:10]
Zeile 1: Zeile 1:
-====== Grafik-Display am STM32F4-Discovery ====== 
-Das kleine Grafikdisplay wird bereits sehr erfolgreich mit den größeren AVRs eingesetzt. ARM Controller rufen mit ihrer Leistungsfähigkeit geradezu nach grafischen Displays. Dieser Abschnitt soll kurz umreißen wie man ein solches Display, unter Nutzung verfügbarer Bibliotheken,​ effizient einsetzt. 
  
-//Bitte beachten Sie, dass dieser Abschnitt des Tutorials auf die Kenntnisse der vorangegangenen Abschnitte aufbaut. Gern können wir dieses Anwendungsbeispiel auch gemeinsam in einem unserer [[http://​shop.myavr.de/​index.php?​sp=pages/​schulung/​arm_aufbauseminar_mc.htm|ARM-Aufsteiger-Seminare]] entwickeln.//​ 
- 
-====== Benötigte Hardware und Software ====== 
-Natürlich benötigen wir zunächst ein Grafik-Display für unser STM32F4-Discovery. Dazu bietet sich für den versierten Bastler der Bausatz an oder, wer es nicht so mit dem Löten hat, die bestückte Variante. 
-  * [[http://​shop.myavr.de/​ARM-Produktlinie/​GraficLCD%20Add-On,​%20Bausatz.htm?​sp=article.sp.php&​artID=200095|Grafik-Display Bausatz]] 
-  * [[http://​shop.myavr.de/​ARM-Produktlinie/​GraficLCD%20Add-On.htm?​sp=article.sp.php&​artID=200096|Grafik-Display fertig bestückt]] 
- 
-Dieser Tutorialabschnitt wendet sich vor allem an die Nutzer eines STM32 Einsteigersets. Daher sollten folgende Hard- und Softwarekomponenten verfügbar sein: 
-  * [[http://​shop.myavr.de/​ARM-Produktlinie/​STM32F4-Discovery.htm?​sp=article.sp.php&​artID=200072|STM32F4 Discovery]] 
-  * [[http://​shop.myavr.de/​ARM-Produktlinie/​mySTM32-Board-F4D,​%20Bausatz.htm?​sp=article.sp.php&​artID=200075|das Zusatzboard als Bausatz]] 
-  * [[http://​sisy.de|Die SiSy-Demo]] oder  
-  * [[http://​shop.myavr.de/​index.php?​sp=artlist_kat.sp.php&​katID=7|besser gleich eine Lizenz]] 
- 
-====== Das Grafik-Display Add-On ====== 
-Das GLCD-Add-On basiert auf dem myAVR-Steckverbinder. Dieser verfügt über 20 Pins. Dabei sind 18 Pins für IO-Leitungen frei verfügbar. Das Add-On kann somit an alle [[http://​shop.myavr.de/​index.php?​sp=artlist_kat.sp.php&​katID=14|myAVR-Systemboards]] als auch an die ARM-Erweiterungsboards für das [[http://​shop.myavr.de/​ARM-Produktlinie/​mySTM32-Board-F4D,​%20Bausatz.htm?​sp=article.sp.php&​artID=200075|mySTM32F4D]] und das [[http://​shop.myavr.de/​ARM-Produktlinie/​mySTM32-Board-F0D,​%20Bausatz.htm?​sp=article.sp.php&​artID=200098|mySTM32F0D]] angeschlossen werden. 
- 
->>​{{:​graficlcd-addon_g.png|}}{{:​techb_schaltplan-graficlcd-addon.png?​200|}} 
- 
-Die derzeitige Version des mySTM32 Erweiterungsboards verfügt über keine festen Verbindungen zur Erweiterungsbuchse. Diese kann der Anwender selbst frei wählen und entweder per Patchkabel (rechtes Bild) oder mit festen Lötverbindungen realisieren (linkes Bild). ​ 
- 
->>>>​{{:​grafic-lcd_mit_discoveryf4-plus1.png?​260|}} {{:​glcdpng.png?​250|}} 
- 
-Die folgenden Bilder zeigen die Realisierung mit Lötverbindungen. 
- 
->>​{{:​loetplan_mystm32f4_glcd.png?​350|}}{{:​mystm32f4_beispiel_glcd.png?​350|}} 
- 
- 
-====== Vorbereitungen ======= 
-Legen Sie ein neues SiSy-Projekt mit dem Vorgehensmodell ARM und ARM Framework sowie ein Klassendiagramm mit der Zielsprache //ARM C++// an. Beachten Sie die Einstellungen für die Zielplattform STM32F4-Discovery. Laden Sie bitte die Diagrammvorlage für eine //ARM C++ Applikation//​. 
- 
->​{{:​neuesklasendiagramm.jpg?​280|}}{{:​umlprojekt6nu.jpg?​200|}}{{:​umlprojekt7gg.jpg?​200|}} 
- 
-===== Übersetzen,​ übertragen und testen ===== 
-Das Programmgerüst sollte übersetzt (kompilieren,​ linken) und in den Programmspeicher des Controllers übertragen werden (brennen). ​ 
- 
->​{{:​erstellenbrennen.png?​300|}} {{:​brennen.jpg?​350|}} 
- 
-Damit steht die Verbindung zum Controller und wir können mit der Arbeit beginnen. 
- 
-====== Die Bibliotheken ====== 
-Bis hier war es das allgemeine Prozedere, um eine STM32 Anwendung anzulegen. Die aktuellen Treiber für das Grafikdisplay holen wir uns ebenfalls aus dem SiSy-LibStore. Dazu ziehen wir aus der Objektbibliothek das Element LibStore. Daraufhin öffnet sich der SiSy-LibStore-Dialog. Als Suchbegriff geben wir //GLCD// ein. 
- 
->>>​{{:​glcdlibstoresuchen.jpg?​600|}} 
- 
-Wählen sie die angebotene Komponente aus und importieren Sie diese in das Diagramm. Beachten Sie, dass Sie den Baustein für den STM32F4 importieren. 
- 
-====== Erste Schritte ====== 
-Das Paket wird jetzt als verfügbare Komponente im Klassendiagramm angezeigt. Wählen sie auf der Komponente //rechte Maustaste - in neuem Fenster öffnen//. Jetzt können wir uns die innere Struktur der Komponente ansehen. ​ 
- 
-===== Die Klasse GraficLcd ===== 
-Die Klasse //​GraficLcd//​ besteht aus einer Reihe Attributen, unter anderem vom Typ //​DigitalOut//, ​ welche die einzelnen Steuer- und Datenleitungen abstrahieren. Diese sind öffentlich,​ damit das Display vom Anwender konfiguriert werden kann. Die für uns wichtigsten Operationen,​ um später das Spiel zu realisieren,​ können wir auch schon erkennen. 
-  * init() 
-  * light() 
-  * clear() 
-  * setPos() 
-  * write() 
-  * circle() 
-  * line() 
-  * rect() 
-  * ... 
- 
->>​{{:​paketglcdstm32.jpg?​700|}} 
- 
-Um das Display zu verwenden, muss eine Referenz der Klasse //​GraficDisplay//​ in das Klassendiagramm der Anwendung gezogen werden. Diese ist mit einer Aggregation an die Applikations-Klasse anzubinden. Benutzen Sie als Rollenbezeichner den namen //​**+lcd**//​. 
- 
->>​{{:​glcdtestapp.jpg?​700|}} 
- 
-===== Initialisierung des Displays ===== 
-Für die Initialisierung des Displays wählen wir die Operation //onStart// der Klasse //​Application//​ aus. Zuerst sind die einzelnen Steuer- und Datenleitungen zu konfigurieren. Danach kann der eigentliche Displaycontroller initialisiert werden. Vergleichen Sie dazu die Pinbelegung des GLCD-Add-On und des STM32F4 Discovery. 
- 
->>><​code cpp> 
-/// <​sequence show="​hide"​ text="​config LDC_lines">​ 
-lcd.linePs.config (GPIOB,​BIT15);​ 
-lcd.lineC86.config(GPIOB,​BIT14);​ 
-lcd.lineLight.config(GPIOB,​BIT13);​ 
-lcd.lineE.config(GPIOB,​BIT12);​ 
-lcd.lineRw.config(GPIOB,​BIT11);​ 
-lcd.lineRs.config(GPIOB,​BIT9);​ 
-lcd.lineRes.config(GPIOB,​BIT4);​  
-lcd.lineCs.config(GPIOB,​BIT8);​ //​B7 
-lcd.dataPort.config(GPIOE,​0xFF00);​ //​ E8..E15 
-/// </​sequence>​ 
-lcd.init(); 
-lcd.clear();​ 
-lcd.light();​ 
-</​code>​ 
- 
-Zu guter Letzt schließen wir die Initialisierung mit einem //clear// des Displayinhaltes (falls noch Schrott vom letzten Versuch zu sehen ist) und dem Einschalten der Hintergrundbeleuchtung ab. Die beiden Kommentar-Tags ​ 
- 
->>><​code cpp> /// <​sequence show="​hide"​ ... > </​code>​ 
- 
-und  
- 
->>><​code cpp> /// </​sequence>​ </​code>​ 
- 
-sorgen dafür, dass im Sequenzdiagrammgenerator dieser Teil zugeklappt wird. 
- 
->>>>>>​{{:​glcdinitseq.jpg?​250|}} 
- 
-===== Texte ausgeben ===== 
-Die Ausgabe von Texten erfolgt an einer beliebigen grafischen Position. Dazu benutzen wir die Operation //setPos//. Der Cursor wird bei der Ausgabe des Textes mit der Operation //write// automatisch weiter gestellt. Deshalb setzt sich jede weitere Ausgabe an jeweils der letzten Cursorposition fort. 
- 
-<code cpp> 
-lcd.setPos(40,​ 20);          // Cursor positionieren 
-lcd.write("​Hallo mySTM32"​); ​ // Textausgabe 
-lcd.write("​!!!"​); ​           // den Text fortsetzen 
-</​code>​ 
- 
-Erstellen Sie die Anwendung und übertragen Sie diese in den Programmspeicher des Controllers. Testen sie das Programm! 
- 
-===== Grafische Primitive ===== 
-... Punkt, Linie, Rechteck, Kreis 
- 
-<code c> 
-lcd.line(10,​10,​50,​50);​ 
-lcd.rect(20,​10,​40,​20);​ 
-lcd.circle(30,​30,​10,​true);​ 
-</​code>​ 
- 
-Erstellen Sie die Anwednung und übertragen Sie diese in den Programmspeicher des Controllers. Testen sie das Programm! 
- 
-====== Eine kleine Anwendung ====== 
-Es soll eine kleine Spielanwendung,​ in Anlehnung alter Spieleklassiker erstellt werden. Wir nennen unser Projekt //​**SoloSquash**//​. Bei diesem Spiel ist mit einem Schläger zu verhindern, dass ein Ball ein Tor trifft. Der Ball muss gegen die gegenüberliegenden Wände (Spielfeld/​Display-Grenzen) gespielt werden. An den Wänden prallt der Ball ab. Trifft der Ball das Tor, ist das Spiel vorbei. Den Schläger wollen wir mit dem Lagesensor auf dem STM32F4 Board steuern. Im Folgenden ein kleines [[http://​de.wikipedia.org/​w/​index.php?​title=Storyboard|Storyboard]] für das Spielszenario. 
- 
- 
-===== Aufgabe ===== 
->>>​{{:​uc_stmball.jpg?​600|}} 
- 
->>>​{{:​storyboard.jpg?​600|}} 
- 
-===== Lösungsansatz ===== 
-Aus der textuellen und bildlichen Aufgabenstellung lassen sich folgende Klassenkandidaten ableiten: 
- 
-  * SoloSquash, Spiel, Anwendung (Synonyme) 
-  * Schläger 
-  * Tor 
-  * Ball 
-  * Bewegungssensor 
-  * Display 
- 
- 
- 
-===== Das Projekt SoloSquash vorbereiten ===== 
-Falls Sie die oben beschriebenen Arbeitschritte noch nicht ausgeführt haben legen Sie ein  neues SiSy-Projekt mit dem Namen //​SoloSquash//​ an. Importieren Sie das aktuelle ARM-Framework für den STM32F4 aus dem SiSy-LibStore in das neue Projekt. Erstellen Sie ein Klassendiagramm mit dem Namen //​SoloSquash//​. Wählen Sie als Einstellungen für die Zielplattform //ARM C++, STM32F4 Discovery, ST-Link V2//. Laden Sie aus dem LibStore das Grundgerüst für eine ARM Applikaton und die Komponente für das GLCD. 
- 
-===== Entwurf und Beginn der iterativen Realisierung ===== 
-Aus den Klassenkandidaten leiten wir die Fachklassen für unser Klassenmodell ab. Einige der Klassen sind durch die verwendeten Vorlagen bereits verfügbar. So zum Beispiel die Klassen für die Applikation,​ den Bewegungssensor und das Grafikdisplay. Wir ergänzen unser Klassenmodell als erstes um die Fachklassen //​Schlaeger//,​ //Ball// und //Tor//. Die aggregieren wir in der Anwendungsklasse //​Application//​ des Spiels. ​ 
- 
->>​{{:​class1solosquash.jpg?​700|}} 
- 
-Der nächste Schritt soll erst einmal sein, dass alle Spielelemente an einer bestimmten Position angezeigt werden. Dabei soll der Ball als Kreis, der Schläger als Rechteck und das Tor als Linie angezeigt werden. Daraus ergeben sich für die Klassen eine Reihe von Attributen, die jeweils die Position und Größe der Elemente definieren. Zusätzlich geben wir den Klassen noch eine Operation zum Anzeigen. Ergänzen Sie das Klassendiagramm wie folgt: 
- 
->>​{{:​class2solosquash.jpg?​700|}} 
- 
-Jetzt müssen die Operationen mit Leben erfüllt werden. Ergänzen Sie die angegebenen Operationen wie folgt: 
- 
->>>​**Application::​onStart()** 
->>><​code cpp> 
-/// <​sequence show="​hide"​ text="​config LDC_lines">​ 
-lcd.linePs.config ( GPIOB,​BIT15);​ 
-lcd.lineC86.config( GPIOB,​BIT14);​ 
-lcd.lineLight.config( GPIOB,​BIT13);​ 
-lcd.lineE.config( GPIOB,​BIT12);​ 
-lcd.lineRw.config( GPIOB,​BIT11);​ 
-lcd.lineRs.config( GPIOB,​BIT9);​ 
-lcd.lineRes.config( GPIOB,​BIT4);​  
-lcd.lineCs.config( GPIOB,​BIT8);​ //​B7 
-lcd.dataPort.config(GPIOE,​0xFF00);​ //​ E8..E15 
-/// </​sequence>​ 
-lcd.init(); 
- 
-/// <​sequence show="​hide"​ text="​Intro anzeigen">​ 
-lcd.clear();​ 
-lcd.light();​ 
-lcd.setPos(35,​ 20); 
-lcd.write("​Willkommen"​);​ 
-lcd.setPos(55,​ 28); 
-lcd.write("​zu"​);​ 
-waitMs(500);​ 
-lcd.setPos(16,​ 32); 
-lcd.fontStyle=LCD_FONT_WIDE;​ // LCD_FONT_FIXED,​ LCD_FONT_NORMAL 
-lcd.write("​SoloSquash!"​);​ 
-waitMs(1000);​ 
-lcd.line(20,​55,​100,​55);​ 
-waitMs(500);​ 
-lcd.rect(60,​50,​70,​54,​true);​ 
-waitMs(500);​ 
-lcd.circle(50,​50,​3,​true);​ 
-/// </​sequence>​ 
- 
-waitMs(2000);​ 
-lcd.clear();​ 
-</​code>​ 
- 
-Das Display wird initialisiert und ein Begrüßungsbildschirm angezeigt. Dieser bleibt 2 Sekunden stehen. Danach wird das Display gelöscht und das Spiel kann beginnen. 
- 
->>>​**Application::​onWork()** 
->>><​code cpp> 
-tor.show(); 
-schlaeger.show();​ 
-ball.show();​ 
-waitMs(10); ​   // vorläufig bis wir eine bessere Idee haben ;-) 
-</​code>​ 
- 
-Die Operation //onWork// wird aus der //​Mainloop//​ der Applikation aufgerufen. Hier können wir alles unterbringen was keine strengen Zeitkriterien erfüllen muss. Das Aufrufen der Wartefunktion //waitMs//, um einfach nur Rechenzeit zu verbraten, ist jedoch nicht wirklich sexy und sollte im weiteren Verlauf eleganter gelöst werden. 
- 
->>>​**Schlaeger::​show()** 
->>><​code cpp> 
-#define posY 55 
-app.lcd.rect(posX,​posY,​posX+breite,​posY+hoehe,​true);​ 
-</​code>​ 
- 
-Der Schläger wird an einer festen Y-Position angezeigt. Die X-Position, Breite und Höhe des Schlägers sind Attribute. Es ist natürlich auch denkbar die Y-Position als Attribut zu realisieren. 
- 
->>>​**Ball::​show()** 
->>><​code cpp> 
-app.lcd.circle(posX,​posY,​radius,​true);​ 
-</​code>​ 
- 
-Ball und Tor bedürfen wohl vorerst keiner weiteren Erläuterung. 
- 
->>>​**Tor::​show()** 
->>><​code cpp> 
-app.lcd.line(1,​63,​127,​63);​ 
-</​code>​ 
- 
-Erstellen Sie die Anwendung und übertragen Sie diese in den Programmspeicher des Controllers. Testen sie das Programm! 
- 
- 
->>>​{{:​introsolosquash.jpg?​300|}} {{:​iteration1.jpg?​300|}} 
- 
-**WOW!** OK, weiter im Text 8-) 
- 
-===== Die Ballbewegung realisieren ===== 
-Die Bewegung des Balls soll der Einfachheit halber zunächst nur diagonal erfolgen. Falls Sie Spaß an der Idee finden können Sie gern das Spiel verfeinern und ausbauen. Bei näherer Betrachtung kommt die Bewegung in einer Spielanimation dadurch zustande, dass dem Auge durch eine Folge von Einzelbildern die Bewegung "​vorgegaukelt"​ wird. Der Ball vollzieht auf dem Display eine schrittweise Änderung (step) seiner Position in X-Richtung und in Y-Richtung. An den den Displaygrenzen als "​Spielfeldrändern"​ (64x128 Pixel) muss eine Richtungsänderung (abprallen) realisiert werden. 
- 
-Zunächst ergänzen wir die Klasse //Ball// mit der Operation //move()// und die Attribute //stepX// sowie //stepY//. 
- 
->>>​{{:​ballmove.png?​200|}} 
- 
-Ergänzen Sie die folgende Operation mit dem Quellcode für die Ballbewegung. 
- 
->>>​**Ball::​move()** 
->>><​code cpp> ​ 
-// ein Schritt 
-posX+=stepX;​ 
-posY+=stepY;​ 
- 
-// oben checken 
-if (posY <= radius) ​ 
- stepY = +1; 
- 
-// links checken 
-if (posX <= radius) 
- stepX = +1; 
-  
-// rechts checken 
-if (posX >= 127-radius) 
- stepX = -1; 
- 
-// unten checken 
-if (posY >= 63-radius) 
- stepY = -1; 
-</​code>​ 
- 
-Damit das auch zu sehen ist, muss die Operation //onWork// um die Nachricht //​ball.move()//​ erweitert werden. 
- 
->>>​**Application::​onWork()** 
->>><​code cpp> ​ 
-tor.show(); 
-schlaeger.show();​ 
-ball.move();​ 
-ball.show();​ 
-waitMs(10); ​   // vorläufig bis wir eine bessere Idee haben ;-) 
-</​code>​ 
- 
-Übersetzen Sie das Programm. Korrigieren sie ggf. Schreibfehler und übertragen Sie das Programm auf den Controller. 
- 
->>>​{{:​ballmove.jpg?​300|}} 
- 
-Beim Testen der Anwendung fällt sofort auf, dass der Ball sich zwar bewegt, aber er hinterlässt eine unschöne "​Schleifspur"​. Wir haben vergessen den Ball an der alten Position verschwinden zu lassen. Dafür bietet das Display verschiedene Modi, um Pixel auf dem Display zu manipulieren. ​ 
- 
-  * SET ... Pixel setzen 
-  * CLR ... Pixel löschen 
-  * XOR ... Pixel invertieren 
- 
-Diese Möglichkeiten sind in der Treiber-Klasse für das Display als Definitionen hinterlegt. 
- 
-  * LCD_MODE_SET 
-  * LCD_MODE_CLR 
-  * LCD_MODE_XOR 
- 
-Die von uns verwendete Operation //circle// benötigt mindestens drei Parameter (x,​y,​radius). Zusätzlich können als optionale Parameter die Füllung und der Pixelmodus angegeben werden. Daraus ergibt sich folgende Signatur der Operation: 
- 
->><​code cpp> GraficLcd::​cirle( uint8_t x, uint_8_t y, uint8_t radius, bool fill=false, mode=LCD_MODE_SET);​ </​code>​ 
- 
-Wir ändern den Code für die Ballbewegung wie folgt: 
- 
->>><​code cpp> ​ 
-// alten Ball löschen 
-app.lcd.circle(posX,​posY,​radius,​true,​LCD_MODE_CLR);​ 
- 
-// ein Schritt 
-posX+=stepX;​ 
-posY+=stepY;​ 
- 
-// oben checken 
-if (posY <= radius) ​ 
- stepY = +1; 
- 
-// links checken 
-if (posX <= radius) 
- stepX = +1; 
-  
-// rechts checken 
-if (posX >= 127-radius) 
- stepX = -1; 
- 
-// unten checken, hier ist später das Spiel aus "GAME OVER" 
-if (posY >= 63-radius) 
- stepY = -1; 
-</​code>​ 
-  
-Übersetzen Sie das Programm. Korrigieremn sie ggf. Schreibfehler und übertragen Sie das Programm auf den Controller. 
- 
->>><​html><​iframe width="​640"​ height="​480"​ src="​https://​www.youtube.com/​embed/​0u4MxISenCs"​ frameborder="​0"​ allowfullscreen></​iframe></​html>​ 
- 
-Damit ist die Ballbewegung vorerst realisiert. Es fehlt nur noch das geforderte Systemverhalten,​ wenn der Ball das Tor berührt. Dann soll das Spiel ja zu Ende sein. Es macht jedoch noch nicht wirklich viel Sinn, bevor wir das Tor nicht mit dem Schläger verteidigen können. 
- 
-===== Die Schlägersteuerung realisieren ===== 
-Für die Steuerung des Schlägers soll der Bewegungssensor genutzt werden. Dieser wurde im Tutorial bereits besprochen. Es ist eine Instanz des Sensors anzulegen und wir müssen ermitteln, welche Sensorwerte für die Steuerung des Schlägers in Frage kommen. Als Erstes sollten wir uns Überlegen, in welcher Beziehung steht der Schläger mit dem Sensor. Dafür kommen in der UML folgende Beziehungstypen in Betracht: 
- 
-  * der Schläger kennt den Sensor = gerichtete Assoziation 
-  * der Schläger hat den Sensor =  Aggregation 
-  * der Schläger ist der Sensor = Generalisierung 
- 
-Alle drei Varianten sind durchaus machbar. Wir arbeiten mit dem aus objektorientierter Sicht  mutigsten Entwurf für das Problem weiter, der Generalisierung. Suchen Sie im Framework das Paket //​EvalBoard_Stm32f4Discovery//​ und ziehen sie das Paket und die Klasse //​F4dMotionSensor//​ in das Diagramm. Ergänzen Sie das Klassendiagramm wie folgt (Paket EvalBoard, Generalisierung zur Sensorklasse):​ 
- 
->​{{:​mitmotion.png?​700|}} 
- 
-Jetzt können wir die Sensordaten ermitteln und am besten gleich mal auf dem Display ausgeben. Diesen Code realisieren wir in der Operation //show()// des Schlägers. 
- 
->>><​code cpp> 
-#define posY 55 
-app.lcd.rect(posX,​posY,​posX+breite,​posY+hoehe,​true);​ 
- 
-// Testdaten ermitteln 
-String txt; 
-s32 x,y,z; 
-this->​getAcc(x,​y,​z);​ 
-txt.format("​x=%d ​   ",x); 
-app.lcd.setPos(10,​10);​ 
-app.lcd.write(txt);​ 
-txt.format("​y=%d ​   ",y); 
-app.lcd.setPos(10,​20);​ 
-app.lcd.write(txt);​ 
-txt.format("​z=%d ​   ",z); 
-app.lcd.setPos(10,​30);​ 
-app.lcd.write(txt);​ 
-</​code>​ 
- 
-Übersetzen Sie das Programm, korrigieren sie ggf. Schreibfehler und testen Sie das System. Beobachten Sie die Veränderung der Sensordaten auf dem Display bei der gewünschten Steuerbewegung. 
- 
->>>​{{:​sensordaten.jpg?​300|}} 
- 
-Der Test sollte ergeben, dass die Y-Koordinate die besten Werte für die Schlägersteuerung liefert. Im Ruhezustand liegt der Y-Wert etwa bei 50 bis 70. Neigt man das Board etwa 45° nach rechts folgt der Wert bis etwa 600. Wenn das Board nach links geneigt wird, liegt der Y-Wert bei 45° etwa bei -600. Der Schläger sollte seine Position natürlich nicht um bis zu 600 Pixel verändern. Aber wenn wir den Wert durch 100 teilen, erhalten wir einen sehr netten Intervall von -6 bis +6. Im Ruhezustand ist der Wert dann 0. Was will man mehr. Ändern Sie die Operation //show// des Schlägers wie folgt ab: 
- 
- 
->>><​code cpp> 
-#define posY 55 
-s32 x,y,z; 
-this->​getAcc(x,​y,​z);​ 
-app.lcd.rect(posX,​posY,​posX+breite,​posY+hoehe,​true,​LCD_MODE_CLR);​ 
-posX += y/100; 
-if ( posX <= 0 ) 
-   posX = 0; 
-if ( posX >= 127-breite ) 
-   posX = 127-breite; 
-app.lcd.rect(posX,​posY,​posX+breite,​posY+hoehe,​true,​LCD_MODE_SET);​ 
-</​code>​ 
- 
-Übersetzen Sie das Programm, korrigieren sie ggf. Schreibfehler und testen Sie das System. Jetzt sollte sich der Schläger gefühlvoll über die Neigung des Boards bewegen lassen. 
- 
-===== Das kleine Projekt fertigstellen ===== 
-Die grundlegenden Algorithmen sind gelöst. Es müssen noch Lösungen für das Auftreffen des Balls auf den Schläger und für das //​GAME-OVER//​ Szenario sowie eine elegantere Lösung für das Timing gefunden werden. Wenden wir uns zuerst der Trefferauswertung zu. Der Ball verfügt über eine X- und Y-Position sowie über einen Radius. Diese Werte sollen als Parameter einer Nachricht an den Schläger gesendet werden. Dieser antwortet mit //true// oder //false//. Die Nachricht nennen wir am besten gleich //​treffer//​. Erweitern Sie die Klasse //​Schlaeger//,​ wie in der folgenden Darstellung gezeigt, um die Operation //​treffer//​. 
- 
->>>​{{:​treffer.png?​400|}} 
- 
-Die Quellcodes für die Operationen //treffer// des Schlägers und //move// des Balls sind wie folgt zu ergänzen: 
- 
->>>​**Schlaeger::​treffer()** 
->>><​code cpp> 
-if ( x >= posX && ​ x <= posX+breite && y + radius >= 55 ) 
- return true; 
-</​code>​ 
- 
->>>​**Ball::​move()** 
->>><​code cpp> 
-// alten ball löschen 
-app.lcd.circle(posX,​posY,​radius,​true,​LCD_MODE_CLR);​ 
-// ein Schritt 
-posX+=stepX;​ 
-posY+=stepY;​ 
-// oben checken 
-if (posY <= radius) ​ 
- stepY = +1; 
-// links checken 
-if (posX <= radius) 
- stepX = +1; 
-// rechts checken 
-if (posX >= 127-radius) 
- stepX = -1; 
-// Schläger checken 
-if (app.schlaeger.treffer(posX,​posY,​radius)) 
- stepY=-1; 
-// unten checken, Game Over? 
-if (posY >= 63-radius) 
- stepY = -1; 
-</​code>​ 
- 
-Übersetzen und Übertragen Sie das Programm auf den Controller. Testen sie, ob der Schläger den Ball jetzt abwehren kann. Das "Game Over"​-Szenario planen wir wie folgt zu realisieren:​ Die Applikation erhält ein Attribut //​gameOver//​. Sobald dieses Attribut den Wert //true// annimmt, wird das Spiel beendet und das Spielende angezeigt. Zusätzlich bereiten wir noch das elegantere Timing vor. Ergänzen Sie das Klassendiagramm wie folgt: 
- 
->>>​{{:​finalgame.png?​700|}} 
- 
-Die Klasse //​Application//​ wurde durch die Attribute //​gameOver//,​ //refresh// und //speed// sowie die Operationen //​onTimer10ms//​ und //​onTimer1s//​ ergänzt. Zuerst passen wir den Quellcode im Ball an.  ​ 
- 
->>>​**Ball::​move()** 
->>><​code cpp> 
-// alten ball löschen 
-app.lcd.circle(posX,​posY,​radius,​true,​LCD_MODE_CLR);​ 
-// ein Schritt 
-posX+=stepX;​ 
-posY+=stepY;​ 
-// oben checken 
-if (posY <= radius) ​ 
- stepY = +1; 
-// links checken 
-if (posX <= radius) 
- stepX = +1; 
-// rechts checken 
-if (posX >= 127-radius) 
- stepX = -1; 
-// Schläger checken 
-if (app.schlaeger.treffer(posX,​posY,​radius)) 
- stepY=-1; 
-// unten checken, Game Over !!!! 
-if (posY >= 63-radius) 
- app.gameOver=true;​ 
-</​code>​ 
- 
-Danach bietet es sich an, erst mal die einfacheren Codes der Timer-Ereignisse zu realisieren. 
- 
->>>​**Application::​onTimer10ms()** 
->>><​code cpp> 
-refresh++; 
-</​code>​ 
- 
->>>​**Application::​onTimer1s()** 
->>><​code cpp> 
-speed--; 
-if (speed<​=0) 
-   ​speed=0;​ 
-</​code>​ 
- 
-Zum Schluss passen wir die Operation //onWork// an. 
- 
->>>​**Application::​onWork()** 
->>><​code cpp> 
-if (refresh>​=speed/​10) 
-{ 
- tor.show();​ 
- schlaeger.show();​ 
- ball.move();​ 
- ball.show();​ 
- refresh=0; 
-} 
- 
-if (gameOver) 
-{ 
- lcd.clear();​ 
- lcd.setPos(16,​ 32); 
- lcd.fontStyle=LCD_FONT_WIDE;​ 
- lcd.write("​GAME OVER!"​);​ 
- while (true) ​ 
- { 
- lcd.light(false);​ 
- waitMs(1000);​ 
- lcd.light(true);​ 
- waitMs(1000);​ 
- } 
-}</​code>​ 
- 
-Das Attribut //refresh// ist ein Up-Counter, der alle 10 Millisekunden um eins erhöht wird. Das Attribut //speed// ist ein Down-Counter,​ der pro Sekunde um eins verringert wird. Beide zusammen bewirken, dass die Spieldynamik langsam zunimmt und damit auch der Schwierigkeitsgrad. Zusätzlich ist das böse //​waitMs(10)//​ aus unserem Spielzyklus und damit theoretisch Rechenzeit frei für weitere Funktionen. Bei //gameOver == true// wird der Bildschirm gelöscht und die Ausschrift "GAME OVER!" angezeigt. Das Display fängt an zu blinken und nur noch ein RESET kann das System aus der "​End-Schleife"​ holen. 
- 
-===== Test ===== 
-Übersetzen und übertragen Sie das Programm auf den Controller. 
- 
-**Viel Spaß beim Spielen und beim Weiterentwickeln!** 
- 
-{{:​superballstm32.zip|Projektarchiv}} 
- 
- 
- 
-====== Videozusammenfassung ====== 
->>><​html><​iframe width="​560"​ height="​315"​ src="​https://​www.youtube.com/​embed/​AGKBTGTnKlo"​ frameborder="​0"​ allowfullscreen></​iframe></​html>​ 
- 
-====== Seminarhinweise ====== 
- 
-[[http://​shop.myavr.de/?​sp=schulungen.sp.php|Seminarangebote]]