Datum:
Morgen, ich habe ein IC von TI TLC5941 (16xLED Dimmen per PWM 12Bit) Controller AtMega48-20PU ... der LED Treiber will 196 Bit Daten für die PWM (12 Bit pro LED). nach dem man dem IC alle 196 Bit geschickt hat brauch man ein PWM Ref CLK...das IC Zählt die Clocks und sobald der Wert für den jeweiligen Kanal erreicht ist schaltet er sie ein. nach 4096 clocks jedoch brauche ich ein blank Signal um den Zähler zurück zusetzen... logisch und eigentlich auch kein Problem...oder? ...also aus meinem Standpunkt schon.... am liebsten würde ich denn Ref. CLK über ein PWM out erzeugen da ich den nur einmal setzen muss und mich das keine Prozessorzeit kostet.... nur brauch ich im Endeffekt ein Interrupt nach 4096 Clocks um dann die PWM abzuschalten den Black zuschicken und wieder anzufangen. meine Überlegung war: wenn ich ne 100Hz PWM will muss ich 100 mal neu Anfangen d.h. 4069 Clocks mal 100 => 4096 KHZ die ich als Ref CLK brauche eigentlich etwas mehr da ich zwischendrin immer noch den blank brauche ... sagen wir 0,5MHz ist eine runde Sache .... kann ich irgendwie auswerten wann 4096 Clocks aus einem PWM Ausgang rausgekommen sind? ohne gleich 50% Cpu time zu verlieren? oder hat da einer eine bessere Idee (das ist meine Hoffnung^^) mfg Nova
Datum:
@ Nova (Gast) >CLK...das IC Zählt die Clocks und sobald der Wert für den jeweiligen >Kanal erreicht ist schaltet er sie ein. Eher aus. >am liebsten würde ich denn Ref. CLK über ein PWM out erzeugen da ich den >nur einmal setzen muss und mich das keine Prozessorzeit kostet.... nur Genau. >brauch ich im Endeffekt ein Interrupt nach 4096 Clocks um dann die PWM >abzuschalten den Black zuschicken und wieder anzufangen. >wenn ich ne 100Hz PWM will muss ich 100 mal neu Anfangen d.h. 4069 >Clocks mal 100 => 4096 KHZ die ich als Ref CLK brauche eigentlich etwas nein, eher 100 Hz * 4096, macht 409,6 kHz. >kann ich irgendwie auswerten wann 4096 Clocks aus einem PWM Ausgang >rausgekommen sind? >ohne gleich 50% Cpu time zu verlieren? Man braucht hier zwei Timer. Einer macht den Takt von 400 kHz, der andere die 100 Hz. MFG Falk
Datum:
du meinst also ich lasse die einfach immer laufen .... naja die idee ist nicht dumm... das SPI interface ist getrennt d.h. auch wenn die pwm gerade läuft kann ich daten nachschieben. der Blank muss aber ein kurzer strob sein ... das würde ich über ein compare machen ... den pin kurz toggeln (12ns reichen) joar... is ne gute idee ... kostet mich 1 8-bit timmer + OCA pin und 1 16-bit timmer naja nicht schön aber was besseres ist mir auch nicht eingefallen^^ danke aufjedenfall erstmal
Datum:
@ Nova (Gast) >der Blank muss aber ein kurzer strob sein ... das würde ich über ein >compare machen ... den pin kurz toggeln (12ns reichen) joar... is ne >gute idee ... Nöö, vollkommen unsinnig. Den Strobe machst du schlicht per Software, nachdem du neue Daten geladen hast. >kostet mich 1 8-bit timmer + OCA pin und 1 16-bit timmer naja nicht >schön aber was besseres ist mir auch nicht eingefallen^^ Wird hier auch nicht besser. MFG Falk
Datum:
Angehängte Dateien:ich meine nicht den Strob sondern den blank... den Strob braucht man natürlich für das SPI Interface aber nicht für den PWM Cycle soweit ich das im Datenblatt richtig verstanden habe wird mit dem gclk, der mit dem PWM Pin erzeugt wird, der interne Counter hochgezählt mit dem an verglichen wird, um den Counter wieder auf 0 zusetzen brauche ich ein blank da er das nicht von alleine macht. d.h. für jeden PWM Durchgang brauche ich 4096 GCLK’Ss dann ein Blank und dann wieder 4096 GCLK’s und dann ein Blank ... und so weiter ... bei 100 Hz PWM brauch ich auch 100 Blanks egal ob ich was an den Dimm werten der einzelnen Kanäle änder oder nicht ... wenn ich das richtig verstanden habe ist das SPI Interface unabhängig von der eigentlichen PWM Erzeugung ...
Datum:
So also die Timmer sind am laufen...420KHz und 100,2Hz passt ganz gut aber hat einer ne Idee wie ich 16 x 5bit in 8 bit Packete verpacke? meine Idee ist:
uint8_t byte_count = 0;
uint8_t dot_count = 0;
int8_t frei = 8;
uint8_t send_byte = 0;
while ( dot_count <= 15)
{
send_byte |= dot_array[dot_count]>>frei;
byte_count++;
dot_count++;
frei = byte_count*8 - dot_count*5;
while (frei > 0)
{
send_byte |= dot_array[dot_count] << (8 - frei);
frei = byte_count*8 - (dot_count+1)*5;
if (frei >= 0) dot_count++;
else frei = frei * -1;
}
SPI_SEND(send_byte);
send_byte = 0;
}
|
kann das so gehen? oder hat da mal wieder wer ne bessere idee^^ gruß Sascha
Datum:
@ Nova (Gast) >hat einer ne Idee wie ich 16 x 5bit in 8 bit Packete verpacke? Wieso 16x5? Das Ding hat 6 Bit/Punkt. Ist aber auch etwas doof, weil eben nicht 8 :-0 >kann das so gehen? oder hat da mal wieder wer ne bessere idee^^ Ohne das Programm jetzt im Detail geprüft zu haben würde ich sagen dass es zwar prinzipiell läuft, auf dem AVR aber relativ viel Zeit braucht. Keine Ahung wieviel, aber Bitschieben mit variabler Stellenzahl kostet viel Zeit. Besser ist es, feste Bitmasken zu verwenden. etwa so.
uint8_t bit_count_in; uint8_t bit_count_out=0; uint8_t dot_count; uint8_t send_byte = 0; uint8_t mask_in; uint8_t mask_out = 0x80; for (dot_count = 0; dot_count < 16; dot_count++) { mask_in = 0x20; for (bit_count_in=0; bit_count_in < 6; bit_count_in++) { if (dot_array[dot_count] & mask_in) send_byte |= mask_out; mask_in >= 1; mask_out >= 1; bit_count_out++; if (bit_count_out == 8) { bit_count_out=0; SPI_SEND(send_byte); send_byte = 0; mask_out = 0x80; } } } |
Das sollte recht flott laufen, weil nur eine feste Maske geschoben wird, das dauert jeweils 1 Takt ;-) >Nova (Gast) >gruß Sascha Soviel zum Thema Geschlechterzuordnung von Nicknames im Internet . . . ;-) Mfg Falk
Datum:
Ich habe das mal so gelöst, indem ich GSCLK und die Daten-Clock beide an die SPI-Clock gehängt habe. Dann einen Zustandsautomaten in den SPI-Interrupt, der sich um den Datenversand (erst Dummydaten, am Ende richtige Daten + Blank/Strobe) kümmert. Damit läuft die ganze Ansteuerung eines TLC5940 nebenher ab und man braucht sich nicht weiter drum kümmern. Geht allerdings nur, wenn man die SPI-Schnittstelle nicht noch für was anderes braucht.
Datum:
joar äää^^ naja and der spi hängt noch ein FRAM ein FLASH eine SD karte ein RFM12 eine RTC und porterwieterung + Front Board ^^ den SPI ist im dauer Stress ;-) meine lösung sieht jetzt mal so aus:
while ( dot_count <= 15)
{
send_byte |= dot_array[dot_count]>>frei;
PORTA = send_byte;
byte_count++;
dot_count++;
frei = byte_count*8 - dot_count*5;
while (frei > 0)
{
send_byte |= dot_array[dot_count] << (8 - frei);
PORTA = send_byte;
frei = byte_count*8 - (dot_count+1)*5;
if (frei >= 0) dot_count++;
}
if (frei < 0) frei = 5 - (frei * -1);
SPI_Transfer(send_byte);
send_byte = 0;
}
|
die Variante von Falk habe ich wenn ich erlichbin bei ersten drübergucken nicht verstanden ... muss mir das mal im Simulator angucken ... aber nur vom einwürf im endeffect brach ich ein bitstrom von 96bit nur für die Stromeinstellung. aber danke schon mal^^ das besste ist amanfang hatte ich keine Idde und bei ab tippen von dem code vom Schmirpapier ins Forum habe ich schon 3 Fehler gefunden...
Datum:
edit: also laut AVR Studio kommt da kein einziges Bit in der SPI rutine an ... hattes du das schon mal getestet oder frei schnautze mal runter getippt?
Datum:
edit2: (sry fürs spammen) in meinem code ist noch meine debug ausgabe drin
while ( dot_count <= 15)
{
send_byte |= dot_array[dot_count]>>frei;
byte_count++;
dot_count++;
frei = byte_count*8 - dot_count*5;
while (frei > 0)
{
send_byte |= dot_array[dot_count] << (8 - frei);
frei = byte_count*8 - (dot_count+1)*5;
if (frei >= 0) dot_count++;
}
if (frei < 0) frei = 5 - (frei * -1);
SPI_Transfer(send_byte);
send_byte = 0;
}
|
gruß Sascha
Datum:
War nur so mal fix hingeschrieben, ist aber korrekt, bis auf zwei vergessene ">" Zeichen bei den Schiebeoperationen. Hier nochmal etwas aufgeräumt und simulationsfreundlich.
#include <stdint.h> // Datenfeld für Segmente uint8_t dot_array[16] = {0x10, 0x15, 0x3f, 0x30, 0x00, 0x00, 0x3F, 0xFF, 0x10, 0x15, 0x3f, 0x30, 0x00, 0x00, 0x3F, 0xFF}; void send_tlc5941(uint8_t dot_array[]) { uint8_t bit_count_in; // Zähler für Bits in den Eingangsdaten 6Bit/Segment uint8_t bit_count_out=0; // Zähler für Bits in den Ausgangsdaten 8Bit/SPI Byte uint8_t dot_count; // Zähler für Arrayindex der EIngangsdaten uint8_t byte_in; // Eingangsdaten uint8_t byte_out = 0; // Ausgangsdaten uint8_t mask_out = 0x80; // Bitmaske für Ausgangsdaten for (dot_count = 0; dot_count < 16; dot_count++) { // Schleife über Eingangsarray byte_in = dot_array[dot_count]; // nächstes Element laden // Schleife über 6 Bit/Eingangsbyte for (bit_count_in=0; bit_count_in < 6; bit_count_in++) { if (byte_in & 0x20) byte_out |= mask_out; // Prüfe Bit #5, wenn ja in den Ausgangsdaten setzen byte_in <<= 1; // nächstes Bit hinschieben mask_out >>= 1; // Maske auf nächsten Ausgangsbit schieben bit_count_out++; // Bitzähler für Ausgang++ if (bit_count_out == 8) { // 8 Bits erreich, Ausgangsbyte voll? bit_count_out=0; // Zähler rücksetzen //SPI_SEND(byte_out); // senden byte_out = 0; // Zähler rücksetzen mask_out = 0x80; // Maske rücksetzen } } } } int main (void) { send_tlc5941(dot_array); while(1); } |
MfG Falk
Datum:
ja fettes DANKE!!!! du schiebst dir das einafch zurecht... sag ich jetzt mal so ... du maskirst immer nur ein Bit und schiebst das dann in das Ausgangsbyte rein ... saubere Idee ... und ja du ahst recht das ist schneller^^ mfg Sascha
Datum:
so da bin ich mal wieder^^ wenn ich das richtig verstanden habe muss ich dein Code so ändern damit er mir die 12 bit PWM in 8 bit Packete verpackt:
void send_tlc5941_pwm(uint16_t pwm_array[]) {
TLC5941_PORT &= ~(1<<TLC5941_MODE);
uint8_t bit_count_in; // Zähler für Bits in den Eingangsdaten 6Bit/Segment
uint8_t bit_count_out=0; // Zähler für Bits in den Ausgangsdaten 8Bit/SPI Byte
uint8_t dot_count; // Zähler für Arrayindex der EIngangsdaten
uint16_t byte_in; // Eingangsdaten
uint8_t byte_out = 0; // Ausgangsdaten
uint16_t mask_out = 0x0400; // Bitmaske für Ausgangsdaten
for (dot_count = 0; dot_count < 16; dot_count++) { // Schleife über Eingangsarray
byte_in = pwm_array[dot_count]; // nächstes Element laden
// Schleife über 6 Bit/Eingangsbyte
for (bit_count_in=0; bit_count_in < 12; bit_count_in++) {
if (byte_in & 0x0200) byte_out |= mask_out; // Prüfe Bit #12, wenn ja in den Ausgangsdaten setzen
byte_in <<= 1; // nächstes Bit hinschieben
mask_out >>= 1; // Maske auf nächsten Ausgangsbit schieben
bit_count_out++; // Bitzähler für Ausgang++
if (bit_count_out == 8) { // 8 Bits erreich, Ausgangsbyte voll?
bit_count_out=0; // Zähler rücksetzen
SPI_Transfer(byte_out); // senden
byte_out = 0; // Zähler rücksetzen
mask_out = 0x0400; // Maske rücksetzen
}
}
}
TLC5941_PORT |= (1<<TLC5941_LOAD);
TLC5941_PORT &= ~(1<<TLC5941_LOAD);
TLC5941_PORT |= (1<<TLC5941_MODE);
}
|
richitg?
Datum:
@ Nova (Gast)
>richitg?
Leider nein. Deine Ausgangsdaten sind immer noch nur 8 Bit, dort also
keine Änderung. LEdiglich die EIngangsdaten wachsen von 6 auf 12 Bit, es
ändert sich nur byte_in und die Maske für byte_in. Einfacher als du
denkst ;-)
Siehe unten.
void send_tlc5941_pwm(uint16_t pwm_array[]) { TLC5941_PORT &= ~(1<<TLC5941_MODE); uint8_t bit_count_in; // Zähler für Bits in den Eingangsdaten 6Bit/Segment uint8_t bit_count_out=0; // Zähler für Bits in den Ausgangsdaten 8Bit/SPI Byte uint8_t dot_count; // Zähler für Arrayindex der Eingangsdaten uint16_t byte_in; // Eingangsdaten uint8_t byte_out = 0; // Ausgangsdaten uint8_t mask_out = 0x80; // Bitmaske für Ausgangsdaten for (dot_count = 0; dot_count < 16; dot_count++) { // Schleife über Eingangsarray byte_in = pwm_array[dot_count]; // nächstes Element laden // Schleife über 6 Bit/Eingangsbyte for (bit_count_in=0; bit_count_in < 12; bit_count_in++) { if (byte_in & 0x0200) byte_out |= mask_out; // Prüfe Bit #12, wenn ja in den Ausgangsdaten setzen byte_in <<= 1; // nächstes Bit hinschieben mask_out >>= 1; // Maske auf nächsten Ausgangsbit schieben bit_count_out++; // Bitzähler für Ausgang++ if (bit_count_out == 8) { // 8 Bits erreich, Ausgangsbyte voll? bit_count_out=0; // Zähler rücksetzen SPI_Transfer(byte_out); // senden byte_out = 0; // Zähler rücksetzen mask_out = 0x80; // Maske rücksetzen } } } TLC5941_PORT |= (1<<TLC5941_LOAD); TLC5941_PORT &= ~(1<<TLC5941_LOAD); TLC5941_PORT |= (1<<TLC5941_MODE); } |
MfG Falk
Datum:
jo sehr cool ... das ding geht ja^^ wenn ich fertig bin soll das mal ein rgb folienleier werden mit den ic's auf dem folien leiter und mir ist aufgefallen man sollte das spi schon voll aufdrehen (8MHz) bei 100 KHz SPI Clk Flacken die LED's
Datum:
da ist auch noch ein fehler
if (byte_in & 0x0200) byte_out |= mask_out; // Prüfe Bit #12, wenn ja in den Ausgangsd |
das muss "glaube ich" so aussehen
if (byte_in & 0x0800) byte_out |= mask_out; // Prüfe Bit #12, wenn ja in den Ausgangsd |
Datum:
ja ok^^ in zwichen bin ich da auch durchgestiegen^^ Ps.: geile Variante total einfach, multifunktional einsetz bar, SAU schnell, und ich bin nicht draufgekommen -_- .... ps kennsich wer mit controllerlosen displays aus? Beitrag "GLCD Interface" kann mir da wer weiter helfen?

