Hilfe beim Speichern und Abrufen von PWM Werten gesucht

Hallo,

ich benötige noch einmal eure Hilfe für Teil 2 meines "großen" Projektes (Anfänger)!

Teil 1 funktioniert schon einwandfrei:
Entsprechend der Position eines Potis am analogen Eingang werden an zwei PWM Ausgängen verschiedene, von mir festgelegte Werte ausgegeben.

Teil 2 kriege ich bisher aber nicht hin: Ich will nämlich o.g. PWM Werte im EEProm speichern und später wieder ausgeben.

Wie das mir das genau vorstelle, möchte ich kurz beschreiben:

An den Eingängen des Arduino Uno hängen 4 Taster sowie ein Poti, an zwei PWM Ausgängen je eine LED. (Die Taster nenne ich mal A, B, C, D). Das Poti steht an irgendeiner Stelle, und die beiden LEDs leuchten unterschiedlich hell entsprechend den Werten, die ich im sketch für die betreffende Potiposition eingetragen habe.

Jetzt will ich ich Taster A drücken, und die momentan ausgegebenen PWM Werte sollen im EEprom gespeichert werden. Danach drehe ich das Poti in eine andere Position, drücke wieder Taster A, und auch diese Werte sollen gespeichert werden.

Später möchte ich durch Drücken der Taster B oder C die gespeicherten PWM Werte wieder abrufen, woraufhin die LEDs entsprechend dieser Werte leuchten werden. Um dann wieder in den „Poti Modus“ kommen (das Leuchten der LEDs hängt von der Potistellung ab) drücke ich Taster D.

Uwe hatte mir schon eine erste Hilfe gegeben:

Du kontrollierst den Taster und wen dieser gedrückt wird (zB LOW-HIGH-Übergang) Speicherst Du den Analogwert ab un setzt eine Statusvariable auf 1. Beim Drücken des zweiten Tasters setzt Du die Statusvariable auf 0.
Solange diese Statusvariable 1 ist nimmst Du den gespeicherten Wert ansonsten den gelesenen Analogwert von den Potis.

Im Prinzip verstehe ich das, ich stehe aber doch noch zu sehr am Anfang, um es auch umsetzen zu können. Ich wäre sehr dankbar, wenn mir jemand genauer sagen würde, wie ich meine Idee konkret angehen soll bzw. wo ich mich schlau machen kann.
Vielen Dank !

stringwalker

Im Prinzip verstehe ich das, ich stehe aber doch noch zu sehr am Anfang, um es auch umsetzen zu können. Ich wäre sehr dankbar, wenn mir jemand genauer sagen würde, wie ich meine Idee konkret angehen soll bzw. wo ich mich schlau machen kann.

Was an Uwe's Vorgehen verstehst Du denn nicht? Ich finde es sehr anschaulich erklärt. Erwarte bitte nicht, dass wir Dir hier fertigen Code vorsetzen, zuerst musst Du mal Eigeninitiative zeigen. Versuche mal, Uwe's Beschreibung in Code umzusetzen. Wenn das nicht funktioniert, poste den Code, den Du hast und wir versuchen Dir zu erklären, was Du noch verbessern musst.

Fang' doch mal mit den Arduino Beispielen an.
z.B. "DigitalReadSerial" zum Thema Taster lesen.
z.B. "switchCase" zum Thema unterschiedliche Aktionen auf Basis einer Zustandsvariablen.

Dann kannst du mit den Tastern B, C, D eine Zustandsvariable setzen, mit der du dann unterschiedliche Aktionen auslöst.
Und, wie schon Uwe geschrieben hat, beim drücken von A eine Zustandsvariable toggeln
(if (Taste A pressed) PWMSet = !PWMSet) und den Speicherplatz zu selektieren.

Und dann, wie pylon geschrieben hat, Codieren, wenns nicht geht posten und konkret nachfragen.

Grüße

Gunther

Was an Uwe's Vorgehen verstehst Du denn nicht? Ich finde es sehr anschaulich erklärt. Erwarte bitte nicht, dass wir Dir hier fertigen Code vorsetzen, zuerst musst Du mal Eigeninitiative zeigen.

Pylon, ich erwarte keineswegs, dass mir jemand einen fertigen Code vorsetzt, und Eigeninitiative entwickle ich reichlich, das kannst du mir glauben. :slight_smile: So einfach ist die Umsetzung für einen Anfänger aber eben doch nicht, auch wenn man es prinzipiell versteht.

Ich komme aus dem auch oft komplexen Gebiet der Analog- und Digitalelektronik und habe viele Leute im Netz beraten, die nicht klar kamen mit ihren Projekten. Deshalb weiß ich, mit welchen Tücken sich Einsteiger herumschlagen, und dass sich für sie imanchmal Hürden auftun, die ich kaum nachvollziehen kann.
Sie erwarten aber (i.d.R.) auch keine fertig dimensionierte Schaltung incl. Platinenlayouts von mir, es sind viel mehr Antworten auf Fragen wie : Lässt es sich überhaupt realisieren, was ich mir da vorstelle, wo fange ich an, unter welchen Fachbegriffen sollte ich im Netz suchen etc. Damit kommt ein interessierter Einsteiger dann einen guten Schritt weiter.

Gunther hat mir genau in diesem Sinne schon mal geholfen, danke dafür, damit kann ich etwas anfangen.

Mein Code (noch ohne die Speicherung) sieht übrigens bisher so aus, da werde ich also die Tasterabfragen einarbeiten:

const int potiMin = 0; // Poti Minimum
const int potiorMax = 1023; // Poti Maximum
int ledPin1 = 9; // LED 1 an digital pin 9
int ledPin2 = 10; // LED 2 an digital pin 10

void setup()
{

Serial.begin(9600); // serielle Schnittstelle initialisieren
}

void loop()
{
int poti = analogRead(A0); // Poti auslesen
int range = map(poti, 0, 1023, 0, 63); // Poti Gesamtbereich in 64 Bereiche unterteilen

switch (range)
{
case 0:
Serial.println("B 01"); // "B 01" für Bereich 1 anzeigen
analogWrite(ledPin1, 0); // PWM Wert 0 an LED 1
analogWrite(ledPin2, 50); // PWM Wert 50 an LED 2
break;
.
.
.
case 63:
Serial.println("B 63"); // "B 64" für Bereich 63 anzeigen
analogWrite(ledPin1, 255); // PWM Wert 255 an LED
analogWrite(ledPin2, 182); // PWM Wert 182 an LED 2
break;

}
delay(10);
}

Grüße,
stringwalker

Als erstes habe ich mal Deinen Code massiv verkürzt, indem ich die möglichen Werte in einem Array abspeichere. Damit dies nicht mehr RAM benötigt (davon haben wir nicht so viel), wird das im Flash gespeichert, was leider etwas komplizierteren Code beim Auslesen verlangt.

Bitte frage nach, wenn Du etwas nicht verstehst.

const int potiMin = 0;                                // Poti Minimum
const int potiorMax = 1023;                       // Poti Maximum
uint8_t ledPin1 = 9;                                  // LED 1 an digital pin 9
uint8_t ledPin2 = 10;                                // LED 2 an digital pin 10

PROGMEM uint8_t outvalue1[] = {0, ..., 255}; // Liste der möglichen Werte für LED1
PROGMEM uint8_t outvalue2[] = {50, ..., 182}; // Liste der möglichen Werte für LED2
  
void setup()
{
  Serial.begin(9600);                                // serielle Schnittstelle initialisieren
}

void loop()
{
  int poti = analogRead(A0);                   // Poti auslesen
  int range = map(poti, 0, 1023, 0, 63);   // Poti Gesamtbereich in 64 Bereiche unterteilen
                                                                 
  Serial.print("B ");                    // "B X" für Bereich X anzeigen
  Serial.println(range);
  analogWrite(ledPin1, pgm_read_byte(outvalue1+range));                // PWM Wert aus Flash an LED 1
  analogWrite(ledPin2, pgm_read_byte(outvalue2+range));               // PWM Wert aus Flash an LED 2
  delay(10);                               
}

Jetzt geht es noch darum, die Taster auszulesen. Hast Du dazu auch schon Code geschrieben? Versuche das mal und teste es an Deiner Schaltung aus.

Pylon,
vorab erst einmal herzlichen Dank für die Mühe, die du dir gemacht hast. "Massiv gekürzt" ist aber wohl eine ziemliche Untertreibung - meine Güte, der Code ist ja nur noch ein Bruchteil so lang!

Natürlich verstehe ich jetzt vieles noch nicht (PROGMEM uint8_t outvalue1 u.a.), aber das recherchiere ich selbst und frage nach, wenn ich dann immer noch etwas nicht kapiere.

Eines wüsste ich aber gerne doch gleich:

Serial.print("B "); // "B X" für Bereich X anzeigen
Serial.println(range);

Verstehe ich es richtig, dass die Leerstelle hinter dem B ("B ") in Verbindung mit der nächsten Befehlszeile dazu führt, dass die einzelnen Poti Bereiche angezeigt werden? Das wäre ja elegant.

Mit den Tastern bin ich leider noch nicht weiter, aber sowie ich etwas habe, melde ich mich. Ich hatte Überlegungen auf Basis meines Codes angestellt, aber der gefällt mir jetzt nicht mehr, und ich muss erst die Folgen deiner massive Kürzung verdauen... :slight_smile:

Nochmals danke

stringwalker

Falls die Beziehung Helligkeit und Potistellung linear ist braucht es keine Wertetabelle sondern es genügt ein map() Funktion.

Grüße Uwe

stringwalker:
Serial.print("B "); // "B X" für Bereich X anzeigen
Serial.println(range);

Verstehe ich es richtig, dass die Leerstelle hinter dem B ("B ") in Verbindung mit der nächsten Befehlszeile dazu führt, dass die einzelnen Poti Bereiche angezeigt werden? Das wäre ja elegant.

So ist es.
Serial.print("abc"); gibt eben die Zeichen "abc" auf dem Monitor aus, und bleibt direkt dahinter stehen.

Serial.print("abc");
Serial.print("def"); ergibt die Ausgabe "abcdef"; zusätzlich die Zeile
Serial.print(val); ergibt die Ausgabe "abcdef23", wenn val gerade den Wert 23 hat.

erst wenn du ein
Serial.print[u]ln/u; ausgibst, fängt die neue Zeile an.

Uwe

Falls die Beziehung Helligkeit und Potistellung linear ist braucht es keine Wertetabelle sondern es genügt ein map() Funktion.

Nein, diese Linearität gibt es nicht, mein Ziel ist es ja gerade, mit Hilfe vom Arduino ganz krumme Kennlinien zu erzeugen (läuft über nachgeschaltete Optokoppler etc.).

Gunther

erst wenn du ein
Serial.println(irgendwas); ausgibst, fängt die neue Zeile an.

Es war genau dieses kleine "ln" im println, das ich überlesen hatte. Erklärung kapiert, danke für ein weiteres Aha-Erlebnis.

Pylon,
dein Code war doch für mich Neuling anfangs völlig kryptisch, aber ich habe gründlich recherchiert und gelernt und verstehe ihn jetzt vollständig. Hinterher erscheint alles wie so oft ganz logisch.

Du hast angeboten, evt. anfallende Fragen zu beantworten. Grundsätzlich ist mir, wie gesagt, alles klar, in vier Fällen wäre ich aber noch für eine Bestätigung dankbar:

uint8_t ledPin1 = 9;

uint_8 speichert "a type of unsigned integer of length 8 bits", und byte speichert einen ganzzahligen 8-bit Wert.
Dann wären die Befehle m.E. doch austauschbar, oder nicht? (Byte wäre dabei für mich der "griffigere", weil aussagekräftigere Befehl.)

Um PROGMEM im Code nutzen zu können, muss ich am Anfang die passende Bibliothek anfordern. Ich habe das im Code ergänzt. OK?

PROGMEM uint8_t outvalue1[]

outvalue hatte ich zuerst für einen Befehl gehalten, aber es ist nur ein Name für eine Variable. Ist das eine Art Standardbezeichnung, die man verwendet? Dann wäre es sinnvoll, sich das auch anzueignen.

analogWrite(ledPin1, pgm_read_byte(outvalue1+range)); // PWM Wert aus Flash an LED 1

Für das das Pluszeichen habe ich bisher keine direkte Erklärung gefunden. Ich denke, es gehört zur syntax und sagt hier: Ordne dem gespeicherten Wert von outvalue dem entsprechenden Potibereich zu. Richtig?

Jetzt kann mich an meine Taster machen, es war mir aber erst einmal wichtiger, vor dem nächsten Schritt bis hierhin alles zu verstehen.

Ach so, klar geworden ist mir natürlich auch, dass die Werte per Tastendruck gar nicht mehr gespeichert werden müssen, da sie ja schon im Flashspeicher liegen. Ich muss vielmehr - wenn das Poti an der "richtigen" Stelle steht - die aktuellen PWM Werte irgendwie "markieren", um sie dann später auf Tastendruck wieder hervorholen zu können. Ich arbeite dran.

Grüße an euch alle

stringwalker

uint_8 speichert "a type of unsigned integer of length 8 bits", und byte speichert einen ganzzahligen 8-bit Wert.
Dann wären die Befehle m.E. doch austauschbar, oder nicht? (Byte wäre dabei für mich der "griffigere", weil aussagekräftigere Befehl.)

Ja, beides ist auf dem Arduino gleich.
Aber zB schon int ist auf verscheidenen Systemen nicht eindeutig.
Darum ist uint_16 eindeutig und entspricht bei Arduino einem unsigned INT. Es ist nur etwas Übung und Gewohnheit nötig um auch mit uint_8 oder uint_16 klarzukommen.

Grüße Uwe

Verstehe, dann werde ich lieber gleich die universellen Begriffe verwenden. Die Möglichkeiten der Microcontroller faszinieren mich viel zu sehr, um mich nur auf den Arduino festzulegen.

Grüße zurück
stringwalker

Dann kannst du mit den Tastern B, C, D eine Zustandsvariable setzen, mit der du dann unterschiedliche Aktionen auslöst.
Und, wie schon Uwe geschrieben hat, beim drücken von A eine Zustandsvariable toggeln
(if (Taste A pressed) PWMSet = !PWMSet) und den Speicherplatz zu selektieren.

Hallo,
also ich habe mich wirklich redlich bemüht, eure Vorschläge umzusetzen, habe einiges auf den Arduino Seiten gelernt, aber ich hänge jetzt fest. Zwar kann ich Taster abfragen und irgend etwas einschalten, aber es gelingt mir einfach nicht, das z.B. mit switch/case, if... etc. sinnvoll zu verbinden.

Könnt ihr bitte noch einmal einen Blick auf meinen Code werfen und mich beraten?

Ich habe an der Stelle, wo es nicht weitergeht, nochmal notiert, was da passieren soll. Steht zwar auch oben, aber ist hier für euch vielleicht hilfreich.

#include <avr/pgmspace.h>

const int potiMin = 0;           // Poti Minimum
const int potiMax = 1023;     // Poti Maximum
uint8_t ledPin1 = 9;             // LED 1 an digital pin 9
uint8_t ledPin2 = 10;           // LED 2 an digital pin 10

int tasterRec = 2;               // merkt sich auf Tastendruck die aktuellen PWM Werte
int tasterWrite1 = 4;           // gibt die ersten "gemerkten" Werte an ledPin1 und ledPin2 aus
int tasterWrite2 = 7;           // gibt die als zweites "gemerkten" Werte an den ledPin1 und ledPin2 aus
int tasterPoti = 8;               // führt zurück zum Potimodus, PWM Werte entsprechen Potistellung

int tasterStatusRec;            // Variable zur Aufname des Status TasterRec
int tasterStatusWrite1;        // Variable zur Aufname des Status TasterWrite1
int tasterStatusWrite2;        // Variable zur Aufname des Status TasterWrite2
int tasterStatusPoti;            // Variable zur Aufname des Status TasterPoti


PROGMEM uint8_t outvalue1[] = 
{
  0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,                         // Liste der möglichen Werte für LED1
                                                                                          // hier stehen z.Zt. hauptsächlich nur Platzhalter
 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,                    
 
 32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
 
 48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63} ;

   
PROGMEM uint8_t outvalue2[] = 
{

 25,24,53,52,51,25,24,8,2,24,25,4,3,2,241,39,                           // Liste der möglichen Werte für LED2
                                                                                            // hier stehen z.Zt. ebenfalls nur Platzhalter
 16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,                    
 
 2,1,0,9,8,21,6,25,4,3,212,211,210,209,208,207,
 
 20,20,20,26,2,2,21,199,198,197,16,15,94,93,12,10,}; 
 
  
void setup()
{
  Serial.begin(9600);                    // serielle Schnittstelle initialisieren
  
pinMode(tasterRec,INPUT);           // Pin 2 ist Input
pinMode(tasterWrite1,INPUT);       // Pin 4 ist Input
pinMode(tasterWrite2,INPUT);       // Pin 7 ist Input
pinMode(tasterPoti,INPUT);           // Pin 8 ist Input
}

void loop()
{
  int poti = analogRead(A0);                                           // Poti auslesen
  int range = map(poti, 0, 1023, 0, 63);                           // Poti Gesamtbereich in 64 Bereiche unterteilen
                                                                 
  Serial.print("B ");                                                       // "B x", zeigt aktuellen Potibereich an
  
  analogWrite(ledPin1, pgm_read_byte(outvalue1+range));   // PWM Wert aus Flash an LED 1
  analogWrite(ledPin2, pgm_read_byte(outvalue2+range));   // PWM Wert aus Flash an LED 2

  
 /*             Und hier fangen die Probleme an!
  
                 Mit dem tasterRec sollen die PWM Werte, die gerade durch die aktuelle Potistellung an den Leds anliegen, im Speicher irgendwie
                 "markiert" werden, damit ich sie später mit tasterWrite1 - unabhängig von der Potistellung - wieder an den ledPins ausgeben kann.
                                             
                 Das gleiche möchte ich einer zweiten Potistellung und den den dazu gehörenden PWM Werten mit tasterRec und TasterWrite2 erreichen. 
                                             
                 tasterPoti muss mich zurück in den Potimodus bringen, damit wieder die der momentanen Potistellung entsprechenden PWM Werte 
                 an den ledPins ankommen
 */                                             
           
  tasterStatusRec = digitalRead(tasterRec);     
  if(tasterStatusRec == HIGH)                               
   ...?                                                            // Ich kann doch die PWM Werte hier nicht nochmal speichern, sie sind doch schon im Flash...?
   
  tasterStatusWrite1 = digitalRead(tasterWrite1);
  if(tasterStatus == HIGH)
  
  
  tasterStatusWrite2 = digitalRead(tasterWrite2);
  if(tasterStatus == HIGH)
  
    
  tasterStatusPoti = digitalRead(tasterPoti);
  if(tasterStatus == HIGH)
  
        
  delay(10);                               
}

Danke für eure Hilfe und Grüße von
stringwalker

Für das das Pluszeichen habe ich bisher keine direkte Erklärung gefunden. Ich denke, es gehört zur syntax und sagt hier: Ordne dem gespeicherten Wert von outvalue dem entsprechenden Potibereich zu. Richtig?

Nein. pgm_read_byte() liest ein Byte aus dem Flashspeicher (der vorher per PROGMEM markiert werden musste). Der einzige Parameter dabei ist die Adresse. outvalue1 ist die Basisadresse des gespeicherten Arrays. Den Wert am entsprechenden Index erhalte ich, indem ich den gewünschten Index zur Basisadresse addiere. Der Ausdruck

pgm_read_byte(outvalue1+range)

entspricht also einem

outvalue1[range]

wenn PROGMEM nicht verwendet worden wäre.

  Serial.print("B ");                                                       // "B x", zeigt aktuellen Potibereich an

In meinem Code war die nachfolgende Zeile wichtig, das Serial.println(range) ist also notwendig, sonst siehst Du nicht das, was Du erwartest.

  tasterStatusRec = digitalRead(tasterRec);     
  if(tasterStatusRec == HIGH)

Der Code im if wird durchgehend ausgeführt, solange der Taster gedrückt ist. Solltest Du damit also Werte z.B. im EEPROM speichern wollen, so machst Du das sehr häufig und Dein EEPROM ist relativ schnell verschlissen. Du musst also noch zusätzlichen Code einbauen, der feststellt, wann der Wechsel von LOW nach HIGH passiert und nur dann reagiert. Genau genommen solltest Du den Taster noch entprellen, also auf den Wechsel nur reagieren, wenn der letzte Wechsel nicht eine gewisse Zeit zurückliegt.

// Ich kann doch die PWM Werte hier nicht nochmal speichern, sie sind doch schon im Flash...?

Du musst ja auch nicht die Ausgabewerte speichern, sondern den Wert der "range"-Variable. Die bestimmt, welcher Wert ausgegeben wird.

Der Code im if wird durchgehend ausgeführt, solange der Taster gedrückt ist.

Ja stimmt, das darf natürlich nicht sein beim Beschreiben des EEProms, das hatte ich nicht bedacht. Man braucht also einen Flankendetektor für pos. oder neg. Flanken, wie das in der Digitaltechnik genannt wird. Die Notwendigkeit, mechanischerSchalters zu entprellen, ist mir auch klar.

Leider merke ich aber, dass ich noch vieles lernen muss, ehe ich mein Projekt zu Ende kriege. Der Anfang ist zwar gemacht, aber es ist doch sehr frustrierend, dass es jetzt so viele Hindernisse und Misserfolge gibt.

Ich mache mich jetzt erst einmal daran, grundsätzliche Elemente zu beherrschen wie EEProm beschreiben, Flashspeicher auslesen u.a. So komme ich jedenfalls nicht mehr befriedigend voran.

Danke trotzdem für eure Hilfe und Geduld.

stringwalker

Mach dir nix draus.

Ich habe auch gelernt, dass Taster einlesen recht schwierig ist.

Nicht das codieren an sich, das ist einfach.
Aber sich vorher zu überlegen: "was soll eigentlich genau passieren, wenn ich einen Taster drücke, oder gedrückt halte, oder loslasse, oder schnell hinterander drücke....."
Ich habs bei mir gerade zum fünften Mal umprogrammiert, bis ich alles so hatte, das die Reaktion des Systems auf meinen Tastendruck so war, wie ich es (eigentlich) wollte.
die ersten vier male war mir nicht so ganz klar, was ich eigentlich will.......

Das entprellen an sich geht einfach:
nimm die Bilbliothek "bounce" sowie die darin enthaltenen Funktionen und alles wird gut.

Gunther

Danke für die Aufmunterung und den Link! Die bounce lib ist super.

Grüße
stringwalker