Hallo, ich habe ein Programm in C geschrieben für einen ATmega128. Alles funktioniert wunderbar. Aber ich habe für mein Verständnis trotzdem noch eine Frage zur Verwendung von "volatile" bei der Deklaration der Variablen: Mein Programm besteht aus der Mainloop und 2 Interrupts. Alle globalen Variablen, die ich nur in der Mainloop benutze, habe ich ganz normal deklariert. Bei allen Variablen, die ich in den Interrupts nutze, habe ich diese zusätzlich mit "volatile" deklariert. Jetzt meine Fragen: 1) Wenn ich eine Variable global deklariere, sie aber nur innerhalb einer einzigen Interrupt-Funktion verwende, muss ich diese dann auch mit "volatile" deklarieren ? 2) Wenn ich eine Variable global deklariere und diese in unterschiedlichen Interrupts verwende (nicht jedoch in der Mainloop!), muss ich diese dann auch mit "volatile" deklarieren? Wir gehen davon aus, dass eine ISR nicht durch eine andere ISR unterbrochen werden kann. Vielen Dank für Erklärungen!! Hannes
> 1) Wenn ich eine Variable global deklariere, sie aber nur innerhalb > einer einzigen Interrupt-Funktion verwende, muss ich diese dann auch mit > "volatile" deklarieren ? Nein, allerdings waere es sinnvoller sie dann auch nur in der Interruptfunktion zu deklarieren. Im Zweifel als static. > 2) Wenn ich eine Variable global deklariere und diese in Nein, allerdings solltest du nochmal ueber deinen Programierstil nachdenken. :) Olaf
Hannes schrieb: > 1) Wenn ich eine Variable global deklariere, sie aber nur innerhalb > einer einzigen Interrupt-Funktion verwende, muss ich diese dann auch mit > "volatile" deklarieren ? nein > 2) Wenn ich eine Variable global deklariere und diese in > unterschiedlichen Interrupts verwende (nicht jedoch in der Mainloop!), > muss ich diese dann auch mit "volatile" deklarieren? Wir gehen davon > aus, dass eine ISR nicht durch eine andere ISR unterbrochen werden kann. nein > Vielen Dank für Erklärungen!! nur Variablen die in er Main verwendet werden und in einer ISR geändert werden müssen volatile sein. Sonst fängt der Compiler an die main so zu optimieren, weil er nicht "sieht" das die Variabel sich ändert.
Hannes schrieb: > 1) Wenn ich eine Variable global deklariere, sie aber nur innerhalb > einer einzigen Interrupt-Funktion verwende, muss ich diese dann auch mit > "volatile" deklarieren ? Nein. Volatile sagt dem Compiler, dass sich die Variable im Programmverlauf "von aussen" ändern kann. Volatile müssen nur die Variablen sein, die sich "unvohergesehen" ändern können. > 2) Wenn ich eine Variable global deklariere und diese in > unterschiedlichen Interrupts verwende (nicht jedoch in der Mainloop!), > muss ich diese dann auch mit "volatile" deklarieren? Wir gehen davon > aus, dass eine ISR nicht durch eine andere ISR unterbrochen werden kann. Nein, weil sie sich nicht "von aussen unvorhergesehen" ändern kann. Somit darf der Compiler diese Variable auch in der ISR lokal optimieren.
:
Bearbeitet durch Moderator
1) Nein 2) Ja (der Compiler weiß ja nicht, wann ein Zugriff in einer ISR war)
Sicherheitshalber noch der Hinweis, dass "volatile" kein Allheilmittel ist. Bei Variablen mit mehr als 1 Byte Grösse, die in main() und im Interrupt verwendet werden, empfiehlt es sich, den Zugriff mit "atomic block" zu schützen.
Teo D. schrieb: > 2) Ja (der Compiler weiß ja nicht, wann ein Zugriff in einer ISR war) nein, muss er auch nicht, weil beim Start einer isr immer die Daten aus Ram laden muss. (und am ende wieder wegschreiben).
Peter II schrieb: > Teo D. schrieb: >> 2) Ja (der Compiler weiß ja nicht, wann ein Zugriff in einer ISR war) > nein, muss er auch nicht, weil beim Start einer isr immer die Daten aus > Ram laden muss. (und am ende wieder wegschreiben) Jo, wenn man genauer drüber nachdenkt....
Peter II schrieb: > nur Variablen die in er Main verwendet werden und in einer ISR geändert > werden müssen volatile sein. Immer diese falsche Einschränkung, die man immer wieder ließt. Es spielt keine Rolle, wo geändert und wo nur gelesen wird. Es kommt zwar deutlich seltener vor, dass eine Variable in Main geändert und in der ISR nur gelesen wird, aber auch dann muss sie volatile sein.
Hallo zusammen, vielen Dank für die vielen klaren Antworten. So in etwa habe ich mir das gedacht, war mir aber wie gesagt nicht sicher. @ Olaf: Zu meinem Programmierstil: mein Programm ist recht übersichtlich, etwa 3 Seiten Code. Ich habe absichtlich alle Variablen global deklariert, weil ich dann nach dem Compilieren genau sehe, wie mein RAM-Verbrauch ist. Das ist bequem so finde ich. Wie könnte ich das besser/einfacher machen? nochmal @ Olaf: Was macht der Compiler genau, wenn ich eine Variable im Interrupt als 'static' deklariere? Wo ist der Unterschied zu einer ganz normalen Deklaration innerhalb der ISR? vielen Dank ! Hannes
Hannes schrieb: > Was macht der Compiler genau, wenn ich eine Variable im > Interrupt als 'static' deklariere? Wo ist der Unterschied zu einer ganz > normalen Deklaration innerhalb der ISR? Eine normale Variablendeklaration innerhalb einer Funktion (also auch innerhalb einer ISR) wird automatisch genannt. Die Variable landet auf dem Stack, d.h. sie verliert beim Beenden der Funktion ihren Wert und ist beim Aufruf der Funktion auch nicht initialisiert (d.h. es kann beliebiger Unfug drinstehen). Wird eine Variable innerhalb einer Funktion als static deklariert, entspricht das einer globalen Variablen, d.h. sie landet nicht auf dem Stack, sondern ist dem Linker bekannt (und Du erhältst Deine Informationen über den RAM-Verbrauch). Die Variable behält auch über mehrere Funktionsaufrufe hinweg ihren Wert, verhält sich also genau so wie eine globale Variable. Die Variable ist aber außerhalb der Funktion nicht "sichtbar", d.h. auf sie kann nicht von anderen Orten über ihren Namen zugegriffen werden. Zu guter letzt: Eine Variable, die außerhalb einer Funktion als static deklariert wird, ist nur innerhalb der zugehörigen C-Übersetzungseinheit sichtbar, d.h. aus anderen C-Übersetzungseinheiten kann nicht auf sie über ihren Namen zugegriffen werden. Davon abgesehen verhalten sich auch diese Variablen wie globale Variablen.
> Das ist bequem so finde ich. Du sollst aber kein bequemes leben haben. Du sollst gut lesbaren und wiederverwendbaren Source schreiben den du auch noch in einem Jahr verstehst. Und da ist es eine Hilfe wenn Variablen nur dort bekannt sind wo sie auch gebraucht werden. > Interrupt als 'static' deklariere? Der Variableninhalt bleibt dann auch nach der Funktion erhalten und ist beim naechsten IRQ wieder verfuegbar. Technisch bedeutet es das die Variable dann nicht auf dem Stack liegt. Olaf
Super, vielen Dank für die sehr guten Erklärungen. Ich habe meinen Code entsprechend umgebaut und es funktioniert nach wie vor alles wunderbar. Das werde ich zukünftig so beibehalten !
Hallo Hannes, Georg hat mit seinem Kommentar recht: Georg G. schrieb: > Sicherheitshalber noch der Hinweis, dass "volatile" kein Allheilmittel > ist. Bei Variablen mit mehr als 1 Byte Grösse, die in main() und im > Interrupt verwendet werden, empfiehlt es sich, den Zugriff mit "atomic > block" zu schützen. auch wenn jemand meinte, ihn negativ zu bewerten zu müssen. Aus der AVRlibc Doku:
1 | #include <inttypes.h> |
2 | #include <avr/interrupt.h> |
3 | #include <avr/io.h> |
4 | #include <util/atomic.h> |
5 | volatile uint16_t ctr; |
6 | ISR(TIMER1_OVF_vect) |
7 | {
|
8 | ctr--; |
9 | }
|
10 | ...
|
11 | int
|
12 | main(void) |
13 | {
|
14 | ...
|
15 | ctr = 0x200; |
16 | start_timer(); |
17 | sei(); |
18 | uint16_t ctr_copy; |
19 | do
|
20 | {
|
21 | ATOMIC_BLOCK(ATOMIC_FORCEON) |
22 | {
|
23 | ctr_copy = ctr; |
24 | }
|
25 | }
|
26 | while (ctr_copy != 0); |
27 | ...
|
28 | }
|
ATOMIC_BLOCK(..) sperrt Int's für die Dauer des Blocks. ATOMIC_FORCEON ist für den Fall daß Int's hinterher freigegeben sein sollen, 1 Register und 2..3 Befehle kürzer als ATOMIC_RESTORESTATE, was der SREG-Zustand sicher und am Ende wieder den alten SREG-Zustand herstellt.
:
Bearbeitet durch User
Hallo Carl, vielen Dank für das prima Beispiel. Das habe ich so noch nicht verwendet, aber ich habe auch 16 und 32 Bit Variablen in meiner Mainloop, das betrifft mich also auch. Ich werde das jetzt gleich ergänzen... Vielen Dank für die tolle Unterstützung! Bei jedem Projekt mit neuen Fragen lernt man hier im Forum viel dazu! Hannes
Carl D. schrieb: > ATOMIC_BLOCK(..) sperrt Int's für die Dauer des Blocks. Ähemm.. nun, die AVR-Leute machen das eben so, allerdings halte ich es für einen doch eher unüberlegten Programmierstil, mit dem Aus- und Einschalten der Interrupts um sich zu werfen. Auf größeren Systemen macht man sowas nicht und dort, wo es irgend ein OS gibt, erst recht nicht. Also sollte man sich besser überlegen, wie man bei nichtatomaren Zugriffen die beteiligten Instanzen miteinander synchronisiert. Oftmals ist solcher Zugriff bei genauerer Betrachtung auch garnicht notwendig. Es ist ja nicht wie im Kindergarten, wo ein Kind sagt "ich male A auf die Tafel" und zugleich Kind 2 ein B drauf malen will und ebenso zu gleicher Zeit Kind 3 ein C malen will. Stattdessen hat man es eigentlich immer mit Kommunikation zu tun, also wo eine Instanz eier anderen etwas sagen will. Und das kann man IMMER ohne ..BLOCK schreiben, indem man einen Handshake einrichtet: Beide Instanzen halten einen atomaren Kenner vor (z.B. ein Byte). Die ansagende Instanz ändert irgend etwas, z.B. den Inhalt eines struct's und dann ändert sie ihren Kenner (z.B. kenner++ ). Die zuhörende Instanz vergleicht ihren Kenner mit dem anderen und greift nur auf den struct zu, wenn beide ungleich sind. Anschließend macht sie ihren Kenner dem Kenner des Ansagenden gleich und fertig ist die Laube - ganz OHNE konkurrierende Zugriffe. W.S.
W.S. schrieb: > Carl D. schrieb: >> ATOMIC_BLOCK(..) sperrt Int's für die Dauer des Blocks. > > Ähemm.. nun, die AVR-Leute machen das eben so, allerdings halte ich es > für einen doch eher unüberlegten Programmierstil, mit dem Aus- und > Einschalten der Interrupts um sich zu werfen. Auf größeren Systemen > macht man sowas nicht und dort, wo es irgend ein OS gibt, erst recht > nicht. Ja, auf Mainframes hab ich das auch nie gemacht, aber es geht hier um einen AVR, der zwischen jedem Lesen eines Bytes einer Mehrbyte-Variable aus dem RAM durch einen Interrupt unterbrochen werden kann. Und dann hat man ein "geht meistens"-Programm. Also besser verstehen, was passieren kann und gleich richtig machen. BTW, ein AVR-Leut bin ich dann, wenn ich es mit dem AVR zu tun habe. Ich kann aber auch ARM-Leut, x86/x64-Leut, /370-Leut, ... Und in jedem Fall muß man die Besonderheiten der HW beachten. Aber was red ich, das weißt du sicher selbst. Und Lock-Free macht eigentlich nur Sinn, wenn man mehrere Cores hat, die auch wirklich parallel zugreifen. AVR!
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.