Forum: Compiler & IDEs Variablenwert(0/1) einem Ausgangspin zuordnen


von Ein Fest a. (einfest_a)


Lesenswert?

Hallo,
ich wollte mal fragen, ob das auch eleganter geht:
1
if (anAus)
2
{
3
    PORTB |= (1<<PB0)
4
}
5
    else
6
{
7
    PORTB &= ~(1<<PB0)
8
}
Die Variable kommt aus einem größeren Programm, ich muss sie also in der 
Form haben.

Mir wäre etwas in dem Stiel:
"PinX = anAus" lieber.

Ich habe den Code gleich 10mal in einer ISR, deshalb hätte ich es gern 
etwas schlanker ;)

Für einen hilfreichen Tipp bin ich dankbar.

Gruß,
Tobi

von Karl H. (kbuchegg)


Lesenswert?

Ein Fest a. schrieb:

> Ich habe den Code gleich 10mal in einer ISR, deshalb hätte ich es gern
> etwas schlanker ;)

Das sieht nur in C so behäbig aus. Der Compiler macht das schon ziemlich 
schlank.

In C kannst du noch syntaktischen Zucker drüberstreuen, damit es nicht 
ganz so behäbig aussieht. Aber am Prinzip ändert sich dadurch nichts.

zb kannst du dir die Details in einem Makro verstecken.

von Ein Fest a. (einfest_a)


Lesenswert?

Danke für die Antwort!

...dann werde ich es wohl so lassen...

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Karl Heinz Buchegger schrieb:
> In C kannst du noch syntaktischen Zucker drüberstreuen, damit es nicht
> ganz so behäbig aussieht. Aber am Prinzip ändert sich dadurch nichts.

Z.B. mit meiner sbit.h
1
if (anAus)
2
{
3
    PORT_B0 = 1;
4
}
5
    else
6
{
7
    PORT_B0 = 0;
8
}

Man könnte auch schreiben:
1
  PORT_B0 = !!anAus;
Aber das ergibt größeren Code.


Peter

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:
> Karl Heinz Buchegger schrieb:
>> In C kannst du noch syntaktischen Zucker drüberstreuen, damit es nicht
>> ganz so behäbig aussieht. Aber am Prinzip ändert sich dadurch nichts.
>
> Z.B. mit meiner sbit.h
>
1
> if (anAus)
2
> {
3
>     PORT_B0 = 1;
4
> }
5
>     else
6
> {
7
>     PORT_B0 = 0;
8
> }
9
>

Was mich immer schon mal interessiert hat.
Kriegt der gcc das hin, dass er da Einzelbitoperationen draus macht, 
oder erledigt er das über Read/Modify/Update?

von Peter D. (peda)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Kriegt der gcc das hin, dass er da Einzelbitoperationen draus macht,
1
void test( uint8_t anAus )
2
{
3
  if (anAus)
4
  82:   88 23           and     r24, r24
5
  84:   11 f0           breq    .+4             ; 0x8a <test+0x8>
6
    PORT_B0 = 1;
7
  86:   c0 9a           sbi     0x18, 0 ; 24
8
  88:   08 95           ret
9
  else
10
    PORT_B0 = 0;
11
  8a:   c0 98           cbi     0x18, 0 ; 24
12
  8c:   08 95           ret


Peter

von Karl H. (kbuchegg)


Lesenswert?

>     PORT_B0 = 1;
>   86:   c0 9a           sbi     0x18, 0 ; 24

Hey. cool.

Deine Struktur-Geschichte hätte nämlich einen reisengroßen Vorteil. Man 
könnte für alle Hardwareregister eine entsprechende Struktur generieren, 
mit sauberen Bitnamen:

   TIMSK.TOIE0 = TRUE;

und wenn das TIEO1 Bit bei einem µC nicht im TIMSK Register ist, dann 
schreit der Compiler. Die "klassischen" Fehler, dass die WGM Bits in 2 
verschiedenen Config-Register sind, wären damit entschärft.

Bzw. dann weitergehend überhaupt nur noch

   TOIE0 = TRUE;

und der Compiler "sucht selber raus", in welchem Register das 
entsprechende Bit sitzt.

Das wär schon was.

von Ralf G. (ralg)


Lesenswert?

Ich häng' mich mal rein.
Könnte sowas in der ein oder anderen Situation nicht noch 
übersichtlicher sein:
1
//                      Access bits like variables:
2
// frei nach Peter Dannegger
3
struct bits {
4
  uint8_t b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1;
5
} __attribute__((__packed__));
6
/*
7
#define SBIT_(port,pin) ((*(volatile struct bits*)&port).b##pin)
8
#define SBIT(x,y)       SBIT_(x,y)
9
*/
10
11
#ifdef PORTD
12
#define PortD  (*((volatile struct bits*)&PORTD))
13
#endif // PORTD

Aufruf:
1
PortD.b2 = 1;

von Karl H. (kbuchegg)


Lesenswert?

Ralf G. schrieb:
> Ich häng' mich mal rein.
> Könnte sowas in der ein oder anderen Situation nicht noch
> übersichtlicher sein:

Läuft ja im Kern aufs selbe raus.


> Aufruf:
>
1
> PortD.b2 = 1;
2
>


So wie Peter das macht, geht das ja noch einen Schritt weiter. In einem 
realen Programm wirst du ja nicht, oder nur selten, an PortD.b2 etwas 
zuweisen. Denn an dem Pin hängt ja etwas. Zb eine Error Led oder eine 
Busy LED. D.h. du möchtest gerne schreiben:
1
#define ERROR_LED   SBIT( PORTD, 2 )
2
#define BUSY_LED    SBIT( PORTD, 3 )
3
4
...
5
6
   ERROR_LED = 1;
7
   BUSY_LED = 0;

Sein SBIT Makro dient dazu, eine Verknüpfung zwischen Port und Pin zu 
schaffen, damit man im eigentlichen Code überhaupt nichts nacharbeiten 
muss, wenn Funktionseinheiten ihre Pinzuordnung wechseln.

von Sepp (Gast)


Lesenswert?

1
#define PortD  (*((volatile struct bits*)&PORTD))

Kann man wirklich immer so auch auf DDRD und PIND zugreifen??
ich mein so: (ich weiß nicht, ob die '+1' so stimmt, aber ich meine ja 
nr vom Prinzip her...
1
#define PinD  (*((volatile struct bits*)&(PORTD+1)))

Gruß Sepp

von Karl H. (kbuchegg)


Lesenswert?

Sepp schrieb:

> Kann man wirklich immer so auch auf DDRD und PIND zugreifen??
> ich mein so: (ich weiß nicht, ob die '+1' so stimmt, aber ich meine ja
> nr vom Prinzip her...

Ich meine mich zu erinnern, dass es beim Mega128 (F oder G) eine 
Ausnahme gibt.

von Ralf G. (ralg)


Lesenswert?

Karl Heinz Buchegger schrieb:
>
> Läuft ja im Kern aufs selbe raus.
>
> ...
>
> So wie Peter das macht, geht das ja noch einen Schritt weiter. In einem
> realen Programm wirst du ja nicht, oder nur selten, an PortD.b2 etwas
> zuweisen.

Naja, es wird sozusagen aus dem Port dann eine Datenstruktur.
So wie bei:

Karl Heinz Buchegger schrieb:
> TIMSK.TOIE0 = TRUE;

Das 'PORT_D2' wollte ich vermeiden.

> Denn an dem Pin hängt ja etwas. Zb eine Error Led oder eine
> Busy LED. D.h. du möchtest gerne schreiben:
> #define ERROR_LED   SBIT( PORTD, 2 )
> #define BUSY_LED    SBIT( PORTD, 3 )
>
> ...
>
>    ERROR_LED = 1;
>    BUSY_LED = 0;
>
> Sein SBIT Makro dient dazu, eine Verknüpfung zwischen Port und Pin zu
> schaffen, damit man im eigentlichen Code überhaupt nichts nacharbeiten
> muss, wenn Funktionseinheiten ihre Pinzuordnung wechseln.

Das wäre dann mit direktem Zugriff auf SBIT. Mmh, auch eine Möglichkeit.

Vielleicht noch zur Disskusion:
1
#define PinB_(n)  ((*((volatile struct bits*)&PINB)).b##n)
2
3
// ...
4
5
 if ( PinB_(1 << B_EIGENES) == 0 )
6
 {
7
// ...
8
 }
Naja, vielleicht auch nicht besser als:
1
 if ( (~PINB) & (1 << B_EIGENES) )
2
 {
3
// ...
4
 }

von Karl H. (kbuchegg)


Lesenswert?

Ralf G. schrieb:

> Vielleicht noch zur Disskusion:

Wie immer du das machen willst.
Eines muss sowieso klar sein: Das alles ist nur syntaktischer Zucker auf 
C-Ebene. Wenn unten nach dem compilieren sowieso wieder das gleiche raus 
kommst, geht es nur darum, wie man das Zeugs dann im Code so verwenden 
kann, dass man möglichst wenig Fehler machen kann. Und wenn, soll der 
Compiler sie abfangen können.

>  if ( PinB_(1 << B_EIGENES) == 0 )

> Naja, vielleicht auch nicht besser als:

>  if ( (~PINB) & (1 << B_EIGENES) )


Der springede Punkt um den es mir an dieser Stelle geht besteht darin, 
dass du im Code hier 2 Informationen stehen hast, die zusammenstimmen 
müssen:

* welcher Port
* welches Bit an diesem Port

Du hast also eine mögliche Fehlerquelle, indem du zwar das richtige Bit 
aber am falschen Port angibst. Kein Compiler hilft dir dabei, wenn du 
schreibst

   if( PinD_( B_EIGENES ) == 0 )   // OOps, sollte eigentlich PinB sein!

Muss ich selber aber an dieser Stelle keine 2 Informationen in 
Übereinstimmung bringen

   if( B_EIGENES == 0 )

und durch die Makroexpansion expandiert das zum richtigen Bit am 
richtigen Port, dann hab ich eine potentielle Fehlerquelle weniger.

D.h. neben den syntaktischen Zucker, hat das ganze Abspecken durchaus 
einen ernsten Hintergrund in der passiven Programmierung.

von Ralf G. (ralg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> if( B_EIGENES == 0 )

Was ist jetzt schon wieder 'B_EIGENES'? Könnte man hier fragen. Um 
dann vielleicht zum Verständis viele verschachtelte Makros 
auseinanderpflücken zu müssen.

TIMSK.TOIE0 = TRUE;   // (c) khb
... da weiß man, was man hat!

von Karl H. (kbuchegg)


Lesenswert?

Ralf G. schrieb:
> Karl Heinz Buchegger schrieb:
>> if( B_EIGENES == 0 )
>
> Was ist jetzt schon wieder 'B_EIGENES'? Könnte man hier fragen.

:-)

Kann man.
Du bist doch mit dem B_EIGENES hochgekommen.

In der Realität steht dann da eben

   if( DRUCK_ALARM )


weil DRUCK_ALARM dann eben so definiert ist, dass das ein Eingangspin am 
Port C, Pin 5 ist, an dem der Wasserdrucksensor einen Überdruck anzeigt.

von Ralf G. (ralg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Du bist doch mit dem B_EIGENES hochgekommen.

Ich weiß! :)

Hatte da eher im Blick, dass es sich um so eine 'verschachtelte' 
Konstruktion wie von Peter handeln könnte. Und wenn dann schon das|die 
Makro(s) fehlerhaft sind...
Also, Makros aus Makros aus Makros aufrufen, versuche ich lieber zu 
vermeiden.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Man könnte auch schreiben:
1
PORT_B0 = !!anAus;
> Aber das ergibt größeren Code.

Wenn die Variable den zu setzenden Wert schon in Bit0 enthält, geht auch
1
void anaus (unsigned char an)
2
{
3
    PORT_B5 = an;
4
}
1
anaus:
2
  sbrc r24,0
3
  sbi 0xb,5
4
  sbrs r24,0
5
  cbi 0xb,5
6
  ret

von Rolf M. (rmagnus)


Lesenswert?

Johann L. schrieb:
> Peter Dannegger schrieb:
>> Man könnte auch schreiben:PORT_B0 = !!anAus;> Aber das ergibt größeren Code.
>
> Wenn die Variable den zu setzenden Wert schon in Bit0 enthält,

... oder sie, wie es offensichtlich hier sinnvoll wäre, vom Typ bool ist

> geht auch
1
#include <stdbool.h>
2
3
void anaus (bool an)
4
{
5
    PORT_B5 = an;
6
}

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.