Frohes Allerheiligen, Ich bin relativ neu in AVRC. Bei einem Aktuellen Projekt musste ich einen long integer an eine Routine übergeben und darin damit rechnen. Nach langer Fehlersuche stellte sich heraus, dass Atmel Studio ein Problem damit hat long integer Zahlen an eine Routine zu übergeben. Wen man die Zahl direkt als Dezimalzahl als Parameter eingibt, wird sie unter einem Zahlenwert von 32768 (Bit15) ins falsche Byte geschrieben. Wenn ich sie aber vorher in eine variable schreibe, und diese dann als Parameter angebe funktioniert die Übertragung. (Wie man auf den Bildern sehen kann). Ist dieses Verhalten normal?
Gabriel M. schrieb: > Ist dieses Verhalten normal? nein, könnte aber nur ein Ansicht Problem beim Debugger sein. Da der Code nichts sinnvolles macht, wird vermutlich alles wegoptimiert. Dann sieht der Debugger nichts sinnvolles. Außerdem hat das Studio kein Problem sondern wenn überhaupt der Compiler (GCC)
Peter II schrieb: > könnte aber nur ein Ansicht Problem beim Debugger sein. Ich hatte das problem vorher schon auf dem µC, weshalb ich dieses einfache testprogramm geschrieben habe Peter II schrieb: > Da der Code nichts sinnvolles macht, wird vermutlich alles wegoptimiert. > Dann sieht der Debugger nichts sinnvolles. Optimizer ist aus. Peter II schrieb: > Außerdem hat das Studio kein Problem sondern wenn überhaupt der Compiler > (GCC) Ja
Gabriel M. schrieb: > Ich hatte das problem vorher schon auf dem µC, weshalb ich dieses > einfache testprogramm geschrieben habe teste mal mit:
1 | Routine(long l) |
2 | {
|
3 | long v = l; |
4 | if (v == 10 ) { |
5 | v = 1; |
6 | }
|
7 | }
|
und schau ob er ins if geht.
Die Zeile, auf die der Pfeil zeigt, ist eigentlich die Zeile, die als nächstes ausgeführt wird. Mach mal ein zusätzliches NOP dahinter und mache einen zusätzlichen Step (oder setze den Breakpoint auf das NOP).
Peter II schrieb: > und schau ob er ins if geht. bei
1 | routine(10); |
nicht, bei
1 | long l = 10; |
2 | routine(1); |
schon Stefan E. schrieb: > Mach mal ein zusätzliches NOP dahinter und > mache einen zusätzlichen Step (oder setze den Breakpoint auf das NOP). hilft auch nichts
:
Bearbeitet durch User
Gabriel M. schrieb: > bei > > routine(10); > > nicht, > > bei > > long l = 10; > routine(1); > > schon gibt es Warnungen beim Compiler? Alle Warnungen eingeschaltet (-Wall ?)
Bitte gewoehne dir diesen Mist mit int, long, etc. ab und nimm die Datentypen aus der stdint.h. ((u)int8_t, (u)int16_t, (u)int32_t, usw.) Da weiss jeder gleich beim raufschauen, wie gross der Wertebereich ist, und der Wertebereich ist damit auf allen Platformen immer gleichgross. int ist auf dem AVR 2 Byte gross, bei long muesste ich erst mal nachschauen. Wie schnell man sich mit int, long, etc. ins Knie schiesst kann man an der Referenzimplementierung von MD5 (RFC1321) sehen. Die Funktioniert naemlich nur auf x86 richtig, aber nicht mehr auf x86_64. https://de.wikipedia.org/wiki/Message-Digest_Algorithm_5#Referenzimplementation Stefan E. schrieb: > Die Zeile, auf die der Pfeil zeigt, ist eigentlich die Zeile, die als > nächstes ausgeführt wird. So siehts aus. Stefan E. schrieb: > Mach mal ein zusätzliches NOP dahinter und > mache einen zusätzlichen Step (oder setze den Breakpoint auf das NOP). Oder einfach ein return; :)
Peter II schrieb: > gibt es Warnungen beim Compiler? Alle Warnungen eingeschaltet (-Wall ?) Warnungen sind Eingeschalten, es gibt aber keine Kaj G. schrieb: > nimm die > Datentypen aus der stdint.h. Hab ich probiert, selber fehler
Ich würde die Funktionmal vor main() schieben, damit beim Aufruf auch bekannt ist, daß sie einen long Parameter hat. Ohne dies wird long als 64Bit übergeben, 10 aber als 16bit, denn ist ist ja nicht 10L. Vermutlich wird der Compiler aber einige Anmerkungen (Warnings) dazu gemacht haben. Sollte man gelegentlich lesen ;-)
Carl D. schrieb: > Ich würde die Funktionmal vor main() schieben, damit beim Aufruf auch > bekannt ist, daß sie einen long Parameter hat. Ohne dies wird long als > 64Bit übergeben, 10 aber als 16bit, denn ist ist ja nicht 10L. sehr guter Hinweis. > Vermutlich wird der Compiler aber einige Anmerkungen (Warnings) dazu > gemacht haben. Sollte man gelegentlich lesen ;-) angeblich keine Warnungen.
Peter II schrieb: > Carl D. schrieb: >> Ich würde die Funktionmal vor main() schieben, damit beim Aufruf auch >> bekannt ist, daß sie einen long Parameter hat. Ohne dies wird long als >> 64Bit übergeben, 10 aber als 16bit, denn ist ist ja nicht 10L. > > sehr guter Hinweis. Bei mir hat sich der Nebel eben längst gelichtet. >> Vermutlich wird der Compiler aber einige Anmerkungen (Warnings) dazu >> gemacht haben. Sollte man gelegentlich lesen ;-) > > angeblich keine Warnungen. Dazu kenn ich den GCC zu gut, um das zu glauben. BTW: Die Typen aus inttypes.h sind natürlich wichtig, wenn Algoritmen auf bestimmten Bitlängen basieren, helfen aber nicht bei mangelndem Sprachverständnis.
:
Bearbeitet durch User
Irgendwer schrieb: > Probier mal: > routine(10L); > routine((long)10); vermutlich verschwindet der Effekt, aber das eigentliche Problem ist damit nicht behoben. Es muss auch mit 10 ohne cast oder Suffix gehen.
Beitrag #5193791 wurde von einem Moderator gelöscht.
Dieter F. schrieb im Beitrag #5193791: > Schau mal hier, > > https://gcc.gnu.org/wiki/avr-gcc > > was bei einem 8-Bit-Prozessor mit GCC long bedeutet (oder versuche mal > "long long" :-) ) was meinst du damit? eine 10 passt doch wohl in 32bit rein.
Peter II schrieb: > was meinst du damit? Habe ich zurückgezogen, weil nur bei einer Direktive "-mint8" so. Vegiss es bitte einfach ...
Carl D. schrieb: > Ich würde die Funktionmal vor main() schieben, damit beim Aufruf auch > bekannt ist, daß sie einen long Parameter hat. Ohne dies wird long als > 64Bit übergeben, 10 aber als 16bit, denn ist ist ja nicht 10L. Ok Funktioniert, wieder was dazugelernt Carl D. schrieb: > Vermutlich wird der Compiler aber einige Anmerkungen (Warnings) dazu > gemacht haben. Sollte man gelegentlich lesen ;-) Stimmt, er gibt eine Warnung, habe sie vorher im Simulator Fenster übersehen Vielen Dank an alle :)
Peter II schrieb: > Irgendwer schrieb: >> Probier mal: >> routine(10L); >> routine((long)10); > > vermutlich verschwindet der Effekt, aber das eigentliche Problem ist > damit nicht behoben. > > Es muss auch mit 10 ohne cast oder Suffix gehen. Das Problem ist, daß eine nicht deklarierte Funktion (->Prototyp), denn sie wird erst nach Verwendung definiert, in C erlaubt ist, aber dann davon ausgegangen wird, daß die Parameter vom (implementierungsabhänigen) Typ int sind, hier 16bit. Deswegen warnt der GCC, und macht es dann falsch oder richtig, je nach Gusto des Benutzers. Für mich macht er es nur richtig, denn er soll 16bit übergeben und da paßt 10 gut rein. Würde man 100000 übergeben wollen, dann würde er wieder warnen, weil das nicht in 16bit paßt. Ob später jemand die Funktion ganz anders definiert, interessiert dann nicht mehr. Aber wie gesagt, es hagelt genügend Warnungen um dem Rätsel auf die Spur zu kommen, im 10er- und 100000er-Fall. BTW, volatile wurde als Lösungsansatz noch gar nicht vermutet :-)
Damit ein Wert korrerkt übergeben werden kann, ist i.d.R ein korrekter Prototyp notwendig. Daher: -Wmissing-prototypes -Wstrict-prototypes zu den Compileroptionen hinzufügen und entsprechende Warnungen beheben. Oder gar: -Werror=missing-prototypes -Werror=strict-prototypes Ohne Prototyp hat ein Aufruf foo (0) implizit int foo (int) also Prototyp, auch wenn ein long übergeben wird, d.h. Ohne vorherige Deklaration ist foo (0L) immer noch int foo (int).
:
Bearbeitet durch User
Kaj G. schrieb: > Bitte gewoehne dir diesen Mist mit int, long, etc. ab und nimm die > Datentypen aus der stdint.h. ((u)int8_t, (u)int16_t, (u)int32_t, usw.) > Da weiss jeder gleich beim raufschauen, wie gross der Wertebereich ist, > und der Wertebereich ist damit auf allen Platformen immer gleichgross. Es ist außer für direkte Kommunikation auf Binärebene nie zwingend erforderlich, die Variablen auf feste Größen zu zwingen. Das kann auch zu suboptimalem Code führen. Erforderlich ist, dass man versteht, wie die Typen funktionieren und welche Wertebereiche sie garantiert haben - und dass man beim Definieren einer Variable kurz darüber nachdenkt. Sinnvoll können auch die [u]int_least*_t und [u]int_fast*_t sein. Da hat man seinen Wertebereich auch garantiert, aber man gibt dem Compiler mehr Raum zum Optimieren. > int ist auf dem AVR 2 Byte gross, bei long muesste ich erst mal > nachschauen. long ist auf allen konformen Compilern mindestens 32 Bit groß. Das Problem hier ist ja auch nicht die Größe von int oder long, sondern dass die Funktion vor Verwendung nicht deklariert war. Carl D. schrieb: > Für mich macht er es nur richtig, denn er soll 16bit übergeben und da > paßt 10 gut rein. Würde man 100000 übergeben wollen, dann würde er > wieder warnen, weil das nicht in 16bit paßt. Ob später jemand die > Funktion ganz anders definiert, interessiert dann nicht mehr. Dann kommt dafür aber die Warnung, dass diese Definition der Funktion nicht mit der vorherigen (in diesem Fall impliziten) Deklaration übereinstimmt. > Aber wie gesagt, es hagelt genügend Warnungen um dem Rätsel auf die Spur > zu kommen, im 10er- und 100000er-Fall. > BTW, volatile wurde als Lösungsansatz noch gar nicht vermutet :-) Man kann nicht jedes "seltsame Variablenproblem" mit volatile erschlagen.
Rolf M. schrieb: > Es ist [...] nie zwingend erforderlich Das habe ich auch nicht gesagt. Aber der Vorteil ist erheblich: Jeder, der sich den Code anschaut sieht auf anhieb welche Wertebereiche angenommen werden koennen, ohne das sich der Leser Gedanken darueber machen muss auf welcher Platform der Code laeuft. Und wie das Beispiel von MD5 zeigt, scheint es ja doch hin und wieder erforderlich zu sein, eben um die korrekte Funktion zu garantieren. Aber was rede ich hier. Sicherheit und korrekte Funktion interessiert ja eh niemanden. Rolf M. schrieb: > Sinnvoll können auch die [u]int_least*_t und [u]int_fast*_t sein. Da hat > man seinen Wertebereich auch garantiert, aber man gibt dem Compiler mehr > Raum zum Optimieren. Ja, deswegen schrieb ich auch "usw." mit dazu :-)
Kaj G. schrieb: > Das habe ich auch nicht gesagt. Aber der Vorteil ist erheblich: > Jeder, der sich den Code anschaut sieht auf anhieb welche Wertebereiche > angenommen werden koennen, ohne das sich der Leser Gedanken darueber > machen muss auf welcher Platform der Code laeuft. Mindest-Wertebereiche können auch für die eingebauten Typen angenommen werden. Beispiel: Ein long ist wie schon gesagt mindestens 32 Bit breit. Beim int sind es nur 16 Bit. Brauche ich also Werte größer als 32k, nehme ich einen long und keinen int. > Und wie das Beispiel von MD5 zeigt, scheint es ja doch hin und wieder > erforderlich zu sein, eben um die korrekte Funktion zu garantieren. Wie gesagt: Erforderlich ist, zu wissen, welche Garantien die Typen einem geben und insbesondere auch, welche sie nicht geben. Wenn man das nicht beachtet und einfach davon ausgeht, dass die Datentypen so sind wie auf der Plattform, die man aktuell vor sich hat, wird das ganze eben nicht portabel. > Aber was rede ich hier. Sicherheit und korrekte Funktion interessiert ja > eh niemanden. Doch, die interessiert schon. Aber man muss ja nicht unbedingt die Schrauben mit dem Hammer in die Wand schlagen, nur weil man keine Lust hat, den Umgang mit dem Akkuschrauber zu lernen.
Rolf M. schrieb: > Erforderlich ist, zu wissen, welche Garantien die Typen > einem geben und insbesondere auch, welche sie nicht geben. Also: Im Prinzip genau so, wie bei einem Autohändler. Flücht :) MfG Paul
Rolf M. schrieb: > Sinnvoll können auch die [u]int_least*_t und [u]int_fast*_t sein. Da hat > man seinen Wertebereich auch garantiert, aber man gibt dem Compiler mehr > Raum zum Optimieren. Mal interessehalber: welcher Compiler optimiert da denn? Die Implementierungen der least - und fast - Typen, die ich so kenne ( sind allerdings nicht viele), sind reine typedefs auf Typen aus der stdint. Der Compiler selber kennt least- und fast gar nicht. Oliver
Oliver S. schrieb: > sind reine typedefs auf Typen aus der stdint das ist schon die Optimierung. Viele "moderne" Befehlssätze (namentlich ARM) implementieren Arithmetik (nur) auf Basis der "natürlichen" Hauptregisterbreite und müssen - wenn man ihnen was anderes gibt - aufwendige extend- und truncate-Befehle einfügen. Der Sachverhalt ändert sich ja nicht, kann also direkt per typedef vorgegeben werden.
Oliver S. schrieb: > Rolf M. schrieb: >> Sinnvoll können auch die [u]int_least*_t und [u]int_fast*_t sein. Da hat >> man seinen Wertebereich auch garantiert, aber man gibt dem Compiler mehr >> Raum zum Optimieren. > > Mal interessehalber: welcher Compiler optimiert da denn? Die > Implementierungen der least - und fast - Typen, die ich so kenne ( sind > allerdings nicht viele), sind reine typedefs auf Typen aus der stdint. > Der Compiler selber kennt least- und fast gar nicht. War vielleicht etwas falsch ausgedrückt. Was ich meinte, war, dass die _fast-Typen ggf. auch größer sein können als die angegebene Größe, falls dadurch die Nutzung auf dem vorliegenden Prozessor effizienter ist.
Rolf M. schrieb: >> BTW, volatile wurde als Lösungsansatz noch gar nicht vermutet :-) > > Man kann nicht jedes "seltsame Variablenproblem" mit volatile > erschlagen. ich hoffe der humoristische Teil meines Vorschlags wurde verstanden. Abgesehen von unvermeidbaren, aber seltener auftretenden Compilerfehlern, bekommt man eigentlich nur das in MaschinenCode umgesetzt, was man in Hochsprache formuliert hat. Und wenn man genauer nachdenkt, was man da geschrieben, stellt man nicht selten fest, was das für ein Blödsinn war.
Gabriel M. schrieb: > Ist dieses Verhalten normal? Ja. Ohne Deklaration nimmt der Compiler als Argument int an und gibt eine entsprechende Warnung aus. Mit routine( 10UL ) kannst Du ihm sagen, daß er ein long übergeben soll. Die saubere Lösung ist aber, alle Funktionen vor ihrer ersten Benutzung zu deklarieren.
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.