Forum: Mikrocontroller und Digitale Elektronik C -> Port Bitset


von Peter K. (Gast)


Lesenswert?

Hallo,

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

von Guest (Gast)


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)


Lesenswert?

SETBIT(LED,GREEN) hiesse es bei mir, Makro dazu:
1
#define SETBIT(REG, PIN) ((REG) |= (uint32_t) 1 << (PIN))
2
#define CLEARBIT(REG, PIN) ((REG) &= ~((uint32_t) 1 << (PIN)))
3
#define CHANGEBIT(REG, PIN, WERT) ((WERT) ? SETBIT((REG), (PIN)) : CLEARBIT((REG), (PIN)))
4
#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)


Lesenswert?

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

von Peter D. (peda)


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)


Lesenswert?

Geht bedingt mit bit-field:

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

Ist aber implementation-defined.

von Peter K. (Gast)


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


Lesenswert?

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

Nein.

von Wilhelm M. (wimalopaan)


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)


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)


Lesenswert?

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

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

von Cyblord -. (cyblord)


Lesenswert?

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

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

von A. S. (Gast)


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:
1
/* reihenfolge Compiler(setting) und HW-spezifisch */
2
struct sLedPort
3
{
4
unsigned char Green : 1;
5
unsigned char       : 2;
6
unsigned char Blue  : 1;
7
unsigned char       : 3;
8
unsigned char Yellow: 1;
9
}
10
11
/* Zuordnung an eine Adresse ist Compiler-spezifisch, hier mit @ */
12
struct sLedPort Leds @0x80;
13
/* Vielleicht noch das Datenrichtungsregister ...  */
14
struct sLedPort LedsDDR @0x85;
15
16
   /* im Code im Init */
17
   LedsDDR.Green = 1;
18
   LedsDDR.Blue  = 1;
19
   LedsDDR.Yellow= 1;
20
   ...
21
   /* und später */
22
   if(started) {Leds.Green=1;}
23
   else        {Leds.Green=0;}
24
   /* oder gar mit success = 0 oder 1 */
25
   Leds.Blue=success;

von Stefan F. (Gast)


Lesenswert?

Keep it simple!

In einer Header Datei:
1
// Green LED = PB2 high active
2
#define LED_GREEN_ON  { DDRB |= (1<<PB2)); PORTB |= (1<<PB2)); }
3
#define LED_GREEN_OFF { PORTB &= ~(1<<PB2)); }
4
5
// Button = PB3 low active
6
#define BUTTON_PRESSED (!(PINB & (1<<PB3)))

In einer *.c oder *.cpp Datei:
1
if (BUTTON_PRESSED)
2
{
3
    LED_GREEN_ON;
4
}
5
else
6
{
7
    LED GREEN OFF;
8
}

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

von foobar (Gast)


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. (Gast)


Lesenswert?

Stefan F. schrieb:
> LED GREEN OFF

Huch, da fehlen die Unterstriche: LED_GREEN_OFF;

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


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)


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. (Gast)


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)


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)


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)


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)


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)


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
1
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. (Gast)


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)


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)


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)


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)


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)


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:
1
extern volatile uint16_t  PORTA __attribute__((__sfr__));
2
typedef struct tagPORTABITS {
3
  uint16_t RA0:1;
4
  uint16_t RA1:1;
5
  uint16_t RA2:1;
6
  uint16_t RA3:1;
7
  uint16_t RA4:1;
8
} PORTABITS;
9
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)


Lesenswert?

Mach dir eine C++ Klasse und überschreibe den Zuweisungs-Operator.

von A. S. (Gast)


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)


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. (Gast)


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.

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.