Pimp-My-Kicker a.k.a. Wham-O-Meter

Software

Ausführlicher möchte ich die erstellte Software beschreiben, hier steckt einiges an Know-How drin. Der Wham-O-Meter v1.2 ist selbstverständlich verfügbar und steht genau wie die Platinen-Files unter der CC 3, nc-by-sa; bitte beachtet dies, bevor ihr ihn in eurem Projekt verwendet. Der Quellcode ist an sich kommentiert und sollte einen guten Einstieg ermöglichen.

Beschreibung der Messung auf Softwareseite

Zunächst muss eine Unterbrechung der Lichtschranke von Mikrocontroller ausgewertet werden. Es sind dabei 24 Kanäle für 24 Lichtschranken zu berücksichtigen. Am schnellsten funktioniert das, wenn die MCU für jede Lichtschranke einen sogenannten externen Interrupt besitzt und damit nur dann für die Messung arbeitet, wenn dies durch Balleintritt erforderlich ist.

Globale Variablen

Wir benötigen einige globale Variablen für die Zeitmessung; global, damit der Kompiler die Adressen direkt statisch in den ISR ablegen kann und wir nicht viel manuell kopieren müssen.

Pin-Change-Interrupts

Das System für viele externe Interrupts ist beim ATmega644 etwas einfacher gestrickt: für jeden der vier Ports A, B, C und D lassen sich sogenannte Pin-Change-Interrupts (PCINTn) definieren. Sie lösen aus, sobald sich der Pegel an mindestens einem Pin 0-7 des Ports von einer 0 in eine 1 oder umgekehrt ändert („toggle“). Dabei ist jedem Port genau ein Interuptvektor zugeteilt, es existieren also maximal vier unterschiedliche Vektoren und damit maximal vier unterschiedliche Interrupt-Service-Routinen (ISR).

Leider kann man nicht aus der Hardware herauslesen, welcher PIN des Ports die Änderung ausgelöst hat, sondern muss den alten Zustand in einer Variable speichern und bei jedem Auslösen mit dem aktuellen PIN[A-D]-Register vergleichen. Daraus erhält man den jeweiligen Pin.

Neben dem globalen Interrupt-Enable-Flag müssen die PCINT wie folgt initialisiert werden:

PCMSK1 wird nicht intialisiert, genauso wie Bit 2 im Pin-Change-Interrupt-Control-Register (PCICR). Beide gehören zu Port B, den wir für das LCD und die Tasten verwenden.

Um die Interrupt-Service-Routine möglichst klein zu halten und die eigentlichen Berechnungen nur im Hauptprogramm durchzuführen, werden wir bei jedem Pin-Change-Interrupt die gleiche Routine ausführen und den aktuellen Timer-Wert mit allen drei PIN-Registern zur späteren Auswertung in ein RAM-Array bestehend aus einem Struct speichern.

Sobald einer der 24 Pins seinen Pegel ändert, wird eine der drei folgenden ISR angesprungen:

Zeitmessung

Der ATmega644 besitzt einen 16-bit Timer/Counter (TIMER1), den wir -wie oben im Quelltext ersichtlich- einfach beim ersten Unterbrechen einer Lichtschranke starten und dann laufen lassen. Außerdem erhalten wir durch das Meßsystem in einem RAM-Array die gemessenen Zeiten und PIN?-Zustände. Es gilt nun, diese Daten in fünf Schritten auszuwerten:

  1. Aus einer PIN[A,C,D]-Änderung (0->1) den Anfangszeitpunkt einer Lichtschrankenunterbrechnung bestimmen.
  2. Aus einer PIN[A,C,D]-Änderung (1->0) den Endzeitpunkt einer Lichtschrankenunterbrechnung bestimmen.
  3. Die Zeitdifferenz (=Unterbrechnungszeit) jeder Lichtschranke berechnen
  4. Das Maximum der Zeitdifferenzen finden.
  5. Die Geschwindigkeit berechnen.

Die Rohdaten liegen im Struct-Array timerValues, die Startzeitpunkte werden wir in timerValuesStart schreiben, die Endzeitpunkte in timerValuesEnd. Weiterhin wird das Maximum in lastMeasuredTicks ermittelt werden und die Geschwindigkeit in lastMeasuredSpeed.

Hier der auswertende Code aus der main()-Funktion im Zustand MAIN_SCREEN:

Benötigt der Ball zu lange, so läuft der Timer über. In diesem Falle haben wir eine ISR, die keine Zeit berechnet, alle Variablen zurücksetzt und den Timer ausschaltet.

Wir können nun berechnen, welche minimale und theoretisch maximale Geschwindigkeit unser Equipment messen kann. Die Zeit, die ein Timertick in Sekunden benötigt, ist:

  • \text{Taktfrequenz} f_\text{CPU} = 10000000\frac{1}{\text{s}}
  • t_\text{timerzylkus} = \frac{prescaler}{f_\text{CPU}}=\frac{64}{10000000\frac{1}{\text{s}}} =0{,}0000064\text{s}

Mit einem angenommenen Balldurchmesser von 0,034 Metern (34mm) errechnet sich die minimale Geschwindigkeit bei gemessenen 65535 Timerzylen zu:

  • v_\text{min} = \frac{\text{s}}{65535 \cdot t_\text{timerzylkus}}=\frac{0{,}034m}{65535 \cdot 0{,}0000064\text{s}} \simeq 0{,}081 \frac{\text{m}}{\text{s}} = 0{,}2916 \frac{\text{km}}{\text{h}}

und die maximale Geschwindigkeit mit nur einem Timerzyklus zu:

  • v_\text{max} = \frac{\text{s}}{1 \cdot t_\text{timerzylkus}}=\frac{0{,}034m}{1 \cdot 0{,}0000064\text{s}} = 5312{,}5 \frac{\text{m}}{\text{s}} = 19125 \frac{\text{km}}{\text{h}}

Das sollte wohl reichen…

Messablauf

Trifft also ein Ball auf’s Tor, so wird die erste Lichtschranke unterbrochen und die ISR angesprungen. Dort wird der Timer aktiviert und zurückgesetzt.

Nun wird für jede weitere Änderung der Lichtschranken der Timerwert und die neuen PIN-Zustände in timerValues gespeichert, der allererste Zählerwert wird dabei immer 0 sein. Ob ein Interrupt eine Unterbrechnung oder Wiederherstellung darstellt, wissen wir noch nicht.

Bewegt sich der Ball nun aus dem Detektorfeld heraus, so werden die Lichtschranken nach und nach wiederhergestellt. Ist keine mehr unterbrochen, so wird der Timer abgeschaltet und dem Hauptprogramm das Token zum Berechnen der Zeiten übergeben. Es ermittelt das Maximum der gemessenen Unterbrechungszeitenin der Variable lastMeasuredTicks und stellt die gemessene Geschwindigkeit auf dem Display dar.

Grafisches LCD

Wenden wir uns dem grafischen LCD zu. Das DOGM der Firma Electronic Assembly in Lochham ist ein ziemlich komfortables Bastel-LCD, da es für das eigentliche Anzeigemodul bereits einen Treiber und einen Displaycontroller mitbringt, der auch noch mit minimaler externer Beschaltung die nötige Kontrastspannung selbst erzeugen kann. Dennoch ergeben sich bauartbedingt ein paar Besonderheiten:

  • Das LCD muß (!) nach Anlegen der Versorgungsspannung einmal auf seinem Hardware-Reset-Pin zurückgesetzt werden.
  • Es kann nur beschrieben, nicht aber gelesen werden. Ein Video-Ram ist für die komfortable Programmierung erforderlich.
  • Schriftarten und Zeichensätze sucht man vergebens, der Controller kann nur einzelne Bits und Bytes darstellen. Man muss die Routinen zur Schriftdarstellung und das Erzeugen von Zeichensätzen selbst in die Hand nehmen.
  • Daten werden über SPI seriell und über ein 9. Bit (GPIO) an den Controller gesendet.

Ich habe mir eine eigene Bibliothek geschrieben, die mal generisch für Grafik-LCDs ausgebaut werden kann, im Moment aber nur das DOGM132 unterstützt.

Initialisierung

Zunächst müssen MCU- und LCD-Hardware initialisiert werden. Für das DOGM ist die SPI-Schnittstelle wie folgt zu konfigurieren, es sind nur die relevanten Bits beschrieben:

Einmalig muss das LCD zurückgesetzt werden, es schließt sich eine Reihe von Initialisierungsbefehlen an, die aus dem Datenblatt übernommen wurden.

Header-File

Das Header-File bietet eine Reihe von Grafikfunktionen, die alle ausschließlich auf dem Video-RAM arbeiten:

Schriftarten

Zeichensätze besitzen die DOGM-Serien nicht, man kann immer nur einzelne Pixel in Byte-Gruppen zusammengefasst setzen oder löschen. Es sind somit für jede gewünschte Schriftart eigene Bytemuster zu generieren, die das jeweilige Zeichen darstellen.

Es existiert dafür der GLCD-FontCreator (Version 2.1), ein Java-Programm, das aus Systemschriftarten für avr-gcc entsprechende Headerfiles erzeugt. Für eine Schriftart sind damit mehrere Optionen einstellbar:

  • Startzeichen (standart 0x20): Ab welchem Zeichen sollen Bitmuster erzeugt werden? Es wird automatisch ein Offset mit in die Schriftart integriert, sodass man im C-Quelltext ganz normale Strings verwenden kann („Hallo Welt!“) und nicht selbst von jedem gewünschten Zeichen noch den Hexwert des Startzeichens abziehen muss.
  • Endezeichen: Das letzte zu erzeugende Bitmuster.
  • Schriftart: Welche (TrueType oder OpenType) Schriftart soll abgebildet werden?
  • Schriftgröße: Welche Größe in Point (pt) soll erzeugt werden? Leider haben manche Schriftarten oben wegen einzelner Zeichen bei allen Buchstaben Versatz; man muss sie dann mit negativen Koordianten platzieren. Ist ein bißchen Try-and-Error.
  • Für große Schriftarten kann zusätzlicher Abstand eingegeben werden, sodass z.B. 3 statt nur einem Pixel horizontaler Abstand zwischen zwei Zeichen besteht.

Der Aufbau einer Schriftart ist dann wie folgt:

Es muss dann nur noch glcd_puts() aufgerufen werden, mit einem Pointer auf vBuffer, das den Text enthält und einem PGM_P-Pointer auf die jeweilige Schriftart. Leider hat der FontCreator einen Bug und erstellt die Schriftarten bei mehr als einem Byte pro Spalte nicht richtig. Die Funktion glcd_puts() korrigiert diesen Fehler.

Button-Behandlung

Die Knöpfe sind relativ schnell erklärt. Wir haben nur zwei Anforderungen:

  • Sie müssen entprellt werden. Ein Druck darauf soll von der Software auch nur als solcher gewertet werden.
  • Ich möchte verschieden lange Drücke unterscheiden können (kurz, mittel und lang), damit bei Zahlenwertänderungen durch längeres Drücken der jeweiligen Knöpfe der Wert schneller geändert werden kann (z.B. +-1, +-10, +-50).

Dafür spendiere ich jedem Knopf eine Zählervariable, die repräsentativ die gedrückte Zeit wiederspiegelt. Ist ein Knopf nicht gedrückt, so ist die entsprechende Variable 0.

Jetzt wird innerhalb der Hauptschleife der Zustand des entsprechenden Portpins abgefragt. Ist er 0 (Taster schaltet gegen Masse!), so gilt der Knopf als gedrückt und der Knopfzähler wird inkrementiert. Eine Besonderheit ergibt sich noch beim OK-Button, der über zwei Pins angeschlossen ist. Die gesamte Logik, die bei jedem Durchlauf der Hauptschleife einmal aufgerufen werden muss, lautet dann:

Sehr einfach kann ich nun abfragen:

  • ob der Knopf erstmalig gedrückt wurde (z.B. dnBtn==1). Das wird nur einmal in der gesamten Hauptschleife der Fall sein, beim nächsten mal ist es schon 2.
  • ob der Knopf ein bisschen länger gedrückt wurde (dnBtn>10 && dnBtn<50)
  • ob er sehr lange gedrückt wurde (dnBtn==50)

Natürlich läßt sich die exakte Zeit nicht angeben, da viele Hauptschleifen-Zustände unterschiedlich lange benötigen. Es fällt im praktischen Betrieb aber nicht auf.

Mehrsprachigkeit

Ein schönes, wenn auch ein „Nur-weil-ich-kann“-Feature ist die durchgängige Mehrsprachigkeit des Systems. Jeder Text auf dem Display hat ein Pendant auf Englisch und kann in Echtzeit gewechselt werden. Zwar könnte man das Speicherlayout als einen großen String realisieren und sich immer von 0-Byte zu 0-Byte durchhangeln, da wir aber genug Platz im ROM haben, gefiel mir die Variante mit den mehrdimensionalen Arrays besser. Wie wir später noch sehen werden, hat das den Nachteil, dass die Dimensionen manuell übergeben werden müssen.

Ein mehrsprachiger String im Flash-ROM hat damit diesen Aufbau:

Die erste Dimension ist folglich die der Sprachen, die zweite Dimension, die bei variablem Inhalt als Maximum der Stringlängen +1 für das abschließende 0-Byte angegeben werden muss, speichert die Länge der Inhalte. PROGMEM weist avrgcc an, die Variable ausschließlich im Flash-ROM abzulegen, und nicht zusätzlich im RAM.

Speichert man die aktuell ausgewählte Sprache in einem globalen uint8_t:

so läßt sich sehr schön auf den Inhalt der Variablen zugreifen:

Der Compiler errechnet automatisch die Startadresse von MyString an der richtigen Sprachstelle anhand der Variable currentLanguage und der absoluten Startadresse von myString im Flash-ROM.

Auch das Behandeln von zweidimensionalen Strings für Menüs verläuft ähnlich:

Serielle Kommunikation

Die beiden Einheiten sollen zwecks synchroner Displayanzeige auch miteinander kommunizieren können. Unterschiedliche Events wie z.B. Tore beim Gegner müssen auf beiden LCDs dargestellt werden, chic wäre auch die Übertragung von Konfigurationseinstellungen.

Ursprünglicher Plan

Am Anfang hatte ich vorgesehen, den Hardware-UART beider Einheiten über Kreuz zu verbinden und einfach so Nachrichten auszutauschen. Die einzige Schwierigkeit bestand darin, dass so auch zwei Lichtschranken jeweils miteinander verbunden sind, und sich partout nicht asynchron unterbrechen ließen: War die eine unterbrochen, so wurde durch das Licht auf der anderen Seite die Leitung trotzdem noch ausreichend gegen Masse geschaltet, sodass beide MCUs keine Unterbrechung registrierten.

Die Kommunikation hingegen hatte einigermaßen funktioniert, Nachrichtentechniker wissen ja mit AWGN-Kanälen umzugehen, nicht wahr. Fehlerkorrektur und zusätzliche Redundanz wären hier die Stichwörter.

Eine separate Leitung („IRQ“) hätte beiden MCUs signalisiert, wann sie ihre Receiver einschalten sollen, und wann es regulärer Lichtschrankenbetrieb ist.

Verbesserung

Das ist aber dann nicht Made-in-Germany, deswegen galt es zu überlegen, wie das System verbessert werden kann:

  • Full-Duplex ist nicht erforderlich: Eine Einheit reagiert immer nur auf die Nachrichten, die Verarbeitungszeit dazwischen erlaubt einen Betrieb der Leitung auch im Halbduplex.
  • Aufwand der Fehlerkorrektur: Kann ich die Fehlerrate des Kanals drücken, so muß ich keinen Aufwand und keine Ressourcen mehr für Fehlererkenung und -Korrektur spendieren; die MCU hat Zeit für andere Dinge.

Auf den entscheidenden Punkt brachte es dann die Application Note 274 von Atmel, die da lautet „Single-wire Software UART“. Über eine Leitung scheint die Kommunikation sogar Interrupt-gesteuert möglich zu sein. Nach etwas Einlesen waren schnell zwei Einschränkungen gefunden:

  1. Es ist die Leitung auf beiden Seiten an einen vollwertigen externen (EINT[0-2]) Interrupt anzuschließen. Dieser startet den Empfang.
  2. Ein freier 8bit-Timer ist ebenso erforderlich.

Der Ablauf der Kommunikation gestaltet sich dann denkbar einfach:

  • Sender generiert Start-Bit und startet Timer
  • Empfänger feuert EINTn ISR und startet Timer plus x µ-Sekunden (76 Cycles), damit er die Mitte trifft zum Samplen
  • Sender legt nun in seiner Timer-ISR Bit für Bit auf die Leitung (EINT deaktiviert)
  • Empfänger tastet in seiner Timer-ISR (+76 Cycles!) die Leitung ab und empfängt so Bit für Bit.
  • Nach jeweils 1 Bit Start + 8 Bit Daten +  1 Bit Stop ist das Datum empfangen; es kann optional eine Art Software-ISR angesprungen werden.
  • Beide Einheiten aktivieren wieder ihren EINTn…

Änderungen im Header-File

Nachdem Atmel die Workbench zum Programmieren benutzt, mußte ich im Headerfile nur die Bezeichnungen der Register ändern auf die des ATmega644 und durch die Namen vom avrgcc ersetzen. Außerdem schreibe ich das fertig empfangene Byte direkt in den RX-Buffer, damit das Hauptprogramm dieses auswerten kann. Die relevanten Ausschnitte hier nachfolgend.

Hier Ausschnitte aus dem C-File

Zum Senden eines Strings wird uart_puts() mit dem entsprechenden String synchron aufgerufen. Als Besonderheit sei hier angemerkt, dass meine Nachrichten alle mit dem Semikolon (Zeichen 59) abgeschlossen werden. Möchtet ihr die Lib auch einsetzen, solltet ihr oben das zusätzliche SW_UART_Transmit(59) und das nachfolgende while() entfernen.

Nachrichten

Zu guter Letzt noch die Nachrichten, die übertragen werden können:

Hauptprogramm und State-Machine

Bleibt nur noch eines zu erledigen; nämlich das Hauptprogramm zu erläutern. Es besteht im Wesentlichen aus vier Teilen, die jetzt im einzelnen beschrieben werden.

EEPROM-Layout

Im EEPROM werden Konfiguration und High-Score-Liste gespeichert, wir belegen von den 4096 möglichen Bytes 98, und damit etwa 4% des verfügbaren Platzes. Er ist aufgeteilt in:

Globale Variablen

Auch bei den globalen Variablen sollte es wenig Verständnisschwierigkeiten geben:

Interrupt-Service-Routinen

Die Beschreibung der Messung wurde an voriger Stelle bereits vorgenommen, ich spare mir eine genauere Erklärung hier. Auch der Sourcecode ist ausführlich dokumentiert.

Hilfsfunktionen

Es gibt ein paar kleine Hilfsfunktionen, die das Leben erleichtern, aber speziell für das Kickerprojekt geschrieben wurden und deswegen nicht in den Bibliotheken Platz finden.

  • void handleLightrayInterrupted(unsigned char whichLightray): Schon erklärte Lichtschrankenbehandlung
  • void handleLightrayRestored(unsigned char whichLightray): Schon erklärte Lichtschrankenbehandlung
  • void glcd_drawMenuFrame(PGM_P headerText): Zeichnet den Menürahmen mit angegebener Überschrift
  • void glcd_renderMenu(PGM_P menuHeader, PGM_P menuItems, unsigned char menuItemCount, unsigned char dimension, unsigned char menustate, unsigned char old_menustate): Zeichnet angegebenes Menü mit Überschrift und animiert, wenn sich die letzten beiden Variablen unterscheiden.
  • void clearTimerArrays(): Löscht die timerValue*-Arrays
  • signed char strpos_P(char *haystack, PGM_P needle): wie strpos, nur mit needle im Flash-ROM
  • unsigned char isTop10Record(double *valueToTest): Testet, ob eine übergebene Geschwindigkeit für einen TOP10-Platz berechtigt ist.
  • void newGame(): Startet ein neues Spiel und setzt die Variablen zurück-

Initialisierung

Nicht viel zu schreiben hier. Nach ein paar lokalen Variablen für das Hauptprogramm folgt die Initialisierung der Ports, der Timer, des Software-UARTs und des LCDs.

Darauf kommt der Splashscreen, und das Laden der Konfiguration aus dem EEPROM. Weiterhin wird die Bildschirm-Statemachine auf den Hauptbildschirm initialisiert.

State-Machine

Generell ist es bei der Software so, dass sie nach der Initialisierung in einer großen Schleife, tatsächlich mit while (1) {…} programmiert, abläuft.

Leeren des Display-VRAMs

Damit alle Bildschirme auf einen leeren weißen Bildschirm zeichnen können, wird der VRAM am Anfang der Schleife geleert.

Verarbeiten der seriellen Schnittstelle

Nun wird die serielle Schnittstelle lesend bedient, es wird also anhand des asynchron im Interrupt-Betrieb befüllten rxBuffer’s eine auszuführende Aktion festgelegt, sofern der Buffer eine entsprechende Anweisung enthält. Die Aktion kann beispielsweise sein, in den Bildschirm mit der Tor-Animation zu wechseln.

Zum Senden steht jedem Zustand die Funktion uart_puts() zur Verfügung. Sie erwartet einen Pointer auf den RAM-Buffer mit dem zu sendenden Text; sofern der Text 0-Bytes enthält, muss zusätzlich txBufferIndex manuell auf die richtige Länge gesetzt werden. Dadurch wird vollständiges Übertragen gewährleistet.

Zustände

Es folgen alle Zustände und Bildschirme, die irgendwie auf dem LCD angezeigt werden können. Ein Zustand ist dabei immer gleich aufgebaut.

  • Zuerst wird das Layout gezeichnet, z.B. mit glcd_drawMenuFrame().
  • Dann werden Berechnungen auf lokalen oder globalen Variablen durchgeführt.
  • Die Ergebnisse werden mit sprint_f() in einen Buffer geschrieben und mit glcd_puts() ebenfalls ausgeben.
  • Abschließend erfolgt das Prüfen der Buttons, wobei mit <UP>/<DOWN> meist eine lokale Variable (z.B. die aktuelle Menüposition) geändert und mit <OK> eine Aktion ausgelöst wird.

Möchte man beispielsweise von einem Zustand in einen anderen springen, so wäre ein schneller C-Code dafür:

Dieses kleine Stück Code definiert zwei Zustände Z1 und Z2; merke, dass pro Hauptschleifendurchlauf nur einmal das Case-Statement ausgeführt wird. Ich kann also in Z1 den currentScreenState ändern, ohne dass dies sofort Auswirkungen hat. Erst mit dem nächsten Durchlauf würde der Z2 angesprungen werden.

Die Funktion des längeren Drückens ist auch demonstriert: in Z1 muß man lange auf den Button drücken für eine Änderung, in Z2 reicht ein kurzer Druck aus. Aber Vorsicht: Der Zähler muß auf 51 gesetzt werden, wodurch er (siehe oben) nicht mehr inkrementiert wird. Würde dies nicht geschehen, so würde man in Z2 den Knopf gedrückt halten können, dann 49 Durchläufe in Z1 verbleiben und automatisch wieder in Z2 springen. In den meisten Fällen wird dieses Verhalten nicht gewünscht sein, sondern wie bei einer IR-Fernbedienung sollte die Aktion erst bei erneutem Druck ausgelöst werden. Das wird so bewerkstelligt.

Definition und Auswertung von Menüs

Auch bei Menüs gibt’s Besonderheiten zu beachten. Eine Funktion rendert dieses vollständig und animiert es, falls der alte selektierte Eintrag sich vom aktuellen unterscheidet.

Leider verliert der C-Compiler beim Übergeben von mehrdimensionalen Arrays die Dimension dieser und übergibt nur noch den Pointer auf die Startadresse. Nachdem ich in drei Dimensionen arbeite, war mir der Aufwand mit Structs und dem sizeOf()-Operator zu groß, ich übergebe daher Größe und Länge der Strings manuell. Die Funktion berechnet sich den Zeiger auf den auszugebenden String des Menüs selbst.

Auch geben die beiden letzten Parameter an, welche Zustände -sprich Einträge- des Menüs dargestellt werden sollen.

Wird nun currentMenuState mit <UP> und <DOWN> geändert, so kann die Funktion das Menü völlig selbstständig neu rendern.

Möchte ich die Auswahl eines Menüs auswerten und daraufhin Bildschirm wechseln, so geschieht dies durch ein weiteres CASE-Statement innerhalb eines Bildschirms:

So wird zunächst das Menü gerendert und wenn erforderlich animiert und dann auf Knopfdrücke reagiert: Menüselektion wechseln und bei OK den neuen Screen anspringen im nächsten Durchlauf der Hauptschleife. Damit ist die Hauptschleifen-Statemachine schon erklärt. Für die einzelnen Zustände möge man wiederum den dokumentierten Quelltext zu Rate ziehen.

Aktualisieren des Displays

Bleibt nur noch, die gezeichneten Inhalte tatsächlich vom VRAM auf das LCD zu schreiben, das geschieht am Ende der Hauptschleife.

8 Kommentare zu “Pimp-My-Kicker a.k.a. Wham-O-Meter”

1.   Kommentar von doc
Erstellt am 26. August 2010 um 17:17 Uhr.

Sieht ziemlich cool aus. Kann’s kaum erwarten, mal daran zu spielen!

2.   Kommentar von Waldemar
Erstellt am 17. April 2013 um 22:15 Uhr.

Hi , ich habe mal dein Wham-O-Meter nachgebaut (Einseitig) nur für die Anzeige der Geschwindigkeiten! COOL das Teil. DANKE dir dafür 😉 Ich bin auch ein Kickerfanatiker und E-Techniker u. ich muss sagen das Teil funktioniert recht gut, obwohl Bälle die weiter unten an den Photoempfängern vorbeirauschen langsamer angezeigt werden als weiter oben (Streuung der IR-LED ist Schuld). Hab noch paar Komentare:

bzgl. deiner Ball-Durchmesser-Kalibrierungsfunktion:
-> die läuft zwar, macht aber nix !!!

-> hab die Kalibrierung per hand vorgenommmen. Einfach den
Balldurchmesser eingeben, funktioniert aufgrund der IR-Streuung
nicht korrekt! Ich ließ den Ball öfters durch die Lichtschranke von
ner Höhe h fallen und hab die Geschwindigkeit nach v=sqr(2gh) bestimmt
und verglichen mit der Anzeige. Der wahre Durchmesser ist bei meinem
Ball 35mm u. damit die Aneige stimmt muß ich bei mir D = 27mm eingeben.

bzgl. Die Top-Ten-Liste:
-> Die Top-Ten-Liste sollte vorher schon mit Werten gefüllt werden,
( vielleicht ab 25 Km/h ), da das ständige Eintippen der Rekorde
am Anfang doch schon ziemlich nervt 😉

Nun denn weiter so ! Gruss aus Berlin

3.   Kommentar von McSeven
Erstellt am 17. April 2013 um 22:31 Uhr.

Grüße, freut mich ja sehr, dann wärst Du schon der dritte, der’s erforlgreich nachgebaut hat. Wenn du hast, schick gerne Fotos, bau ich dann hier auf die Webseite.

Re 1) Ok, das kann sein. Ist wohl eher eine Spielerei, wobei es bei mir funktionierte…

Re 2) Ah richtig, es kommt wirklich auf die IR-Strahlen drauf an. Wir hatten dann noch Strohhalme drübergestülpt, um die Streuung zu verbessern. Du könntest auch mal schauen, ob die LEDs richtig auf die Transistoren ausgerichtet sind…

Re 3) Gute Idee, dazu wäre einfach im EEPROM-Initial-Wert (bei dem Array) statt 10x “ “ ein passender Wert einzutragen, z.B. 10x „CPU“ mit eben 25,00km/h. Das kannst sogar selbst machen und dann flashen.

„Nur einseitig???“ Spaß machts doch erst mit zwei LCDs =) Cheers

4.   Kommentar von Waldemar
Erstellt am 14. August 2014 um 19:19 Uhr.

Hallo Christoph,
ich habe mal wieder Zeit gefunden und habe mein Kicker um eine zweite Anzeige erweitert. Mein Bruder will jetzt auch einen Wham-O-Meter 😉 Deshalb hab ich hier wieder reingeschaut und mich gewundert, dass du so schnell geantwortet hast auf mein Kommentar von letztem mal 😉 Da bin ich etwas langsamer ;(
Das mit den Initialwerten im EEPROM hatte ich natürlich auch so gemacht. Auch deine Idee mit den Strohhalmen hatte ich in etwas massiverer Form siehe Bilder) LG Waldemar

5.   Kommentar von Ole
Erstellt am 08. Januar 2015 um 09:48 Uhr.

Hey, größten Respektfür das, was ihr geleistet habt. Ich bin total neidisch darauf und würde es gerne nachbauen. Leider bin ich ein absoluter Anänger und stehe mit der Fertigung der Platinen vor einem Problem. Könnt ihr mir da vielleicht eine Anleitung zur Fertigung geben, die für absolute dummies ist?
Liebe Grüße!

6.   Kommentar von McSeven
Erstellt am 08. Januar 2015 um 09:56 Uhr.

Grüße, Danke erst einmal. Also, dann würde ich Dir den PCB-Pool empfehlen, der macht super Platinen. Sich das selbst beizubringen für so ein großes Projekt ist vielleicht etwas viel. Ansonsten existieren im Internet dutzende Anleitungen, eine Suchmaschine Deiner Wahl hilft Dir unter dem Stichwort „Platinen ätzen“ weiter. Cheers, Christoph

7.   Kommentar von Ole
Erstellt am 08. Januar 2015 um 15:15 Uhr.

Ui, habe mir gerade dazu Videos angeguckt und sehe, dass das sehr schwierig werden wird… Eine weitere Sache: ich möchte dieses Projekt mit einem Arduino verwirklichen, der hat aber keine 24 Ports. wie habt ihr das Portproblem gelöst ?
LG

8.   Kommentar von McSeven
Erstellt am 08. Januar 2015 um 22:55 Uhr.

Grüße, hm, also wie ich soll ich das schonend verpacken… Eventuell ist so eine komplexe Sache für den Einstieg in die Mikrocontroller-Welt etwas viel. Mit dem Arduino-Ökosystem kenne ich mich nicht so gut aus, kann Dir also nicht garantieren, daß die Firmware, die aus den Sketches erstellt wird, auch wirklich schnell genug für das genaue Zeitmessen ist. Zu Deiner Frage: Der Mega644 hat die entsprechende Anzahl an GPIO-Pins mit Pin-Change-Interrupts, deswegen hatten wir damals kein Problem 🙂

Einen Kommentar hinterlassen