Public țintă: Acest ghid este scris pentru inginerii de firmware, arhitecții RTOS și managerii de program OEM care trebuie să înțeleagă cum un stilou de citire își programează playerul audio, decodorul OID, gestionarul de butoane, stiva BLE și agentul OTA — și cum această planificare afectează latența vizibilă pentru utilizator, autonomia bateriei și tichetele de service. Se presupune că știți ce este un fir de execuție sau un task, dar nu este necesar să fiți expert într-un anumit RTOS.
Fiecare stilou de citire rulează mai multe taskuri în același timp, chiar și cel mai mic P2 cu cei 64 KB de Flash ai săi. Pe un T2 tipic s-ar putea găsi 24–38 de taskuri active: un task decodor OID, un task mixer audio, un task UI, un task supervizor Wi-Fi, un task server BLE GATT, un task de staging OTA, un task de monitorizare a bateriei, un task de gestionare a energiei și mai multe taskuri de interogare a senzorilor. Planificatorul decide care dintre ele rulează pe CPU la fiecare microsecundă.
Acest articol vă ghidează prin cele patru familii de planificatoare utilizate în cele 11 SKU-uri ale noastre (B1, B2, F8, K7, KF1, L5, P2, Q6, Q8, T2, Y6) — FreeRTOS, Zephyr, AliOS Things și Linux CFS — și arată atribuirea priorităților, latența de preempțiune, mitigarea inversiunii priorităților și politica de reducere a sarcinii pe care le folosim pentru fiecare.
1. Ce face de fapt un planificator al stiloului de citire
Un planificator are trei sarcini:
- Alege ce task rulează pe CPU în fiecare moment.
- Impune prioritățile — taskurile cu prioritate mai mare preiau controlul înaintea celor cu prioritate mai mică.
- Limitează latența dintre un eveniment (întrerupere, semnal) și trezirea taskului de răspuns.
La un stilou de citire, a treia sarcină este cea mai importantă. Un utilizator atinge cu stiloul un punct OID, senzorul OID declanșează o întrerupere DMA-complete, taskul decodor OID trebuie să se trezească în 1–4 ms pentru a decoda pagina, iar taskul player audio trebuie să înceapă streamingul în 50 ms. Dacă planificatorul nu poate limita această latență, utilizatorul aude o întârziere.
2. Cele patru familii de planificatoare pe care le livrăm
| Planificator | SO | SKU-uri care îl utilizează | Model de preempțiune | Frecvența tick |
|---|---|---|---|---|
| FreeRTOS bazat pe tick | FreeRTOS 10.4 | B1, B2, P2 | Preempțiune bazată pe priorități | 1 kHz |
| Zephyr fără tick | Zephyr 3.4 LTS | K7, KF1 | Prioritate + termen | 100 Hz linie de bază, inactiv fără tick |
| AliOS Things | AliOS Things 3.3 | Q6, Q8 | Preempțiune bazată pe priorități | 1 kHz |
| Linux CFS | Linux 5.10 / 5.15 | F8, L5, T2, Y6 | Completely Fair Scheduler cu grup RT | 250 Hz / 1 kHz |
Împărțirea două treimi RTOS, o treime Linux oglindește piața mai largă a stilourilor de citire. Alegerea planificatorului este determinată de alegerea SO-ului discutată în TC-097, dar politica de priorități este determinată de setul de funcționalități al SKU-ului.
3. Planificator FreeRTOS pe B1, B2, P2
FreeRTOS folosește un planificator preempitiv cu priorități fixe, cu opțiune de diviziune a timpului. Fiecare task are o prioritate de la 0 (cea mai mică) la configMAX_PRIORITIES - 1 (cea mai mare, de obicei 32 sau 56). Planificatorul rulează întotdeauna taskul gata cu cea mai mare prioritate; egalitățile se rezolvă prin felie de timp.
Pe firmware-ul 2.4.7 al B2, tabelul nostru de priorități este:
| Prioritate | Task | Scop | Stivă |
|---|---|---|---|
| 7 (cea mai mare) | audio_dma_isr |
Handler DMA-complete (amânat din ISR) | 256 biți |
| 6 | oid_decode |
Decodare pagină OID | 1.024 biți |
| 5 | audio_player |
Decodare MP3 + alimentare DAC | 2.048 biți |
| 4 | button_handler |
Debounce + apăsare lungă | 512 octeți |
| 3 | battery_monitor |
Sondaj indicator baterie | 512 octeți |
| 2 | led_pattern |
Animație LED RGB | 256 octeți |
| 1 | idle_hook |
Intrare idle fără tick-uri | 128 octeți |
| 0 | ota_staging |
Recepție + scriere OTA | 1.024 octeți |
ISR-ul este amânat către un task de prioritate ridicată prin xQueueSendFromISR + taskNOTIFY astfel încât fluxul audio nu se blochează niciodată pe cod ISR lung. Prioritatea 7 înseamnă că handler-ul audio DMA rulează înaintea decodorului OID pentru următoarele 200 µs — important deoarece buffer-ul audio DMA trebuie reumplut în maximum 2 ms pentru a evita un glitch.
Sfat pentru cumpărător: Dacă vedeți o fișă tehnică pentru un stilou de citit care listează 4 sau mai puține priorități, fugiți. Stilourile de citit reale au nevoie de cel puțin 6.
4. Scheduler-ul Zephyr pe K7 și KF1
Zephyr folosește un scheduler bitmap multi-coadă, cu planificare opțională pe termen limită. Task-urile au o prioritate de la -16 (cea mai mică) la 15 (cea mai mare). Pe firmware-ul K7 1.9.5 și firmware-ul KF1 1.9.5, tabelul nostru de priorități este:
| Prioritate | Thread | Scop |
|---|---|---|
| 15 | audio_stream_isr |
Audio DMA finalizat |
| 14 | oid_decode |
Decodare OID |
| 13 | audio_player |
Decodare MP3 |
| 12 | ble_gatt |
Server BLE GATT |
| 11 | ble_adv |
Publicitate BLE |
| 10 | button_handler |
Butoane + apăsare lungă |
| 9 | battery_monitor |
Sondaj baterie |
| 8 | led_pattern |
Animație LED |
| 7 | sensor_calib |
Derivă calibrare senzor |
| 6 | fs_commit |
Commit sistem de fișiere |
| 5 | ota_staging |
Staging OTA |
| 4 | shell |
Shell de depanare |
| 3 | sys_workq |
Coadă de lucru sistem |
| 2 | idle |
Idle fără tick-uri |
Modul idle fără tick-uri din Zephyr pe K7 reduce consumul de la 4,8 mA la 1,6 mA în stare inactivă. Aceasta este diferența de 3,2 mA care oferă modelului K7 autonomia de 6,0 h cu bateria Li-ion 14500 de 800 mAh.
K7 dispune și de un fir de execuție în modul deadline (audio_player_deadline) folosit atunci când OEM-ul are nevoie de planificare audio sub-milisecundă. Activăm acest lucru prin flag-ul de compilare firmware CONFIG_DEADLINE=y, care este activ în mod implicit pe KF1 și dezactivat pe K7 pentru a economisi 32 KB de Flash.
5. Planificatorul AliOS Things pe Q6 și Q8
AliOS Things 3.3 folosește un planificator preemptiv bazat pe priorități, foarte asemănător cu FreeRTOS. Acceptă până la 32 de priorități și are un nucleu krhino integrat pe care îl folosim din 2022. Tabela de priorități pentru Q6 \/ Q8 arată astfel:
| Prioritate | Sarcină | Scop |
|---|---|---|
| 7 (cea mai mare) | audio_isr |
Transfer DMA audio încheiat |
| 6 | oid_decode |
Decodare OID |
| 5 | audio_player |
Decodare MP3 + DAC |
| 4 | btn_event |
Debounce buton |
| 3 | ble_gatt |
BLE GATT |
| 3 | ble_adv |
Publicitate BLE |
| 2 | pwr_mgr |
Manager alimentare |
| 2 | batt_poll |
Indicator baterie |
| 1 | led_pat |
Animație LED |
| 0 | ota_recv |
Receptor OTA |
Planificatorul AliOS are o comutare de context ușor mai rapidă decât FreeRTOS pe Cortex-M4 deoarece AliOS folosește mai puține registre în contextul salvat. Măsurătoarea noastră este de 1,4 µs pe Q8 față de 2,0 µs pe B2 — semnificativ pentru întreruperile audio de prioritate mare.
6. Planificatorul Linux CFS pe F8, L5, T2, Y6
Linux folosește planificatorul Completely Fair Scheduler (CFS) pe F8, L5, T2 și Y6. CFS nu are priorități fixe în sensul RTOS. În schimb, dispune de:
- Valori nice de la -20 (cea mai mare) la 19 (cea mai mică).
- Grup de timp real (
SCHED_RR,SCHED_FIFO) cu priorități 1–99. - cgroups pentru gruparea sarcinilor.
- Planificare deadline (
SCHED_DEADLINE) pentru sarcini hard-realime.
Pe firmware-ul L5 versiunea 4.2.0, tabelul nostru de priorități este:
| Prioritate Linux | Proces | Descriere |
|---|---|---|
SCHED_FIFO 90 |
audio-daemon |
Mixer audio + DAC |
SCHED_FIFO 80 |
oid-decoder |
Decodare OID |
SCHED_FIFO 70 |
wifi-supervisor |
Roaming Wi-Fi |
SCHED_FIFO 60 |
ble-gatt |
BLE GATT |
SCHED_RR 50 |
button-handler |
Buton + apăsare lungă |
SCHED_RR 40 |
battery-monitor |
Indicator baterie |
nice -10 |
led-pattern |
Animație LED |
nice -5 |
ui-thread |
Randare UI |
nice 0 |
asr-engine |
ASR pe dispozitiv |
nice 5 |
logging-daemon |
Telemetrie |
nice 10 |
cache-cleanup |
Curățare cache |
nice 19 |
analytics |
Analiză anonimă |
Daemon-ul audio rulează la SCHED_FIFO 90 pentru a garanta faptul că preia controlul în fața tuturor sarcinilor, cu excepția celor din kernel. Decodorul OID rulează la SCHED_FIFO 80 astfel încât poate prelua controlul de la motorul ASR. Motorul ASR rulează la nice 0 deoarece este orientat pe debit, nu pe latență.
7. Inversiunea priorității: capcana
Inversiunea priorității este ceea ce se întâmplă atunci când o sarcină cu prioritate scăzută deține un mutex de care are nevoie o sarcină cu prioritate ridicată. Sarcina cu prioritate ridicată se blochează așteptând sarcina cu prioritate scăzută; între timp, o sarcină cu prioritate medie preia controlul de la sarcina cu prioritate scăzută. Sarcina cu prioritate ridicată rămâne blocată până când sarcina cu prioritate medie cedează.
Am întâlnit inversiunea priorității în teren o singură dată. Pe firmware-ul Q8 versiunea 2.6.4 (predecesorul versiunii 3.3.1), un mutex al UI era deținut de o sarcină de logging cu prioritate scăzută în timp ce playerul audio încerca să-l achiziționeze; playerul audio s-a blocat timp de 12 ms — perceptibil ca un glitch.
Măsura de mitigare pe care o livrăm pe fiecare SKU RTOS este mutex-uri cu moștenire de prioritate (numite și mutex-uri cu plafon de prioritate):
- FreeRTOS utilizează
xSemaphoreCreateMutex()(care acceptă moștenirea de prioritate în versiunile recente). - Zephyr dispune de
k_mutexcu moștenire de prioritate activată implicit. - AliOS dispune de
krhino_mutex_create()cu fanionulRHINO_MUTEX_PRIO_INHERIT. - Linux utilizează
pthread_mutexattr_setprotocol(... PTHREAD_PRIO_INHERIT ...).
Daemonul audio L5 folosește mutexuri POSIX cu moștenire de prioritate în jurul handle-ului ALSA PCM. Decoderul video T2 procedează la fel în jurul handle-ului VPU.
8. Tickless Idle și Economie de Energie
Cea mai mare economie de energie pe care o poate oferi un planificator pe un SKU RTOS este tickless idle. Când nu există nimic de făcut, planificatorul dezactivează întreruperea SysTick, iar CPU-ul intră într-o așteptare de consum redus. Când survine următorul eveniment (expirarea unui timer, o întrerupere), planificatorul trezește CPU-ul și reprogramează SysTick.
Economiile tickless idle pe gama noastră (măsurate la 25 °C, 3,7 V):
| SKU | Curent inactiv cu tick | Curent inactiv tickless | Economie |
|---|---|---|---|
| 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 % |
SKU-urile Linux nu au tickless idle în sensul RTOS. Acestea dispun de guvernatori cpuidle care selectează un C-state mai profund când runqueue-ul este gol. F8 atinge 14 mA în inactivitate, L5 atinge 18 mA, T2 atinge 22 mA — în continuare de 4–10× mai mult decât inactivitatea RTOS.
9. Eliminarea Sarcinii și Comportamentul la Supraîncărcare
Când CPU-ul unui pen reader este suprîncărcat — de exemplu, decoderul OID declanșează 5 pagini în 200 ms pentru că utilizatorul răsfoiește o carte — planificatorul trebuie să renunțe la lucrări pentru a menține fluxul audio sănătos. Politica noastră de eliminare a sarcinii:
| Condiție | Acțiune |
|---|---|
| Adâncime coadă OID > 8 | Elimină cel mai vechi, înregistrează un contor |
| Aproape de subalimentarea buffer-ului audio | Crește scurt prioritatea task-ului OID |
| Adâncime coadă stivă BLE > 16 | Deconectează, reconectează |
| Adâncime coadă TX Wi-Fi > 32 | Elimină cadrele cu prioritate scăzută |
| Linux loadavg > 4 | Limitare motor ASR la 50 % CPU |
| Baterie < 8 % | Dezactivare Wi-Fi, reducere luminozitate LED, comutare pe audio mono |
Trucul „creștere temporară a priorității sarcinii OID" este o creștere dinamică a priorității pe care o implementăm în sarcina playerului audio. Când bufferul audio este gol peste 75 %, sarcina audio trimite o notificare decoderului OID pentru a-i crește prioritatea pentru următoarele 50 ms. Acest lucru oferă 20–40 ms de rezervă la B2 în cele mai defavorabile scenarii.
10. Inspecția planificatorului și depanare
Fiecare SKU expune o API de inspecție a planificatorului prin USB sau BLE. API-ul returnează:
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;
OEM-urile pot interoga această API în timpul QA din fabrică pentru a captura o „amprentă a planificatorului" — o imagine a utilizării CPU pe sarcină. Am livrat peste 47.000 de astfel de amprente din 2023. Amprenta este neprețuită pentru diagnosticarea problemelor din teren: un stilou care indică battery_monitor consumând 28 % CPU în loc de 4 % așteptat are o eroare de polling undeva.
Pentru SKU-urile Linux, aceleași date sunt disponibile prin /proc/<pid>/stat și top -H. Includem o unealtă scheduler-diag în firmware-ul L5 4.2.0 care capturează amprenta într-un fișier JSON și o trimite prin Wi-Fi la cerere.
11. Cum să alegeți planificatorul potrivit pentru stiloul dumneavoastră de citit
Dacă sunteți un OEM care alege un planificator pentru un nou SKU de stilou de citit, iată regula empirică:
| Scenariu de achiziție | Planificator recomandat | SKU sugerat |
|---|---|---|
| Sub 5 $, o singură limbă, doar atingere | FreeRTOS bazat pe tick | P2, B1 |
| Gama medie bilingv, BLE | Zephyr cu tickless | K7, KF1 |
| Gama medie cu OID personalizat, BLE | AliOS Things | Q6, Q8 |
| Wi-Fi + ASR + video 720p | Linux CFS cu SCHED_FIFO pentru audio |
L5 |
| Apărare sau construcție premium, hard real-time | Linux cu patch PREEMPT_RT | T2 |
| Premium K–12 cu streaming de conținut | Linux CFS cu planificare deadline | Y6 |
Sfat pentru cumpărător: Cereți întotdeauna OEM-ului două metrici ale planificatorului: latența de preempțiune în cel mai defavorabil caz de la o sarcină cu prioritate scăzută la una cu prioritate ridicată (µs) și curentul în repaus cu modul tickless activat (mA). Orice valoare peste 10 µs sau 4 mA în repaus este un semnal de alarmă pentru cazurile de utilizare a stilourilor de citit K–3.
Lecturi conexe
- Procesul de fabricație OEM al stilourilor de citit
- Stilou de citit RTOS vs sistem de operare embedded Linux
- Arhitectura driverelor stiloului de citit HAL și BSP
- Latența întreruperilor și timp real la stiloul de citit
- Criptografia și TrustZone la stiloul de citit
- Fluxul de boot BIOS și boot de încredere la stiloul de citit