Hallo
darf ich Code aus der Interruptroutine der Übersichtlichkeit halber auf
Funktionen aufteilen? Ich bastel mit PIC16/PIC18, dort muß ich ja in der
ISR auswerten, woher der Interrupt kommt und würde gerne den ISR-Code
der verschiedenen Quellen auf Funktionen aufteilen. Die paar
zusätzlichen Takte für die Funktionsaufrufe würden nicht stören.
Quasi:
Karsten schrieb:> Die paar> zusätzlichen Takte für die Funktionsaufrufe würden nicht stören.
das nicht, aber es werden dann viel mehr Register gesichert als vorher.
(zumindest ist es bei GCC und den AVRs so).
Wird dann beim PIC1x nicht anders sein. Was wär hier "best practice" um
den Code der ISR übersichtlicher zu gestalten, bzw. den ISR-Code auf die
entsprechenden Sources der Peripheriekomponenten aufzuteilen?
Karsten schrieb:> darf ich Code aus der Interruptroutine der Übersichtlichkeit halber auf> Funktionen aufteilen?
Mach doch mal eine Minimalanwendung, dann siehst du im Assemblerlisting,
was dich der Spaß kostet (Zeit, Speicherplatz). Wenn's nicht stört, kann
man das so machen.
Karsten schrieb:> Was wär hier "best practice"
Flag setzen und Code in der Hauptschleife abarbeiten.
Ralf G. schrieb:>> Was wär hier "best practice"> Flag setzen und Code in der Hauptschleife abarbeiten.
Wofür dann noch ISR? Dann kann ich ja auch zyklisch in der Hauptschleife
auf das Interruptflag pollen, vorausgesetzt die Hauptschleife ist kurz
genug
Bsp:
ADC_ISR: Wert auslesen, in Variable schreiben, nächsten Kanal
selektieren, Wandlung starten
I2C_ISR: Daten vom Master empfangen, in Array schreiben oder Daten aus
dem Array an den Master senden
UART_ISR: 1-Wire Kommunikation (TX Byte/Bit, RX Byte/Bit,
SearchFirst/Next)
In der Hauptschleife mache ich doch schon so langweilige Dinge wie PID,
Taster abarbeiten, Display beschreiben, ADC-Werte über Lookup in
Temperaturen/Volumenströme umrechnen, ...
Karsten schrieb:> Wofür dann noch ISR?
Die ISR ist beim Empfang normalerweise dafür da, die Daten abzuholen und
bestanfalls kurz vorzubereiten. Die Weiterverarbeitung findet dann im
Hauptprogramm statt.
Beim Senden werden vorbereitete Daten in der ISR rausgeschickt, ggf. mit
Double-Buffer-handling.
Beim Senden oder Empfangen von Daten koppelt man das üblicherweise mit
einem DMA.
"Best practise" ist mMn. ein System mit (RT)OS:
Die ISR überträgt die empfangenen Daten in den Empfangsbuffer (besser:
Peripheral -> DMA -> Empfangsbuffer).
ISR bzw. DMA Ready Interrupt setzt dann das Event für die Task, welche
die Daten verarbeitet, und schaltet ggf. den DMA Buffer um.
Ich bin der Meinung, dass sich ein RTOS auch auf kleinen Systemen lonht,
da das Hauptprogramm dadurch stark entwirrt wird; der Overhead ist
vernachlässigbar ggü. dem Gewinn. Bei guter Strukturierung kann der
Overhead eines RTOS sogar kleiner werden, als die "manuelle" Variante.
Vor allem wird alles übersichtlicher und deterministischer.
Arbeitet man auf einem Cortex-M, kann man sich im RTOS Viewer grafisch
anschauen, wie gross die System- bzw. Interruptlast ist.
Random .. schrieb:> Arbeitet man auf einem Cortex-M, kann man sich im RTOS Viewer grafisch> anschauen, wie gross die System- bzw. Interruptlast ist.
Ja genau, so ein RTOS Viewer ist sehr praktisch und gibt es z.B bei
Segger embOS und nennt sich embOSView. Mit embOS geht das nicht nur mit
einem Cortex-M sondern auch mit PIC16/PIC18.
Gibt's wohl nicht bei KeilRTX.
SCNR ;-)
Um auf die Eingangsfrage zurückzukommen:
Ja, kann man machen, ist schon wegen der Übersicht auch zu empfehlen,
und "Register-Rett-Orgien" (wie vielleicht beim AVR) gibt es nicht (der
PIC hat gar nicht so viele Register, die gerettet werden könnten)
Bei den Enhanced Midrange (PIC16F1xxx) rettet die Hardware sowieso schon
alle Prozessor-Register automatisch beim Interrupt-Eintritt und stellt
sie bei Rücksprung wieder her..
Karsten schrieb:> Wird dann beim PIC1x nicht anders sein.
Ich war erst geneigt, dem zu widersprechen, weil PICs ja nicht direkt
viele Register (im herkömmlichen Sinn) haben. Dafür haben sie aber diese
Unmengen an Pseudo-Registern (FSRx, TBLPTRx) im RAM die eine ISR auch
nicht überschreiben darf. Ohne statische Code-Analyse müßte der Compiler
alle diese Register sichern, was die Interrupt-Latenz in unerträgliche
Höhe klettern ließe. Ein PIC-Compiler muß also etwas ähnliches machen
wie AVR-gcc - sich die ISR "ansehen" und dann nur die Register sichern,
die in der ISR auch verändert werden.
Wenn man jedoch Funktionen aus einer ISR aufruft, dann kann der Compiler
i.d.R. in diese Funktionen nicht "hineinsehen". Dann muß er alle
Register sichern, die im ABI als caller saved spezifiziert sind.
> Was wär hier "best practice" um> den Code der ISR übersichtlicher zu gestalten, bzw. den ISR-Code auf die> entsprechenden Sources der Peripheriekomponenten aufzuteilen?
Verwende Funktionen. Wenn es dann immer noch schnell genug ist, bist du
fertig. Ansonsten mußt du optimieren. Aber wie immer gilt: optimiere
erst dann, wenn es notwendig wird.