Stefan hat doch schon die richtige Idee in den Topf geworfen, die
richtige Lösung ist da komplett auf while() zu verzichten für einzelne
Komponenten, das Programm läuft immer durch und es wird anhand von
Zuständen entschieden wie und wo verzweigt wird.
Sein Beispiel-Programm ist jetzt nicht so der Hit, da jeder "Thread" mit
millis() seine eigene Auswertung macht, anstatt in der loop() Funktion
für eine gemeinsame Zeitbasis zu sorgen indem die Funktionen nur alle
10ms aufgerufen werden, aber das Prinzip ist nicht falsch.
Ob man nun einen Taster oder ein Touch-Event auswertet, oder was
anderes, man muss nicht das geplante Intervall abwarten.
Mit einer festen Zeitbasis, etwa 1ms, 5ms oder 10ms, oder was auch immer
passend erscheint, fragt man den Taster zyklisch ab.
Ist der Taster betätigt zählt das Programm hoch, ist er nicht betätigt
zählt das Programm gegen Null runter.
Überschreitet der Zähler eine Schwelle wird die nächste Funktion
ausgelöst.
Ja, so kann man nebenbei Taster entprellen, z.B. im 1ms Raster abfragen
und wenn der Wert über z.B. 9 geht, dann gilt der Taster als betätigt.
Jetzt kann man da noch viel mehr mit machen, im einfachsten Fall setzt
man den Zähler auf einen festen Wert damit er nicht weiter hoch zählt.
Oder man lässt ihn weiter hoch zählen bis zu einer weiteren Schwelle für
eine zweite Funktion und dann vielleicht noch weiter zu einer dritten
Schwelle ab welcher der Taster als verklemmt gilt.
Oder man setzt den Zähler auf Null so das durch fortlaufendes Drücken
des Tasters die Funktion wiederholt ausgelöst wird.
Und so weiter, das Prinzip ist jedenfalls möglichst an keiner Stelle im
Programm auf irgendwas zu warten, es sei denn sehr kurz wie zum Beispiel
ein SPI.transfer(data) ein blockierender Aufruf ist.