Hallo,
ich möchte in einem Register eines Mikrocontrollers auf einzelne Bits
des Registers zugreifen und diese setzen. Ich möchte dabei aber auf
unschönes Shiften und Maskieren von Bits verzichten. Ich stelle mir
eher z.B. so etwas vor:
Register_Name.Bit4 =0xFF;
Wie kann man so etwas am besten realisieren (am liebsten mit einem
kleinen Beispiel)? Super, wenn Ihr mir helfen könntet.
Dnake und schönen Gruß
Thomas
> Register_Name.Bit4 =0xFF;
Sicher, dass Du weißt, was Du willst??? Seit wann kann ein einzelnes
Bit 0xFF sein???
Abgesehen davon: Schau mal hier ins AVR-GCC-Tutorial. Da steht eine
nette Möglichkeit drin, wie man so was lösen kann (Stichwort
struct-Variable)
Ohne maskieren oder shiften wird das schwierig, ein Byte ist nunmal die
kleineste adressierbare Einheit. Und kleiner als ein char geht eben
nicht.
Es gibt in Assembler (aber ich glaube du sprichst eher von C) aber
einige die Instruktionen sbi und cbi. Ein Blick ins Instruction set
hilft weiter.
Setzen in C ist doch nicht so schwer, oder?
Register |= (1<<Bitnr)
Und löschen ist auch nicht so schwer:
Register &= ~(1<<Bitnr)
und über das Maskieren musst du dir nichtmal mehr Gedanken machen.
Gruß Andreas
ist immer vom Chip und Assembler abhängig. Bein 8051 kann man zB. Set
Px.x schreiben. Dann wird bit X im Port x gesetzt.
mit CPL Px.x wird das Bit negiert.
Mann kann SetB Bit Adr. setzt ein Bit an Adresse nehmen.
Man kann einem Bit auch einen "Namen" geben und dann setzten oder
zurücksetzen. zB.
LED EQU P3.1
setb LED ; LED an
clr LED ; LED aus Beides wirkt auf das Pin Port 3.1.
Anstelle von Ports können auch RAM adressen dienen.
> Register_Name.Bit4 =0xFF;
Das deutet eigentlich eindeutig auf C hin (auch wenn die Zuweisung
unsinnig ist).
Hatte grad gesehen, dass die Frage sich nur auf Register bezog. Da geht
das mit ner struct natürlich nicht. Das haut nur bei Variablen hin. In
einer Hinsicht hat AndreasB aber recht: So schlimm ist das mit dem
shiften gar nicht. Und man kann den Code hinterher noch lesen. Und es
ist C-konform, was wichtig sein kann für die Portierbarkeit des Codes.
OK, so wie ich das jetzt verstanden habe, finktioniert das wie folgt:
ich definiere ein struct
struct {
unsigned char bit_1:1;
unsigned char bit_2:1;
unsigned char bit_3:1
...
} x;
anschließend weise ich x der physikalischen Adresse meines Registers
zu, z.B.
#define Reg *((volatile unsigned char *)0x8002300)
Dann bin ich in der Lage mit x.bit_1 = 1 das erste Bit des Registers zu
setzen. Ist das richtig ? Wenn ja, wäre das so eine Lösung, die ich
suche.
Klar funktioniert das auch mit Shiften, aber diese Lösung (vorausgesetz
sie funktioniert...) finde ich schon eleganter und ist auch irgendwie
übersichtlicher.
Wie sieht es mit dem Speicherbedarf aus, entstehen dadurch irgendwelche
Nachteile ?
Nein, das funktioniert nicht.
Schon mal deshalb nicht, weil du einer Variablen die
der Compiler anlegt, nicht einfach eine andere Adresse
zuweisen kannst.
Wie wärs denn einfach mit einem Makro (Setbit,Clearbit) was du dir
selber machst, und was deine von dir als "unschönes Shiften und
Maskieren von Bits" bezeichnete Vorgehensweise weitgehend verdrängt.
Naja, mit einem Pojnter auf eine derartige Struktur lässt sich das schon
hinbekommen:
struct RegisterBitfeld
{
unsigned char bit0 : 1;
unsigned char bit1 : 1;
unsigned char bit2 : 1;
//etc.
};
struct RegisterBitfeld *pRegister;
pRegister = (struct RegisterBitfeld *) ((void *) 0xDEADFACE);
pRegister->bit0 = 1;
pRegister->bit1 = 1;
pRegister->bit2 = 0;
Wie effizient der vom Compiler erzeugte Code in diesem Falle ist, das
gilt es allerdings zu untersuchen.
Da Bitfelder an sich vom Typ int sind, wäre obendrein zu untersuchen,
ob der gegebene Compiler "struct RegisterBitfeld" so interpretiert,
daß
sizeof (struct RegisterBitfeld) == 1
ist. Ist das nicht der Fall, so sollte man weitere Versuche
einstellen.
Oder sich auf überaus erfreuliche Seiteneffekte einstellen ...
Tandaradei, singt der Debugger
Schade, dass das nicht so einfach funktioniert. Aber wie darf man das
Beispiel in AVR-GCC-Tutorial verstehen ?
@Simon Küppers:
Kannst du mir kurz ein Beispiel für ein solches Makro geben (sorry, bin
Anfänger...)
Gruß Thomas
@Rufus: Vielleicht wäre es sogar sinnvoll, eine Union statt struct zu
nehmen. Man könnte dann auch die 8 bit in einem Rutsch schreiben, wenn
man möchte.
ich mach es so
.DSEG ;Reserviert jeweils 1 Byte im SRAM um Register zu sparen
Steuerbyte1: .byte 1
lds temp, Steuerbyte1 ;Lade temp mit SRAM)
sbr temp, 0b00001000 ;Setze Bit 3
sts Steuerbyte1, temp ;Sichere temp im SRAM
dauert zwar ein paar Takte länger aber ich spare mir dadurch einige
Register, früher haben die mir immer nicht gereicht, weil z.b. alleine
für die 8 ADC Kanäle die meisten schon belegt hatte.
Mit der Equ-Möglichkeit kann es manchmal zu Fehlern beim Assemblieren
kommen wenn das in der Includedatei anderst beschrieben ist.
Das auslesen mach ich dann auch durch eine UND-Maskierung
Der springende Punkt ist, dass du eben nicht
das Makro _XRS direkt benutzt. Ambesten vergist du
gleich wieder, dass dieses Makro existiert.
Als Programmierer benutzt du immer nur die Makros
SET und RESET
Wo liegt das Problem?
0xDEADFACE ist zunächst mal einfach nur eine Zahl. Allerdings
in hexadezimaler Schreibweise. Der Rest ist einfach nur
casting.
(void*) 0xDEADFACE
weist den Compiler an, er möge doch bitte so tun, als ob die
Zahl eine Adresse in den Speicher wäre. Das castet also den
int um in einen Pointer. Noch ist keine Aussage darüber gefallen
was man sich denn im Speicher unter der Adresse 0xDEADFACE vorstellen
soll. Liegt dort ein int, oder ein double oder ...
niemand weis es. Bisher ist nur von einem Pointer die Rede.
Aber das aendert sich jetzt:
(struct RegisterBitFeld*) ( ... )
Das sagt jetzt dem Compiler, dass er mal davon ausgehen soll,
dass er an der angegebenen Speicheradresse eine struct
RegisterBitfeld vorfinden wird. Hier wird also eine konkrete
Aussgae darüber getroffen wie die Bytes die an der angegebenen
Adresse zu finden sind zu interpretieren sind.
Wird denn auch an Adresse 0xDeadFace (wohl nen AVR mit ordentlich RAM;-)
ein struct zu finden sein oder zeigt z.B. pRegister->bit0 wildlings auf
Speicher? ;-)
So z.B.:
struct RegisterBitfeld
{
unsigned char bit0 : 1;
unsigned char bit1 : 1;
unsigned char bit2 : 1;
//etc.
};
struct RegisterBitfeld *pRegister;
struct RegisterBitfeld MyRegister; //neu
pRegister = &MyRegister; //
pRegister->bit0 = 1;
pRegister->bit1 = 1;
pRegister->bit2 = 0;
wär ich als nicht so routinierter nicht über ein absturzträchtiges
Beispiel (was mir ja nicht in den Sinn kam) ins Grübeln gekommen...
PS. Wäre Rufus Version gekürzt zu
pRegister = (struct RegisterBitfeld *) 0xDEADFACE;
nicht genausogut? Testcompilierung läuft jedenfalls anstandslos durch.
> pRegister = (struct RegisterBitfeld *) 0xDEADFACE;> nicht genausogut? Testcompilierung läuft jedenfalls anstandslos> durch.
Ja, klar, ginge auch.
Ein cast ist die Anweisung an den Compiler:
"Vergiss jetzt mal dein Typsystem. Ich als Programmierer
sag die jetzt wo's langgeht und du hältst dich da raus.
Wenn ich ich dir sage, dass das so ist, dann ist das so.
Aus, ende, basta!"
-> Ein cast ist eine Waffe!
> Wird denn auch an Adresse 0xDeadFace
:-)
DEADFACE benutzt man in Beispielen wenn man eine Hex-Zahl
braucht, bei der der Wert an sich keine Rolle spielt, sondern
man ein Prinzip illustrieren möchte. Ein anderes Beispiel
wäre zb. foo. foo wird als Funktionsname genommen, wenn man
einfach nur eine Funktion braucht, wobei es auf die Funktion
an sich nicht ankommt. Bsp. Funktionspointer. Die eigentliche
Funktion ist völlig irrelevant. Alles was man braucht ist
irgendeine Funktion:
typedef void (*Fnct)( void );
void foo()
{
}
Fnct FunctionPointer = foo;
0xDEADFACE hat natürlich den Charme ein Wort zu ergeben und
trotzdem eine gültige 32 Bit Zahl zu sein :-)
@Der springende Punkt ist, dass du eben nicht
das Makro _XRS direkt benutzt. Ambesten vergist du
gleich wieder, dass dieses Makro existiert.
Als Programmierer benutzt du immer nur die Makros
SET und RESET!
Ne - ich mag nicht vergessen das es dieses Makro gibt, denn es wird vom
Makro Reset verwendet - und eben da komme ich nicht weiter. Wenn ich
mitteles Reset XRS aufrufe, dann fehlt mir da irgendwo der zweite
Parameter.
> Ne - ich mag nicht vergessen das es dieses Makro gibt
Das sollst du aber.
Alle Makros die mit '_' beginnen, gehen den
Programmierer nichts an :-) '_' ist reserviert
für den Compiler workspace.
> und eben da komme ich nicht weiter
Dann musst du mal nachlesen, nach welchen Regeln
der Präprozessor Makro-Expansionen macht. Vor allen
Dingen nicht vergessen: Makro Expansionen sind reine
Textersetzungen! Sonst steckt da nichts dahinter:
SET( LED_ERR )
expandiert durch das Makro
#define SET(x) _XS(x)
zu
_XS( LED_ERR )
danach werden die Argumente selbst expandiert und LED_ERR ersetzt
_XS( PORTC,3)
somit ist das SET Makro (inkl.Argumente) selbst komplett ersetzt
und es wird versucht ob es weitere Makro-Ersetzungen mit diesem
(Zwischen-)Ergebnis gibt.
Gibt es, _XS ist selbst ein Makro
#define _XS(x,y) x |= 1<<y
wird es angewendet, so ergibt sich
PORTC |= 1 << 3