Forum: Mikrocontroller und Digitale Elektronik AtMega 48 Timmer


von Nova (Gast)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@  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

von Nova (Gast)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@  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

von Nova (Gast)


Angehängte Dateien:

Lesenswert?

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 ...

von Falk B. (falk)


Lesenswert?

Stimmt alles soweit.

von Nova (Gast)


Lesenswert?

humm ... dann wird das wohl auf ein versuch ankommen ^^

von Nova (Gast)


Lesenswert?

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:
1
uint8_t byte_count = 0;
2
uint8_t dot_count = 0;
3
int8_t frei = 8;
4
uint8_t send_byte = 0;
5
6
while ( dot_count <= 15)
7
{ 
8
   send_byte |= dot_array[dot_count]>>frei;
9
10
   byte_count++;
11
   dot_count++;
12
   frei = byte_count*8 - dot_count*5;
13
14
   while (frei > 0)
15
   {
16
       send_byte |= dot_array[dot_count] << (8 - frei);
17
       frei = byte_count*8 - (dot_count+1)*5;
18
       if (frei >= 0) dot_count++;
19
       else           frei = frei * -1;         
20
   }
21
22
   SPI_SEND(send_byte);
23
   send_byte = 0;
24
}

kann das so gehen? oder hat da mal wieder wer ne bessere idee^^

gruß Sascha

von Falk B. (falk)


Lesenswert?

@  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.
1
uint8_t bit_count_in;
2
uint8_t bit_count_out=0;
3
uint8_t dot_count;
4
uint8_t send_byte = 0;
5
uint8_t mask_in;
6
uint8_t mask_out = 0x80;
7
8
for (dot_count = 0; dot_count < 16; dot_count++) {
9
    mask_in = 0x20;
10
    for (bit_count_in=0; bit_count_in < 6; bit_count_in++) {
11
        if (dot_array[dot_count] & mask_in) send_byte |= mask_out;
12
        mask_in >= 1;
13
        mask_out >= 1;
14
        bit_count_out++;
15
        if (bit_count_out == 8) {
16
            bit_count_out=0;
17
            SPI_SEND(send_byte);
18
            send_byte = 0;
19
            mask_out = 0x80;
20
        }
21
    }
22
}

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

von Maik F. (sabuty) Benutzerseite


Lesenswert?

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.

von Nova (Gast)


Lesenswert?

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:
1
    while ( dot_count <= 15)
2
    { 
3
        send_byte |= dot_array[dot_count]>>frei;
4
      PORTA = send_byte;
5
         byte_count++;
6
         dot_count++;
7
         frei = byte_count*8 - dot_count*5;
8
9
         while (frei > 0)
10
         {
11
             send_byte |= dot_array[dot_count] << (8 - frei);
12
        PORTA = send_byte;
13
             frei = byte_count*8 - (dot_count+1)*5;
14
             if (frei >= 0)   dot_count++;                       
15
         }
16
17
    if (frei < 0)   frei = 5 - (frei * -1);         
18
       SPI_Transfer(send_byte);
19
       send_byte = 0;
20
       }

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...

von Nova (Gast)


Lesenswert?

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?

von Nova (Gast)


Lesenswert?

edit2:  (sry fürs spammen)

in meinem code ist noch meine debug ausgabe drin
1
    while ( dot_count <= 15)
2
    { 
3
        send_byte |= dot_array[dot_count]>>frei;
4
         byte_count++;
5
         dot_count++;
6
         frei = byte_count*8 - dot_count*5;
7
8
         while (frei > 0)
9
         {
10
             send_byte |= dot_array[dot_count] << (8 - frei);
11
             frei = byte_count*8 - (dot_count+1)*5;
12
             if (frei >= 0)   dot_count++;                       
13
         }
14
15
    if (frei < 0)   frei = 5 - (frei * -1);         
16
       SPI_Transfer(send_byte);
17
       send_byte = 0;
18
       }

gruß Sascha

von Falk B. (falk)


Lesenswert?

War nur so mal fix hingeschrieben, ist aber korrekt, bis auf zwei 
vergessene ">" Zeichen bei den Schiebeoperationen. Hier nochmal etwas 
aufgeräumt und simulationsfreundlich.

1
#include <stdint.h>
2
3
// Datenfeld für Segmente
4
uint8_t dot_array[16] = {0x10, 0x15, 0x3f, 0x30, 0x00, 0x00, 0x3F, 0xFF,
5
                         0x10, 0x15, 0x3f, 0x30, 0x00, 0x00, 0x3F, 0xFF};
6
7
void send_tlc5941(uint8_t dot_array[]) {
8
 
9
uint8_t bit_count_in;       // Zähler für Bits in den Eingangsdaten 6Bit/Segment
10
uint8_t bit_count_out=0;    // Zähler für Bits in den Ausgangsdaten 8Bit/SPI Byte
11
uint8_t dot_count;          // Zähler für Arrayindex der EIngangsdaten
12
uint8_t byte_in;            // Eingangsdaten
13
uint8_t byte_out = 0;       // Ausgangsdaten
14
uint8_t mask_out = 0x80;    // Bitmaske für Ausgangsdaten
15
16
    for (dot_count = 0; dot_count < 16; dot_count++) {      // Schleife über Eingangsarray
17
        byte_in = dot_array[dot_count];                     // nächstes Element laden
18
                                                            // Schleife über 6 Bit/Eingangsbyte
19
        for (bit_count_in=0; bit_count_in < 6; bit_count_in++) {    
20
            if (byte_in & 0x20) byte_out |= mask_out;       // Prüfe Bit #5, wenn ja in den Ausgangsdaten setzen
21
            byte_in <<= 1;                                  // nächstes Bit hinschieben
22
            mask_out >>= 1;                                 // Maske auf nächsten Ausgangsbit schieben
23
            bit_count_out++;                                // Bitzähler für Ausgang++
24
            if (bit_count_out == 8) {                       // 8 Bits erreich, Ausgangsbyte voll?
25
                bit_count_out=0;                            // Zähler rücksetzen
26
                //SPI_SEND(byte_out);                       // senden
27
                byte_out = 0;                               // Zähler rücksetzen
28
                mask_out = 0x80;                            // Maske rücksetzen
29
            }
30
        }   
31
    }
32
}
33
34
int main (void) {
35
36
    send_tlc5941(dot_array);
37
    while(1);
38
}

MfG
Falk

von Nova (Gast)


Lesenswert?

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

von Nova (Gast)


Lesenswert?

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:
1
void send_tlc5941_pwm(uint16_t pwm_array[]) {
2
 
3
  TLC5941_PORT  &=  ~(1<<TLC5941_MODE);
4
5
  uint8_t bit_count_in;       // Zähler für Bits in den Eingangsdaten 6Bit/Segment
6
  uint8_t bit_count_out=0;    // Zähler für Bits in den Ausgangsdaten 8Bit/SPI Byte
7
  uint8_t dot_count;          // Zähler für Arrayindex der EIngangsdaten
8
  uint16_t byte_in;           // Eingangsdaten
9
  uint8_t byte_out = 0;       // Ausgangsdaten
10
  uint16_t mask_out = 0x0400; // Bitmaske für Ausgangsdaten
11
12
    for (dot_count = 0; dot_count < 16; dot_count++) {      // Schleife über Eingangsarray
13
        byte_in = pwm_array[dot_count];                     // nächstes Element laden
14
                                                            // Schleife über 6 Bit/Eingangsbyte
15
        for (bit_count_in=0; bit_count_in < 12; bit_count_in++) {    
16
            if (byte_in & 0x0200) byte_out |= mask_out;     // Prüfe Bit #12, wenn ja in den Ausgangsdaten setzen
17
            byte_in <<= 1;                                  // nächstes Bit hinschieben
18
            mask_out >>= 1;                                 // Maske auf nächsten Ausgangsbit schieben
19
            bit_count_out++;                                // Bitzähler für Ausgang++
20
            if (bit_count_out == 8) {                       // 8 Bits erreich, Ausgangsbyte voll?
21
                bit_count_out=0;                          // Zähler rücksetzen
22
                SPI_Transfer(byte_out);                     // senden
23
                byte_out = 0;                               // Zähler rücksetzen
24
                mask_out = 0x0400;                          // Maske rücksetzen
25
            }
26
        }   
27
    }
28
  TLC5941_PORT  |=   (1<<TLC5941_LOAD);
29
  TLC5941_PORT  &=  ~(1<<TLC5941_LOAD);
30
  TLC5941_PORT  |=   (1<<TLC5941_MODE);
31
}

richitg?

von Falk B. (falk)


Lesenswert?

@  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.
1
void send_tlc5941_pwm(uint16_t pwm_array[]) {
2
 
3
  TLC5941_PORT  &=  ~(1<<TLC5941_MODE);
4
5
  uint8_t bit_count_in;       // Zähler für Bits in den Eingangsdaten 6Bit/Segment
6
  uint8_t bit_count_out=0;    // Zähler für Bits in den Ausgangsdaten 8Bit/SPI Byte
7
  uint8_t dot_count;          // Zähler für Arrayindex der Eingangsdaten
8
  uint16_t byte_in;           // Eingangsdaten
9
  uint8_t byte_out = 0;       // Ausgangsdaten
10
  uint8_t mask_out = 0x80;    // Bitmaske für Ausgangsdaten
11
12
    for (dot_count = 0; dot_count < 16; dot_count++) {      // Schleife über Eingangsarray
13
        byte_in = pwm_array[dot_count];                     // nächstes Element laden
14
                                                            // Schleife über 6 Bit/Eingangsbyte
15
        for (bit_count_in=0; bit_count_in < 12; bit_count_in++) {    
16
            if (byte_in & 0x0200) byte_out |= mask_out;     // Prüfe Bit #12, wenn ja in den Ausgangsdaten setzen
17
            byte_in <<= 1;                                  // nächstes Bit hinschieben
18
            mask_out >>= 1;                                 // Maske auf nächsten Ausgangsbit schieben
19
            bit_count_out++;                                // Bitzähler für Ausgang++
20
            if (bit_count_out == 8) {                       // 8 Bits erreich, Ausgangsbyte voll?
21
                bit_count_out=0;                            // Zähler rücksetzen
22
                SPI_Transfer(byte_out);                     // senden
23
                byte_out = 0;                               // Zähler rücksetzen
24
                mask_out = 0x80;                            // Maske rücksetzen
25
            }
26
        }   
27
    }
28
  TLC5941_PORT  |=   (1<<TLC5941_LOAD);
29
  TLC5941_PORT  &=  ~(1<<TLC5941_LOAD);
30
  TLC5941_PORT  |=   (1<<TLC5941_MODE);
31
}

MfG
Falk

von Nova (Gast)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

@  Nova (Gast)

>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

Dann machst du was falsch. Die LED-Ansteuerung ist unabhängig von der 
SPI-Geschwindigkeit.

MFG
Falk

von Nova (Gast)


Lesenswert?

da ist auch noch ein fehler
1
if (byte_in & 0x0200) byte_out |= mask_out;     // Prüfe Bit #12, wenn ja in den Ausgangsd

das muss "glaube ich" so aussehen
1
if (byte_in & 0x0800) byte_out |= mask_out;     // Prüfe Bit #12, wenn ja in den Ausgangsd

von Falk B. (falk)


Lesenswert?

Stimmt, kleiner Aufmerksamkeitstest ;-)

von Nova (Gast)


Lesenswert?

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?

von Michael A. (ammannm)


Lesenswert?

Guten Tag!

Ich weiss, der Beitrag ist etwas älter aber der Beste den ich zum Thema 
TLC4951 gefunden habe!

Ich bin an einem Projekt in dem zwei TLC4951's die Hauptrolle haben.
Die LED Reihe bestehet aus 2x16 LED. Die 2 TLC's sollen die Daten für 
die LED's innerhalb einer 1/100s erhalten und danach auch genau so lange 
anzeigen.

Bin kein Experte im Programmieren und werde mich mit dem Code weiter 
oben noch ein wenig auseinander setzen.

Da ich die Daten schneller senden müsste, könnte ich auch den Timer 1 im 
CTC-Modus verwenden und eine Taktrate von z.B. 10MHz...?

Da ich später 32 RGB LED verwenden möchte bräuchte ich 6xTLC4951 somit 
32 x 12 Bit x 3 Farben = 1152 Bit und bei 10MHz eine Übertragungszeit 
von 0.0001152 plus BLANK und XLAT Zeit.
Kommt das hin?

Könnte man den oben genannten Code so anpassen?
1
ISR(TIMER1_COMPA_vect){
2
  send_tlc5941_pwm(/*Array mit Bild*/)  
3
}
4
5
void SPI_Transfer(void) {
6
  /*Datenausgabe an beliebigem Pin*/
7
  /*Clock 0>1>0*/
8
}


Vielen Dank für die Antworten!

Gruss Michael

von Falk B. (falk)


Lesenswert?

@ Michael Ammann (ammannm)

>Die LED Reihe bestehet aus 2x16 LED. Die 2 TLC's sollen die Daten für
>die LED's innerhalb einer 1/100s erhalten und danach auch genau so lange
>anzeigen.

Kein Problem.

>Da ich die Daten schneller senden müsste, könnte ich auch den Timer 1 im
>CTC-Modus verwenden und eine Taktrate von z.B. 10MHz...?

Nimm das SPI Modul des Mikrocontrollers.

>Da ich später 32 RGB LED verwenden möchte bräuchte ich 6xTLC4951 somit
>32 x 12 Bit x 3 Farben = 1152 Bit und bei 10MHz eine Übertragungszeit
>von 0.0001152 plus BLANK und XLAT Zeit.

Der Fachmann nennt das 115,2us, weil kein Mensch soviele Nullen 
schreiben und abzählen will.

>Kommt das hin?

Ja.

>Könnte man den oben genannten Code so anpassen?

Sicher. Wo ist das Problem? Statt 16 nimmst du halt 32 oder mehr für 
dot_count.

von Michael A. (ammannm)


Angehängte Dateien:

Lesenswert?

Danke Falk Brunner!

Habe Testprogramm geschrieben mit dem Code aus den Beiträgen weiter 
oben.
Weiter habe ich mich über den SPI schlau gemacht. Die Taktrate kann 
nicht voll ausgenutzt werden, da SPR0 und SPR1 wenn nicht high, 
Systemtakt/4 ergeben was leider schade ist. Kann sein dass ich noch zu 
wenig darüber gelesen habe (SPI ist neu für mich!) und es einen anderen 
Weg gibt um das ganze schneller laufen zu lassen?!

Der Code ist im Anhang. Was noch fehlt sind die XLAT und BLANK Impulse 
um die Daten zu übernehmen.

Werde den Code heute nicht mehr testen können. Hoffe habe keine Fehler 
gemacht?!

Gruss Michael

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.