Forum: Mikrocontroller und Digitale Elektronik C-Code -> Verständnis problem..


von Jan H. (janiiix3)


Lesenswert?

Moin Moin,

hier mal ein Code Ausschnitt..
1
void ht1632c_set(uint8_t x, uint8_t y, uint8_t val)
2
{
3
    uint8_t bitval;
4
    int addr;
5
6
    if ((x < 0) || (y < 0) || (x > 31) || (y > 7)) 
7
  {
8
      return;
9
    }
10
  
11
    bitval = 8>>(x&3);  // compute which bit will need set (Will equal 1, 2, 4, or 8)
12
    addr = ((x>>3)<<4)+(y<<1)+((x&4)>>2);  // compute which memory word this is in
13
  
14
    if (val) // Modify the shadow memory
15
  {  
16
      ht1632_shadowram[addr] |= bitval;
17
    }
18
    else 
19
  {
20
      ht1632_shadowram[addr] &= ~bitval;
21
    }
22
 
23
}

Das ganze ist ein Teil für eine 8x8 Matrix mit einem "Schattenspeicher".
Ich bin noch nicht ganz auf den Sinn von dieser Zeile gekommen..
1
    bitval = 8>>(x&3);  // compute which bit will need set (Will equal 1, 2, 4, or 8)
Wieso wird genau mit '3' verUNDET?

--
Augenkrebs.

: Bearbeitet durch User
von PittyJ (Gast)


Lesenswert?

x&3 nimmt die letzen beiden Bits von x.
Man erhält dann
0&3 -> 0
1&3 -> 1
2&3 -> 2
3&3 -> 3
4&3 -> 0
5&3 -> 1
6&3 -> 2
7&3 -> 3
.. usw

von Carl D. (jcw2)


Lesenswert?

PittyJ schrieb:
> x&3 nimmt die letzen beiden Bits von x.
> Man erhält dann
> 0&3 -> 0
> 1&3 -> 1
> 2&3 -> 2
> 3&3 -> 3
> 4&3 -> 0
> 5&3 -> 1
> 6&3 -> 2
> 7&3 -> 3
> .. usw

Und man die Acht (1000) max. drei mal nach rechts schieben kann, bevor 
sie "unten" rausfällt.

von yesitsme (Gast)


Lesenswert?

Da braucht jemand die zwei niederwertigsten Bits. Vielleicht ist der 
darunter liegende Speicher als 4-Bit pro Adresse organisiert. Hast du 
ein Datenblatt zu dem Chip?

von Jan H. (janiiix3)


Angehängte Dateien:

Lesenswert?

yesitsme schrieb:
> Da braucht jemand die zwei niederwertigsten Bits. Vielleicht ist der
> darunter liegende Speicher als 4-Bit pro Adresse organisiert. Hast du
> ein Datenblatt zu dem Chip?

Habe ich jetzt auch heraus gefunden.. Das war mir unklar wieso man das 
so auf den ersten Blick "kompliziert" gemacht hat..

Datenblatt ist im Anhang.

von Dietrich L. (dietrichl)


Lesenswert?

Was mir hier:

Jan H. schrieb:
> void ht1632c_set(uint8_t x, uint8_t y, uint8_t val)
> {
> ....
>
>     if ((x < 0) || (y < 0) || (x > 31) || (y > 7))

auffällt: da "x" und "y" unsigned int sind, kann es doch eigentlich kein 
"<0" geben!? Oder was C bzw. der Compiler daraus?

von Jan H. (janiiix3)


Lesenswert?

Jan H. schrieb:
> addr = ((x>>3)<<4)+(y<<1)+((x&4)>>2);  // compute which memory word this
> is in

Da muss man auch erstmal überlegen..
Hat jemand eine "einfache" Erklärung?

von yesitsme (Gast)


Lesenswert?

Jan H. schrieb:
> Hat jemand eine "einfache" Erklärung?

Es ist einfacher zu verdrahten wenn man ein paar Spalten/Zeilen anders 
anschließt.

von Jan H. (janiiix3)


Lesenswert?

yesitsme schrieb:
> Jan H. schrieb:
>> Hat jemand eine "einfache" Erklärung?
>
> Es ist einfacher zu verdrahten wenn man ein paar Spalten/Zeilen anders
> anschließt.

Meinst Du wirklich das es etwas mit der Verdrahtung zu tun hat?

von Jan H. (janiiix3)


Lesenswert?

Scheint wirklich immer in 4 Bits unterteilt zu sein.
Addr. 0x01 ist das erste Nibble vom Byte, Addr. 0x02 dann das zweite 
usw...

von Falk B. (falk)


Lesenswert?

Jan H. schrieb:
> Jan H. schrieb:
>> addr = ((x>>3)<<4)+(y<<1)+((x&4)>>2);  // compute which memory word this
>> is in
>
> Da muss man auch erstmal überlegen..
> Hat jemand eine "einfache" Erklärung?

Das sind halt viele Bitmanipulationen in einer Zeile. FÜr BASCOM-Fans 
eher unverdaulich ;-)

addr = ((x>>3)<<4)+(y<<1)+((x&4)>>2);

(x>>3) schiebe X um 3 Bit nach rechts, sprich Division durch 8
((x>>3)<<4) schiebe das Ergebnis wieder um 4 Bit nach links damit wird 
erreicht, daß die unteren 4 Bit 0 sind.
(y<<1) Y um 1 Bit nachnlinks Schieben, Multiplikation mit 2
(x&4) Maskierung von Bit #2, alle anderen werden auf 0 gesetzt
>>2 Das Ergebnis 2 Bit rechts schieben, Division durch 4

Auf Seite 5 vom Datenblatt sieht man die Speichermatrix, daraus kann man 
die Berechnung der Adresse ableiten.

von Falk B. (falk)


Lesenswert?

Jan H. schrieb:
> Scheint wirklich immer in 4 Bits unterteilt zu sein.
> Addr. 0x01 ist das erste Nibble vom Byte, Addr. 0x02 dann das zweite
> usw...

Ja logisch, schon mal SELBER in das Datenblatt geschaut! Das sind 64x4 
Speicherbits!

von Joe F. (easylife)


Lesenswert?

Dieser Code hat Optimierungspotential.
1
if ((x < 0) || (y < 0) || (x > 31) || (y > 7)) 
2
{
3
}

Das würde ich z.B. komplett weglassen, und darauf achten, dass beim 
Funktionsaufruf die Grenzen von x und y eingehalten werden.
Testen auf negative Werte macht aus bereits genannten Gründen eh keinen 
Sinn.

1
addr = ((x>>3)<<4)+(y<<1)+((x&4)>>2);

Hier würde ich eine Look-Up-Table vorziehen.
Insgesamt benötigt diese dann 64 Einträge (Bytes).

1
const uint8_t addr_lut[8][8] = {
2
(...)
3
};
4
5
addr = addr_lut[y][x>>2];

: Bearbeitet durch User
von Jan H. (janiiix3)


Lesenswert?

Joe F. schrieb:
> Dieser Code hat Optimierungspotential.
>
> Hier würde ich eine Look-Up-Table vorziehen.
> Insgesamt benötigt diese dann 64 Einträge (Bytes).
>
>
>
1
> const uint8_t addr_lut[8][8] = {
2
> (...)
3
> };
4
> 
5
> addr = addr_lut[y][x>>2];
6
>

Schicke Idee, Wie komme ich jetzt auf die Adressen? Ist sicherlich sehr 
simpel?

von Falk B. (falk)


Lesenswert?

Jan H. schrieb:
> Schicke Idee, Wie komme ich jetzt auf die Adressen? Ist sicherlich sehr
> simpel?

Die stehen in der Tabelle . . .

Wobei das hier Overkill ist, die Berechnung tut es auch und ist nicht 
wesentlich länger.

von Tim T. (tim_taylor) Benutzerseite


Lesenswert?

Jan H. schrieb:
> Schicke Idee, Wie komme ich jetzt auf die Adressen? Ist sicherlich sehr
> simpel?

Scharf nachdenken oder von der Routine für alle gültigen Kombinationen 
von x und y ausgeben lassen.

Gehe mal davon aus das deine Frage darauf abzielt woher du die Werte für 
die Tabelle bekommst.

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Ich glaub die Adressberechnung geht deutlich einfacher und 
übersichtlicher.
1
addr = y<<1;
2
if (x<4) addr++;

von Dr. Sommer (Gast)


Lesenswert?

Joe F. schrieb:
> Hier würde ich eine Look-Up-Table vorziehen.

Warum überhaupt? Ein Haufen Zahlen in einem Array ist wohl eher nicht 
lesbarer als ein paar Bit-Manipulationen.

von Dr. Sommer (Gast)


Lesenswert?

Falk B. schrieb:
> Ich glaub die Adressberechnung geht deutlich einfacher und
> übersichtlicher.

Glaube ich nicht.
z.B. x=31, y=0.

((x>>3)<<4)+(y<<1)+((x&4)>>2) ist dann 49. Bei dir kommt 0 raus.

Ich finde die Formel gar nicht so schlimm. Man könnte noch
1
addr = ((x & 0xF8)<<1)|(y<<1)|((x>>2)&1);
draus machen, ist aber Geschmackssache. Der Compiler wird eh die 
effizienteste Variante wählen. Ein guter Kommentar würde es vielleicht 
richten:
1
Bildet die Bits von x und y auf addr ab:
2
Bits 3-4 von x auf 4-5
3
Bits 0-2 (alle) von y auf 1-3
4
Bit    2 von x auf 0

von Falk B. (falk)


Lesenswert?

Dr. Sommer schrieb:

>> Ich glaub die Adressberechnung geht deutlich einfacher und
>> übersichtlicher.
>
> Glaube ich nicht.
> z.B. x=31, y=0.

Dann hat irgendwer x und y verwechselt. Auch im Originalcode!

von Jan H. (janiiix3)


Lesenswert?

Dr. Sommer schrieb:
> ((x>>3)<<4)+(y<<1)+((x&4)>>2) ist dann 49. Bei dir kommt 0 raus.
>
> Ich finde die Formel gar nicht so schlimm. Man könnte nochaddr = ((x &
> 0xF8)<<1)|(y<<1)|((x>>2)&1);draus machen, ist aber Geschmackssache. Der
> Compiler wird eh die
> effizienteste Variante wählen. Ein guter Kommentar würde es vielleicht
> richten:
> Bildet die Bits von x und y auf addr ab:
> Bits 3-4 von x auf 4-5
> Bits 0-2 (alle) von y auf 1-3
> Bit    2 von x auf 0

Da gebe ich Dir vollkommen Recht. Wenn man nicht gerade den Code 
geschrieben hat und nicht wirklich das "Vorgehen" versteht, hätten ein 
paar Zeilen Kommentar einiges leichter gemacht.

von Yalu X. (yalu) (Moderator)


Angehängte Dateien:

Lesenswert?

Der Chip ist ja für Displays mit 32 Zeilen und 8 Spalten (32z×8s), oder
wahlweise auch 24 Zeilen und 16 Spalten (24z6s) gemacht.

Die Routine aus dem Eröffnungsbeitrag scheint für 8z×32s-Displays
vorgesehen zu sein:

1
    if ((x < 0) || (y < 0) || (x > 31) || (y > 7))

Am einfachsten wäre es, das 8z×32s-Display wie ein um 90° gedrehtes
32z×8s-Display zu behandeln (oben im Bild). Dann ist

1
  bitval = 1 << (x & 3);
2
  addr   = (x << 1) + (y >> 2);

Möglicherweise hat es jemanden gestört, dass bei dieser Vorgehensweise
Zeilen zu Spalten und Spalten zu Zeilen werden, und das Display deswegen
so angeschlossen wie unten im Bild gezeigt. Dadurch wir die Berechnung
der Adresse etwas komplizierter.

Jan H. schrieb:
> Das ganze ist ein Teil für eine 8x8 Matrix

Wenn nur ein 8×8-Display angeschlossen ist, ist x<8, so dass

1
    addr = ((x>>3)<<4)+(y<<1)+((x&4)>>2);

vereinfacht werden kann zu

1
    addr = (y << 1) + (x >> 2);

was (mit vertauschten x und y) exakt dem Ausdruck von oben entspricht.

von Jan H. (janiiix3)


Lesenswert?

Yalu X. schrieb:
> Der Chip ist ja für Displays mit 32 Zeilen und 8 Spalten (32z×8s), oder
> wahlweise auch 24 Zeilen und 16 Spalten (24z6s) gemacht.
>
Das mit der "umständlichen" Berechnung muss schon so sein. Das Display 
ist anscheinend so verdrahtet.
Die "if" Abfrage kann natürlich verbessert werden.

von yesitsme (Gast)


Lesenswert?

Jan H. schrieb:
> Meinst Du wirklich das es etwas mit der Verdrahtung zu tun hat?

Ja. Warum sollte man sonst x in 2 Teile teilen, auseinander schieben um 
Platz für y zu bekommen?


Yalu X. schrieb:
> Möglicherweise hat es jemanden gestört, dass bei dieser Vorgehensweise
> Zeilen zu Spalten und Spalten zu Zeilen werden, und das Display deswegen
> so angeschlossen wie unten im Bild gezeigt. Dadurch wir die Berechnung
> der Adresse etwas komplizierter.

Vielleicht ist
1
- - - -
2
3
- - - -
besser zu routen als
1
| || || || |
("-" bzw. "|" = 8 Pins eines LED Matrix Modules in Reihe)

von yesitsme (Gast)


Lesenswert?

Joe F. schrieb:
> Dieser Code hat Optimierungspotential.
> if ((x < 0) || (y < 0) || (x > 31) || (y > 7))
> {
> }
>
> Das würde ich z.B. komplett weglassen, und darauf achten, dass beim
> Funktionsaufruf die Grenzen von x und y eingehalten werden.

Im grunde will jeder Benutzer dieser Funktion die Prüfung haben. Also 
warum die Verantwortung hier an alle Benutzer geben anstatt es einmal an 
Zentraler Stelle zu lösen?


> Testen auf negative Werte macht aus bereits genannten Gründen eh keinen
> Sinn.

Wenns kein undefined behaviour ist, würde ich das der 
Vollständigkeithalber drin lassen.

von Dr. Sommer (Gast)


Lesenswert?

yesitsme schrieb:
> Wenns kein undefined behaviour ist, würde ich das der
> Vollständigkeithalber drin lassen.

Der Compiler wird den Vergleich einfach wegoptimieren und ggf. warnen.

yesitsme schrieb:
> Im grunde will jeder Benutzer dieser Funktion die Prüfung haben

Man könnte auch in der Funktion ein assert() verwenden. Werte außerhalb 
des Bereichs zu übergeben ist ein Fehler, der dann in der Debug-Version 
zum Abbruch führt.

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.