www.mikrocontroller.net

Forum: Compiler & IDEs Mehrere Bits gleichzeitig ändern


Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

zur Adressierung eines Multiplexer-ICs muss ich laufend mehrere Bits
eines Ports verändern, ohne jedoch die restlichen Bits zu verändern.
Geht dieses einfacher als mein bisheriger Weg?
#define MUX_ADRESS_MASK  0x78            /* Diese Maske maskiert Bit 6
bis 3, da 0x78 = 0b01111000 */

static u8 MuxAdress_u8    = 0x00;
       u8 NewMuxAdress_u8 = 0x00;
       u8 PortMirror_u8   = 0x00;

if (MuxAdress_u8 < 0x0F) {              /* Sicherstellen, dass
MuxAdress nie 15 überschreitet und damit mehr als 4 Bits benötigt */
  MuxAdress_u8++;
} else {
  MuxAdress_u8 = 0x00;
}

PortMirror_u8 = PORTB;/* Aktuellen Zustand von Port B lesen */
PortMirror_u8 &= ~MUX_ADRESS_MASK;  /* Bits 6 bis 3 aus der Portkopie
löschen */

NewMuxAdress_u8 = (MuxAdress_u8 << 3);  /* Adressierungsmaske auf Bit 6
bis 3 'positionieren' */

PortMirror_u8 |= NewMuxAdress_u8;  /* Neue Adresse in Portkopie setzen
*/

PORTB = PortMirror_u8;      /* PORT B mit geänderter Adressierung
schreiben */

Autor: Rainer Spitzhirn (rainersp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!
Generell wirds Du nicht rumkommen, den alten Zustand auszulesen, die
relevanten Bits zu sichern und zu einem neuen Zustand
zusammenzusetzen.

Wenn sich z.B. bei Dir hier nur die Adresse MuxAdress_u8 ändert, so
kannst Du auch einfach PORTB = (PORTB & (MUX_ADRESS_MASK << 3) ) |
(Mux_Adress_u8<<3) rechnen.

Das lässt sich aber auch noch vereinfachen. Deine Fallunterscheidung
lässt sich auch ohne Sprünge schön hinkriegen:
MuxAdress_u8 = (Mux_Adress_u8+1) & 0x0F;

Damit bleibst Du auch im Wertebereich von 0x00..0x0F. Das anschließende
Verschieben kannst Du Dir auch sparen:
Mux_Adress_u8 = (Mux_Adress_u8 + 0x08) & (MUX_ADRESS_MASK<<3);

Damit wird auch Deine Portzuweisung noch einmal einfacher:
PORTB = (PORTB & (MUX_ADRESS_MASK<<3) ) | Mux_Adress_u8;

Gefährlich ist damit vielleicht nur, dass am Port, je nach Umsetzung
durch den C-Compiler, unerwünschte und ungültige Zustände entstehen.
Wenn Du damit leben kannst, ist es gut, ansonsten musst Du mit einer
Hilfsvariablen arbeiten.

Gruß,
Rainer

P.S. Wenn Du mit cc5x arbeitest, dann musst Du einige der Anweisungen
oben zerlegen.

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum bastelst du die Multiplexer-Bits immer neu zusammen? Du könntest
diese fixen Werte einfach aus einem Array (Tabelle) holen oder als
Konstante einsetzen.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für Eure Antworten.

@Rainer: Kannst Du mir bitte erklären, warum Du die Maske, die bereits
Bit 6 bis 3 maskiert, immer um 3 Bits nach links schiebst? Das verstehe
ich nicht.

Die Anweisung 'MuxAdress_u8 = (Mux_Adress_u8+1) & 0x0F;' gefällt mir
besser als die if()-Schleife, danke für den Tipp!


@Stefan: Ein Array nimmt mir Platz im RAM (welcher eh knapp ist), wie
sieht es mit den Zugriffszeiten aus, wenn ich das Array als Konstante
im Flash habe? Obiger Code befindet sich nämlich in einer ISR.

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn ich das richtig verstanden habe, kann ich meinen Code also
folgendermaßen optimieren - richtig?
/* Diese Maske maskiert Bit 6 bis 3, da 0x78 = 0b01111000 */
#define MUX_ADRESS_MASK  0x78

static u8 MuxAdress_u8    = 0x00;

/* MuxAdress um 1 erhöhen, nur das LOW-Nibble anzeigen */
MuxAdress_u8 = ((MuxAdress_u8++) & 0x0F);

/* Neue MuxAdress in PORTB eintragen */
PORTB = ((PORTB & ~MUX_ADRESS_MASK) | (MuxAdress_u8 << 3));

Autor: Rainer Spitzhirn (rainersp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Genau, da war ich wohl etwas zu schluderig mit der Masker ,-) Du musst
Dir auf jeden Fall das Ergebnis des Compilers ansehen, sonst kann es
passieren, dass der Port sich mehr als einmal ändert. Und genau das
wolltest Du nicht!

Autor: Christoph Kessler (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In Assembler:
Bits toggeln geht am einfachsten mit EXOR, sonst Setzen mit OR,
Rücksetzen mit AND

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In C:
Bits toggeln geht am einfachsten mit ^, sonst Setzen mit |,
Rücksetzen mit &

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Compiler macht dieses daraus:

[Assembler]
    PORTA = ((PORTA & ~MUX_ADRESS_MASK) | (MuxAdress_u8 << 3)); /* Neue
MuxAdress in PORTB eintragen */
 25a:  2b b3         in  r18, 0x1b  ; 27
 25c:  27 78         andi  r18, 0x87  ; 135
 25e:  99 27         eor  r25, r25
 260:  33 e0         ldi  r19, 0x03  ; 3
 262:  88 0f         add  r24, r24
 264:  99 1f         adc  r25, r25
 266:  3a 95         dec  r19
 268:  e1 f7         brne  .-8        ; 0x262 <__vector_16+0x2a>
 26a:  28 2b         or  r18, r24
 26c:  2b bb         out  0x1b, r18  ; 27
[/Assembler]

Die letzte Anweisung schreibt den Inhalt von r18 an die Adresse 0x1B,
dort liegt bei ATMega32 PORTA -- also scheint alles i.O.

Autor: Rainer Spitzhirn (rainersp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich kenne mich mit dem ATMega32 ja gar nicht aus, aber die Lösung sieht
mir nicht so optimal aus.

Kannst Du mal posten was rauskommt, wenn Du vorher MuxAdress_u8 um 8
erhöhst, so dass der Links-Shift entfallen kann? Das Shiften passiert
ja hier durch eine Schleife! Das kann schneller gehen!

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du meinst mit 'um 8 erhöhen' sicherlich 'mit 8 multiplizieren', da
ein Linkshift ja eine Multiplikation mit 2 entspricht. Dann habe ich
folgendes

[A]
      PORTA = ((PORTA & ~MUX_ADRESS_MASK) | (MuxAdress_u8 * 8)); /*
Neue MuxAdress in PORTA eintragen */
 276:  2b b3         in  r18, 0x1b  ; 27
 278:  27 78         andi  r18, 0x87  ; 135
 27a:  99 27         eor  r25, r25
 27c:  88 0f         add  r24, r24
 27e:  99 1f         adc  r25, r25
 280:  88 0f         add  r24, r24
 282:  99 1f         adc  r25, r25
 284:  88 0f         add  r24, r24
 286:  99 1f         adc  r25, r25
 288:  28 2b         or  r18, r24
 28a:  2b bb         out  0x1b, r18  ; 27
[/A]

Autor: Tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Rainer: Wieso um 8 erhöhen? Muss man doch mit 8 multiplizieren...

Der Assembler Code ist dahin gehend nicht optimal, dass einerseits eine
Schleife zum Schieben benutzt wird und dazu noch unnötigerweise das
Carry in r25 gesichert wird.
3 Mal um 1 Bit schieben wäre bestimmt ein paar Takte schneller.

Gruss
Tom

Autor: Tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das bezog sich natürlich auf das obere Beispiel.
Aber du kannst alles was mit r25 zu tun hast raus schmeissen, wird eh
nie wieder benötigt.

Autor: Christoph Kessler (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Manchmal sehr wichtig: vor dem Lesen Interrupts abstellen, nach dem
Rückschreiben wieder zulassen, sonst kann es böse gelegentliche Fehler
geben, wenn die Interruptroutine die gelesenen Bits ändert, und das
Hauptprogramm sie wieder auf den alten Wert zurückstellt. Auch Timer
können ihre Bits in der Zwischenzeit geändert haben, daher sollte alles
so schnell wie möglich passieren.

Autor: Rainer Spitzhirn (rainersp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Tom: Die acht zu addieren ist richtig. Das ist äquivalent mit der
Addition von 1 und anschließendem Links-Shift um drei. Ich will hier
aber auch niemanden dazu motivieren, im erzeugten Assembler-Code
rumzuwerkeln.

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nee

Spitzhirn hat recht! Statt um eins zu erhöhen und dann drei Bits zu
shiften, kannst Du auch gleich 8 dazu addieren. Der uC braucht nämlich
gleich lang um +8 zu rechnen, wie bei +1, Du sparst Dir aber die
Shifts, bzw. die Multiplikation *8!

Das ganze sieht dann so aus:
// Diese Maske maskiert Bit 6 bis 3, da 0x78 = 0b01111000 
#define MUX_ADRESS_MASK  0x78   

static u8 MuxAdress_u8    = 0x00;   

// MuxAdress um 8 erhöhen und die gewünschten Bits ausmaskieren 
MuxAdress_u8 = ((MuxAdress_u8+8) & MUX_ADRESS_MASK);    

// Neue MuxAdress in PORTB eintragen
PORTB = ((PORTB & ~MUX_ADRESS_MASK) | MuxAdress_u8);  

Gruss Peter

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.