Superschnelle parallele Abfrage von Datenpins? (Dartboard)

Hallo,

Ich bin neu hier. Mit dem Arduino UNO beschäftige ich mich schon eine Weile auf etwas fortgeschrittener Anfängerebene. Bisher hauptsächlich so Kleinigkeiten wie irgendwelche Signale oder Messwerte abfragen und weiter leiten und/oder Schaltfunktionen ausführen. Also nichts Besonderes. Bin kein Programmieranfänger. Allerdings fehlt mit bezüglich C/C++ und Arduino die Erfahrung in tieferer Ebene. Mein Schwerpunkt liegt in VB/VBA/Assembler.

Meine Aufgabe: Mein bestehendes funktionierendes Dartboard bezüglich der Matrix abfragen und Ausgabe der Trefferpunkte des jeweiligen Wurfes in deutscher Sprache. Ohne Auswertungen der Treffer und Summen und resultierender anderer Situation. Das soll weiter das Dartboard selbst machen. Nur der Zahlenwert des Treffers und ob "double", "triple" oder "Bullseye".

Wie man mit dem Arduino so eine Matrix komplett selbst abfragen kann habe ich schon in Dartselbstbauanleitungen gefunden und wollte Teile davon für mein Projekt verwenden. Aber das ist grausig zu langsam. :frowning:

Ich habe also weiter gestöbert und bin dann auf Portmanipulation gekommen. Das sei x-fach schneller als "digitalread". Und interruptgesteuert wäre es noch besser. Allerdings bräuchte ich 8 interruptfähige Eingangsbits.

Falls ich das "Pollen" mit einem "UNO" nicht hinbekomme kaufe ich eben einen "DUE". Der hat ja genug interruptfähige Bits.

Nun mal konkret: Das Board hat eine Matxis aus 8x8 Leitungen. Auf den ersten 8 Leitungen (Spalten der Matrix) "zirkuliert" von der Boardelektronik gesteuert ein aktives Bit (Low). Also nacheinander immer alle 8 Leitungen kurz low. Umlauffrequenz ca. 3 bis 5 Kilohertz.

Sobald ein Dart trifft, wird eine der 8 Matrixreihenleitungen (9 bis 16) kurz low. Je nachdem wie lange der Dart den Kontakt auslöst, wiederholt sich der Low-Impuls zyklisch. Aber das spielt keine Rolle, weil beim ersten Erkennen schon eine Auswertung beginnen soll und dann eine winzige Wartepause eingelegt wird.

Nach dem Start des Programm, Einbinden von Bibliotheken (welche?) und Durchlauf des Setups folgt die Mainloop.

In dieser Mainloop möchte ich zyklisch alle 8 Bits der Reihen-Leitungen 9 bis 16 gleichzeitig lesen. Solange das gelesene Byte Binär = 11111111 ist passiert nichts und die Loop rennt weiter im Kreis.

Wenn das gelesene Byte <> 11111111 ist, wird es der Variablen Y zugewiesen und umgehend die ersten 8 Bits der Spalten-Leitungen 1 bis 8 dazu in eine Variable (X) gelesen werden.

Damit habe ich also Spalte und Reihe der Dartmatrix, die getroffen wurde. Damit rufe ich dann die Tonausgabe auf. Z.B. call maketon(x,y). Hier werden die binären Matrixwerte in Spalten und Reihennummern umgewandelt aus einem entsprechend aufgebauten Dateiverzeichnis die passenden Textdateien abgespielt.

Dann wieder die Loop von vorne.

Wie gesagt, mich interessiert hauptsächlich wie ich es in einer Loop schaffe 8 Digitalports gleichzeitig in eine Byte-Variable zu bekommen.

So ein Matrixbit liegt nur ca. 10µS lang an! Dann folgt das nächste. Und das zweite Byte der Auswertung, also die Spaltennummer muss innerhalb dieser Pulsdauer kommen sonst ist der Spaltenimpuls schon bei der nächsten Spalte. Also so schnell wie möglich.

Für ein paar Ideen und vielleicht ein paar Codeschnipsel zu der schnellsten parallelen Abfrage wäre ich dankbar. Gerne stelle ich dann mein Bastelergebnis, wenn es klappt, auch hier zur Verfügung.

Allerdings bräuchte ich 8 interruptfähige Eingangsbits.

Hat der UNO doch....
Sogar viel mehr.

Suchtipp: PCINT

Tipp:
Datenblatt des ATMega328P lesen!

Wie gesagt, mich interessiert hauptsächlich wie ich es in einer Loop schaffe 8 Digitalports gleichzeitig in eine Byte-Variable zu bekommen.

byte dData = PIND;

Tipp:
Datenblatt des ATMega328P lesen!

Am schnellsten geht es natürlich, einen 8-Bit Port direkt einzulesen. Allerdings hat der UNO keinen kompletten 8-Bit Port frei.

Eine Alternative wäre evtl. einen 74HC148 Encoder einzusetzen. Den schließt Du an den Matrix-Eingang an. Sobald einer der Eingänge LOW wird, gibt es ein Sammelsignal ( das Du per Interrupt auswerten kannst - da brauchst Du dann auch nur einen ), und an 3 Ausgangspins wird die Nummer des 'LOW' Bits ausgegeben. Die Information zur Bitnummer und den Matrix-Ausgang ( insgesamt 11 Bits ) kannst Du dann auf 2 Ports verteilen und entsprechend schnell einlesen.

10µs sind aber schon sehr sportlich - auch mit Interrupt. Du musst ja bedenken, dass die Interrupts auch mal kurz gesperrt sein können. Evtl. wäre es da noch sinnvoll zwischen Encoder / Matrixausgang und Arduino-Input ein Latch einzusetzen. Mit ein klein bisschen externer HW wird das Ganze zeitlich auf jeden Fall wesentlich entspannter.

Spontan fällt mir MCP23S17 ein, da Du Spalten und Zeilen gleichzeitig lesen könntest und "The Interrupt Capture register captures port values at the time of the interrupt, thereby saving the condition that caused the interrupt." Adafruit bietet eine Programmbibliothek an.

Alle Pins des ATmega328 sind Interruptfähig. Nur daß dieser Interrupt nicht so benutzerfreundlich Auskunft gibt wie der an den pins 2 und 3. Es wird angezeigt daß ein Interrupt auf einem Pin ausgelöst wurde und muß dann auslesen welches Pin die Quelle war.

Andererseits ist die Reihenfolge der angesteuerten Spalten ja bekannt. Sie folgt einer fixen Reihenfolge. Also könntest Du auch nur die erste Spalte auf 2 oder 3 legen und die anderen 7 Spalten auf andere Pins, die nur algemeine Interrupts können. Aber die Reihenfolge ist ja bekannt und so muß ja nicht mal die Interruptquelle abgefragt werden.

Grüße Uwe

10 µs sind zwar nicht sehr langsam, aber alle 4 Datenports eines atmega328 - basierten Arduino auslesen dauert keine Microsekunde. Daraus 2x8 bit zusammensammeln nochmal so lang.
Der Vorteil eines Controllers ist, dass die Datenports keine externe Peripherie, sondern integraler Teil des Controllers sind (und mit einem einzigen Prozessortakt gelesen/geschrieben werden).

Zu PCINT hat combie dir schon einen Tip gegeben.
Mir scheint, hier könnte tatsächlich eine ISR hilfreich sein, um die 16 Datenbit in zwei Byte zu sammeln, und "Ereignis" (d.h. die 8 Datenbit sind != 0xFF) signalisieren.

loop kann dann beliebig langsam schauen, ob "Ereignis" passiert ist, die 2 Byte übernehmen und das "Ereignis", evtl. erst nach einer Entprellzeit, quittieren (Datenbytes für die ISR wieder freigeben).

Kommt es vor, dass bei einem Treffer mehr als eine der 8x8 Kombinationen entsteht ?

agmue:
Spontan fällt mir MCP23S17 ein, da Du Spalten und Zeilen gleichzeitig lesen könntest

Aufgrund der integrierten Latch-Funktion ist das natürlich eine perfekte Lösung, die das Zeitverhalten deutlich unkritischer macht. Die SPI-Schnittstelle erlaubt auch ein sehr schnelles Auslesen.

@TO: Erkennt man einen Treffer eigentlich nur im Moment das Auftreffen des Pfeils, oder erkennt man das solange der Pfeil in der Scheibe steckt? Wäre wichtig bei der Betrachtung, wenn mal ein Scan aus Zeitgründen nicht erkannt wird.

combie:
Hat der UNO doch....
Sogar viel mehr.

Suchtipp: PCINT

Tipp:
Datenblatt des ATMega328P lesen!

byte dData = PIND;

Tipp:
Datenblatt des ATMega328P lesen!

Danke - ich hatte irgendwo gestern gelesen, dass es nur 2 Interrupt-Pins gibt. Ok - mit PCINT geht wohl mehr. Muss ich mir mal Beispiele suchen.
Datenblätter sind schön - aber nicht so meine Welt, wenn ich nicht genau weiß, was ich suchen muss. Praxisbeispiele helfen mir besser.
Danke dir

lupus1952:
Muss ich mir mal Beispiele suchen.

Eines, was auch ich genommen habe.
https://arduino-projekte.webnode.at/registerprogrammierung/pinchangeinterrupt/

PS: Um das DaBla kommst nicht rum. Da solltest querlesen.

Datenblätter sind schön - aber nicht so meine Welt, wenn ich nicht genau weiß, was ich suchen muss. Praxisbeispiele helfen mir besser.

Das glaube ich dir nicht.
Scheint mir eher so eine Art Durchhalteparole zu sein.

Ob du es glaubst, oder auch nicht, Datenblätter kann man auch von vorn bis hinten durchlesen.
JEDER muss das, wenn er nicht dumm im Nebel stochern will.

Bespiele führen oft in die Irre!
Sie zeigen nur WAS jemand getan hat, nicht WARUM das so getan wurde.

Das WARUM wird dann meist/gerne mit Fantasie und Irrtümern aufgefüllt.
Und diese Irrationalitäten stehen einem dann jahrelang im Wege rum.

MicroBahner:
Am schnellsten geht es natürlich, einen 8-Bit Port direkt einzulesen. Allerdings hat der UNO keinen kompletten 8-Bit Port frei.

Macht nichts - kann das auch zerstückeln

Eine Alternative wäre evtl. einen 74HC148 Encoder einzusetzen. Den schließt Du an den Matrix-Eingang an.

Ok - sinnvoller ist es m.E. den Matrixausgang zu nehmen. Die Eingangsleitungen werden permanent durchgetaktet. Das Programm müsste also dauernd sinnlos für die IRQs arbeiten. Wenn die Matixausgänge genommen werden gäbe es immer nur einen Interrupt, wenn ein Dart einschlägt.

Sobald einer der Eingänge LOW wird, gibt es ein Sammelsignal ( das Du per Interrupt auswerten kannst - da brauchst Du dann auch nur einen ), und an 3 Ausgangspins wird die Nummer des 'LOW' Bits ausgegeben. Die Information zur Bitnummer und den Matrix-Ausgang ( insgesamt 11 Bits ) kannst Du dann auf 2 Ports verteilen und entsprechend schnell einlesen.

Wie gesagt - umgekehrt halte ich es für besser. Aber deine Idee ist super. Jetzt brauche ich nur noch ein Beispiel mit Schaltplan. Habe zwar schon einiges gefunden. Aber das waren alles zu große Sachen um sie auf Anhieb zu verstehen oder es war für ganz andere Anwendungen wie LCD-Anstuerungen u.s.w.

Ich würde 2 solcher Encoder nehmen. Einen für die Matrixausgänge und dann einen Interrupt auslösen und das Muster auslesen. Dann mit dem anderen ohne Interrupt das ja gleichzeitig anliegende zugehörige Eingangsmuster der Matrix mit auslesen. Dann habe ich bei entsprechender Verdrahtung der Encoder in einem Arduinobyte "blitzschnell" die fertige Matrixadresse mit 2 x 3 Bits in einem Byte.

10µs sind aber schon sehr sportlich - auch mit Interrupt.

Es gibt Anleitungen für das komplette Verarbeiten so einer Dartmatrix nur mit dem Arduino als Ersatz für eine Dartelektronik. Dort scannt der Arduino die Matrix per digitalwrite an die Pins mit maximaler Geschwindigkeit, die er kann und liest entsprechend nach jedem Takt alle 8 Ausgänge mit digitalread in einer Schleife aus. Angeblich geht das gut.

Sowas funktioniert nur nicht, wenn eine Dartelektronik selbst scannt. Da ist die Abfrage mit digitalread an beiden Ports viel zu langsam. Bis da ein Signalwechsel vom Matrixausgang erkannt und dekodiert wurde, ist die Matix schon weiter und eine Abfrage der Eingänge würde irgend was liefern nur nicht das Bit was richtig wäre.

Du musst ja bedenken, dass die Interrupts auch mal kurz gesperrt sein können. Evtl. wäre es da noch sinnvoll zwischen Encoder / Matrixausgang und Arduino-Input ein Latch einzusetzen. Mit ein klein bisschen externer HW wird das Ganze zeitlich auf jeden Fall wesentlich entspannter.

Die Interrupts werden höchstens während der ISR gesperrt. Während der Zeit ist ja aber ein Darteinschlag bereits erkannt und es kommt so schnell nichts mehr. Preller machen auch nichts. Sobald eine negative Flanke an einem Matrixsausgang erkannt wurde, ist Pause bis zum nächsten Dart. Niemand wirft so schnell hintereinander, dass es da Timingprobleme geben könnte.

Ich werde es jetzt mal mit reiner Software versuchen. Also PCINT u.s.w. Da brauche ich noch ein paar Beispielsketche um mir passende Tipps und Lösungen zu klauen

Und wenn das nicht klappt, kommt deine Hardwarelösung. Was ich aber vermeiden möchte. Die Zeit der feinen Lötarbeiten auf Lichraster ist bei mir wegen meiner Augen eigentlich vorbei. Ich vermeide es wenn möglich.

Danke dir für die gute Idee mit Encoder

agmue:
Spontan fällt mir MCP23S17 ein, da Du Spalten und Zeilen gleichzeitig lesen könntest und "The Interrupt Capture register captures port values at the time of the interrupt, thereby saving the condition that caused the interrupt." Adafruit bietet eine Programmbibliothek an.

Auch ein interessanter Ansatz. Aber doch viel Verdrahtungsaufwand. Ich werde es mal mit PCINT versuchen
Danke

uwefed:
Alle Pins des ATmega328 sind Interruptfähig. Nur daß dieser Interrupt nicht so benutzerfreundlich Auskunft gibt wie der an den pins 2 und 3. Es wird angezeigt daß ein Interrupt auf einem Pin ausgelöst wurde und muß dann auslesen welches Pin die Quelle war.

Das war mir neu und wurde am Anfang des Threads bereits kurz erwähnt

Andererseits ist die Reihenfolge der angesteuerten Spalten ja bekannt. Sie folgt einer fixen Reihenfolge. Also könntest Du auch nur die erste Spalte auf 2 oder 3 legen und die anderen 7 Spalten auf andere Pins, die nur algemeine Interrupts können. Aber die Reihenfolge ist ja bekannt und so muß ja nicht mal die Interruptquelle abgefragt werden.

Grüße Uwe

Das bringt eigentlich nichts, wenn ich nur einen Pin separat behandle. Die Reihenfolge der Matrixspalten ist bekannt. Die Spalten werden "im Kreis" permanent nacheinander praktisch nahtlos angesteuert.

Ich möchte aber nicht mit den Eingängen triggern, weil das ja pro Umlauf ein Programmteil auslösen würde.

Getriggert wird mit einem von 8 Ausgänge beim Eintreffen eines Darts. Also wenn sich an den Ausgängen was ändert. Erst dann wird es interessant zu wissen, welches Eingangsmuster dazu anliegt.

Danke

So wie ich verstanden habe bleibt der Kontakt zwischen Spalten und Reihen im getroffenen Segment kurz bis länger bestehen. Im schlimmsten Fall für eine Abtastzyklus; im besten Fall für mehrere.

Kann man ausschließen daß ein Kontakt bestehenbleibt solange der Dart in der Scheibe steckt?

Bei der angegebenen Taktfrequenz (Umlauffrequenz ca. 3 bis 5 Kilohertz.) von ca 25µS pro Spalte und nächste Kontrolle nach ca 175 µS ergeben sich die notwendige Geschwindigkeit der Reaktion des Programms.

Du hast also alle 25 µS eine Messung zu machen.

Grüße Uwe

michael_x:
10 µs sind zwar nicht sehr langsam, aber alle 4 Datenports eines atmega328 - basierten Arduino auslesen dauert keine Microsekunde. Daraus 2x8 bit zusammensammeln nochmal so lang.
Der Vorteil eines Controllers ist, dass die Datenports keine externe Peripherie, sondern integraler Teil des Controllers sind (und mit einem einzigen Prozessortakt gelesen/geschrieben werden).

Zu PCINT hat combie dir schon einen Tip gegeben.
Mir scheint, hier könnte tatsächlich eine ISR hilfreich sein, um die 16 Datenbit in zwei Byte zu sammeln, und "Ereignis" (d.h. die 8 Datenbit sind != 0xFF) signalisieren.

loop kann dann beliebig langsam schauen, ob "Ereignis" passiert ist, die 2 Byte übernehmen und das "Ereignis", evtl. erst nach einer Entprellzeit, quittieren (Datenbytes für die ISR wieder freigeben).

Ich werde es zuerst mit PCINT versuchen. Entprellen spielt keine Rolle. Der erste erkannte Puls löst den weiteren Programmschritt (Tonausgabe) aus. Da kann dann prellen was will.

Kommt es vor, dass bei einem Treffer mehr als eine der 8x8 Kombinationen entsteht ?

Bei 2 "Leitungen" mit je 8 Pins gibt es maximal 64 Kombinationen. Tatsächlich sind es aber nur 62 Felder auf dem Dartboard. Also werden nicht alle Kombinationen erzeugt.

Danke

MicroBahner:
Aufgrund der integrierten Latch-Funktion ist das natürlich eine perfekte Lösung, die das Zeitverhalten deutlich unkritischer macht. Die SPI-Schnittstelle erlaubt auch ein sehr schnelles Auslesen.

@TO: Erkennt man einen Treffer eigentlich nur im Moment das Auftreffen des Pfeils, oder erkennt man das solange der Pfeil in der Scheibe steckt? Wäre wichtig bei der Betrachtung, wenn mal ein Scan aus Zeitgründen nicht erkannt wird.

Der Kontakt wird nur beim richtigen Einschlag des Darts kurz ausgelöst. Sonst könnten weitere Treffer im selben Feld ja nicht erkannt werden. Es kann vorkommen, dass ein schlecht geworfener und "schief" aufschlagender Dart keinen Kontakt auslöst. Ist aber eine Frage der Boardqualität. Ein nicht registrierter Dart (Scan) ist kein Weltuntergang. Es soll ja nur eine akustische Aktion ausgelöst werden. Die Zählung und Auswertung lauft ja durch die Boardelektronik unabhängig

my_xy_projekt:
Eines, was auch ich genommen habe.
Pin Change Interrupt :: Meine Arduino-Projekte

PS: Um das DaBla kommst nicht rum. Da solltest querlesen.

Danke für den Link - die Seite habe ich inzwischen schon in meinen Favoriten gespeichert.
Dazu eine für euch vielleicht dumme Frage:
//Setzen des PCIE2-Bit im Pin Change Interrupt Control Register (PCICR)
PCICR |= (1 << PCIE2);
Was macht diese Anweisung mit den << ?
Wie schon gesagt, ich komme aus der Basic-Ecke und C ist mir ziemlich fremd. Ich verstehe das ganze Beispielscript nur am Rand. Deswegen habe ich das erst mal Beiseite gelegt.

Was macht diese Anweisung mit den << ?

Arithmetic operators


Wie schon gesagt, ich komme aus der Basic-Ecke und C ist mir ziemlich fremd.

C++, nicht C

Tipp: Es gibt immer noch Bücher.
Und ja, es ist schon von Vorteil, wenn man die Sprache kann, welche man da benutzen möchte.

lupus1952:
Der Kontakt wird nur beim richtigen Einschlag des Darts kurz ausgelöst.

Dann ist der von agmue vorgeschlagene Baustein doch die perfekte Lösung für dich. Und das Timing wird wesentlich entspannter.

Hallo,

Mein Schwerpunkt liegt in VB/VBA/Assembler.

Dann schreib es in Assembler wenn dir C/C++ nicht liegt. Nur eine Frage musste mir beantworten. Assemblerprogrammierer sind doch auf Manuals angewiesen, woher kommt dann die scheu? Anders gesagt müßte dir Manual lesen im Blut liegen. Wenn du das ehrlich beantwortest bekommste eine Chance.