Forum: Mikrocontroller und Digitale Elektronik LEDs flackern bei höherer PWM-Frequenz


von Lap (Gast)


Lesenswert?

Ich hab hier ein Stück eines RGB LED Streifens mit 5050er, diese werden 
über ein PWM-Signal gedimmt. Bei einer niedrigen Frequenz geschieht das 
flimmerfrei, bei einer höheren fangen sie deutlich an zu flackern.

Nun messe ich mit dem DMM (leider kein Oszilloskop zur Hand) eine 
Frequenz von ~4kHz und den eingestellten Duty-Cycle, trotzdem flackert 
es erheblich.
Bei ~100Hz ist das nicht der Fall.

Angesteuert werden die einzelnen Farben über BC547 Transistoren die an 
einem 74HC595 hängen, der seine Daten von einem Arduino bekommt.

von Ulrich F. (Gast)


Lesenswert?

Ein Ossi wäre nicht schlecht, denn dann könnte man schauen, ob das 
Netzteil zappelt. Vielleicht mag es ja die 4kHz nicht ....


Evtl macht auch dein soft PWM Sorgen.....

Ich würde ja zu sowas tendieren: http://www.adafruit.com/product/815

von Wolfgang A. (Gast)


Lesenswert?

Ulrich F. schrieb:
> Ein Ossi wäre nicht schlecht,

Ein kleiner Logikanalysator für <10€ würde vielleicht auch schon weiter 
helfen. Oder natürlich das Programm, so dass man den Code mal auf einen 
µC schießen und sich das Ganze ansehen kann.

von Lap (Gast)


Lesenswert?

1
#include <SPI.h>
2
3
uint8_t ledAnzahl = 36;
4
uint8_t aufloesung = 127;// bei 255 flackern
5
uint8_t latchpin = 2;
6
uint8_t regNum;
7
uint8_t bitNum;
8
uint8_t dataSet;
9
uint8_t registerData[5];
10
uint8_t ledData[1][47];
11
12
void setup(){
13
  pinMode(latchpin, OUTPUT);
14
  digitalWrite(latchpin,LOW);
15
  SPI.begin();
16
  SPI.setBitOrder(MSBFIRST);
17
  SPI.setClockDivider(SPI_CLOCK_DIV2);
18
  SPI.setDataMode(SPI_MODE0);
19
  for(uint8_t i=1;i<ledAnzahl;i++)
20
  {
21
    ledData[0][i]=200;
22
  }
23
}
24
25
void loop(){
26
  for(uint8_t count = 0;count<aufloesung;count++){ 
27
    for(uint8_t ledNum = 0;ledNum<ledAnzahl;ledNum++){
28
      regNum = (ledNum/8);
29
      bitNum = (ledNum-(regNum*8));
30
      if(ledData[dataBlock][(ledNum+1)]>=count){
31
        registerData[regNum]|=(1<<bitNum);
32
      }
33
    }    
34
    for(uint8_t y = 0;y<6;y++){
35
      SPI.transfer(registerData[y]);
36
      registerData[y]=0;
37
      digitalWrite(latchpin,HIGH);
38
      digitalWrite(latchpin,LOW);
39
      
40
      
41
    }
42
    
43
  }
44
}

Hab mir aber schon die Werte ausgeben lassen, funktioniert soweit alles.

Kann es selbst bei der geringen Belastung am Netzteil liegen?

von Lap (Gast)


Lesenswert?

dataSet = dataBlock, hatte schon weiter dran rumgespielt um den Fehler 
zu finden

von Michael K. (Gast)


Lesenswert?

Ah, arduino ....
Das SPI und PWM ganz unterschiedliche Dinge sind ist Dir aber klar,
 oder ?
PWM wird mit Timern in Hardware erzeugt damit es keine Aussetzer gibt.
SPI ist das Timing relativ schnuppe.

Ohne zu verstehen was Du da tust, gehe ich davon aus das der 
erschreckend unperformante Arduino krams bei höheren Frequenzen an den 
Poller läuft und ganze Pulsfolgen auslässt, was Du als flackern siehst.

von Lap (Gast)


Lesenswert?

Kann dich beruhigen, ich weiß was ich tue.
Per SPI bekommt das 74HC595 seine Daten, die wie man im Code sieht so 
gesendet werden dass eine PWM zustande kommt.

Im loop() steht nur 'SPI.transfer()' und 'digitalWrite()' welche auf 
Arduino Krams bzw. Libraries zurückgreifen, bei beiden kann ich mir aber 
nicht vorstellen dass viel mehr als 16 (bei SPI.transfer()) oder 2 Takte 
(bei digitalWrite()) benötigt werden, das sollte der Compiler schon 
hinbekommen.

von Michael K. (Gast)


Lesenswert?

Lap schrieb:
> ich weiß was ich tue.
Warum fragst Du uns dann ?

> kann ich mir aber
> nicht vorstellen dass viel mehr als 16 (bei SPI.transfer()) oder 2 Takte
> (bei digitalWrite()) benötigt werden

Als ich das mal vor 2 Jahren auf dem Mega128 Arduino ausprobiert habe:
digitalWrite(latchpin,HIGH);
digitalWrite(latchpin,LOW);
digitalWrite(latchpin,HIGH);

-> 5us Puls

Bekommt der Compiler nicht hin.

SPI schafft nicht zwingend die konstanten Datenraten um eine 
Flackerfreie PWM zu machen.
Ohne ISR mit Arduino Mühlstein um den Hals schon mal garnicht.

Was macht eigentlich SPI.transfer() ?
Sendet das willkürlich auf Teufel komm raus, oder interessieren auch die 
Buffer Flags ?
Sendest Du zu früh haust Du die Daten weg, sendest Du zu spät enstehen 
Pausen.

Immer noch sicher das Du weißt was Du tust und was da passiert ?

Da Du den Code nicht kennst der da wirklich läuft ist das ein großes 
Fragezeichen. Das ist genau das Problem das die fortgeschrittenen MCU 
Bändiger mit dem Arduino haben.

Ohne Logig Analyser oder Oszi kannst Du weiter Raten oder Kaffesatz 
lesen.

von Wolfgang A. (Gast)


Lesenswert?

Lap schrieb:
> bei beiden kann ich mir aber nicht vorstellen dass viel mehr
> als ... 2 Takte (bei digitalWrite()) benötigt werden

Da täuscht du dich gewaltig. Die Funktion digitalWrite() braucht auf 
einem 16MHz ATmega328 etliche µs.

von Martin S. (led_martin)


Lesenswert?

Michael Knölke schrieb:
> Ohne Logig Analyser oder Oszi kannst Du weiter Raten oder Kaffesatz
> lesen.

Oder C, b.z.w. Assembler (für solche Sachen durchaus sinnvoll) lernen, 
dann wird das Datenblatt des verwendeten ATMEL-Controllers allerdings 
zur Pflichtlektüre. Ich bin hier gerade an einer LED-Spielerei dran mit 
12 Software-PWM Kanälen mit mehr als 10 Bit Auflösung und einer 
PWM-Frequenz von 5 kHz.

Wenn bei Dir 100 Hz geht, aber 4 kHz nicht, hast Du schon 300 Hz 
probiert? Das ist fürs Auge auch schon recht ruhig, und vielleicht 
klappt das ja noch, ohne größere Änderungen.

Mit freundlichem Gruß - Martin

von Lap (Gast)


Lesenswert?

Dann vielen Dank, hab jetzt mal einen Logic Analyzer bestellt, das 
interessiert mich jetzt genauer.

Wird wohl ein AtTiny einspringen müssen oder direkt der AtMega, Arduino 
war hauptsächlich der Bequemlichkeit gedacht, vorallem zum Debuggen.

von Ulrich F. (Gast)


Lesenswert?

>oder 2 Takte (bei digitalWrite())
eher 150 ;-)

von Karl H. (kbuchegg)


Lesenswert?

?
1
uint8_t registerData[5];
2
3
...
4
    for(uint8_t y = 0;y<6;y++){
5
      SPI.transfer(registerData[y]);
6
      registerData[y]=0;

du stehst mit Arrays auf Kriegsfuss.

Ein Array, das du mit einer Länge von 5 definierst
1
uint8_t registerData[5];

hat als gültige Indizes
0, 1, 2, 3, 4

Zähl nach. Das sind genau 5 Stück.

Wenn du also hier
1
    for(uint8_t y = 0;y<6;y++){
2
      SPI.transfer(registerData[y]);
3
      registerData[y]=0;
auf das Array Element mit dem Index 5 zugreifst, dann bist du ausserhalb 
der Array Grenzen. Denn ein Element mit dem Index 5 existiert schlicht 
und ergreifend nicht.

Generell: wenn ich so was sehe
1
  for(uint8_t i=1;i<ledAnzahl;i++)
also eine for-Schleife, die offenbar irgendwie mit Arrays im 
Zusammenhang steht, und bei der die Schleifenvariable mit einem 
Startwert von 1 beginnt, dann läuten bei mir die Alarmglocken!

Du könntest dir viel Kopfzerbrechen ersparen, wenn du dir angewöhnen 
würdest,  Array Größen fürs erste per #define an einer Stelle 
zusammenzufassen und for-Schleifen, deren Schleifenvariable als Index in 
ein Array dient und deren Absicht es ist, einfach nacheinander mit allen 
Array-Einträgen etwas zu tun, immer noch dem Muster 'beginnend bei 0 und 
kleiner als das Längen-Makro' zu formulieren.
1
#define REGISTER_LEN  5
2
3
uint8_t registerData[REGISTER_LEN];
4
5
....
6
7
8
    for(uint8_t y = 0; y < REGISTER_LEN; y++) {
Damit hast du erst mal ein allgemeines Schema, über das man sich so erst 
mal keinen Kopf zu zerbrechen braucht und welches man auf Anhieb 
erkennt. Erst wenn die for-Schleife nicht so aussieht, insbesondere wenn 
die Schleifenvariable nicht bei 0 beginnt, dort kein 'kleiner' steht 
oder dort nicht die Array-Länge auftaucht bzw. dort gar irgendeine 
Zahlenkonstante steht, dann muss man gesondert analysieren, ob das 
soweit korrekt ist. Aber in der gezeigten Form kannst du gut und gerne 
mehr als 90% aller for-Schleifen abhandeln, die mit Arrays zu tun haben 
OHNE dass da erst mal von irgendeiner Seite die Gefahr eines Fehlers 
droht.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Im übrigen:

Was soll es für einen Sinn haben, nach jeder SPI AUsgabe von 8 Bit die 
ganze 595 Kaskade zu latchen?

Logisch, dass du dann die Bits auf ihrem Durchmarsch siehst.

Gelatched wird ganz zum Schluss, wenn alle Bits in die 595 Kaskade 
eingetaktet wurden
1
    for(uint8_t y = 0; y < REGISTER_LEN; y++) {
2
      SPI.transfer(registerData[y]);
3
      registerData[y]=0;
4
    }
5
6
    digitalWrite(latchpin,HIGH);
7
    digitalWrite(latchpin,LOW);

Dann ist es auch nicht mehr ganz so schlimm, wenn der digitalWrite eher 
eine lahme Kröte ist.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Lap schrieb:

> Wird wohl ein AtTiny einspringen müssen oder direkt der AtMega,

Korrigier erst mal deine Programmfehler.
Merksatz: es gibt kein Programm, das einfach genug wäre, um nicht 
fehlerhaft sein zu können.

Und den Kram rund um
1
uint8_t ledAnzahl = 36;
2
...

führst du erst mal alles auf REGISTER_LEN zurück.
Wenn du 5 Stück 595 kaskadiert hast und dementsprechend
1
#define REGISTER_LEN   5

definierst hast, dann ist die Anzahl der damit steuerbaren LED
1
#define ANZAHL_LED  ( REGISTER_LEN * 8 )
und nichts anderes.

Dementsprechend lautet dann die Definition des Arrays
1
uint8_t ledData[ANZAHL_LED];

und die Zuweisung der ersten gültigen Werte in Setup bzw. die 
Abarbeitung der PWM
1
void setup(){
2
  pinMode(latchpin, OUTPUT);
3
  digitalWrite(latchpin,LOW);
4
  SPI.begin();
5
  SPI.setBitOrder(MSBFIRST);
6
  SPI.setClockDivider(SPI_CLOCK_DIV2);
7
  SPI.setDataMode(SPI_MODE0);
8
9
  for(uint8_t i = 0; i < ANZAHL_LED; i++)
10
  {
11
    ledData[i] = 200;
12
  }
13
}
14
15
void loop()
16
{
17
  for(uint8_t count = 0; count < aufloesung; count++ )
18
  { 
19
    for(uint8_t ledNum = 0; ledNum < ANZAHL_LED; ledNum++ )
20
    {
21
      regNum = ledNum / 8;
22
      bitNum = ledNum - (regNum*8);
23
24
      if( ledData[ledNum] >= count )
25
        registerData[regNum] |= (1<<bitNum);
26
    }
27
  }
28
29
...

Hast du anstelle von 5 Stück 595 deren 6 kaskadiert, dann änderst du 
REGISTER_LEN auf 6
1
#define REGISTER_LEN   6
und den Rest des Programms passt der Compiler für dich anhand der Makros 
an.

Dein Compiler hilft dir, bzw. die Programmiersprache stellt dir Mittel 
und Wege zur Verfügung, damit du kritische Abhängigkeiten von Zahlen 
nicht selbst händisch verwalten musst. Du musst diese Möglichkeiten nur 
benutzen. Das ist zwar momentan ein klitzekleines bisschen mehr 
Schreibarbeit, aber die Rate der auf solche Dinge zurückzuführenden 
Fehler sinkt drastisch.

: Bearbeitet durch User
von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Lap schrieb:
> Arduino war hauptsächlich der Bequemlichkeit gedacht, vorallem zum
> Debuggen.
Funktioniert das jetzt? Mir schien das Debuggen mit dieser Plattform 
immer unglaublich umständlich. Eben, weil kein Debugger verfügbar 
ist...

: Bearbeitet durch Moderator
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.