Forum: Mikrocontroller und Digitale Elektronik C: Ein Bit setzen und eins löschen in einem Schritt?


von Max (Gast)


Lesenswert?

Hallo,

ich wollte für eine Drehrichtungsumpolung (L298) die zwei 
angeschlossenen Ausgänge in einem Makro umschalten (definiert, kein 
togglen). Die übliche Methode
1
#define RECHTS        PORTD |= (1 << PIN0)
2
#define RECHTS_AUS    PORTD &= ~((1 << PIN0))

kenne ich, wollte das aber gerne mit

|, & und ~ in einer Zeile machen. Pseudocode:
1
#define RECHTS        PORTD: PIN0=1 und PIN1=0
2
#define LINKS         PORTD: PIN0=0 und PIN1=1

Würde mich freuen, wenn mir schnell jemand auf die Sprünge helfen 
könnte. AVR-GCC Tutorial gibt das leider nicht her.

von Lehrmann M. (ubimbo)


Lesenswert?

Max schrieb:
> AVR-GCC Tutorial gibt das leider nicht her.

Das stimmt - was es aber gibt ist ein Artikel:

http://www.mikrocontroller.net/articles/Bitmanipulation

Was da nicht steht gibt's nicht =) xD

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Max schrieb:
> Hallo,
>
> ich wollte für eine Drehrichtungsumpolung (L298) die zwei
> angeschlossenen Ausgänge in einem Makro umschalten (definiert, kein
> togglen). Die übliche Methode
>
>
1
> #define RECHTS        PORTD |= (1 << PIN0)
2
> #define RECHTS_AUS    PORTD &= ~((1 << PIN0))
3
>

Ich nehme mal an die Hardware ist AVR.
1
PORTD |= (1 << PIN1);
2
PORTD &= ~(1 << PIN0);

hat den Vorteil, daß die Einzeloperationen atomar sind und der Code 
etwas hürzer (PINx zu Compilezeit bekannt).
1
PORTD = (PORTD | (1 << PIN1)) & ~(1 << PIN0);
Ist hingegen nicht atomar umsetzbar, kann dafür aber eine Race-Condition 
verursachen wenn PORTD in einer ISR verändert wird. Dafür gint's 
hier kein Glitch, d.h. die beiden BIts werden gleichzeitig gesetzt, 
nicht nacheinander.

von Max (Gast)


Lesenswert?

Besten Dank, Johann,

das
1
PORTD = (PORTD | (1 << PIN1)) & ~(1 << PIN0);

war genau wonach ich gesucht hatte.

von Tom (Gast)


Lesenswert?

@ Johann L.

> kann dafür aber eine Race-Condition
> verursachen wenn PORTD in einer ISR verändert wird.

Magst Du das bitte mal etwas ausführlicher erklären? Wenn es geht mit 
Beispiel? Ich möchte das gerne verstehen.

von Gerd (Gast)


Lesenswert?

Da wird der unterschied zwischen 'in einer Zeile' und 'mit einem Befehl' 
deutlich. Trotzdem würde ich den Zweizeiler vorziehen. Läßt sich 
leichter debuggen. Man kann kann sonst wie komplizierte Formeln in einer 
Zeile ausrechnen lassen. Aber setz mal einen Breakpoint innerhalb einer 
C-Zeile. Es sei den man ist geil das disassembly in Assembler 
anzuschauen.

von Klaus W. (mfgkw)


Lesenswert?

Lehrmann Michael schrieb:
> Was da nicht steht gibt's nicht =) xD

naja...


Was z.B. da nicht steht und die Eltern auch nie erzählt
haben, ist etwa folgendes (vorausgesetzt, die beiden Ausgänge
liegen in benachbarten Bits, z.B. Bit 3 und 4):
1
typedef struct
2
{
3
    unsigned int bit0:1;
4
    unsigned int bit1:1;
5
    unsigned int bit2:1;
6
    unsigned int ausgaenge_L298:2;
7
    unsigned int bit5:1;
8
    unsigned int bit6:1;
9
    unsigned int bit7:1;
10
} meineigenes_port_d_layout_t;
11
12
13
#define ddrb  (*(volatile meineigenes_port_d_layout_t*)(&DDRB))
14
#define portb (*(volatile meineigenes_port_d_layout_t*)(&PORTB))
15
16
// Datenrichtung setzen:
17
ddrb.ausgaenge_L298 = 0b11;
18
19
// Ausgänge setzen:
20
enum eRichtung
21
{
22
  linksrum  = 0b01;
23
  rechtsrum = 0b10;
24
};
25
26
portb.ausgaenge_L298 = linksrum;
27
portb.ausgaenge_L298 = rechtsrum;

ddrb und portb sind jetzt Überlagerungen von DDRB bzw. PORTB
mit dem Layout der struct, in der die Einzelteile mit
Bitfeldern definiert sind.

Die restlichen Bits könnte man natürlich je nach Anwendung
entsprechend anders definieren, hier sind sie einfach als
einzelne Bits benamst.

von Karl H. (kbuchegg)


Lesenswert?

Tom schrieb:
> @ Johann L.
>
>> kann dafür aber eine Race-Condition
>> verursachen wenn PORTD in einer ISR verändert wird.
>
> Magst Du das bitte mal etwas ausführlicher erklären? Wenn es geht mit
> Beispiel? Ich möchte das gerne verstehen.

Eine Race Condition entsteht, wenn es wie bei einem Rennen auf die 
Details ankommt um zu entscheiden wer gewinnt. 2 Prozesse duellieren 
sich quasi darum wer gewinnt.

Im konkreten Beispiel

Du hast eine Interrupt Service Routine, die im PORTB nach jeweils 1 
Sekunde das Bit 3 setzt. Dieses Bit wird zb in der Hauptroutine 
ausgewertet.

Im Hauptprogramm steht zb

    PORTB = PORTB | ( 1 << PB2 );
    if( PORTB & ( 1 << PB3 ) {    // wenn in der Interrupt Routine die
                                  // Zeitmarkierung gesetzt wurde
      mach irgendwas

Bis hier hin klingt alles harmlos.

Aber jetzt kann es zu folgender Situation kommen:

    PORTB = PORTB | ( 1 << PB2 );

wird abgearbeitet und zwar genau so wie es da steht.

   PORTB   wird ausgelesen  und hätte zb diesen Inhalt 00000000
           also alle Bits auf 0

      jetzt kommt ein Interrupt. Wie programmiert setzt er das Bit 3
      auf 1. PORTB wird also programmgemäss auf 00001000 gesetzt.

      es geht aus dem Interrupt heraus und die Arbeit geht dort weiter
      wo sie unterbrochen wurde, nämlich beim fertigmachen von
      PORTB = PORTB | ( 1 << PB2 )

   PORTB wurde ja schon ausgelesen und hatte 00000000 ergeben
   da wird jetzt noch Bit 2 gesetzt, das ergibt 00000100
   und das wird auf PORTB zurückgeschrieben

   Neuer Inhalt von PORTB ist also 00000100


Nur: Wo ist jetzt das gesetzte Bit 3 hingekommen?
Es ist gelöscht worden, weil es hier eine Race Condition gibt. Je 
nachdem wie genau die Umstände sind, wer ganz zum Schluss die Nase das 
kleine bischen weiter vorne hat, ist Bit 3 entweder tatsächlich alle 1 
Sekunde auf 1 oder aber die ISR verliert und Bit 3 wird nicht auf 1 
gesetzt (bzw. in diesem Fall gleich wieder gelöscht).

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Tom schrieb:
> @ Johann L.
>
>> kann dafür aber eine Race-Condition
>> verursachen wenn PORTD in einer ISR verändert wird.
>
> Magst Du das bitte mal etwas ausführlicher erklären? Wenn es geht mit
> Beispiel? Ich möchte das gerne verstehen.

Ich hatte weiterführende Artikel verlinkt. Je nach Bildschirm sind die 
hier verwendeten Link-Farben allerdings kaum von der normalen Textfarbe 
zu unterscheiden.

von Tom (Gast)


Lesenswert?

@ Johann L.

Jetzt sehe ich den Link. War ein wenig atomar. :-)

@ Karl heinz Buchegger

Das wird also eine Race-Condition genannt.

Danke euch beiden.

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.