Hi,
ich bin aktuell dran einen TLC5947 anzusteuern. Das funktioniert soweit,
Daten in einem Array vorgeben etc klappt problemlos.
Jetzt hab ich mir eine Funktion geschrieben, die ich einfach mit dem
Ausgang (Kanal 0..23) fütter und dem PWM Wert.
Bei geraden Kanälen klappt das Problemlos (0, 2, 4, 6...), nur die
Ungeraden wollen nicht. Die richtigen Byte's im Array werden
geschrieben, allerdings ist die Helligkeit viel zu hoch. Selbst bei 10
(von 4095) sind die LEDs an den ungeraden Kanälen extrem Hell.
Meine Funktion: 1 | char lightdata[36] = {
| 2 | 0x00,0x00,0x00,0x00,0x00,0x00,
| 3 | 0x00,0x00,0x00,0x00,0x00,0x00,
| 4 | 0x00,0x00,0x00,0x00,0x00,0x00,
| 5 | 0x00,0x00,0x00,0x00,0x00,0x00,
| 6 | 0x00,0x00,0x00,0x00,0x00,0x00,
| 7 | 0x00,0x00,0x00,0x00,0x00,0x00
| 8 | };
| 9 |
| 10 | void SetLight(uint8_t kanal, uint16_t data)
| 11 | {
| 12 | if (kanal % 2 == 0) //Auf Gerade Zahl prüfen
| 13 | {
| 14 | kanal *= 1.5;
| 15 | lightdata[kanal] = data;
| 16 | lightdata[kanal+1] = data<<8 | (lightdata[kanal+1] & 0x0f);
| 17 | }
| 18 | else
| 19 | {
| 20 | kanal *= 1.5;
| 21 | lightdata[kanal] = data | (lightdata[kanal] & 0xf0);
| 22 | lightdata[kanal+1] = data<<8;
| 23 | }
| 24 | }
| 25 |
| 26 |
| 27 | int main(void)
| 28 | {
| 29 | while(1)
| 30 | {
| 31 | SetLight(5, 10);
| 32 | }
| 33 | }
|
Die Multiplikation mit 1.5 ist um vom Kanal auf das richtige Byte zu
kommen (12Bit PWM = 1 1/2Byte). Das geht wie gesagt Problemlos, die
Richtige Zahl kommt bei raus.
Problem ist die Helligkeit.
Wo liegt mein Fehler, bzw Denkfehler? Ich starr da jetzt schon über eine
Stunde drauf.
Bin dankbar für jede Hilfe!
Martin K. schrieb:
> kanal *= 1.5
Das hat zwar mit deinem eigentlichen Problem vermutlich nichts zu tun,
aber bist du sicher, dass bei der Multiplikation eines u8int_t mit einer
float-Konstante wirklich immer das richtige heraus kommt?
Der Wert von kanal wird dabei nämlich zunächst nach float gewandelt,
dann kommt die (float-)Multiplikation mit 1.5, dann wird zurück nach
u8int_t gecastet. (Ist im erzeugten Assemblercode auch gut zu sehen.)
1 | kanal *= 1.5;
| 2 | f4: 68 2f mov r22, r24
| 3 | f6: 70 e0 ldi r23, 0x00 ; 0
| 4 | f8: 80 e0 ldi r24, 0x00 ; 0
| 5 | fa: 90 e0 ldi r25, 0x00 ; 0
| 6 | fc: 0e 94 b8 03 call 0x770 ; 0x770 <__floatsisf>
| 7 | 100: 20 e0 ldi r18, 0x00 ; 0
| 8 | 102: 30 e0 ldi r19, 0x00 ; 0
| 9 | 104: 40 ec ldi r20, 0xC0 ; 192
| 10 | 106: 5f e3 ldi r21, 0x3F ; 63
| 11 | 108: 0e 94 86 02 call 0x50c ; 0x50c <__mulsf3>
| 12 | 10c: 46 2f mov r20, r22
| 13 | 10e: 57 2f mov r21, r23
| 14 | 110: 68 2f mov r22, r24
| 15 | 112: 79 2f mov r23, r25
| 16 | 114: cb 01 movw r24, r22
| 17 | 116: ba 01 movw r22, r20
| 18 | 118: 0e 94 99 00 call 0x132 ; 0x132 <__fixunssfsi>
|
Mit einer kleinen Umformulierung (kanal += kanal / 2) wird
Integer-Arithmetik verwendet und viel weniger Code erzeugt: 1 | kanal += kanal / 2;
| 2 | d8: e8 2f mov r30, r24
| 3 | da: e6 95 lsr r30
| 4 | dc: e8 0f add r30, r24
|
Martin K. schrieb:
Du willst die Daten ja wohl so anordnen: 1 | ------------------------------------------------
| 2 | | lightdata[2] | lightdata[1] | lightdata[0] |
| 3 | ------------------------------------------------
| 4 | |B,A,9,8,7,6,5,4|3,2,1,0,B,A,9,8|7,6,5,4,3,2,1,0|
| 5 | ------------------------------------------------
| 6 | |7,6,5,4,3,2,1,0|7,6,5,4,3,2,1,0|7,6,5,4,3,2,1,0|
| 7 | ------------------------------------------------
|
wobei 0 das LSB und B das MSB des PWM-Werts ist. (Bits 0 bis 11 von
data.
Schaun wir mal.
Gerade Kanalnummer
> lightdata[kanal] = data;
Hm. Implizites Abschneiden widerstrebt mir immer etwas. data & 0xFF wäre
etwas sicherer. Aber sei's drum.
> lightdata[kanal+1] = data<<8 | (lightdata[kanal+1] & 0x0f);
Nein.
Du willst die Bits 8 bis 11 von data in die Bits 0 bis 3 von
lightdata[kanal] packen.
Der passende Ausdruck ist entweder
((data >> 8) & 0x0F) oder
((data & 0x0F) >> 8)
Die oberen vier Bit von lightdata[kanal+1] sollen erhalten bleiben, die
unteren vier für das bitweise Oder gelöscht werden.
Der passende Ausdruck ist lightdata[kanal+1] & 0xF0
Also insgesamt: 1 | lightdata[kanal+1] = ((data >> 8) & 0x0F) | (lightdata[kanal+1] & 0xF0);
|
Jetzt der ungerade Kanal:
> lightdata[kanal] = data | (lightdata[kanal] & 0xf0);
Auch hier stimmt die Bitmaske für die "Altdaten" nicht und data muss um
vier bit nach links geschoben werden. 1 | lightdata[kanal] = ((data & 0x0F) << 4) | (lightdata[kanal] & 0x0F);
|
> lightdata[kanal+1] = data<<8;
Du willst Bit 4 bis 11 haben. Also 1 | lightdata[kanal+1] = (data >> 4) & 0xFF;
|
So. Das Ergebnis des Compiler-Durchlauf kommt gleich.
Grüße
Stefan
Stefan Wagner schrieb:
> Das Ergebnis des Compiler-Durchlauf kommt gleich.
1 | #include <stdint.h>
| 2 |
| 3 | char lightdata[36] = {
| 4 | 0x00,0x00,0x00,0x00,0x00,0x00,
| 5 | 0x00,0x00,0x00,0x00,0x00,0x00,
| 6 | 0x00,0x00,0x00,0x00,0x00,0x00,
| 7 | 0x00,0x00,0x00,0x00,0x00,0x00,
| 8 | 0x00,0x00,0x00,0x00,0x00,0x00,
| 9 | 0x00,0x00,0x00,0x00,0x00,0x00
| 10 | };
| 11 |
| 12 | void SetLight(uint8_t kanal, uint16_t data)
| 13 | {
| 14 | if (kanal % 2 == 0) //Auf Gerade Zahl prüfen
| 15 | {
| 16 | kanal += kanal / 2;
| 17 | lightdata[kanal] = data; //& 0xFF;
| 18 | lightdata[kanal+1] = ((data >> 8) & 0x0F) | (lightdata[kanal+1] & 0xF0);
| 19 | }
| 20 | else
| 21 | {
| 22 | kanal += kanal / 2;
| 23 | lightdata[kanal] = ((data & 0x0F) << 4) | (lightdata[kanal] & 0x0F);
| 24 | lightdata[kanal+1] = (data >> 4); // & 0xFF;
| 25 | }
| 26 | }
| 27 |
| 28 |
| 29 | int main(void)
| 30 | {
| 31 | while(1)
| 32 | {
| 33 | SetLight(5, 10);
| 34 | }
| 35 | }
|
lässt sich compilieren, aber ich habe auf diesem PC keinen lauffähigen
Simulator (sieht aus, als vertrügen sich AVR Studio 4 und Atmel Studio 6
nicht richtig.)
Na, probier es mal aus und gib eine Rückmeldung.
Grüße
Stefan
PS: Zu den beiden auskommentierten Ausdrücken "& 0xFF;": Ich habe das
explizite Wegmaskieren des high byte jetzt doch mal auskommentiert.
Hi,
erstmal vielen dank! Das mit dem float vermeiden werd ich definitiv so
übernehemn.
Dein Vorschlag Stefan funktioniert leider nicht, ich habs einfach mal
1:1 übernommen
Als Beispiel:
Angeschlossen ist es immer Kanal 0 Blue, Kanal 1 Green, Kanal 2, Red.
Schreib ich "SetLight(0, 40);" sind die LEDs blau.
Schreib ich "SetLight(0, 4000);" werden die LEDs türkis, also es kommt
viel grün mit.
SetLight(1, 40); sollte grün sein, ist aber blau.
Ich versteh aktuell nicht ganz, warum du >> verschiebst, nicht << ? Ich
möchte doch Bit 0-7 vom uint16_t an die Stelle 8-15 bringen?
Ich spiel mal noch ein wenig rum.
Martin K. schrieb:
> funktioniert leider nicht
Stimmt, hab ich gerade auch gemerkt. Für ungerade Kanäle war der Index
falsch, da hab ich beim Kopieren des Codes nicht aufgepasst.
1 | void SetLight(uint8_t kanal, uint16_t data)
| 2 | {
| 3 | if (kanal % 2 == 0) //Auf Gerade Zahl prüfen
| 4 | {
| 5 | kanal += kanal / 2;
| 6 | lightdata[kanal] = data; //& 0xFF;
| 7 | lightdata[kanal+1] = ((data >> 8) & 0x0F) | (lightdata[kanal+1]&0xF0);
| 8 | }
| 9 | else
| 10 | {
| 11 | kanal += kanal / 2;
| 12 | lightdata[kanal+1] = ((data&0x0F) << 4) | (lightdata[kanal+1]&0x0F);
| 13 | lightdata[kanal+2] = (data >> 4);
| 14 | }
| 15 | }
|
> Ich versteh aktuell nicht ganz, warum du >> verschiebst, nicht << ? Ich
> möchte doch Bit 0-7 vom uint16_t an die Stelle 8-15 bringen?
Sagst du bitte, welche Stelle im Code du meinst?
Grüße
Stefan
Ich meine bei 1 | lightdata[kanal+1] = ((data >> 8) & 0x0F) | (lightdata[kanal+1]&0xF0);
| 2 | |H i e r|
|
Muss ich nicht 8bits nach links schieben, damit die bits 0-7 an der
Stelle 8-15 sitzen, damit.....
moment... denkfehler.
bei uint8_t = uint16_t hol ich mir ja die unteren 8bits aus dem 16bit
int, nicht, nicht die oberen.
Leider funktioniert das mit den ungeraden noch immer nicht. Die Geraden
gehen wie gehabt. Interessanterweise geht auch meine Version, obwohl ich
die bits ja falsch rum schiebe?! Ich glaub ich mach heute mal schluss.
Die ungeraden zeigen wie gesagt weiterhin seltsames verhalten, der kanal
sollte ja durch stimmen. Mit dem +1 und +2 landet man dann im falschen Byte.
Die Helligkeit ändert sich nicht und es ist bei "SetLight(3, 2500);"
Kanal 2 (rot) und Kanal 3 (Blau) leicht am glimmen. Höhere Werte lassen
das rot dunkler werden, das blau leicht heller. Bei niedriegeren werten
kommt grün dazu (Kanal 4). Also ganz kurios...
So, nochmal vielen dank für die super hilfe!
Es funktioniert jetzt: 1 | if (kanal % 2 == 0) //Auf Gerade Zahl prüfen
| 2 | {
| 3 | kanal += kanal / 2;
| 4 | lightdata[kanal] = data & 0xff;
| 5 | lightdata[kanal+1] = data >> 8 | (lightdata[kanal+1] & 0xf0);
| 6 | }
| 7 | else
| 8 | {
| 9 | kanal += kanal / 2;
| 10 | lightdata[kanal] = data << 4 | (lightdata[kanal] & 0x0f);
| 11 | lightdata[kanal+1] = data >> 4;
| 12 | }
|
Hab heute nochmal alles in Ruhe aufgeschrieben und Schritt für Schritt
vor gegangen. Dabei ist mir auch noch aufgefallen, das ich die Bytes
falsch herum raus sende (Schieberegister...)
Wie gesagt, es geht! Und nochmal danke für die Hilfe.
Stefan Wagner schrieb:
> Für ungerade Kanäle war der Index falsch,
Das war natürlich falsch. (kanal + kanal / 2) zeigt schon an die
richtige Stelle. Aber das hast du ja auch gemerkt.
Martin K. schrieb:
> Hab heute nochmal alles in Ruhe aufgeschrieben und Schritt für Schritt
> vorgegangen. Dabei ist mir auch noch aufgefallen, das ich die Bytes
> falsch herum raus sende (Schieberegister...)
Ok, das konnte ich natürlich nicht sehen.
> Wie gesagt, es geht! Und nochmal danke für die Hilfe.
Bitte, gerne.
Vielleicht noch ein Tipp: Spiel mal verschiedene Bitschiebereien und
Bitmaskierereien im Simulator durch (Optimierung dafür maximal auf -O1
stellen). Das gibt ein besseres Gefühl dafür, was da passiert.
Grüße
Stefan
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|