Forum: Mikrocontroller und Digitale Elektronik TLC5947 Ansteuerung


von Martin H. (martin_h85)


Angehängte Dateien:

Lesenswert?

Guten Abend,

ich habe gerade einen kleinen Testaufbau mit einem TLC5947 gemacht. Dazu 
habe ich erst mal 2 RGB Leds angeschlossen, also Kanal 0-5.

Nun wollte ich das IC per Hardware SPI ansteuern. Dazu habe ich mich am 
Datenblatt orientiert. Leider klappt es noch nicht so richtig.
Die Ausgabe ist eher zufällig, was bedeutet wenn ich die ersten 8 bit 
komplett High setzte sind zwei Kanäle an.

Kann mal wer bitte ein paar Anmerkungen dazu machen, den ich sehe den 
Fehler noch nicht.



Die Belegung ist im Code dokumentiert, mein Atmel läuft mit 20MhZ. Iref 
ist auf 1,8k und auch sonst sollte der Hardware Aufbau OK sein.


MfG Martin

von Silvan K. (silvan) Benutzerseite


Lesenswert?

Warum?:
1
_delay_us(10);
2
_delay_us(10);
3
_delay_us(10);

... und nicht:
1
_delay_us(30);

????

von Martin H. (martin_h85)


Lesenswert?

Hallo,

das war nur durchs kopieren, hatte es nicht raus genommen. Aber die 
Funktion ist ja gleich.

Ich werde es beim nächsten mal zusammenfassen.


MfG Martin

von Karl H. (kbuchegg)


Lesenswert?

Wieso 37?

24 Kanäle a 12 Bit:   24 * 12 = 288
aufgeteilt in 8 Bit   288 / 8 = 36

von Martin H. (martin_h85)


Lesenswert?

Moin

sollte es nicht bei < 37 bis 36 laufen ? Oder habe ich da in meiner 
Müdigkeit einen Denkfehler gemacht ?


Gruß Martin

von Karl H. (kbuchegg)


Lesenswert?

Martin H. schrieb:
> Moin
>
> sollte es nicht bei < 37 bis 36 laufen ? Oder habe ich da in meiner
> Müdigkeit einen Denkfehler gemacht ?

Probiers mit einer kleineren Zahl aus

for ( i = 0; i < 8; ++i )

ergibt Werte für i von

 0, 1, 2, 3, 4, 5, 6, 7

zähl nach. Sind genau 8 Stück.

(Du hast die 0 vergessen)

von Martin H. (martin_h85)


Lesenswert?

Moin,

ok, aber der Fehler ist der Gleiche. bei z.B. 0xFF als erstem Wert. sind 
zwei Kanäle gesetzt.


MfG Martin

von Karl H. (kbuchegg)


Lesenswert?

Martin H. schrieb:
> Moin,
>
> ok, aber der Fehler ist der Gleiche. bei z.B. 0xFF als erstem Wert. sind
> zwei Kanäle gesetzt.

Was bedeutet für dich "gesetzt"

Da die ersten 12 Bit für den ersten Kanal sind, bleiben noch 4 Bit 
übrig, die im 2.ten Kanal eine von 0 verschiedene PWM erzeugen. 2 Leds 
sind hell. Die eine mehr, die andere weniger. Insofern ist das zu 
erwarten.

von Martin H. (martin_h85)


Lesenswert?

Morgen,

Also es ist ja so das die ersten 8 Bit High sind und der Rest Low. Wenn
ich nicht irre sollte so doch der erste Kanal den Wert 255 Dezimal
haben, wobei 4096 volle Helligkeit wäre. Der zweite Kanal bleibt
unberührt.

Könntest du nochmal etwas näher erklären wieso das logisch ist, das zwei
Leds an sind ?


MfG Martin

von Karl H. (kbuchegg)


Lesenswert?

Martin H. schrieb:
> Morgen,
>
> Also es ist ja so das die ersten 8 Bit High sind und der Rest Low.

Arg.
Du hast recht: 0xFF sind ja nur 8 Bit.

Mein Fehler.

Wenn du alles 0 rausschiebst: Sind dann alle Kanäle 0?


Ich würde an deiner Stelle nicht damit anfangen in einer Schleife da 
Daten rauszublasen. Ich würde so vorgehen (und werde das die nächsten 
Tage auch tun, meine Samples sind von TI eingetroffen):

Erst mal eine Belegung raustakten. Dann mittels einem XLATCH Puls 
übernehmen und nachsehen, ob die Ausgänge stimmen.

Im Moment hab ich einen Knopf im Hirn (muss die Hitze sein). Ich komm 
nicht damit zurecht, ob deine XLATCH und BLANK Pulsflanken-reihenfole 
korrekt ist oder nicht.

von Martin H. (martin_h85)


Lesenswert?

Morgen,

ja wenn ich nur 0x00 schreibe ist alles aus. Bei 0xFF sind zwei an. Aber 
bei 0x0F nur einer, ich denke das ich da irgendwo einen Dreher drin 
habe. Ich werde es nochmal testen.

Kann man mit dem Hardware SPI eigentlich gezielt 12 Bit senden ?

Oder muss ich mir eine Funktion schreiben um das zu zerlegen.



MfG Martin

von Karl H. (kbuchegg)


Lesenswert?

Martin H. schrieb:

> ja wenn ich nur 0x00 schreibe ist alles aus. Bei 0xFF sind zwei an. Aber
> bei 0x0F nur einer, ich denke das ich da irgendwo einen Dreher drin
> habe. Ich werde es nochmal testen.
>
> Kann man mit dem Hardware SPI eigentlich gezielt 12 Bit senden ?

Nicht das ich wüsste.
Das ist alles auf Byte ausgelegt

> Oder muss ich mir eine Funktion schreiben um das zu zerlegen.

Wirst du wohl müssen.

von Karl H. (kbuchegg)


Lesenswert?

Tomaten auf den Augen :-)
1
    //send the next data
2
    for(i=0;i<37;i++){
3
      SPDR = (send[i]); 
4
    }

He - du kannst doch nicht einfach 36 (37) Bytes in das SPDR stopfen! Du 
musst schon warten, bis das Byte draussen ist und das SPDR wieder 
aufnahmefähig ist.
1
    //send the next data
2
    for( i = 0; i < 36; i++ ) {
3
      spi_wait();
4
      SPDR = send[i]; 
5
    }

von Martin H. (martin_h85)


Lesenswert?

Morgen,


oh ja guter Einwand, das stimmt, jetzt scheint es auch besser zu laufen. 
Manchmal passieren einem echt die dümmsten Fehler.

Ich werde es nachher wenn ich von der Arbeit komme nochmal genauer 
überprüfen. Aber es sieht jetzt schon wesentlich besser aus ;)


MfG Martin

von Joe D. (Gast)


Lesenswert?

Hallo zusammen,
ich lese in immer mehr Foren und Beiträgen und auch in meinem Buch die 
folgende Zeile, wenn es um die SPI-Kommunikation geht:

// Wait for transmission complete
while(!(SPSR & (1 << SPIF)));

Mir ist der logische &-Operator an sich klar. Was ich aber nicht 
verstehe:
wie kann diese Zeile funktionieren?
In dieser Zeile ver-UND-et man ein komplettes Register mit einer 
logischen 1 an Bit Stelle 7, in welchem auch das SPIF-Bit sitzt.


Danke für jede hilfreiche Antwort =)

von ohne Worte (Gast)


Lesenswert?

Joe D. schrieb:
> In dieser Zeile ver-UND-et man ein komplettes Register mit einer
> logischen 1 an Bit Stelle 7, in welchem auch das SPIF-Bit sitzt.

ganz genau, man testet damit ob das Bit gesetzt ist oder nicht (das 
Register wird damit nicht verändert!)

von Joe D. (Gast)


Lesenswert?

Vielen Dank für die Antwort.

Inzwischen habe ich schon die nächste Frage und hoffe auch dieses mal 
auf Feedback.

Ich habe inzwischen mit der Implementierung von Adafruit für den Arduino 
mit TLC 5947 eine Referenzimplementierung für meinen ATmega328P 
vorgenommen.
1
/*
2
 * TLC5947_Adafruit.c
3
 *
4
 * Created: 12.04.2015 19:56:15
5
 *  Author: Joe
6
 
7
 Test with the Adafruit TLC5947 Breakout Board and a ATmega 328P Controller
8
 
9
 R(Iref) = 3k3 => 15mA per channel 
10
 
11
 18 Channels connected to a standard 5mm throuh-hole LED
12
 
13
 => 270mA for all LED channels 
14
 
15
 DIN -> Digital 11 --/-- PB3 (MOSI)
16
 CLK -> Digital 13 --/-- PB5 (SCK)
17
 LAT -> Digital 9  --/-- PB1 (OC1A/PCINT1) 
18
 /OE -> Digital 10 --/-- PB2 (SS)
19
 */ 
20
21
#define numdrivers 1
22
#define F_CPU 1000000
23
24
#define SIN PB3
25
#define SCK PB5
26
#define XLAT PB1
27
#define BLANK PB2
28
#include <avr/io.h>
29
#include <stdlib.h>
30
#include <util/delay.h>
31
32
uint16_t *pwmbuffer;
33
34
void TLC5947_init()
35
{
36
  DDRB |= (1 << XLAT) | (1 << BLANK) | (1 << SIN) | (1 << SCK);
37
  
38
  pwmbuffer = (uint16_t *)calloc(2, 24*numdrivers);
39
}
40
41
void setPWM(uint8_t chan, uint16_t pwm)
42
{
43
  if(pwm > 4095)
44
  {
45
    pwm = 4095;
46
  }
47
  if(chan > 24*numdrivers)
48
  {
49
    return;
50
  }
51
  
52
  pwmbuffer[chan] = pwm;
53
}
54
55
void writePWM(void)
56
{
57
  PORTB &= ~(1 << XLAT);
58
   //digitalWrite(_lat, LOW);
59
   // 24 channels per TLC5974
60
   for (int8_t c=24*numdrivers - 1; c >= 0 ; c--) 
61
   {
62
    // 12 bits per channel, send MSB first
63
     for (int8_t b=11; b>=0; b--) 
64
     {
65
    PORTB &= ~(1 << SCK);
66
       
67
       
68
       if (pwmbuffer[c] & (1 << b))
69
       {
70
         //PORTB |= (1 << SIN);
71
         PORTB = (1 << SIN);
72
       }
73
       else
74
       {
75
         //PORTB &= ~(1 << SIN);
76
         PORTB = (0 << SIN); 
77
       }
78
       //Pulse SCK to shift in Grayscale-Data
79
                         PORTB |= (1 << SCK);
80
       PORTB &= ~(1 << SCK);
81
       
82
     }
83
   }
84
   //Pulse XLAT to transfer from Grayscale-Shift-Register to Latch-Register
85
   PORTB |= (1 << XLAT);
86
   PORTB &= ~(1 << XLAT);
87
}
88
89
void clearAll_2(void)
90
{
91
  //XLAT low
92
  PORTB &= ~(1 << XLAT);
93
  
94
  for(int i = 0; i < 12; i++)
95
  {
96
    //SCK low
97
    PORTB &= ~(1 << SCK);
98
    
99
    //SIN low
100
    PORTB = (0 << SIN);
101
                //PORTB &= (1 << SIN);
102
    
103
    //Pulse SCK
104
    PORTB |= (1 << SCK);
105
    PORTB &= ~(1 << SCK);
106
  }
107
  //Pulse XLAT
108
  PORTB |= (1 << XLAT);
109
  PORTB &= ~(1 << XLAT);
110
}
111
112
113
114
115
int main(void)
116
{
117
  TLC5947_init();
118
  
119
  //Turn On LED-channels
120
  PORTB &= ~(1 << BLANK);
121
  //clearAll();
122
  
123
  
124
    while(1)
125
    {
126
    
127
    setPWM(0, 4095);
128
    writePWM();
129
    _delay_ms(2000);
130
    clearAll_2();
131
    _delay_ms(2000);
132
    
133
    }
134
}
Wenn ich nun in der "writePWM" Fkt. die Zeile
PORTB &= ~(1 << SIN);
so arbeitet der TLC5947 ordnungsgemäß wie er soll.

Verwende ich allerdings in meiner eigens geschriebenen Fkt. "clearAll_2" 
dieselbe Zeile
PORTB &= ~(1 << SIN);
treten Fehler auf.

Beispielsweise setze ich über setPWM(0, 4095) zunächst Channel 0 auf PWM 
100% und will dann die in das GS-Shift-Register geschobenen "1"en um 12 
verschieben, also, sodass Channel 1 auf PWM 100% ist.

Das sieht dann aber so aus, dass dann erst CHannel 0 aufleuchtet und 
Channel 1 auch, während Channel 0 an bleibt ?!?!?

Mir ist klar, dass, wenn PB3 vorher "0" war, dann durch &= aus 0&0 auch 
wieder null wird.
Aber anscheinend sieht der µController das anders oder habe ich einen 
Denkfehler?

Verwende ich
PORTB = (0 << SIN);
klappt es so wie gewollt.

Hat einer ne Idee?
Vielen Dank!

: Bearbeitet durch User
von ohne Worte (Gast)


Lesenswert?

Joe D. schrieb:
> PORTB = (0 << SIN);

statt dessen kannst du auch PORTB = 0 schreiben

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.