Für mein Projekte habe ich mir einen Arduino Mega 2560 zugelegt.
Ich möchte mit dem Arduino mit meiner Digitalen Carrera Bahn kommunizieren.
Diese schickt zum einen Daten digital über die Schiene mit der ManchesterCodierung, Flanken alle 50 oder 100µs.
Zum Anderen will ich Daten über UART an die Bahn schicken.
Leider habe ich jetzt einige Probleme mit dem Empfangen der Daten, die über die Bahn geschickt werden. Ich habe eine Anpassungsschaltung gebaut, die den Pegel der Bahn auf 5V reduziert. Angeschlossen ist der Ausgang der Schaltung an INT0 des Arduinos. Ich messe die TimerTicks zwischen den Interrupts um den ManchesterCode zu dekodieren und verarbeite die Zeit in einer State-Maschine. Zum Debuggen habe ich ein USB-Oszilloskop.
Ich lasse zum einen einen Ausgang toggeln, wenn der Interrupt ausgelöst wird (ist auch synchron zur Bahn). Und zum anderen lasse ich einen Ausgang toggeln, wenn in der Loop Funktion der Interrupt gearbeitet wird. Leider passt die Signale nicht immer zusammen. Ich habe noch ein Bild vom Oszilloskop angehängt, siehe Anhang. Das blaue Signal ist die Referenz. Das rote Signal ist das Toggeln in der Loop-Funktion. Hat jemand eine Idee von es liegen könnte, dass in der Loop-Funktion nicht alle Interrupts bearbeitet werden?
Hallo Uwe,
Bei dem blauen Signal haben die langen Flanken eine Länge von 100micro sekunden.
Die kurzen 50micro Sekunden. Ich kann morgen nochmal ein Screenshot von einer Messungen machen
blue55:
Ich möchte mit dem Arduino mit meiner Digitalen Carrera Bahn kommunizieren.
Diese schickt zum einen Daten digital über die Schiene mit der ManchesterCodierung, Flanken alle 50 oder 100µs.
Das sind extrem schnelle Signale.
Mal zum Vergleich: Bei den Leichtathletik-Europameisterschaften wurden sportliche Leistungen auf hundertstel Sekunden genau erfaßt. Eine hundertestel Sekunde gleich 10 Millisekunden gleich 10000 Mikrosekunden. Eine Signaldauer von 50µs entspricht nur fünf tausendstel einer hundertstel Sekunde.
Ein mit 16 MHz getakteter Controller kann in 50µs nur 50*16 = 800 einzelne Clock-Takte ausführen.
blue55:
Leider habe ich jetzt einige Probleme mit dem Empfangen der Daten, die über die Bahn geschickt werden.
Du bist Dir offenbar nicht darüber im klaren, wie schnell 800 Clock-Takte auf einem AVR-Controller verbraten werden können und programmierst ein Programm, das sich in aller Gemütlichkeit alle Zeit der Welt nimmt.
Dadurch werden Deine Interrupts überlaufen und Dein Programm verarbeitet dann eben nicht jeden Impuls, sondern läßt ab und zu mal einen aus. Wenn Du nur etwas schneller bist als der vorgegebene Takt, ist alles in Ordnung. Und bist Du nur einen winzigen Bruchteil zu spät dran mit der Verarbeitung, summieren sich die fehlenden Zeiten auf bis der Takt Deine Verarbeitung überläuft.
Welche Probleme es beim Arbeiten mit schnellen Taktzeiten geben kann, hat Charlie Chaplin doch schon vor fast 100 Jahren vormacht:
Und Dein Programm ist wie Charlie Chaplin, der mit der hohen Taktfolge nicht klarkommt.
Das nur begrenzte Rechenzeit zwischen den Interrupts zur Verfügung steht, war mir schon klar
Jedoch hätte ich gedacht, dass die Zeit zwischen den Interrupts zum Toggeln der LED reicht.
Was in meinem Code verbrät denn die 800 Takte oder was ist besonders zeitauswendig?
Auf einem Atmega162 lief der Code ja auch schon mal und der war nur mit 8Mhz getaktet.
Liegt es evtl. an der Arduino IDE? Auf dem Atmega162 hatte ich die Pins direkt angesprochen.
Wird im Loop nur die Loop-Funktion aufgerufen oder macht die IDE noch was anderes in jedem Loop?
Eben ist mir noch was aufgefallen was nicht ganz schlüssig ist.
Auf der Main-Seite vom Mega 2560 steht das der INT0 an den ArduinoPin 2 angeschlossen.
Laut dem Schema ist aber INT4 an dem ArduinoPin 2 angeschlossen und INT0 an Pin 21?
genau das wird das Problem sein. Klingt alles sehr ähnlich wie beim Meßschieber auslesen. DigitalRead dauert dafür zu lange, Du mußt direkt an die Register.
Das auslesen von Deinem Protokoll sollte ähnlich vom Meßschieber sein. Ich warte in einer while Schleife auf den Anfang der Daten und lese dann hintereinander eine bestimmte Anzahl an Daten ein. Nur hast Du keine getrennte Clock- und Datenleitung wenn ich das richtig lese. Das Grundprinzip sollte jedoch schon helfen, denke ich. Funktioniert alles ohne Interrupt. Für Daten aller 7,5ms hat man für Spielereien zwischendurch kaum Zeit. Da mußte dann vielleicht doch einen Interrupt verwenden und darin notgedrungen die Daten einlesen. Wenn Du aber danach wieder Zeit für andere Dinge vertrödelst und der Interrupt wieder dazwischen funkt, kommste auch mit den anderen Dingen nicht mehr hinter, weil der der Interrupt laufend unterbricht.
blue55:
Was in meinem Code verbrät denn die 800 Takte oder was ist besonders zeitauswendig?
So auf den ersten Blick sehe ich drei Stellen, wo Du überflüssige Extrazeit verbrätst:
Du verwendest die vergleichsweise langsamen Arduino-Komfortbefehle "digitalRead" und "digitalWrite" anstelle schneller direkter Registerzugriffe auf PINx und PORTx Register zum Lesen und Setzen digitaler Ein-/Ausgänge.
Zur Zeitmessung aktivierst Du den Timer1, obwohl die Arduino-Software standardmäßig bereits eine Zeitmessung über Timer0 aktiviert, mit 4µs zeitlicher Auflösung über die Funktion "micros()".
Du verläßt die loop-Funktion.
Zu 3. Wenn die Funktionen in einer loop extrem zeitkritisch sind, kannst Du bei Arduino-Boards, die für die USB-Seriell Verbindung zum PC einen zweiten Chip draufhaben (FTDI, Atmega16U2) wie auch beim MEGA-Board die loop-Funktion problemlos mit einer Endlosschleife versehen, dass die Funktion gar nicht zum Ende kommt:
void loop() {
while (1)
{
// hier der Code
}
}
Bei loop-Funktionen, die extrem oft laufen sollen und extrem schnell/zeitkritisch sind, kannst Du die Drehzahl der loop damit etwas erhöhen.
Vielen dank für die Hilfe. Ich hab den Code jetzt darauf umgestellt die Ports direkt ansprechen.
Timer1 läuft jetzt auch durch und wird nicht gestoppt sondern nur zurückgesetzt.
Damit kann ich jetzt alle Flanken empfangen und die Daten verarbeiten.
Digitalread und write sind leider wirklich zu langsam für die Anwendung.