Hallo nochmal,
ich habe da noch ein kleines Problem.
Gegeben eine 40-Bit-Variable. Von der möchte ich nun z.B. die Bits
0,3,6,14,27,38 in eine neue Variable übernehmen.
Natürlich könnte man das jetzt hin- und hershiften und die Bits mit UND
auslesen. Da ich das ganze aber ca. 20 mal machen muss, stellt sich die
Frage, ob man das nicht irgendwie einfacher bzw. effizienter machen
könnte.
Falls jemand eine Idee hat wäre ich sehr dankbar.
mfg
Michael
Michael K. schrieb:> Hallo nochmal,>> ich habe da noch ein kleines Problem.>> Gegeben eine 40-Bit-Variable. Von der möchte ich nun z.B. die Bits> 0,3,6,14,27,38 in eine neue Variable übernehmen.> Natürlich könnte man das jetzt hin- und hershiften und die Bits mit UND> auslesen.
Da würde ich nicht lange Shiften, sondern ganz einfach abfragen ob das
jeweilige Bits gesetzt ist und wenn ja, dann in der Zielvariablen
ebenfalls das entsprechende Bit setzen.
targe = 0;
if( source & ( 1 << 0 )
target |= 0x01;
if( source & ( 1 << 3 )
target |= 0x02;
if( source & ( 1 << 6 )
target |= 0x04;
Danke. So ist es wirklich einfacher.
Ich nutze jetzt die Gelegenheit einfach mal und frage,
was es mit dieser Notation auf sich hat.
Mit ( 1 << 0 ) schiebe ich eine 1 an das 0. Bit. Kann man sich das so
wie eine imaginäre Vergleichsvariable vorstellen?
mfg
Michael
Michael K. schrieb:> Danke. So ist es wirklich einfacher.
Je nach Prozessor ggf. auch schneller.
> Ich nutze jetzt die Gelegenheit einfach mal und frage,> was es mit dieser Notation auf sich hat.>> Mit ( 1 << 0 ) schiebe ich eine 1 an das 0. Bit.
Quasi. Du hast eine 1 und schiebst sie um 0 Bits. Das ist zwar das
gleiche, als wenn du sie gar nicht schieben würdest, aber paßt von der
Schreibweise her besser zu den anderen Zeilen und mach so den Code
übersichtlicher.
> Kann man sich das so wie eine imaginäre Vergleichsvariable vorstellen?
Es ist eine Maske. Mit "source & ( 1 << 0 )" legst du eine temporäre
Kopie von source an, wo du alle Bits bis auf das nullte gelöscht werden.
Wenn das Ergebnis dann nicht 0 ist, muß das bedeuten, daß in source das
nullte Bit gesetzt ist. Du legst also quasi eine Maske über source, die
dir nur das nullte Bit zeigt und den Rest "verdeckt" (ausmasikert).
Es gibt leider ein kleines Problem:
if( source & ( 1 << 15 ))... funktioniert,
if( source & ( 1 << 20 )) nicht mehr.
source ist ein uint64_t, die Maske aber anscheinend nicht.
Kann es sein, dass ich jetzt einen Typecast brauche? Wenn ja, wo?
Danke!
Michael
Edit: Hab es gefunden, if( source & ((uint64_t)1 << 20 )) funktioniert
:)
Letzte Frage für heute:
ich will Bits, z.B 1101 0000 um 4 Stellen nach rechts schieben (durch 16
teilen): Schreibweise?
t = 4 >> ROW; funktioniert nicht, ebensowenig die gewohnte Schreibweise
aus C++: t = ROW >> 4;
Michael K. schrieb:> Edit: Hab es gefunden, if( source & ((uint64_t)1 << 20 )) funktioniert> :)
Alternativ: if( source & (1ULL << 20 ))
Man benötigt das hier, weil 1 und 20 vom Typ int sind und damit die
Schiebeoperation mit diesem Typ durchgeführt wird. Und da läuft dann das
Ergebnis über. Wenn du für wenigstens einen der beiden Operanden
explizit einen ausreichend großen Typ angibst, wird auch der Shift mit
dem entsprechenden Typ durchgeführt.
Michael K. schrieb:> Letzte Frage für heute:>> ich will Bits, z.B 1101 0000 um 4 Stellen nach rechts schieben (durch 16> teilen): Schreibweise?>> t = 4 >> ROW; funktioniert nicht, ebensowenig die gewohnte Schreibweise> aus C++: t = ROW >> 4;
Wie äußert sich "funktioiniert nicht" in deinem Programm? Die
Fehlerbeschreibung könnte etwas detailierter sein. Die "gewohnte
Schreibweise aus C++" ist richtig.
Michael K. schrieb:> Letzte Frage für heute:>> ich will Bits, z.B 1101 0000 um 4 Stellen nach rechts schieben (durch 16> teilen): Schreibweise?>> t = 4 >> ROW; funktioniert nicht,
logisch. Das ist ja auch eine ganz andere Operation.
Zwischen 25/16 und 16/25 besteht nun mal ein Unterschied im
Zahlenwert des Ergebnisses.
> ebensowenig die gewohnte Schreibweise> aus C++: t = ROW >> 4;
Wenn du durch 16 teilen willst, dann wäre die gewohnte Schreibweise
t = ROW / 16;
und sofern deine Operation am besten umgangssprachlich mit dem Terminus
"dividiert" beschrieben wird, solltst du das auch so verwenden. Wenn
diese Division mittels einer Shift Operation implementiert werden kann,
dann wird der Compiler das auch mittels Shift machen.
Michael K. schrieb:> Es gibt leider ein kleines Problem:>> if( source & ( 1 << 15 ))... funktioniert,>> if( source & ( 1 << 20 )) nicht mehr.>> source ist ein uint64_t, die Maske aber anscheinend nicht.> Kann es sein, dass ich jetzt einen Typecast brauche? Wenn ja, wo?
Die 1 ist ein Integer (der Standardtyp halt). Wenn du etwas anderes
möchtest, kannst du das auch durch Suffixe angeben:
L für long
U für unsigned
LL für long long
Man kann auch das U mit den anderen Kombinieren:
UL für unsigned long
ULL für unsigned long long
Bei dir wäre das dann (source & ( 1ULL << 20 ))
Du kannst für die Suffixe auch Kleinbuchstaben nehmen.
Rolf Magnus schrieb:> Alternativ: if( source & (1ULL << 20 ))> Man benötigt das hier, weil 1 und 20 vom Typ int sind und damit die> Schiebeoperation mit diesem Typ durchgeführt wird. Und da läuft dann das> Ergebnis über. Wenn du für wenigstens einen der beiden Operanden> explizit einen ausreichend großen Typ angibst, wird auch der Shift mit> dem entsprechenden Typ durchgeführt.
Stimmt nicht ganz. Eine Vergrößerung der 20 würde nicht helfen. Bei den
Schiebeoperatoren findet keine Größenangleichung der Typen der beiden
Operanden statt.
Michael K. schrieb:> source ist ein uint64_t
Wenn Du den AVR-GCC benutzt, schließen sich Effizienz und 64 Bit
gegenseitig aus.
64 Bit ist sogar noch langsamer und größer, als float.
Zerlege die 64 Bit in 2 * 32 Bit und Dein Programm läuft mindestens
10-mal schneller.
Peter
>> Ergebnis: NOT IN SCOPE
Schalte den Optimizer aus. Der hat schlicht und ergreifend deine
Variable t wegoptimiert
> Das mit der Aufteilung in 2 x 32 Bit klingt interessant.
Ist auch (zumindest für mich) übersichtlicher, als wie wenn man da
ständig mit so großen Zahlen hantieren muss.
Michael K. schrieb:> Die Optimierung steht bereits auf -Oo, leider mit dem selben Ergebnis.
Ausnahmsweise die Variable mal zum Testen 'volatile' setzen.
//Vergleichsvektor f. Spaltenparität DPCOL anlegen
30
31
uint8_tDPCOL=0;
32
33
if(Daten&(1<<4))
34
DPCOL|=0x01;
35
if(Daten&(1<<3))
36
DPCOL|=0x02;
37
if(Daten&(1<<2))
38
DPCOL|=0x04;
39
if(Daten&(1<<1))
40
DPCOL|=0x08;
41
42
//Vektoren für berechnete Parität anlegen
43
uint16_tPROW=0;
44
uint8_tPCOL=0;
45
uint64_tROW=0,t=0;
46
47
//PR8 berechnen
48
ROW=Daten&0x7800;//ROW8
49
t=ROW>>1;//DAS HIER FUNKTIONIERT NICHT
50
51
t=t^3;//Dummykrempel wg. Optimierung
52
if(ROW==0x73FCA0000)
53
if(t==0x31)
54
return0;
55
return1;
56
};
Die Funktion wird in main mit
1
valid=CheckParity(Data);
aufgerufen.
Also der Optimierer macht einem das debuggen echt nicht leicht,
wenngleich hier anscheinend ein anderes Problem vorliegt (wie gesagt -
Opt. steht auf -O0)
Vielen Dank nochmal für die Hilfe!
mfg
Michael
Michael K. schrieb:> Hier nochmal die komplette Funktion:
Du magst mich blind nennen, aber ich kann da nicht erkennen, dass t
innerhalb der Funktion eine lokale Variable wäre.
Habe das Problem entdeckt.
Wenn ich die Zeile mit "Step into" ausführe, geht es nicht.
Mit "Step over" funktioniert es.
Weiß jemand wieso das so ist?
mfg
Michael
Peter Dannegger schrieb:> Michael K. schrieb:>> source ist ein uint64_t>> Wenn Du den AVR-GCC benutzt, schließen sich Effizienz und 64 Bit> gegenseitig aus.> 64 Bit ist sogar noch langsamer und größer, als float.>> Zerlege die 64 Bit in 2 * 32 Bit und Dein Programm läuft mindestens> 10-mal schneller.
Wenn es C-Typen mit 512 Bits gäbe — was auf einem 64-Bit System einem
64-Bit Typ auf AVR entspricht — glaub mir, auch die würden verwendet
werden...
Also irgendwie mag mich gcc nicht sonderlich :)
ich rufe die Funktion
1
externvoidmyfkt(uint8_tvec)
2
{
3
vec=(vec*3)^2;
4
if(vec==0xFF)
5
res=1;
6
return;
7
};
z.b. folgendermaßen (vereinfacht) aus einer anderen Funktion auf:
1
uint8_tres;
2
res=7;
3
myfkt(res);
und bekomme die angezeigten Werte.
Der Wert ändert sich bei der Übergabe, obwohl beide den selben Datentyp
haben.
Also zur Zeit ist echt irgendwie der Wurm drin. Was mache ich nur
falsch? Und was bedeuten die Zeichen, z.B. '-' hinter einer Zahl im
Watch?
Danke für die Geduld mit mir.
mfg
Michael
Michael K. schrieb:
Ich hab stark den Eindruck, du versuchst eine Programmiersprache
mithilfe von Debugger und Versuch+Irrtum zu erlernen.
Die Frustrationsgrenze bei dem Ansatz ist nicht fern...
Es stimmt, ich bin noch ziemlich am Anfang was C angeht. Insbesondere
die Eigenheiten bei der Hardwareprogrammierung (also so Sachen, dass
Codeteile, die ich zum Testen einbaue, einfach wegoptimiert werden oder
die Sache mit dem nötigen Typecast) machen mir noch Probleme.
Nichtsdestotrotz weiß ich nicht was bei dem obigen Funktionsaufruf
falsch sein soll. Bei anderen Funktionen geht das ohne Probleme, hier
plötzlich nicht mehr. Das ist es auch, was mich irgendwie etwas
frustriert, es scheint irgendwie willkürlich zu sein.
Ich meine ich könnte natürlich alles global machen. Aber es stört mich
immer gewaltig, wenn ich etwas nicht verstanden habe. Deshalb reite ich
dann auch so lange darauf rum.
Michael
Johann L. schrieb:> Wenn es C-Typen mit 512 Bits gäbe — was auf einem 64-Bit System einem> 64-Bit Typ auf AVR entspricht — glaub mir, auch die würden verwendet> werden...
Was aber überhaupt nichts mit meiner Aussage zu tun hat, daß auf dem
AVR-GCC die 64Bit grottenschlecht implementiert sind.
Wenn man performante 64Bit haben will, muß man zu einem
kostenpflichtigen AVR-Compiler greifen, z.B. IAR.
Peter
Ich weiß nicht, ob überhaupt jemand wegen der schlechten Implementierung
die 64Bit ernsthaft verwendet.
Es könnte daher gut sein, daß die Probleme des OP nicht an seinen
C-Kenntnissen liegen, sondern daß da einige Bugs drin sind.
Ich hätte sie mal auf dem ATtiny2313 benötigt, aber da haben die 5kB
64Bit-Lib nicht reingepaßt.
Ich habs dann in Assembler gelöst.
Peter
Peter Dannegger schrieb:>> Wenn man performante 64Bit haben will, muß man zu einem> kostenpflichtigen AVR-Compiler greifen, z.B. IAR.
GCC (und auch avr-gcc) sind freie Software und jeder der wirklich
will, kann dazu beitragen. Solche Features werden idR von demjenigen
imlementiert/beigesteuert, dem es am wichtigsten ist. Im Umkehrschluß
folgt daraus, daß es niemandem wichtig genug ist.
Wenn man all die Zeit und Beiträge zusammenzählt, in denen darüber
lamentiert wurde, hätte man bestimmt schon mindestens 10 long long
Implementierungen für avr-gcc ;-)
Peter Dannegger schrieb:> Ich weiß nicht, ob überhaupt jemand wegen der schlechten Implementierung> die 64Bit ernsthaft verwendet.>> Es könnte daher gut sein, daß die Probleme des OP nicht an seinen> C-Kenntnissen liegen, sondern daß da einige Bugs drin sind.
Die Logik erschliesst sich mir nicht. Es gibt keine 64-Bit
Implementierung in avr-gcc (ausser Shifts ab 4.7). Alles, was mit long
long zu tun hat, wird vom maschinen*un*abhängigen Teil von GCC
verarbeitet, und der ist weit besser getestet als der AVR-Teil.
Was den OP angeht, so ist res im Schnipsel
1
uint8_t res;
2
res = 7;
3
myfkt (res);
eine lokale Variable, während sie in
1
void myfkt (uint8_t vec)
2
{
3
vec = (vec*3)^2;
4
if (vec == 0xFF)
5
res = 1;
6
}
offenbar global ist (avr-gcc unterstützt keine lokalen Funktionen).
Zudem dünkt mir, daß ^2 Quadrieren darstellen soll und nicht
Exclusiv-Oder?
Danke. Zunächst mal ist der Inhalt der Funktion willkürlich (ich wollte
nur sicherstellen dass es nicht wieder wegoptimiert wird).
Wieso ist vec global? So wie ich das sehe existiert sie nur in myfkt.
Kannst Du die Sache mit den nichtunterstützten lokalen Funktionen
genauer erklären? Wäre sehr daran interessiert!
mfg
Michael
MichaelK schrieb:> Danke. Zunächst mal ist der Inhalt der Funktion willkürlich (ich wollte> nur sicherstellen dass es nicht wieder wegoptimiert wird).
Wenn ich mir die Funktion anschaue und den Wert von vec, dann sehe ich,
daß res in myfkt nicht verändert wird. Und wenn ich das sehe, kann es
ein Compiler prinzipiell auch :-)
Was die nervigen Optimierungen angeht, ist bestimmt auch
Compilerfehler eine interessante Lektüre für dich.
Johann L. schrieb:> Solche Features werden idR von demjenigen> imlementiert/beigesteuert, dem es am wichtigsten ist. Im Umkehrschluß> folgt daraus, daß es niemandem wichtig genug ist.
Nö, damit hat es überhaupt garnichts zu tun.
Es gibt eben nur wenige, die wissen, wie man einen Compiler compiliert.
Den Assemblercode für die 4 Grundrechenarten 64bittig hinzuschreiben,
ist Pippifax, das schreib ich Dir schnell eben mal hin.
Aber wie man sowas in einen Compiler reinpfrimelt, da guck ich wie ein
Schwein ins Uhrwerk.
Peter
Also es funktioniert nun. War tatsächlich (wieder) ein Problem mit der
Optimierung. Gibt es denn keine Möglichkeit, diese zumindest zum Testen
der Funktionen komplett auszuschalten? Selbst mit -O0 wird trotzdem
(wenn wohl auch weniger) optimiert.
Vielen Dank für eure Hilfe!
mfg
Michael
Michael K. schrieb:> War tatsächlich (wieder) ein Problem mit der> Optimierung.
Es gibt tatsächlich weder jetzt noch wieder Probleme mit der
Optimierung. Es gibt nur fehlerhaften Code, die der Optimierer dann
anders versteht, als der Programmierer.
> Selbst mit -O0 wird trotzdem (wenn wohl auch weniger) optimiert.
Schlicht falsch.
Oliver
Peter Dannegger schrieb:> Johann L. schrieb:>> Solche Features werden idR von demjenigen>> imlementiert/beigesteuert, dem es am wichtigsten ist. Im Umkehrschluß>> folgt daraus, daß es niemandem wichtig genug ist.>> Nö, damit hat es überhaupt garnichts zu tun.> Es gibt eben nur wenige, die wissen, wie man einen Compiler compiliert.
Das ist aber kein Geheimnis. Es läßt sich dank Internet leicht
herausfinden.
> Den Assemblercode für die 4 Grundrechenarten 64bittig hinzuschreiben,> ist Pippifax, das schreib ich Dir schnell eben mal hin.> Aber wie man sowas in einen Compiler reinpfrimelt, da guck ich wie ein> Schwein ins Uhrwerk.
Gut, das ist nicht ganz einfach. Da muß man schon etwas Zeit
investieren. Aber wie Johann schon schrieb: Wenn du das nicht tust, ist
es dir wohl auch nicht so wichtig. Dann solltest du dich aber nicht
beschweren, daß andere das nicht für dich implementieren.
Rolf Magnus schrieb:> Das ist aber kein Geheimnis. Es läßt sich dank Internet leicht> herausfinden.
Den Eindruck habe ich aber ganz und garnicht.
Wenn ich so sehe, wie komplex die Erstellung einer neuen WINAVR Version
ist, dann sehe ich für mich überhaupt nicht die geringste Chance.
Ich komme ja aus der Hardware-Ecke. Ich hab mal ein paar DOS-Programme
auf dem PC entwickelt (Turbo-Pascal, -C), mehr nicht.
Ich müßte total vom Urschleim anfangen und erstmal Windows-Programme
erstellen lernen.
Rolf Magnus schrieb:> Gut, das ist nicht ganz einfach.
Das ist dann quasi das Kill-Kriterium, wenn es sogar schon für einen
Fachmann wie Dich nicht einfach ist.
Rolf Magnus schrieb:> Da muß man schon etwas Zeit> investieren.
"Etwas Zeit" ist lustig. Ich würde bei meinem Kenntnisstand nicht unter
ein Mannjahr ansetzen.
Peter
Peter Dannegger schrieb:> Ich müßte total vom Urschleim anfangen und erstmal Windows-Programme> erstellen lernen.
Mit Windows hat das wenig zu tun, ausser dass der Compiler auch darunter
läuft. Der GCC Build-Vorgang entstammt einer Unix-Umgebung mit den dort
üblichen und ggf. nach Windows rüber geschleppten Tools. Der Einstieg
ist daher in einer Linux-Umgebung etwas einfacher. Mit der
Standardversion von GCC - also ohne bei AVRs nötige Patch-Orgien - ist
das fast Plug-and-Play.
Programme erstellen musst du dazu ohnehin nicht. Nur in der Lage sein,
den Build-Vorgang fehlerfrei durchziehen zu können. Denn es geht hier
darum, den bestehenden Code zu modifizieren/erweitern und Lib-Code zu
erzeugen. Oder wolltest du gleich den ganzen Compiler mitsamt einer
Windows-GUI neu schreiben?
Peter Dannegger schrieb:> Rolf Magnus schrieb:>> Das ist aber kein Geheimnis. Es läßt sich dank Internet leicht>> herausfinden.>> Den Eindruck habe ich aber ganz und garnicht.> Wenn ich so sehe, wie komplex die Erstellung einer neuen WINAVR Version> ist, dann sehe ich für mich überhaupt nicht die geringste Chance.WinAVR ist wesentlich mehr als ein schlichter Build der avr-Tools: Es
gibt zahlreiche Patches, einen Installer, avrdude, simulavr, Doku,
Beispielprogramme, etc. die man alle für eine Arbeit an avr-gcc nicht
braucht und schon garnicht deren Generierung/Distribution.
> Ich komme ja aus der Hardware-Ecke.
Und ich komme aus der Mathematik/Physik-Ecke. Die Ausrede zählt also
nicht :-)
> Ich müßte total vom Urschleim anfangen und erstmal Windows-Programme> erstellen lernen.
Der Urschleim ist hier eine unixoide Shell, zB unter irgendeinem Linux
nebst Editor. Programme aus der Unix-Welt für MS-Windows zu erzeugen
ist nochmal was anderes, als nativ unter Linux zu arbeiten. Aber das
brauchst du alles nicht; es genügt, unter Linux zu bleiben.
An Editoren gibt es i.W. zwei: Emacs und vi/vim. Vielleicht gibt's auch
Exoten, die was anderes verwenden; ist mir aber noch nicht zu Ohren
gekommen.
> Rolf Magnus schrieb:>> Da muß man schon etwas Zeit investieren.>> "Etwas Zeit" ist lustig. Ich würde bei meinem Kenntnisstand nicht unter> ein Mannjahr ansetzen.
Da gibt's zwei Optionen:
· Die Light-Version: Alles, was in der libgcc gemacht werden kann,
dort implementieren. Das ist in Assembler und kein Problem hinzu-
schreiben. Am eigentlichen Compiler ändert sich nichts.
Arbeitsschritte sind dann
1) Den Lizenzkram abhaken
2) Asm in libgcc hinzufügen
3) Den Build auf die Reihe bekommen
4) Regression-Tests laufen lassen, if error goto 2)
5) Patch machen, Review + Commit
Dies geht für Multiplikation, Division und Modulo, jedoch nicht für
Addition, Subtraktion und Vergleich, die haupt Performance-Bremsen
bleiben also. Aber man lernt schon mal mit dem Monstrum GCC umzugehen.
Ist bereits gemacht für einfache Funktionen wie Shifts, Parity, etc.
· Die Dröhn-Packung:
6) Implementiere a = b für 64-Bit Typen in avr-gcc
7) Rest ist Kinderkram. Was zu aufwändig ist, um inline
auszugeben: siehe oben.
1) ist nervig aber diesen Punkt ist alles andere Makulatur.
Peter Dannegger schrieb:> Johann L. schrieb:>> Solche Features werden idR von demjenigen>> imlementiert/beigesteuert, dem es am wichtigsten ist. Im Umkehrschluß>> folgt daraus, daß es niemandem wichtig genug ist.>> Nö, damit hat es überhaupt garnichts zu tun.> Es gibt eben nur wenige, die wissen, wie man einen Compiler compiliert.
Das ist kein Geheimnis und das Leichteste von allem.
Um ein neues Feature ein GCC zu bekommen gibt es zwei Wege: Man macht's
selber oder beauftragt jemanden, der es tut. Letzteres scheitert wohl
an mancher Firmenphilosophie nach dem Motto: Anstatt einen Entwickler
zur Implementierung von Feature X anzuheuern, bezahle ich lieber das
gleiche Geld von mehreren 1000€ für einen anderen Compiler. Nach dem
Motto: Wenn ich die Entwicklung bezahlte, würden ja auch andere davon
profitieren, und das geht nun wirklich zu weit.
Andererseits scheint selbst Atmel Probleme zu haben, GCC-Entwickler an
Land ziehen zu können. Die gibt's eben nicht an jeder Straßenecke...
> Den Assemblercode für die 4 Grundrechenarten 64bittig hinzuschreiben,> ist Pippifax, das schreib ich Dir schnell eben mal hin.> Aber wie man sowas in einen Compiler reinpfrimelt, da guck ich wie ein> Schwein ins Uhrwerk.
He he, den Spruch kannte ich bisher noch nicht.
Oliver schrieb:> Michael K. schrieb:>>>> Selbst mit -O0 wird trotzdem (wenn wohl auch weniger) optimiert.>> Schlicht falsch.
Einige wenige Dinge werden auch mit -O0 "optimiert", zB Faltung von
Konstanten. Ansonsten wäre eine globale Variable nicht einfach per
1
int a = 40 + 2;
initialisierbar und müsste aufwändig irgendwo im Startup-Code geschehen.
Schon einmal etwas von BitVariablen in C gehört?
struct{
uint_16 Bit1 :1;
uint_16 Bit2 :1;
...
uint_16 Bit10 :1;
uint_16 rest :6;// wichtig sonst meckert der Compiler
}Ergebnisvariable;
if(Quelle & Bitmaske)
Ergebnisvariable.Bitx (x = 1..10) = 1;
else
Ergebnisvariable.Bitx (x = 1..10) = 0;
Könnte man auch als Makro mit dem Fragezeichenoperator schreiben. Da
habe ich aber nicht so den Zugang gefunden.
Die Art hätte den Charme, dass man die Strukturvariable über eine UNION
definiert. Auf der Füllseite bitorientiert zugreifen und auf der anderen
Seite wordorientiert weiterverarbeiten könnte.
Ist übrigens ANSI-C-Standard.
Ist ein Vorsschlag, viel Erfolg
cskulkw schrieb:> Schon einmal etwas von BitVariablen in C gehört?
Schon mal was davon gehört, dass Bitfelder in C weit weniger stark
definiert sind, als die meisten Programmierer gemeinhin annehmen?
Zum Beispiel ist m.W. nicht definiert in welcher Reihenfolge der
COmpiler die Bits anordnen muss. Fängt er beim LSB an oder beim MSB?
Muss er die Bits in derselben Reihenfolge in die Bytes packen in der du
sie angegeben hast? Was ist das geforderte Verhalten, wenn ein Bitfeld
über eine Bytegrenze geht, wie hat sich der Compiler dann zu verhalten,
welche Möglichkeiten hat er?
Wenn du bei deiner Aufgabenstellung dich dann mal fragst: Ist eine oder
mehrere der obigen Fragen (und noch ein paar andere, die mir momentan
nicht einfallen) in meiner konkreten Aufgabenstellung wichtig (wie es zb
beim Einlesen von Daten von einem File ist, die dann auf Bitebene
auseinanderklamüsert werden müssen) und du beantwortest dir diese Frage
mit "ja, ist bei mir wichtig", dann solltest du von Bitfeldern Abstand
nehmen. Selbst dann, wenn es mit deiner jetzigen Compilerversion
funktioniert. Mit der nächsten Compilerversion oder gar einem ganz
anderen Compiler kann nämlich alles wieder ganz anders sein.
Im Prinzip hast du Recht, aber man muß es auch nicht übertreiben.
Ich bin meistens ein ziemlicher Pedant, wenn es um portables
Programmieren geht.
Aber Bitfummelei in den Registern eines AVR oder jedes anderen MC ist
inhärent komplett unportabel.
Und daß im gcc die Reihenfolge der Bitfelder gedreht wird, werde ich
nicht mehr erleben (jaja, ist sowieso nicht mehr weit bis dahin,
trotzdem).
Das ganze Gefummel nicht mehr über den ganzen Quelltext zu verstreuen,
sondern einmal Bitfelder zu definieren, und dann mit z.B.
Bits setzen und löschen zu können, finde ich schon wesentlich lesbarer
und fehlerärmer als mit manuellem Bitschieben und & und |:
1
#define PORT_FARBE (PORTA)
2
#define DDR_FARBE (DDRA)
3
#define PIN_ROT (4)
4
#define PIN_GRUEN (2)
5
#define PIN_BLAU (0)
6
7
// Richtung setzen der Bits in Port A:
8
DDR_FARBE|=0b11<<PIN_ROT;
9
DDR_FARBE|=0b11<<PIN_GRUEN;
10
DDR_FARBE|=0b11<<PIN_BLAU;
11
12
// Ausgabe von Werten in Port A:
13
PORT_FARBE&=~(0b11<<PIN_ROT);// alten Wert zu 0
14
PORT_FARBE|=0<<PIN_ROT;// neue Bits setzen
15
PORT_FARBE&=~(0b11<<PIN_GRUEN);// alten Wert zu 0
16
PORT_FARBE|=2<<PIN_GRUEN;// neue Bits setzen
17
PORT_FARBE&=~(0b11<<PIN_BLAU);// alten Wert zu 0
18
PORT_FARBE|=3<<PIN_BLAU;// neue Bits setzen
Das Gefummel mit &= und |= macht man nur, wenn man es so gewohnt ist.
Elegant ist es wohl kaum.
In letzterem Beispiel findet man ja kaum noch die Werte 0, 2 und 3, die
man setzen will, vor lauter Müll drumrum.
Und selbst wenn ich aus irgendwelchen Gründen solange lebe, daß ich die
Bitfelder noch drehen muß: dann ändere ich im Programmkopf die
Definitionen der Bitfelder, und fertig.
Der ganze restliche Quelltext bleibt unverändert und lesbar.
Bis dahin hätte ich mich schon mehrfach mit der Bitschieberei und den &
und | verfranzt.
Das ist wie mit Braunkohl und Korn: man muß da geboren sein, um es zu
mögen, oder Alkoholiker sein.
Karl Heinz Buchegger schrieb:> ... dann solltest du von Bitfeldern Abstand> nehmen. Selbst dann, wenn es mit deiner jetzigen Compilerversion> funktioniert. Mit der nächsten Compilerversion oder gar einem ganz> anderen Compiler kann nämlich alles wieder ganz anders sein.
Wieso sollte sich sowas mit der Compilerversion ändern? Ohne triftigen
Grund wird sich eine ABI nicht "per Magie" ändern, und wenn, wird das
idR verschaltert so daß man die Wahl hat, zB weil neue Derivate zur
Architektur hinzukommen.