Hallo Forum,
ich habe gerade heute eine Software SPI für 3 LED Treiber geschrieben,
die jeweils die Datenpins (SIN1 bis SIN3) an einem Pin von einem mega88
verbunden hat.
Es funktioniert auch alles soweit ganz hervorragend, allerdings bin ich
mir nicht sicher ob man die Routine nicht vielleicht ein wenig schneller
machen kann, da sie in einer for Schleife hängt und sich somit schon
wenige Clocks bemerkbar machen würden.
Hinter SIN1 bis SIN3 verbirgt sich nichts weiteres als PORTD.3 bis
PORTD.5, was mein Compiler (CodevisionAVR) bestimmt recht efficient
umsetzt.
Daten[] ist ein Array aus dem Struct:
1
structdata
2
3
{
4
unsignedchargs[24];
5
unsignedchardc[12];
6
};
7
8
// Beginn der SPI MSB first
9
SIN1=Daten[0].dc[i]>>7;
10
SIN2=Daten[1].dc[i]>>7;
11
SIN3=Daten[2].dc[i]>>7;
12
SCLK=HIGH;
13
SCLK=LOW;
14
15
SIN1=(Daten[0].dc[i]&0b01000000)>>6;
16
SIN2=(Daten[1].dc[i]&0b01000000)>>6;
17
SIN3=(Daten[2].dc[i]&0b01000000)>>6;
18
SCLK=HIGH;
19
SCLK=LOW;
20
21
SIN1=(Daten[0].dc[i]&0b00100000)>>5;
22
SIN2=(Daten[1].dc[i]&0b00100000)>>5;
23
SIN3=(Daten[2].dc[i]&0b00100000)>>5;
24
SCLK=HIGH;
25
SCLK=LOW;
26
27
SIN1=(Daten[0].dc[i]&0b00010000)>>4;
28
SIN2=(Daten[1].dc[i]&0b00010000)>>4;
29
SIN3=(Daten[2].dc[i]&0b00010000)>>4;
30
SCLK=HIGH;
31
SCLK=LOW;
32
33
SIN1=(Daten[0].dc[i]&0b00001000)>>3;
34
SIN2=(Daten[1].dc[i]&0b00001000)>>3;
35
SIN3=(Daten[2].dc[i]&0b00001000)>>3;
36
SCLK=HIGH;
37
SCLK=LOW;
38
39
SIN1=(Daten[0].dc[i]&0b00000100)>>2;
40
SIN2=(Daten[1].dc[i]&0b00000100)>>2;
41
SIN3=(Daten[2].dc[i]&0b00000100)>>2;
42
SCLK=HIGH;
43
SCLK=LOW;
44
45
SIN1=(Daten[0].dc[i]&0b00000010)>>1;
46
SIN2=(Daten[1].dc[i]&0b00000010)>>1;
47
SIN3=(Daten[2].dc[i]&0b00000010)>>1;
48
SCLK=HIGH;
49
SCLK=LOW;
50
51
SIN1=Daten[0].dc[i]&0b00000001;
52
SIN2=Daten[1].dc[i]&0b00000001;
53
SIN3=Daten[2].dc[i]&0b00000001;
54
SCLK=HIGH;
55
SCLK=LOW;
wäre super wenn da jemand eine Idee hätte
Gruß
Kai
Hallo Kai,
schau doch mal, ob dein compiler immer neu auf das Array zugreift.
"Daten[0].dc[i]" wird 8 mal benötigt. ([1][2] auch)
Evtl. vorne eine Zuweisung Dummy_0 (1 2) die im Register
gehalten wird.
Kurz im Assemblerlisting prüfen.
Gruß Hans
Da ich mich fast nicht mit Assembler auskenne hab ich mal mittendrin den
entsprechenden Teil des Codes herauskopiert:
1
CBI 0xB,6
2
3
; 165
4
5
; 166 SIN1 = (Daten[0].dc[i] & 0b01000000) >> 6;
6
7
__POINTW1MN _Daten,24
8
9
ADD R30,R16
10
11
ADC R31,R17
12
13
LD R30,Z
14
15
ANDI R30,LOW(0x40)
16
17
SWAP R30
18
19
ANDI R30,0xF
20
21
LSR R30
22
23
LSR R30
24
25
CPI R30,0
26
27
BRNE _0x1F
28
29
CBI 0xB,3
30
31
RJMP _0x20
32
33
_0x1F:
34
35
SBI 0xB,3
36
37
_0x20:
38
39
; 167 SIN2 = (Daten[1].dc[i] & 0b01000000) >> 6;
40
41
__POINTW1MN _Daten,60
42
43
ADD R30,R16
44
45
ADC R31,R17
46
47
LD R30,Z
48
49
ANDI R30,LOW(0x40)
50
51
SWAP R30
ich hatte überlegt ob es nicht vielleicht noch eine geschicktere
Möglichkeit gibt die Bits zu maskieren, weil ich sie ja alle der Reihe
nach brauche, bin aber zu keinem Ergebnis gekommen
hab es jetzt mal für die gesamte Senderoutine geändert und statt 4,6
Sekunden braucht er jetzt nur noch 4,0
Ist zwar alles von Hand gemessen, aber es hat sich schonmal gelohnt,
vielen Dank :)
Das Programm ist gleichzeitig auch noch kleiner geworden
Hat zur Maskierung vielleicht jemand noch eine Idee?
Ich hatte überlegt ob es vielleicht möglich ist das Byte immer um eins
nach links zu verschieben und abzufragen ob es einen overflow gibt oder
irgendetwas in der Art. Die Größe des Programmcodes ist erstmal egal.
Wäre für weiteres Input sehr dankbar :)
Gruß
Kai
Hallo
das ist eine ganz nette Idee, allerdings kann ich mir nicht wirklich
vorstellen, dass sie schneller ist, weil ich bei deiner Version zweimal
auf den Datenpin SIN123 zugreifen muss und da sich dahinter auch eine
Maskierung eines Bits versteckt, würde ich mal tippen, dass es langsamer
ist, werde es aber morgen mal versuchen. Heute studiere ich nochmal den
geposteten Beitrag
Sind 6 Ticks pro SINX (wenn Dein Compiler gut ist).
avr-gcc macht das:
1
ldi r19,lo8(7)
2
.L107:
3
cbi 56-0x20,0
4
sbrc r18,7
5
sbi 56-0x20,0
6
lsr r18
7
cbi 56-0x20,1
8
sbrc r25,7
9
sbi 56-0x20,1
10
lsr r25
11
cbi 56-0x20,2
12
sbrc r24,7
13
sbi 56-0x20,2
14
lsr r24
15
sbi 56-0x20,3
16
cbi 56-0x20,3
17
subi r19,lo8(-(-1))
18
brpl .L107
Spart zum einen Platz und ist schneller als Du denkst, weil das genze
Rumgeschiebe wegfällt. Schneller geht's dann wenn man die Schleife noch
aufrollt, und noch mehr rausholen geht per Assembler wie im Link oben.
der Assembler sagt mir zwar nicht sehr viel ich werde es morgen aber mal
ausprobieren, dass ich die Schleife auflöse und mir damit das >>= 1
erspare
Ich werde berichten ob es einen Unterschied macht.
Sollte ich eigentlich generell die Daten erst aus dem Struct holen und
zwischenspeichern auch wenn ich sie nur zweimal brauche? Würde sich ja
fast anbieten
Konkret geht es um eine Array Konvertierung, die jedes Mal vor der
Senderoutine abläuft um die Daten invertiert hintereinander zu ordnen
1
Daten[i].gs[0]=single_color[i].gs[15]>>4;
2
3
Daten[i].gs[1]=single_color[i].gs[15]<<4;
4
5
Daten[i].gs[1]|=single_color[i].gs[14]>>8;
6
7
Daten[i].gs[2]=single_color[i].gs[14];
8
9
10
11
Daten[i].gs[3]=single_color[i].gs[13]>>4;
12
13
Daten[i].gs[4]=single_color[i].gs[13]<<4;
14
15
Daten[i].gs[4]|=single_color[i].gs[12]>>8;
16
17
Daten[i].gs[5]=single_color[i].gs[12];
18
19
20
21
Daten[i].gs[6]=single_color[i].gs[11]>>4;
22
23
Daten[i].gs[7]=single_color[i].gs[11]<<4;
24
25
Daten[i].gs[7]|=single_color[i].gs[10]>>8;
26
27
Daten[i].gs[8]=single_color[i].gs[10];
28
29
30
31
Daten[i].gs[9]=single_color[i].gs[9]>>4;
32
33
Daten[i].gs[10]=single_color[i].gs[9]<<4;
34
35
undnochvielweiter:P
Vielen Dank schonmal für die Hilfe
PS: Deine Homepage ist sehr schön und übersichtlich gestaltet und vor
allem hast du komplett fertige Projekte vorzuweisen!! Respekt :)
Kai Franke wrote:
> Sollte ich eigentlich generell die Daten erst aus dem Struct holen und> zwischenspeichern auch wenn ich sie nur zweimal brauche? Würde sich ja> fast anbieten
Ob es notwendig ist hängt von Deinen Geschwindigkeitsvorgaben ab und
davon, wie gut der Compiler optimiert und wieviel Informationen er
bekommt.
1
>Daten[i].gs[0]=single_color[i].gs[15]>>4;
2
>Daten[i].gs[1]=single_color[i].gs[15]<<4;
3
>Daten[i].gs[1]|=single_color[i].gs[14]>>8;
4
>Daten[i].gs[2]=single_color[i].gs[14];
Wenn Daten[i].gs[0] zur gleichen Adresse auflöst wie
single_color[i].gs[15], dann ergeben sich zB andere Werte, als wenn das
nicht der Fall ist.
Nun könnte man einwänden, das sei pathologisch und wer Symbole so
definiert sei selber Schuld. Dennoch darf ein Compiler Optimierungen
nicht machen, wenn er die Ungleichheit nicht nachweisen kann. Und
Schalter wie -optimize-if-code-not-braindead hab ich noch nie gesehen
;-)
Um guten Code aus nem Compiler zu bekommen, ist es lohnend, sich
anzuschauen, was er aus dem C-Code so bastelt und bei welchen
C-Konstrukten sich Hilfestellung lohnt. Auf dem PC ist einem das
Wurscht, aber auf nem kleinen µC ist die Gemengelage etwas anders. Und
Asseneber-Code zu überfliegen und kurz zu beurteilen ist wesentlich
weniger Arbeit, als selber in Assembler zu proggen.
ZB erkennt man mehrfache Speicherzugriffe, und kann dem Compiler dann
Hilfestellung über lokale Vaiablen geben, in die man die Werte lädt.
Der C-Code wird dadurch nicht schöner, aber was man an asm-Instruktionen
spart, spart man dann an Speicher und an Laufzeit.
Hallo,
wie versprochen habe ich das heute alles mal ausprobiert und habe
durchaus wieder etwas Zeit eingespart. Jetzt wollte ich das ganze auch
noch bei der Arraykonvertierung ausnutzen, jedoch stimmt da irgendetwas
noch nicht.
So funktioniert der Code:
1
Daten[i].dc[0]=single_color[i].dc[15]<<2;
2
Daten[i].dc[0]|=single_color[i].dc[14]>>4;
3
Daten[i].dc[1]=single_color[i].dc[14]<<4;
4
Daten[i].dc[1]|=single_color[i].dc[13]>>2;
5
Daten[i].dc[2]=single_color[i].dc[13]<<6;
6
Daten[i].dc[2]|=single_color[i].dc[12];
7
...usw...
so allerdings nicht mehr und ich sehe einfach nicht wieso
1
dummy2=single_color[i].dc[15]<<2;
2
dummy3=single_color[i].dc[14];
3
dummy2|=dummy3>>4;
4
Daten[i].dc[0]=dummy2;
5
6
dummy2=dummy3<<4;
7
dummy3=single_color[i].dc[13];
8
dummy2|=dummy3>>2;
9
Daten[i].dc[1]=dummy2;
10
11
dummy2=dummy3<<6;
12
dummy2|=single_color[i].dc[12];
13
Daten[i].dc[2]=dummy2;
es handelt sich bei allen Variablen um unsigned chars
Entwarnung!
Der Code funktionert so wie ich ihn gepostet habe, ich dachte nur, dass
an der Übertragung was nicht stimmt, weil die LEDs angefangen haben zu
flackern. Das war aber nur so, weil ich während der Datenübertragung
alle LEDs ausgeschaltet hatte (ist laut Datenblatt empfohlen) und die
Konvertierung jetzt so schnell ging, dass man dieses AN und AUS schalten
als Flackern gesehen hat. Ich habe jetzt den netten Hinweis des
Datenblatts ignoriert und es funktioniert wunderbar.
Wenn jemand eine Ansteuerung für den TLC5946 braucht, soll er sich bei
mir melden, ich stelle es aber auch noch als Open Source in die
Codesammlung.
Vielen Dank an alle!
Grüße
Kai