Forum: Mikrocontroller und Digitale Elektronik Zugriff auf einzelne Bits in einem Register


von Thomas (Gast)


Lesenswert?

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

von johnny.m (Gast)


Lesenswert?

> 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)

von AndreasB (Gast)


Lesenswert?

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

von Stephan H. (stephan-)


Lesenswert?

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.

von johnny.m (Gast)


Lesenswert?

> 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.

von Thomas (Gast)


Lesenswert?

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 ?

von Karl heinz B. (kbucheg)


Lesenswert?

Nein, das funktioniert nicht.
Schon mal deshalb nicht, weil du einer Variablen die
der Compiler anlegt, nicht einfach eine andere Adresse
zuweisen kannst.

von Simon K. (simon) Benutzerseite


Lesenswert?

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.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

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

von Thomas (Gast)


Lesenswert?

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

von Simon K. (simon) Benutzerseite


Lesenswert?

@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.

von Thomas O. (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

Mit Macros geht das beim AVR-GCC z.B. so:
1
#define RESET(x)        _XRS(x)
2
#define SET(x)          _XS(x)
3
4
#define _XRS(x,y)       x &= ~(1<<y)
5
#define _XS(x,y)        x |= 1<<y
6
7
#define LED_I2C         PORTD,5
8
9
#define LED_ERR         PORTC,3
10
11
// Code:
12
13
 if( Error )
14
   RESET( LED_ERR );
15
 else
16
   SET( LED_ERR );


Die Verschachtelung ist nötig, damit die Aufteilung in 2 Argumente
keinen Fehler erzeugt (wird nur bei der ersten Macroexpansion
geprüft).


Peter

von Hannes (Gast)


Lesenswert?

@Peter

wenn ich _XRS(LED_ERR) aufrufe, dann ist doch mein y undefiniert!

von Karl H. (kbuchegg)


Lesenswert?

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

von micha (Gast)


Lesenswert?

Ui... kann mir bitte jemand Rufus's Zeile
pRegister = (struct RegisterBitfeld *) ((void *) 0xDEADFACE);
erläutern?

von Karl H. (kbuchegg)


Lesenswert?

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.

von micha (Gast)


Lesenswert?

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.

von micha (Gast)


Lesenswert?

Nachtrag zum PS:
Wohin als auf Speicher sollte auch ein Zeiger zeigen können?

von Karl H. (kbuchegg)


Lesenswert?

> 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 :-)

von Hannes (Gast)


Lesenswert?

@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.

von Karl H. (kbuchegg)


Lesenswert?

> 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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.