Hallo,
bin etwas am verzweifeln. Ich lege global ein struct, mit signed char,
an.
Dieser wird auf 0 gesetzt.
Nun simuliere ich in AVR Studio 5 (5.0.1163).
Wenn ich nun in der Funktion "acc" diese Variable dekrementiere, erhalte
ich 255, erwarte jedoch -1.
In der Watchlist wird der Typ der Variablen "AXIS.acc_x" korrekt als
"signed char:8" angezeigt, trotzdem der Wert 255... Eigentlich, kann
dieser Typ doch min. -127 werden!
Inkrementiere ich die Variable, läuft sie auch bis 255!
Kann mir das jemand erklären, bzw helfen??
globdef.h
Dennis Hoh schrieb:> In der Watchlist wird der Typ der Variablen "AXIS.acc_x" korrekt als> "signed char:8" angezeigt, trotzdem der Wert 255... Eigentlich, kann> dieser Typ doch min. -127 werden!
Vielleicht hat der Debugger schlicht ein Problem mit signed Bitfeldern.
Wieso überhaupt ein Bitfeld?
Stefan Ernst schrieb:> Dennis Hoh schrieb:>> In der Watchlist wird der Typ der Variablen "AXIS.acc_x" korrekt als>> "signed char:8" angezeigt, trotzdem der Wert 255... Eigentlich, kann>> dieser Typ doch min. -127 werden!>> Vielleicht hat der Debugger schlicht ein Problem mit signed Bitfeldern.
Nicht nur der Debugger, sondern auch ein ATmega1284P? Hier verursacht es
ebenfalls Probleme.
> Wieso überhaupt ein Bitfeld?
Der gezeigte ausschnitt ist nur ein vereinfachter Teil des Bitfeldes.
Habe das gleiche Problem mit
- AxisRelPos:16;
- AxisGoTo:16;
- AxisDirection:1
Hier in voller Größe:
Dennis Hoh schrieb:>> Vielleicht hat der Debugger schlicht ein Problem mit signed Bitfeldern.> Nicht nur der Debugger, sondern auch ein ATmega1284P? Hier verursacht es> ebenfalls Probleme.
Woran machst du das fest?
Der Unterschied zwischen -1 und 255 liegt (momentan) lediglich darin,
welche Ausgabefunktion zur Anzeige eines Wertes benutzt wird. Auf
Bitebene sind beide 8-Bit Zahlen völlig identisch. Beide Zahlen haben
alle Bits auf 1.
typedef struct
{
volatile uint8_t AxisStateRef:1;
volatile uint8_t AxisStateRdy:1;
volatile uint16_t AxisAbsPos:15;
volatile uint16_t AxisMaxPos:15;
volatile int16_t AxisRelPos:16;
volatile int16_t AxisGoTo:16;
volatile signed char AxisAcc:8;
volatile signed char AxisDirection:8;
}structaxis;
Ich seh ehrlich gesagt nicht wirklich, wozu hier Bitfelder notwendig
sein sollen. Wenn du Glück hast, dann hat der Compiler lediglich
AxisStateRef und AxisStateRdy in ein gemeinsames Byte zusammengelegt und
den Rest jeweils auf Bytegrezen allokiert. Wenn du Pech hast, dann hat
er dir die 15 Bit Sachen in die Bytes hineingefriemelt und zahlst dafür
mit einem horrendem Laufzeitpenalty weil jeder Zugriff in eine Maskier
und Schiebeorgie ausartet.
Dennis Hoh schrieb:> #define xAxis 0> structaxis AXIS[xAxis];
Ein Array mit Größe 0 ? Dann als Array definiert, aber nicht als Array
angesprochen.
> AXIS.acc_x = 0;
Lässt sich so nicht kompilieren.
Ansonsten, wenn man die Fehler korrigiert, zeigt zumindest Studio 4
korrekt -1, -2, usw. an.
Dies läuft in Studio sowie im Controller ohne Fehler, solange
AXIS[AxisSelect].AxisDirection = 1;
Mit AXIS[AxisSelect].AxisDirection = -1; zählt er mit jedem Schritt 255
zu AXIS[AxisSelect].AxisRelPos hinzu, obwohl er in die andere Richtung
fährt.
typecast habe ich auch schon ohne Erfolg versucht.
AXIS[AxisSelect].AxisRelPos += (int16_t)AXIS[AxisSelect].AxisDirection;
Dennis Hohmann schrieb:> structaxis AXIS[2];
Wenn Du 3 Achsen hast, würd' ich AXIS auch mit Größe 3 anglegen.
Ohne Bitfielddefinition für die Ints geht's, ansonsten zählt er von 0
auf 65535. Die Frage wurde schon gestellt: Warum glaubst Du Bitfields zu
benötigen ?
Der erzeugter Maschinencode ist übrigens in beiden Fällen gleich, egal
mit welcher Optimierung.
Die Angabe der Bits bzw. Weglassen derselben hat offenbar nur Einfluss
darauf, wie Studio die Werte interpretiert.
MWS schrieb:> Warum glaubst Du Bitfields zu benötigen ?
Um Platz zu sparen! Bis jetzt dachte ich es bringt was. Wie würdest du
das aufbauen?
3 Achsen, div. Werte pro Achse?
MWS schrieb:>Die Frage wurde schon gestellt: Warum glaubst Du Bitfields zu benötigen ?
Vor allem ist das hier:
> volatile uint8_t AxisStateRef:1;> volatile uint8_t AxisStateRdy:1;> volatile uint16_t AxisAbsPos:15;> volatile uint16_t AxisMaxPos:15;
eine sehr ungünstige Anordnung.
Im Bezug auf das Problem könnte folgende Passage aus der ISO-Norm
relevant sein:
****
[...] for bit-fields, it is implementation-defined whether the specifier
int designates the same type as signed int or the same type as unsigned
int.
****
int16_t dürfte auf dem AVR ein typedef für int sein. Dann wäre es nach
obiger Regel möglich, daß AxisRelPos und AxisGoTo vorzeichenlos sind.
Die Fehlerbeschreibung läßt vermuten, daß dem so ist.
Ok, hab grade mal nageschaut. Daran liegts wohl doch nicht. int16_t ist
ein typedef für signed int, muß also auch in Bitfeldern
vorzeichenbehaftet sein.
Dennis Hoh schrieb:> MWS schrieb:>> Warum glaubst Du Bitfields zu benötigen ?>> Um Platz zu sparen! Bis jetzt dachte ich es bringt was.
Ok, und warum denkst du, daß ein gewöhnlicher int16_t mehr Platz
braucht, als ein Bitfeld mit 16 Bit Breite?
Dennis Hoh schrieb:> Um Platz zu sparen! Bis jetzt dachte ich es bringt was. Wie würdest du> das aufbauen?>> 3 Achsen, div. Werte pro Achse?
Da wird gespart, lobenswert :D
Nur bei 3 Achsen nicht wirklich sinnvoll, reden wir wieder drüber bei 30
Achsen.
Oder ist's eine Tiny, wo jedes Byte zählt ?
Wenn sich zwei oder mehr Variablen ein Byte teilen müssen, so zwingst Du
den Compiler zu Masken- oder Schiebeoperationen, das Sparen von SRam
kostet Dich also mehr Flash und Rechenzeit. Musst halt abwägen.
Über einen Umweg habe ichs jetzt "hingebogen"!
Im Prinzip, lege ich die globale Var in eine lokale Var um.
1
int16_tx=X.AxisDirection;
2
X.AxisAbsPos+=x;
So jedoch ging es nicht!
1
X.AxisAbsPos+=(int16_t)X.AxisDirection;
Es schein in der Tat ein Debugger/Compiler-Bug zu sein.
(Oder eine Einstellung)
In AVR Studio 4 geht es. Unter Studio 4 compiliert, läuft es auch im
ATmega1284P.
Dennis Hoh schrieb:> Über einen Umweg habe ichs jetzt "hingebogen"!
Das scheint mir der falsche Weg gewesen zu sein.
Dennis Hoh schrieb:> Es schein in der Tat ein Debugger/Compiler-Bug zu sein.
Nein, es ist kein Bug, hab's nochmal nachgesehen. Du hast sicherlich den
Schalter -funsigned-bitfields im makefile drin.
Auszug aus "Using the GNU Compiler Collection"
> -fsigned-bitfields> -funsigned-bitfields> ...> These options control whether a bit-field is signed or unsigned, when> the declaration does not use either signed or unsigned.
Entferne oder ersetze den Schalter durch -fsigned-bitfields und die
Darstellung zumindest in Studio4 ist in Ordnung, auch ohne Umwege.
Kannst mal testen und sagen, ob das dann auch in Studio5 in Ordnung ist.
Nachtrag:
Nachdem mir auffiel, daß es keinen Unterschied machen darf, ob
-fsigned-bitfields oder -funsigned-bitfields benutzt wird, sobald der
Integer als signed oder unsigned definiert wurde, hab' ich mir das
nochmal näher angesehen.
In stdint.h wird (u)int16 wie folgt definiert:
uint16_t --> typedef unsigned int uint16_t;
int16_t --> typedef signed int int16_t;
Damit darf der Schalter -f(un)signed-bitfields keine Wirkung mehr haben.
Hat er aber. Und zwar nicht nur wie AVR-Studio im Watch-Fenster den Wert
ausgibt, sondern auch im Code.
Folgende Struct-Deklarationen müssten sich exakt entsprechen:
1
typedefstruct{
2
unsignedcharAxisStateRef:1;
3
unsignedcharAxisStateRdy:1;
4
unsignedintAxisAbsPos:15;
5
unsignedintAxisMaxPos:15;
6
signedintAxisRelPos:16;
7
signedintAxisGoTo:16;
8
signedcharAxisAcc:8;
9
signedcharAxisDirection:8;
10
}structaxis;
1
typedefstruct{
2
uint8_tAxisStateRef:1;
3
uint8_tAxisStateRdy:1;
4
uint16_tAxisAbsPos:15;
5
uint16_tAxisMaxPos:15;
6
int16_tAxisRelPos:16;
7
int16_tAxisGoTo:16;
8
signedcharAxisAcc:8;
9
signedcharAxisDirection:8;
10
}structaxis;
Wird -funsigned-bitfields verwendet, dann unterscheidet sich der hierzu
erzeugte Code jedoch, im Beispiel:
Wird mit signed / unsigned int deklariert, so wird AxisRelPos richtig
als vorzeichenbehafteter Integer betrachtet.
Wird dagegen int16_t deklariert, wird AxisRelPos als vorzeichenloser
Integer behandelt.
Das ist also nicht mehr nur ein Anzeigeproblem von AVR-Studio, sondern
tatsächlich ein Fehler bei der Compilierung.
Erkennbar im Beispielcode an der unterschiedlichen Ausführung des
Branches. Mit "signed int AxisRelPos" wird PORTC = 0, mit "int16_t
AxisRelPos" wird PORTC = 255 ausgeführt.
Das darf so nicht sein. Würd' vorschlagen, das sich das jemand Anders
noch anschaut, nicht dass ich was übersehen hab'.
Kann auch sein, daß dieser Fehler bekannt und in einer späteren
AVR-GCC-Version bereits behoben ist. Verwendet wurde WinAVR-20090313.
Die richtige stdint.h wurde benutzt, geprüft durch Umbenennen.
Die "volatile" im Struct hab' ich raus genommen. Sobald der Struct
selbst als volatile deklariert wurde, sollte es für die einzelnen
Structmember nicht mehr notwendig sein.
MWS schrieb:> Entferne oder ersetze den Schalter durch -fsigned-bitfields und die> Darstellung zumindest in Studio4 ist in Ordnung, auch ohne Umwege.> Kannst mal testen und sagen, ob das dann auch in Studio5 in Ordnung ist.
Im Studio 5 ist der Schalter mit: "Default bitfield type is unsigned"
benannt.
Hierbei bin ich davon ausgegangen, wenn es explizite als signed
deklariert ist, dieser Schalter nicht wirkt.
YOU just made my day!
Das hat mich 5 Tage gekostet...
Kannst dir eine Flasche Sekt abholen!
Dennis Hoh schrieb:> MWS schrieb:>> Warum glaubst Du Bitfields zu benötigen ?>> Um Platz zu sparen!
Eine plain vanilla struct braucht 12 Bytes. Mit deinem Bitfield kommst
du auf 11 Bytes runter, bei gleichzeitig komplexeren Code.
Bei 3 Achsen hast du ganze 3 Bytes eingespart, bei 33 Bytes (36)
Speicherverbrauch.
Bringts das wirklich?
> Bis jetzt dachte ich es bringt was. Wie würdest du> das aufbauen?
plain vanilla.
Dennis Hoh schrieb:> YOU just made my day!
Schön, freut mich :-)
Lies Dir aber noch meinen Beitrag oben durch, ich halte es für möglich
(nicht getestet), dass uint16_t mit -fsigned-bitfields zu einem signed
int wird. Wenn Du ganz sicher gehen willst, verwende bei der Definition
des Bitfields nicht die Kurzformen in16_t, uint16_t, sondern mach's wie
im Beispiel im ersten Struct.
> Kannst dir eine Flasche Sekt abholen!
Wo denn ? ;-)
Karl Heinz Buchegger schrieb:> Eine plain vanilla struct braucht 12 Bytes. Mit deinem Bitfield kommst> du auf 11 Bytes runter, bei gleichzeitig komplexeren Code.> Bei 3 Achsen hast du ganze 3 Bytes eingespart, bei 33 Bytes (36)> Speicherverbrauch.
Hatte ich hier schon angesprochen:
MWS schrieb:> Wenn sich zwei oder mehr Variablen ein Byte teilen müssen, so zwingst Du> den Compiler zu Masken- oder Schiebeoperationen, das Sparen von SRam> kostet Dich also mehr Flash und Rechenzeit. Musst halt abwägen.
Karl Heinz, ich würde Dich bitten, meine oben gemachten Erkenntnisse zu
überprüfen.
MWS schrieb:> Karl Heinz, ich würde Dich bitten, meine oben gemachten Erkenntnisse zu> überprüfen.
Ich habs mir durchgelesen.
Das was du da geschrieben hast, klingt alles schlüssig und dürfte nicht
so sein. Ich check das nochmal mit dem C-Standard ab.
(Zeigt eigentlich nur, dass Bitfields anscheinend von niemandem für
solche Sachen benutzt werden, sonst wärs schon aufgefallen. Bitfields
nimmt man normalerweise um Einzelbits (true/false) in eine Struktur zu
quetschen, maximal hat man da Dinge die 4 oder 8 Werte unsigned annehmen
können. Aber tatsächliche Rechenwerte quetscht keiner in Bitfiels
zusammen. In 30 Jahren kann ich mich an keinen einzigen derartigen Fall
erinnern)
MWS schrieb:> In stdint.h wird (u)int16 wie folgt definiert:>> uint16_t --> typedef unsigned int uint16_t;> int16_t --> typedef signed int int16_t;
Nö, wird es nicht. Das ist nur der DOXYGEN-Teil. Die echte Definition
steht weiter unten:
Dann gibt es ja keinen Grund uint16_t, int16_t und Co. zu verwenden,
oder muss ich jetzt jedesmal vorher in der stdint.h nach der der genauen
Definition schauen? Wofür gibt es die Definitionen dann überhaupt?
Ganz so schwarz muss man auch nicht malen.
Die haben schon ihren Sinn.
Bei der nächsten stdint.h wird man das vielleicht auf ein explizites
signed int ändern und gut ists. Das hat halt einfach wer übersehen, weil
es normalerweise ja auch egal ist, und nur bei Bitfeldern einen
Unterschied macht.
Stefan Ernst schrieb:> Nö, wird es nicht. Das ist nur der DOXYGEN-Teil. Die echte Definition> steht weiter unten:
Ah, ok, danke.
Hab' die erste Fundstelle genommen :D