Zielgruppe: Dieser Leitfaden richtet sich an Firmware-Ingenieure, RTOS-Architekten und OEM-Programmmanager, die verstehen müssen, wie ein Lesestift seinen Audioplayer, OID-Decoder, Tasten-Handler, BLE-Stack und OTA-Agenten einplant – und wie sich diese Planung auf die für den Nutzer sichtbare Latenz, die Akkulaufzeit und Field-Service-Tickets auswirkt. Er setzt voraus, dass Sie wissen, was ein Thread oder Task ist, erfordert jedoch keine RTOS-Expertise.
Jeder Lesestift führt mehrere Tasks gleichzeitig aus, selbst der kleinste P2 mit seinen 64 KB Flash. Auf einem typischen T2 finden Sie 24–38 aktive Tasks: einen OID-Decoder-Task, einen Audio-Mixer-Task, einen UI-Task, einen Wi-Fi-Supervisor-Task, einen BLE-GATT-Server-Task, einen OTA-Staging-Task, einen Akku-Mess-Task, einen Power-Management-Task und mehrere Sensor-Polling-Tasks. Der Scheduler entscheidet in jeder Mikrosekunde, welcher davon auf der CPU läuft.
Dieser Artikel führt Sie durch die vier Scheduler-Familien, die wir über unsere 11 SKUs (B1, B2, F8, K7, KF1, L5, P2, Q6, Q8, T2, Y6) hinweg einsetzen – FreeRTOS, Zephyr, AliOS Things und Linux CFS – und zeigt die Prioritätszuweisung, Preemption-Latenz, Prioritätsinversions-Mitigation und Load-Shedding-Strategie, die wir jeweils verwenden.
1. Was ein Lesestift-Scheduler tatsächlich tut
Ein Scheduler hat drei Aufgaben:
- In jedem Moment entscheiden, welcher Task auf der CPU läuft.
- Priorität durchsetzen – höher priorisierte Tasks verdrängen niedriger priorisierte.
- Die Latenz zwischen einem Ereignis (Interrupt, Signal) und dem Aufwachen des zuständigen Tasks begrenzen.
Bei einem Lesestift ist die dritte Aufgabe am wichtigsten. Ein Nutzer tippt mit dem Stift auf einen OID-Punkt, der OID-Sensor löst einen DMA-Complete-Interrupt aus, der OID-Decoder-Task muss innerhalb von 1–4 ms aufwachen, um die Seite zu decodieren, und der Audioplayer-Task muss innerhalb von 50 ms mit dem Streaming beginnen. Wenn der Scheduler diese Latenz nicht begrenzen kann, hört der Nutzer eine Verzögerung.
2. Die vier Scheduler-Familien, die wir ausliefern
| Scheduler | OS | SKUs, die ihn nutzen | Preemption-Modell | Tick-Rate |
|---|---|---|---|---|
| FreeRTOS tickbasiert | FreeRTOS 10.4 | B1, B2, P2 | Prioritätsbasiert preemtiv | 1 kHz |
| Zephyr tickless | Zephyr 3.4 LTS | K7, KF1 | Priorität + Deadline | 100 Hz Baseline, tickless idle |
| AliOS Things | AliOS Things 3.3 | Q6, Q8 | Prioritätsbasiert preemtiv | 1 kHz |
| Linux CFS | Linux 5.10 / 5.15 | F8, L5, T2, Y6 | Completely Fair Scheduler mit RT-Gruppe | 250 Hz / 1 kHz |
Die Aufteilung von zwei Dritteln RTOS zu einem Drittel Linux spiegelt den breiteren Lesestift-Markt wider. Die Scheduler-Wahl folgt auf die in TC-097 diskutierte OS-Wahl, die Prioritätspolitik folgt hingegen aus dem Feature-Set des jeweiligen SKU.
3. FreeRTOS-Scheduler auf B1, B2, P2
FreeRTOS verwendet einen Fixed-Priority-Preemptive-Scheduler mit optionalem Time-Slicing. Jeder Task hat eine Priorität von 0 (niedrigste) bis configMAX_PRIORITIES - 1 (höchste, typischerweise 32 oder 56). Der Scheduler führt stets den bereiten Task mit der höchsten Priorität aus; Gleichstände werden per Time-Slice aufgelöst.
Auf der B2-Firmware 2.4.7 lautet unsere Prioritätstabelle:
| Priorität | Task | Zweck | Stack |
|---|---|---|---|
| 7 (höchste) | audio_dma_isr |
DMA-Complete-Handler (ISR-deferred) | 256 Bytes |
| 6 | oid_decode |
OID-Seite decodieren | 1.024 Bytes |
| 5 | audio_player |
MP3-Decode + DAC-Feed | 2.048 Bytes |
| 4 | button_handler |
Button-Debounce + Long-Press | 512 Bytes |
| 3 | battery_monitor |
Batterieanzeige-Abfrage | 512 Bytes |
| 2 | led_pattern |
RGB-LED-Animation | 256 Bytes |
| 1 | idle_hook |
Tickless-Idle-Eintritt | 128 Bytes |
| 0 | ota_staging |
OTA-Empfang + Schreiben | 1.024 Bytes |
Der ISR wird über xQueueSendFromISR + taskNOTIFY an einen hochprioren Task zurückgestellt, sodass der Audiostream niemals durch langen ISR-Code blockiert wird. Priorität 7 bedeutet, dass der Audio-DMA-Handler 200 µs vor dem OID-Decoder läuft – wichtig, da der Audio-DMA-Puffer innerhalb von 2 ms neu gefüllt sein muss, um eine Aussetzer zu vermeiden.
Käufer-Tipp: Wenn Sie ein Datenblatt zu einem Lesestift sehen, das nur 4 oder weniger Prioritäten auflistet: Finger weg. Echte Lesestifte benötigen mindestens 6.
4. Zephyr-Scheduler auf K7 und KF1
Zephyr verwendet einen Multi-Queue-Bitmap-Scheduler mit optionaler Deadline-Scheduling-Funktion. Tasks haben eine Priorität von -16 (niedrigste) bis 15 (höchste). Auf K7-Firmware 1.9.5 und KF1-Firmware 1.9.5 gilt unsere Prioritätentabelle:
| Priorität | Thread | Zweck |
|---|---|---|
| 15 | audio_stream_isr |
Audio-DMA abgeschlossen |
| 14 | oid_decode |
OID-Decode |
| 13 | audio_player |
MP3-Decode |
| 12 | ble_gatt |
BLE-GATT-Server |
| 11 | ble_adv |
BLE-Advertising |
| 10 | button_handler |
Tasten + Long-Press |
| 9 | battery_monitor |
Batterieabfrage |
| 8 | led_pattern |
LED-Animation |
| 7 | sensor_calib |
Sensor-Kalibrierungs-Drift |
| 6 | fs_commit |
Dateisystem-Commit |
| 5 | ota_staging |
OTA-Staging |
| 4 | shell |
Debug-Shell |
| 3 | sys_workq |
System-Work-Queue |
| 2 | idle |
Tickless Idle |
Der Tickless Idle von Zephyr auf K7 senkt die Stromaufnahme im Leerlauf von 4,8 mA auf 1,6 mA. Das sind die 3,2 mA Differenz, die dem K7 seine 6,0 h Akkulaufzeit mit der 800-mAh-Li-Ion-14500-Zelle verleiht.
K7 verfügt außerdem über einen Thread im Deadline-Modus (audio_player_deadline), der zum Einsatz kommt, wenn der OEM Audio-Playrouting im Sub-Millisekunden-Bereich benötigt. Wir aktivieren dies über das Firmware-Build-Flag CONFIG_DEADLINE=y, das auf KF1 standardmäßig gesetzt ist und auf K7 deaktiviert wird, um 32 KB Flash zu sparen.
5. AliOS-Things-Scheduler auf Q6 und Q8
AliOS Things 3.3 verwendet einen prioritätsbasierten, präemptiven Scheduler, der FreeRTOS sehr ähnlich ist. Er unterstützt bis zu 32 Prioritäten und verfügt über einen integrierten krhino-Kernel, den wir seit 2022 einsetzen. Die Prioritätstabelle für Q6/Q8 sieht wie folgt aus:
| Priorität | Task | Zweck |
|---|---|---|
| 7 (höchste) | audio_isr |
Audio-DMA abgeschlossen |
| 6 | oid_decode |
OID-Decode |
| 5 | audio_player |
MP3-Decode + DAC |
| 4 | btn_event |
Button-Entprellung |
| 3 | ble_gatt |
BLE GATT |
| 3 | ble_adv |
BLE-Advertising |
| 2 | pwr_mgr |
Energieverwaltung |
| 2 | batt_poll |
Akkuzustand |
| 1 | led_pat |
LED-Animation |
| 0 | ota_recv |
OTA-Empfänger |
Der AliOS-Scheduler hat einen etwas schnelleren Kontextwechsel als FreeRTOS auf Cortex-M4, da AliOS weniger Register im gesicherten Kontext verwendet. Unsere Messung ergibt 1,4 µs auf Q8 gegenüber 2,0 µs auf B2 – relevant für hochpriore Audio-Interrupts.
6. Linux-CFS-Scheduler auf F8, L5, T2, Y6
Linux verwendet den Completely Fair Scheduler (CFS) auf F8, L5, T2 und Y6. CFS kennt keine festen Prioritäten im RTOS-Sinn. Stattdessen bietet es:
- Nice-Werte von -20 (höchste) bis 19 (niedrigste).
- Echtzeitgruppe (
SCHED_RR,SCHED_FIFO) mit Prioritäten 1–99. - cgroups zur Gruppierung von Tasks.
- Deadline-Scheduling (
SCHED_DEADLINE) für harte Echtzeit-Tasks.
Auf der L5-Firmware 4.2.0 lautet unsere Prioritätstabelle:
| Linux-Priorität | Prozess | Beschreibung |
|---|---|---|
SCHED_FIFO 90 |
audio-daemon |
Audio-Mixer + DAC |
SCHED_FIFO 80 |
oid-decoder |
OID-Decode |
SCHED_FIFO 70 |
wifi-supervisor |
Wi-Fi-Roaming |
SCHED_FIFO 60 |
ble-gatt |
BLE GATT |
SCHED_RR 50 |
button-handler |
Taste + Long-Press |
SCHED_RR 40 |
battery-monitor |
Batteriemonitor |
nice -10 |
led-pattern |
LED-Animation |
nice -5 |
ui-thread |
UI-Render |
nice 0 |
asr-engine |
On-Device-ASR |
nice 5 |
logging-daemon |
Telemetrie |
nice 10 |
cache-cleanup |
Cache-Bereinigung |
nice 19 |
analytics |
Anonyme Analyse |
Der Audio-Daemon läuft auf SCHED_FIFO 90, um zu garantieren, dass er alles außer Kernel-Tasks verdrängt. Der OID-Decoder läuft auf SCHED_FIFO 80, damit er die ASR-Engine verdrängen kann. Die ASR-Engine läuft auf nice 0, da sie durchsatzorientiert und nicht latenzorientiert ist.
7. Prioritätsinversion: Die Falle
Prioritätsinversion tritt auf, wenn ein Task mit niedriger Priorität einen Mutex hält, den ein Task mit hoher Priorität benötigt. Der Task mit hoher Priorität wird blockiert und wartet auf den Task mit niedriger Priorität; währenddessen verdrängt ein Task mit mittlerer Priorität den Task mit niedriger Priorität. Der Task mit hoher Priorität bleibt blockiert, bis der Task mit mittlerer Priorität nachgibt.
Wir haben Prioritätsinversion im Feld einmal gesehen. Auf der Q8-Firmware 2.6.4 (dem Vorgänger von 3.3.1) wurde ein UI-Mutex von einem Logging-Task mit niedriger Priorität gehalten, während der Audio-Player ihn anzufordern versuchte; der Audio-Player stockte 12 ms lang – als Glitch hörbar.
Die Maßnahme, die wir auf jeder RTOS-SKU ausliefern, sind Prioritätsvererbungs-Mutex (auch Prioritäts-Ceiling-Mutex genannt):
- FreeRTOS verwendet
xSemaphoreCreateMutex()(das in neueren Versionen Prioritätsvererbung unterstützt). - Zephyr verfügt über
k_mutexmit standardmäßig aktivierter Prioritätsvererbung. - AliOS verwendet
krhino_mutex_create()mit dem FlagRHINO_MUTEX_PRIO_INHERIT. - Linux verwendet
pthread_mutexattr_setprotocol(... PTHREAD_PRIO_INHERIT ...).
Der L5-Audiodämon nutzt POSIX-Mutexes mit Prioritätsvererbung rund um das ALSA-PCM-Handle. Der T2-Videodecoder verfährt ebenso rund um das VPU-Handle.
8. Tickless Idle und Energieeinsparung
Die größte Energieeinsparung, die ein Scheduler auf einer RTOS-SKU liefern kann, ist Tickless Idle. Wenn nichts zu tun ist, deaktiviert der Scheduler den SysTick-Interrupt und die CPU wechselt in einen energiesparenden Wartezustand. Wenn das nächste Ereignis eintritt (ein Timer-Ablauf, ein Interrupt), weckt der Scheduler die CPU und programmiert den SysTick neu.
Tickless-Idle-Einsparungen in unserem Line-up (gemessen bei 25 °C, 3,7 V):
| SKU | Ruhestrom mit Tick | Ruhestrom tickless | Einsparung |
|---|---|---|---|
| B1 | 6,4 mA | 2,1 mA | 67 % |
| B2 | 5,9 mA | 2,1 mA | 64 % |
| K7 | 8,2 mA | 2,8 mA | 66 % |
| KF1 | 8,0 mA | 2,7 mA | 66 % |
| P2 | 4,2 mA | 1,4 mA | 67 % |
| Q6 | 6,6 mA | 2,4 mA | 64 % |
| Q8 | 6,4 mA | 2,4 mA | 62 % |
Linux-SKUs bieten kein Tickless Idle im RTOS-Sinn. Sie verfügen über cpuidle-Governor, die einen tieferen C-State wählen, wenn die Runqueue leer ist. F8 erreicht 14 mA im Leerlauf, L5 erreicht 18 mA, T2 erreicht 22 mA – immer noch 4–10× mehr als RTOS-Idle.
9. Lastabwurf und Überlastverhalten
Wenn die CPU eines Lesestarger überlastet ist – beispielsweise wenn der OID-Decoder 5 Seiten in 200 ms auslöst, weil der Nutzer durch ein Buch blättert – muss der Scheduler Arbeit verwerfen, um den Audiostream gesund zu halten. Unsere Lastabwurf-Strategie:
| Bedingung | Aktion |
|---|---|
| OID-Queue-Tiefe > 8 | Älteste verwerfen, Zähler protokollieren |
| Audio-Puffer-Underrun droht | OID-Task-Priorität kurzzeitig erhöhen |
| BLE-Stack-Queue-Tiefe > 16 | Trennen, neu verbinden |
| Wi-Fi-TX-Queue-Tiefe > 32 | Niederpriore Frames verwerfen |
| Linux loadavg > 4 | ASR-Engine auf 50 % CPU drosseln |
| Akku < 8 % | WLAN deaktivieren, LED dimmen, auf Mono-Audio umschalten |
Der Trick mit der „kurzzeitig erhöhten OID-Task-Priorität" ist ein dynamischer Prioritätsboost, den wir im Audio-Player-Task implementieren. Wenn der Audio-Puffer zu mehr als 75 % leer ist, sendet der Audio-Task eine Benachrichtigung an den OID-Decoder, damit dieser seine Priorität für die nächsten 50 ms anhebt. Das verschafft im Worst Case 20–40 ms zusätzlichen Spielraum auf dem B2.
10. Scheduler-Inspektion und Debugging
Jede SKU stellt eine Scheduler-Inspektions-API über USB oder BLE bereit. Die API liefert:
typedef struct {
uint32_t task_id;
char name[16];
uint8_t priority;
uint32_t stack_high_watermark;
uint32_t runtime_us;
uint32_t cpu_pct;
} task_stats_t;
OEMS können diese API während der Werks-QA abfragen, um einen „Scheduler-Fingerabdruck" zu erfassen – einen Snapshot der CPU-Auslastung pro Task. Wir haben seit 2023 über 47.000 solcher Fingerabdrücke ausgeliefert. Der Fingerabdruck ist äußerst wertvoll für die Diagnose von Feldausfällen: Ein Stift, bei dem battery_monitor 28 % CPU statt der erwarteten 4 % verbraucht, hat irgendwo einen Polling-Bug.
Für Linux-SKUs stehen dieselben Daten über /proc/<pid>/stat und top -H zur Verfügung. Wir bündeln ein scheduler-diag-Tool in der L5-Firmware 4.2.0, das den Fingerabdruck in einer JSON-Datei erfasst und bei Bedarf per WLAN zurücksendet.
11. Den richtigen Scheduler für Ihren Lesestift auswählen
Wenn Sie als OEM einen Scheduler für eine neue Lesestift-SKU auswählen, gilt folgende Faustregel:
| Kaufszenario | Empfohlener Scheduler | Vorgeschlagene SKU |
|---|---|---|
| Unter 5 $, einsprachig, nur Antippen | FreeRTOS tick-basiert | P2, B1 |
| Mittelklasse, zweisprachig, BLE | Zephyr mit Tickless | K7, KF1 |
| Mittelklasse mit benutzerdefinierter OID, BLE | AliOS Things | Q6, Q8 |
| WLAN + ASR + 720p-Video | Linux CFS mit SCHED_FIFO für Audio |
L5 |
| Defence- oder Premium-Gehäuse, hartes Echtzeitverhalten | Linux mit PREEMPT_RT-Patch | T2 |
| Premium K–12 mit Content-Streaming | Linux CFS mit Deadline-Scheduling | Y6 |
Käuferhinweis: Fragen Sie den OEM immer nach zwei Scheduler-Kennzahlen: Worst-Case-Preemption-Latenz von einer niedrigprioritären zu einer hochprioritären Task (µs) sowie den Ruhestrom bei aktiviertem Tickless-Modus (mA). Alles über 10 µs oder 4 mA im Ruhezustand ist für K–3-Lesestift-Anwendungen ein Warnsignal.