Forum: Mikrocontroller und Digitale Elektronik TLC5947 und ATmega16 Bitmanipulation


von Christian K. (christiankarle)


Angehängte Dateien:

Lesenswert?

Hallo liebe Community,

Momentan bin ich gerade dabei den TLC5947 per SPI an einem ATmega16 zu 
betreiben.

Der TLC5947 ist folgendermaßen mit dem ATmega16 verbunden:


TLC5947 ---> ATmega16

XLAT    ---> PB2
BLANK   ---> PB1
SCLK    ---> PB7 (SCK)
SIN     ---> PB5 (MOSI)

Alle Ausgänge sind als Ausgänge geschalten. Der Pin PB4 (SS) am ATmega16 
ist auch auf Ausgang geschaltet (muss im Mastermode als Ausgang 
konfiguriert sein).

Der ATmega16 läuft @ 8MHz mit dem internen Oszillator.
Der SPI-Takt beträgt 1/16 * CPU-Takt.

Jetzt mal zum TLC5947:

Der TLC5947 erwartet von mir 288 Bits. Diese werden in einem Char-Array 
an den TLC5947 gesendet. Zuerst muss das MSB-Bit für Kanal 23 gesendet 
werden, dann folgen 11 weitere Bits. Das wiederholt sich dann, bis alle 
Kanäle gesendet wurden.


Meine Methode Set_Channel_Brightness (uint8_t Channel,uint16_t 
Brightness) überprüft, ob der vom Benutzer eingegebene Kanal (Channel) 
gerade oder ungerade ist und führt daraufhin die Bitmanipulationen am 
Array durch, indem es die Position der LSB-Bits aus dem Char-Array LSB 
holt, welches die Position des LSB-Bits im Array lightdata darstellt und 
daraufhin die Bits verändert.

Soweit so gut...

Gebe ich jetzt also einen konstanten Wert in die Methode ein, so 
leuchten die jeweiligen LED's auch munter, in der von mir gewünschten 
Helligkeit. (Glaube ich zumindest).

Übergebe ich nun aber der Methode einen festen Kanal und als Helligkeit 
eine Variable vom Typ uint16_t und lasse diese hochzählen, so sind sehr 
starke Abstufungen beim Dimmen zu erkennen.

Da ich hier nun bereits seit Stunden verzweifle, bitte ich hier nun um 
Hilfe.

Meine Vermutungen sind:

1. Meine Bitmanipulationsmethode ist fehlerhaft.
2. Das SPI-Timing zwischen ATmega16 und TLC5947 stimmt nicht.

Im Anhang poste ich noch den Quelltext und meine Vorüberlegungen, die 
ich im Bezug auf die Bitmanipulation auf dem Papier durchgeführt habe.

Vielen Dank im Voraus,

Christian

von Falk B. (falk)


Lesenswert?

@ Christian Karle (christiankarle)

>Der TLC5947 erwartet von mir 288 Bits.

Also 36 Bytes.

>Meine Methode Set_Channel_Brightness (uint8_t Channel,uint16_t

Das ist ein stinknormale Funktion. Das it einfach C, kein echtes C++.
Sorry ;-)

>Brightness) überprüft, ob der vom Benutzer eingegebene Kanal (Channel)
>gerade oder ungerade ist und führt daraufhin die Bitmanipulationen am
>Array durch, indem es die Position der LSB-Bits aus dem Char-Array LSB
>holt, welches die Position des LSB-Bits im Array lightdata darstellt und
>daraufhin die Bits verändert.

Ziemlich umständlich. Kann man aber machen.

>Übergebe ich nun aber der Methode einen festen Kanal und als Helligkeit
>eine Variable vom Typ uint16_t und lasse diese hochzählen, so sind sehr
>starke Abstufungen beim Dimmen zu erkennen.

Wie stark? Es kann auch sein, dass du einen normalen Effekt beim 
linearen Dimmem beobachtest, siehe LED-Fading

>1. Meine Bitmanipulationsmethode ist fehlerhaft.

Du verwendest das MSB Array nicht. Ist das normal?

Man kann das alles einfach im Simulator laufen lassen und prüfen.

>2. Das SPI-Timing zwischen ATmega16 und TLC5947 stimmt nicht.

Deine Ansteuerung mit BLANK und XLAT sieht komisch aus. Das macht man 
eigentlich anders.

von Christian K. (christiankarle)


Angehängte Dateien:

Lesenswert?

Falk Brunner schrieb:
> Deine Ansteuerung mit BLANK und XLAT sieht komisch aus. Das macht man
> eigentlich anders.

Kannst Du mir bitte verraten wie die Ansteuerung richtig funktioniert, 
ich werde aus dem Datenblatt einfach nicht schlau...

von Christian K. (christiankarle)


Lesenswert?

Das LED-Faden ist extrem hart. Für mich sieht das so aus, als würde die 
LED von einer Helligkeit zur nächsten springen.

von Christian K. (christiankarle)


Lesenswert?

Noch etwas ist mir aufgefallen:

Die "Sprünge" passieren erst schnell und werden dann immer langsamer.
Ich denke die Sprünge repräsentieren das MSB-Bit, dass sich um eine 
Stelle nach links schiebt...

Kann es sein, dass die Bitmanipulationen die ich durchführe doch 
fehlerhaft sind, oder die Methode mit der Variablen x nicht klar kommt 
(Probleme mit dem Datentyp) ?

von Falk B. (falk)


Lesenswert?

@ Christian Karle (christiankarle)

>Kannst Du mir bitte verraten wie die Ansteuerung richtig funktioniert,
>ich werde aus dem Datenblatt einfach nicht schlau...

Zuerst solltest du die Funktion zum Setzen eines Kanals vom Update der 
Daten trennen. Denn man will bisweilen auch mehrere Kanäle ändern und 
DANACH erst einen Update machen. Dann ist ein Update eher so
1
{
2
  for( i=0; i<36; i++ )
3
  {
4
    SPDR = ( lightdata [ i ] );
5
    while ( ! ( SPSR & ( 1 << SPIF ) ) );
6
  }
7
8
  xlat_high();   // Daten vom Schieberegister ins die PWM-Register übernehmen
9
  xlat_low();
10
  
11
}

BLANK brauchst du beim TLC5947 eigentlich nicht, denn der hat einen 
internen PWM-Takt. Lege BLANK konstant auf LOW. Das macht die 
Ansteuerung etwas einfacher, bringt aber kleinere Probleme beim Dimmen, 
weil die PWM nicht mit dem Dimmen synchronisiert werden kann. Dazu 
hatten wir schon mal einen Beitrag.

Beitrag "Re: Arduino Nano und TLC5947 - Projekt zur Beleuchtungssteuerung im Auto - bräuchte paar Tipps&Hilfe"

Und noch was ganz wichtiges. DU HAST DIE VOLATILE-PEST! Was soll das?

>Das LED-Faden ist extrem hart. Für mich sieht das so aus, als würde die
>LED von einer Helligkeit zur nächsten springen.

Das klingt danach, als ob deine Bitmanipulation schief geht, 
wahrscheinlich wird das LSB ins MSB kopiert oder ähnlich. Dann springt 
die Helligkeit natürlich stark. Deine Hilfs-Arrays mit LB und MSB 
brauchst du nicht, das kann man online berechnen.

von Christian K. (christiankarle)


Angehängte Dateien:

Lesenswert?

Ich habe jetzt mal eine Art Wechsler für die Helligkeit programmiert.
Die LED schaltet nun zwar von Dunkel nach Hell, jedoch nicht mehr zurück 
nach Dunkel.

Blank ist jetzt dauerhaft auf Low.

von Christian K. (christiankarle)


Angehängte Dateien:

Lesenswert?

Ich habe es jetzt mal durch den Simulator gejagt.

Es scheint in der Methode ein Problem beim löschen im Array zu geben.

( ( lightdata [ LSB [ Channel ] ] ) & 0x00 ) anscheinend wird hier 
nichts gelöscht.

Der Simulator liefert : (Anhang).

Anscheinend werden also die Bits schon an die richtigen Stellen 
geschoben, jedoch die alten nicht gelöscht und auch keine neuen Bits in 
ein bereits beschriebenes Byte geschrieben ?!

Der Schreibvorgang findet nur ein mal statt...

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

Noch was. Warum schreibst du auf deinem Zettel die Indizes in Dezmal und 
dann im LSB/MSB Array in Hex? Damit es nicht zu einfach lesbar ist?

>Ich habe jetzt mal eine Art Wechsler für die Helligkeit programmiert.

Gut.

>Die LED schaltet nun zwar von Dunkel nach Hell, jedoch nicht mehr zurück
>nach Dunkel.

Du hast immer noch nicht erhkärt, waruum du das MSB-Array nicht benutzt. 
Das ist ein Fehler. Denn mal ganz einfach betrachtet, musst du für Kanal 
0 die Bytes 34 (MSB) und 35 (LSB) verändern. Und das tust du nicht! 
Ausserdem ist deine ODER-Verknüfung FALSCH! Das sehe ich gerade erst. Du 
mußt normal zuweisen! Ausserdem übertreibst du es mit den Leerzeichen 
und Klammern.
Eher so.
1
void Set_Channel_Brightness ( volatile uint8_t Channel, uint16_t Brightness )
2
{
3
  
4
  if ( (Channel & 1) == 0 )  // & ist einfacher und schneller als %
5
  {
6
    lightdata[LSB[Channel]] = (uint8_t)Brightness;   
7
    lightdata[MSB[Channel]] = (lightdata[MSB[Channel]] & 0xF0 ) | ( Brightness >> 8 );
8
  }
9
  else
10
  {  
11
    lightdata[LSB[Channel]] = (lightdata[LSB[Channel]] & 0x0F ) | ( Brightness << 4 );
12
    lightdata[MSB[Channel]] = Brightness >> 4 ;
13
  }
14
}

Man kann die LSB/MSB Arrays auch weglassen, das kann man leicht 
berechnen.
Etwa so.
1
void Set_Channel_Brightness ( uint8_t Channel, uint16_t Brightness )
2
{
3
  uint8_t i;
4
5
  i = 35-(Channel*3/2);
6
  
7
  if ( (Channel & 1) == 0 )  // & ist einfacher und schneller als %
8
  {
9
    lightdata[i] = (uint8_t)Brightness;
10
    i--;   
11
    lightdata[i] = (lightdata[i] & 0xF0 ) | ( Brightness >> 8 );
12
  }
13
  else
14
  {  
15
    lightdata[i] = (lightdata[i] & 0x0F ) | ( Brightness << 4 );
16
    i--;
17
    lightdata[i] = Brightness >> 4 ;
18
  }
19
}

von Achim S. (Gast)


Lesenswert?

Christian Karle schrieb:
> Die LED schaltet nun zwar von Dunkel nach Hell, jedoch nicht mehr zurück
> nach Dunkel.

wenn ich es richtig sehe veroderst die Werte in Lightdata immer nur.

   lightdata [ LSB [ Channel ] ] |= ( (

Ein Bit, das einmal gesetzt wurde, hat keine Chance mehr auf 0 zurück zu 
gehen.

von Christian Karle (Gast)


Lesenswert?

Kann mir mal Jemand anhand eines Beispieles den Unterschied zwischen der 
normalen Zuweisung und der oder Zuweisung erklären?

Ich kenne den Unterschied leider nicht...

von Achim S. (Gast)


Lesenswert?

Warun verwendest du dann |=   ?

|= nimmt den alten Wert, verodert ihn bitweise mit den neuen Daten, und setzt das 
als neuen Wert.

z.B. alter Wert von lightdata ist 0b01010101

lighdata |= 0b00110011 ergibt 0b01110111
lighdata = 0b00110011 ergibt 0b00110011

von Falk B. (falk)


Lesenswert?

@Christian Karle (Gast)

>Kann mir mal Jemand anhand eines Beispieles den Unterschied zwischen der
>normalen Zuweisung und der oder Zuweisung erklären?

>Ich kenne den Unterschied leider nicht...

Dann wird es HÖCHSTE Zeit für ein C-Grundlagenbuch.

Siehe Bitmanipulation.

von Christian K. (christiankarle)


Lesenswert?

Falk Brunner schrieb:
> lightdata[LSB[Channel]] = (uint8_t)Brightness;
>     lightdata[MSB[Channel]] = (lightdata[MSB[Channel]] & 0xF0 ) | (
> Brightness >> 8 );

Guten Morgen,

ich habe mir das jetzt noch einmal zum Gemüte geführt.

Du castest die Variable Brightness in der ersten Zeile doch in eine 
uint8_t
und in der nächsten Zeile verschiebst Du alle Bits in der Variablen um 8 
Stellen nach rechts. Bei dieser Aktion gehen doch aber die 4 
hochwertigsten Bits verloren, oder sehe ich das falsch?

von Falk B. (falk)


Lesenswert?

@Christian Karle (christiankarle)

>> lightdata[LSB[Channel]] = (uint8_t)Brightness;
>>     lightdata[MSB[Channel]] = (lightdata[MSB[Channel]] & 0xF0 ) | (
>> Brightness >> 8 );

>Du castest die Variable Brightness in der ersten Zeile doch in eine
>uint8_t

Ja.

>und in der nächsten Zeile verschiebst Du alle Bits in der Variablen um 8
>Stellen nach rechts.

Ja.

> Bei dieser Aktion gehen doch aber die 4
>hochwertigsten Bits verloren, oder sehe ich das falsch?

Das siehst du falsch. Wenn ich um 8 Bits nach rechts schiebe, gehen die 
unteren 8 Bit verloren. ABER!

Bei KEINER Aktion gibt es einen Schreibzugriff (Zuweisung) auf die 
Variable Brightness, es wird immer nur lesend zugegriffen. Da kann man 
machen was man lustig ist, die Variable selbst wird dabei in eine 
(unsichtbare) Zwischenvariable kopiert und verändert und das Ergebnis 
lightdata[] zugewiesen. Die echte Variable Brightness wird dabei nicht 
verändert!

Das sind aber die GRUNDLEGENDSTEN Grundlagen der Programmierung. Wenn 
dir sowas unklar ist, darfst du nicht weiter machen sondern musst erst 
noch einmal die Grundlagen durcharbeiten.

Hast du meine FUNKTIONEN mal probiert?

von Falk B. (falk)


Lesenswert?

Hmmm,

da ich zufällig gerade an einem Projekt mit dem sehr verwandten TLC5940 
arbeite, hier eine Korrektur.

Besser so.
1
void Set_Channel_Brightness ( uint8_t Channel, uint16_t Brightness )
2
{
3
  uint8_t i;
4
5
  i = (23-Channel)*3;  // Nibblestartindex
6
  
7
  if (i & 1)  // ungerades Startnibble
8
  {
9
    i>>=1;     // in Byteindex umwandeln
10
    lightdata[i] = (lightdata[i] & 0xF0 ) | ( Brightness >> 8 ) & 0x0F;
11
    i++;   
12
    lightdata[i] = (uint8_t)Brightness;
13
  }
14
  else
15
  { 
16
    i>>=1;
17
    lightdata[i] = Brightness >> 4;
18
    i++;
19
    lightdata[i] = (lightdata[i] & 0x0F ) | ( Brightness << 4 ) & 0xF0;
20
  }
21
}

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.