Hallo!
Ich habe in C/C++ (ATmega328) zum Speichern der Ausgangszustände ein
Bitfeld definiert:
1
structAusgaenge// jeweils 1 Bit fuer Speicherung der Zustaende der Ausgänge
2
{
3
uint16_tA1:1;
4
uint16_tA2:1;
5
uint16_tA3:1;
6
uint16_tA4:1;
7
uint16_tA5:1;
8
uint16_tA6:1;
9
uint16_tA7:1;
10
uint16_tA8:1;
11
uint16_tA9:1;
12
uint16_tA10:1;
13
uint16_tA11:1;
14
uint16_tA12:1;
15
uint16_tA13:1;
16
}
17
Ausgang;
Nun wollte ich folgendes machen:
1
for(uint8_ti=1;i<=13;i++)
2
{
3
Serial.print("A");
4
Serial.print(i);
5
Serial.print(": ");
6
Serial.println(Ausgang.A(i));
Funktioniert aber leider so nicht.
Was mache ich falsch?
Können einzelne Bits in Bitfelder überhaupt über Variablen so
angesprochen werden?
Ich hab mir jetzt so einige Diskussionen über Bitfelder durchgelesen und
das Gefühl bekommen lieber die Finger davon zu lassen und stattdessen
ganz klassich einfach ein ganzes Byte pro Merker zu verwenden.
C hat anscheinend keine vernünftigen "Werkzeuge" um einzelne Bit zu
adressieren. Zumindest habe ich den Eindruck bekommen.
Ist dem so?
Danke!
Gruß
Rick
A13 ist ein Variablenname und damit kein Index A[13]. Selbst wenn es ein
Index wäre, würde er nicht mit runden Klammern angesprochen.
(Es gibt Sprachen, in denen sich Feldnamen als Variable ansprechen
lassen, aber C++ gehört nicht dazu. In Matlab wäre Ausgang.("A13") ein
valider Ausdruck.)
(Für die Variablennamen in struct/Bitfields gilt das Gleiche wie für
Variablennamen in anderen Speicherbereichen.)
Der sinnvolle Anwendungsbereich von Bitfeldern ist sehr begrenzt.
Wahrscheinlich lässt sich Dein Problem auf irgendeine andere Weise
besser lösen.
Zwar könnte man sich eine halblegale Lösung mit einer Union basteln, nur
erfordert die dann die passende Bitmaske, und damit ist der ganze Sinn
des Bitfields dahin. Dann kannst du auch gleich auf dem uint arbeiten.
Da die Zustände der Ausgänge aber doch eh in den PORTn-Registern stehen,
ist der Sinn der ganzen Aktion sowieso fraglich.
Oliver
Oliver S. schrieb:> Da die Zustände der Ausgänge aber doch eh in den PORTn-Registern stehen,> ist der Sinn der ganzen Aktion sowieso fraglich.
Es handelt sich hierbei nicht um die Ausgänge des uC, sondern um
Ausgänge einer Regelung deren Status über den DL-Bus eingelesen wurden.
Rick M. schrieb:> C hat anscheinend keine vernünftigen "Werkzeuge" um einzelne Bit zu> adressieren.
Natürlich nicht!
Aber dein C++ kann das!
z.B. durch überladen des [] Operators.
Die Arduino EEPROM Lib tut ähnliches. Da kannste dir evtl. was
abschauen.
Rick M. schrieb:> Ich habe in C/C++ (ATmega328) zum Speichern der Ausgangszustände ein
C und C++ ist nicht das selbe! Das Serial.print sieht mir nach Arduino
aus, also C++.
Wenn du mit ganzen Bytes arbeiten würdest "unsigned char
A[(13+CHAR_BIT-1)/CHAR_BIT];" könntest du z.B. mit "A[i/CHAR_BIT] &
(1<<(i%CHAR_BIT))" ein bestimmtes Bit abfragen. (hier ist i 0 relativ!).
Von Bitfeldern kann man keine Arrays haben, daher kann man da nicht
einfach so eins per index abfragen. Man könnte dafür aber eine Funktion
mit switch case anlegen:
1
bool get_bit(Ausgang a, int i){
2
switch(i){
3
case 0: return a.A0;
4
case 1: return a.A1;
5
case 2: return a.A2;
6
...
7
}
8
}
Da wir hier C++ haben, könntest du auch den [] und = Operator überladen.
Ich hab schon lange kein C++ mehr gemacht, irgend was in die richtung:
1
class BitRef {
2
private:
3
const int bit;
4
unsigned char*const byte;
5
public:
6
BitRef(unsigned char* b, int i) : byte(b), bit(i) {
Rick M. schrieb:> ganz klassich einfach ein ganzes Byte pro Merker zu verwenden.
Ja, das ist einfach und schnell.
Rick M. schrieb:> C hat anscheinend keine vernünftigen "Werkzeuge" um einzelne Bit zu> adressieren.
Überhaupt kein Problem.
> (a >> i) & 1
Oft ist es günstiger, a & i zu verwenden und dann i zu schieben, vor
allem, wenn man mehrere Bits hintereinander abfragen will. i erfüllt
zwei Aufgaben:
Bitmaske und Schleifenvariable (die nicht gezählt, sondern geschoben
wird).
Wichtig ist ja nur, ob es gleich oder ungleich 0 ist.
for(uint16_t i=1<<13; i; i>>=1){Serial.println(a&i?"1":"0");}
Immer diese blöden Bitfelder und dann immer von Anfängern. Leute, lasst
das einfach. Oder macht es wenigstens wenn ihr euch besser auskennt.
Bitfelder bringen so wenig. Arbeitet mit Bytes. Macht es euch leichter.
Kapselt Funktionalität gut und sauber in Funktionen. Nicht in
Bitfeldern.
Torsten B. schrieb:> Oft ist es günstiger, a & i zu verwenden und dann i zu schieben, vor> allem, wenn man mehrere Bits hintereinander abfragen will. i erfüllt> zwei Aufgaben:
Genau richtig, insbesondere auch weil die AVR keinen Barrelshifter haben
und (1 << i) langsam ist (O(i) Takte). Okay, bei der Ausgabe via
Serialport spielt das keine Rolle, aber an anderer Stelle vielleicht
schon.
In C++ gibt es auch noch std::bitset was ziemlich genau das macht:
Daniel A. schrieb:> Da wir hier C++ haben, könntest du auch den [] und = Operator überladen.
Nur leider liefert der AVR-GCC ja die C++ stdlib nicht mit.
Mal ne doofe frage zu meinem eigenen Code oben ...
Wie müsste ich meine Anfrage für eine KI formulieren, damit ich einen
Code wie den von mir oben geschrieben bekomme?
Gestern gab es einen Thread über KI-Code generieren...
Man kann den Bits dann natürlich auch gleich einen Namen verpassen
Cyblord -. schrieb:> Immer diese blöden Bitfelder und dann immer von Anfängern.
Bitfelder stehen am Anfang des Programmierens. Ohne Bitfelder kann man
den Mikrocontroller nicht programmieren.
Niklas G. schrieb:> Daniel A. schrieb:>> Da wir hier C++ haben, könntest du auch den [] und = Operator überladen.>> Nur leider liefert der AVR-GCC ja die C++ stdlib nicht mit.
Das ist dabei kein Hinderungsgund!
Niklas G. schrieb:> In C++ gibt es auch noch std::bitset
Hier allerdings schon.
Wobei es auch für AVR eine Libstdc++ Implementierung gibt.
Nicht vollständig, aber immerhin.
Georg M. schrieb:> Bitfelder stehen am Anfang des Programmierens. Ohne Bitfelder kann man> den Mikrocontroller nicht programmieren.
Ihm möchte Leute für blöd erklären, da ist ihm jedes Mittel recht.
Man kann das auch stur runterhacken. Nicht schön, keine statische oder
dynamische Erkennung von Fehlern, die Typen sind unzweckmäßig und der
Aufwand (bei mehr Pins) groß.
Aber es läuft erstmal und man kann weitermachen. Und sich später um
Alternativen bemühen. Und wenn man mehrere Ports braucht, wird man
Muster erkennen, die man vereinheitlichen kann.
1
uint16_tGetAusgang(inti)
2
{
3
switch(i)
4
{
5
case1:returnAusgang.A1;
6
case2:returnAusgang.A2;
7
case3:returnAusgang.A3;
8
...
9
case13:returnAusgang.A13;
10
}
11
return0;/* wasimmer der Default sein soll im Fehlerfall */
Georg M. schrieb:> Torsten B. schrieb:>> a & i>> Sorry, das verstehe ich nicht.
Ja, ist ein wenig ungünstig ausgedrückt. Mit i ist hier eine Maske
gemeint.
Beispiel:
Hier wird der Verschiebeoperator mit der Konstante 1 verwendet. Das ist
auf einem AVR wesentlich effizienter als eine Verschiebung mit einer
variablen Anzahl von Bits.
Georg M. schrieb:> Bitfelder stehen am Anfang des Programmierens.
Mitnichten.
> Ohne Bitfelder kann man den Mikrocontroller nicht programmieren.
Man hat in C schon Hardware-nahe programmiert, als es Bitfelder
überhaupt noch nicht gab. Für solche Programmierung wurde C ursprünglich
entwickelt.
Bitfelder wurden nicht für die Abbildung von Hardware in die Sprache
eingebaut, sondern um Daten platzsparend unterzubringen. RAM war teuer,
Disks waren es auch.
Georg M. schrieb:> Was falsch?
Man braucht keine Bitfelder, um Bits zu manipulieren, siehe
Bitmanipulation <-- draufklicken!
Von daher ist Deine Aussage:
> Ohne Bitfelder kann man den Mikrocontroller nicht programmieren.
komplett falsch.
q.e.d.
Ich würde sie auch niemals einem Anfänger empfehlen. Bitfelder sind
höchst inkompatibler Blödsinn.
(prx) A. K. schrieb:> Man hat in C schon Hardware-nahe programmiert, als es Bitfelder> überhaupt noch nicht gab.
Also standen Bits irgendwie ganz chaotisch, ungeordnet im Speicher?
Georg M. schrieb:> Also standen Bits irgendwie ganz chaotisch, ungeordnet im Speicher?
Bitfelder sind eine Compiler-Erweiterung.
Speicher sind i.d.R. byteweise (oder in Vielfachen davon) organisiert.
Die kann auch sehr einfach als Bitmaske ansprechen.
Ansonsten: Troll woanders!
Georg M. schrieb:> (prx) A. K. schrieb:>> Man hat in C schon Hardware-nahe programmiert, als es Bitfelder>> überhaupt noch nicht gab.>> Also standen Bits irgendwie ganz chaotisch, ungeordnet im Speicher?
Man kann mit den Operatoren ~, <<, >>, & und | sämtliche Bitoperationen
machen, die man braucht. Bitfelder, so wie sie in C definiert sind, sind
überhaupt kein Muss, um Bitmanipulationen vorzunehmen.
Bitfelder in C sind - wie es A.K. schon sagte - dazu gedacht,
Informationen kompakt zu speichern. Mit Bitmanipulationen hat das
überhaupt nichts zu tun. Die Brücke zu Bitmanipulationen kann man nur
bauen, indem man (höchst inkompatbile) Annahmen über die verwendete
Hardware (Stichwort: Endianess) macht. Das ist aber niemals im Sinne der
Erfinder der C-Bitfelder gewesen.
Frank M. schrieb:>> Ohne Bitfelder kann man den Mikrocontroller nicht programmieren.>> komplett falsch.>> q.e.d.
Wie programmiert man die Register? Betrachtet man sie als
Zahlen/Variablen, oder als Bitfelder?
"...einzelne Bits oder Gruppen von Bits aneinandergereiht werden"
https://de.wikipedia.org/wiki/Bitfeld
Georg M. schrieb:> Also standen Bits irgendwie ganz chaotisch, ungeordnet im Speicher?
nicht chaotisch. Sondern auf eine von mehrerenmöglichen
spezifizierten Weisen.
"Mehrere mögliche" bedeutet nicht portabel, jederzeit änderbar, nicht
unbedingt so, wie man es braucht, per Compiler-Option auf einmal ganz
anders.
Beispielsweise ist nicht klar, ob A1 im OP das höchst- oder
niederwertigste Bit ist (also Daten-Bit "D0" oder "D15" repräsentiert).
Niklas G. schrieb:> Nein, die sind im C bzw. C++ Standard spezifiziert.
Es gab C in früher Version bereits, als es einen Standard dafür noch
nicht gab, nicht einmal die K&R Erstausgabe.
Siehst Du da Code, wo C-Bitfelder genutzt werden?
> Betrachtet man sie als Zahlen/Variablen, oder als Bitfelder?
Man kann sie als Bitfelder betrachten, ohne C-Bitfelder zu benutzen.
Auch Dein zitierter Wikipedia-Artikel modifiziert mit den C-Bitfeldern
keine Hardware-Register!
Falls Du es immer noch nicht gemerkt hast: Es geht um die Verwendung von
C-Bitfeldern und nicht allgemein um Bitfelder, an denen Du Dich
aufhängst.
Ich hatte mal einen echt doofen Bug mit anonymen Bitfeldern. Bei "struct
{ int : 8; int y : sizeof(int)*CHAR_BIT-8; } x = {0}; ...", könnte man
ja meinen, das wird alles genullt. Das anonyme Bitfeld aber nicht
unbedingt...
Das wäre nur bei statischen Daten ein Bug der Entwicklungsumgebung, weil
nur die komplett genullt werden, Füllbits engeschlossen.
Bitfelder sind eher schwach definiert, was die Sprache angeht. Wer
solche Konstrukte für zuverlässige Abbildung von Hardware nutzen will,
sollte auf Ada umsteigen. Da wurde das von vorneherein genau definiert.
Niklas G. schrieb:> Nein, die sind im C bzw. C++ Standard spezifiziert.
Mag sein. Aber sie sind nicht hardware-gebunden, was mit der o.g.
falschen Aussage impliziert wird.
Wer außer dem (Pre-) Compiler kümmert sich denn um die Bitfelder bzw.
übersetzt sie in Maschinencode?
Als Compiler-Erweiterung sehe ich Funktionen an, die in früheren
Versionen nicht vorhanden waren.
Rahul D. schrieb:> Mag sein. Aber sie sind nicht hardware-gebunden, was mit der o.g.> falschen Aussage impliziert wird.
Was heißt "hardware-gebunden"? Aller Code läuft auf Hardware...
Rahul D. schrieb:> Als Compiler-Erweiterung sehe ich Funktionen an, die in früheren> Versionen nicht vorhanden waren.
Also einen großen Teil der aktuellen C-Versionen? Da ist ja schon
einiges hinzugekommen. Die meisten Leute verstehen unter
Spracherweiterungen Features, welche nicht in der Spezifikation
auftauchen und nur von einzelnen Compilern unterstützt werden.
Georg M. schrieb:> Bitfelder stehen am Anfang des Programmierens. Ohne Bitfelder kann man> den Mikrocontroller nicht programmieren.
Was dein Bild zeigt, ist überhaupt kein Bitfeld. Es ist ein
Prozessorregister.
Ein Register oder eine Speicherzelle wird nicht dadurch zum Bitfeld,
dass man die Bits in einer Dokumentation nummeriert und das Ganze dann
"Bitfeld" nennt.
Rahul D. schrieb:> Mag sein. Aber sie sind nicht hardware-gebunden, was mit der o.g.> falschen Aussage impliziert wird.
Missverständlich. Sie müssen korrekt benutzt werden. Anfänger treffen
hier aber oft Annahmen die so nicht definiert sind.
Bitfelder sind eine Möglichkeit eine 1 Bit Info unter einem Namen zu
speichern.
Bitfelder sind aber kein Ersatz für Bitoperationen auf Bytes, wenn ich
am Ende das ganze Byte brauche.
Rahul D. schrieb:> Wer außer dem (Pre-) Compiler kümmert sich denn um die Bitfelder bzw.> übersetzt sie in Maschinencode?
Der Präprozessor, der mit dem "Pre-" vielleicht gemeint ist,
interessiert sich in keinster Weise für Sprachkonstrukte von C, soweit
diese nicht mit # anfangen. Also auch nicht für Bitfelder.
Meistens ist es besser, auch 1 Bit Infos, einfach unter einem Byte zu
speichern und auf Bitfelder zu verzichten. Es gibt so gut wie keinen
Nachteil. Der gesparte Speicher dürfte heute keine Rolle mehr spielen.
Man kann die Variable ebenfalls benennen, man kann die in Arrays packen,
in Strukturen. Ohne Probleme.
Einfach den Unsinn mit Bitfelder sparen. Vor allem als Anfänger.
Bei Bitfeldern, die Hardware abbilden, sollte man einrechnen, dass man
die Kontrolle über die durchgeführten Zugriffe durch den Prozessor
verliert. Bei Registern, die bei jedem Zugriff irgendwas auslösen, kann
das fatal sein.
Walter T. schrieb:> Daniel A. schrieb:>> könnte man>> ja meinen, das wird alles genullt.>> Könnte man meinen, das erste Feld würde genullt.
Bei C werden, wenn man ein Feld eines Structs initialisiert, alle
anderen mit 0 initialisiert. Padding muss aber nicht zwingend genullt
werden, und - das wusste ich damals nicht - anonyme Bitfelder auch
nicht.
Cyblord -. schrieb:> Meistens ist es besser, auch 1 Bit Infos, einfach unter einem Byte zu> speichern und auf Bitfelder zu verzichten. Es gibt so gut wie keinen> Nachteil. Der gesparte Speicher dürfte heute keine Rolle mehr spielen.
Man hat dann aber verloren, wenn die einzelnen Bits in einer Schleife
verarbeitet werden sollen.
Rick M. schrieb:> Es handelt sich hierbei nicht um die Ausgänge des uC, sondern um> Ausgänge einer Regelung deren Status über den DL-Bus eingelesen wurden.
Ich verstehe den TO auch so, er bekommt irgendwoher einen uint16 und
möchte die einzelnen Bits auswerten. Da hilft es wenig ihm den Tipp zu
geben für die Bits komplette Bytes zu verwenden.
Obelix X. schrieb:> Cyblord -. schrieb:>> Meistens ist es besser, auch 1 Bit Infos, einfach unter einem Byte zu>> speichern und auf Bitfelder zu verzichten. Es gibt so gut wie keinen>> Nachteil. Der gesparte Speicher dürfte heute keine Rolle mehr spielen.>> Man hat dann aber verloren, wenn die einzelnen Bits in einer Schleife> verarbeitet werden sollen.
Wieso das? Du kannst so viele Variablen wie du willst in einem Array
halten und in Schleifen verarbeiten.
Obelix X. schrieb:> Ich verstehe den TO auch so, er bekommt irgendwoher einen uint16 und> möchte die einzelnen Bits auswerten. Da hilft es wenig ihm den Tipp zu> geben für die Bits komplette Bytes zu verwenden.
Naja, "x & (1ul<<i)" usw. funktioniert auch bei uint16_t.
Niklas G. schrieb:> Genau richtig, insbesondere auch weil die AVR keinen Barrelshifter haben
Doch haben sie. Eigentlich alle außer den "klassischen" Tinys.
Ob S. schrieb:> Doch haben sie. Eigentlich alle außer den "klassischen" Tinys.
Der mir bekannte Teil des AVR-Befehlssatzes kennt nur Shift/Rotate um
eine Stelle.
Niklas G. schrieb:> Ob S. schrieb:>> Doch haben sie. Eigentlich alle außer den "klassischen" Tinys.>> Mit welcher Instruktion nutzt man den?
Irgendwie wenig überraschend: Bei allen, die mit "mul" anfangen.
Ob S. schrieb:> Bei allen, die mit "mul" anfangen.
Das spricht einen Hardware-Multplizierer an, keinen Barrel-Shifter.
Hättest Du ein Beispiel für den Ersatz von x>>y durch eine einfache
Multiplikation? Auch bei 1<<x ergibt sich nicht automatisch, dass
Multiplikation und Potenzierung das Gleiche wären.
(prx) A. K. schrieb:> Ob S. schrieb:>> Doch haben sie. Eigentlich alle außer den "klassischen" Tinys.>> Der mir bekannte Teil des AVR-Befehlssatzes kennt nur Shift/Rotate um> eine Stelle.
Ja, das heißt aber nicht, dass es keinen Barrelshifter gäbe.
Man kann übrigens auch unter Verwendung der mul-Instruktion shiften...
OK, lohnt nur bei großen Shifts. So ungefähr ab 5 Bits, wenn ich das
richtig in Erinnerung habe. Irgendwann hatte ich das mal analysiert, ist
aber inzwischen mehr als ein Jahrzehnt her.
(prx) A. K. schrieb:> Ob S. schrieb:>> Bei allen, die mit "mul" anfangen.>> Das spricht einen Hardware-Multplizierer an, keinen Barrel-Shifter.
Heilige Einfalt. Was glaubst du, was ein "Hardware-Multiplizierer" wohl
im Kern sein mag...
Ob S. schrieb:> Heilige Einfalt. Was glaubst du, was ein "Hardware-Multiplizierer" wohl> im Kern sein mag...
Ich habe eine gewisse Vorstellung davon, wie ein kombinatorischer
Multiplizierer funktioniert und ging bisher davon aus, dass das so
implementiert sei. Ein Barrel-Shifter ist nicht dabei. Aber wenn dir
andere Informationen vorliegen: immer raus damit.
Frank M. schrieb:> Falls Du es immer noch nicht gemerkt hast: Es geht um die Verwendung von> C-Bitfeldern und nicht allgemein um Bitfelder, an denen Du Dich> aufhängst.
In der Überschrift steht "Bitfeld" und nicht "C-Bitfeld", und so habe
ich es auch verstanden.
Georg M. schrieb:> In der Überschrift steht "Bitfeld" und nicht "C-Bitfeld", und so habe> ich es auch verstanden.
Im ersten Post ist aber direkt ein C-Bitfeld zu sehen.
Cyblord -. schrieb:> Im ersten Post ist aber direkt ein C-Bitfeld zu sehen.
Ja, Ok.
Ich dachte, man versucht es komplizierter als nötig zu machen, habe also
alles falsch verstanden.
Ob S. schrieb:> Man kann übrigens auch unter Verwendung der mul-Instruktion shiften...>> OK, lohnt nur bei großen Shifts. So ungefähr ab 5 Bits, wenn ich das> richtig in Erinnerung habe.
Ja, das geht mittels eines Zugriffs auf eine 8 Byte große Lookup-Tabelle
mit den Werten {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80} und
einer Multiplikation. Das dürfte auf einem AVR sogar ab einem variablen
Shift um 2 schneller als die Variante mit Schleife sein. Noch schneller
geht es ohne MUL und ohne Schleife, wenn man dafür 8·256=2048 Bytes für
die Lookup-Tabelle opfert.
(prx) A. K. schrieb:> Ob S. schrieb:>> Heilige Einfalt. Was glaubst du, was ein "Hardware-Multiplizierer" wohl>> im Kern sein mag...>> Ich habe eine gewisse Vorstellung davon, wie ein kombinatorischer> Multiplizierer funktioniert und ging bisher davon aus, dass das so> implementiert sei. Ein Barrel-Shifter ist nicht dabei.
Auch meine Vorstellungskraft reicht nicht aus, um in einem halbwegs
sinnvoll implementierten Multiplizierer (egal welcher Architektur) einen
Barrel-Shifter zu erkennen.
Aber vielleicht kann Ob. S. den Multiplizierer seiner Vorstellung
skizzieren und darin den Barrel-Shifter markieren.
Hallo,
ich würde es anders lösen.
Eine 16 Bit Variable für die 13 Bits um die Zustände zu speichern.
enums, Namen für Bitnummer, praktisch dein struct Bitfeld nur in enums.
Funktion zum setzen der enum Bitnummer in der 16 Bit Variable.
Funktion zum löschen der enum Bitnummer in der 16 Bit Variable.
Eine Funktion zum iterieren über die 13 Bit Positionen für Portübergabe
etc..
Wenn man in einer for Schleife viele/alle Bitpositionen vergleichen
möchte, dann fängt man mit shiften nicht immer bei 0 an. Man führt die
aktuelle Bitposition vom Shift für den kompletten Schleifendurchlauf
mit. Damit muss je Schleifendurchlauf immer nur einmal weiter geschiftet
werden.
Damit die Bitschubserei schneller geht, für den Fall das man die
aktuelle Bitpos. in einer Schleife nicht mitführen kann, kann man sich
eine weitere Funktion anlegen, Bitnummer als Eingangsparameter, damit
springt man in ein Switch-Case und bekommt die Wertigkeit des Bits
zurück.
Yalu X. schrieb:> Auch meine Vorstellungskraft reicht nicht aus, um in einem halbwegs> sinnvoll implementierten Multiplizierer (egal welcher Architektur) einen> Barrel-Shifter zu erkennen.
Nunja. Die Tatsache, dass die mul*-Instruktionen der AVR8 zwei und nicht
nur einen Takt kosten, läßt stark vermuten, dass hier eine Kombination
aus einem Barrel-Shifter und kombinatorischer Logik verwendet wurde.
Wär's rein kombinatorisch, sollte nämlich schlicht ein Takt reichen.
Aber das ist natürlich nur ein Indiz, kein wirklicher Beweis. Insofern
gebe ich dir Recht.
Für wirkliche Beweise müßte man an Infos rankommen, die MC kaum
freiwillig rausrücken dürfte.
Arduino F. schrieb:> Bitte!> C++, nicht C.
Übrigens, ist C++ immer die bessere Wahl?
Ich denke, es ist schon ein Unterschied, ob man einen kleinen
Mikrocontroller programmiert (größtenteils Hardware Programmierung) oder
ein Office-Paket.
Georg M. schrieb:> Übrigens, ist C++ immer die bessere Wahl?
Ich persönlich: Denke schon!
Georg M. schrieb:> Ich denke, es ist schon ein Unterschied, ob man einen kleinen> Mikrocontroller programmiert (größtenteils Hardware Programmierung) oder> ein Office-Paket.
Sicherlich!
Ich sehe keine Nachteile für C++ auf µC.
Alexander schrieb:> Die eigentliche Frage war doch nur, ob es möglich ist zur Laufzeit> Variablenbezeichner dynamisch zu konstruieren.
Ja und darauf gibt es zwei Antworten:
1.) Nein, nicht mit solchen Bitfeldern.
2.) Die gesamte Aufgabe löst man anders.
Alexander schrieb:> Die eigentliche Frage war doch nur, ob es möglich ist zur Laufzeit> Variablenbezeichner dynamisch zu konstruieren.
Ein klares nein!
Das können nur Interpretersprachen.
Bei Kompilersprachen gehen die Variabelenbezeichner (meist?) während der
Übersetzung verloren.
Arduino F. schrieb:> Alexander schrieb:>> Die eigentliche Frage war doch nur, ob es möglich ist zur Laufzeit>> Variablenbezeichner dynamisch zu konstruieren.>> Ein klares nein!> Das können nur Interpretersprachen.
Das nennt sich "Reflection" und ist nicht auf Interpretersprachen
beschränkt. Beispiele für kompilierte Sprachen mit Reflection sind Java,
C# (und andere .NET-Sprachen), Go, Object Pascal, Objective C und
natürlich Common Lisp.
Georg M. schrieb:> Ich denke, es ist schon ein Unterschied, ob man einen kleinen> Mikrocontroller programmiert (größtenteils Hardware Programmierung)
Für Bitschubsereien, Konstanten und alles was vorab errechnet werden
kann ist man bei C++ besser aufgehoben. In C teilen sich Präprozessor
und Compiler die Arbeit nicht immer optimal, auch wenn C kontinuierlich
nachzieht.
Allerdings sind unsere C++-Projekte meist aus dem Ruder gelaufen, weil
es unüberschaubare Möglichkeiten gibt. Und da es uns nicht auf ein paar
kB ankommt, sind wir wieder bei C. (Ja, man kann Programmierer zähmen
und sogar einen Klammerstil vorgeben ;-).
Daniel A. schrieb:> Obelix X. schrieb:>> Ich verstehe den TO auch so, er bekommt irgendwoher einen uint16 und>> möchte die einzelnen Bits auswerten. Da hilft es wenig ihm den Tipp zu>> geben für die Bits komplette Bytes zu verwenden.>> Naja, "x & (1ul<<i)" usw. funktioniert auch bei uint16_t.
Hast du den Beitrag gelesen, auf den ich mich beziehe?
Nachdem ich nun erfahren habe, dass Bitfeld nicht nur ein Begriff ist,
sondern dass es unter diesem Ausdruck auch ein konkretes Sprachelement,
eine Struktur gibt, verstehe ich immer noch nicht den praktischen Sinn,
die Nützlichkeit.
Georg M. schrieb:> verstehe ich immer noch nicht den praktischen Sinn, die Nützlichkeit.
Man kann Daten mit weniger als 8bit platzsparend unterbringen und
dennoch recht komfortabel zugreifen, meistens auf Kosten der Laufzeit.
Georg M. schrieb:> verstehe ich immer noch nicht den praktischen Sinn,> die Nützlichkeit.Walter T. schrieb:> Der sinnvolle Anwendungsbereich von Bitfeldern ist sehr begrenzt.
Bitfelder sind dann sinnvoll, wenn Speicherplatz wirklich gespart werden
muss.
Da die Variablen in Funktionen ohnehin nur sehr kurz leben, in
Funktionen also fast nie.
Wenn man Daten mehr oder weniger dauerhaft speichern will, z.B. in einem
EEPROM oder globale Flags, sind Bitfelder teilweise sinnvoll.
Walter T. schrieb:> Wenn man Daten mehr oder weniger dauerhaft speichern will, z.B. in einem> EEPROM oder globale Flags, sind Bitfelder teilweise sinnvoll.
Aber genau hier lauert das Problem. Die Repräsentation von Bitfeldern im
Speicher ist nicht garantiert.
Georg M. schrieb:> verstehe ich immer noch nicht den praktischen Sinn,> die Nützlichkeit.
Wenn du keine ganzen Bytes brauchst, und du willst unbedingt etwas platz
sparen, kannst du damit den Compiler Zeugs zusammenfassen lassen, und
musst das nicht selbst manuell mit Bitshifts machen.
Ich hatte das z.B. mal verwendet, um bei einer Streaming UTF-8
Validierungsfunktion, den Zustand in nur 1 Byte (bzw. sogar nur 7 Bits,
wird aber aufgerundet), zu packen:
https://github.com/Daniel-Abrecht/ml666/blob/41d337313e4178bd2bbd882d6f7fa62c7cba44a6/include/ml666/utils.h#L62-L77https://github.com/Daniel-Abrecht/ml666/blob/41d337313e4178bd2bbd882d6f7fa62c7cba44a6/src/utils.c#L341-L413
Ist sicher nicht besonders Effizient, und ich weiss nicht mehr, warum
ich das in 1 Byte packen wollte. Ich glaube ich wollte das irgendwo in
ein Struct Reinquetschen, wo gerade noch etwas Platz übrig war
(Padding), und wo ich dieses klein halten wollte, weil es sehr oft
verwendet worden wäre.
Cyblord -. schrieb:> Aber genau hier lauert das Problem. Die Repräsentation von Bitfeldern im> Speicher ist nicht garantiert.
Ist in den meisten Fällen nicht schlimm. Das EEPROM wird meist vom
selben Programm gelesen wie vorher geschrieben.
Bei Dateien ist die Serialisierung sowieso komplizierter als eine
einzelne Variable oder so zu dumpen.
Cyblord -. schrieb:> Walter T. schrieb:>> Wenn man Daten mehr oder weniger dauerhaft speichern will, z.B. in einem>> EEPROM oder globale Flags, sind Bitfelder teilweise sinnvoll.>> Aber genau hier lauert das Problem. Die Repräsentation von Bitfeldern im> Speicher ist nicht garantiert.
Nein, bei den beiden geschilderten Problemstellungen ist das kein
Problem. Das Problem faengt erst da an, wenn irgendwer auf die geniale
Idee kommt, irgendwelche Register, hinter denen sich HW verbirgt, mit
sowas "schoener" machen zu wollen. Wo halt die Reihenfolge der Bitfelder
von LSB zu MSB oder umgekehrt nicht mehr wurst ist.
Gruss
WK
Niklas G. schrieb:> Was heißt "hardware-gebunden"? Aller Code läuft auf Hardware...
Guck dir mal den 8051 an. Der hat Bitfelder bzw. Befehle, um Bits in
Register direkt anzusprechen.
(prx) A. K. schrieb:> Der Präprozessor, der mit dem "Pre-" vielleicht gemeint ist,> interessiert sich in keinster Weise für Sprachkonstrukte von C, soweit> diese nicht mit # anfangen. Also auch nicht für Bitfelder.
Deswewgen auch in Klammern.
Cyblord -. schrieb:> Bitfelder sind aber kein Ersatz für Bitoperationen auf Bytes, wenn ich> am Ende das ganze Byte brauche.
Genau. Was der Compiler daraus macht, könnte man auch von Hand
erledigen.
Das ist eine Compiler-Erweiterung, die Komfort schafft.
Wo die definiert ist und wer die umsetzt, ist für Anfänger (und viele
andere) ziemlich egal.
Man sollte schon wissen, was passiert. Es ist ja keine Magie.
Naja, bei Registern sollte das ja kein Problem sein. Die sind sowieso
Platformabhängig, da macht es also nichts, sich auf das zu Verlassen,
was der Compier da an verhalten spezifiziert (zumindest sofern er das
tut). Dann muss man halt wissen, wie der das handhabt, genauso wie man
auch wissen muss, was die Register tun.
Ein grösseres Problem ist, wenn manche meinen, sie könnten die Bitfelder
Platformunabhängig zum Dekodieren von Protokollen nutzen, und es dann
halt auf einer anderen Plattform nicht mehr geht, weil die da halt
anders angeordnet wurden.
Rahul D. schrieb:> Das ist eine Compiler-Erweiterung, die Komfort schafft.
Nein, das ist keine Compiler-Erweiterung. Bitfelder sind teil des C
Standards, es sind nur gewisse Aspekte davon IB.
Daniel A. schrieb:> Ein grösseres Problem ist, wenn manche meinen, sie könnten die Bitfelder> Platformunabhängig zum Dekodieren von Protokollen nutzen, und es dann> halt auf einer anderen Plattform nicht mehr geht, weil die da halt> anders angeordnet wurden.
Protokolle sollten schon eindeutig und nicht von irgendwelchen Endians
o. dergl. abhängig sein.
Und wenn, dann ist die Umsetzung das Problem des Entwicklers /
Programmierers (m/w/d).
Etwas Grundwissen sollte man schon haben oder sich erarbeiten.
> Rahul D. schrieb:>> Das ist eine Compiler-Erweiterung, die Komfort schafft.>> Nein, das ist keine Compiler-Erweiterung. Bitfelder sind teil des C> Standards, es sind nur gewisse Aspekte davon IB.
Der Ur-C-Compiler hatte bzw. der -Standard kannte diese Funktion nicht,
also ist es eine Erweiterung. Die Bitfelder kamen doch erst in "letzter"
Zeit dazu (ca. in den letzten 20 Jahren - bei einer Programmiersprache,
die ca. 50 Jahre alt ist...).
Dass der Standard mit der Zeit erweitert wird, ist ja nichts negatives.
Rahul D. schrieb:> Guck dir mal den 8051 an. Der hat Bitfelder bzw. Befehle, um Bits in> Register direkt anzusprechen.
Das können viele andere Architekturen auch.
Rahul D. schrieb:> Genau. Was der Compiler daraus macht, könnte man auch von Hand erledigen
Das gilt aber für viele Dinge in C. Das (a << b) lässt sich auf
Plattformen wie AVR wie gesagt überhaupt nicht direkt umsetzen sondern
braucht auch eine vom Compiler implementierte Hilfsfunktion. Sind
Bitshifts jetzt auch eine "Compiler-Erweiterung"? Wie ist es mit
16bit-Multiplikation, Division, usw.?
Rahul D. schrieb:> Der Ur-C-Compiler hatte bzw. der -Standard kannte diese Funktion nicht,> also ist es eine Erweiterung.
Aber eine Erweiterung durch den Standard, nicht einen Compiler. Somit
ist der Begriff "Compiler-Erweiterung" ziemlich unpassend. Insbesondere
auch weil dann fast alle Features die man heute so nutzt eine
"Compiler-Erweiterung" wären, z.B. die "+=" Operatoren, Parameter-Typen,
void-FUnktionen usw...
Niklas G. schrieb:> Das gilt aber für viele Dinge in C. Das (a << b) lässt sich auf> Plattformen wie AVR wie gesagt überhaupt nicht direkt umsetzen sondern> braucht auch eine vom Compiler implementierte Hilfsfunktion.
Dann schau dir mal die Befehle LSL, LSR, ROL, ROR, ASR im
AVR-Befehlssatz an.
Pilot P. schrieb:> Dann schau dir mal die Befehle LSL, LSR, ROL, ROR, ASR im> AVR-Befehlssatz an.
Klär mich gerne auf wie man damit direkt (in 1 Instruktion und 1 Takt)
ein "a << b" umsetzt wenn "b" erst zur Laufzeit bekannt ist.
Wird jetzt gerade darüber diskutiert, dass 1 Befehl in einer Hochsprache
nicht 1 Befehl in Assembler und auch nicht 1 Takt in der Ausführung
entspricht?
Walter T. schrieb:> Wird jetzt gerade darüber diskutiert, dass 1 Befehl in einer> Hochsprache> nicht 1 Befehl in Assembler und auch nicht 1 Takt in der Ausführung> entspricht?
Ja, nach Rahuls Logik sind Sprachelemente wie C-Bitfields die nicht
direkt einzelne Assemblerbefehle übersetzt werden können
"Compiler-Erweiterungen". Daher müssen beim AVR auch variable Bitshifts,
16bit-Multiplikationen und Zugriffe auf lokale Stack-Variablen
(erfordern Adressberechnungen) "Compiler-Erweiterungen" sein.
Die Diskussion geht jetzt völlig in die falsche Richtung. Es geht doch
nicht um die Implementation von Sprachelementen. Sondern um die mögliche
Repräsentation von Bitfeldern im Speicher.
Niklas G. schrieb:> Rahuls Logik
Für feststehende Begriffe eigene Bedeutungen unterzuschieben ist keine
Logik. Also braucht man sich damit auch nicht zu beschäftigen.
Mit Leuten mit eigener Semantik ist genauso wie mit einer rauchenden
Raupe sinnvoll zu diskutieren.
Niklas G. schrieb:> Man kann Daten mit weniger als 8bit platzsparend unterbringen und> dennoch recht komfortabel zugreifen, meistens auf Kosten der Laufzeit.
Ich meine, die Wertigkeit eines Bytes bleibt doch immer die gleiche,
ob jetzt nur 3 Bit oder 8 Bit relevant sind. Platz für 8 Bit ist doch
immer gegben. Also so wie auch Integervariablen auf einem 32Bit System
in 4 Bytes gehalten werden.
Gibt es denn noch was kleineres als 1 Bit ?
Dachte immer, das wäre die kleinste Einheit, so wie ich das mal
vor über 35 Jahren gelernt habe :
1
1 Byte = 8 Bit
2
1 KB = 1024 Bytes
3
1 MB = 1024 KB
4
usw.
Jedenfalls kenne ich mal keine Sprache, die < 1 Bit kann.
Speichertechnisch,
wenn auf Datenträger geschrieben wird, sogar nur byteweise.
Klär mich doch mal auf.
Niklas G. schrieb:> Aber eine Erweiterung durch den Standard, nicht einen Compiler. Somit> ist der Begriff "Compiler-Erweiterung" ziemlich unpassend. Insbesondere> auch weil dann fast alle Features die man heute so nutzt eine> "Compiler-Erweiterung" wären, z.B. die "+=" Operatoren, Parameter-Typen,> void-FUnktionen usw...
Habe ich mich auf einen speziellenn Compiler bezogen?
Für die Kortinthenkacker:
Bitfelder sind eine Erweiterung von C-Compilern (im Vergleich zu
früheren Versionen, die dem Standard hinzugefügt wurde).
Zufrieden?
Ob andere Programmiersprachen Bitfelder unterstüzen, weiß ich nicht
(interessiert hier ja auch gar nicht).
Heinz B. schrieb:> Jedenfalls kenne ich mal keine Sprache, die < 1 Bit kann
Hä? Davon habe ich überhaupt nicht geredet. Mit Bitfields kann man z.B.
eine 3bit Variable, eine 4bit Variable und eine 1bit Variable in einem
Byte unterbringen. Genau das was hier von Anfang an diskutiert wurde.
Ich habe nur noch mal auf den Punkt gebracht, dass der Sinn von
C-Bitfields diese Speicherersparnis ist und nichts anderes.
Rahul D. schrieb:> Bitfelder sind eine Erweiterung von C-Compilern (im Vergleich zu> früheren Versionen, die dem Standard hinzugefügt wurde).> Zufrieden?
Sind dann der "+=" Operator, void-Funktionen, Parameter mit Typangabe
auch Erweiterungen für dich?
Niklas G. schrieb:> Sind dann der "+=" Operator, void-Funktionen, Parameter mit Typangabe> auch Erweiterungen für dich?
Nur mal so zur "Logik": Wenn es etwas in einem Standard bisher nicht
gab, das aber im Standard definiert wurde: Wie sollte man das dann
nennen?
Wurde die Programmiersprache dann erweitert?
Wer kümmert sich dann um die Umsetzung dieser Erweiterung im
Maschinencode?
Rahul D. schrieb:> Wie sollte man das dann nennen?
Sprachelement/Feature. Anfangs kann man noch "neu" dazu schreiben, aber
"neu" sind C-Bitfields sicher nicht mehr.
Rahul D. schrieb:> Wer kümmert sich dann um die Umsetzung dieser Erweiterung im> Maschinencode?
Weil alle Sprachfeatures von Compilern umgesetzt werden, auch die die
von Anfang an in der Sprache waren, ist es unlogisch nur bei "neuen"
Features ein "Compiler" voranzustellen. Logischer wäre es, den Ausdruck
"Compiler-Erweiterung" nur für Features zu nutzen, die nur von einem
bestimmten Compiler, aber nicht vom Standard unterstützt werden.
Rahul D. schrieb:> Wenn es etwas in einem Standard bisher nicht> gab, das aber im Standard definiert wurde: Wie sollte man das dann> nennen?
Entweder es gibt etwas im Standard, oder eben nicht. Wenn man es genauer
wissen will, kann man angeben, welchen Standard man meint.
In C89 z.B. sind bitfields schon drin, in C23 sind sie immer noch. Und
ältere Standards gibt es nicht wirklich (Ausser man sieht K&R als
(quasi-)Standard?).
Übrigens, jeder C Standard sagt immer, die älteren sind damit obsolete &
ersetzt. Der C Standard ist also immer der neuste.
Ich persönlich bevorzuge aber C11/C17 gegenüber C23.
Niklas G. schrieb:> Sind dann der "+=" Operator [...] auch Erweiterungen für dich?
Die waren von Anfang an drin, zunächst jedoch mit abweichender Syntax.
Rahul D. schrieb:> Die Bitfelder kamen doch erst in "letzter" Zeit dazu (ca. in den> letzten 20 Jahren - bei einer Programmiersprache, die ca. 50 Jahre alt> ist...).
Da hast du dich etwas verschätzt. Die Bitfelder gab es zwar nicht von
Anfang an, wurden bereits 1978 (also vor 46 Jahren) im K&R-Buch
beschrieben.
Es gab sie somit schon mindestens 11 Jahre vor dem ersten ANSI- und 12
Jahre vor dem ersten ISO-Standard.
Moin,
Yalu X. schrieb:> Es gab sie somit schon mindestens 11 Jahre vor dem ersten ANSI- und 12> Jahre vor dem ersten ISO-Standard.
Und ich bin mir auch ziemlich sicher, dass schon in einer
einsemestrigen, kombinierten Pascal/C-Vorlesung, die ich weit zurueck im
letzten Jahrtausend besuchte, eindringlich davor gewarnt wurde, die zu
verwenden, weil nicht definiert ist, in welcher Reihenfolge/Richtung der
Compiler die verbaut...
Gruss
WK
Das ist ja mein Defizit :
Was ist denn eine 1Bit oder 3Bit Variable ?
Jedenfalls hatte ich im PC-Bereich noch nie was davon gehört.
Mag es vl. im Mikroprozessorbereich geben.
Selbst bei WINDOWS kann man nur byteweise Speicher allocieren.
Allocmem verlangt da die Anzahl Bytes nicht die Anzahl Bits.
Oder schreib mal hier, wie du mit der API 3 Bit Speicher
allocierst.
Yalu X. schrieb:> Da hast du dich etwas verschätzt. Die Bitfelder gab es zwar nicht von> Anfang an, wurden bereits 1978 (also vor 46 Jahren) im K&R-Buch> beschrieben.>> Es gab sie somit schon mindestens 11 Jahre vor dem ersten ANSI- und 12> Jahre vor dem ersten ISO-Standard.
Ok. Mit C programmiere ich erst seit 2005. Danach wurden die Bitfelder
populär (zumindest gab es zu der Zeit und danach hier im Forum diverse
Anfragen zu dem Thema - soweit ich mich richtig erinnere).
Rahul D. schrieb:> Mit C programmiere ich erst seit 2005. Danach wurden die Bitfelder> populär
Das bezweifle ich. Gerade in den 20 Jahren davor war Portabilität im
embedded-Bereich weniger wichtig. Es gab noch kein Internet, wo man
seinen Kram für ganz andere Prozessorfamilien bereitgestellt hat.
Du hast einen neuen Prozessor, ein oder ein paar Dutzend Projekte damit
und entweder vom Compilerhersteller oder nach ein paar Stunden sämtliche
Register-Elemente zugreifbar in der Form Uart.Flags.OERR als einzelnes
Bit. Oder als 5Bit breites Register von B2..B6 um schreiben zu können:
Uart.BRR.PRE=31.
Davor kam eine Stunde blättern im Compilerhandbuch nach Reihenfolge,
padding, etc. So wie Du auch zu Beginn wissen willst, was (-5%3) ergibt
oder sizeof(int).
Bruno V. schrieb:> Das bezweifle ich.
Tu, was du willst. Es sind meine Erfahrungen und Erinnerungen.
> Gerade in den 20 Jahren davor war Portabilität im> embedded-Bereich weniger wichtig. Es gab noch kein Internet, wo man> seinen Kram für ganz andere Prozessorfamilien bereitgestellt hat.
Damals habe ich mir teilweise noch (AVR-)Datenblätter ausgedruckt.
Einen Kommentar zum Rest erspare ich mir, da ich den Sinn deines
Geschrieben nicht verstehe.