Also ich habe jetzt mit AVR Studio 5 ein altes Projekt versucht zu kompilieren und irgendwie ist der Wurm drinn. Bei 4.XX konnte man z.B. ohne Problem Code generieren, wenn man z.B. mit der UART Schnittstelle gearbeitet hat, es gab lediglich ne Warnung wenn die Datentypen anders waren als vorgesehen. In Version 5 bricht der sofort ab, wenn z.B. eine Variable uint8_t sein soll, aber volatile uint8_t ist. Wie kann man dem Compiler das austreiben? Gibts da n Kommando/Befehl? Warum ist Atmel so eine ungeheure Bastelbude was das angeht? Knut
:
Verschoben durch Moderator
Knut schrieb: > Also ich habe jetzt mit AVR Studio 5 ein altes Projekt versucht zu > kompilieren und irgendwie ist der Wurm drinn. ??? > Bei 4.XX konnte man z.B. ohne Problem Code generieren, wenn man z.B. mit Ich glaube nicht dass Du wirklich generieren meinst, eher wohl schreiben? > der UART Schnittstelle gearbeitet hat, es gab lediglich ne Warnung wenn > die Datentypen anders waren als vorgesehen. In Version 5 bricht der > sofort ab, wenn z.B. eine Variable uint8_t sein soll, aber *volatile* > uint8_t ist. Ist doch gut, das ist potentiell ein Bug? > Wie kann man dem Compiler das austreiben? Gibts da n Kommando/Befehl? > Warum ist Atmel so eine ungeheure Bastelbude was das angeht? Ist es nicht. Das ist der benutzte GCC und vermutlich die von der IDE per Default benutzten Optionen. Wenn Du das wirklich abstellen willst: Projektoptionen im AVR Studio und die Doku zu den GCC-Optionen ansehen. Hinweis: Du willst die Warnungen/Fehler nicht wirklich einfach deaktivieren. Das sind fast(!) alles potentielle Bugs, man kommt wesentlich besser weg wenn man die Ursachen verstehen lernt und den Code fixt. Gerade bei volatile...
Knut schrieb: > In Version 5 bricht der sofort ab, wenn z.B. eine Variable uint8_t sein > soll, aber volatile uint8_t ist. Bist du dir da sicher? Poste mal den code dazu.
Knut schrieb: > Warum ist Atmel so eine ungeheure Bastelbude was das angeht? Weil du deinem Compiler offenbar Bastelbuden-Code vorlegst. ;-) Nicht alles, was compiliert, muss auch notwendigerweise korrekter Code sein. Man kann lediglich sagen, dass ein Compiler aus korrektem Code auch ein korrektes Programm bauen muss, aber er ist nicht verpflichtet, jede Unkorrektheit wirklich festzustellen und mit einem Fehler oder einer Warnung zu honorieren. Da der GCC stetig immer besser wird, Unkorrektheiten zu erkennen, moniert er nun halt Dinge an, die in deinem Code zwar bereits früher unkorrekt waren, was er aber in einer älteren Version noch nicht erkannt hat. Die Firma Atmel hat damit weiter nichts zu tun, als dass sie einen recht guten Compiler zusammen mit ihrer IDE ausliefert.
Hmm... seit der neuen Toolchain meckert nun auch 4.19. Ist vlt. auch gar nicht schelcht. Aber wenn ich eine Variable volatile mache, weil sie in einer ISR benutzt wird, aber als "normal" vorgesehen ist, wie kann ich das Problem umgehen, dass er meckert? Knut
Knut schrieb: > wie kann ich das Problem umgehen, dass er meckert? Du darfst erstmal "volatile" verstehen. Nur, weil die Variable in einer ISR benutzt wird, muss sie nicht volatile sein. Wenn der Code trotz der Warnung funktioniert, ist es ein gutes Zeichen, dass das volatile gar nicht nötig war.
Man muß Variablen nicht volatile machen. Man kann sie mit eine Macro im Main-Kontext nach volatile casten:
1 | // force access of interrupt variables
|
2 | #define IVAR(x) (*(volatile typeof(x)*)&(x))
|
Peter
Peter Dannegger schrieb: > Man kann sie mit eine Macro im Main-Kontext nach volatile casten Bei 8bit-Werten klappt das Makro noch, aber bei größeren Werten ist es eine recht unangenehm zu debuggende Fehlerquelle, sollte man als Warnung dazuschreiben. Im allgemeinen wär's besser, vorher "volatile" zu verstehen, und sich zu überlegen, ob man atomaren Zugriff oder eine memory barrier braucht, und ob man dies durch ein schreibfaules einfaches "volatile" simulieren kann.
Vielleicht sollte er mal ein Minimal-Beispiel posten, das seiner Meinung nach compilieren sollte, aber den Fehler wirft.
Εrnst B✶ schrieb: > Bei 8bit-Werten klappt das Makro noch, aber bei größeren Werten ist es > eine recht unangenehm zu debuggende Fehlerquelle, sollte man als Warnung > dazuschreiben. Was genau meinst du damit?
Stefan Ernst schrieb: > Was genau meinst du damit? Beispiel:
1 | volatile uint16_t counter; |
2 | |
3 | ISR: |
4 | counter++; |
5 | |
6 | |
7 | Main: |
8 | uint16_t x=counter; // oder x=IVAR(counter), wenn counter nicht volatile |
9 | print_uint16(x); |
Denkbare Ausgabe: ... 0x00FF, 0x0000, 0x0101, ... oder ... 0x00FF, 0x01FF, 0x0101, ... Einfach weil während dem umkopieren von counter in x ein Interrupt auftreten kann (erstes Byte kopiert, ISR verändert beide, zweites Byte kopiert) Lösung: interrupts zwischendurch abschalten. #include <util/atomic.h> hat dafür passende Makros.
Εrnst B✶ schrieb: > Einfach weil während dem umkopieren von counter in x ein Interrupt > auftreten kann (erstes Byte kopiert, ISR verändert beide, zweites Byte > kopiert) Und was bitte hat das jetzt mit volatile und dem Makro zu tun?
Stefan Ernst schrieb: > Und was bitte hat das jetzt mit volatile und dem Makro zu tun? Nichts. Außer das Programmieranfängern hier im Zusammenhang mit ISRs immer sofort "volatile","volatile","volatile" an den Kopf geworfen wird, als wäre es die Allheillösung. Ist es nicht. Es ist Anfängern nur leichter zu erklären, und macht im einfachen Fall (Flag in ISR setzen, in main auswerten) ja auch das richtige.
Εrnst B✶ schrieb: > Nichts. Ausser das Programmieranfängern hier im Zusammenhang mit ISRs > immer sofort "volatile","volatile","volatile" an den Kopf geworfen wird, > als wäre es die Allheillösung. Ist es nicht. Es ist Anfängern nur > leichter zu erklären. Und warum folgt daraus jetzt, dass das Makro bei Variablen größer als 8 Bit nicht funktioniert? Einziger Zweck des Makros ist es, den Zugriff auf eine Variable volatile zu machen, ohne dass die Variable selbst global volatile sein muss. Niemand hat behauptet, dass der Zugriff damit auch automatisch atomar wäre, oder dass das Thema "atomarer Zugriff" bei Verwendung des Makros nicht beachtet zu werden braucht. "Dein Auto ist kaputt!" "Ja? Wieso, was ist denn damit?" "Es kann nicht fliegen."
Εrnst B✶ schrieb: > Es ist Anfängern nur > leichter zu erklären, Das ist erstens falsch, und zweitens auch... Es gibt beim Zusammenspiel von ISR's mit dem Restprogramm zwei Probleme zu lösen. Das eine löst volatile, das andere atomic. Die Probleme (und auch die Lösungen) haben nichts miteinander zu tun, und sind einzeln oder auch kombiniert anzuwenden. Oliver
Stefan Ernst schrieb: > "Dein Auto ist kaputt!" > "Ja? Wieso, was ist denn damit?" > "Es kann nicht fliegen." Typisches "Beratungsgespräch" hier im Forum: "Ich hab da eine ISR ...." "mach Volatile" "Ich hab da eine ISR und volatile" "mach Volatile" "hab ich doch schon" "erm.... mach noch mehr Volatile, Alle variablen" "Häh?" "und schalt die Optimierung aus!" "Huh? Aber ich will doch nur meinen 16Bit-Zähler aus der ISR verwenden..." Stefan Ernst schrieb: > Einziger Zweck des Makros ist es, den Zugriff auf eine Variable volatile > zu machen, ohne dass die Variable selbst global volatile sein muss. Dafür ist es ja gut und toll. Aber man sollte darauf hinweisen, dass es eben bei 16Bit-Variablen nicht für atomaren Zugriff sorgt, und es deswegen nicht alleine zur Lösung aller Interrupt-Sorgen führt. Was im Gegensatz dazu die ATOMIC_BLOCK-Makros wesentlich besser erreichen, denn da wird beim "cli"/"sei" per Memory-Barrier ebenfalls ein neu-laden der Variable aus dem Ram erzwungen, und das volatile somit in fast allen Fällen überflüssig.
wie weiter oben beschrieben MUSS eine Varaible, die in einem anderen Kontext (aka ISR) verwendet wird, volatile sein. Der Compiler denkt dass im obigen Beispiel in der main nie schreibend auf counter zugegriffen wird. Und folglich wird er den Zugriff bei Optimierung auch einsparen - er "kennt" ja den Wert. volatile erzwingt das das erneute Lesen aus der globalen Variable. Egal, ob der Interrupt ein- oder ausgeschaltet ist. probier doch mal aus: uint16_t counter; ISR: counter++; main: counter = 0; while(counter > 100) { counter = 0; } Ohne Optimierung steht der Code noch im erzeugten Object-File. Aber mit Optimierung (-Os) wird der Code der while Schleife "brutal" gelöscht - weil die Bedingung sonst nie erfüllt worden wäre. Adib.
Atomic und Volatile sind 2 völlig verschiedenen Dinge. Ihre einzige Gemeinsamkeit ist, daß beide in Zusammenhang mit Interrupts auftreten können. Meistens macht das Atomic auch implizit ein Volatile. Aber allein es kann Nachteile bezüglich der Laufzeit haben. Es macht schon einen Unterschied, ob eine Interruptsperre 6 Zyklen dauert oder über 1000 Zyklen:
1 | #include <util/atomic.h> |
2 | |
3 | #define IVAR(x) (*(volatile typeof(x)*)&(x))
|
4 | |
5 | uint16_t foo; |
6 | |
7 | void test( float fval ) |
8 | {
|
9 | uint16_t val; |
10 | |
11 | val = fval / 123.4; |
12 | ATOMIC_BLOCK(ATOMIC_FORCEON){ |
13 | foo = val; |
14 | }
|
15 | }
|
16 | |
17 | void test1( float fval ) |
18 | {
|
19 | uint16_t val; |
20 | |
21 | val = fval / 123.4; |
22 | ATOMIC_BLOCK(ATOMIC_FORCEON){ |
23 | IVAR(foo) = val; |
24 | }
|
25 | }
|
Peter
Oliver schrieb: > Es gibt beim Zusammenspiel von ISR's mit dem Restprogramm zwei Probleme > zu lösen. Das eine löst volatile, das andere atomic. Die Probleme (und > auch die Lösungen) haben nichts miteinander zu tun, und sind einzeln > oder auch kombiniert anzuwenden. Für einen ambitionierter Anfänger, der sich hier auf den Seiten bewegt, ist es nicht einfach, diese beiden Probleme auseinander zu halten. Wahrscheinlich wurde schon zu viel darüber geschrieben. Wo findet man die anfängertauglichen Merksätze "volatile muss man verwenden, wenn...", "Interruptsperren muss man wegen nicht atomarer Zugriffe sperren, wenn..."? Ich habe mir das wie folgt zusammengereimt. Bin mir aber nicht sicher und bitte um Verbesserung: ------------------------------------------------------------------------ --- Globale Variablen, die innerhalb einer ISR und auch vom übrigen Programm verwendet werden, müssen als volatile gekennzeichnet werden. Bei Zugriff und Veränderung von Variablen und Registern, die - sowohl in einer ISR als auch im übrigen Programm verwendet werden - und mehrere MC-Takte brauchen, müssen Interrupts gesperrt werden. Wenn der MC keine verschachtelten Interrupts zulässt, sind diese Interruptsperren nur im übrigen Programm und nicht in den ISRn notwendig.
Reiner Franke schrieb: > Wenn der MC keine verschachtelten > Interrupts zulässt, sind diese Interruptsperren nur im übrigen Programm > und nicht in den ISRn notwendig. Es dürfte die absolute Ausnahme sein, daß sich Interrupts gegenseitig Daten austauschen. Daher braucht auch bei Verschachtelung der Interrupt (in der Regel) kein volatile oder atomic. Peter
Danke Peter für's durchschauen der vorgeschlagenen "Merksätze". Ich gehe jetzt mal davon aus, dass bis auf den einen Einwand alles gepasst hat.
Peter Dannegger schrieb: > Es macht schon einen Unterschied, ob eine Interruptsperre 6 Zyklen > dauert oder über 1000 Zyklen: Aber nicht mit zweierlei Maß messen, gell?
1 | #include <util/atomic.h> |
2 | |
3 | #define IVAR(x) (*(volatile typeof(x)*)&(x))
|
4 | |
5 | uint16_t foo; |
6 | uint16_t volatile vfoo; |
7 | |
8 | void test( float fval ) |
9 | {
|
10 | uint16_t val; |
11 | |
12 | val = fval / 123.4; |
13 | ATOMIC_BLOCK(ATOMIC_FORCEON){ |
14 | vfoo = val; |
15 | }
|
16 | }
|
17 | |
18 | void test1( float fval ) |
19 | {
|
20 | uint16_t val; |
21 | |
22 | val = fval / 123.4; |
23 | ATOMIC_BLOCK(ATOMIC_FORCEON){ |
24 | IVAR(foo) = val; |
25 | }
|
26 | }
|
...und nochmal vergleichen ;-) Zumindest mit 4.3, 4.5, 4.6 und 4.7 sehe ich identische Ergebnisse.
Johann L. schrieb: > Aber nicht mit zweierlei Maß messen, gell? Es ging darum, daß Atomic auch automatisch Volatile ersetzt. Und mein Beispiel zeigt, das tut es nicht. Da Du nun gleich die Variable volatile machst, ist logisch, daß dann der Code zum volatile-Cast identisch sein muß. Ich benutze lieber den Cast. Dann kann der Interrupt optimieren und im Main sehe ich sofort, daß es sich um eine Interruptvariable handelt. Peter
Εrnst B✶ schrieb: > Stefan Ernst schrieb: >> Einziger Zweck des Makros ist es, den Zugriff auf eine Variable volatile >> zu machen, ohne dass die Variable selbst global volatile sein muss. > > Dafür ist es ja gut und toll. Aber man sollte darauf hinweisen, dass es > eben bei 16Bit-Variablen nicht für atomaren Zugriff sorgt, und es > deswegen nicht alleine zur Lösung aller Interrupt-Sorgen führt. Achtung: Es sorgt bei 8-Bit Variablen auch nicht (unbedingt) für atomaren Zugriff!. Es ist eine völlig andere Baustelle. Nämlich die, dass der Variablenzugriff nicht wegoptimiert wird. > Was im Gegensatz dazu die ATOMIC_BLOCK-Makros wesentlich besser > erreichen, denn da wird beim "cli"/"sei" per Memory-Barrier ebenfalls > ein neu-laden der Variable aus dem Ram erzwungen, und das volatile somit > in fast allen Fällen überflüssig. Das halte ich für eine SEHR gewagte These. Der ATOMIC_BLOCK sorgt mit Sicherheit nicht für das Neu-Laden einer Variable innerhalb dieses Blockes! Du darfst die beiden Probleme nicht miteinander vertauschen. Das sind (wie schon mehrfach erwähnt) zwei verschieden Probleme und manchmal ist es nötig, dass beides angewendet wird. Um Atomizität herzustellen und gleichzeitig das Wegoptimieren eines Variablenzugriffes zu verhindern.
Peter Dannegger schrieb: > Johann L. schrieb: >> Aber nicht mit zweierlei Maß messen, gell? > > Es ging darum, daß Atomic auch automatisch Volatile ersetzt. Nein, atomic ersetzt nicht volatile und auch nicht umgekehrt. > Und mein Beispiel zeigt, das tut es nicht. > > Da Du nun gleich die Variable volatile machst, ist logisch, daß dann der > Code zum volatile-Cast identisch sein muß. > [...] und im Main sehe ich sofort, daß es > sich um eine Interruptvariable handelt. Wenn es darum geht, den eigenen Code zu verstehen, sind manchmal auch Kommentare dienlich ;-)
Hallo, das geht aber am eigentlichen Thema vorbei. Das Problem waren ja Fehler beim Compilieren von aus Studio 4 in Studio 5 übernommenen Code. Was mir fehlt ist eine Aussage wie der Code von 4 nach 5 gekommen ist. Ich habe da andere Erfahrungen. Ich musste AVR Studio 5 einsetzen, weil Studio 4 und WINAVR nicht unter Win7 zum laufen zu kriegen waren. Ich habe alle Projekte (>100) und 90% davon mit UART-Funktion in Studio 5 über die Import-Funktion nach Studio 5 konvertiert. Alle Projekte ließen sich auf Anhieb kompilieren und liefen auch in der Schaltung so wie sie sollten.
tobi schrieb: > Hallo, > > das geht aber am eigentlichen Thema vorbei. Das Problem waren ja Fehler > beim Compilieren von aus Studio 4 in Studio 5 übernommenen Code. Jein. Das 'Problem' sind erst mal unterschiedliche Compilerversionen bzw. Einstellungen in der Commandline des Compileraufrufes. Die sind insofern ein 'Problem', weil eine neuere Compilerversion oft pingeliger ist und Dinge anmerkt, die die alte Version durchgehen liess. Das bedeutet nicht automatisch, dass der geschriebene Code in der vorhergehenden Version korrekt gewesen wäre. Es bedeutet lediglich, dass der Compiler damals noch nicht die notwendigen Tests mit hatte um diese Problemkreise zu erkennen. D.h. die 'Fehler' waren auch vorher schon drinnen, nur sind sie vom Compiler nicht angemerkt worden. Und die letzte Compilerversion (bze. die geänderten Command line Argumente) tun das eben. > über die Import-Funktion nach Studio 5 konvertiert. Alle Projekte ließen > sich auf Anhieb kompilieren und liefen auch in der Schaltung so wie sie > sollten. Und so sollte es auch sein, wenn man qualitativ guten C Code schreibt.
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.