
Bei einem E-Paper, das 17 Sekunden für einen Refresh braucht, ist Rendergeschwindigkeit doch egal? Nicht ganz. Jede Millisekunde, in der die CPU auf 240 MHz rechnet, kostet Akku. Und ein Wandernavi, das nach einem halben Tag leer ist, ist ein Briefbeschwerer. Also habe ich am 10. Oktober einen Bildaufbau auseinandergenommen und nachgeschaut, wo die Zeit bleibt.
Wo die Zeit bleibt
Der größte Brocken war die Mathematik. Jeder Bildaufbau hat für jeden Wegpunkt des Tracks die Kachel berechnet, mit pow, log, tan und cos in doppelter Genauigkeit. Das macht der ESP32-S3 in Software, und bei einem langen Track hat das rund eine Sekunde pro Render gekostet. Jetzt wird die Position eines Wegpunkts einmal beim Laden als 32-Bit-Festkommawert auf der Weltkarte gespeichert. Kachel und Pixel einer Zoomstufe sind dann nur noch Bitshifts. Die Position des Geräts nutzt dieselbe Rechnung, damit Marker und Track auf denselben Pixeln landen.
Der zweite Brocken war das Zeichnen Pixel für Pixel. Den Framebuffer zu leeren hat jeden der 268 800 Pixel einzeln gesetzt und 157 ms gedauert, mit memset sind es 3 ms. Kartenkacheln werden jetzt auf das Display zugeschnitten, bevor sie gezeichnet werden, und zeilenweise kopiert, zwei Pixel pro Byte. Pixel für Pixel lag jeder Schreibzugriff in einer anderen Cache-Line des PSRAM. Das Ergebnis ist Byte für Byte identisch, das habe ich auf dem Gerät geprüft.
Dazu kleinere Dinge. Der Track wird pro Render nur noch einmal durchlaufen statt fünfmal, jeder Durchlauf durch die Liste im PSRAM kostete 6 ms, auch wenn er nichts tat. Das Höhenprofil mit 7302 Punkten wird einmal in einen Canvas gezeichnet und danach nur noch kopiert. Ein kompletter Render mit GPS-Fix und Track ging so von 269 ms auf 172 ms. Den Rest bis 105 ms holt der Release-Build mit -O2 und ohne Heap-Poisoning. Einen Track laden dauert jetzt 1,1 statt 1,8 Sekunden.
Weniger Takt, weniger Funk
Mit aktivem Power Management läuft die CPU nur noch mit 240 MHz, wenn eine Task sie braucht, sonst mit 80 MHz. Tiefer geht es nicht, weil Bluetooth und der UART des GPS am APB-Takt von 80 MHz hängen. Auf dem Kartenbildschirm mit Fix läuft die CPU 10 % der Zeit auf vollem Takt. Light Sleep bleibt vorerst aus, dafür sind UART, Bluetooth und die Versorgungspins noch nicht bereit.
Bluetooth war der nächste Verbraucher. Ein Handy, das 5 Minuten weder liest noch schreibt, wird jetzt getrennt. Danach sendet India Navi nur noch für 5 Sekunden Advertising, und zwar immer dann, wenn das Display ohnehin neu zeichnet. Dazwischen ist der Bluetooth-Stack komplett gestoppt. Die App weiß das und wartet beim Wiederverbinden auf das nächste Display-Update.
Auch wenn der Akku leer ist, passiert jetzt etwas Sinnvolles. Nach drei Messungen in Folge bei 5 % ohne Ladegerät zeigt das Display „Battery empty“, das GPS-Modul geht in Standby, damit es seine Uhr behält, und das Gerät in Deep Sleep. Das Bild bleibt auf dem E-Paper stehen, ohne Strom zu brauchen. Kommt ein Ladegerät dazu, startet die Karte von selbst, sobald der Akku sich erholt hat.
Aufräumen, bevor es zu spät ist
Am gleichen Wochenende habe ich die Task-Architektur umgebaut. Vorher deklarierte eine tasks.h die Schnittstelle jedes Moduls, main.c enthielt die Power-Task und den Button, und Event-Queue, SD-Mutex und Akkuwerte waren globale Variablen, die jeder direkt benutzte. Jetzt hat jedes Modul einen eigenen Header in include/navi/ und startet seine Task selbst. Die Event-Queue und der SD-Mutex sind privat, die GUI fragt den Status der Module ab, statt dass die Tasks GUI-Objekte beschreiben. Von außen sieht man davon nichts, aber der Umbau hat einen Fehler ans Licht gebracht: Ein Tastendruck, während das Ausschaltbild noch gezeichnet wurde, hat das Gerät direkt wieder eingeschaltet.
Apropos Taste: Ein kurzer Druck auf der Karte hat das Gerät manchmal ausgeschaltet statt den Zoom zu wechseln. Jedes Prellen belegte einen Platz in der Event-Queue der Größe 6, das Loslassen ging verloren und der Timer für den langen Druck lief ab. Jetzt meldet der Interrupt nur noch eine Änderung, und die Haupttask liest den Pegel nach 30 ms. Manchmal ist Embedded eben doch noch Elektronik.