mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik C -> Port Bitset


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Peter K. (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

stehe gerade auf dem Schlauch.
Ich habe mehrere Leuchtdioden.
Diese würde ich gerne so..
Led.Green = 1 // Diode an
Led.Blue = 0 // Diode aus
Ist das überhaupt ohne Makros möglich?

von Guest (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Könnte mit einem Union und einem Bitfield gehen wenn das ganze eine 
Zeiger auf den Port ist. Bin mir aber nicht ganz sicher.

von Anselm (Gast)


Bewertung
0 lesenswert
nicht lesenswert
SETBIT(LED,GREEN) hiesse es bei mir, Makro dazu:
#define SETBIT(REG, PIN) ((REG) |= (uint32_t) 1 << (PIN))
#define CLEARBIT(REG, PIN) ((REG) &= ~((uint32_t) 1 << (PIN)))
#define CHANGEBIT(REG, PIN, WERT) ((WERT) ? SETBIT((REG), (PIN)) : CLEARBIT((REG), (PIN)))
#define READBIT(REG, PIN) (((REG) >> (PIN)) & 1)
LED ist dann dein Ausgangpin und GREEN die Nummer (alles in den defines 
zu erledigen)

von Guest (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Wollte er nicht wissen ob es ohne Makros möglich ist :D

von Peter D. (peda)


Bewertung
0 lesenswert
nicht lesenswert
Mit . ist das vermutlich ne ganze Menge Schreibarbeit.
Wenns auch ohne . sein darf, gibt es hier schon fertige Macros:

Beitrag "Re: Port als Variable"

von Wilhelm M. (wimalopaan)


Bewertung
1 lesenswert
nicht lesenswert
Geht bedingt mit bit-field:

https://en.cppreference.com/w/c/language/bit_field

Ist aber implementation-defined.

von Peter K. (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Geht bedingt mit bit-field:
>
> https://en.cppreference.com/w/c/language/bit_field
>
> Ist aber implementation-defined.

Okay. Aber da sehe ich nichts vom Port Zugriff?

von A. F. (artur-f) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Peter K. schrieb:
> Ist das überhaupt ohne Makros möglich?

Nein.

von Wilhelm M. (wimalopaan)


Bewertung
-1 lesenswert
nicht lesenswert
Peter K. schrieb:
> Okay. Aber da sehe ich nichts vom Port Zugriff?

Es gibt unzählige Versuche, das mit bit-fields zu lösen - auch hier im 
Forum.

Die sind alle falsch bis zweifelhaft, weil sie ausnahmslos mindestens 
implementation-defined behaviour dabei haben, manche im Kontext von C++ 
mit union as type-punning UB.

Also: entweder richtig machen (Klasse in C++) oder sein lassen.

von zitter_ned_aso (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Peter K. schrieb:
> Ist das überhaupt ohne Makros möglich?

Wie ist das mit Makros möglich? (diese Schreibweise mit LED._____)

von foobar (Gast)


Bewertung
1 lesenswert
nicht lesenswert
> Wie ist das mit Makros möglich? (diese Schreibweise mit LED._____)

Mit genügend hoher "ugly-hack-Schwelle":
#define LED (*(volatile struct { char Red:1,Green:1,Blue:1,rest:5; } *)&PORTB)

von Cyblord -. (cyblord)


Bewertung
0 lesenswert
nicht lesenswert
foobar schrieb:
>> Wie ist das mit Makros möglich? (diese Schreibweise mit LED._____)
>
> Mit genügend hoher "ugly-hack-Schwelle":
>
> #define LED (*(volatile struct { char Red:1,Green:1,Blue:1,rest:5; } 
> *)&PORTB)
> 

Es gibt einen fest reservierten Platz in der Hölle, für Leute die so was 
machen.

von A. S. (achs)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Ist aber implementation-defined.

Ja. Aber ein Portzugriff ist nicht nur an den Compiler, sondern auch an 
die HW genagelt, also relativ überschaubar das Problem.

Wenn der Port ganz normal memory-mapped ist und bitweise adressierbar 
ist, dann geht das meistens relativ straight:
/* reihenfolge Compiler(setting) und HW-spezifisch */
struct sLedPort
{
unsigned char Green : 1;
unsigned char       : 2;
unsigned char Blue  : 1;
unsigned char       : 3;
unsigned char Yellow: 1;
}

/* Zuordnung an eine Adresse ist Compiler-spezifisch, hier mit @ */
struct sLedPort Leds @0x80;
/* Vielleicht noch das Datenrichtungsregister ...  */
struct sLedPort LedsDDR @0x85;

   /* im Code im Init */
   LedsDDR.Green = 1;
   LedsDDR.Blue  = 1;
   LedsDDR.Yellow= 1;
   ...
   /* und später */
   if(started) {Leds.Green=1;}
   else        {Leds.Green=0;}
   /* oder gar mit success = 0 oder 1 */
   Leds.Blue=success;  



von Stefan F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Keep it simple!

In einer Header Datei:
// Green LED = PB2 high active
#define LED_GREEN_ON  { DDRB |= (1<<PB2)); PORTB |= (1<<PB2)); }
#define LED_GREEN_OFF { PORTB &= ~(1<<PB2)); }

// Button = PB3 low active
#define BUTTON_PRESSED (!(PINB & (1<<PB3)))

In einer *.c oder *.cpp Datei:
if (BUTTON_PRESSED)
{
    LED_GREEN_ON;
}
else
{
    LED GREEN OFF;
}

Der Compiler reduziert das optimal auf Einzelbit-Befehle (cbi und sbi).

: Bearbeitet durch User
von foobar (Gast)


Bewertung
0 lesenswert
nicht lesenswert
> #define LED_GREEN_OFF { PORTB &= ~(1<<PB2)); }

Immer ein "do-while(0)" um die geschweiften Klammer!  Dann geht dein 
Beispiel auch ohne die geschweiften Klammern beim "if".

von Stefan F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Stefan F. schrieb:
> LED GREEN OFF

Huch, da fehlen die Unterstriche: LED_GREEN_OFF;

von Apollo M. (Firma: @home) (majortom)


Bewertung
0 lesenswert
nicht lesenswert
foobar schrieb:
> Immer ein "do-while(0)" um die geschweiften Klammer!  Dann geht dein
> Beispiel auch ohne die geschweiften Klammern beim "if".

bei gcc nicht nötig, da impliziert durch die {} klammer und sowieso 
schon alles ugly.

mt

von foobar (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
>> Immer ein "do-while(0)" um die geschweiften Klammer!
>
> bei gcc nicht nötig,

Bei jedem C-Compiler nötig!  Siehe http://c-faq.com/cpp/multistmt.html

von Stefan F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Da ich hinter if, else, while, for, ... sowieso immer geschweifte 
Klammern schreibe, ist mit dieser kleine gemeine Unterschied noch nie 
aufgefallen.

von MaWin (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Stefan F. schrieb:
> #define LED_GREEN_ON  { DDRB |= (1<<PB2)); PORTB |= (1<<PB2)); }

Was unterscheidet PB2 von PA2? Die (1<<PB2) schreibweise verblödet 
langsam. Wieviel Klammern sind falsch? :-<

#define LED_GREEN_ON  { DDRB |= BIT_2; PORTB |= BIT_2; }

von foobar (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
> #define LED_GREEN_ON  { DDRB |= BIT_2; PORTB |= BIT_2; }

Besser
  #define LED_GREEN_ON  do { DDRB |= BIT_2; PORTB |= BIT_2; } while (0)

;-)

von Wilhelm M. (wimalopaan)


Bewertung
3 lesenswert
nicht lesenswert
MaWin schrieb:
> #define LED_GREEN_ON  { DDRB |= BIT_2; PORTB |= BIT_2; }

Was ist denn das für ein Blödsinn: jedesmal, wenn man die LED 
einschalten will auch das DDR zu setzen.

von Wilhelm M. (wimalopaan)


Bewertung
1 lesenswert
nicht lesenswert
Stefan F. schrieb:
> // Green LED = PB2 high active
> #define LED_GREEN_ON  { DDRB |= (1<<PB2)); PORTB |= (1<<PB2)); }
> #define LED_GREEN_OFF { PORTB &= ~(1<<PB2)); }

Das ist wirklich sehr unschön: PB2 an drei Stellen redundant zu 
verwenden.

Für (1 << X) hatte Atmel/MicroChip schon seit Urzeiten _BV(x) 
eingeführt.

von Wilhelm M. (wimalopaan)


Bewertung
1 lesenswert
nicht lesenswert
A. S. schrieb:
> Wenn der Port ganz normal memory-mapped ist und bitweise adressierbar
> ist, dann geht das meistens relativ straight:
> /* reihenfolge Compiler(setting) und HW-spezifisch */
> struct sLedPort
> {
> unsigned char Green : 1;
> unsigned char       : 2;
> unsigned char Blue  : 1;
> unsigned char       : 3;
> unsigned char Yellow: 1;
> }

So etwas führt immer zu RMW-Sequenzen im Maschinencode.

Bei µC, die einzelne set/clear oder flag Register haben (also alle etwas 
moderneren), ist etwa ein
PORTA.DIRSET = _BV(1);

die einzig richtige Möglichkeit.

Das mit den Bit-Fields für µC-Register ist und bleibt ein Hack.

von A. S. (achs)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> So etwas führt immer zu RMW-Sequenzen im Maschinencode.

Das stimmt nicht und wäre auch unlogisch. Wenn der Controller einzelbits 
manipulieren kann, tut er es.

Ich sehe nicht, dass irgendeine Makrolösung hier geradlieniger ist.

von Wilhelm M. (wimalopaan)


Bewertung
2 lesenswert
nicht lesenswert
A. S. schrieb:
> Das stimmt nicht und wäre auch unlogisch. Wenn der Controller einzelbits
> manipulieren kann, tut er es.

Wenn der µC das kann, und der Compiler diese Optimierung verwendet, ja.

Bei den alten AVR gilt das eben nur für die unteren Register.

Um hier vollkommen neutral zu sein, haben moderne µC (auch die 
avr0/tiny1) die set/clear bzw. flag Register. DA funktioniert das dann 
bei jedem Register. Bei einem bit-field oberhalb von 0x1f hätten wir 
sonst wieder RMW- Sequenzen (mit all ihren sonstigen Nachteilen wie 
nicht-Atomarität).

A. S. schrieb:
> Ich sehe nicht, dass irgendeine Makrolösung hier geradlieniger ist.

Da sowieso nicht.
Ich sagte ja schon oben: entweder richtig per bit-shift oder eben in 
einer Klasse abstrahieren.

von S. R. (svenska)


Bewertung
4 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Ich sagte ja schon oben: entweder richtig per bit-shift
> oder eben in einer Klasse abstrahieren.

Oder akzeptieren, dass der ganze Krempel ohnehin hardware- und 
compilerspezifisch ist und hässlich sowieso. Es muss nicht vollkommen 
neutral sein.

Ob ich nun ein hässliches Makro, ein hässliches Bitfeld-Overlay oder 
eine hässliche Templateklasse im Header verstecke, ist (zumindest bei 
den üblichen AVR-Anwendungen) relativ egal. Hauptsache, der eigentliche 
Code sieht halbwegs sauber und übersichtlich aus.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
S. R. schrieb:
> Ob ich nun ein hässliches Makro, ein hässliches Bitfeld-Overlay oder
> eine hässliche Templateklasse im Header verstecke, ist (zumindest bei
> den üblichen AVR-Anwendungen) relativ egal.

Nun, wir sind in C, da geht es halt nicht besser als mit einem 
Macro-Hack. Wobei ich immer eine Funktion vorziehen würde (als 
header-only Bibliothek mit anderen nettes Sachen).

Wer mit dem IB der bit-fields und den sonstigen Nachteilen leben möchte, 
kann das ja gerne machen. In meinen Augen ist das jedenfalls kein Weg.

Natürlich, in C++ hat man dafür sein Klassentemplate und entsprechende 
Spezialisierungen für irgendwelche µC-Abnormitäten. Und wenn man das 
gescheit macht, finde ich das auch auf der Bibliotheksseite gar nicht 
mal so hässlich ;-)

von Cyblord -. (cyblord)


Bewertung
-2 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> MaWin schrieb:
>> #define LED_GREEN_ON  { DDRB |= BIT_2; PORTB |= BIT_2; }
>
> Was ist denn das für ein Blödsinn: jedesmal, wenn man die LED
> einschalten will auch das DDR zu setzen.

Ist halt so Arduino Style. Wobei da dann noch 29 andere Dinge gemacht 
werden. Aber man ist auf dem gleichen Weg.

von Klaus (Gast)


Bewertung
1 lesenswert
nicht lesenswert
Cyblord -. schrieb:
> foobar schrieb:
>>> Wie ist das mit Makros möglich? (diese Schreibweise mit LED._____)
>>
>> Mit genügend hoher "ugly-hack-Schwelle":
>>> #define LED (*(volatile struct { char Red:1,Green:1,Blue:1,rest:5; }
>> *)&PORTB)
>>
> Es gibt einen fest reservierten Platz in der Hölle, für Leute die so was
> machen.

Das liefert Microchip in hunderten von Headerfiles für die PICs mit:
extern volatile uint16_t  PORTA __attribute__((__sfr__));
typedef struct tagPORTABITS {
  uint16_t RA0:1;
  uint16_t RA1:1;
  uint16_t RA2:1;
  uint16_t RA3:1;
  uint16_t RA4:1;
} PORTABITS;
extern volatile PORTABITS PORTAbits __attribute__((__sfr__));
Da erinnert einen der Compiler, ist der gcc, auch daran, da0 es Bit 5 
auf Port A nicht gibt. Und ich mach mir das mit

#define LED_GREEN PORTAbits.RA0
LED_GREEN = 1;

auch noch den Code lesbarer. In den Himmel komm ich sowieso nicht, dafür 
gibts in der Hölle free WLan.

MfG Klaus

von Heinz (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Mach dir eine C++ Klasse und überschreibe den Zuweisungs-Operator.

von A. S. (achs)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Nun, wir sind in C, da geht es halt nicht besser als mit einem
> Macro-Hack.

Warum schreibst Du das, wo Du doch mein reines C-Beispiel gelesen hast? 
Es ist kein Macro nötig und schön sowieso nicht.

von Wilhelm M. (wimalopaan)


Bewertung
0 lesenswert
nicht lesenswert
A. S. schrieb:
> Warum schreibst Du das, wo Du doch mein reines C-Beispiel gelesen hast?

Zitiere mich dann bitte richtig. Das gesamte Statement war:

Wilhelm M. schrieb:
> Nun, wir sind in C, da geht es halt nicht besser als mit einem
> Macro-Hack. Wobei ich immer eine Funktion vorziehen würde (als
> header-only Bibliothek mit anderen nettes Sachen).
>
> Wer mit dem IB der bit-fields und den sonstigen Nachteilen leben möchte,
> kann das ja gerne machen. In meinen Augen ist das jedenfalls kein Weg.

von Stefan F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Wilhelm M. schrieb:
> Was ist denn das für ein Blödsinn: jedesmal, wenn man die LED
> einschalten will auch das DDR zu setzen.

Ist nur ein Vorschlag. Man muss ja nicht alles 1:1 nachmachen.

Cyblord -. schrieb:
> Ist halt so Arduino Style.

Ja, geht in die Richtung. Allerdings 30x mal schneller als Arduino.

: Bearbeitet durch User

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.

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