Raspberry pi pico/pico2をVScode PlatformIOでC++開発① Debug/FreeRtOS/USBCDC

カテゴリー: シングルボードコンピュータ  タグ:

Raspberry pi pico/pico2(以降pico/pico2)のC++開発環境にはずっと試行錯誤していて、Arduino IDEである程度要望は達成できてはいたのですが、やっとESP32で使い慣れたVScode+PlatformIOで業務レベル開発ができるようになったので覚書です

要望というのは

  1. (必須)RP2040/RP2350両方開発したい
  2. (必須)FreeRtOS → Arduinoモードを使う時点で目標達成
  3. (必須)USBCDC
  4. (必須)デバッガで書込み(いちいちBoot&ResetしてからのD&Dが甚だ面倒くさい)
  5. Arduinoのライブラリが使える
  6. picoSDKの書式も使える
  7. (ささやかな要望)pioasmも開発環境内で使いたい
  8. (ささやかな要望)ソースファイルはプロジェクトルートの下の「src」にしたい

最近安定化したVScode拡張機能「Raspberry Pi Pico」がかなり良い線です、が、私のスキルではFreeRtOSが組み込めず、また、ウイザードで作成するとソースフォルダがプロジェクトルートになるのでいちいちCmakeLists.txtを編集するのが面倒でした

そのほか、

デバッグが佳境に入ると頻繁にキーボードに手を伸ばすことすら面倒なので、F5キー周辺のみの「デバッグ用USBキーボード」もpicoで作ってみて手元近くに置いて「効率化?」もしてみました

オールpicoのお話になっております

2025/03/14 修正 githubアドレスについて致命的なミスがありましたm(_ _)m。お詫びして修正しました。

VScode PlatformIOでpicoを使う

通常のPlatformIOのpico開発は、うまく機能しておらず諦めかけていたところ、たどり着いたのがmaxgerhardt / platform-raspberrypi 」。世の中にはこんなアレンジできてしまう人がいるんだ!、とひとしきり感心した後、コメントをDeepLで翻訳すると、作成者曰く

このフォークは、Raspberry Pi RP2040とRP2350 Arduino Core for PlatformIOのアップストリーム開発が進んでいないために作成されました」というわけで、

maxgerhardt版platformが決めて

組み込み方はいくつかあるようですが、PIO HomeでPlatformsを選択し、「Advanced Installation」から、

2025/03/14 修正

 「https://github.com/platformio/platform-raspberrypi.git」

 「https://github.com/maxgerhardt/platform-raspberrypi.git」

 

を打ち込むとインストールできる。プロジェクト毎に使い分けたいときにはplatformio.iniに

 platform = https://github.com/platform-raspberrypi.git」      

 「platform = https://github.com/maxgerhardt/platform-raspberrypi.git

と記述すれば良いようだ。Platformsに下記が追加されたらOK

※公式からの是非は不明なので、現状は自己責任レベルかな、とは思いますが、社内用から始め治具にもどんどん使っています。ほんと感謝m(_ _)m

ArduinoとPicoSDK両方が使えることがわかる

Pico-Probeの用意

D&D書き込みを避けるには、デバッガで書き込むしかないのである

デバッガはpicoを使った「pico-probe」

公式ページからたどり、「raspberry pi/debug prove」から、「 debugprobe_on_pico.uf2 」をロードして、通常の書き込みモードでマスストレージpicoに書き込む。pico2の場合は「~pico2.uf2」というファイル名をロードすることになる

ターゲットとの接続

 pico-probe側③ピンGND   ターゲット側DEBUGコネクタ②ピンGND

 pico-probe側④ピンGP2   ターゲット側DEBUGコネクタ①ピンSWC

 pico-probe側⑤ピンGP3   ターゲット側DEBUGコネクタ③ピンSWD

接続の様子。下の写真は、上がデバッガpico、下がターゲットpico2

デバッグで使うのは黒・青・黄の3本

UART経由のシリアルモニターを使う方はTx、Rxも配線しますが、今回は使わないのでSWC/SWDのみとしました

テストプログラムの作成

PlatformIO+NewProgramからのウイザードで下のようにしてみた。ターゲットはpico2です

platformio.iniのデバッガの設定

[env:rpipico2]
platform = raspberrypi
board = rpipico2
framework = arduino
upload_protocol = cmsis-dap
debug_tool = cmsis-dap
debug_init_break = tbreak setup	;
build_type = debug
board_build.core = earlephilhower
board_build.filesystem_size = 0m

FreeRtOS・USBCDCのテストプログラム

タスクでLEDを点滅しながら、別タスクでUSBCDCに文字列を送り、USBCDCからポーリングで文字受信し’a’ならメッセージを送るプログラム。

今回は1文字のみの受信確認だが、USBCDCでは受信割り込みという概念がなく、そのかわりバックグラウンドで受信バッファに取り込まれるので、文字列受信ができる

したがって受信割り込みハンドラを使わなくてもタスクで数msループでポーリングで十分にシリアル送受信が可能

#include <Arduino.h>
#include <FreeRTOS.h>
#include <task.h>

void ledTask(void *pvParameters) {
	bool isOn = false;
	while(1) {
		digitalWrite(25, isOn = !isOn);
		Serial.printf("Hello! \n\r");
		vTaskDelay(500);
	}
}
void mainTask(void *pvParameters) {
	for(;;) {
		if (Serial.available() > 0) {
			char c = Serial.read();
			if (c == 'a') {
				Serial.printf("Receive!\n\r");
			}
		}
		vTaskDelay(5);
	}
}

void setup() {
 	pinMode(25, OUTPUT);
	xTaskCreate(mainTask, "MainTask", 1024, NULL, 1, NULL);
	xTaskCreate(ledTask,  "LedTask",  1024, NULL, 1, NULL);
}

void loop() {}
 

タスクを生成したら、ディレイはすべてvTaskDeley関数で行う。sleepが使えるのはタスク生成前までにしておいたほうが無難

arduinoモードではvTaskScheduler()関数は不要らしい。記述するとおかしくなるようだ

TeraTermで受信中およびキー入力の様子の画像

デバッグ開始:F5、実行:F5、デバッグ終了:Shift+F5

デバッグは?

F5を押してロードからsetup()で停止するまで10数秒かかる。大規模プログラムではもっとかかるかもしれない。debugspeed = 2000とかしても上限があるらしく、指定しなくても特に変わらない

詳細なテストではないですが、デバッガpico-probeをpicoとpico2で比較した結果、上のテストプログラム程度ならロード時間は1秒も変わらないようでした。

ブレークを決めると普通に止まる。変数Watchも普通にできる。ロード時間がかかるとは言え、シリアルprintと併用すればさらに効率は上がる

デバッグ用に専用キーボードをpicoで製作

Platformio.iniにdebug_init_break = tbreak setupの記述を入れたので、実行させると一旦setup()で止まる。したがって、通常実行させるにはF5を二回押すことになる

この記述をコメントアウトすると、.platformio下のテンポラリソース内で止まるのでこれはこれでヤバい。現段階では止まらないで一気に実行する設定がわからない

実行・停止・更新のたびにPCキーボードに手を伸ばすのはちょっと煩雑なので、デバッグ専用のキーボードを作って手元で操作できるようにした。これによりちょっとした変更とマウス操作は右手だけで、ターゲット操作とデバッグ操作は左手に分けて出来る。私にとっては少し効率が上がる

配線はGP2:F5、GP3:Shift+F5、GP4:Ctrl+F5、GP5:Ctrl+Shift+F5。

raspberry pi 4/5などのpythonデバッグにも使えるようにCtrl+F5やCtrl+Shift+F5キーを増やした。まあ、ショートカットキーを変えても良いのだが・・・今更使い慣れているので

#include <Arduino.h>
#include <Keyboard.h>

bool keyPressed[8] = {false, false, false, false, false, false, false, false};	// キーが押されたかどうかを追跡するフラグ

void setup() {
	// GPIOピンを入力として設定
	pinMode(2, INPUT_PULLUP);  // GP2
	pinMode(3, INPUT_PULLUP);  // GP3
	pinMode(4, INPUT_PULLUP);  // GP4
	pinMode(5, INPUT_PULLUP);  // GP5
	Keyboard.begin();          // USBキーボードの初期化
}

void loop() {
	// GP2 → F5
	if (digitalRead(2) == LOW && !keyPressed[0]) {
		Keyboard.press(KEY_F5);
		keyPressed[0] = true;	// キーが送信されたことを記録
	} else if (digitalRead(2) == HIGH) {
		keyPressed[0] = false;	// GPIOがHIGHになったらフラグをリセット
		Keyboard.release(KEY_F5);
	}
	// GP3 → Ctrl+F5
	if (digitalRead(3) == LOW && !keyPressed[1]) {
		Keyboard.press(KEY_LEFT_SHIFT);
		Keyboard.press(KEY_F5);
		keyPressed[1] = true;
	} else if (digitalRead(3) == HIGH) {
		keyPressed[1] = false;
		Keyboard.release(KEY_LEFT_SHIFT);
		Keyboard.release(KEY_F5);
	}
	// GP4 → Shift+F5
	if (digitalRead(4) == LOW && !keyPressed[2]) {
		Keyboard.press(KEY_LEFT_CTRL);
		Keyboard.press(KEY_F5);
		keyPressed[2] = true;
	} else if (digitalRead(4) == HIGH) {
		keyPressed[2] = false;
		Keyboard.release(KEY_LEFT_CTRL);
		Keyboard.release(KEY_F5);
	}
	// GP5 → Ctrl+Shift+F5
	if (digitalRead(5) == LOW && !keyPressed[3]) {
		Keyboard.press(KEY_LEFT_CTRL);
		Keyboard.press(KEY_LEFT_SHIFT);
		Keyboard.press(KEY_F5);
		keyPressed[3] = true;
	} else if (digitalRead(5) == HIGH) {
		keyPressed[3] = false;
		Keyboard.release(KEY_LEFT_CTRL);
		Keyboard.release(KEY_LEFT_SHIFT);
		Keyboard.release(KEY_F5);
	}
	delay(10);	// デバウンス処理のために少し待機
}

HIDキーボードを使うには、platformio.iniには下の一行を追加

lib_deps = keyboard

キー操作に関するファームでは、操作を間違うとソースファイルに連続打鍵してえらいことになった経験があるので、ここはpico-probeを使わないでマスストレージにuf2を書き込む方法でしっぽりとプログラミング。やっとうまくいった(´▽`) ホッ

以上

お気軽にコメントをどうぞ。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)