Ist es eine gute Idee, eine Anzeige (in diesem Fall eine 8x8-LED-Matrix) in einem Interrupt-Handler zu aktualisieren?

Nehmen wir an, ich habe einen Arduino, der mit einer 8x8 LED matrix.

74HC595In meinem Fall wird die LED-Matrix mit zwei Schieberegistern (eines für die Zeilen und eines für die Spalten) angesteuert .

Da es nur den Status von 8 LEDs gleichzeitig (eine Reihe) einstellen kann, ist die Idee, die Reihen schnell genug zu aktualisieren, damit das menschliche Auge das Blinken nicht bemerkt.

Dies geschieht normalerweise in der loop()Methode:

int led_status[8]; //contains LED status (which is on/off) for each row

void loop() {
   process_otherstuff(); //eg: key inputs,...
   display(); //shift led_status[] to shift register and latch result.   
}

Hier ist ein potenzielles Problem: Was ist, wenn die CPU einen zeitaufwändigen Prozess in der Schleifenmethode ausführen muss (zB: schwere Mathematik)? Die Methode display() wird mit einer gewissen Verzögerung aufgerufen, die sich bemerkbar machen könnte (LED beginnt zu blinken oder noch schlimmer: stoppt aktualisiert, scheint in einer Zeile hängen zu bleiben).

Eine mögliche Lösung, die mir einfällt, ist die Aktualisierung der LED-Matrix in einem Interrupt-Handler:

void setup()
{
  analogWrite(3, 100);
  attachInterrupt(1, refreshdisplay, RISING);
}

int row = 0;
void refreshdisplay()
{
   //refresh one single row
   refreshrow(row++);
   if(row > 7) row = 0;
}

Ist das eine gute Idee? Auf diese Weise wird die Anzeige so schnell wie nötig aktualisiert, egal wie viel Zeit in der Methode loop() verbracht wird. Ein potenzielles Problem, das ich sehe, ist, dass jetzt viel Zeit im Interrupt verbracht wird, was normalerweise eine schlechte Sache ist (der Interrupt-Handler sollte so kurz wie möglich sein).

Eine andere Lösung besteht darin, einen separaten LED-Controller-Treiber zu verwenden (z. B. über einen I2C-Bus mit Arduino verbunden), dies erfordert jedoch eine Änderung der Komponentenliste.

Scheuen Sie sich nicht, komplexe ISRs zu schreiben. Es ist ein weiteres dieser Dinge wie "keine globalen Strukturen verwenden!!" die nicht ganz so universell sind, wie man denkt. Wenn Ihre Hauptschleife nicht zeitkritisch ist, schadet es nicht, sie zu "unterbrechen". Warten in ISR ist keine so gute Idee. Verwenden Sie besser einen Timer, um die ISR nach der gewünschten Verzögerung erneut auszulösen.
Wenn Ihr Interrupt zeilenweise aktualisiert wird und Sie acht Zeilen haben, möchten Sie, dass das gesamte Array in mindestens 1/30 Sekunden aktualisiert wird, um die Persistenz der Vision aufrechtzuerhalten. Sie möchten also, dass ein On-Chip-Timer mindestens 240 Interrupts pro Sekunde auslöst. Hinzu kommt die Zeit, die der Interrupt-Handler benötigt, um den aktuellen Zustand des Prozessors zu speichern und wiederherzustellen. Dies scheint machbar und ein vernünftiger Ansatz zu sein. Vergessen Sie nicht, Ihren Array-Zeiger sowie Ihren Zeilenzähler zurückzusetzen.

Antworten (2)

Ja, das ist der richtige Ansatz – aktualisieren Sie die Anzeige in einem ISR.

Selbst geringfügige Timing-Änderungen können zu visuellen Artefakten führen, daher ist es notwendig, das Timing ziemlich eng zu halten, wenn Sie ein qualitativ hochwertiges Erscheinungsbild der Anzeige wünschen.

Verwenden Sie nach Möglichkeit Hardware (z. B. das SPI-Peripheriegerät), um in die Schieberegister zu schreiben, und verwenden Sie natürlich eine relativ hohe Taktrate. Wenn Sie dies tun, sollte die Gesamtzeit, die in der ISR verbracht wird, nur wenige Prozent der Prozessorbandbreite betragen, und da sie in kleinen „Blöcken“ verwendet wird, wird die Hintergrundverarbeitung fast so ablaufen, als ob Sie einen Prozessor hätten, der beispielsweise mit 12 MHz läuft statt 16 MHz.

Einfache und (relativ) zeitkritische Prozesse wie das Aktualisieren einer Anzeige sind genau das, wofür ISRs gut sind. Es ist auch eine gute Zeit (während Sie sich in einer periodischen ISR befinden), um beispielsweise Eingangsschalter auf Entprellung zu scannen.

Genau die richtige Antwort. Persönliche Erfahrung: Ich habe einmal mit diesen alphanumerischen 4-Zeichen-Displays von HP (im Wesentlichen eine 5x28-Matrix) mit einer 8051-basierten MCU gearbeitet. Ich stellte fest, dass sogar der mit dem Eintritt in die ISR verbundene Timing-Jitter ein visuelles Flackern erzeugte. Ich musste eine "präzise Timing"-Technik implementieren (Lesen und Ändern des Hardware-Timer-Werts, anstatt ihn einfach blind zu löschen), um etwas Akzeptables zu erhalten.

Normalerweise werden Interrupt-Handler tatsächlich für kurze Aktionen verwendet. Der Grund ist, dass das Hauptprogramm blockiert.

Wenn der Haupthandler jedoch keine zeitkritischen Aktionen ausführt, ist dies ein geringeres Problem.

Stellen Sie sicher, dass der Interrupt-Handler nicht länger als den nächsten Interrupt-Aufruf braucht. Dann erhalten Sie einen Unterbrechungsruf innerhalb eines Unterbrechungsrufs usw.

In Ihrem Fall dauert Ihre Aktualisierungsfunktion wahrscheinlich immer gleich lange. Sie können es messen (führen Sie einen Test mit 1000 Iterationen durch und teilen Sie die Zeit durch 1000, oder 1M ist, dass Sie es nicht messen können). Und Sie kennen die Bildwiederholfrequenz. Dann wissen Sie, wie viel Prozent (abzüglich etwas Overhead) für Ihr Hauptprogramm übrig bleiben.

Persönlich denke ich, dass das Schreiben von 64 GPIO-Pins, auch mit Schieberegistern, sehr schnell erledigt sein sollte, aber Testen ist besser als Annehmen.

Normalerweise würden Sie verschachtelte Interrupts deaktivieren und / oder sie sind sowieso standardmäßig deaktiviert. Es sei denn, Sie mögen Schmerzen.