Verständnisfrage zu Rot-Geber ALPS STEC12

Hallo in die breite Masse,

Ich möchte o.g. Rotary Encoder zur Steuerung der Drehrichtung, wie auch der Roationsgschwindigkeit verwenden. Die vorhandene Tasterfunktion soll neben einem Übergang bei der Rotationsgeschwindigkeit zum Starten und Stoppen der Drehbewegung genutzt werden.
Vom angedachten Programmlauf folgender:

  • über einen weiteren Schalter wird nur für den Programmstart die Drehrichtung vorbestimmt, und hat dann keine weitere Funktion mehr.
  • die Rotation wir dadurch erzeugt, das in einer festen Folge die Steuerpins über ein Array nacheinander angesprochen werden. Je nach vorgegebener Drehrichtung läuft die Zählschleife entweder vorwärts oder rückwärts. Soweit alles noch kein Problem :wink:
  • Die Startgeschwindigkeit ist ein fester Wert im Programm, muss aber ggf durch Anderung im Programm anpassbar sein.
  • durch drehen an diesem Geber soll immer und unabhängig der Startdrehrichtung bei einer Linksdrehung oder Rastung bei einem Drehsinn entgegen des Uhrzeigers die Geschwindigkeit bis zu einem Grenzwert erhöht werden, ist die Startdrehrichtung im Uhrzegersinn, dann Verlangsamt werden, auch wieder bis zu einem Grenzwert und dann aufhören. Beim weiterdrehen des Gebers in die gleiche Richtung soll dann auch die Drehrichtung gändert werden. Ein darüberhinausdrehen des Rot-Gebers über die Maximalgeschwindigket soll das Programm ignorieren. Im Stopp Falll müssen die Steuerpins jedoch aktiv bleiben, oder weiterhin auf High bleiben. Wird zwischenzeitlich die Taste des Rot-Gebers gedrückt, soll die resultierend Drehbewegung schlagartig aufhören, und bei erneutem Tasterdruck fortgesetzt werden.
    Nun habe ich folgendes Problem, wo mir der richtige Umsetzungsgedanke fehlt.
    Beide Ereignisse Drehen am Rot-Geber, oder die Tasterbetätigung auch am Rot-Geber müssen über einen Interrupt abgefangen werden.
    Versuche ich nun das Durchzählen der For-Schleife mit einem Delay auszubremsen, um die gewünsche Geschwindigkeit einzustellen, müsste der Delay Wert im Falll das die Roation gestoppt werden soll, theoretisch unendlich sein.
    Der Versuch die Weiterschaltung im Array über einen Timerinterrupt durchzuführen scheitert bei mir n der Tatsache, dass die Timerlaufzeit über einen Parameter bestimmt werden müsste.
    Weiterhin fehlt mir auch ein Lösungsansatz, wie ich verhindern kann, das die Pin Umschaltung unvollständig abläuft. Dazu noch folgendes. Es sind und müssen, zumindest fast in Echtzeit immer 3 der Pins aus dem Array auf High geschaltet sein.
    Das heißt doch wohl, wenn jetzt meine Gedanken Richtig sind, dass während des Umschaltprozesses der Interrupt zwar bemerkt weden darf und soll, aber erst nachdem der Umschaltvorgang stattgefunden hat zur Ausführung kommen darf ?

Ich bedanke mich für jeden Input, und wüsche ein schönes WE

Gru0 Andy

Hallo,
Ich gehe davon aus, das es bei der Drehbewegung um einen Schrittmotor geht.

Ich würde den Geber über Interrupts auswerten und auch den Motor selbst über einen Timer Interrupt steuern. Je nachdem wie oft dieser dann aufgerufen wird, kann sich das Bitmuster an den Steuerpins dann schneller oder langsamer ändern. Das Timer Register selbst muss dann über den aktuellen Wert des Drehgebers berechnet werden.

Beim Drucken der Taste wird der Timer gestoppt/gestartet.

Gruß,
Horst

Schaltplan posten
Echtbild posten
kompilierbaren Sketch in Code Tags posten - der Sketch muss (soll) gar nicht alles enthalten - nur das eigentliche Problem zeigen können - und kompilierbar sein.

Generell sei anzumerken: wenn jemand Interrupts verwendet und nicht auf Delay verzichten kann, dann sollte der jenige das "Blink Without Delay" Beispiel noch ein paar mal durchgehen, bis das letzte Delay aus dem Code verschwunden ist.

Hallo,

du hast leider falsch geraten :wink:
Es geht im erweiterten Sinne um eine Lichtsteuerung.
Dabei geht es um eine vielzahl ( LED GRAB ) kreisförmig angeordnet LEDs.
Jetzt geht es erst einmal um die Machbarkeit und den Speciherbedarf. Da das Arduino nur über eingeschränkt nutzbare Port-Pins verfügt, erst einmal im kleinen.
Jetzt sollen erst eiinmal nur 3 LEDs eine Dreiecksform darstellend eine Drehbewegung ( ähnliche einer klassischen Rundumleuchte mit 3 Speigeln ) vollführen. Schlussendlich soll für Darstellungszwecke, dann aber nicht mehr mit einer direkten PIN Ansteuerung sondern über mehrere PCA9685 Port-Expander mit PWM 320 RGB LEDs angesteuert werden, dass dann nicht nur 3, sondern bis zu 12 LEDs gleichzeitig, auch mit unterschiedlichen Abständen zueinander und in unterschiedlichen Farben diese Drehbewegung ausführen.
Jetzt geht es erst einmal um die reine Steuerung, oder Steuerbarkeit und welche Ressourcen dafür benötigt werden. Vielleicht ist auch ein solcher AVR ATMEGA zu schach auf der Brust eine solche Aufgabe zu übernehmen.

Gruß
Andy

320 RGB LEDs = 960 LEDs ... 16 Ausgänge am PCA9685 ... ernsthaft du willst 60 davon verbauen?

glaubst nicht, dass das mit WS2812 oder APA106 einfacher wäre? Aber im Grunde genommen ist das egal, du steuerst halt 3 bis 12 dieser LEDs an. Du hast Code der sich um die LED Ansteuerung kümmert und einen Code der sich um den Incrementalgeber kümmert. Warum brauchst du da ein einziges delay?

WS2812 und Interrupt sehe ich problematisch da während der Datenausgabe auf die WS2812 jeglicher Interrupt abgeschaltet werden muß. Die Datenausgabe für 320 WS2812 dauert ca 1mS.

Außerdem prellt der mechanische kontakt des Drehgebers was Mehrfachaufrufe des Interrupts zur folge hat.

Grüße Uwe

Hallo noiasca,

ich weiss zwar nicht was es dich angeht was ich verbauen möchte oder nicht. Zudem wenn ich die Preise sehe, die Verfügbarkeit sehe, und dann noch erkennen muss, dass du den Inhalt meiner Frage nicht im geringsten verstanden hast. Also lass es ...
Von den PCF9685 habe ich 100 Stück rumliegen, eine 4 PIN RGB LED kostet mich keine 30 Cent pro Stück, und ich erspare mir zudem den Code für eine PWM Regleung . Zur Spannungsversorgung habe ich ebenfalls ein ganze Kiste MCP1726 1,5 A Festspannungsregler herumliegen.
Also mache dir mal bitte weniger Gedanken über irgendwelche Projektkosten.
Ich hatte eine klare Frage formuliert, aber von dir kommt alles, nur keine passende Antwort.
Und ich hatte in meinem zweiten Post eindeutig geschreiben, jetzt geht es erst einmal darum, das ganze Spiel als Versuchschaltung mit 15 einfarbigen LEDs aufzubauen, und die Funktion mit dem ROT-Geber so hinzubekommen, dass das gewünschte Ergebnis zustande kommt.
Also bitte beim Thema und der Frage bleiben.

Dadurch das das ganze später mal Ausmaße annehmen wird, größenmäßig und der Ring aus Segmenten bestehen wird, damit dieser Transportfähig bleibt, bekommt jedes Segemt eine eigene Spromversorgung, und die Verbindung zur Steuerzentrale wird mit jeweils einer 3 poligen Steuerleiting I²c + GND umgesetzt.

Danke trotzdem, dass du dir Gedanken über schon gelegte Eier gemacht hast, aber dennoch und nochmals hat das nichts mit meiner Frage zu tun

Gruß
Andy

Hallo,

Jetzt mal dazu, der Drehgeber soll nicht dazu genutzt werden, um ein Rotationssignal auszulösen, sondern einfach nur dazu, die Geschwindigkeit und den Drehsinn zu ändern. Das sind alles keine zeitkritischen Funktionen. Ein softwareseitiges Entprellen mit einer Totzeit von 20 ms sind überhaupt kein Problem.
Und ob, oder wann dort mal einer dran herumdreht, an diesem ROT-Geber steht auch noch auf einem ganz anderen Blatt.

Gruß
Andy

Wohl wahr.
In der Praxis empfiehlt es sich daher, die Geschwindigkeitssteuerung eben nicht mit for() und delay() zu implementieren, sondern die Schleifeneigenschaft von loop() auszunutzen und die Zeitsteuerung über millis() und Vergleich mit vergangener Zeit seit letztem "TimeTick" zu realisieren.
Das ist genau das, was Blink Without Delay macht. Ob Du dabei eine, drei oder später 30 LEDs pro Tick behandelst, ist (fast) wurscht. Die Behandlung darf nicht länger dauern als das Zeitintervall lang ist.

Prinzipiell auch richtig.
Das mir bekannte übliche Vorgehen ist, dass die Interrupt-Service Routinen nur Ereignisse mitteilen und im asynchron dazu laufenden "Hauptprogramm" eine Zustandsmaschine implementiert ist, die - wenn es denn der jeweilige Zustand zulässt - auf das Ereignis reagiert. Im Zustand "Umschaltung aktiv" ist das nicht der Fall, in einem Nachfolgezustand "keine Umschaltung aktiv - warte auf/mache was anderes" kann das Ereignis bearbeitet werden.

Hallo,

Entweder ich habe mich geistig schon verrannt, oder ich verstehe den Zusammenhang wirklich nicht.
Auch wenn ich das mit einem LOOP machen würde, muss ich doch im STOPP Fall, den Programmteil nachhaltig anhalten, oder in eine Endlose Warteschleife schicken, bis dass Signal egal über Tastendruck, oder einer Drehbewegung am ROT-Geber kommt, dass der Ablauf, der schlussendlich zu dieser optischen Rotationsbewegung führt angehalten wird. Dabei muss aber auch noch sichergestellt sein, dass die aktuelle Parrung an LEDs die Zeitgleich leuchten sollen auch an bleiben.

OKay ich habe einen Loop.
Was würde dort stehen ?
Schalte das erste der 5 Paare zu je 3 LEDs an.
-> Soweit richtig ?
Hier kommt jetzt die Verzögeung bsi diese Dreiergruppe wieder ausgeschaltet wird. Oder im Falle das die Rotationsdarstellung angehalten werden soll - was dann ?
Jetzt wie mache ich das in einem Loop ohne Schleife, das ich nun entweder das nächste, oder das vorherige Paar ansteuere ? Das ist schließlich abhängig von der Drehrichtung.

const int PINs[] = { ' hier die Liste der 15 PIns an denen die LEDs angeschlossen sind '};
int Pausenzeit = 1000;
// Setup-Teil
loop() {
  if  (Pausenzeit < 0)  {
    for (int i = 4; i >= 0; i--) {
      LEDs_ON(PINs[i], PINS[i+5], PINs[i+10]);
     // hier stand im ersten Test noch _delay_ms(abs(Pausenzeit)
     LEDs_OFF(PINs[i], PINS[i+5], PINs[i+10]);
   }
 if  (Pausenzeit > 0)  {
    for (int i = 0; i <5 0; i++) {
      LEDs_ON(PINs[i], PINS[i+5], PINs[i+10]);
     // hier stand im ersten Test noch _delay_ms(abs(Pausenzeit)
     LEDs_OFF(PINs[i], PINS[i+5], PINs[i+10]);
   } 
  // jetzt kommt der Teil was mache ich wenn Pausenzeit auf 0 ist, oder wenn durch den Taster der Stopp der Rotationsbewegung gefordert ist ?
 }

Mein Gedankenansatz war, dass ich den Encoder abfrage und dann einen Zahlenwert zurückgeliefert bekomme, der einer Verögerungszeit positiv oder negativ entspricht.

und hier an dieser stelle komme ich nicht weiter.
Ich muss auf 3 Pins mit oder über 2 unterschiedliche Interrupte reagieren können.
Also müsste ich doch, den Taster an einen PIN hängen der über den Vector PCINT1, und der Encoder über den Vector PCINT2 ? Das ich zuvor über PCMSK die Pins definieren muss habe ich auch schon verstanden.

Und nun noch einml auf das " BlinkWithoutDelay" zurück zu kommen. Das nützt mir nur dahingehend etwas, solange auch ein Weiterschalten gefordert ist.

Ja, noiasca ich habe mir diesen Abschnitt durchgelesen, ich habe auch den Ablauf, zumindest glaube ich das, vertanden. Aber was ist wenn keine Drehbewegung oder ein Weiterschalten benötigt wird ? Der Ablauf ist auch nur ein Herunterzählen irgendwelcher Taktzeiten in dem Falle Milisekundent. Oder stimmt das etwa nicht ??
Somit würde im Fall dessen, dass keine Fortschalten aktuell gewünscht wird, was notwenig sein ? const long interval = 1000; müsste in diesem Fall den Wert unendlich annehmen.

Also machen wir mal sachlich weiter !
Schalter 1 an PIN PXx wird mit dem Programm start abgefragt, und bestimmt die Startdrehrichtung .
Nun beginnt die Rotationsdarstellung.
Sie läuft.
Erfolgt nun ein Tastendruck auf den ROT-Geber, soll die aktulle Position eingeschlatet bleiben, oder wenn gerade in der Umschaltphase die letzte aktive wieder angehen. Dann könnte der Prozessor in den Schlafmodus gehen, und auf den nächsten Interrupt warten,ausgelöst durch entweder durch einen erneuten Tastendruck, dann geht es in der selben Richtung und mit der selben Geschwindigkeit weiter.
Wird nun der ROT-Geber entgegen der Uhrzeigerrichtung gedreht, soll weil der aktuelle Zustand ja STOPP ist, die Drehbewegung mit der größten Verzögerung gleichbedeutend der langsamsten Drehgeschwindigkeit auch entgegen des Uhrzeigersinns aufgenommen werden. Dreht man ihn nach recht, oder im Uhrzeigersinn, geht es mit der langsmsten Geschwindigkeit im Uhrzeigersinn weiter.
Jetzt noch der Teil, das Ding dreht sich schon, also soll wenn die Bewegung in Uhrzeigersinn läuft die Geschwindigkeit Schritt für Schritt verlangsamt werden, bis es schließlich zu Stillstand kommt. Hat man nun die Rostationsbewegung über den ROT-Geber zum Stillstandgebracht, darf aber nur in diesem Fall, eine Tasterbetätigung zu keiner Funktion führen. Wird im Anschluss der ROT-Geber egal in welcher Richtung wieder von dieser virtuellen Nullposition heruntergedreht, soll gleich dem Drehsinn am ROT-Geber die Rotationsdarstellung wieder beginnen.
Jede Drehbewegung am ROT-Geber über die Minimal und Maximal Werte hinaus, soll das Programm ignorieren.

Ich hoffe das war nun verständlich und ausführlich genug ?

Gruß
Andy

Hallo noiasca,

Das glaube ich mal so nicht.
Die Blink without delay Funktion ist auch ur eine Herunterzählschleife. Oder etwas nicht ?

Und nun haben wir wohl wieder das selbe Problem wie bei einem Delay, wenn keine Drehbewegung angefordert ist. Intervall müsste den Wert unendlich annehmen, weil keiner weiss oder vorhersagen kann, ob es mal weitergehen wird, oder diese Schaltung einfach nur abgeschaltet wird.

Dann erkläre mir bitte verständlich und nachvollziehbar, wenn die Umschaltfunktion in einem Loop() innerhalb von MAIN läuft, wie ich dann aus einer Interruptbehandlung heraus, ohne einen rjmp im Interrupthandler die CPU in den Tiefschlaf schicken kann !

Gruß
Andy

Nein, eine Schleife ist es nicht. auch keine Funktion, sondern ein Design Pattern.

Ich stimme dir zu.

Achtung - langer Sermon :slight_smile:

Das wäre eine neue Anforderung.
Ist Deine Anwendung kritisch was den Stromverbrauch angeht? Nur dann lohnt es sich über "schlafen" = Abschalten nachzudenken und da bin ich dann raus. Kenne das vom ESP32, aber nicht bei den ATmegas.

Ich meine, dass diese Annahme falsch ist.
Die loop() wird zyklisch ein paar Tausend Mal in der Sekunde aufgerufen. Ersatzcode (nicht sichtbar wenn normal über Arduino IDE gearbeitet wird). An Kommentarzeilen schiebt das Arduino-System ggf. noch was ein.

// ...
setup();
// ...

while (1)
{
  // ...
  loop();
  // ...
}

In der loop() selbst kannst Du dann entscheiden, ob etwas zu tun ist oder nicht. Falls nicht ist auch nicht schlimm - dreht er sich halt einmal im Kreis.

Ja, endlose Warteschleife - aber das heißt nur "(aktuell) nix zu tun".
Ansonsten hast Du doch (viele) Zustände wie

  • dreht sich mit konstanter Geschwindigkeit im Uhrzeigersinn
  • dreht sich mit abnehmender Geschwindigkeit im Uhrzeigersinn
  • steht 1 (nix zu tun)
  • dreht sich mit zunehmender Geschwindigkeit gegen den Uhrzeigersinn
  • dreht sich mit konstanter Geschwindigkeit gegen den Uhrzeigersinn
  • dreht sich mit abnehmender Geschwindigkeit gegen den Uhrzeigersinn
  • steht 2 (nix zu tun)
  • dreht sich mit zunehmender Geschwindigkeit im Uhrzeigersinn

Die Richtungsabhängigkeit kann man eliminieren, aber das ist Feintuning.
Das wäre in der loop dann ein switch mit acht case. In jedem davon kannst Du einzeln mit millis() Zeitintervalle abbilden. In den Fällen mit sich ändernder Geschwindigkeit dann verschachtelt (einmal für die Änderung der Geschwindigkeit und einmal für die Rotation).
In jedem der Zustände ist die Auswertung bestimmter Ereignisse - und damit ein Übergang in einen anderen Zustand - erlaubt oder verboten.

Wenn die einzelnen Aufgaben schön in Funktionen verpackt werden, ist Entkopplung gegeben und man kann das dabei entstandene Monster hinterher auch sinnvoll auf weniger Zustände eindampfen.
Beispielsweise kann die Behandlung der einzelnen LEDs in den for-Schleifen in einer Funktion landen, die in den cases mit konstanter Geschwindigkeit und mit zu/abnehmender Geschwindigkeit mit der dann gerade gültigen Geschwindigkeit aufgerufen wird.

Das ist nur eine mögliche Skizze einer Lösung, es gibt ganz sicher noch bessere (mit Objekten).
Wie schrub ein Forumsphiloph mal sinngemäß: C-Programmierer sind für Objektorientierung verdorben - mir geht das wohl so.

Schuldig, im Sinne der Anklage.

Nicht endgültig verdorben, aber die C Prinzipien liegen wie Betonklötze vor den sich neu mit C++ anbietenden Wegen.

Vergleich:
Ein Fluss, vor 100 Jahren begradigt, dann eine Stadt drum rum gebaut. Und jetzt soll er wieder frei mäandern dürfen.
Ja, etwa in dieser Art sind die Probleme.

Danke für die kompetente Hilfe !

Hallo,

Ich weiss nicht ob ich das jetzt richtig deute, weil ohne das ich eine abweichende Aussage mache, erzählen mir hier viele Leute immer irgendetwas ...

Ich fasse mal deinen Abschnitt zusammen, reduziere das auf 3 Zustände.

  • drehe dich mit konstanter Geschwindigkeit in die eine Richtung
  • bleibe stehen, oder tue nix
  • drehe dich mit konstanter Geschwindigkeit in die andere Richtung.

und dann noch den Zustand aus, was aber nichts mit dem Programm zu tun hat :wink:

Die eigentliche Geschwindigkeitsreglung wäre in dem Falle nur die Änderung der An-Zeit.
über diesen Rot-Encoder soll nun die An-Zeit bestimmt werden.
Das heißt nicht, dass sich der Encoder jetzt ständig mitdrehen muss.
Ich versuche das jetzt wiederholt noch einmal zu erklären.
Schalter steht auf Off, die Startdrehbewegung ist jetzt rechts. Der Startparameter für die Anzeit eines LED Paare ist jetzt nur mal zum Verständnis 1000ms.
Da es nur 15 LEDs gibt, und immer 3 zeitgleich angesteurt werden, gibt es nur 5 Steps und es hat den Anschein das Teil hätte sich einmal gedreht, obwohl nur 1/3 Umdrehung gemacht wurde. Aber auch egal.
Dreht nun der Bediener eine Rastposition am Encoder nach rechts wird aus den 1000 ms nun 950 ms - das licht läuft etwas schneller. Das ganze aber nur bis 200ms. Damit haben wir die höchste Geschwindigkeit ereicht.
Dreht man nun nach links, das selbe Spiel immer 50ms mehr pro Raste bis man den Wert 2500 ms erreicht hat. Das ist die Minimalgeschwindigkeit.
Dreht man nun noch eine Raststellung weiter nach links, tritt der Zustand "tue nix" ein.
Wieder eine Raste weiter beginnt sich das Dinge wieder mit Minimalgeschwindigkeit aber in entgegengesetzte Richtung zu drehen. Und so setzt sich das fort, bis wieder die Maximalgeschwindigkeit erreicht ist.

Hier ist auch wirklich nichts Zeitkritisch, dass man um ein Prellen dahingehend ausblenden kann, dass man wirklich eine Verweilzeit, oder Delay Einbaut, bevor die Interruptabfrage wieder freigegeben wird.

Eine Tastendruck stoppt die ganze Rotation, inwiefern nicht "Nichtstun" angesagt ist.
ein erneuter Tastendruck, und es geht so weiter wie bisher.

Schlimm in dem Sinne ist es schon, weshalb ich um fachkundigen Rat gebeten habe, weil mein Programm mit der For() Schleife macht die 1/3 Bewegung voll bevor es stoppt.

Das heißt, die Bescheunigung findet von einem aufgeruffenen LED Paar zum nächsten statt, indem einfach die Anzeit aprubt geändert wird.

Meine weiterführenden Gedanken waren, ein Interrupt über PCINT1 für den Encoder, PCINT2 für den Taster, und einen Timer-Interrupt in dem Falle wohl mit Parameterübergabe, der die Anzeit regelt.

Und jetzt bin ich wieder bei der Ausgangsfrage wie bekomme ich das mit den 3 Interrupten in C umgesetzt ? Schaut man in das Datasheet zu dem AVR findet man dort nur Beispiele in Assembler, die man so nicht 1 zu 1 auf C übertragen kann.

Leider so muss ich auch sagen, man findet viele viele Einträge zu diesen Encodern, entweder sie sind liniar geschrieben, ohne Interrupthandling, oder die benötigen Limitangaben für eine maximale Anzahl an Steps, die es bei mir nicht gibt.
und jetzt wieder DENN ! Wenn die Drehbewegung über den Taster gestoppt wurde, und die nächste Interruptauslösung über den Encoder kommt, dann hat sich mein Mittelpunkt verschoben. Also bleibt mir nur der Weg, so sehe ich das, dass ich aus dem Encoder-Interrupthandling nur die Zahlen, oder Werte 1 oder -1 zurückbekomme, damit ich

Pausenzeit += (Encoder-Return * 50);
if (Pausenzeit > 2000) {
  Pausenzeit = 2000;
}
if (Pausenzeit < -2000) {
  Pausenzeit = -2000;
}
if ((Pausenzeit == 2000) or (Pausenzeit == -2000) {
  // tue nix
}

und dann müsste der Timer nur noch mit abs(Pausenzeit) gefüttert werden.

Wer kann mir nun hierbei anhand eines praktischen Beispiels helfen ?

Gruß
Andy

Tipp:
Es gibt nicht nur "entweder oder"!

Für händisch bediente Encoder ist es meist sinnvoll sie in einem Timerinterrupt, und nicht in einem Pin Interrupt, auszuwerten. Wenn denn überhaupt Interrupts dafür verwendet werden sollen.

Erstaunlicherweise gibt es kein unendlich großen Zahlen.
Alles hat seine Grenzen.
Von daher kann es schon Sinn machen, die Grenzen anzugeben, bzw. zu definieren.

Es wäre sogar falsch, diese Limits zu ignorieren.

a)
du holst dir von deinem Incrementalgeber eine Usereingabe.
Der User dreht nach links oder nach rechts.
Das wertest aus.
Damit stellst du eine Soll-Geschwindigkeit von
2500 bis 200
0
-200 bis - 2500ms
Jede einzelne Veränderung erhöht oder vermindert die Sollgeschwindigkeit um 50 ms (mit der Beschränkung in der "Mitte" des Wertebereichs)

b)
Mit der Sollgeschwindigkeit (und dem Vorzeichen) bewegst du dein LED Muster nach Links oder rechts.

zwei verschiedene Aufgaben - zwei verschiedene Funktionen.
Beide können im Loop laufen.

Wenn der Ablauf richtig dargstellt ist - könnte man sich das ansehen wie man das umsetzt.
Wenn der Ablauf falsch ist - könntest du ihn richtig stellen.

... und
c)
Erster Tastendruck stellt die Sollgeschwindigkeit sofort auf 0. Das aktuelle Vorzeichen und die vorher gültige Sollgeschwindigkeit werden aufgehoben.

d)
Zweiter Tastendruck stellt die ursprünglichen Bedingungen wieder her.

Fehlt noch was?

a + b sind in meinem POC bisher 84 Zeilen,


Kein delay und eine Encoder Library ohne Interrupt - auch nur mit millis.

... wenn c/d auch eine Anforderung sind, dann wird sich das in den verbleibenden 16 Zeilen wohl auch noch ausgehen, wenn sich der @andy742000 vieleicht mal wieder meldet.