Bits spiegeln in Hardware oder in Software?

OP (Firma: Brainit GmbH) #2993216
Lesenswert?

Hallo zusammen,

folgendes Problem: ich designe grad ein kleines Platinchen, welches 
(u.a.) einen ATmega328 und ein Display haben soll. Fürs Display brauch 
ich ein komplettes Byte (8 bits, D0..D7) da bietet sich Port D an. Die 8 
bits liegen sowohl am Display als auch am AVR recht nett nebeneinander. 
Da ich aber das Display aus Platzgründen auf der Lötseite montieren 
will, sind die bits genau verdreht (gespiegelt) also PD0->D7, PD1->D6 
usw.

Nun hab ich zwei Möglichkeiten: Layout einfach halten, und die Bits in 
Software spiegeln, oder Schaltplan und Software "sauber" halten, dafür 
ein kleiner Alptraum beim layouten (wird einseitig).

Da es ein grafisches Display ist, wird da das Spiegeln eh nicht so 
tragisch, da sowieso eine Menge Bit-Schubserei damit verbunden ist. Mehr 
Sorgen machen mir diverse Kommando-Sequenzen, hier muss auf jeden Fall 
gespiegelt werden. Allerdings sind das wieder z.T. hart codierte 
Kommandos, da könnte ich das Spiegeln ja dem Präprozessor umhängen.

Frage 1: welche Variante würdet ihr empfehlen?

Frage 2: wie spiegelt man effizient am AVR (in C!)

Frage 3: wie spiegelt man im Präprozessor?


Danke, Michi
#2993228
Lesenswert?

>dafür ein kleiner Alptraum beim layouten (wird einseitig).
Wenn Dir das schon Albträme bereitet, dann solltest Du Deinen 
Beruf/Hobby wechseln!

Das einzige Richtige: Schliesse das Teil richtig an!

-Display oder uC auf die andere LPL-Seite?
-Anderen uC Port benutzen mit passender Pin folge?
-Signale von "innen" bzw. von der anderen Seite auf das Display führen?
-Drähte (schlimmstenfalls)
Gast #2993252
Lesenswert?

> Das einzige Richtige: Schliesse das Teil richtig an!

Was für ein Blech Du da redest...

Schon mal was von Effizienz gehört? Meinst Du, das Display ginge kaputt, 
wenn man die Datenleitungen anders anschliesst? Du bist wahrscheinlich 
auch jemand, der ein RAM "richtig" anschliesst: d0-d7 an d0-d7 und 
a0-a... an a...

Wahrscheinlich müsste seine Platine größer werden, um das einseitige(!) 
Layout fassen zu können. Aber Hauptsache, das Display ist richtig 
angeschlossen... Kopfschüttel

Volker
OP (Firma: Brainit GmbH) #2993258
Lesenswert?

Eine Frage noch zur mirror() Funktion vom Peter:
1
unsigned char mirror( unsigned char n )
2
{
3
  n = ((n >> 1) & 0x55) | ((n << 1) & 0xaa); 
4
  n = ((n >> 2) & 0x33) | ((n << 2) & 0xcc); 
5
  n = ((n >> 4) & 0x0f) | ((n << 4) & 0xf0); 
6
  return n;
7
}

gibts einen Trick die Funktion in ein Preprozessor-Makro zu wandlen, 
damit der Preprozessor bei Aufruf mit konstanten Werten gleich das 
Ergebnis errechnet?
Persönliche Seite #2993282
Lesenswert?

Michael Reinelt schrieb:

> gibts einen Trick die Funktion in ein Preprozessor-Makro zu wandlen,
> damit der Preprozessor bei Aufruf mit konstanten Werten gleich das
> Ergebnis errechnet?
1
#define mirror(bits) __builtin_avr_insert_bits (0x01234567, bits, 0)

http://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/AVR-Built_002din-Functions.html#AVR-Built_002din-Functions
OP (Firma: Brainit GmbH) #2993302
Lesenswert?

Johann L. schrieb:

>
1
#define mirror(bits) __builtin_avr_insert_bits (0x01234567, bits,
2
> 0)

ich bin sprachlos....

da musste ich doch sofort mal ein testprogramm mit drei varianten 
machen:

V1:
1
uint8_t mirror1( uint8_t n )
2
{
3
  n = ((n >> 1) & 0x55) | ((n << 1) & 0xaa); 
4
  n = ((n >> 2) & 0x33) | ((n << 2) & 0xcc); 
5
  n = ((n >> 4) & 0x0f) | ((n << 4) & 0xf0); 
6
  return n;
7
}
wird zu
1
  mov r18,r24
2
  ldi r19,0
3
  lsl r18
4
  rol r19
5
  andi r18,170
6
  lsr r24
7
  andi r24,lo8(85)
8
  or r24,r18
9
  mov r18,r24
10
  ldi r19,0
11
  lsl r18
12
  rol r19
13
  lsl r18
14
  rol r19
15
  andi r18,204
16
  lsr r24
17
  lsr r24
18
  andi r24,lo8(51)
19
  or r24,r18
20
  swap r24
21
  ret

V2:
1
uint8_t mirror2( uint8_t n )
2
{
3
    return __builtin_avr_insert_bits (0x01234567, n, 0);
4
}
wird zu
1
  lsl r24
2
  adc r24,__zero_reg__
3
  mov __tmp_reg__,r24
4
  bst r0,1
5
  bld r24,7
6
  bst r0,2
7
  bld r24,6
8
  bst r0,3
9
  bld r24,5
10
  bst r0,5
11
  bld r24,3
12
  bst r0,6
13
  bld r24,2
14
  bst r0,7
15
  bld r24,1
16
  ret

Und der test mit konstantem Wert:
1
uint8_t mirror3 (void)
2
{
3
    return __builtin_avr_insert_bits (0x01234567, 0x12, 0);
4
}
wird zu
1
  ldi r24,lo8(72)
2
  ret

Sieht doch gut aus, oder?
Gast #2993364
Lesenswert?

Michael Reinelt schrieb:
> Frage 1: welche Variante würdet ihr empfehlen?

Die Standardvariante, keinen Spiegel. Lohnt sich nicht bei 8 Leitungen. 
Du kannst keine Standard Lib benutzen und das umdrehen eines Bytes 
braucht 8x so viele Takte wie das einfache senden.

Das noch bei einem Grafik Display wo große Datenmengen geschubst werden.

Da blickt man auch nicht mehr durch irgendwann (schätze so ca. nach 3 
Tagen). 8 bedrahtete null Ohm Widerstände lösen das Problem besser.
Gast #2993381
Lesenswert?

Meine Meinung:

- bei doppelseitiger Platine mit Durchkontaktierungen: richtig 
anschließen und enstprechend routen

- bei einseitiger Platine: einfach in Software die Bits vertauschen, bei 
der LCD Ansteuerung ist doch die Geschwindigkeit egal
Gast #2993441
Lesenswert?

Peter Dannegger schrieb:
> Alle Konstanten, also auch Zeichensatztabellen kann man schon zur
> Compilezeit durch das Macro jagen.
> Da kostet das Spiegeln also keine zusätzliche Laufzeit.

Ich werd mal ein bisschen akademisch (die Frage lässt sich ja durch "7 
Brücken" lösen) jetzt weiß ich auch wo der Song herkommt ;-).

Wie viele Konstanten gibt es denn so bei GLCD Ansteuerung? Fonts und 
Steuerkommandos Ok, aber alle anderen Elemente werden doch errechnet.
OP (Firma: Brainit GmbH) #2993477
Lesenswert?

Peter Dannegger schrieb:
> Alle Konstanten, also auch Zeichensatztabellen kann man schon zur
> Compilezeit durch das Macro jagen.
> Da kostet das Spiegeln also keine zusätzliche Laufzeit.

Da hatte ich meine Zweifel, da deine Funktion ja streng genommen kein 
Makro ist, und sich auch schwer in ein solches umwandlen lässt (weil n 
mehrmals zugewiesen/verwendet wird).

Allerdings steigt meine Hochachtung vor dem gcc:
1
uint8_t mirror1( uint8_t n )
2
{
3
  n = ((n >> 1) & 0x55) | ((n << 1) & 0xaa); 
4
  n = ((n >> 2) & 0x33) | ((n << 2) & 0xcc); 
5
  n = ((n >> 4) & 0x0f) | ((n << 4) & 0xf0); 
6
  return n;
7
}
8

9
uint8_t test4 (void)
10
{
11
    return mirror1(0x12);
12
}

er macht tatsächlich folgendes aus test4:
1
  ldi r24,lo8(72)
2
  ret
Gast #2993536
Lesenswert?

Meine Meinung:

Programmiere Deine Display-Module so, wie Du sie für einen normalen Port 
machen würdest. Mache Dir eine Hardware-Kapsel, wo Du an einer einzigen 
zentralen Stelle spiegelst und auf den Port schreibst.
Hat den Nachteil, daß es mehr Ressourcen frißt als eine perfekte 
Durchoptimierung, aber den Vorteil, daß Du Deine Display-Module 
unabhängig von der Hardware sind, und logisch und einfach 
nachzuvollziehen bleiben.
Hardware-Abhängigkeit sollte man immer in einer "HAL" kapseln und nicht 
quer durch ein Projekt durchziehen.
#2993537
Lesenswert?

Michael Reinelt schrieb:
> Da hatte ich meine Zweifel, da deine Funktion ja streng genommen kein
> Makro ist, und sich auch schwer in ein solches umwandlen lässt (weil n
> mehrmals zugewiesen/verwendet wird).

Daher macht man sowas auch eher mit einer Inline-Funktion. Wenn man ganz 
sicher gehen will, dass der Compiler sie inlint, kann man es auch 
erzwingen:
1
static inline __attribute__((__always_inline__)) uint8_t mirror1( uint8_t n )
2
{
3
  n = ((n >> 1) & 0x55) | ((n << 1) & 0xaa); 
4
  n = ((n >> 2) & 0x33) | ((n << 2) & 0xcc); 
5
  n = ((n >> 4) & 0x0f) | ((n << 4) & 0xf0); 
6
  return n;
7
}

Man kann die Entscheidung aber auch dem Compiler überlassen und ihm nur 
mit dem Wörtchen "inline" einen Tipp geben. Bei nicht-konstanten Werten 
kostet der Funktionsaufruf ja im Vergleich zur Codegröße nicht viel 
zusätzliche Zeit, aber ggf. recht viel Programmspeicher, wenn man ihn 
oft kopiert.

Wichtig ist auch noch, dass die Funktion in der gleichen 
Compilierungseinheit (C-Datei) definiert ist, um die Optimierung zu 
ermöglichen. Daher würde ich sie als "static inline"-Funktion in eine 
Headerdatei packen.
#2993550
Lesenswert?

Bronco schrieb:
> Hat den Nachteil, daß es mehr Ressourcen frißt als eine perfekte
> Durchoptimierung, aber den Vorteil, daß Du Deine Display-Module
> unabhängig von der Hardware sind, und logisch und einfach
> nachzuvollziehen bleiben.

Wenn man die Hardwareabstraktion ebenfalls in einer Header-Datei mit 
Inline-Funktionen macht, kostet das praktisch keine Ressourcen. :-)
OP (Firma: Brainit GmbH) #2993656
Lesenswert?

Fabian O. schrieb:
> Daher macht man sowas auch eher mit einer Inline-Funktion. Wenn man ganz
> sicher gehen will, dass der Compiler sie inlint, kann man es auch
> erzwingen:

Streng genommen hat das gar nichts mit inlining zu tun, sondern mit 
"constant folding". Und das hat mich überrascht, wie gut dieses constant 
folding funktioniert.
Gast #2995320
Lesenswert?

Michael Reinelt schrieb:
> Fabian O. schrieb:
>> Daher macht man sowas auch eher mit einer Inline-Funktion. Wenn man ganz
>> sicher gehen will, dass der Compiler sie inlint, kann man es auch
>> erzwingen:
>
> Streng genommen hat das gar nichts mit inlining zu tun, sondern mit
> "constant folding".

Ja, aber das klappt bei Funktionsparametern natürlich nur, wenn die 
Funktion auch inline aufgelöst wird.

Fabian O. schrieb:
> Man kann die Entscheidung aber auch dem Compiler überlassen und ihm nur
> mit dem Wörtchen "inline" einen Tipp geben. Bei nicht-konstanten Werten
> kostet der Funktionsaufruf ja im Vergleich zur Codegröße nicht viel
> zusätzliche Zeit, aber ggf. recht viel Programmspeicher, wenn man ihn
> oft kopiert.

Zur Not kann man auch verzweigen, abhängig davon, ob der Wert eine 
Konstante ist oder nicht. Dazu gibt's bei gcc __builtin_constant_p().

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren