Viele LEDs mit PCA9685 dimmen - Problem

Guten Tag liebes Forum,
ich möchte für die Modelleisenbahn eine Steuerung entwickeln, mit der ich Lichtsignale schalten kann oder besser gesagt, ich möchte, dass sie beim Einschalten hochdimmen und beim Ausschalten runterdimmen.
Da der Arduino an sich nur recht wenig PWM Pins hat, habe ich mir die I2C Erweiterung mit PCA9685 angeschaut.
Nun habe ich ein Programm erstellt, mit dem ich 2 Leds im Wechsel dimmen kann. Das funktioniert so weit gut.
Dann habe ich das Programm auf 16 Leds erweitert und schon braucht er viel länger beim dimmen.
Offensichtlich habe ich was falsch gemacht oder nicht beachtet.
Ich komme aber auch derzeit nicht auf eine bessere Lösung.

Es wäre cool, wenn ihr euch das mal anschauen könnt.

Und ja, ich weiß, dass man es auch "Lichtsteuerung" auch in einer For Schleife aufrufen könnte, aber das macht keinen Unterschied.

/*************************************************** 
 das Programm soll mal für 5 PCA9685 mit je 16 LEDs funktionieren, deswegen habe ich es schon dafür vorbereitet.
 ****************************************************/
bool soll[1][16] = {1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
bool soll_alt[1][16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
long zeit[1][16];
uint16_t helligkeit[1][16];


#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

void setup() {
  Serial.begin(115200);
  Serial.println("GPIO Test an Pins 0 und 1 vom Board!");

  pwm.begin();
  pwm.setPWMFreq(1000);  // Set to whatever you like, we don't use it in this demo!

  Wire.setClock(400000);
}
uint16_t x = 0;
uint16_t y = 0;
void loop() {

Lichtsteuerung(0,0);
Lichtsteuerung(0,1);
/*Lichtsteuerung(0,2);
Lichtsteuerung(0,3);
Lichtsteuerung(0,4);
Lichtsteuerung(0,5);
Lichtsteuerung(0,6);
Lichtsteuerung(0,7);
Lichtsteuerung(0,8);
Lichtsteuerung(0,9);
Lichtsteuerung(0,10);
Lichtsteuerung(0,11);
Lichtsteuerung(0,12);
Lichtsteuerung(0,13);
Lichtsteuerung(0,14);
Lichtsteuerung(0,15);*/

test();
}

int State;
long zeit_test = 0;
void test()
{
  if(millis() - zeit_test > 10000)
  {
    zeit_test = millis();
    switch(State)
    {
      case 0: soll[0][0] = 1;soll[0][1] = 0;State = 1;break;
      case 1: soll[0][0] = 0;soll[0][1] = 1;State = 0;break;
    }
  }
}

void Lichtsteuerung(int dev,int nr)
{
  if(soll[dev][nr] != soll_alt[dev][nr])
  {
    zeit[dev][nr] = millis();
    soll_alt[dev][nr] = soll[dev][nr];
  }
  
  if(soll[dev][nr] == 0)
  {
    if(helligkeit[dev][nr] > 1)
    {
      helligkeit[dev][nr]--;
    }
    else
    {
      helligkeit[dev][nr] = 0;
    }
  }
  else if(soll[dev][nr] == 1 && millis()- zeit[dev][nr] > 1800)
  {
    if(helligkeit[dev][nr] < 4095)
    {
      helligkeit[dev][nr]++;
    }
    else
    {
      helligkeit[dev][nr] = 4095;
    }
  }
  if(dev == 0)
  {
     pwm.setPWM(nr, 0, helligkeit[dev][nr]);     
  }
    
}

Ich habe auch mal ein Video hochgeladen: PWM Test PCA9685 - YouTube
Da könnt ihr euch mal anschauen, wie das Verhalten ist.

Die Funktion ist so, dass er alle 10 Sekunden den Soll-Wert der LED wechselt. Also 1 oder 0 (Einschalten oder Ausschalten). Das wird in der Funktion test übernommen.

In der Funktion Lichtsteuerung wertet er aus, ob ein neuer Sollwert vorhanden ist. Wenn Ja, dann wird die Zeit Variable gesetzt, damit das Einschalten der LED um 1,8s verzögert wird. Dann je nach Soll Wert wird der Helligkeitswert reduziert oder erhöht bis zum Maximum.

Das ganz natürlich flexibel gehalten, damit später bis zu 16 LEDs je PCA gesteuert werden könnten.
Bei Rückfragen stehe ich gern zur Verfügung.
Vielen Dank für eure Hilfe.

LG
Daniel

Hallo,
dann schau dir mal die MobaTools an, da kannst du jeden Pin des Uno zum Dimmen verwenden.
Und du brauchst keine zusätzliche Hardware zum Dimmen. Außer evtl. Transistoren, wenn es mehrere Leds am Pin sein sollen.

danieldzi:
Dann habe ich das Programm auf 16 Leds erweitert und schon braucht er viel länger beim dimmen.

Es wäre cool, wenn ihr euch das mal anschauen könnt.

Bei Rückfragen stehe ich gern zur Verfügung.

Rückfrage ist gut... Hast Du das mal erst mit 2 und dann mit 16 LED ausprobiert ohne die ganzen zweidimensionalen Arrays?
Ich denke Du brauchst das zweite Array nicht; die Zustände der PIN kannst Du abfragen.
Ich bin da noch nicht ganz durchgestiegen, aber kann es sein, das sich deine Zeiten aufaddieren?

Mangels Kommentaren ist dein Programm recht schwer zu durchblicken. Das Problem dürfte die Ansteuerung über I2C sein. Wenn Du 16 Led's langsam runterdimmen willst, müssen da ziemlich viele Daten über den Bus gehen, und das dürfte zu lang dauern. Bei 2 Led's ist das natürlich nicht so viel, deshalb geht es da. Für alle 16 Leds müssen immerhin pro Dimmstufe 64 Byte übertragen werden. Evtl. könnte man das optimieren, wenn man immer alle 64 Register in einem Rutsch beschreibt. Das scheint die Adafruit-Lib aber nicht herzugeben, und man müsste es komplett selbst machen.
Edit: Der Fall, das alle Led's gleichzeitig auf- bzw. abgedimmt werden müssen ist aber auch eher praxisfremd. Es ändern sich ja nicht immer alle Signalbilder gleichzeitig.

Wie Dieter schon vorgeschlagen hat, könntest Du für die Ansteuerung von Lichtsignalen auch die MobaTools verwenden - die sind u.a. genau für diesen Zweck geschrieben und können an allen Digitalpins Leds weich an- und ausschalten. Auf Basis der MobaTools gibt es auch einen kompletten DCC-Zubehörecoder, der Lichtsignale entsprechend ansteuern kann.

also das sind 5 bytes pro Ausgang

  _i2c->beginTransmission(_i2caddr);
  _i2c->write(PCA9685_LED0_ON_L + 4 * num);
  _i2c->write(on);
  _i2c->write(on >> 8);
  _i2c->write(off);
  _i2c->write(off >> 8);
  _i2c->endTransmission();

Ich bezweiflich dass man das merken sollte, ob man nun 2 (x5) oder 16 (x5) ansteuert.
Ich vermute eher einen Ablauffehler in deinen ifs. Mangels IC kann ich das aber nicht testen.

Edit:
mit den normalen PWM pins würde ein gedämpftes ein/aus in etwa so aussehen:

/*
   LEDs an PWM pins, die "langsam" aufleuchten/verlöschen

 */

class SmoothLed {
    byte state = 1;                      // 0 off, 1 on
    unsigned long previousMillis;        // last timestamp
    uint16_t currentBrightness = 0;      // actual brightness of Pin
    const uint16_t maxBrightness = 255;  // native PWM is only 8 bit
    const uint8_t interval = 15;         // delay for each step
    const byte ledPin;                   // a GPIO for the LED

  public:
    SmoothLed(byte ledPin) : ledPin(ledPin)
    {}

    void begin() {
      pinMode(ledPin, OUTPUT);
    }
    void on() {                    // switch smooth on
      state = 1;
    }
    void off() {                   // switch smooth off
      state = 0;
    }
    void tick() {
      if (state == 1 && currentBrightness < maxBrightness && millis() - previousMillis > interval)
      {
        currentBrightness++;
        analogWrite(ledPin, currentBrightness);
        previousMillis = millis();
      }
      else if (state == 0 && currentBrightness > 0 && millis() - previousMillis > interval)
      {
        currentBrightness--;
        analogWrite(ledPin, currentBrightness);
        previousMillis = millis();
      }

    }
};

// native PWM pins

SmoothLed nativeLed[] {3, 5, 6, 9, 10, 11};

// der Nachtwächter schaltet im  5 Sekunden Rhythmus
// die Lampen ein bzw. aus 
void nachtwaechter()
{
  static uint32_t previousPhase = 0;
  uint32_t currentPhase = (millis() / 5000) % 4;
  if (currentPhase != previousPhase)
  {
    previousPhase = currentPhase;
    switch (currentPhase)
    {
      case 1:
        Serial.println("alles ein");
        for (auto &i : nativeLed) i.on();
        break;
      case 3:
        Serial.println("alles aus");
        for (auto &i : nativeLed) i.off();
        break;
    }
  }
}


void setup() {
  Serial.begin(115200);
  for (auto &i : nativeLed) i.begin();

}

void loop() {
  // logic
  nachtwaechter();

  // call each object
  for (auto &i : nativeLed) i.tick();
}

bei entsprechenden Interesse könnte man das vermutlich auch relativ einfach für I2C ICs anpassen.

Interessant fände ich den Aspekt, wie so ein "Lichtsignal" als Objekt aussehen soll. Das hat ja sicher mehr als nur eine LED, und welche "Bilder" es zeigen soll.
Denn eigentlich will man vermutlich nicht einzelne LEDs am Signal schalten sondern eher "Halt, Fahrt, Langsamfahrt, Rangierfahrt..." was weis ich ...

Hat da der Franz-Peter was in seiner Bastelkiste? Ganz sicher, oder?

noiasca:
Ich bezweiflich dass man das merken sollte, ob man nun 2 (x5) oder 16 (x5) ansteuert.
Ich vermute eher einen Ablauffehler in deinen ifs.

und welche "Bilder" es zeigen soll.
Denn eigentlich will man vermutlich nicht einzelne LEDs am Signal schalten sondern eher "Halt, Fahrt, Langsamfahrt, Rangierfahrt..." was weis ich ...

Ja.
Jepp.

... und hier kommt der springende Punkt.
Ich denke nicht, das es sich um Signale für den Fahrbetrieb handelt. Wer dimmen will, macht Ambient.
Aber evtl. klärt der TO noch auf...

my_xy_projekt:
Ich denke nicht, das es sich um Signale für den Fahrbetrieb handelt.

Warum nicht? Der Wechsel eines Signalbilds bei Lichtsignalen geht nicht 'hart', sondern 'weich' vonstatten. D. h. die jeweiligen Lampen glühen beim Auschalten noch nach, und auch beim Einschalten ist nicht sofort die volle Helligkeit da. Ich denke hier geht es darum, dieses Verhalten nachzubilden.
Mein oben verlinkter Decoder simuliert dieses weiche Überblenden der Signalbilder auch.

habs mal für einen SX1509 *) gemacht.

Falls für den TO nicht ganz klar ist, was da nun abläuft:
Grundsätzlich macht man sich erst mal eine generelle Klasse für das sanfte ein/ausschalten.
Dann kann man die "Lampen" anlegen
Dann fasst man mehrere dieser Lampen zu einem Objekt zusammen
Dieses Objekt behandelt man dann als Gesamtes.

Das "Praktische" daran, sieht man vermutlich erst wenn man wirklich mit mehreren Signalen daherkommt.

Ungetestet, ich hoffe aber es hilft trotzdem ein wenig:

/*
   SX1509 LED-Treiber
*/

#include <Wire.h>                        // Include the I2C library (required)
#include <SparkFunSX1509.h>              // Include SX1509 library
const byte SX1509_ADDRESS = 0x3E;        // SX1509 I2C address
SX1509 pwm0;                                  // Create an SX1509 object

// und nun das ganze für I2C
class SmoothLedI2C {
  private:
    byte state = 1;                      // 0 off, 1 on
    unsigned long previousMillis;        // last timestamp
    uint16_t currentBrightness = 0;      // actual brightness of Pin
    const uint16_t maxBrightness = 255;  // native PWM is only 8 bit
    const uint8_t interval = 15;         // delay for each step
    SX1509& chip;                        // reference to the I2C chip (object)
    const byte ledPin;                   // a pin for the LED
  public:
    SmoothLedI2C(SX1509& chip, byte ledPin) : chip(chip), ledPin(ledPin)
    {}
    void begin() {
      chip.pinMode(ledPin, ANALOG_OUTPUT);
    }
    void on() {                    // switch smooth on
      state = 1;
    }
    void off() {                   // switch smooth off
      state = 0;
    }
    void tick() {
      if (state == 1 && currentBrightness < maxBrightness && millis() - previousMillis > interval)
      {
        currentBrightness++;
        chip.analogWrite(ledPin, currentBrightness);
        previousMillis = millis();
      }
      else if (state == 0 && currentBrightness > 0 && millis() - previousMillis > interval)
      {
        currentBrightness--;
        chip.analogWrite(ledPin, currentBrightness);
        previousMillis = millis();
      }
    }
};

// jetzt brauchen wir erst mal ein paar bunte Lampen
SmoothLedI2C t1LampeRot(pwm0, 0);    // jetzt können wir den Ausgang 0 am chip pwm0 mit diesem Objektnamen ansprechen
SmoothLedI2C t1LampeGelb(pwm0, 1);
SmoothLedI2C t1LampeGruen(pwm0, 2);

class TrafficLight {
  private:
    SmoothLedI2C & red;      // a reference to a LED
    SmoothLedI2C & yellow;
    SmoothLedI2C & green;
  public:
    TrafficLight(SmoothLedI2C & red , SmoothLedI2C & yellow, SmoothLedI2C & green) :
      red(red), yellow(yellow), green(green)
    {}
    void begin()
    {
      red.begin(); yellow.begin(); green.begin();
    }
    void setRed()
    {
      red.on(); yellow.off(); green.off();
    }
    void setYellow()
    {
      red.off(); yellow.on(); green.off();
    }
    void setGreen()
    {
      red.off(); yellow.off(); green.on();
    }
    void tick()
    {
      red.tick(); yellow.tick(); green.tick();
    }
};

// und jetzt machen wir aus drei Lampen eine Ampel:
TrafficLight ampelA{t1LampeRot, t1LampeGelb, t1LampeGruen};

// Die Verkehrsleitstelle kümmert sich um die Ampel
void verkehrsleitstelle()
{
  static uint32_t previousPhase = 0;
  uint32_t currentPhase = (millis() / 5000) % 6;
  if (currentPhase != previousPhase)
  {
    previousPhase = currentPhase;
    switch (currentPhase)
    {
      case 1:
        Serial.println("rot");
        ampelA.setRed();
        break;
      case 3:
        Serial.println("yellow");
        ampelA.setYellow();;
        break;
      case 5:
        Serial.println("green");
        ampelA.setGreen();;
        break;
    }
  }
}


void setup() {
  Serial.begin(115200);

  Wire.begin();
  pwm0.begin(SX1509_ADDRESS);     // chip initialisieren

  ampelA.begin();
  ampelA.setYellow();
}

void loop() {
  // logic
  verkehrsleitstelle();

  // call each object
  ampelA.tick();
}

*) imho könnte man am SX1509 dazu auch das pulsen/hearbeat entsprechend parametrieren. Aber es soll ja nur ein Beispiel als Ersatz für das native PWM analogWrite sein.

Ich halte den PCA9685 für ungeeignet um einzelne LED zu dimmen. Da bietet sich der TLC5947 besser an da dieser direkt LED ansteuern kann ohne daß es Vorwiderstände an den LED braucht.
Da der TLC eine SPI ähnliche Schnittstelle hat und die Daten seriell in einem Block verschickt werden ist die Datenübertragung sehr viel schneller.
Wenn Du LED-Gruppen über einen Transistor / MOSFET ansteuern willst dann ist der PCA richtig.

Grüße Uwe

Ich hab schon #6 kurz gelesen - ich war leider noch immer beim auslesen Deines Codes.
Ok. Ja, ich geb zu, ich hab das so nicht mehr in Erinnerung gehabt. Die Signalanlage 10 mtr vom Gartenzaun weg ist doch etwas länger her. Und das mitfahren im Führerstand der S-Bahn auch ... ;(
[edit] ich verspreche das Elternhaus in den nächsten Tagen aufzusuchen....[/edit]

Trotzdem hier mein Text:

Ich hab mir nochmal Gedanken gemacht.
Die Chips sind schick. Die können ggfls 1Mhz I2C :wink:
Aber!
Meine erste Frage: brauchst Du die 12bit / 4096 Dimmschritte???

AAHHH!
Und dann kommt zumindest teilweise die Erleuchtung, wenn man mal den ganzen Kram zusammenbastelt, das er lesbar wird....

Ich habe mal versucht zu kommentieren, was ICH da rauslese.
Und vor allem sortiert!

/***************************************************
  das Programm soll mal für 5 PCA9685 mit je 16 LEDs funktionieren, deswegen habe ich es schon dafür vorbereitet.
 ****************************************************/
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

bool soll[1][16] = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
bool soll_alt[1][16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
long zeit[1][16];
uint16_t helligkeit[1][16];


// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

int State;
long zeit_test = 0;
uint16_t x = 0;
uint16_t y = 0;

void setup() {
  Serial.begin(115200);
  Serial.println("GPIO Test an Pins 0 und 1 vom Board!");
  pwm.begin();
  pwm.setPWMFreq(1000);  // Set to whatever you like, we don't use it in this demo!
  Wire.setClock(400000);
}

void loop()
{
  Lichtsteuerung(0, 0); // Aufruf chip 0 - PIN 1 
  Lichtsteuerung(0, 1); // Aufruf chip 0 - PIN 2
  test(); // Da soll was passieren....
}


void test()
{
  if (millis() - zeit_test > 10000) // Wenn Zeit abgelaufen
  {
    zeit_test = millis(); // Merke Zeit
    switch (State)        // Wähle Modus aus
    {
      case 0: soll[0][0] = 1; soll[0][1] = 0; State = 1; break; // chip 0 PIN 0 = 1 & chip 0 PIN 1 = 0 & next
      case 1: soll[0][0] = 0; soll[0][1] = 1; State = 0; break; // chip 0 PIN 0 = 0 & chip 0 PIN 1 = 1 & next
    }
  }
}

void Lichtsteuerung(int dev, int nr)
{
  if (soll[dev][nr] != soll_alt[dev][nr])  // chipNr:PINNr != LetzteAbfrage?
  {
    zeit[dev][nr] = millis();              // Zeit merken
    soll_alt[dev][nr] = soll[dev][nr];     // chipNr:PINNr merken
  }

  if (soll[dev][nr] == 0)                  // Inhalt von chipNr:PINNr == 0 ??
  {
    if (helligkeit[dev][nr] > 1)           // PWM von chipNr:PINNr > 1?
    {
      helligkeit[dev][nr]--;               // PWM --
    }
    else                                   // Wenn also PWM chipNr:PINNr <=1
    {
      helligkeit[dev][nr] = 0;             // Schreib ne 0. Das mach auch, wenn schon ne 0 drin steht...
    }
  }
  else if (soll[dev][nr] == 1 && millis() - zeit[dev][nr] > 1800) // else! Inhalt von chipNr:PINNr == 1 UND 1,8 sekunden abgelaufen ??
  {
    if (helligkeit[dev][nr] < 4095)        // Helligkeit noch nicht am Endpunkt?
    {
      helligkeit[dev][nr]++;               // ++
    }
    else
    {
      helligkeit[dev][nr] = 4095;          // kann man machen.... würde ich anders lösen
    }
  }
  if (dev == 0)                             // Wenn chip0
  {
    pwm.setPWM(nr, 0, helligkeit[dev][nr]);  // hier bin ich raus - muss ich noch nachlesen.
  }
}

Beschreib doch mal was Du willst.
Ich erfass das nicht, was Du mit den 1,8 sekunden im Zusammenhang mit der 10sekunden-Sequenz bezweckst.

So, ich hab das jetzt mal aufgebaut und gemessen. Die Übertragungszeit ist in der Tat erstmal nicht das Problem. Für die Übertragung der Daten für alle 16 Ausgänge braucht er ca. 3,5ms. Das sollte zumindest mal für einen PCA kein Problem sein. Ausserdem ist es natülich absolut unnötig, die Daten für die Led zu übertragen, bei denen sich gar nichts tut. Da ist also noch viel Optimierungspotential.

Die Ansteuerung im Sketch funktioniert aber überhaupt nicht. Auch wenn man die Aufrufe für alle 16 Ausgänge aktiviert, werden immer nur die ersten 2 angesteuert, für die restlichen wird immer nur '0' als PWM-Wert übertragen. Da ist also noch schwer der Wurm drin.

Kleines update:
Ich habe den Inkrement - wert (helligkeit[dev][nr]++;) auf helligkeit[dev][nr] += 16; geändert und natürlich den anderen auf -= 16;
jetzt klappt es deutlich besser. Das Dimmen ist zwar logischerweise nicht mehr ganz so weich, aber für die Signalsteuerung völlig ausreichend.

Vielleicht habt ihr aber dennoch eine gute Idee, warum es nicht so geht, wie ich es ursprünglich geplant hatte.

Oder ihr kennt eine gute Alternative.

Vielen Dank und eine schöne Woche.
LG
Daniel

danieldzi:
Oder ihr kennt eine gute Alternative.

An sich bietet sich ja an, das Dimmen für die Leds in eine Klasse zu verpacken. Dann kann man die im loop ganz gezielt ein/ausschalten. Ausserdem entsteht nur dann Datenverkehr auf dem I2C-Bus, wenn sich an einer Led auch was tut.
Eine kleine Demo dazu, wie ich mir das vorstelle. Der PWM-Wert wird im 10ms-Raster verändert (während des Dimmens). Verändert sich der PWM-Wert nicht, wird für die jeweilige Led auch nichts übertragen. Mit der Methode 'set' kann der Zielwert eingstellt werden ( als byte-Wert, 0...255, 0= ganz aus, 255=voll an). Mit 'get' kann der aktuelle Wert abgefragt werden.

/***************************************************
  das Programm soll mal für 5 PCA9685 mit je 16 LEDs funktionieren, deswegen habe ich es schon dafür vorbereitet.
 ****************************************************/
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

class PCAdimmer {
  public:
    PCAdimmer() {
      // Konstruktor ist leer
    };
    //-----------------------------------------------
    static bool dimtic() {
      // Diese Methode ist klassenglobal für alle Instanzen
      // Sie muss im loop() zyklisch aufgerufen werden
      // Funktionswert wird alle 10ms true. Dann müssen die process-Methoden aufgerufen werden
      static unsigned long lastTic;
      const unsigned long ticZeit = 10;
      bool ticVal = false;
      if ( millis() - lastTic > ticZeit ) {
        lastTic += ticZeit;
        ticVal = true;
      }
      return ticVal;
    }
    //-----------------------------------------------
    void begin( byte numLed, int inc ) {
      // Grundparamter setzen
      _numLed = numLed & 0x0f;
      _incDim = inc;
    }
    //-----------------------------------------------
    void process() {
      // Im loop alle 10ms aufrufen ( mit Hilfe von dimtic() )
      // prüfen ob auf/abgedimmt werden muss
      if ( _actDim != _sollDim ) {
        // Aktion notwendig
        if ( _actDim < _sollDim ) {
          // aufblenden
          _actDim += _incDim;
          if ( _actDim > _sollDim ) _actDim = _sollDim ;
        } else {
          // abblenden
          _actDim -= _incDim;
          if ( _actDim < _sollDim ) _actDim = _sollDim ;
        }
        pwm.setPWM(_numLed, 0, _actDim );
      }
    }
    //-----------------------------------------------
    void set( byte value) {
      // Ziel-PWM setzen ( 0=aus, 255= voll an )
      _sollDim = value << 4; // _sollDim ist 12 Bit
    }
    //-----------------------------------------------
    byte get() {
      // aktuellen pwm-Wert auslesen ( 0...255 )
      return _actDim >> 4;
    }
    //-----------------------------------------------
    // interne Variable
  private:
    int _actDim;    // aktuelle Dim-Stufe
    int _sollDim;   // ZielStufe
    int _incDim;    // Increment pro tic
    byte _numLed;   // Nummer der Led ( 0...15 )
}; 
//-------------- Ende Klassendefiniton 'PCAdimmer' --------------------------

PCAdimmer dimLed[16]; // Instanzen für alle 16 Leds anlegen.

void setup() {
  Serial.begin(115200);
  Serial.println("GPIO Test an Pins 0 und 1 vom Board!");
  pwm.begin();
  pwm.setPWMFreq(1000);  // Set to whatever you like, we don't use it in this demo!
  Wire.setClock(400000);
  for ( byte i = 0; i < 16; i++ ) {
    dimLed[i].begin( i, 50 );       // Led-Nummer und Tempo des Auf/Abdimmens einstellen
  }
}

void loop()
{
  if ( PCAdimmer::dimtic() ) {
    // alle Instanzen auf notwendigen inc/dec prüfen
    for ( auto& leds : dimLed ) leds.process();
  }
  //zur Demo Leds nacheinander ein- ausschalten
  {
    static byte ledIx = 0;
    static byte togState = 0;   // Ziel-zustand der Led (aus=0, ein=255), wechselt nach jedem Durchlauf
    if ( dimLed[ledIx].get() == togState) {
    // Alternative if-Bedingung mit überlappendem Ein- Ausschalten der Leds
    // if ( (togState == 255 && dimLed[ledIx].get() > 100 ) || (togState == 0 && dimLed[ledIx].get() < 155 ) ) {
      // Led hat Endzustand erreicht, weiterschalten
      if ( ledIx == 15 ) {
        // Ledzustand wechseln
        togState = (togState == 0) ? 255 : 0;
      }
      if ( ++ledIx > 15 ) ledIx = 0;
      dimLed[ledIx].set(togState);
    }
  }
}

Edit: Spasseshalber habe ich noch eine alternative if-Bedingung ( Zeile 98 ) eingefügt. Wenn man die aktivert, und die darüber (Zeile 96) auskommentiert, schalten die Led's überlappend ein und aus. Als Demo für gleichzeitiges Dimmen mehrer Leds un unterschiedlichen Dimm-Zuständen und die Möglichkeiten der get-Methode.

ein globaler ticker - nette Idee. (!)
karma+

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.