Hallo zusammen,
ich == GCC-Anfänger :-))
ich habe ein Byte FLAGS.
Dort speichere ich allerhand Zustände. Nun möchte ich einige Bits
sezten und einige löschen.
Im GCC tutorial und in den entsprechenden Verweisen zur Bitmanipulation
werden immer mal ein oder meherere Bit gesetzt und mal welche gelöscht.
Habe ich auch verstanden.
Bits setzen:
#define BIT2 (2)
.
#define BIT5 (5)
.
.
.
FLAGS |= (1<<BIT5)|(BIT2)
Bits löschen:
FLAGS &= ~((1<<BIT5)|(1<<BIT2))
Soweit klar, muss man nur wissen und verstanden haben...
Wenn ich jetzt BIT5 und BIT1 setzen will UND BIT2 und BIT3 löschen,
(die restlichen sollen bleiben, wie sie sind) was mach ich denn dann??
FLAGS |= (1<<BIT5) | (1<<BIT1)
FLAGS &= ~((1<<BIT3) |(1<<BIT2)) würde gehen.
Das geht doch sicher auch irgentwie in einer Zeile, oder?
dankeschön
AxelR.
So sollte das funktionieren:
FLAGS = (FLAGS | ((1<<BIT5) | (1<<BIT1)) & ~((1<<BIT3) |(1<<BIT2))
(Einfach klammern, wie in der Mathematik...)
Ich finde das allerdings deutlich weniger übersichtlich als die
"ausführliche" Variante. Im compilierten Code ergeben sich damit
sicher auch keine Vorteile.
#define GPRMC (4)
#define GPGGA (5)
case ('A'): // The GPGGA sentence ID contains "A"
//getrennte Umschaltung der Stati
GPS_STATUS &= ~(1<<GPRMC);
d0: 80 91 0a 02 lds r24, 0x020A
d4: 8f 7e andi r24, 0xEF ; 239
d6: 80 93 0a 02 sts 0x020A, r24
GPS_STATUS |= (1<<GPGGA);
da: 80 91 0a 02 lds r24, 0x020A
de: 80 62 ori r24, 0x20 ; 32
e0: 80 93 0a 02 sts 0x020A, r24
// oder als Einzeiler
GPS_STATUS = (GPS_STATUS|(1<<GPGGA) )&(~(1<<GPRMC));
e4: 80 91 0a 02 lds r24, 0x020A
e8: 80 62 ori r24, 0x20 ; 32
ea: 8f 7e andi r24, 0xEF ; 239
ec: 80 93 0a 02 sts 0x020A, r24
return;
f0: 08 95 ret
}
return;
GPS_STATUS musste ich als 'volatile' deklarieren, sonst hat der
Compiler schlicht weg garnichts gemacht.
Das lerne ich noch...
Da ich eh nur zwei Datensatztypen (GGA+RMC) habe, könnte man auch mit
"^=" arbeiten.
Fazit:
Der Einzeiler ist (unwesentlich) kürzer, vielen Dank nochmals.
AxelR.
> GPS_STATUS musste ich als 'volatile' deklarieren, sonst hat der> Compiler schlicht weg garnichts gemacht.
Dann hast du das Ergebnis wohl nicht weiter benutzt.
Wenn es um das gleichzeitig setzen und löschen geht, könntest Du ja auch
mit einer Hilfsvariable arbeiten und zunächst da alles machen und dann
zurückschreiben.
@Werner
naja, soo gleichzeitig muss es nicht... trotzdem Danke!
@Jörg
Ja, ich hatte die Variable noch nicht "in Verwendung". Das stimmt
wohl - so weit war ich da noch nicht. In der Hauptschleife frage ich
nach, ob beide GPS-Datensätze vorliegen, um diese dann verarbeiten zu
können.
Da es nur zwei sind ($GGA und $RMC), waren da noch Bits in der Variable
"GPS_STAUS" frei. Es gibt im Programm zwar eine Variable namens
"sentence_type". Aber die wollte ich sparen und statt dessen BIT 5
und 6 oderso von "GPS_STAUS" mit heranziehen.
Ich werde mir mal die Bitfields (hiess doch so?) ansehen. Damit gehts
vielleicht einfacher. Men "IF" Konstrukt in der Main sieht toll aus!
Passt (fast) garnicht in eine Zeile
if (((GPS_STATUS & (1<<RMC_OK)) == (1<<RMC_OK)) && ((GPS_STATUS &
(1<<GGA_OK))== (1<<GGA_OK)))
Wenn da jemand eine Idee hat... Sollte aber so den Regeln entsprechen.
rmc_ok und gga_ok sind per define festgelegt
#define GGA_OK (2)
#define RMC_OK (3)
Bin noch am lernen. sprintf für Debugausgaben ist ja wohl der Hammer!
Da platzt mein kleiner Mega48 ja sofort aus allen Nähten :-(( schmoll
Dabei wollte ich unterwegs nur Grad, Minuten und sekunden
(Länge/Breite) in eine ganzzahl wandeln und mit der von Zuhause
vergleichen. Das kann ich mir wohl in "C" und mit dem Mega48
abschminken - schade...
Viele Grüße, schönen Dank
AxelR.
Das geht kompakter:
if (((GPS_STATUS & (1<<RMC_OK)) == (1<<RMC_OK)) && ((GPS_STATUS &
(1<<GGA_OK))== (1<<GGA_OK)))
kann auch als
if ((GPS_STATUS & (1<<RMC_OK)) && (GPS_STATUS & (1<<GGA_OK)))
geschrieben werden. Da in beiden Teilausdrücken GPS_STATUS mit nur
einem Bit verANDet wird, genügt es, auf != 0 abzufragen.
Das macht das doch schon ein bisschen lesbarer, oder?
> Ja, ich hatte die Variable noch nicht "in Verwendung".
Dann ist der Compiler wirklich eifrig im Wegoptimieren. volatile kann
dagegen helfen, man sollte nur dran denken, es wieder rauszuwerfen,
wenn der Code fertig ist.
> Ich werde mir mal die Bitfields (hiess doch so?) ansehen. Damit> gehts vielleicht einfacher.
Ja, für derartige Flags nehme ich gern mal bit fields.
> Bin noch am lernen. sprintf für Debugausgaben ist ja wohl der> Hammer! Da platzt mein kleiner Mega48 ja sofort aus allen Nähten> :-((
Debuggen sollte man auch nicht auf einem ATmega48. Fürs Debuggen kann
man nie genug Ressourcen frei haben. Nimm einen ATmega168, der ist
pin- und funktionskompatibel bei deutlich mehr Speicher.
printf() ist halt vom Prinzip her riesig, da die Entscheidung, was von
den Möglichkeiten wirklich benötigt wird, erst zur Laufzeit feststeht.
> Dabei wollte ich unterwegs nur Grad, Minuten und sekunden> (Länge/Breite) in eine ganzzahl wandeln und mit der von Zuhause> vergleichen. Das kann ich mir wohl in "C" und mit dem Mega48> abschminken - schade...
Oder halt mit itoa() arbeiten, da hast du eben nur keine netten Dinge
wir Vornullen etc.
Wenn es reicht, auf !=0 abzufragen, dann könnte man ja auch eine weitere
Definition einführen, wo beide (RMC_OK und GGA_OK) enthalten sind. Dann
allerdings nicht mehr auf !=0 abfragen, sondern auf die Summe beider.
#define GSP_OK (12) //BIT2 und BIT3 zusammen
if ((GPS_STATUS & GPS_OK) == GPS_OK) müsste ja gehen, oder?
Naja - macht jedenfall Spaß.
Obwohl ich besser und schneller kleine Leiterplatten entwerfen kann.
Vlt.kann ich da ja mal jemanden helfen - würde mich freuen!
man kann eben nicht alles können :-))
Ich habe im Makefile jetzt erstmal die minimalistische Printf Variante
eingestellt.
Das brachte nochmal 1K einsparung. Ich könnte die Daten aber auch auf
PORTB ohne Printf ausgeben...
Jetzt werd ich erstmal einen Mega168(wiedermal nur im MLF)
"anfassbar" machen. Habe i.M. keinen anderen zur Hand.
VlG
AxelR.
Hi,
du koenntest auch die Output Funktionen von Peter Danneger aus der
Codesammlumng nehmen. Der Code von PeDa ist fuer C51, aber ich hatte
sie damals auf GCC portiert.
Der Code braucht 900 Byte fuer String, hex, Dezi, Float usw.
Gruß,
Dirk
..Output Funktionen..Codesammlumng..Peter Danneger
finde ich nicht!
ich habe nach "C51", "Output", "PeDa", "float", "printf" usw.
in der Codesammlung gesucht :-(( määäh
> Das geht kompakter:>> if (((GPS_STATUS & (1<<RMC_OK)) == (1<<RMC_OK)) && ((GPS_STATUS &> (1<<GGA_OK))== (1<<GGA_OK)))>> kann auch als>> if ((GPS_STATUS & (1<<RMC_OK)) && (GPS_STATUS & (1<<GGA_OK)))
Oder noch ne Variante:
Wenn man Schreibarbeit sparen möchte, kann man sich oft mit einem
Makro helfen (auch wenn ich nicht so ein Fan von Makros bin, na ja
von C++ halt 'verdorben' )
#define BIT_SET( Where, What ) ( Where & (1<<What) == (1<<What) )
Dann schreibst du
if( BIT_SET( GPS_STATUS, RMC_OK ) &&
BIT_SET( GPS_STATUS, GCA_OK ) )
Wobei ich sagen muss, dass ich solche && oder || verknüpfte
logische Ausdrücke überhaupt gerne auf 2 Zeilen aufteile.
@Dominik
gefunden und kopiert - Danke! Probier ich heute abend aus.
@Karl-Heinz
Makros? intesessant... und hier mal einleuchtend (habe da aber auch
schon ganz übelst komplizierte Sachen gesehen)
Man kann doch nicht einfach so eine neue Zeile anfangen?
Nun, offensichtich doch -
Mega168 im MLF nach elm-chang (kopfüber mit doppeltape auf lochraster)
adaptiert (schwitz) gg
Schönen Gruß
AxelR.
Warum so umständlich?
#define BIT_SET( Where, What ) ( Where & (1<<What) == (1<<What) )
Den Vergleich kann man sich hierbei schenken
#define BIT_SET( Where, What ) ( Where & (1<<What))
da nur ein Bit abgefragt wird.
Ein blöder Compiler macht daraus womöglich einen richtigen Vergleich,
statt einfach auf 0 bzw. Nichtnull zu testen.
Ich würde allerdings bei der Namenswahl auch noch etwas länger
nachdenken, es geht ja nicht darum, ein Bit zu setzen, sondern um zu
testen, ob es gesetzt ist. Das nur am Vertauschen von "bit" und
"set" festzumachen ...
> Warum so umständlich?
:-)
Weil es immer wieder Programmierer gibt, die sich in den
Fuss schiessen.
Im Ernst. Ich hab vor Jahren den Vergleich mal weggelassen.
Prompt hat ein Kollege das hier gebaut (sinngemaess):
int NrBitsSet = BIT_SET( PORTA, 2 ) +
BIT_SET( PORTA, 3 ) +
BIT_SET( PORTA, 4 );
if( NrBitsSet > 1 ) // wenn mindestens 2 von 3 gesetzt sind
> BIT_IS_SET klingt gut
Meist zieht man das IS vor:
IS_BIT_SET
Alle Dinge die mit 'IS' beginnen sind 'Prädikate' (liefern
also eine Ja/Nein Aussage). Ist aber nur so eine Konvention.
> C++ ist sogar noch viel cooler.
Da steh ich voll drauf :-)
> Man kann doch nicht einfach so eine neue Zeile anfangen?> Nun, offensichtich doch -
Klar kann man.
Wenn dein Compiler deinen Source Code in die Finger
kriegt (nach dem Präprozessor), dann ist das erste
was er macht: Er ersetzt den sog. 'Whitespace' durch
genau ein Leerzeichen. Whitespace sind Sequenzen, die
nur aus Leerzeichen, Tabulatoren, Zeilentrenner bestehen.
Oh. Vorher werden, glaub ich, noch Kommentare durch ein Leerzeichen
ersetzt.
d.h. Dein Compiler macht aus dem hier
int i;
int j;
for( i = 0; i < 8; ++i )
j = 5;
das hier:
int i; int j; for( i = 0; i < 8; ++i ) j = 5;
Gibt auch keinen Grund das nicht zu tun. Der Compiler
wird durch die C Grammatik udnd die ';' geleitet
und nicht durch die Zeilentrenner.
Fortran ist da noch konsequenter: Alle Leerzeichen (natuerlich
nicht in Strings) werden entfernt bevor die Anweisung
übersetzt wird.
d.h. man kann in Fortran auch so schreiben:
I N T E G E R * 4 ALPHA
R E A L * 8 BETA
BETA = A L P H A * 2
man kann aber auch so schreiben :-)
INTEGER*4ALPHA
REAL*8BETA
BETA=ALPHA*2