Forum: PC-Programmierung Aufgabe Ampelsteuerung Studium


von Robin W. (robin_w831)


Angehängte Dateien:

Lesenswert?

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
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von Rene K. (xdraconix)


Lesenswert?

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!

von Da Baby (Gast)


Lesenswert?

Als ob man nicht schnell im Compiler nachprüfen könnte

von Wilhelm M. (wimalopaan)


Lesenswert?

Soll das C oder C++ sein?

von Frank O. (frank_o)


Lesenswert?

Hier ist die gesamte Problemstellung dargestellt:

https://www.mikrocontroller.net/articles/Statemachine

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


Lesenswert?

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

von Robin W. (robin_w831)


Lesenswert?

Vielen Dank Leute.

Hab es schon verstanden :D

von Tilo R. (joey5337) Benutzerseite


Lesenswert?

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
von Rene K. (xdraconix)


Lesenswert?

Okay Jungs! Danke für eure Aufklärung. Also bleibe ich mal beim Bits 
schieben. :-)

Aber interessant das es so etwas gibt.

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


Lesenswert?

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).

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

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


Lesenswert?

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
von PittyJ (Gast)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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.

von DerEinzigeBernd (Gast)


Lesenswert?

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

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

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


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

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.

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


Lesenswert?

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?

von Wilhelm M. (wimalopaan)


Lesenswert?

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