<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Library on Schallberts Blog</title><link>https://blog.schallbert.de/tags/library/</link><description>Recent content in Library on Schallberts Blog</description><generator>Hugo</generator><language>de</language><atom:link href="https://blog.schallbert.de/tags/library/index.xml" rel="self" type="application/rss+xml"/><item><title>ClickEncoder</title><link>https://blog.schallbert.de/projects/encoder/</link><pubDate>Mon, 01 Jan 0001</pubDate><author>Schallbert</author><guid>https://blog.schallbert.de/projects/encoder/</guid><description type="html">&#10; &lt;img src="https://blog.schallbert.de/assets/images/encoder/encoder-thumb.jpg"&#10; class="post-cover"&#10; alt="Image: Encoder on Breadboard"&#10; title="ClickEncoder" /&gt;&#10;&lt;h2 id="projekt-steckbrief"&gt;Projekt-Steckbrief&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Schwierigkeitsgrad: Mittel 3/5&lt;/li&gt;&#10;&lt;li&gt;Kosten: 0€&lt;/li&gt;&#10;&lt;li&gt;Zeitaufwand: ~6h&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="überblick"&gt;Überblick&lt;/h2&gt;&#10;&lt;p&gt;&lt;figure class="media-frame media-frame--right"&gt;&#10; &lt;img src="https://raw.githubusercontent.com/isocpp/logos/64ef037049f87ac74875dbe72695e59118b52186/cpp_logo.svg" alt="Image: C&amp;#43;&amp;#43; logo"&gt;&lt;/figure&gt;&#10;Bibliothek für das &lt;a href="https://www.arduino.cc/en/Reference/Libraries" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Arduino Framework&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt;, geschrieben in C++.&lt;/p&gt;&#10;&lt;p&gt;Der Quellcode selbst besteht nur aus ein paar hundert Zeilen. Er ist leichtgewichtig und leistungsfähig, da der verbesserte Algorithmus weniger Berechnungen benötigt und gleichzeitig eine bessere Zustandsinterpretation bietet als das ursprüngliche Projekt, von dem sie abgezweigt ist. Die Bibliothek ist offiziell auf &lt;a href="https://platformio.org/lib/show/11808/ClickEncoder" target="_blank" rel="noopener noreferrer" class="external-link"&gt;PlatformIO IDE&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; veröffentlicht und wird auf &lt;a href="https://github.com/Schallbert/encoder" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Github&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; gehostet und gepflegt. Sie enthält Beispiele sowohl für Arduino- als auch für PlatformIO-IDEs und bietet Unittests, die nach der Anpassung an die spezifische Anwendung ausgeführt werden können.&lt;/p&gt;&#10;&lt;h2 id="motivation"&gt;Motivation&lt;/h2&gt;&#10;&lt;p&gt;Ich habe zunächst die von &lt;a href="https://github.com/0xPIT/encoder" target="_blank" rel="noopener noreferrer" class="external-link"&gt;0xPIT&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; veröffentlichte Originalbibliothek verwendet, aber diese verursachte bei mir einige Compiler-Warnungen, außerdem waren die Messwerte des Encoders nicht einwandfrei und manchmal gab es Jitter oder zurückspringende Werte, was ich nicht akzeptieren wollte.&lt;/p&gt;&#10;&lt;h2 id="lösung"&gt;Lösung&lt;/h2&gt;&#10;&lt;p&gt;Da das Archiv ungepflegt aussah und meine Anfrage für Verbesserungen nicht beantwortet wurde, beschloss ich, diese Probleme selbst zu beheben. Ich habe also behutsam einen Teil des Codes umgeschrieben. Für mein damaliges Projekt benötigte ich auch ein &amp;ldquo;Wiederholungssignal&amp;rdquo;, wenn eine Taste kontinuierlich gedrückt wird, also fügte ich auch dies hinzu. Schließlich trennte ich den Encoder-Algorithmus von der Tastenbehandlung und stellte separate Klassen bereit, so dass nun bei Bedarf jede beliebige Kombination aus Taste und Encoder verwendet werden kann, jeweils mit konfigurierbarem Verhalten.&lt;/p&gt;&#10;&lt;h2 id="encoder-algorithmus-erklärt"&gt;Encoder Algorithmus erklärt&lt;/h2&gt;&#10;&lt;h2 id="api"&gt;API&lt;/h2&gt;&#10;&lt;p&gt;Die API von ClickEncoder ist einfach und leicht zu verstehen: &lt;code&gt;service()&lt;/code&gt; führt die Geschäftslogik aus und die Zustandsinterpretation durch. Diese Methode muss regelmäßig ausgeführt werden, um zu überprüfen, ob der Encoder seit seinem letzten Aufruf bewegt wurde, in welche Richtung, wie viele Schritte, und diese Werte dann in einer Akkumulatorvariablen speichern.&#10;&lt;code&gt;getIncrement()&lt;/code&gt; gibt die Schrittänderungen des Encoders seit dem letzten Aufruf zurück. &lt;code&gt;getAccumulate()&lt;/code&gt; gibt die Summe der Inkremente seit dem Start zurück. &lt;code&gt;getButton()&lt;/code&gt; gibt den aktuellen Button-Status zurück.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;service&lt;/span&gt;();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getIncrement&lt;/span&gt;();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;getAccumulate&lt;/span&gt;();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Button&lt;span style="color:#f92672"&gt;::&lt;/span&gt;eButtonStates getButton();&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="encoder-status"&gt;Encoder-Status&lt;/h3&gt;&#10;&lt;p&gt;Der Encoder verwendet einen &lt;a href="https://de.wikipedia.org/wiki/Gray_code" target="_blank" rel="noopener noreferrer" class="external-link"&gt;Gray Code&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; zur Kodierung der Schritte. &lt;code&gt;getBitCode()&lt;/code&gt; wandelt diesen Code, der über die Hardware-Pins A und B des Encoders übertragen wird, in eine &amp;ldquo;0&amp;hellip;3&amp;rdquo;-Rasten-Interpretation um, wobei die Zahlen bei einer Rechtsdrehung ansteigen und bei einer Linksdrehung abfallen. Dies geschieht effizient ohne Verwendung von &lt;code&gt;if()&lt;/code&gt; oder anderer Verzweigungslogik.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; Encoder&lt;span style="color:#f92672"&gt;::&lt;/span&gt;getBitCode()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// GrayCode convert&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// !A &amp;amp;&amp;amp; !B --&amp;gt; 0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// !A &amp;amp;&amp;amp; B --&amp;gt; 1&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// A &amp;amp;&amp;amp; B --&amp;gt; 2&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// A &amp;amp;&amp;amp; !B --&amp;gt; 3&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; currentEncoderRead &lt;span style="color:#f92672"&gt;=&lt;/span&gt; digitalRead(pinA);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; currentEncoderRead &lt;span style="color:#f92672"&gt;|=&lt;/span&gt; (currentEncoderRead &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// bit0 of result to be inverted if set&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; currentEncoderRead &lt;span style="color:#f92672"&gt;^=&lt;/span&gt; digitalRead(pinB);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; currentEncoderRead;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Ich habe hier mit einem Trick gearbeitet, sodass man sich den Code-Schnipsel oben möglicherweise mehrfach ansehen muss. Der Zustand von &lt;code&gt;PinA&lt;/code&gt; wird auf Bit1 von &lt;code&gt;currentEncoderRead&lt;/code&gt; geschrieben, indem ich es mit der &lt;code&gt;&amp;lt;&amp;lt;&lt;/code&gt;-Operation nach links schiebe. &lt;em&gt;Gleichzeitig bleibt der Status des 0. Bits durch &lt;code&gt;|=&lt;/code&gt; aber erhalten&lt;/em&gt;. Würde ich jetzt &lt;code&gt;PinB&lt;/code&gt; einfach logisch &lt;code&gt;&amp;amp;=&lt;/code&gt; anbinden, dann würde ich den Gray-Code verletzen, nachdem &lt;code&gt;A&amp;amp;&amp;amp;B=2&lt;/code&gt; sein sollte und nicht &lt;code&gt;3&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Sind &lt;code&gt;A&lt;/code&gt; und &lt;code&gt;B&lt;/code&gt; also beide nicht gesetzt, bleibt das Ergebnis &lt;code&gt;0&lt;/code&gt;, denn &lt;code&gt;00^00=00=0&lt;/code&gt;. Ist nur &lt;code&gt;A&lt;/code&gt; gesetzt, &lt;code&gt;B&lt;/code&gt; aber nicht, so lautet das Ergebnis &lt;code&gt;11^00=11=3&lt;/code&gt;. Wenn &lt;code&gt;B&lt;/code&gt; gesetzt ist, &lt;code&gt;A&lt;/code&gt; aber nicht, ergibt sich &lt;code&gt;00^01=01=1&lt;/code&gt;. Sind beide gesetzt, schließlich &lt;code&gt;11^01=10=2&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Für weitere Informationen zum Thema logische Binäroperationen siehe &lt;a href="https://blog.schallbert.de/projects/fireplace/#galois-lfsr-implementierung"&gt;mein Steckbrett-Lagerfeuer Projekt&lt;/a&gt;&lt;/p&gt;&#10;&lt;h3 id="zustandsinterpretation"&gt;Zustandsinterpretation&lt;/h3&gt;&#10;&lt;p&gt;Nachdem der Bitcode gelesen wurde, wird die Variable &lt;code&gt;rawMovement&lt;/code&gt; gesetzt, die uns sagt, wie weit der Encoder zwischen zwei Durchläufen der &lt;code&gt;service()&lt;/code&gt; Routine gedreht wurde. Hier sieht man, dass dieser Schritt zeitkritisch ist: Es kommt zu Aliasing-Fehlern, wenn der Geber schneller einmal alle Positionen wechselt, als dass &lt;code&gt;service()&lt;/code&gt; erneut aufgerufen wird. In diesem Falle würden Schritte verloren gehen.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Encoder&lt;span style="color:#f92672"&gt;::&lt;/span&gt;handleEncoder()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; encoderRead &lt;span style="color:#f92672"&gt;=&lt;/span&gt; getBitCode();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// bit0 set = status changed, bit1 set = &amp;#34;overflow 3&amp;#34; where it goes 0-&amp;gt;3 or 3-&amp;gt;0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; rawMovement &lt;span style="color:#f92672"&gt;=&lt;/span&gt; encoderRead &lt;span style="color:#f92672"&gt;-&lt;/span&gt; lastEncoderRead;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastEncoderRead &lt;span style="color:#f92672"&gt;=&lt;/span&gt; encoderRead;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// This is the uint-&amp;gt;int magic, converts raw to: -1 counterclockwise, 0 no turn, 1 clockwise&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int8_t&lt;/span&gt; signedMovement &lt;span style="color:#f92672"&gt;=&lt;/span&gt; ((rawMovement &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;) &lt;span style="color:#f92672"&gt;-&lt;/span&gt; (rawMovement &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;));&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; encoderAccumulate &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; signedMovement;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; encoderAccumulate &lt;span style="color:#f92672"&gt;+=&lt;/span&gt; handleAcceleration(signedMovement);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Hier ein weiterer Trick: &lt;code&gt;signedMovement&lt;/code&gt; wird berechnet, indem die Bewegung in eine vorzeichenbehaftete Ganzzahl umgewandelt wird, die &lt;code&gt;0&lt;/code&gt; als &amp;ldquo;nicht gedreht&amp;rdquo;, &lt;code&gt;-1&lt;/code&gt; als &amp;ldquo;links gedreht&amp;rdquo; und &lt;code&gt;1&lt;/code&gt; als &amp;ldquo;rechts gedreht&amp;rdquo; darstellt. Dabei wird das Verhalten bei Integer-Überlauf ausgenutzt, in Kombination mit der Interpretation von &amp;ldquo;signedness&amp;rdquo;.&lt;/p&gt;&#10;&lt;p&gt;Abschließend wird die Akkumulation durchgeführt und die Beschleunigung, sofern konfiguriert, dem Akkumulator hinzugefügt, je nachdem, wie schnell der Geber gedreht wird.&lt;/p&gt;&#10;&lt;p&gt;Um diese beiden Methoden ohne Verzweigungen zu programmieren, habe ich mehrere Abende Grübelei und praktisch einen halben Block kariertes Papier benötigt. Offen gesagt haben mich diese paar Zeilen Code an den Rande der Leistungsfähigkeit meines Hirns gebracht. Ich habe das Programmieren eben nicht von der Pike auf gelernt 😅&lt;/p&gt;&#10;&lt;h3 id="die-methode-getaccumulate"&gt;Die Methode getAccumulate()&lt;/h3&gt;&#10;&lt;p&gt;Sie ist wirklich einfach. Sie gibt nur den internen akkumulierten Wert zurück, der der Konfiguration des Encoders entspricht, wie viele Ticks einen Schritt ausmachen.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; Encoder&lt;span style="color:#f92672"&gt;::&lt;/span&gt;getAccumulate()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (encoderAccumulate &lt;span style="color:#f92672"&gt;/&lt;/span&gt; stepsPerNotch);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="die-methode-getincrement"&gt;Die Methode getIncrement()&lt;/h3&gt;&#10;&lt;p&gt;Gibt zurück um wie viele Ticks sich der Encoder seit dem letzten Aufruf dieser Methode bewegt hat.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; Encoder&lt;span style="color:#f92672"&gt;::&lt;/span&gt;getIncrement()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; accu &lt;span style="color:#f92672"&gt;=&lt;/span&gt; getAccumulate();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;int16_t&lt;/span&gt; encoderIncrements &lt;span style="color:#f92672"&gt;=&lt;/span&gt; accu &lt;span style="color:#f92672"&gt;-&lt;/span&gt; lastEncoderAccumulate;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastEncoderAccumulate &lt;span style="color:#f92672"&gt;=&lt;/span&gt; accu;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; (encoderIncrements);&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="ermittlung-des-tasterzustands"&gt;Ermittlung des Tasterzustands&lt;/h2&gt;&#10;&lt;h3 id="die-methode-handlebutton"&gt;Die Methode handleButton()&lt;/h3&gt;&#10;&lt;p&gt;Alle Tasterzustände leiten sich von zwei Basiszuständen ab: gedrückt oder nicht gedrückt. Dies spiegelt sich in der Logik wider:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; Button&lt;span style="color:#f92672"&gt;::&lt;/span&gt;handleButton()&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;{&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (lastGetButtonCount &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; ENC_BUTTONINTERVAL)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; lastGetButtonCount &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (digitalRead(pinBTN) &lt;span style="color:#f92672"&gt;==&lt;/span&gt; pinActiveState)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; handleButtonPressed();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; handleButtonReleased();&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (doubleClickTicks &lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; {&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;--&lt;/span&gt;doubleClickTicks;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Der Taster kann so konfiguriert werden, dass er seltener ausgelesen wird als der Encoder. Wenn dies der Fall ist, bleibt diese Methode zwischenzeitlich ohne Aktion.&lt;/p&gt;&#10;&lt;h3 id="taste-gedrückt"&gt;Taste gedrückt:&lt;/h3&gt;&#10;&lt;p&gt;&lt;code&gt;handleButtonPressed()&lt;/code&gt; kann entweder &lt;code&gt;Closed&lt;/code&gt;, &lt;code&gt;Held&lt;/code&gt; oder &lt;code&gt;LongPressRepeat&lt;/code&gt; zurückgeben, je nachdem wie der Taster konfiguriert ist und wie lange er bereits gedrückt wurde.&lt;/p&gt;&#10;&lt;h3 id="taste-nicht-gedrückt"&gt;Taste nicht gedrückt:&lt;/h3&gt;&#10;&lt;p&gt;&lt;code&gt;handleButtonReleased()&lt;/code&gt; kann entweder &lt;code&gt;Clicked&lt;/code&gt;, &lt;code&gt;Released&lt;/code&gt; oder &lt;code&gt;Doubleclicked&lt;/code&gt; zurückgeben, abhängig von der Konfiguration des Tasters und der Anzahl der Klicks innerhalb einer bestimmten Zeit.&lt;/p&gt;&#10;&lt;h3 id="konfiguration-der-bibliothek"&gt;Konfiguration der Bibliothek&lt;/h3&gt;&#10;&lt;p&gt;Das Verhalten der Encoder/Tastenerkennung kann in der Datei &lt;code&gt;encoder.h&lt;/code&gt; mit Hilfe der unten aufgeführten Konstanten geändert werden. Die Werte wurden bereits optimiert und fühlen sich, zumindest für mich, natürlich an. Sie sollten geändert werden, wenn Sie ein anderes Serviceintervall als &lt;code&gt;1ms&lt;/code&gt; verwenden möchten, da diese Werte alle dieses Intervall als Referenz nehmen.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-c++" data-lang="c++"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ----------------------------------------------------------------------------&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Acceleration configuration (for 1ms calls to ::service())&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; ENC_ACCEL_START &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;150&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// The smaller this value, the quicker you must turn to activate acceleration.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; ENC_ACCEL_SLOPE &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;75&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// the smaller this value, the stronger the acceleration will manipulate values.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// Button configuration (values for 1ms timer service calls)&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;//&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint8_t&lt;/span&gt; ENC_BUTTONINTERVAL &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;20&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// check button every x ms, also debouce time&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt; ENC_DOUBLECLICKTIME &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;400&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// second click within x ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt; ENC_LONGPRESSREPEATINTERVAL &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;200&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// reports repeating-held every x ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;constexpr&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;uint16_t&lt;/span&gt; ENC_HOLDTIME &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1200&lt;/span&gt;; &lt;span style="color:#75715e"&gt;// report held button after x ms&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;// ----------------------------------------------------------------------------&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="verwenden-sie-sie-in-ihrem-projekt"&gt;Verwenden Sie sie in Ihrem Projekt!&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Die Bibliothek ist eine Open-Source-Software, die unter der MIT-Lizenz steht.&lt;/li&gt;&#10;&lt;li&gt;PlatformIO-Benutzer? Ganz einfach, suchen Sie sie in der Ansicht &amp;lsquo;Bibliotheken&amp;rsquo;, die Tags sind Arduino, Encoder, Schallbert. Dann ist es nur noch ein Klick, um sie zu Ihrer Lösung hinzuzufügen.&lt;/li&gt;&#10;&lt;li&gt;Alternativ können Sie auch das &lt;a href="https://github.com/Schallbert/encoder" target="_blank" rel="noopener noreferrer" class="external-link"&gt;repository&lt;span class="external-link-icon" aria-hidden="true"&gt;↗&lt;/span&gt;&lt;/a&gt; klonen oder forken und die Header/Cpp-Datei nach eigenem Ermessen verwenden.&lt;/li&gt;&#10;&lt;li&gt;Sie möchten etwas beitragen? Vielleicht haben Sie eine effizientere Zustandsberechnung für die Taster? Ich würde mich freuen, Ihren Pull Request in meinem Posteingang zu finden.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item></channel></rss>