mikrocontroller.net

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


Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: AndreasB (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Stephan Henning (stephan-)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ?

Autor: Karl heinz Buchegger (kbucheg)
Datum:

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

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rufus Τ. Firefly (rufus) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Thomas (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Thomas O. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit Macros geht das beim AVR-GCC z.B. so:
#define RESET(x)        _XRS(x)
#define SET(x)          _XS(x)

#define _XRS(x,y)       x &= ~(1<<y)
#define _XS(x,y)        x |= 1<<y

#define LED_I2C         PORTD,5

#define LED_ERR         PORTC,3

// Code:

 if( Error )
   RESET( LED_ERR );
 else
   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

Autor: Hannes (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Peter

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: micha (Gast)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: micha (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: micha (Gast)
Datum:

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Hannes (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.