mikrocontroller.net

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


Autor: Max (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

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

kenne ich, wollte das aber gerne mit

|, & und ~ in einer Zeile machen. Pseudocode:
#define RECHTS        PORTD: PIN0=1 und PIN1=0
#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.

Autor: Lehrmann Michael (ubimbo)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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
>
>
> #define RECHTS        PORTD |= (1 << PIN0)
> #define RECHTS_AUS    PORTD &= ~((1 << PIN0))
> 

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

hat den Vorteil, daß die Einzeloperationen atomar sind und der Code 
etwas hürzer (PINx zu Compilezeit bekannt).
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.

Autor: Max (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Besten Dank, Johann,

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

war genau wonach ich gesucht hatte.

Autor: Tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Gerd (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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):
typedef struct
{
    unsigned int bit0:1;
    unsigned int bit1:1;
    unsigned int bit2:1;
    unsigned int ausgaenge_L298:2;
    unsigned int bit5:1;
    unsigned int bit6:1;
    unsigned int bit7:1;
} meineigenes_port_d_layout_t;


#define ddrb  (*(volatile meineigenes_port_d_layout_t*)(&DDRB))
#define portb (*(volatile meineigenes_port_d_layout_t*)(&PORTB))

// Datenrichtung setzen:
ddrb.ausgaenge_L298 = 0b11;

// Ausgänge setzen:
enum eRichtung
{
  linksrum  = 0b01;
  rechtsrum = 0b10;
};

portb.ausgaenge_L298 = linksrum;
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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

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.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

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