Autor |
Bitmanipulationen bei AVR GCC |
|
|
|
|
BID = 703477
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
|
Ich sitze gerade an meinem ersten ATmega8535 und lese gerade im AVR GCC Tutorial von mikrocontroller.net.
Und schon kommt die erste Frage:
Gibts denn keinen besseren (einfacheren) Weg einzelne Bits zu manipulieren als mit Bitmasken?
Ich stell mir das so ähnlich wie bei den PICs vor, a la:
PORTA.3 = 1;
Danke für die Hilfe.
Grüße
Simon
_________________
Simon
IW3BWH |
|
BID = 703515
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
|
Moin Simon,
Im Gegensatz zu dieser unsäglichen BASIC-Syntax (Port.Bit) sind Bitmasken viel intelligenter, lassen sich damit doch gleichzeitig auch mehrere Bits manipulieren.
Zudem erleichtert es z.B., bestimmte Bitpositionen in Treibern festzulegen, wie z.B.
#define LED 5 /* Led ist an Bit5 angeschlossen */
#define LEDPORT PORTD
/* LED anschalten */
LEDPORT |= 1<<LED;
/* LED ausschalten */
LEDPORT &= ~(1<<LED);
/* LED-Zustand umkehren */
LEDPORT ^= 1<<LED;
Allerdings ist diese Syntax (Port.Led) bei anderen Umgebungen durchaus Standard, da dort die Portverwaltung über Strukturen läuft. Und auf Elemente einer Struktur greift man nunmal mit dem .-Operator zu.
Das kann dann wirklich so aussehen:
PORT.Bit4 = 1;
Dennoch sind Bitmasken allgemein der bessere Weg, weil viel allgemeiner anwendbar.
_________________
|
|
BID = 703528
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
Auf den ersten Blick hats mir erstmal die Augen verdreht. Ich war bisher PIC IDE gewöhnt.
Aber wenn du sagst, dass Bitmasken hier üblich sind werd ich mich wohl daran gewöhnen müssen.
Vielen Dank schonmal
Grüße
Simon
_________________
Simon
IW3BWH
|
BID = 703539
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Hallo Simon,
Ja, es stimmt schon: das sieht sehr kryptisch aus .
Aber Bitmasken werden allgemein genutzt, um Bits gezielt zu manipulieren.
So werden z.B. Ganzzahlen benutzt, um unabhängige Flagbits zu speichern und die Auswertung dieser Bits geschieht mit Bitmasken.
Das kann so aussehen:
/* Wie soll die Datei geöffnet werden? */
#define READ 0
#define WRITE 1
#define APPEND 2
#define BINARY 3
FILE* mein_fopen(char const* filename, int mode)
{
if( mode & (1<<READ) ) dothat;
if( mode & (1<<WRITE)) dothis;
if( mode & (1<<APPEND))...;
if( mode & (1<<BINARY))...;
.
.
.
}
int main(int argc, char** argv)
{
.
.
.
/* Binärdatei zum Lesen und Schreiben öffnen */
FILE* meine_datei = mein_fopen("/home/david/datei", 1<<READ|1<<WRITE|1<<BINARY);
.
.
.
}
OK, das Beispiel hinkt: meist schreibt man statt 1<<1 gleich 0x1. Prinzipiell ist es aber synonym zueinander.
Die Operationen dabei (|,&,^,<<) sind dabei Bitoperationen, die man auch anderweitig nutzt. Wenn dir das zu kryptisch ist, dann kannst du dir ja auch, analog zum AVR-Assembler-Dialekt, folgende Makros definieren:
#define sbi(port,bit) port |= (1<<bit)
#define cbi(port,bit) port &=~(1<<bit)
Wenn man in Treibern (sei es z.B. für ein Display) die IOs völlig frei belegen lassen will, dann kann man sogar so nette Dinge konstruieren wie:
/* Strobe-Leitung: */
#define STROBE PORTD, 5
.
.
.
/* im Treiber */
sbit(STROBE);
cbit(STROBE);
.
.
.
Spätestens dann sind Bitmasken essentiell
_________________
|
BID = 703580
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
Ich werds einfach mal mit den Bitmasken versuchen, und wenn ich zu viele "Fehler" reinhaue kann ich ja immer noch mit dem Makros versuchen.
Danke für den vielen Beispielcode. Werde das in Ruhe durcharbeiten.
Als Hintergrundinfo:
Das aktuelle Projekt mit dem ich in die AVR µC einsteigen will ist ein Sensorbus für eine Wetterstation basierend auf RS-485. Eine Interfaceplatine soll von RS-232 auf RS-485 wandeln. Vor allem gehts um die Steuerung von Read/Write auf dem RS-485 Bus da die RS-232 nur RxD und TxD zur Verfügung stellt.
Der Sensorbus wird dann an eine RS-232 eines Routerboards (Mikrotik) angeschlossen das Teil eines WLAN Netzes für Amateurfunk ist.
Wenn alles so funktioniert wie geplant wird jeder WLAN Umsetzer mit einer Wetterstation ausgestattet.
Abgefragt werden die Sensoren im Bus dann von einem fernen Server der die ganze Datenaufbereitung übernimmt und auf einer Website zur Verfügung stellt. Die Sensoren sollen aber auch leicht getestet und konfiguriert werden können, deshalb ein ASCII Protokoll das auch menschenlesbar ist.
Grüße
Simon
_________________
Simon
IW3BWH
|
BID = 703760
BjörnB Stammposter
Beiträge: 242 Wohnort: Dortmund
|
Hallo Simon,
in der avr-libc gibts bereits ein Makro: #define _BV (bit) (1 << (bit))
Damit kann man das Bitshiften optisch unter _BV "verstecken" und z.B. TCCR2 = _BV(COM20)|_BV(CTC2)|_BV(CS20) statt TCCR2 = (1 << COM20)|(1 << CTC2)|(1 << CS20) schreiben, siehe http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_use_bv
Schöne Grüße,
Björn
|
BID = 703764
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Offtopic :
|
Hallo
Also, diese _BV-Dinger zerstören imho jeden schönen Code...
Dann lieber in Pseudo-ASM-Notation (sbit) oder aber dann makrolos.
Das hebt finde ich auch vielmehr den Charakter hervor: die IO-Ports sind ja im RAM gemappt (PORTD ist ein Symbol, was eigentlich ganz eklig aussieht...) und man manipuliert dabei eigentlich bloß Speicherstellen.
(Der GCC optimiert diese RAM-Zugriffe unterhalb einer bestimmten IO-Adresse hin zu den Opcodes für sbi und cbi.)
Schöne Grüße,
David |
_________________
|
BID = 703956
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
Hallo,
da diese BV Dinges auch nichts anderes als die Bitmaske aufbaut werd ich eher die Bitmaske direkt aufbauen, sonst vergisst man irgendwann noch was dahinter steckt und findet Fehler nicht mehr.
_________________
Simon
IW3BWH
|
BID = 707453
gerhard54 Gelegenheitsposter
Beiträge: 76 Wohnort: Wien
|
Hallo,
ich möchte auch meinen Senf dazugeben ;-)....
Prinzipiell stimme ich allen zu, mit Bit-Masken ist es einfacher und übersichtlicher.
Was mich allerdings etwas stört, ist die Sache mit "1 << Bitx".
Aus der Programmierung für UNIX-Systeme bin ich gewohnt, daß Bits mit ihrem WERT und nicht ihrer POSITION kodiert werden. also nicht:
#define READ 0
#define WRITE 1
#define APPEND 2
#define BINARY 3
und
if( mode & (1<<READ) ) dothat;
if( mode & (1<<WRITE)) dothis;
if( mode & (1<<APPEND))...;
if( mode & (1<<BINARY))...;
sondern:
#define READ 1
#define WRITE 2
#define APPEND 4
#define BINARY 8
und
if( mode & READ ) dothat;
if( mode & WRITE) dothis;
if( mode & APPEND)...;
if( mode & BINARY)...;
Kennt jemand den Grund für das für den AVR nicht so gemacht wird?
Mir kommt die zweite Art einfacher und übersichtlicher vor...
Man könnte dan einfach schreiben:
if(PINA & PIN3)
....
LG
Gerhard
[ Diese Nachricht wurde geändert von: gerhard54 am 12 Aug 2010 21:11 ]
|
BID = 707455
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Hallo Gerhard,
Ja, du hast völlig Recht, nicht nur in Unix macht man diese Klimmzüge nicht.
Dies war eher ein allgemeines, eher unpassendes Beispiel für die Schiebeoperatoren.
Gerade in hardwarenahen Systemen bieten sich diese Operatoren aber an, um beispielsweise kleinere Bibliotheken etwas generischer zu programmieren, Beispiel LCDs:
#define LCD_BIT_E 6
#define LCD_BIT_RW 2
#define LCD_BIT_RS 0
int open_lcd(void)
{
PORT |= 1<<LCD_BIT_E;
.
.
.
}
Natürlich kann man auch statt 1<<LCD_BIT_E PORT |= 0x01; schreiben.
Speziell hier finde ich die Schiebeoperatoren wesentlich praktischer, ist aber alles reine Geschmackssache.
_________________
|
BID = 707462
gerhard54 Gelegenheitsposter
Beiträge: 76 Wohnort: Wien
|
Hallo Wulf,
ganz überzeugt hast Du mich nicht, ich finde
#define LCD_BIT_E 0100
#define LCD_BIT_RW 04
#define LCD_BIT_RS 01
int open_lcd(void)
{
PORT |= LCD_BIT_E;
.
}
einfach übersichtlicher und weniger anfällig für (Tip-)Fehler.
Natürlich hat der, der die Library an eine andere Hardware anpasst etwas mehr Arbeit,
Er muß die "Werte" neu berechnen.
Ich hab' sie daher immer oktal geschrieben, da geht das fast von alleine.
#define PIN0 01
#define PIN2 02
#define PIN3 08
#define PIN4 010
...
Aber das ist Geschmackssache und ich will ja keinen Glaubenskrieg entfachen!
Gerhard
|
BID = 707468
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Moin,
ich bin nicht wulf
Jemand, der sowas schon länger macht, der nutzt lieber direkt Masken ohne Schiebeoperation. Ich z.B. notiere sie direkt in hexadezimaler Form, dort sieht der geübte Blick schon, welche Bits gesetzt sind und welche nicht.
Viele fangen aber mit AVRs an und haben von Nichts eine Ahnung. Die schauen dann ins Datenblatt: "Ah, PortD, Pin 4, toll, und wo trage ich das ein?".
Wie gesagt, beide Ausdrücke sind vom Informationsgehalt praktisch gleichwertig.
Glaubenskrieg haben wir bald genug, da müssen wir hier nicht auch schon anfangen...
_________________
|
BID = 707475
clembra Inventar
Beiträge: 5404 Wohnort: Weeze / Niederrhein
|
Ein Grund für diese Schreibweise dürfte auch sein, dass die Angaben in den AVR-Gerätedefinitionen bereits in der Positionsschreibweise vorgegeben sind - es gibt ja nicht nur die externen Ports. Dann hätte man die Wahl zwischen alle vorgefertigten Definitionen (bzw. jene, die man braucht) neu zu notieren, und zwar für jeden Gerätetyp, oder zwei verschiedene Systeme zu nutzen, und da wird man wohl selber durcheinander kommen. Dann lieber die unschönere Schreibweise, dafür aber schön gleichmäßig. Für den µC ist es so oder so egal, da kommt nur noch das Ergebnis an.
_________________
Reboot oder be root, das ist hier die Frage.
|
BID = 707477
DonComi Inventar
Beiträge: 8605 Wohnort: Amerika
|
Ja, die avr-libc dürfte daran Schuld sein.
Zudem sind die Programmbeispiele in den jeweiligen Datenblättern konsequent mit reinen Schiebeoperatoren geschrieben.
Wie gesagt, es ist fast egal. Viel 'bösere' Fehler werden von Anfängern gemacht, die sich noch nicht sehr auskennen - da wird dann für die Bitposition ein zur Compile-time unbekannter Wert benutzt...
Zudem kann man bei Bismasken verdeutlichen, welchen Gültigkeitsbereich (Wortbreite) die Maske hat:
0x1000
0x0080
0x0002
Ist deutlich als 16bit-Maske erkennbar.
_________________
|
BID = 707502
wulf Schreibmaschine
Beiträge: 2246 Wohnort: Bozen
|
Hallo,
jetzt geb ich auch mal wieder meinen Senf dazu.
Hab jetzt etwas rumprobiert und schreibe die Bitmasken jetzt auch direkt. Am liebsten in Binärer Notation, da erkennt dann jeder was gemeint ist. Leider unterstützt der Compiler das noch nicht so lange und könnte mit älteren Versionen Probleme geben.
Etwas was mir allgemein bei den AVR aufgefallen ist:
Für den Anfänger gibt es sehr viel weniger Fallstricke als z.B. bei den PIC.
Grüße
Simon
_________________
Simon
IW3BWH
|