Wie ich bereits in einem anderen Thread schrieb, möchte ich die LEDs in
Tastern mit Hilfe des I2C-Servocontrollers PCA9685 ansteuern. Während
ein estes Testprogramm ohne eigene Klasse problemlos läuft, hat die
Version mit meiner Klasse ein merkwürdiges Problem: Es gibt beim
Compilieren keinen Syntaxerror, aber es passiert in der Ausführung
einfach NICHTS. Wo ist der Fehler? Danke für Tips!
einfacher Bais-Code:
1
#include <Wire.h>
2
#include <Adafruit_PWMServoDriver.h>
3
4
int addr = 0x40;
5
Adafruit_PWMServoDriver led = Adafruit_PWMServoDriver(addr);
6
7
void setup()
8
{
9
led.begin();
10
led.setPWMFreq(1000);
11
Wire.setClock(400000);
12
pinOn(0);
13
pinOn(1);
14
pinOff(2);
15
pinOn(3);
16
}
17
18
void loop(){}
19
void pinOff(int p){led.setPWM(p, 0, 4096);}
20
void pinOn(int p){led.setPWM(p, 4096, 0);}
meine Klasse:
1
#include <Wire.h>
2
#include <Adafruit_PWMServoDriver.h>
3
4
class LEDKey
5
{
6
private:
7
Adafruit_PWMServoDriver *led;
8
public:
9
10
LEDKey(int addr) //0x40 oder 0x41
11
{
12
led = new Adafruit_PWMServoDriver(addr);
13
led -> begin();
14
led -> setPWMFreq(1000);
15
Wire.setClock(400000);
16
}
17
18
void pinOff(int p) {led->setPWM(p , 0 , 4096);}
19
void pinOn(int p) {led->setPWM(p , 4096 , 0);}
20
};
und die Nutzung der Klasse:
1
#include "ledkey.h"
2
int addr = 0x40;
3
LEDKey led = LEDKey(0x40);
4
5
void setup()
6
{
7
Serial.begin(9600);
8
led.pinOn(0);
9
led.pinOn(1);
10
led.pinOn(2);
11
led.pinOff(3);
12
}
13
void loop(){}
Zur Ergänzung: Pin 0 ist die gemeinsame Anode, die Pins 1, 2 u. 3 sind
jeweils die Kathoden der LEDs. Beispiel 1 schaltet die grüne LED ein,
Beispiel 2 sollte die blaue LED einschalten.
Vermeide 'new' wo es nur geht. Immer wenn es einen Weg gibt ohne 'new'
ist dieser Weg zu bevorzugen.
Was mir auch noch auffällt. Hör am besten auf Dinge die kaputt gehen
können in Konstruktoren zu packen. Mach für deine LEDKey Klasse eine
eigene begin Methode. Was passiert mit dem Objekt, wenn der Konstruktor
mittendrin kaputt geht?
sieht man denn die Unterschiede wenn pinOn() schnell nacheinander
aufgerufen wird? Beim Test mit Klassen ist als letztes pinOff(3) drin,
vielleicht siehst du nur nicht das die LED an war.
Das pauschale 'kein new' ist Quatsch. Wenn es so wie hier ein pseudo
statisches new ist, dann ist das kein Problem.
Dann schreibt man beim -> Operator keine Leerzeichen davor und dahinter,
das sieht übel aus.
Und Adafruit_PWMServoDriver led = Adafruit_PWMServoDriver(addr); ist ein
Copy Konstruktor. Ich weiß nicht warum das in vielem Arduino Code drin
ist, es kostet unnötig Speicher und Laufzeit und kann Probleme machen
wenn HW kopiert werden soll.
Adafruit_PWMServoDriver led(addr); reicht.
Frank E. schrieb:> Adafruit_PWMServoDriver led = Adafruit_PWMServoDriver(addr);
Das ist doch irgendwie unnötiger Unfug, möchte ich meinen...
Sollte auch funktionieren:
1
Adafruit_PWMServoDriverled{addr};
Gleiches gilt auch hier!
Frank E. schrieb:> LEDKey led = LEDKey(0x40);
1
// dieses mal mit ein paar Alternativen Schreibweisen.
2
LEDKeyled(0x40);
3
// oder
4
LEDKeyled{0x40};
5
// oder
6
LEDKeyled=0x40;
Dann:
Wenn es einen Konstuktor mit new gibt, sollte es auch einen Destruktor
mit delete geben.
Siehe: RAII
Darum auch: Auf Zeiger verzichten, wenn möglich.
Hier ist es möglich.
Dann, der Aufruf im Konstruktor, die Ursache der Probleme (ohne Gewähr)
1
led->begin();
Das muss nicht klappen, da zu dem Zeitpunkt keine Garantie besteht, dass
die Peripherie schon vollständig initialisiert wurde.
ESP4096 S. schrieb:> Na dann ist es ja gut, dass Keiner davon geredet hat.
Merksätze:
1. Jedes vermeidbare new ist böse.
2. Jedes vermiedene new ist gut.
Bemerke: Zwischen den beiden gibt es eine (winzig kleine?) Hysterese,
der unvermeidbaren new.
ICh habe jetzt mal den Code entsprechend einigen der Empfehlungen
angepasst. Er wird fehlerfrei compiliert, tut aber nach wie vor NIX
(sollte rote LED einschalten).
1
#include <Wire.h>
2
#include <Adafruit_PWMServoDriver.h>
3
4
class LEDKey
5
{
6
private:
7
Adafruit_PWMServoDriver * led;
8
9
public:
10
void begin(int addr)
11
{
12
Adafruit_PWMServoDriver led(addr);
13
delay(100);
14
led.begin(addr);
15
led.setPWMFreq(1000);
16
}
17
void pinOff(int p) {led->setPWM(p , 0 , 4096);}
18
void pinOn(int p) {led->setPWM(p , 4096 , 0);}
19
};
20
21
LEDKey myled;
22
23
void setup()
24
{
25
Wire.begin();
26
Wire.setClock(400000);
27
myled.begin(0x40);
28
myled.pinOn(0);
29
myled.pinOff(1);
30
myled.pinOn(2);
31
myled.pinOn(3);
32
}
33
34
void loop(){}
Ich habe auch die Variante mit "private: Adafruit_PWMServoDriver led; "
und dann mit Punkt statt -> ausprobiert. Ebenfalls kein Compile-Fehler,
aber auch keine Aktion.
Frank E. schrieb:> Ich habe auch die Variante mit "private: Adafruit_PWMServoDriver led; "> und dann mit Punkt statt -> ausprobiert. Ebenfalls kein Compile-Fehler,> aber auch keine Aktion.
Mein Rat:
Du solltest dringenst ein C++ Buch durcharbeiten.
Und auch das logische Denken aktivieren.
Denn: Ausprobieren ist eher selten Zielführend.
Ist mehr ein ahnungsloses stochern im Nebel.
Frank E. schrieb:> Wieso bitte?
jetzt hast du immer noch zwei led Instanzen die nichts miteinander zu
tun haben.
Warum nimmst du nicht den Code vom ersten Arduino Fan?
Damit verlierst du die Flexibilität, die die PMWservo Klasse im
Konstruktor hat, u.A. da ein eigenes "TwoWire" Objekt durchzureichen,
z.B. falls der µC mehrere I²C-Busse hat, oder auch für Soft-I2C usw.
Besser:
Εrnst B. schrieb:> private:> Adafruit_PWMServoDriver * led;> public:> LEDKey(Adafruit_PWMServoDriver * driver): led(driver) {}
und noch schöner mit Referenz wie es auch die Adafruit_PWMServoDriver
Klasse macht. Adafruit_PWMServoDriver funktioniert nur wenn es auch
einen Bus gibt, wenn driver = nullptr ist dann knallt es wieder.
Ich habe selber bemerkt (noch bevor ich den Folge-Post gelesen habe),
dass die erneute Deklaration von led innerhalb von begin() natürlich
falsch ist und das geändert ...
wobei ich den Adafruit_PWMServoDriver schon als Instanz in der Klasse
lassen würde und im Konstruktor den Wire als Referenz übergeben würde.
Mehrere Led Objekte mit dem gleichen Adafruit_PWMServoDriver machen
vermutlich keinen Sinn, aber mehrere Led mit verschiedenen I2C Adressen
(und damit jeweils eigenem Adafruit_PWMServoDriver) schon.
EAF schrieb:> ESP4096 S. schrieb:>> Na dann ist es ja gut, dass Keiner davon geredet hat.>> Merksätze:> 1. Jedes vermeidbare new ist böse.> 2. Jedes vermiedene new ist gut.> Bemerke: Zwischen den beiden gibt es eine (winzig kleine?) Hysterese,> der unvermeidbaren new.
Ich habe den Code benutzt (den von 12:20 und den von 12:59), vielen
herzlichen Dank für die Mühe.
Aber leider gleiches Resultat: Fehlerfreie Compilation, keine Wirkung.
Nochmal der Hinweis: Der "Basiscode" im ersten Post funzt auf
identischer Hardware einwandfrei! Kann es sein, dass der Adafruit-Code
"irgendwie" nicht geeignet ist, in einer Klasse verarbeitet zu werden?
Gibts sowas?
Dann muss ich das Projekt eben "klassisch" mit ungebundenen Funktionen
fertigstellen, das wird mit Sicherheit funktionieren. Wäre nur anders
herum schöner gewesen ... aber ich abe leider nicht endlos Zeit.
J. S. schrieb:> Mehrere Led Objekte mit dem gleichen Adafruit_PWMServoDriver machen> vermutlich keinen Sinn, aber mehrere Led mit verschiedenen I2C Adressen> (und damit jeweils eigenem Adafruit_PWMServoDriver) schon.
Hmmm...
Aus dem Blickwinkel schon.
Mir dachte eigentlich, dass man "class LEDKey" zu einer Fassade oder
Adapter ähnlichen Konstruktion ausbauen kann, welcher verschiedenste LED
Treiber bedienen kann.
Schließlich gibt es ja einige viele Chpis/Möglichkeiten PWM auszugeben,
nicht nur das Adafruit Servo Board.
schade, den I2C PWM Baustein kennt die Simu nicht, oder übersehe ich
etwas?
https://wokwi.com/projects/346671167697846866
oder gibt es bessere?
Heute Abend könnte ich die reale HW ausprobieren, so ein Teil müsste ich
haben.
ATMega ist etwas besser, man kann einen LA an I2C anschliessen. Man
sieht auch das auf dem Bus etwas zyklisch rauskommt, aber der PCA ist
nicht da und entsprechend gibt es NAK auf dem Bus.
https://wokwi.com/projects/346672212853391955
Ist es also das simple Problem das die Abschlusswiderstände am Bus
fehlen?
J. S. schrieb:> Heute Abend könnte ich die reale HW ausprobieren, so ein Teil müsste ich> haben.
Habe ich gerade getestet!
Der Code von 12:59 funktioniert auf dem UNO mir dem Adafruit (Klon)
Dingen.
Frank E. schrieb:> Ich habe den Code benutzt (den von 12:20 und den von 12:59), vielen> herzlichen Dank für die Mühe.>> Aber leider gleiches Resultat: Fehlerfreie Compilation, keine Wirkung.
Da machst du was falsch!
Oder hältst was geheim
EAF schrieb:> J. S. schrieb:>> Heute Abend könnte ich die reale HW ausprobieren, so ein Teil müsste ich>> haben.>> Habe ich gerade getestet!> Der Code von 12:59 funktioniert auf dem UNO mir dem Adafruit (Klon)> Dingen.>> Frank E. schrieb:>> Ich habe den Code benutzt (den von 12:20 und den von 12:59), vielen>> herzlichen Dank für die Mühe.>>>> Aber leider gleiches Resultat: Fehlerfreie Compilation, keine Wirkung.> Da machst du was falsch!> Oder hältst was geheim
Mea culpa!
Irgendwie hängt sich der PCA9685 auf und ist daraus nur durch kompletten
Spannungs-Entzug für mind. 5s zurückzuholen. Oder per speziellem
Reset-Befehl, den ich schließlich in der Adafruit-Lib gefunden habe.
Also: Es funktioniert! Danke und Sorry für de Stress.
J. S. (jojos)
27.10.2022 13:34
>ATMega ist etwas besser, man kann einen LA an I2C anschliessen. Man>sieht auch das auf dem Bus etwas zyklisch rauskommt, aber der PCA ist>nicht da und entsprechend gibt es NAK auf dem Bus.>https://wokwi.com/projects/346672212853391955
Cool .. ich wusste gar nicht, dass man mit Wokwi Logik-Analysator Files
erzeugen kann.
Es ist zwar etwas unpraktisch, dass man PulseView extra auf dem PC
installieren muss, es funktioniert aber schnell und zuverlässig.