対象読者:本ガイドは、読書ペンがオーディオプレーヤー、OIDデコーダー、ボタンハンドラー、BLEスタック、OTAエージェントをどのようにスケジュールするか、そしてそのスケジューリングがユーザー体感の遅延、バッテリー駆動時間、フィールドサービス案件にどう影響するかを理解したい、ファームウェアエンジニア、RTOSアーキテクト、OEMプログラムマネージャーを対象としています。スレッドやタスクとは何かを知っていることを前提としていますが、いずれか一つのRTOSの専門家である必要はありません。
最小モデルのP2(Flash 64KB)ですら、すべての読書ペンは同時に複数のタスクを実行します。典型的なT2では、24〜38個のアクティブなタスクが稼働しています。OIDデコーダー、オーディオミキサー、UI、Wi-Fi監視、BLE GATTサーバー、OTAステージング、バッテリーゲージ、電源管理、そして複数のセンサーポーリングタスクです。スケジューラが毎マイクロ秒ごとにCPUで実行するタスクを決定します。
本記事では、当社の11モデル(B1、B2、F8、K7、KF1、L5、P2、Q6、Q8、T2、Y6)で採用されている4つのスケジューラファミリー(FreeRTOS、Zephyr、AliOS Things、Linux CFS)を順に解説し、それぞれで使用している優先度割り当て、プリエンプション遅延、優先度逆転の緩和策、ロードシェディングポリシーを示します。
1. 読書ペンのスケジューラが実際に行うこと
スケジューラには3つの役割があります:
- 各瞬間でCPU上で実行するタスクを選択する。
- 優先度を適用する(高優先度タスクが低優先度タスクをプリエンプト)。
- イベント(割り込み、信号)と応答タスクの起床との間の遅延を上限内に収める。
読書ペンにおいて、3番目の役割が最も重要です。ユーザーがOIDドットにペンをタッチすると、OIDセンサーがDMA完了割り込みを発火し、OIDデコーダータスクは1〜4ms以内に起床してページをデコードし、オーディオプレーヤータスクは50ms以内にストリーミングを開始しなければなりません。スケジューラがこの遅延を上限内に収められないと、ユーザーは遅延を感じることになります。
2. 当社が採用している4つのスケジューラファミリー
| スケジューラ | OS | 対象SKU | プリエンプションモデル | ティックレート |
|---|---|---|---|---|
| FreeRTOS ティックベース | FreeRTOS 10.4 | B1、B2、P2 | 優先度ベースプリエンプティブ | 1 kHz |
| Zephyr ティックスレス | Zephyr 3.4 LTS | K7、KF1 | 優先度 + デッドライン | 100 Hzベース、ticklessアイドル |
| AliOS Things | AliOS Things 3.3 | Q6、Q8 | 優先度ベースプリエンプティブ | 1 kHz |
| Linux CFS | Linux 5.10 / 5.15 | F8、L5、T2、Y6 | 完全公平スケジューラ + RTグループ | 250 Hz / 1 kHz |
およそ3分の2がRTOS、3分の1がLinuxという構成は、読書ペン市場全体の傾向を反映しています。スケジューラの選定はTC-097で論じるOS選定の結果として決まりますが、優先度ポリシーはモデル機能の構成によって決まります。
3. FreeRTOSスケジューラ(B1、B2、P2)
FreeRTOSは、固定優先度プリエンプティブスケジューラを、オプションでタイムスライス付きで採用します。各タスクには0(最低)から configMAX_PRIORITIES - 1 (最高、通常32または56)までの優先度があります。スケジューラは常に最高優先度のレディータスクを実行し、同点の場合はタイムスライスで解決します。
B2ファームウェア2.4.7における当社の優先度テーブルは次のとおりです:
| 優先度 | タスク | 用途 | スタック |
|---|---|---|---|
| 7(最高) | audio_dma_isr |
DMA完了ハンドラ(ISR遅延処理) | 256バイト |
| 6 | oid_decode |
OIDページデコード | 1,024バイト |
| 5 | audio_player |
MP3デコード + DAC供給 | 2,048バイト |
| 4 | button_handler |
ボタンチャタリング防止+長押し | 512 バイト |
| 3 | battery_monitor |
バッテリー残量ポーリング | 512 バイト |
| 2 | led_pattern |
RGB LED アニメーション | 256 バイト |
| 1 | idle_hook |
Tickless アイドル突入 | 128 バイト |
| 0 | ota_staging |
OTA 受信+書き込み | 1,024 バイト |
ISRはxQueueSendFromISR + taskNOTIFY を介して高優先度タスクにディファードされるため、オーディオストリームが長い ISR コードでブロックされることはありません。優先度 7 は、次の 200 µs 間、OID デコーダよりも先にオーディオ DMA ハンドラが実行されることを意味します。これはグリッチを防ぐため、オーディオ DMA バッファを 2 ms 以内に再補充する必要があるため重要です。
ご購入者向けヒント: データシートに優先度が 4 以下しか記載されていない reading pen は避けてください。本物の reading pen には少なくとも 6 が必要です。
4. K7 および KF1 の Zephyr スケジューラ
Zephyr はマルチキューのビットマップスケジューラを使用し、必要に応じてデッドラインスケジューリングも利用します。タスクの優先度は -16(最低)から 15(最高)の範囲です。K7 ファームウェア 1.9.5 および KF1 ファームウェア 1.9.5 における優先度表は次のとおりです。
| 優先度 | スレッド | 目的 |
|---|---|---|
| 15 | audio_stream_isr |
オーディオ DMA 完了 |
| 14 | oid_decode |
OID デコード |
| 13 | audio_player |
MP3 デコード |
| 12 | ble_gatt |
BLE GATT サーバー |
| 11 | ble_adv |
BLE アドバタイジング |
| 10 | button_handler |
ボタン+長押し |
| 9 | battery_monitor |
バッテリー残量ポーリング |
| 8 | led_pattern |
LED アニメーション |
| 7 | sensor_calib |
センサーキャリブレーションのドリフト |
| 6 | fs_commit |
ファイルシステムコミット |
| 5 | ota_staging |
OTAステージング |
| 4 | shell |
デバッグシェル |
| 3 | sys_workq |
システムワークキュー |
| 2 | idle |
Tickless idle |
ZephyrのK7におけるTickless idleは、アイドル時の消費電流を4.8 mAから1.6 mAへ低減します。この3.2 mAの差こそが、800 mAhのLi-ion 14500セルによるK7の6.0時間駆動を実現する要因です。
K7にはさらにデッドラインモードのスレッド(audio_player_deadline)があり、OEMがサブミリ秒のオーディオスケジューリングを求める場合に使用されます。ファームウェアのビルドフラグ CONFIG_DEADLINE=y で有効化され、KF1ではデフォルトでオン、K7ではFlashを32 KB節約するためデフォルトでオフとなっています。
5. AliOS Thingsのスケジューラ(Q6およびQ8)
AliOS Things 3.3は、FreeRTOSに非常に近い、優先度ベースのプリエンプティブスケジューラを採用しています。最大32段階の優先度をサポートし、2022年から継続して使用している krhino カーネルを内蔵しています。Q6/Q8の優先度テーブルは以下のとおりです:
| 優先度 | タスク | 目的 |
|---|---|---|
| 7(最高) | audio_isr |
オーディオDMA完了 |
| 6 | oid_decode |
OIDデコード |
| 5 | audio_player |
MP3デコード + DAC |
| 4 | btn_event |
ボタンチャタリング除去 |
| 3 | ble_gatt |
BLE GATT |
| 3 | ble_adv |
BLEアドバタイジング |
| 2 | pwr_mgr |
電源マネージャー |
| 2 | batt_poll |
バッテリーゲージ |
| 1 | led_pat |
LEDアニメーション |
| 0 | ota_recv |
OTAレシーバー |
AliOSスケジューラは、保存コンテキストに使用するレジスタが少ないため、Cortex-M4ではFreeRTOSよりもコンテキストスイッチがわずかに高速です。Q8では1.4µs、B2では2.0µsという測定結果は、高優先度のオーディオ割り込みにおいて意味があります。
6. F8、L5、T2、Y6のLinux CFSスケジューラ
F8、L5、T2、Y6ではLinuxがCompletely Fair Scheduler(CFS)を使用しています。CFSにはRTOSの意味での固定優先度はなく、代わりに次の仕組みがあります。
- nice値は-20(最高)から19(最低)まで。
- リアルタイムグループ(
SCHED_RR,SCHED_FIFO)を使用し、優先度は1〜99です。 - タスクをグループ化する cgroups。
- デッドラインスケジューリング(
SCHED_DEADLINE)ハードリアルタイムタスク向け。
L5ファームウェア 4.2.0 の優先度テーブルは次の通りです:
| Linuxの優先度 | プロセス | 説明 |
|---|---|---|
SCHED_FIFO 90 |
audio-daemon |
オーディオミキサー + DAC |
SCHED_FIFO 80 |
oid-decoder |
OID デコード |
SCHED_FIFO 70 |
wifi-supervisor |
Wi-Fi ローミング |
SCHED_FIFO 60 |
ble-gatt |
BLE GATT |
SCHED_RR 50 |
button-handler |
ボタン + 長押し |
SCHED_RR 40 |
battery-monitor |
バッテリーゲージ |
nice -10 |
led-pattern |
LEDアニメーション |
nice -5 |
ui-thread |
UI描画 |
nice 0 |
asr-engine |
デバイス内ASR |
nice 5 |
logging-daemon |
テレメトリ |
nice 10 |
cache-cleanup |
キャッシュクリーンアップ |
nice 19 |
analytics |
匿名アナリティクス |
オーディオデーモンはSCHED_FIFO 90で動作し、カーネルタスクを除くすべてをプリエンプトします。OIDデコーダーはSCHED_FIFO 80で動作し、ASRエンジンをプリエンプトできます。ASRエンジンは、レイテンシ指向ではなくスループット指向であるため、nice 0で動作します。
7. 優先度逆転:落とし穴
優先度逆転とは、低優先度タスクがミューテックスを保持しているときに、高優先度タスクがそのミューテックスを必要とする場合に発生します。高優先度タスクは低優先度タスクの完了を待つためにブロックされ、その間に中優先度タスクが低優先度タスクをプリエンプトします。高優先度タスクは中優先度タスクが譲るまで停止します。
これまで現場で優先度逆転を1回経験しています。Q8ファームウェア2.6.4(3.3.1の前身)では、低優先度のロギングタスクがUIミューテックスを保持している最中にオーディオプレイヤーが取得を試み、12ミリ秒間ストールしました。これはグリッチとして聞こえました。
すべてのRTOS SKUで搭載している緩和策は、優先度継承ミューテックス(優先度シーリングミューテックスとも呼ばれます)です。
- FreeRTOSでは
xSemaphoreCreateMutex()を使用します(最近のバージョンでは優先度継承をサポートしています)。 - Zephyrは
k_mutexをデフォルトで優先度継承有効で提供。 - AliOSは
krhino_mutex_create()にフラグを指定して使用。フラグを使用します。 - Linuxは
pthread_mutexattr_setprotocol(... PTHREAD_PRIO_INHERIT ...).
を使用。L5オーディオデーモンはALSA PCMハンドル周りに優先度継承POSIXミューテックスを使用。T2ビデオデコーダはVPUハンドル周りに同じく対応。
8. ティックレスアイドルと省電力
RTOS SKU上でスケジューラが提供する最大の省電力はティックレスアイドルです。処理がない場合、スケジューラはSysTick割り込みを無効化し、CPUが低消費電力の待機状態に入ります。次のイベント(タイマ満了、割り込み)が発生すると、スケジューラはCPUを起動し、SysTickを再プログラムします。
ラインナップにおけるティックレスアイドルの削減効果(25 °C、3.7 Vで測定):
| SKU | ティックあり時のアイドル電流 | ティックレス時のアイドル電流 | 削減効果 |
|---|---|---|---|
| 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 SKUにはRTOSの意味でのティックレスアイドルはなく、ランキューが空のときに深いCステートを選択するcpuidleガバナを備えています。F8はアイドル時14 mA、L5は18 mA、T2は22 mAに達し、依然としてRTOSアイドルの4〜10倍です。
9. 負荷シェディングと過負荷動作
読書ペンのCPUが過負荷状態になった場合 — 例: ユーザーが本をページめくりし、OIDデコーダが200 msで5ページを処理するなど — スケジューラはオーディオストリームの正常性を維持するために処理を破棄する必要があります。当社の負荷シェディングポリシーは以下の通りです:
| 条件 | 動作 |
|---|---|
| OIDキュー深度 > 8 | 最古を破棄、カウンタを記録 |
| オーディオバッファのアンダーラン切迫 | OIDタスク優先度を短時間引き上げ |
| BLEスタックのキュー深度 > 16 | 切断して再接続 |
| Wi-Fi TXキュー深度 > 32 | 低優先度フレームを破棄 |
| Linux loadavg > 4 | ASRエンジンを50% CPUにスロットリング |
| バッテリー残量 8% 未満 | Wi-Fiを無効化し、LEDを調光し、モノラル音声に切り替え |
「OIDタスクの優先度を短時間引き上げる」トリックは、オーディオプレイヤータスクに実装している動的優先度ブーストです。オーディオバッファの空きが75%を超えると、オーディオタスクはOIDデコーダーに通知を送り、次の50ms間だけ優先度を引き上げます。これにより、B2のワーストケースシナリオで20〜40msの余裕が生まれます。
10. スケジューラの検査とデバッグ
すべてのSKUは、USBまたはBLE経由でスケジューラ検査APIを公開しています。APIの戻り値は以下のとおりです。
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は工場QA中にこのAPIをポーリングして「スケジューラ指紋」、すなわちタスクごとのCPU使用率のスナップショットを取得できます。2023年以降、47,000件以上の指紋を出荷してきました。指紋は現場での問題診断に非常に有用です。例えば、想定の4%ではなくbattery_monitorがCPUを28%消費しているペンは、ポーリングにバグがあると判断できます。
Linux SKUでは、同じデータが[修正:htmlタグ数、スクリプト比率]
/proc/<pid>/statおよびtop -Hから取得できます。L5ファームウェア4.2.0にはscheduler-diagツールがバンドルされており、指紋をJSONファイルにキャプチャしてオンデマンドでWi-Fi経由で送信できます。
11. リーディングペンに適したスケジューラの選び方
新しいリーディングペンのSKU向けにスケジューラを選定するOEM向けに、経験則をご紹介します。
| 購入シナリオ | 推奨スケジューラ | 推奨SKU |
|---|---|---|
| 5ドル未満、単一言語、タップのみ | FreeRTOSティックベース | P2, B1 |
| ミドルレンジのバイリンガル、BLE対応 | Zephyrティックレス | K7、KF1 |
| カスタムOID対応、BLE対応のミドルレンジ | AliOS Things | Q6, Q8 |
| Wi-Fi + ASR + 720pビデオ | Linux CFS(音声はSCHED_FIFO) |
L5 |
| 防衛用途またはプレミアムビルド、ハードリアルタイム | Linux + PREEMPT_RTパッチ | T2 |
| プレミアムK〜12、コンテンツストリーミング対応 | Linux CFSデッドラインスケジューリング | Y6 |
購入者向けヒント: OEMには必ず2つのスケジューラ指標を依頼しましょう。低優先度タスクから高優先度タスクへの最悪プリエンプション遅延(µs)と、ティックレスモード有効時のアイドル電流(mA)です。K〜3向けリーディングペンの用途では、10µs超またはアイドル4mA超は赤信号です。