Multi-task scheduler and priority design across 11 reading pen SKUs, with latency tables, priority-inversion mitigation and pre-emption diagrams for OEMs.

Posted At: Jul 24, 2024 - 71 Views

Reading Pen Multi-Task Scheduler and Priority: FreeRTOS, Zephyr, AliOS and Linux CFS Compared

Audience: This guide is written for firmware engineers, RTOS architects and OEM program managers who need to understand how a reading pen schedules its audio player, OID decoder, button handler, BLE stack and OTA agent — and how that scheduling affects user-visible latency, battery life and field-service tickets. It assumes you know what a thread or task is, but you do not need to be an expert in any one RTOS.

Every reading pen runs more than one task at a time, even the smallest P2 with its 64 KB of Flash. On a typical T2 you might find 24–38 active tasks: an OID decoder task, an audio mixer task, a UI task, a Wi-Fi supervisor task, a BLE GATT server task, an OTA staging task, a battery gauge task, a power-management task and several sensor-poll tasks. The scheduler decides which one runs on the CPU each microsecond.

This article walks you through the four scheduler families used across our 11 SKUs (B1, B2, F8, K7, KF1, L5, P2, Q6, Q8, T2, Y6) — FreeRTOS, Zephyr, AliOS Things and Linux CFS — and shows the priority assignment, pre-emption latency, priority-inversion mitigation and load-shedding policy we use on each.

1. What a Reading Pen Scheduler Actually Does

A scheduler has three jobs:

  1. Choose which task runs on the CPU at each instant.
  2. Enforce priority — higher-priority tasks pre-empt lower-priority ones.
  3. Bound the latency between an event (interrupt, signal) and the response task waking up.

On a reading pen, the third job is the most important. A user taps the pen on an OID dot, the OID sensor fires a DMA-complete interrupt, the OID decoder task must wake up within 1–4 ms to decode the page, and the audio player task must start streaming within 50 ms. If the scheduler cannot bound that latency, the user hears a lag.

2. The Four Scheduler Families We Ship

Scheduler OS SKUs that use it Pre-emption model Tick rate
FreeRTOS tick-based FreeRTOS 10.4 B1, B2, P2 Priority-based pre-emptive 1 kHz
Zephyr tickless Zephyr 3.4 LTS K7, KF1 Priority + deadline 100 Hz baseline, tickless idle
AliOS Things AliOS Things 3.3 Q6, Q8 Priority-based pre-emptive 1 kHz
Linux CFS Linux 5.10 / 5.15 F8, L5, T2, Y6 Completely Fair Scheduler with RT-group 250 Hz / 1 kHz

The two-thirds RTOS, one-third Linux split mirrors the broader reading pen market. The scheduler choice is downstream of the OS choice discussed in TC-097, but the priority policy is downstream of the SKU's feature set.

3. FreeRTOS Scheduler on B1, B2, P2

FreeRTOS uses a fixed-priority pre-emptive scheduler with optional time slicing. Each task has a priority from 0 (lowest) to configMAX_PRIORITIES - 1 (highest, typically 32 or 56). The scheduler always runs the highest-priority ready task; ties are broken by time slice.

On B2 firmware 2.4.7, our priority table is:

Priority Task Purpose Stack
7 (highest) audio_dma_isr DMA-complete handler (ISR-deferred) 256 bytes
6 oid_decode OID decode page 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 Battery gauge poll 512 bytes
2 led_pattern RGB LED animation 256 bytes
1 idle_hook Tickless idle entry 128 bytes
0 ota_staging OTA receive + write 1,024 bytes

The ISR is deferred to a high-priority task via xQueueSendFromISR + taskNOTIFY so the audio stream never blocks on long ISR code. Priority 7 means the audio DMA handler runs before the OID decoder for the next 200 µs — important because the audio DMA buffer must be refilled within 2 ms to avoid a glitch.

Buyer tip: If you see a reading pen datasheet that lists 4 or fewer priorities, run. Real reading pens need at least 6.

4. Zephyr Scheduler on K7 and KF1

Zephyr uses a multi-queue bitmap scheduler with optional deadline scheduling. Tasks have a priority from -16 (lowest) to 15 (highest). On K7 firmware 1.9.5 and KF1 firmware 1.9.5, our priority table is:

Priority Thread Purpose
15 audio_stream_isr Audio DMA done
14 oid_decode OID decode
13 audio_player MP3 decode
12 ble_gatt BLE GATT server
11 ble_adv BLE advertising
10 button_handler Buttons + long-press
9 battery_monitor Battery poll
8 led_pattern LED animation
7 sensor_calib Sensor calibration drift
6 fs_commit Filesystem commit
5 ota_staging OTA staging
4 shell Debug shell
3 sys_workq System work queue
2 idle Tickless idle

Zephyr's tickless idle on K7 drops current draw from 4.8 mA to 1.6 mA in idle. That is the 3.2 mA delta that gives K7 its 6.0 h battery life on the 800 mAh Li-ion 14500 cell.

K7 also has a deadline-mode thread (audio_player_deadline) used when the OEM wants sub-millisecond audio scheduling. We enable this in the firmware build flag CONFIG_DEADLINE=y, which is on by default on KF1 and off on K7 to save 32 KB of Flash.

5. AliOS Things Scheduler on Q6 and Q8

AliOS Things 3.3 uses a priority-based pre-emptive scheduler very similar to FreeRTOS. It supports up to 32 priorities, and has a built-in krhino kernel that we have been using since 2022. The Q6 / Q8 priority table looks like:

Priority Task Purpose
7 (highest) audio_isr Audio DMA done
6 oid_decode OID decode
5 audio_player MP3 decode + DAC
4 btn_event Button debounce
3 ble_gatt BLE GATT
3 ble_adv BLE advertising
2 pwr_mgr Power manager
2 batt_poll Battery gauge
1 led_pat LED animation
0 ota_recv OTA receiver

The AliOS scheduler has a slightly faster context switch than FreeRTOS on Cortex-M4 because AliOS uses fewer registers in the saved context. Our measurement is 1.4 µs on Q8 vs 2.0 µs on B2 — meaningful for high-priority audio interrupts.

6. Linux CFS Scheduler on F8, L5, T2, Y6

Linux uses the Completely Fair Scheduler (CFS) on F8, L5, T2 and Y6. CFS does not have fixed priorities in the RTOS sense. Instead it has:

  • Nice values from -20 (highest) to 19 (lowest).
  • Realtime group (SCHED_RR, SCHED_FIFO) with priorities 1–99.
  • cgroups for grouping tasks.
  • Deadline scheduling (SCHED_DEADLINE) for hard-realime tasks.

On L5 firmware 4.2.0, our priority table is:

Linux priority Process Description
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 Button + long-press
SCHED_RR 40 battery-monitor Battery gauge
nice -10 led-pattern LED animation
nice -5 ui-thread UI render
nice 0 asr-engine On-device ASR
nice 5 logging-daemon Telemetry
nice 10 cache-cleanup Cache cleanup
nice 19 analytics Anonymous analytics

The audio daemon runs at SCHED_FIFO 90 to guarantee it pre-empts everything except kernel tasks. The OID decoder runs at SCHED_FIFO 80 so it can pre-empt the ASR engine. The ASR engine runs at nice 0 because it is throughput-oriented, not latency-oriented.

Pre-emption latency, lower-priority → higher-priority (µs)

7. Priority Inversion: The Trap

Priority inversion is what happens when a low-priority task holds a mutex that a high-priority task needs. The high-priority task blocks waiting for the low-priority task; meanwhile, a medium-priority task pre-empts the low-priority task. The high-priority task is stuck until the medium-priority task yields.

We have seen priority inversion in the field once. On Q8 firmware 2.6.4 (the predecessor of 3.3.1), a UI mutex was held by a low-priority logging task while the audio player tried to acquire it; the audio player stalled for 12 ms — audible as a glitch.

The mitigation we ship on every RTOS SKU is priority inheritance mutexes (also called priority-ceiling mutexes):

  • FreeRTOS uses xSemaphoreCreateMutex() (which supports priority inheritance in recent versions).
  • Zephyr has k_mutex with priority inheritance enabled by default.
  • AliOS has krhino_mutex_create() with the RHINO_MUTEX_PRIO_INHERIT flag.
  • Linux uses pthread_mutexattr_setprotocol(... PTHREAD_PRIO_INHERIT ...).

The L5 audio daemon uses priority-inherited POSIX mutexes around the ALSA PCM handle. The T2 video decoder does the same around the VPU handle.

8. Tickless Idle and Power Savings

The biggest power saving a scheduler can deliver on an RTOS SKU is tickless idle. When there is nothing to do, the scheduler disables the SysTick interrupt and the CPU enters a low-power wait. When the next event fires (a timer expiry, an interrupt), the scheduler wakes the CPU and re-programs the SysTick.

Tickless idle savings on our line-up (measured at 25 °C, 3.7 V):

SKU Idle current with tick Idle current tickless Saving
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 do not have tickless idle in the RTOS sense. They have cpuidle governors that pick a deeper C-state when the runqueue is empty. F8 reaches 14 mA idle, L5 reaches 18 mA, T2 reaches 22 mA — still 4–10× more than RTOS idle.

9. Load Shedding and Overload Behaviour

When a reading pen's CPU is overloaded — for example, the OID decoder fires 5 pages in 200 ms because the user is paging through a book — the scheduler must drop work to keep the audio stream healthy. Our load-shedding policy:

Condition Action
OID queue depth > 8 Drop oldest, log a counter
Audio buffer underrun imminent Increase OID task priority briefly
BLE stack queue depth > 16 Disconnect, reconnect
Wi-Fi TX queue depth > 32 Drop low-priority frames
Linux loadavg > 4 Throttle ASR engine to 50 % CPU
Battery < 8 % Disable Wi-Fi, dim LED, switch to mono audio

The "increase OID task priority briefly" trick is a dynamic priority boost that we implement in the audio player task. When the audio buffer is more than 75 % empty, the audio task sends a notification to the OID decoder to bump its priority for the next 50 ms. This buys 20–40 ms of headroom on B2 in worst-case scenarios.

10. Scheduler Inspection and Debugging

Every SKU exposes a scheduler inspection API over USB or BLE. The API returns:

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 can poll this API during factory QA to capture a "scheduler fingerprint" — a snapshot of CPU usage by task. We have shipped over 47,000 such fingerprints since 2023. The fingerprint is invaluable for diagnosing field issues: a pen that shows battery_monitor consuming 28 % CPU instead of the expected 4 % has a polling bug somewhere.

For Linux SKUs, the same data is available through /proc/<pid>/stat and top -H. We bundle a scheduler-diag tool in the L5 firmware 4.2.0 that captures the fingerprint in a JSON file and ships it back over Wi-Fi on demand.

11. How to Choose the Right Scheduler for Your Reading Pen

If you are an OEM picking a scheduler for a new reading pen SKU, here is the rule of thumb:

Buying scenario Recommended scheduler Suggested SKU
Sub-$5, single-language, tap-only FreeRTOS tick-based P2, B1
Mid-range bilingual, BLE Zephyr with tickless K7, KF1
Mid-range with custom OID, BLE AliOS Things Q6, Q8
Wi-Fi + ASR + 720p video Linux CFS with SCHED_FIFO for audio L5
Defence or premium build, hard real-time Linux with PREEMPT_RT patch T2
Premium K–12 with content streaming Linux CFS with deadline scheduling Y6

Buyer tip: Always ask the OEM for two scheduler metrics: worst-case pre-emption latency from a low-priority to a high-priority task (µs), and the idle current with tickless mode enabled (mA). Anything above 10 µs or 4 mA idle is a red flag for K–3 reading pen use cases.

Related Reading

Request a Quote — Get a Fast Factory Response

Frequently Asked Questions

Which scheduler does the B2 reading pen use?
B2 firmware 2.4.7 uses the FreeRTOS 10.4 tick-based pre-emptive scheduler with priorities 0–7. The audio DMA ISR-deferred task runs at priority 7, the OID decoder at priority 6, and the audio player at priority 5. Idle current is 2.1 mA at 3.7 V with tickless idle enabled.
What is the pre-emption latency on an RTOS reading pen?
On FreeRTOS B2 we measure 2.0 µs; on Zephyr K7 we measure 1.8 µs; on AliOS Q8 we measure 1.4 µs; on Zephyr KF1 with deadline scheduling enabled we measure 0.9 µs. Linux CFS SKUs (F8, L5, T2, Y6) measure 18–24 µs because of the scheduler tick and runqueue logic.
How do you prevent priority inversion on a reading pen?
We use priority-inheritance mutexes on every RTOS SKU: xSemaphoreCreateMutex on FreeRTOS, k_mutex on Zephyr, krhino_mutex_create with the priority-inherit flag on AliOS, and pthread_mutexattr_setprotocol with PTHREAD_PRIO_INHERIT on Linux. We have had one priority-inversion incident in the field since 2023, fixed in Q8 firmware 3.3.1.
Can a reading pen run both RTOS and Linux tasks at the same time?
No. A reading pen runs one OS at a time. The Linux SKUs (F8, L5, T2, Y6) can host an RTOS as a guest under Jailhouse or as a Xenomai thread, but we do not ship this configuration in production today. The cost in terms of validation and certification is high and the benefit is small.
What happens if the scheduler is overloaded?
Our load-shedding policy drops the oldest OID page when the queue depth exceeds 8, increases the OID task priority briefly when the audio buffer is more than 75 % empty, disconnects BLE if the stack queue exceeds 16 frames, drops Wi-Fi low-priority frames above 32 frames, and throttles the ASR engine to 50 % CPU if the Linux loadavg exceeds 4.0.
Your Cart
Your experience on this site will be improved by allowing cookies Cookie Policy
Chat on WhatsApp