Hallo zusammen,
ich wollte mal fragen was erfahrungsgemäß die beste Variante ist in C
"asynchron" Daten auszutauschen, bspw. zwischen einer ISR und main?
In der ISR die Daten in eine Art Schattenregister schreiben und dieses
einmal pro Endlosschleifendurchlauf auslesen oder gleich per FIFO? Oder
lieber ständig die Interrupts ein-/ausschalten? Gibt es noch weitere
Methoden? Hängt es von der Richtung ab (main -> ISR bzw. ISR -> main)
was die beste Lösung ist?
Grüße,
Micha
Nehmen wir an, du willst ein Array aus einer ISR an die main übergeben,
dann könntest du zwei globale Bits erstellen: bReading, bWriting.
ISR:
while(bReading); // wait while reading from main is finished
bWriting = 1;
copyFromISR();
bWriting = 0;
main:
while(bWriting); // wait while writing from ISR is finished
bReading = 1;
copyToMain();
bReading = 0;
Da gibts kein allgemein gültiges Rezept. Ein UART Interrupt z.B. kann
ein empfangenes Datum speichern und evtl. noch ein Flag setzen oder
einen Pointer behandeln.
Eine ISR, die mechanisch ausgelöst wird und was tun soll (ich emuliere
hier z.B. gerade ein Cassettendeck) setzt nur ein Flag und der
(langsame) Rest wird von einer Unterroutine von main() gemacht, da läuft
parallel nämlich noch Digital Audio, das auf keinen Fall unterbrochen
werden darf.
Generell sollten ISR schnell fertig werden und vorhersehbares
Zeitverhalten haben. Däumchen drehen kann man in main() immer noch.
Die Methode mit Schattenregister (also faktisch double buffering) ist
gängig. Man kann das auch recht einfach zu einem Ringpuffer mit
FIFO-Semantik aufbohren. Das habe ich so schon häufiger genutzt.
Interrupts ausschalten würde ich nur im Notfall machen. Manche CPUs
haben Instruktionen für atomare komplexe Operationen, die sind ggf.
nützlich als Alternative dazu.
C Programmierer schrieb:> Nehmen wir an, du willst ein Array aus einer ISR an die main übergeben,> dann könntest du zwei globale Bits erstellen: bReading, bWriting.
Und dann hast du einen Spinlock im IRQ. Halte ich tendenziell für
gefährlich! Dann doch lieber Interrupts einfach deaktivieren, oder?
@Micha (Gast)
>ich wollte mal fragen was erfahrungsgemäß die beste Variante ist in C>"asynchron" Daten auszutauschen, bspw. zwischen einer ISR und main?
Siehe Interrupt.
>In der ISR die Daten in eine Art Schattenregister schreiben und dieses>einmal pro Endlosschleifendurchlauf auslesen oder gleich per FIFO?
Kommt auf die Anwendung an.
>Oder>lieber ständig die Interrupts ein-/ausschalten?
Warum sollte man das tun? Bestenfalls für atomare Zugriffe.
C Programmierer schrieb:> ISR:>> while(bReading); // wait while reading from main is finished
und wer soll bReading freigeben, während du in der ISR deine Kreise
ziehst?
C Programmierer schrieb:> // wait while reading from main is finished
Irgendwelche Waits in der main zu haben ist nicht gut.
Warum soll ich auf einen Subtask warten, wenn ich was besseres zu tun
habe.
Wenn in der main nix zu tun ist, dann legt man sich schlafen oder
übergibt an den Idle-Task. Beim AVR meistens sleep.
UART-I/O kann man immer asynchron machen und die main via signale
über den aktuellen Zustand informieren.
Ich mache das schon immer so, dass ich einen Sende- und einen
Empfangsringpuffer für den UART habe. Die ganze UART-Abwicklung macht
die
zugehörige ISR. Jeweils eine für TX, RX und evt. UART_ERROR_HANDLING.
Viele habe damit ein Problem, weil sie sich nicht "virtuelle,
asynchrone"
Abläufe vorstellen können.
Als Anregung kann ich nur jedem empfehlen sich mal mit einem "echten"
RTOS
zu beschäftigen und die darin verankerte "Quasiparallelität" anzueignen.
Noch einer schrieb:> Oder ob man da überhaupt eine ISR> braucht.
Was ein Quark! Tja, wenn man von Interrupts keine Ahnung hat, dann
kommen solche Empfehlungen heraus. -Gähn-
> Was ein Quark!
Recht nettes Beispiel ist das SD-Card Zeug aus der das alten PIC
Library. main() ruft regelmässig AsyncWriteTasks() auf. Das schaut nach,
ob die SPI Hardware inzwischen das Interrupt-Flag gesetzt hat. ISR wird
nicht aktiviert.
Falk Brunner schrieb:> Siehe Interrupt.
Darin sehe ich am ehesten den Abschnitt "UART mit Interrupts" als
relevant an und das Beispiel dort empfinde ich als relativ speziell.
Speziell deswegen, weil vorausgesetzt wird, dass man sein Protokoll
selbst definieren kann (\0 am Ende). Wenn dies aber nicht der Fall
ist...?
Der Mechanismus an sich ist aber natürlich anpass- und übertragbar.
Micha schrieb:> Speziell deswegen, weil vorausgesetzt wird, dass man sein Protokoll> selbst definieren kann (\0 am Ende).
Natürlich funktioniert das auch nicht wenn \0 ein gültiges Datum ist.
Hallo Micha,
mir scheint, Du fragst eher nach dem richtigen Locking als nach
Datenaustausch. Da gilt, wie schon geschrieben: Für jeden Zweck das
richtige Messer. Viel interessantes dazu findest Du in der Literatur zum
Linux Kernel, zum Beispiel hier http://lwn.net/Kernel/LDD3/ oder hier
http://tldp.org/LDP/tlk/tlk.html.
Gruß,
ah8