Wie führe ich eine synchrone Ausgabe auf dem Arduino durch?

Ich habe ein Gerät, das ich mit dem Arduino steuern möchte.

Es funktioniert so.

  1. Arduino setzt und hält die "Bereitschaftsleitung" auf 5 V hoch.
  2. Das Gerät sieht die Bereitschaftsleitung hoch und sendet 16 Taktimpulse für zwei zu empfangende Nachrichtenbytes.
  3. Bei jedem Taktimpuls setzt der Arduino den Datenausgang auf das, was das Bit sein soll (0 oder 1)
  4. Wenn die zwei Bytes gesendet wurden, wird die 'Bereitschaftsleitung' wieder auf 0 V niedrig gesetzt.

Das Problem, das ich habe, ist, den Arduino mit den Taktimpulsen zu synchronisieren. Derzeit habe ich eine if-Anweisung, die die Datenleitung setzt, wenn sie die Taktleitung hoch sieht, aber das scheint nicht zu funktionieren. Ich denke, ich sollte stattdessen nach der steigenden Flanke suchen.

Wie kann ich diese beiden Geräte richtig synchronisieren?

Klingt nach SPI.
Leider kein SPI. Es ist eine proprietäre Controller-Schnittstelle für einen alten Synthesizer
Trotzdem nehme ich an, dass das Konzept sehr ähnlich ist, wenn nicht dasselbe.

Antworten (3)

Fügen Sie Ihrem Algorithmus diese Schritte hinzu: 0. Bereiten Sie das erste Datenbit vor ... 3. Verwenden Sie einen externen Interrupt bei steigender Flanke, um das vorbereitete Bit auszugeben, und legen Sie das nächste Bit in Ihre Bithaltervariable. Ausgegebene Bits zählen. * Schritt 3 wird automatisch 16 Mal wiederholt (durch externes Gerät ausgelöst). 4. Wenn der Bitzähler 16 erreicht, Ext-Interrupt deaktivieren und Bereitschaftsleitung zurücksetzen.

Hinweis: Auf atmega168/328 kann jeder Pin als externe Interruptquelle bei eingehenden Flankenübergängen mit recht einfacher Technik verwendet werden.

Ich hoffe, Ihr externes Gerät treibt die Taktimpulse nicht zu schnell. Arduino ist bei Reaktionsgeschwindigkeiten von mehr als 1 MHz nicht sehr gut. Ich würde sagen, Sie sollten erwarten, dass dieser Ansatz bis zu einem Takt von 100 kHz zuverlässig funktioniert. Bei höheren Geschwindigkeiten sollte entweder Hardware-SPI verwendet werden, um Ihre Daten auszutakten, oder eine enge Assembler-Codierung. In jedem Fall sind mehrere MHz das Maximum, das Sie hier mit nacktem AVR erreichen können.

Das Intervall zwischen den Impulsen beträgt ~ 120 us, also 1.000.000 / 120 = 8333, und die Geschwindigkeit beträgt 8,3 kHz?
Exakt. Das wird vollkommen in Ordnung sein. Sie müssen auch vorsichtig sein, wenn Sie möchten, dass während Ihrer Datenübertragung andere Interrupts zugelassen werden.

Das SPI hat zwei Modi: Arduino Stock Library unterstützt nativ nur Master. Ihr Szenario klingt genau wie SLAVE, da das andere Ende die Uhr antreibt.

vollständiges Beispiel

Sie können Ihr Ready mit dem SS (als Eingang) verbinden und den SPI mit dem richtigen Phasen-/Polaritätsmodus einrichten. Stellen Sie dann den SPCR als Slave ein. Aktivieren Sie Ihr Ready und warten Sie dann, bis die 8-Bit-Übertragung abgeschlossen ist, entweder durch Interrupt oder Abfrage. Lassen Sie dann die Bereitschaft los.

ATmega328 Datenblatt beachten Abschnitt 18.2 und 18.3.2

Schönes und klares Beispiel. Obwohl für diesen Fall Software SPI ganz in Ordnung ist und es erlaubt, alle Arduino-Pins zu verwenden, nicht nur die, die an hw SPI befestigt sind.

Das Problem hier ist alles über das Timing, also holen wir alle unsere Oszilloskope raus! Grundsätzlich (und etwas zu stark vereinfacht) können die meisten Standard-C-Flusssteuerungsanweisungen keine Timing-Garantien auf einem Mikrocontroller geben. Hardwarebasierte Interrupts garantieren, dass der Code innerhalb einer festen Anzahl von Taktzyklen mit der Ausführung beginnt.

Der Arduino (genauer gesagt der Arduino Uno mit einem ATmega328) hat viele Möglichkeiten, Hardware-Interrupts auszulösen, die manchmal als "Interrupt-Vektoren" bezeichnet werden (Seite 57 des ATmega-Datenblatts). Welche wollen wir? Wir möchten den Code unmittelbar nach dem Übergang des Cock-Pins ausführen, also suchen wir nach einem Pin-Change-Interrupt oder einem externen Interrupt. Abschnitt 12 im Datenblatt geht viel zu sehr ins Detail, aber das Wichtigste ist, dass jeder Port mit 8 Pins einen Interrupt-Vektor teilt! ( arduino.cc/en/Hacking/PinMapping168 ) Dies macht es schwierig, besonders wenn Sie neu bei Mikrocontrollern sind. Die Arduino-Community hat für genau eine solche Situation eine großartige Bibliothek namens PinChangeInt erstellt . (Haftungsausschluss: Ich habe zu PinChangeInt beigetragen)

Das eingebauteDie Interrupt-Creator-Funktion von Arduino (attachInterrupt(interrupt, ISR, mode)) und der Interrupt-Creator von PinChangeInt (PCintPort::attachInterrupt(PIN1, &ISRfunc, EDGE)) verwenden sehr ähnliche Argumente. Die Pin- und Flanken-/Modus-Argumente sind selbsterklärend - Der Pin, an dem Sie den Interrupt wünschen, und die Flanke, die Sie interessiert (höchstwahrscheinlich steigend, aber überprüfen Sie das Zeitdiagramm für Ihre Uhr). Der von Ihnen angegebene Funktionsname wird als Interrupt Service Routine (ISR) angehängt. Immer wenn die Hardware diesen Interrupt sieht, beginnt die ISR mit der Ausführung. Diese Funktion (ISRfunct() oben) akzeptiert keine Argumente und gibt keine Werte zurück (Typ VOID). Sie kann wie jede Funktion auf globale und andere Variablen im Gültigkeitsbereich zugreifen. Um die von Ihnen beschriebene Logik zu implementieren, benötigen Sie eine globale Variable für Ihre Daten und eine für den "Übertragungsindex" oder das letzte gesendete Bit.

Weitere Hintergrundinformationen zu Interrupts auf dem Arduino finden Sie unter den folgenden Links:

ATmega168-Subsystemdiagramme

uchobby.com/index.php/2007/11/24/arduino-interrupts/

gonium.net/md/2006/12/20/handling-external-interrupts-with-arduino/

Um dies zu tun, müsste ich jedoch in der Lage sein, dem Interrupt mitzuteilen, welcher Zustand ausgegeben werden soll (0 oder 1), und den Zähler zu erhöhen, damit er weiß, an welcher Stelle des Bytes er sich befindet. Die Arduino-Interrupt-Funktion kann keine Argumente annehmen.
Ich habe meine Lösung aktualisiert, um einen kurzen Überblick über ISRs auf dem Arduino aufzunehmen. Im Wesentlichen benötigen Sie eine globale oder bereichsweite Variable, um Daten von einer ISR an Ihre Hauptschleife zu übermitteln.