Hallo Zusammen, ich bin gerade an einer Aufgabe hängengeblieben welche ich nicht verstehe. Es geht um eine Ampelsteuerung. Kann mir jemand erklären wie dass funktioniert? :D Bild im Anhang. Vielen Dank schonmal Grüße Robin
:
Verschoben durch Moderator
Robin W. schrieb: > Kann mir jemand erklären wie dass funktioniert? :D Dein Professor bzw. deine Lehr-Unterlagen? Die Aussage "beginnt immer mit dem LSB" ist nicht allgemeingültig richtig (der C-Standard macht dazu nämlich keine Festlegung), aber für den Kontext der Aufgabe natürlich wichtig: ganz oben im Text steht das niederwertigste Bit, LSB (least significant bit), das hat den Zahlenwert 1. Das nächste bit hat dann den Zahlenwert 2 usw. Für den Rest schau bitte nochmal in deine Unterlagen.
Haaaaalt Stop! Soll das heißen das ich in einem struct schon quasi bitweise arbeiten kann?! o.O Warum lege ich dann immer Bytes an und mach Schiebeoperationen darauf?! Seit wann ist das so? Und warum hat mir das noch nie irgendjemand mal gesagt! Ehrlich jetzt. :-D Oh man ey!
Hier ist die gesamte Problemstellung dargestellt: https://www.mikrocontroller.net/articles/Statemachine
Rene K. schrieb: > Soll das heißen das ich in einem struct schon quasi bitweise arbeiten > kann?! Das ist eine Sonderform eines struct, die heißt bitfield. Und ja, die heißt deshalb so, weil man damit bitweise arbeiten kann. > Warum lege ich dann immer Bytes an und mach Schiebeoperationen darauf?! Weil so ziemlich alles an bitfields "implementation defined" ist, d.h. entsprechender Code ist nicht portabel. Ob
1 | anzeige.farben.gelbX = 1; |
daher in anzeige.ausgangsbelegung die Zahl 0x10 oder 0x02 hinterlässt, kann man nicht garantieren. Für die Aufgabenstellung garantiert das die Anmerkung, dass das LSB zuerst kommt, das erklärt gewissermaßen den tatsächlichen Status des "implementation defined" für die angenommene Implementierung. > Seit wann ist das so? Kann ich nicht genau nachvollziehen, war aber mindestens seit der ersten Auflage des "K&R" ("The C programming language") im Jahr 1978 dabei: https://docs.google.com/viewer?a=v&pid=sites&srcid=ZGVmYXVsdGRvbWFpbnxnanVoYWN8Z3g6NTFiY2ExMDdkYTI5YmRmZA
Rene K. schrieb: > Soll das heißen das ich in einem struct schon quasi bitweise arbeiten > kann?! Ja. Tatsächlich wird der Compiler das dann für dich entsprechend übersetzen. Der Maschinencode wird identisch zu deinen von Hand geshifteten Bits. ABER: Das funktioniert unter einer bestimmten Architektur mit einem bestimmten Compiler. C/C++ garantiert dir nicht das exakte Memory-Layout bei einem struct. Implementierungsabhängig könnte der Compiler die 6 bits auch auf 6 Bytes verteilen. Du arbeitest auf der struct-Abstraktion, um das tatsächliche Layout kümmert sich der Compiler. In der Praxis funktioniert das aber. Zumindest so lange man Compiler oder Architektur nicht wechselt. Genauso der wechselseitige Zugriff auf die beiden Union-Teile. Union sagt, dass die sich den Speicher teilen. Aber nicht wie. Genaugenommen ist es Undefined Behaviour, zuerst die einzelnen Bits zu setzen und dann über den union-member ausgangsbelegung auszulesen oder andersrum. Standardkonform ist ausgangsbelegung nur dann definiert, wenn du vorher ausgangsbelegung beschrieben hast. In der Praxis funktioniert das aber. Wichtig ist, dass man solche Tricks auf die Stellen begrenzt, wo es unbedingt notwendig ist. Z.B. um besser auf einzelne Bits eines Hardware-Registers zugreifen zu können. In Header-Files von Mikrocontrollern findet man sowas z.B. relativ häufig. In generischem, nicht hardwarenahem Code hat sowas meiner Meinung nach nichts zu suchen! Undefined Behaviour hat die "großartige" Eigenschaft, zumindest theoretisch irgendwann in bestehendem, unverändertem und gut getestetem Code Fehler zu erzeugen. Einfach weil in 20 Jahren ein Compiler vielleicht mal auf die Idee kommt, irgendwas, was gemäß Standard ohnehin nicht vorkommen sollte, anders zu interpretieren. Edit: Jörg war schneller...
:
Bearbeitet durch User
Okay Jungs! Danke für eure Aufklärung. Also bleibe ich mal beim Bits schieben. :-) Aber interessant das es so etwas gibt.
Tilo R. schrieb: > Undefined Behaviour Ist das hier aber nicht. Das ist "implementation defined", d.h. eine konkrete Implementierung muss dir auch dokumentieren, wie sie es umgesetzt hat. Ist wirklich eine ganz andere nummer als "undefined", denn da kann dir alles mögliche passieren (bis hin zum kompletten Ignorieren von Code, der scheinbar gar nichts damit zu tun hat).
Jörg W. schrieb: > Tilo R. schrieb: >> Undefined Behaviour > > Ist das hier aber nicht. In C++ ist das UB (Lesen des non-active members). In C ist dies ok. Die Festlegung über das LSB ändert IB hier jedoch nicht zu well-defined. Die anderen Bitpositionen bleiben IB.
Wilhelm M. schrieb: > In C++ ist das UB (Lesen des non-active members). OK, da wir aber von einer Ampelsteuerung und einer Studienaufgabe sprechen, würde ich eh erstmal nicht von C++ ausgehen. Offenbar geht's ja hier um die Grundlagen von C, und das "Lesen" von .ausgangsbelegung war wohl eher die Frage, dass die Studenten ein Verständnis dafür entwickeln, was in der Maschine passiert, wenn man ein Bitfield ändert. (Aber akzeptiert, ich hatte Tilos Antwort nicht zu 100 % gelesen zu nächtlicher Stunde und dadurch den Teil verpasst, dass er auch auf die beiden union member eingegangen ist.)
:
Bearbeitet durch Moderator
Warum kann man den 10-Zeiler aus der Aufgabenstellung nicht einfach mal selbst kompilieren, und dann sehen was passiert. Vielleicht selber mal mit den Bits und Bytes spielen, und sich die entstehenden Muster anschauen. 15 Minuten zuhause probiert, und mehr gelernt als aus den Antworten hier im Forum. Ein Tipp: mit printf() kann man sich Variablen anzeigen lassen.
Jörg W. schrieb: > Wilhelm M. schrieb: >> In C++ ist das UB (Lesen des non-active members). > > OK, da wir aber von einer Ampelsteuerung und einer Studienaufgabe > sprechen, würde ich eh erstmal nicht von C++ ausgehen. Offenbar geht's > ja hier um die Grundlagen von C, und das "Lesen" von .ausgangsbelegung > war wohl eher die Frage, dass die Studenten ein Verständnis dafür > entwickeln, was in der Maschine passiert, wenn man ein Bitfield ändert. Selbst wenn wir hier von C ausgehen (kein UB sofern kein strict-aliasing verletzt wird), hat man IB und man lernt nicht das, was der Aufgabenersteller lehren wollte.
:
Bearbeitet durch User
Wilhelm M. schrieb: > hat man IB und man lernt nicht das, was der Aufgabenersteller lehren > wollte. Doch, denn die Implementierung hat er in der Aufgabenstellung mit definiert. Leute, macht aus ein bissel Bitschubsen für Anfänger doch keine Raketenwissenschaft. Um überhaupt erst einmal Bitfelder zu verstehen, hat die Aufgabe ganz offensichtlich (wie uns der TE bestätigt) genügt. Dass das Ganze zumindest unportabel ist, sollte dabei gleich auch mit klar geworden sein.
PittyJ schrieb: > 15 Minuten zuhause probiert, und mehr gelernt als aus den Antworten hier > im Forum. Muss man noch nicht mal zu Hause ausprobieren: https://www.onlinegdb.com/online_c_compiler
Jörg W. schrieb: > Wilhelm M. schrieb: >> hat man IB und man lernt nicht das, was der Aufgabenersteller lehren >> wollte. > > Doch, denn die Implementierung hat er in der Aufgabenstellung mit > definiert. Nein, hat er nicht: lediglich LSB. Sonst nichts. Das fängt schon damit an, dass die Basistypen für die Bitfeld-Elemente nicht (unsigned) int sind. Das ist an sich schon IB. Dann fehlt die Festlegung, dass hier gelten soll, dass alle Bitfeld-Elemente gepackt liegen sollen. Alles nicht angegeben. > > Leute, macht aus ein bissel Bitschubsen für Anfänger doch keine > Raketenwissenschaft. Das mache ich nicht, jedoch sollte man gerade dem Anfänger nicht auf die falsche Fährte führen (und schon gar nicht als Lehrer). Gerade weil es für den Anfänger ggf. schwierig zu verstehen ist, was UB oder IB oder aliasing oder all die anderen Freiheitsgerade eines Compilers in C/C++ sind. > Um überhaupt erst einmal Bitfelder zu verstehen, > hat die Aufgabe ganz offensichtlich (wie uns der TE bestätigt) genügt. Offensichtlich hat der Aufgabensteller die eigentliche Problematik gar nicht verstanden. Sonst hätte er die Aufgabe so nicht gestellt. Denn Bitfelder als Abstraktion von HW-Registern zu verwenden ist einfach nur falsch. > Dass das Ganze zumindest unportabel ist, sollte dabei gleich auch mit > klar geworden sein. Das hoffe ich doch, hoffentlich auch dem Aufgabensteller.
Wilhelm M. schrieb: > Das fängt schon damit an, dass die Basistypen für die Bitfeld-Elemente > nicht (unsigned) int sind. Wofür ist das hier relevant? > Das ist an sich schon IB. Dann fehlt die > Festlegung, dass hier gelten soll, dass alle Bitfeld-Elemente gepackt > liegen sollen. Ist automatisch, denn alle haben nur die Länge 1. Zumindest im C-Standard darf es dann gar kein Padding geben. (Würde mich wundern, wenn C++ das gestattet.) > Offensichtlich hat der Aufgabensteller die eigentliche Problematik gar > nicht verstanden. Sonst hätte er die Aufgabe so nicht gestellt. Denn > Bitfelder als Abstraktion von HW-Registern zu verwenden ist einfach nur > falsch. Wird aber oft gemacht, bspw. findest du in ganz vielen ARM-Headern sowas. Beispiel gefällig?
1 | typedef union |
2 | {
|
3 | struct
|
4 | {
|
5 | uint32_t ISR:9; /*!< bit: 0.. 8 Exception number */ |
6 | #if (__CORTEX_M != 0x04)
|
7 | uint32_t _reserved0:15; /*!< bit: 9..23 Reserved */ |
8 | #else
|
9 | uint32_t _reserved0:7; /*!< bit: 9..15 Reserved */ |
10 | uint32_t GE:4; /*!< bit: 16..19 Greater than or Equal flags */ |
11 | uint32_t _reserved1:4; /*!< bit: 20..23 Reserved */ |
12 | #endif
|
13 | uint32_t T:1; /*!< bit: 24 Thumb bit (read 0) */ |
14 | uint32_t IT:2; /*!< bit: 25..26 saved IT state (read 0) */ |
15 | uint32_t Q:1; /*!< bit: 27 Saturation condition flag */ |
16 | uint32_t V:1; /*!< bit: 28 Overflow condition code flag */ |
17 | uint32_t C:1; /*!< bit: 29 Carry condition code flag */ |
18 | uint32_t Z:1; /*!< bit: 30 Zero condition code flag */ |
19 | uint32_t N:1; /*!< bit: 31 Negative condition code flag */ |
20 | } b; /*!< Structure used for bit access */ |
21 | uint32_t w; /*!< Type used for word access */ |
22 | } xPSR_Type; |
Aus core_cm4.h.
Robin W. schrieb: > ich bin gerade an einer Aufgabe hängengeblieben welche ich nicht > verstehe. Was verstehst du an der Aufgabe nicht? Die Kommentare bei der Deklaration geben dir den entscheidenden Hinweis und die Lösungen stehen sogar schon dabei.
Wolfgang schrieb: > Was verstehst du an der Aufgabe nicht? Dass du irgendwie drei Tage zu spät gekommen bist für deine großartige Frage?
Jörg W. schrieb: > Ist automatisch, denn alle haben nur die Länge 1. Zumindest im > C-Standard darf es dann gar kein Padding geben. (Würde mich wundern, > wenn C++ das gestattet.) Das ist leider in der Allgemeinheit falsch, allerdings ist es in diesem Spezialfall m.E. in C richtig (nicht in C++): die Bit-Felder werden in einer sog. addressable-storage-unit (ASU) untergebracht. Diese muss nur so groß sein, wie das Bit-Feld, und nicht wie angegeben. Ob angrenzende ASU ohne padding allokiert werden, ist unspezifiziert. In dem speziellen Fall dieser Aufgabe werden 1-Bit Bit-Felder deklariert. Die kleinste dafür mögliche ASU wäre 1-Byte. Da aber nur 6 Bit-Felder deklariert werden, sollten die dann alle in dieser ASU ohne padding untergebracht werden. Also: in dieser speziellen Aufgabe dann wohl kein padding. > Wird aber oft gemacht, bspw. findest du in ganz vielen ARM-Headern > sowas. Weiß ich , macht es aber nicht besser ;-) > Beispiel gefällig? > >
1 | > typedef union |
2 | > { |
3 | > struct |
4 | > { |
5 | > uint32_t ISR:9; /*!< bit: 0.. 8 Exception |
6 | > number */
|
7 | > #if (__CORTEX_M != 0x04) |
8 | > uint32_t _reserved0:15; /*!< bit: 9..23 Reserved |
9 | > */
|
10 | > #else |
11 | > uint32_t _reserved0:7; /*!< bit: 9..15 Reserved |
12 | > */
|
13 | > uint32_t GE:4; /*!< bit: 16..19 Greater than |
14 | > or Equal flags */
|
15 | > uint32_t _reserved1:4; /*!< bit: 20..23 Reserved |
16 | > */
|
17 | > #endif |
18 | > uint32_t T:1; /*!< bit: 24 Thumb bit |
19 | > (read 0) */
|
20 | > uint32_t IT:2; /*!< bit: 25..26 saved IT |
21 | > state (read 0) */
|
22 | > uint32_t Q:1; /*!< bit: 27 Saturation |
23 | > condition flag */
|
24 | > uint32_t V:1; /*!< bit: 28 Overflow |
25 | > condition code flag */
|
26 | > uint32_t C:1; /*!< bit: 29 Carry |
27 | > condition code flag */
|
28 | > uint32_t Z:1; /*!< bit: 30 Zero |
29 | > condition code flag */
|
30 | > uint32_t N:1; /*!< bit: 31 Negative |
31 | > condition code flag */
|
32 | > } b; /*!< Structure used for bit |
33 | > access */
|
34 | > uint32_t w; /*!< Type used for word |
35 | > access */
|
36 | > } xPSR_Type; |
37 | >
|
Hier wäre also möglich, dass für das Bit-Feld ISR eine ASU mit 16-Bit benutzt wird. Mit dem folgenden reserved0 ist dann diese ASU voll. Die nä. ASU für GE kann dann einfach nur 1 Byte sein. Zwischen diesen ASUs darf beliebig padding eingebaut werden (ja. ich weiß, das macht sicher kein ernst zu nehmender Compiler, doch darim geht es ja in diesem Moment nicht, wir sind in der Position des language-lawyers). Dann folgt reserved1, damit wäre dann die zweite ASU voll. Dann beginnt die nä. ASU mit wieder beliebig padding zur zweiten ASU. Die Größe und das Alignment der ASUs ist unspezifiziert (nicht implementaion-defined). Wie man sieht ist also auch in C dieses Konstrukt leider eigentlich unbrauchbar ist. Es sei denn, man beschränkt es auf eine bestimmte Compiler-Art/Version. Das sehe ich aber in den typischen CMSIS-Headern nicht. BTW: in C++ hätte man überhaupt keine Chance, also es wäre alles implementation-defined. Aber es wäre auch UB, weil man entsprechend der Aufgabenstellung gezwungen wäre, auf das nicht-aktive Element der Union zuzugreifen...
Beitrag #7335542 wurde vom Autor gelöscht.
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.