Forum: Compiler & IDEs Mehrere Bits gleichzeitig ändern


von Gast (Gast)


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?
1
#define MUX_ADRESS_MASK  0x78            /* Diese Maske maskiert Bit 6
2
bis 3, da 0x78 = 0b01111000 */
3
4
static u8 MuxAdress_u8    = 0x00;
5
       u8 NewMuxAdress_u8 = 0x00;
6
       u8 PortMirror_u8   = 0x00;
7
8
if (MuxAdress_u8 < 0x0F) {              /* Sicherstellen, dass
9
MuxAdress nie 15 überschreitet und damit mehr als 4 Bits benötigt */
10
  MuxAdress_u8++;
11
} else {
12
  MuxAdress_u8 = 0x00;
13
}
14
15
PortMirror_u8 = PORTB;/* Aktuellen Zustand von Port B lesen */
16
PortMirror_u8 &= ~MUX_ADRESS_MASK;  /* Bits 6 bis 3 aus der Portkopie
17
löschen */
18
19
NewMuxAdress_u8 = (MuxAdress_u8 << 3);  /* Adressierungsmaske auf Bit 6
20
bis 3 'positionieren' */
21
22
PortMirror_u8 |= NewMuxAdress_u8;  /* Neue Adresse in Portkopie setzen
23
*/
24
25
PORTB = PortMirror_u8;      /* PORT B mit geänderter Adressierung
26
schreiben */

von Rainer S. (rainersp)


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.

von Stefan (Gast)


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.

von Gast (Gast)


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.

von Gast (Gast)


Lesenswert?

Wenn ich das richtig verstanden habe, kann ich meinen Code also
folgendermaßen optimieren - richtig?
1
/* Diese Maske maskiert Bit 6 bis 3, da 0x78 = 0b01111000 */
2
#define MUX_ADRESS_MASK  0x78
3
4
static u8 MuxAdress_u8    = 0x00;
5
6
/* MuxAdress um 1 erhöhen, nur das LOW-Nibble anzeigen */
7
MuxAdress_u8 = ((MuxAdress_u8++) & 0x0F);
8
9
/* Neue MuxAdress in PORTB eintragen */
10
PORTB = ((PORTB & ~MUX_ADRESS_MASK) | (MuxAdress_u8 << 3));

von Rainer S. (rainersp)


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!

von Christoph Kessler (Gast)


Lesenswert?

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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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

von Gast (Gast)


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.

von Rainer S. (rainersp)


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!

von Gast (Gast)


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]

von Tom (Gast)


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

von Tom (Gast)


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.

von Christoph Kessler (Gast)


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.

von Rainer S. (rainersp)


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.

von Peter (Gast)


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:
1
// Diese Maske maskiert Bit 6 bis 3, da 0x78 = 0b01111000 
2
#define MUX_ADRESS_MASK  0x78   
3
4
static u8 MuxAdress_u8    = 0x00;   
5
6
// MuxAdress um 8 erhöhen und die gewünschten Bits ausmaskieren 
7
MuxAdress_u8 = ((MuxAdress_u8+8) & MUX_ADRESS_MASK);    
8
9
// Neue MuxAdress in PORTB eintragen
10
PORTB = ((PORTB & ~MUX_ADRESS_MASK) | MuxAdress_u8);

Gruss Peter

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.