Forum: Mikrocontroller und Digitale Elektronik mal wieder: Arduino Klasse in Klasse - bitte Hilfe!


von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

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.

: Bearbeitet durch User
von ESP4096 S. (esp4096)


Lesenswert?

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?

von J. S. (jojos)


Lesenswert?

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.

von EAF (Gast)


Lesenswert?

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_PWMServoDriver led {addr};

Gleiches gilt auch hier!
Frank E. schrieb:
> LEDKey led = LEDKey(0x40);
1
// dieses mal mit ein paar Alternativen Schreibweisen.
2
LEDKey led(0x40);
3
// oder
4
LEDKey led {0x40};
5
// oder
6
LEDKey led = 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.

von ESP4096 S. (esp4096)


Lesenswert?

J. S. schrieb:
> Das pauschale 'kein new' ist Quatsch.

Na dann ist es ja gut, dass Keiner davon geredet hat.

: Bearbeitet durch User
von EAF (Gast)


Angehängte Dateien:

Lesenswert?

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.

von ESP4096 S. (esp4096)


Lesenswert?

Schön das wir da einer Meinung sind! :)

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

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.

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

*led und led(addr) sind jetzt zwei Variablen die nix miteinander zu tun 
haben. led-> greift ins Nirwana.

von EAF (Gast)


Lesenswert?

Zufall!?

von EAF (Gast)


Lesenswert?

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.

von EAF (Gast)


Lesenswert?

EAF schrieb:
> Zufall!?

Ach da waren andere schneller....
Soll natürlich heißen:

ESP4096 S. schrieb:
> Schön das wir da einer Meinung sind! :)
Zufall!?

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

Wird compiliert, tut aber auch nix. M.E. maximal an den funktionierenden 
Basiscode aus dem Threadstart angelehnt. Wieso bitte?
1
#include <Wire.h>
2
#include <Adafruit_PWMServoDriver.h>
3
4
class LEDKey{
5
  private:
6
    Adafruit_PWMServoDriver led; 
7
  public:
8
  void begin(int addr){ 
9
    Adafruit_PWMServoDriver led = Adafruit_PWMServoDriver(addr); 
10
    delay(100);
11
    led.begin();
12
    led.setPWMFreq(1000);
13
  }
14
  void pinOff(int p)  {led.setPWM(p , 0 , 4096);}
15
  void pinOn(int p)   {led.setPWM(p , 4096 , 0);}
16
};
17
18
LEDKey myled;
19
20
void setup(){
21
  Wire.begin();
22
  Wire.setClock(400000);
23
  myled.begin(0x40);
24
  myled.pinOn(0);
25
  myled.pinOff(1);
26
  myled.pinOn(2);
27
  myled.pinOn(3);
28
}
29
30
void loop(){}

von J. S. (jojos)


Lesenswert?

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?

: Bearbeitet durch User
von Εrnst B. (ernst)


Lesenswert?

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:
1
   ...
2
class LEDKey {
3
  private:
4
    Adafruit_PWMServoDriver * led;    
5
  public:
6
    LEDKey(Adafruit_PWMServoDriver * driver): led(driver) {}
7
    void begin() {
8
      led->begin();
9
    }
10
   ...
11
};
12
13
   ... main.cpp
14
   Adafruit_PWMServoDriver driver(0x42);
15
   LEDKey myled(&driver);
16
17
  setup() {
18
    ...
19
    myled.begin();

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

Ε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.

von Εrnst B. (ernst)


Lesenswert?

J. S. schrieb:
> und noch schöner mit Referenz

stimmt.
1
Adafruit_PWMServoDriver::Adafruit_PWMServoDriver(const uint8_t addr,
2
                                                 TwoWire &i2c)
3
    : _i2caddr(addr), _i2c(&i2c) {}

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

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 ...

von EAF (Gast)


Lesenswert?

Das mit dem "driver" ist schon wahr/gut/besser/schöner/universeller....
1
#include <Wire.h>
2
#include "ledkey.h"
3
4
constexpr byte addr = 0x40;
5
6
7
Adafruit_PWMServoDriver driver{addr};
8
LEDKey led {driver};
9
10
void setup()
11
{
12
  Serial.begin(9600);
13
  led.begin();
14
  Wire.setClock(400000);
15
  led.pinOn(0);
16
  led.pinOn(1);
17
  led.pinOn(2);
18
  led.pinOff(3);
19
}
20
void loop(){}

-----------------
1
#pragma once
2
3
#include <Wire.h>
4
#include <Adafruit_PWMServoDriver.h>
5
class LEDKey
6
{
7
  private:
8
    Adafruit_PWMServoDriver &driver;   
9
  public:
10
  LEDKey(Adafruit_PWMServoDriver &driver): driver{driver} {} //0x40 oder 0x41
11
12
  void begin()
13
  {
14
    driver.begin();
15
    driver.setPWMFreq(1000);
16
  }
17
18
  void pinOff(int p)  {driver.setPWM(p , 0 , 4096);}
19
  void pinOn(int p)   {driver.setPWM(p , 4096 , 0);}
20
};

von J. S. (jojos)


Lesenswert?

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.

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

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.

: Bearbeitet durch User
von EAF (Gast)


Lesenswert?

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.

von J. S. (jojos)


Angehängte Dateien:

Lesenswert?

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?

: Bearbeitet durch User
von EAF (Gast)


Lesenswert?

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

von Frank E. (Firma: Q3) (qualidat)


Lesenswert?

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.

von Helmut (Gast)


Lesenswert?

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.

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.