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
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.
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.
Ε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?
uint16_tx=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:
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
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_tfoo;
6
uint16_tvolatilevfoo;
7
8
voidtest(floatfval)
9
{
10
uint16_tval;
11
12
val=fval/123.4;
13
ATOMIC_BLOCK(ATOMIC_FORCEON){
14
vfoo=val;
15
}
16
}
17
18
voidtest1(floatfval)
19
{
20
uint16_tval;
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.