第2章: ベアメタルからの出発#
OS を使わずに(ベアメタルで)、loop() の中の自作の協調的スケジューラが、LED の点滅・
サーボ角度の計算(表示だけで、サーボは不要)・A0 の読み取りの 3 つのタスクを順番に
回すところから始めます。
サンプルコード#
ディレクトリ:
docs/os-on-arduino/code/02_baremetalビルドと書き込み:
uv run pio run -d 02_baremetal -t upload
uv run pio device monitor -b 115200
ソースコード#
この章のスケッチです。ファイル名をクリックすると開きます。コードは原稿リポジトリから自動で同期されているので、ここに出ているものが常に最新です。
src/main.cpp — 119 行
1/**
2 * Chapter 2: Cooperative Multitasking Scheduler
3 *
4 * Demonstrates cooperative (non-preemptive) multitasking.
5 * Each task runs to completion before another task can run.
6 *
7 * Problem: If one task takes too long (like heavy_task),
8 * all other tasks are delayed.
9 */
10
11#include <Arduino.h>
12
13#define LED_PIN LED_BUILTIN
14#define SERVO_PIN 9
15
16// --- Scheduler Definition ---
17typedef void (*TaskFunc)(void);
18
19struct Task {
20 TaskFunc func;
21 unsigned long interval;
22 unsigned long last_run;
23 const char* name;
24};
25
26#define MAX_TASKS 4
27Task tasks[MAX_TASKS];
28int task_count = 0;
29
30void scheduler_add_task(TaskFunc func, unsigned long interval, const char* name)
31{
32 if (task_count < MAX_TASKS) {
33 tasks[task_count].func = func;
34 tasks[task_count].interval = interval;
35 tasks[task_count].last_run = 0;
36 tasks[task_count].name = name;
37 task_count++;
38 }
39}
40
41void scheduler_run(void)
42{
43 unsigned long now = millis();
44 for (int i = 0; i < task_count; i++) {
45 if (now - tasks[i].last_run >= tasks[i].interval) {
46 tasks[i].last_run = now;
47
48 Serial.print("[");
49 Serial.print(now);
50 Serial.print("ms] Running: ");
51 Serial.println(tasks[i].name);
52
53 tasks[i].func();
54 }
55 }
56}
57
58// --- Task Definitions ---
59void led_task(void)
60{
61 static bool state = false;
62 state = !state;
63 digitalWrite(LED_PIN, state);
64 Serial.println(" LED toggled");
65}
66
67void servo_task(void)
68{
69 static int angle = 0;
70 angle = (angle + 10) % 180;
71 Serial.print(" Servo angle: ");
72 Serial.println(angle);
73}
74
75void sensor_task(void)
76{
77 int value = analogRead(A0);
78 Serial.print(" Sensor value: ");
79 Serial.println(value);
80}
81
82void heavy_task(void)
83{
84 // Simulate heavy processing
85 Serial.println(" Heavy task start...");
86 delay(200); // 200ms blocking!
87 Serial.println(" Heavy task end");
88}
89
90// --- Setup and Loop ---
91void setup()
92{
93 Serial.begin(115200);
94 while (!Serial) { }
95
96 pinMode(LED_PIN, OUTPUT);
97 pinMode(SERVO_PIN, OUTPUT);
98
99 Serial.println("=== Cooperative Scheduler Demo ===");
100 Serial.println();
101 Serial.println("This demonstrates cooperative multitasking.");
102 Serial.println("Uncomment heavy_task to see the problem with blocking tasks.");
103 Serial.println();
104
105 // Register tasks
106 scheduler_add_task(led_task, 500, "LED");
107 scheduler_add_task(servo_task, 100, "Servo");
108 scheduler_add_task(sensor_task, 200, "Sensor");
109 // Uncomment the following line to see the problem:
110 // scheduler_add_task(heavy_task, 1000, "Heavy");
111
112 Serial.println("Tasks registered. Starting scheduler...");
113 Serial.println();
114}
115
116void loop()
117{
118 scheduler_run();
119}
実機での出力#
Arduino UNO R4 WiFi の実機で実際に取得した出力です(macOS / Windows の 両方で同じ結果を確認しています)。手元の出力と見比べてください。
協調的スケジューラが 3 つのタスクを順に回しています。Sensor value は可変抵抗をつないでいなければ端子が拾うノイズです。
Servo タスクは角度を計算して表示するだけで、サーボには信号を出しません。サーボをつなぐ必要はありません。
Servo angle が 10 度ずつ動き、その合間に LED と Sensor が
挟まるところ。3 つのタスクが順番に回っているのが見えます#
[2500ms] Running: LED
LED toggled
[2500ms] Running: Servo
Servo angle: 70
[2600ms] Running: Servo
Servo angle: 80
[2600ms] Running: Sensor
Sensor value: 219
[2700ms] Running: Servo
Servo angle: 90
[2800ms] Running: Servo
Servo angle: 100
[2800ms] Running: Sensor
Sensor value: 219
...
つまずきやすいポイント#
(読者からの質問に応じて随時追記します。質問は GitHub Issues へどうぞ)