Forum: Compiler & IDEs Aktueller Stand zur Umsetzung von Bitfeldern?


von Sascha W. (arno_nyhm)


Lesenswert?

Hallo,

ich bin/war nun eine ganze Zeit aus der AVR-Programmierung raus und 
steige nun wieder mit einem Projekt ein - zuvor verwendete ich die zu 
diesem Zeitpunkt (vor zwei, drei Jahren?!) normale Vorgehensweise: 
eigenständige WinAVR-Installation die mit den, in einem beliebigen 
Editor erstellten, Ausgangsdaten per Makefile gefüttert wird.
Inzwischen hat sich das 'AVR Studio' ja zum 'Atmel Studio' entwickelt 
und bringt seinen eigenen AVR-GCC gleich mit; 'Atmel Studio 6.0' läuft 
nun auch bei mir und ich finde es soweit auch ganz gut - bin zügig dabei 
mich einzuarbeiten.

Nun zu meiner eigentlich Frage: 'Früher' war die Nutzung von Bitfeldern 
im WinAVR/AVR-GCC ja ziemlich nutzlos, da sie nicht entsprechend 
umgesetz wurden - inzwischen ließt man öfter (auch hier im 
AVR-GCC-Tutorial) über Bitfelder ohne jeglichen Hinweis, dass diese 
eigentlich Nutzlos sind, da sie nicht entsprechend vom Kompiler 
umgesetzt werden - auch habe ich schon ELF-Files mit entsprechendem Code 
gesehen, in dem eine Bitfeld-Struktur korrekt umgesetzt wurde. Hier 
z.B.: Beitrag "AVR-gcc und Bitfelder"

Kann der AVR-GCC inzwischen besser mit Bitfeldern umgehen?
Werden entsprechende Bitfelder im IO-Bereich mit eintaktigen 
sbi/cbi-Assemblerbefehlen umgesetzt (sieht ja ganz so aus, in dem eben 
genannten Forenbeitrag) und außerhalb des IO-Bereich mit entsprechenden 
Bitmanipulationen ('x |= y', 'x &= ~y') auf Byte-Ebene?

von Oliver S. (oliverso)


Lesenswert?

Ich sach mal, Versuch macht kluch...

Wenn du das Studio doch schon installiert hast, probier es halt einfach 
aus. Ändern kannst du an dem Ergebnis dann eh nichts.

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Sascha W. schrieb:
> 'Früher' war die Nutzung von Bitfeldern
> im WinAVR/AVR-GCC ja ziemlich nutzlos, da sie nicht entsprechend
> umgesetz wurden

Das war schon immer Quatsch.

Was allerdings bezüglich Bitfeldern stimmt ist, dass die Reihenfolge
der Bits "implementation-defined" ist, d. h. entsprechender Code ist
nicht portabel und kann selbst für den gleichen Prozessor (also AVR
in diesem Falle) bei einem anderen Compiler genau andersrum
implementiert sein.

Außerdem ist es "implementation-defined", was passiert, wenn ein
Bitfeld eine Grenze der kleinsten allozierbaren Einheit überspannt,
aber das war beim AVR meines Wissens noch nie ein Thema, da er
keinerlei Alignment-Anforderungen seitens der CPU hat.

Solange das kein Problem ist, sind Bitfelder benutzbar und waren es
schon immer, auch "früher".

Es gibt in der avr-libc sogar einen Controller, bei dem die Register
über Bitfelder zugreifbar sind: der ATmega128RFA1.  Das hat den
Vorteil, dass man nicht mehr Gefahr läuft, das Bit in einem völlig
falschen Register zu setzen.  Leider hat sich diese Sichtweise
innerhalb von Atmel nicht durchgesetzt, alle anderen AVRs bieten
derartiges nicht an.  (Außerdem ist Atmel im Moment sowieso mächtig
ins Hintertreffen gekommen, die Unterstützung für ihre neueren
AVRs mal in die offizielle avr-libc zurück zu füttern.)

von Sascha W. (arno_nyhm)


Lesenswert?

Jörg Wunsch schrieb:
> Solange das kein Problem ist, sind Bitfelder benutzbar und waren es
> schon immer, auch "früher".

Dann ist die Aussage, dass 'früher' für jeden einzelnen Bezeichner im 
Bitfeld eine eingene, ein ganzes Byte breite, Variable vom Compiler 
angelegt wurde also aus der Luft gegriffen/unwahr? Eben dies habe ich 
nämlich dazu gelesen.

Das mit der 'implemention-defined' bezüglich der Anordnung der Bits 
macht mein (experimentelles) Vorhaben wohl doch gefährlich, ich habe 
daran gedacht mir eine Bitfeld-Struktur über ein GPIO-Portregister zu 
mappen um die Portpins, mit Klartext-Namen versehen, bequem, wie 
Variablen, ansprechen zu können:
1
#define PortA (*(PortA_t*)&PORTA)
2
3
...
4
5
typedef struct
6
{
7
  unsigned OutLedStatur:1;
8
  unsigned OutLedTrigger:1;
9
  unsigned OutMotorPhaseA:1;
10
  unsigned OutMotorPhaseB:1;
11
} PortA_t;
12
13
PortA_t PortA;
14
15
...
16
17
PortA.OutLedStatus  = 1;
18
PortA.OutLedTrigger = 0;

War aber wohl eher eine fixe Idee, je mehr ich darüber Nachdenke, desto 
schwieriger/sinnfreier erscheint es - auch die Trennung von 
Ein-/Ausgängen in PORTx/PINx-Register macht es nicht besser mit dieser 
Umsetzung. Schon die Tatsache, dass nicht direkt klar ist ob der erste 
Eintrag im Bitfeld dem LSB oder MSB entspricht macht es heikel - wenn 
dies aber nicht einmal irgendwo definiert ist, wie es zu sein hat, ist 
es nicht sinnvoll für diesen Zweck zu gebrauchen.

Bleibt es wohl das beste per #define festgelegte Konstanten für die 
jeweiligen Bits (PA1, PA2, ...) zu benutzen.

Vielen Dank für den Hinweis, Jörg!

Grüße
Sascha

von Klaus (Gast)


Lesenswert?

Ich kenne die AVR nicht, aber hier aus einem PIC C-Manual:

> The convention in the processor header files is that each SFR is
> named, using the same name that appears in the data sheet for the
> part – for example, CORCON for the Core Control register. If the
> register has individual bits that might be of interest, then
> there will also be a structure defined for that SFR, and the name
> of the structure will be the same as the SFR name, with “bits”
> appended. For example, CORCONbits for the Core Control register.
> The individual bits (or bit fields) are named in the structure using
> the names in the data sheet – for example PSV for the PSV bit of the
> CORCON register. Here is the complete definition of CORCON
1
/* CORCON: CPU Mode control Register */
2
extern volatile unsigned int CORCON __attribute__((__near__));
3
typedef struct tagCORCONBITS {
4
  unsigned IF     :1;        /* Integer/Fractional mode */
5
  unsigned RND    :1;        /* Rounding mode  */
6
  unsigned PSV    :1;        /* Program Space Visibility enable */
7
  unsigned IPL3   :1;
8
  unsigned ACCSAT :1;        /* Acc saturation mode */
9
  unsigned SATDW  :1;        /* Data space write saturation enable */
10
  unsigned SATB   :1;        /* Acc B saturation enable */
11
  unsigned SATA   :1;        /* Acc A saturation enable */
12
  unsigned DL     :3;        /* DO loop nesting level status */
13
  unsigned        :4;
14
} CORCONBITS;
15
extern volatile CORCONBITS CORCONbits __attribute__((__near__));
> The symbols CORCON and CORCONbits refer to the same register and
> will resolve to the same address at link time.

Ich denke, so was meinst du. Ich gebe dann via #define den Portbits in 
meiner Schaltung noch passende Namen.

MfG Klaus

von Uwe (de0508)


Lesenswert?

Hallo,

ich verwende immer mit dem avr gcc diese Macros von PeDa:

http://www.mikrocontroller.net/attachment/58294/SBIT.H

Suche mal nach weiteren sbit.h Stellen im Forum für eine weiterführende 
Erklärung.

von Stefan E. (sternst)


Lesenswert?

Sascha W. schrieb:
> Dann ist die Aussage, dass 'früher' für jeden einzelnen Bezeichner im
> Bitfeld eine eingene, ein ganzes Byte breite, Variable vom Compiler
> angelegt wurde also aus der Luft gegriffen/unwahr? Eben dies habe ich
> nämlich dazu gelesen.

Ich kann mich nicht erinnern, ein solches Verhalten jemals gesehen, noch 
darüber gelesen zu haben. Du hast da nicht zufällig was mit dem Debugger 
des AVR-Studios zusammengewürfelt, der bei der Anzeige von Bit-Feldern 
das Problem hatte, für jedes Element immer den ganzen Container 
anzuzeigen?

Sascha W. schrieb:
> Das mit der 'implemention-defined' bezüglich der Anordnung der Bits
> macht mein (experimentelles) Vorhaben wohl doch gefährlich,
> ...
> Schon die Tatsache, dass nicht direkt klar ist ob der erste
> Eintrag im Bitfeld dem LSB oder MSB entspricht macht es heikel - wenn
> dies aber nicht einmal irgendwo definiert ist, wie es zu sein hat, ist
> es nicht sinnvoll für diesen Zweck zu gebrauchen.

Es ist definiert. "implemention-defined" bedeutet, dass der Compiler es 
definiert. Und das bedeutet wiederum, dass er feste Regeln diesbezüglich 
aufstellen und dokumentieren muss, und sich dann natürlich auch an seine 
eigenen Regeln halten muss. "Gefährlich" ist es also nur in Bezug auf 
die Portierbarkeit. Natürlich besteht auch rein theoretisch die Gefahr, 
dass der selbe Compiler seine Regeln von einer Version zur anderen 
ändert, aber warum sollte er das tun? Diese "Gefahr" ist also praktisch 
Null.


PS: In deinem Code-Beispiel hast du dir übrigens das volatile des 
Registers weggecastet, was natürlich eine ganz schlechte Idee ist.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Sascha W. schrieb:
> ich habe
> daran gedacht mir eine Bitfeld-Struktur über ein GPIO-Portregister zu
> mappen um die Portpins, mit Klartext-Namen versehen, bequem, wie
> Variablen, ansprechen zu können:

Ist letztlich ziemlich genau das, was in <avr/iom128rfa1.h> für
alle IO-Register (optional) angeboten wird.  Dort kann man eben
schreiben:
1
TRXPR_struct.slptr = 1;

als Alternative zu
1
TRXPR |= (1 << SPLTR);

Würde man stattdessen sich beispielsweise verschreiben und versuchen:
1
TRX_CTRL_0 |= (1 << SLPTR);

hat der Compiler keine Ahnung, dass man auf das falsche Register
zugreift.  Versucht man aber:
1
TRX_CTRL_0_struct.slptr = 1;

dann wird einem der Compiler mitteilen, dass das so nicht geht.

von Klaus (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> dann wird einem der Compiler mitteilen, dass das so nicht geht.

und die IDE wird einem beim Schreiben die verfügbaren Bits oder Felder 
anbieten.

MfG Klaus

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Klaus schrieb:
> und die IDE wird einem beim Schreiben die verfügbaren Bits oder Felder
> anbieten.

Da bin ich mir nicht ganz so sicher, denn sie müsste dann den
dahinter liegenden Mischmasch aus struct-Definition, typecast,
#define, Zeigerbildung und -dereferenzierung durchschauen.  Ganz
schön viel Anforderung für ein bisschen IDE.

von Karl H. (kbuchegg)


Lesenswert?

Mal eine Frage.
Wer generiert eigentlich die C-Header Files?
Die werden/wurden ja meines Wissens aus den Atmel XML Part Description 
Files generiert. Früher war das wohl Teil der WinAvr Generierung. Macht 
Atmel das heute selber?


Hintergrund.
Ich hab mir mal ein paar entsprechende Part Description Files angesehen. 
Noch aus dem 4-er Studio. So schlimm dürfte das jetzt nicht sein, da 
automatisch für jeden Prozessor ein Header File mit entsprechenden 
Strukturdefinitionen zu erzeugen.
Denn eines muss man schon sagen: Es hätte durchaus was für sich, wenn 
man derartige Strukturen hätte. Alleine die Absicherung, dass das 
entsprechende Bit auch wirklich im Register liegt wäre die Sache schon 
wert.

von Peter D. (peda)


Lesenswert?

Ander AND/OR Schreibweise stört mich auch, daß die fehlerträchtig ist.

Man müßte für alle AVRs alle SFR-Bits als Struct definieren. Ist aber ne 
riesen Fleißarbeit.

Ich hab das mal für die Portpins gemacht:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&p=835728#835728


Peter

von Oliver S. (oliverso)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ich hab mir mal ein paar entsprechende Part Description Files angesehen.
> Noch aus dem 4-er Studio. So schlimm dürfte das jetzt nicht sein, da
> automatisch für jeden Prozessor ein Header File mit entsprechenden
> Strukturdefinitionen zu erzeugen.

Das ist sogar ziemlich einfach. Vor allem, wenn man dann auch noch die 
xml-Files des Studio 5 oder 6 mit benutzt. Deren Format ist etwas 
ausführlicher. Das sollte bis auf die Korrektur der in den xml-Files 
vorkommenden Fehler automatisiert machbar sein.

Ich hatte vor einer Weile mal angefangen, mit den Open-Source 
AVR-Simulatoren etwas rumzubasteln, und mir dafür auch einen 
rudimentären XML-Umsetzer geschrieben, der was so etwas ähnliches 
generiert.

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:

> Wer generiert eigentlich die C-Header Files?
> Die werden/wurden ja meines Wissens aus den Atmel XML Part Description
> Files generiert. Früher war das wohl Teil der WinAvr Generierung. Macht
> Atmel das heute selber?

Die ersten waren alle handgefeilt.  Das lässt sich zumindest für
alle die, die auch noch SIG_*-Vektornamen anbieten, praktisch auch
kaum ändern, d. h. diese kann man auch heute nich auf einfache
Weise neu generieren.  (Das liegt daran, dass die SIG_*-Namen mehr
oder minder "nach Bauchgefühl" benannt worden sind und nicht direkt
aus den XML-Daten ableitbar sind, zumindest nicht 100%ig.)  Kommt
hinzu, dass einige dieser Files eine Rückwärtskompatibilität zu
früheren Versionen mitschleppen (wollen), die dann entsteht, wenn
ein Register mal in einer späteren Datenblattversion (und damit im
XML) umbenannt worden ist.

Kurz und gut: für all diese gäbe es keine Chance, sie einfach mit
wenig Aufwand neu zu generieren.

Neuere Dateien hat Eric dann mit einem Script aus den Studio-4-XMLs
generiert, aber danach haben sie bezüglich avr-libc (und beispiels-
weise Schreibfehlern oder Registerumbenennungen) ggf. ebenfalls ein
"Eigenleben" entwickelt.  Müsste man sich also von Fall zu Fall
ansehen, ob das sich ergebende automatisch generierte File abzüglich
der hinzugefügten Registerstrukturen dann noch mit der existierenden
Version übereinstimmt.

Die, die Atmel Studio mit ausliefert, sind dem Vernehmen nach bei
Atmel aus dem Studio-5/6-XML generiert, aber seit dem Weggang von
Anitha Boyapathi von Atmel ist die Zusammenarbeit der zuständigen
Atmel-Entwickler mit dem avr-libc-Team leider praktisch vollständig
abgerissen. :-(

von Karl H. (kbuchegg)


Lesenswert?

Jörg Wunsch schrieb:

> Kurz und gut: für all diese gäbe es keine Chance, sie einfach mit
> wenig Aufwand neu zu generieren.

Ich hab jetzt auch nicht daran gedacht, die bisherigen Header über Bord 
zu werfen. Die könnten ruhig weiter bestehen.

Ich hab mir das eher so vorgestellt, dass es ein weiteres Include File 
gibt, in dem die Register in Bitstrukturen aufgeschlüsselt werden.
Eventuell wäre es sogar möglich, zb für Timer sowas wie gefakte 
Strukturen zu bauen, so dass man schreiben kann

    _Timer1_CS00 = 1;

und durch die Definitionen schlüsselt sich das selber in die richtigen 
Register auf. Die entsprechenden Informationen wären in den XML 
vorhanden, was ich so gesehen habe.

Ein Mechanismus, ähnlich dem io.h sorgt dann für die entsprechende 
Verteilung des Include auf den konkreten Register Header.
1
#include <avr/io.h>
2
#include <avr/io_reg.h>
3
4
5
int main()
6
{
7
  _PortB.b0 = 1;
8
}

> Die, die Atmel Studio mit ausliefert, sind dem Vernehmen nach bei
> Atmel aus dem Studio-5/6-XML generiert, aber seit dem Weggang von
> Anitha Boyapathi von Atmel ist die Zusammenarbeit der zuständigen
> Atmel-Entwickler mit dem avr-libc-Team leider praktisch vollständig
> abgerissen. :-(

Schade.
Denn: Machbar wäre das schon. Nur macht es nur dann wirklich Sinn, wenn 
man es als eine Art Standard etablieren kann. Am besten wär natürlich, 
wenn auch Atmel da mit am Strang ziehen würde.

Was denkt ihr? Würde es Sinn machen, da sowas wie einen µC.Net 
'Standard' zu kreieren und zu versuchen den zumindest hier bzw. auf 
AVRFreaks zu pushen?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Karl Heinz Buchegger schrieb:
> Was denkt ihr? Würde es Sinn machen, da sowas wie einen µC.Net
> 'Standard' zu kreieren und zu versuchen den zumindest hier bzw. auf
> AVRFreaks zu pushen?

Fände ich absolut sinnvoll.

von Klaus (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Da bin ich mir nicht ganz so sicher, denn sie müsste dann den
> dahinter liegenden Mischmasch aus struct-Definition, typecast,
> #define, Zeigerbildung und -dereferenzierung durchschauen.  Ganz
> schön viel Anforderung für ein bisschen IDE.

Wenn ich die Werte aus den struct-Definitionen verwende, geht das. Bei 
allem, was man hinter #defines "versteckt" nicht, da hast du recht. Aber 
um ein komplexes Peripheral zu initialisieren, wo man sich die #defines 
kneift, da es nur einmal im Code vorkommt, ist es hilfreich. Auch beim 
Schreiben der #defines sieht man sofort, ob ein Portbit auch wirklich 
implementiert ist.

Ich spreche aber hier von MPLABX, die AVR-Umgebung kenne ich nicht. Und 
Microchip liefert für wirklich jeden ihrer Chips diese Headerfiles, und 
da das explizit in der Doku des Compilers erwähnt wird, gehe ich davon 
aus, daß das auch für zukünftige Chips so sein wird.

MfG Klaus

von Sascha W. (arno_nyhm)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Was denkt ihr? Würde es Sinn machen, da sowas wie einen µC.Net
> 'Standard' zu kreieren und zu versuchen den zumindest hier bzw. auf
> AVRFreaks zu pushen?

Wäre gut - wenn man sich grundlegend schon die Arbeit einer solchen 
Implementierung macht ist es auch sinnvoll einen Quasistandard zu 
nutzen.

Genau das von Euch näher beschriebene ist ja auch mein Wunsch - eben nur 
mit dem Unterschied, dass ich mir statt der prozessororientierten 
Bennenung der Bits eine funktionale angedacht habe.
Der Unterschied ist ja aber nur gering bzw. eine funktionale Bennung 
ließe sich ja nachträglich noch im jeweiligen Projekt hinzufügen.

Ich fände es jedenfalls parktisch eben über benannte Strukturen auf die 
einzelnen Bits der Prozessorregister zuzugreifen, nicht nur der 
Sicherheit bei der Entwicklung die richtigen Bits im richtigen Register 
zu setzen halber, sondern auch wegen der besseren Lesbarkeit.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Das ganze hat einige Haken und Ösen, sowohl was die Verwendung von 
volatile angeht als auch was die Gnerierung der Header angeht.

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
Noch kein Account? Hier anmelden.