Karosseriebeleuchtung mit PCA9685 (Zusatz)
Benötigt werden:
- Arduino
- PWM Modul PCA9685
- LEDs und Widerstände
Ziel
Die LEDs sollen im Fade-OUT komplett ausgehen. Asynchrone Animationsabläufe.
Ursache
Es lag daran, dass der Wert nie den Endwert von 4095 erreicht hat. Auch, wenn das High Level nur sehr kurz anliegt, reicht die Spannungsversorgung aus, um einen LED noch glimmen zu lassen.
Daher liegt die Lösung nahe, dass am Ende eines Schleifendurchgang nochmal ein Signal-Zustand festgelegt wird, mit dem genannten Endwert. Jedoch soll das für die asynchronen Vorgänge auch funktionieren. Hier nochmal das Beispiel, in denen die LEDs nicht ganz aus sind.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
void setup() {
pwm.begin();
pwm.setPWMFreq(1600);
Wire.setClock(400000);
}
void loop() {
int stepAdd = 10;
int leds = 4;
for (uint16_t i=0; i<4096; i += stepAdd) {
for (uint8_t pwmnum=0; pwmnum < leds; pwmnum++) {
pwm.setPWM(pwmnum, 0, 4096 - i);
}
}
delay(1000);
for (uint16_t i=0; i<4096; i += stepAdd) {
for (uint8_t pwmnum=0; pwmnum < leds; pwmnum++) {
pwm.setPWM(pwmnum, 0, i);
}
}
delay(2000);
}
Einfache Lösung
Mit der bekannten Schrittweite kann nach dem Schleifendurchlauf bedingt die PWM Ports auf den Max Wert eingestellt werden.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
void setup() {
pwm.begin();
pwm.setPWMFreq(1600);
Wire.setClock(400000);
}
void loop() {
int stepAdd = 10;
int leds = 4;
for (uint16_t i=0; i<4096; i += stepAdd) {
for (uint8_t pwmnum=0; pwmnum < leds; pwmnum++) {
pwm.setPWM(pwmnum, 0, 4096 - i);
}
}
delay(1000);
uint16_t iSetValue = 0;
for (uint16_t i=0; i < 4096; i += stepAdd) {
for (uint8_t pwmnum=0; pwmnum < leds; pwmnum++) {
pwm.setPWM(pwmnum, 0, i);
}
iSetValue = i;
}
if(iSetValue > 4095 - stepAdd) {
for (uint8_t pwmnum=0; pwmnum < leds; pwmnum++) {
pwm.setPWM(pwmnum, 0, 4095);
}
}
delay(2000);
}
Lösung für asynchrone Vorgänge
Das gleiche kann auch für den asynchronen Vorgang verwendet werden. Jedoch hat die bisherige Lösung einen Nachteil: Dass sie schnell unübersichtlich wird.
Die Beispiele habe ich oft nur mit zwei LEDs abgebildet, aber für 16 LEDs wird's schlecht leserlich.
Deshalb gehe ich hier mit der Beispiellösung einen Schritt weiter und lege ein Objekt an, in dem ich für die einzelne LED den Zustand hinterlegen kann.
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
unsigned long mCurrentMillis = 0;
void setup() {
pwm.begin();
pwm.setPWMFreq(1600);
Wire.setClock(400000);
// schaltet alle aus
for (uint8_t pwmnum=0; pwmnum < 16; pwmnum++) {
pwm.setPWM(pwmnum, 0, 4095);
}
}
// status objekt für eine LED
typedef struct {
unsigned long LastMillis; // letzte Zeitstand
unsigned long DistanceFromNowToLast;// Intervall länge
uint16_t Value; // aktuell verwendeter Wert
bool IsFlashing; // soll nur an und aus
bool IsGoingOn; // Schritt soll heller
// oder an sein
} ledStateType;
// Konfiguration einzelner LEDs
ledStateType mLEDs[] = {
{ 0, 5, 0, false, true }, // fade animation
{ 0, 1000, 0, true, true } // flashing
};
void PrepareLED(int ledIndex) {
int stepAdd = 10;
unsigned long rest = mCurrentMillis - mLEDs[ledIndex].LastMillis;
if(rest < mLEDs[ledIndex].DistanceFromNowToLast) {
return;
}
if(mLEDs[ledIndex].IsFlashing) {
if(mLEDs[ledIndex].IsGoingOn) {
mLEDs[ledIndex].Value = 4095;
mLEDs[ledIndex].IsGoingOn = false;
}
else {
mLEDs[ledIndex].Value = 0;
mLEDs[ledIndex].IsGoingOn = true;
}
}
else {
if(mLEDs[ledIndex].IsGoingOn) {
if(mLEDs[ledIndex].Value > stepAdd) {
mLEDs[ledIndex].Value -= stepAdd;
}
else {
mLEDs[ledIndex].Value = 0;
mLEDs[ledIndex].IsGoingOn = false;
}
}
else {
if(mLEDs[ledIndex].Value < 4096) {
mLEDs[ledIndex].Value += stepAdd;
}
if(mLEDs[ledIndex].Value > 4095 - stepAdd) {
mLEDs[ledIndex].Value = 4095;
mLEDs[ledIndex].IsGoingOn = true;
}
}
}
mLEDs[ledIndex].LastMillis = mCurrentMillis;
}
void loop() {
mCurrentMillis = millis();
for(int i = 0; i < 2; i++) {
PrepareLED(i);
pwm.setPWM(i, 0, mLEDs[i].Value);
}
}
Natürlich ist das nur eine Möglichkeit, wie der Programmablauf aussehen könnte. Der geübte C++ Programmierer würde das sicherlich anders lösen können.
Kommentare