Forum: Mikrocontroller und Digitale Elektronik einfache Schreibweise um Pins zu schalten


von R. F. (inet_surfer88)


Lesenswert?

Hallo,

ich schalte in einem Programm häufig von einem Port einzelne Pins aus 
und ein.
Momentan mache ich das mit den Ausdrücken
1
PORTC |= (1 << 0);
 bzw.
1
PORTC &= ~(1 << 0);

Kann ich hier am Anfang der Datei eine Zuweisung machen, damit ich 
später im Programm z.B. anstelle
1
PORTC |= (1 << 0);
 nur noch
1
pinc0_ein;
 oder anstelle von
1
PORTC &= ~(1 << 0);
 nur noch
1
pinc0_aus;
 schreiben muss?

Das würde das Programm wesentlich "lesbarer" machen.


Gruß
inet_surfer88

von Bernd N (Gast)


Lesenswert?

1
struct bits {
2
    uint8_t b0:1;
3
    uint8_t b1:1;
4
    uint8_t b2:1;
5
    uint8_t b3:1;
6
    uint8_t b4:1;
7
    uint8_t b5:1;
8
    uint8_t b6:1;
9
    uint8_t b7:1;
10
} __attribute__((__packed__));
11
12
#define SBIT(port,pin) ((*(volatile struct bits*)&port).b##pin)
13
14
#define TestPin SBIT (PORTD, 7)
Jetzt kannst Du schreiben:

TestPin = 1;
TestPin = 0;

von holger (Gast)


Lesenswert?

#define pinc0_ein PORTC |= (1 << 0)

von Vlad T. (vlad_tepesch)


Lesenswert?

klassischer fall für den präprozessor.
1
#define pinc0_aus do{PORTC &= ~(1 << 0);}while(0)
2
#define pinc0_an  do{PORTC |=  (1 << 0);}while(0)


noch besser (weil sauberer) wären aber inline funktionen.

1
#define LEDBIT  PIN0
2
#define LEDDDR  DDRC
3
#define LEDPORT PORTC
4
#define LEDPIN  PINC
5
6
7
8
static inline void LedOn    ( void ) { LEDPORT |=  (1 << LEDBIT); }
9
static inline void LedOff   ( void ) { LEDPORT &= ~(1 << LEDBIT); }
10
static inline void LedToggle( void ) { LEDPIN  |=  (1 << LEDBIT); }
11
12
static inline uint8_t LedIsOn( void ) { return LEDPORT &= (1 << LEDBIT);}


Edit:
Zu langsam
+ fehler korrigiert

von Hc Z. (mizch)


Lesenswert?

Natürlich kannst Du einen Macro machen.  Ich würde es mir aber nochmal 
überlegen.  Für Dich, der jetzt im Programm „drin” ist, ist ein Macro 
keine Hürde und macht das Programm lesbarer.  Für Dich in zwei Jahren 
oder einen anderen Leser ist das eine Indirektion mehr, die zunächst 
gelernt sein will, um sie als menschlicher Macro-Expander bereit zu 
halten.  Man muss erst mal nachsehen, was der Macro bedeutet, um das 
Programm zu verstehen.

Und schließlich halte ich
1
PORTC |= (1 << 0);
für eine sehr klare Formulierung, die ohne Überlegung jedem, der bloß ab 
und zu mit solchen Programmen zu tun hat, sofort und ohne weitere Umwege 
verständlich ist.  Es ist etwa so, wie wenn ein Englischanfänger in 
englischen Text die Übersetzungen reinschreibt: für den, der nur ein 
wenig damit vertraut ist, stört es später höchstens den Lesefluss.

Und natürlich wäre
1
LCD_STATUS |= (1<<LCD_RS)
noch die  viel bessere Formulierung.

von Vlad T. (vlad_tepesch)


Lesenswert?

Hc Zimmerer schrieb:
> Und schließlich halte ichPORTC |= (1 << 0);
> für eine sehr klare Formulierung, die ohne Überlegung jedem, der bloß ab
> und zu mit solchen Programmen zu tun hat, sofort und ohne weitere Umwege
> verständlich ist.

Naja, klar sit damit nur, dass man den Pin anschaltet, mehr nicht.
Normalerweise schaltet man den ja aber nicht um seiner selbst willen, 
sondern um irgend was zu bewirken.

Es ist somit viel besser, das Resultat zu benennen, als den Weg.
also, statt PinC0_ein, lieber Power_LED_ein(), oder latchData()

Das macht es zum einen einfacher, wenn man die Anschlüsse tauscht, oder 
gar das modul, was an dem Ausgangsport höngt.

von holger (Gast)


Lesenswert?

>Und schließlich halte ich
>PORTC |= (1 << 0);
>für eine sehr klare Formulierung,

Wenn man den Pin dann später mal wechseln möchte
und das da oben an mehreren Stellen im Code steht,
wird es aber ne Menge Arbeit alle Codezeilen zu
ändern. Wenn ein #define in einer Headerdatei
verwendet wird muss man nur ein Zeile ändern.

von Peter D. (peda)


Lesenswert?

Hc Zimmerer schrieb:
> Und schließlich halte ichPORTC |= (1 << 0);
> für eine sehr klare Formulierung,

Für mich ist sowas total unleserlich.

Mich interessiert nicht, welchen Pin ich schubse, sondern wozu das nütze 
ist.
Also schreibe ich lieber:
1
  LED1 = LED_ON;
2
  MOTOR1_LEFT = MOTOR_STOP;
3
  // usw.

Und das geht eben sehr einfach mit dem SBIT Macro.

Außerdem muß man dann für jeden Pin auch nur einen Namen definieren.
Beim OR/AND muß man immer 2 Namen definieren, die Portadresse und die 
Pinnummer.

Leider habe ich noch keinen Weg gefunden, wie man automatisch den DDR- 
und PIN-Pin mit definieren kann. Ein #define innerhalb des #define geht 
nicht.


Peter

von Tony R. (tony)


Lesenswert?

Hc Zimmerer schrieb:
> Und schließlich halte ich
>
1
> PORTC |= (1 << 0);
2
>
> für eine sehr klare Formulierung

Dazu kann ich als PIC-User sagen, dass mir diese schreibweise zunächst 
mal alles andere als "klar vormuliert" vorkam. Da muss man erstmal 
überlegen, welches Bit da jetzt wie wohin geschoben, invertiert und 
ge-AND-et wird...

Da finde ich die PIC-C-18 Schreibweise deutlich schöner:
PORTBbits.RB1 = 1;
z.B.

von R. F. (inet_surfer88)


Lesenswert?

Vlad Tepesch schrieb:
> klassischer fall für den präprozessor.
>
>
1
> 
2
> #define pinc0_aus do{PORTC &= ~(1 << 0);}while(0)
3
> #define pinc0_an  do{PORTC |=  (1 << 0);}while(0)
4
> 
5
>
>

Vielen Dank! Genau das was ich gesucht habe. Habe es gleich mal 
ausprobiert und es funktioniert wunderbar.

Gruß
inet_surfer88

von Gast0 (Gast)


Lesenswert?

1
#define pinc0_aus do{PORTC &= ~(1 << 0);}while(0)
2
#define pinc0_an  do{PORTC |=  (1 << 0);}while(0)

Für was brauch ich denn da die do-while schleife drumrum? Das will mir 
nicht so ganz einleuchten.

Macht nicht
1
#define pinc0_an (PORTC |= (1<<0))
genau das gleiche?

Gruss,
Gast

von R. F. (inet_surfer88)


Lesenswert?

Gast0 schrieb:
>
1
> #define pinc0_aus do{PORTC &= ~(1 << 0);}while(0)
2
> #define pinc0_an  do{PORTC |=  (1 << 0);}while(0)
3
>
>
> Für was brauch ich denn da die do-while schleife drumrum? Das will mir
> nicht so ganz einleuchten.
>
> Macht nicht
>
1
> #define pinc0_an (PORTC |= (1<<0))
2
>
> genau das gleiche?
>
> Gruss,
> Gast


Hallo,

stimmt. Hatte ganz am Anfang schon jemand geschrieben. Als ich es 
ausprobiert hatte, habe ich allerdings eine Fehlermeldung bekommen. Muss 
allerdings ein Tippfehler gewesen sein. Habs jetzt nochmals ausprobiert 
und es funktioniert.

von Uwe .. (uwegw)


Lesenswert?

Peter Dannegger schrieb:
> Leider habe ich noch keinen Weg gefunden, wie man automatisch den DDR-
> und PIN-Pin mit definieren kann. Ein #define innerhalb des #define geht
> nicht.

#define DDR(x) (*(&x - 1))      /* address of data direction register of 
port x */
#define PIN(x) (*(&x - 2))    /* address of input register of port x 
*/

und dann z.B. DDR(PORTB) oder PIN(PORTA)

Das beruht darauf, dass die Register an fortlaufenden Adressen liegen. 
Das habe ich in Peter Fleurys LCD-Lib gefunden. Ist aber nicht gerade 
eine saubere Methode. Denn diese Anordnung der Register ist ja nirgendwo 
garantiert. Beim Mega128/Mega64 ist das PINF-Register etwa getrennt von 
PORTF und DDRF abgelegt. Das fängt er an dieser Stelle dann mit
#if defined(_AVR_ATmega64_) || defined(_AVR_ATmega128_)
    /* on ATmega64/128 PINF is on port 0x00 and not 0x60 */
    #define PIN(x) ( &PORTF==&(x) ? _SFR_IO8(0x00) : (*(&x - 2)) )
#else
  #define PIN(x) (*(&x - 2))    /* address of input register of port x 
*/
#endif
ab. Das ist also extrem kritisch, wenn neue AVRs herauskommen, die 
ebenfalls so eine abweichende Anordnung haben.

von Vlad T. (vlad_tepesch)


Lesenswert?

Gast0 schrieb:
> Macht nicht#define pinc0_an (PORTC |= (1<<0))
> genau das gleiche?

in dem Fall ist es egal.
Spätestens, wenn es mehrere Anweisungen werden, ist essenziell.

#define SR_latch() {PORTC |= (1<<0); PORTC &= ~(1<<0);}

sieht auf den ersten Blick richtig aus.

Baut man das dann aber in ein if-else-konstrukt ein:


if(x>y)
  SR_latch();
else
  ++x;


geht es nicht:
da dann steht:

if(x>y)
  {PORTC |= (1<<0); PORTC &= ~(1<<0);};
else
  ++x;

das semikolon schließt die if-Anweisung unwiederuflich.


Daraus lernt man 2 sachen:
1. Macros, die Anweisungen kapseln immer in do{}while(0)
2. Macros, die Anweisungen kapseln nicht benutzen, dafür gibt es 
inline-Funktionen.

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.