VSCode+ラズピコ拡張 ファイル分割してみる

VSCode+ラズピコ拡張 ファイル分割してみる

皆さん こんにちは

ラズピコ研究員の moon です。

この記事は「初心者必見! Raspberry Pi Picoの C/C++デバッグ環境を容易に構築する」によって環境構築していることを前提にしています。

ファイルを分けてプログラムを構成することは非常に大切です。
私も仕事でコードを書く時には必ずファイル分割しています。
プログラムが大きくなればなるほど、その影響は大きくなります。

一般的なフォルダの仕分け作業と同じです。
後からコードを編集する時にファイルやフォルダで分けておくと見つけやすくなります。
その他にプログラムの場合、分割コンパイルすることで時間の節約になります。

これは例えば、もの凄く大きなファイルのほんの一部を修正した場合、その大きなファイル全体をコンパイルすることになり時間がかかります。
ファイルを小さく分割しておけば、その修正箇所のみをコンパイルすれば良いため時間が節約できるという理屈です。

そこで今回は、VSCode+ラズピコ(Raspberry Pi Pico)拡張で作成したプロジェクトに対してファイル分割の作業を行ってみます。

前回の記事 新規にプロジェクトをつくる を参考にしながらプロジェクトを改造していきます。

作業概要

プロジェクトはタイマー、SPI、UARTの機能を少し実装したもので構成されています。
動作自体は全く意味がなく、これらの機能を確認するためのコードではありません。

元々、ひとつのファイルで構成されているものをファイル分割してみて、それらの関数が正しく動作していることをステップ実行しながら確認するプロジェクトになります。

新規プロジェクトをつくる

前回と同じく、「New C/C++ Project」を使ってプロジェクトを作成します。
プロジェクト名は split としました。

Name を split とします。
Board type で Pico を選択します。
Location は C:\Data\pico としました。
SDK version は v2.2.0 (そのまま)

Features では SPI, UART, HW Timer の3つにチェックを入れます。

Stdio support はチェックしません。
Code generation options は Use project name as entry point file name にのみチェックを入れます。
Debugger は DebugProme(CMSIS-DAP)にチェックを入れます。
CMake Tools の Enable CMake-Tools extension integration にチェックを入れます。

確認後、右下の Create を選択します。

その後、キットを選択します。
(候補の一番下の Pico … を選択する)

次にアクティビティーバーから実行とデバッグを選択します。(または Ctrl + Shift + D)

splitの起動対象を選択するように言われたら、下の split を選択します。

一度プログラムが正しく動作することを確認しておきます。

CMakeLists.txt

プロジェクト自体はCMakeを使っていて、構成をカスタマイズするためにはCMakeLists.txtを編集する必要があります。

ディレクトリとファイル構成

私の環境では C:\Data\pico の下に split プロジェクトを置きました。
今回ファイル分割する、その構成を以下に示します。

split
|— CMakeLists.txt (記入もれしていたので追記しました)
|
|— include — io \ spi_ex.h , uart_ex.h
|
|— src — split.c , CMakeLists.txt (今回のプロジェクトでは不要)
  |
  |— io \ spi_ex.c , uart_ex.c

ディレクトリとファイルを構成する

これらの作業はVSCode上で行うよりも、Windowsのエクスプローラーを使った方が作業が簡単です。
一度、VSCodeを終了します。

まず、split ディレクトリにある build ディレクトリを削除します。
次に、split ディレクトリの下に include と src のフォルダを作成します。

次に、split ディレクトリにある split.c を src の下に移動します。

次に、spi_ex.c を以下の内容で作成します。
spi_ex.c は split\src\io の下に置きます。
自動生成された SPI の初期化部分を抜き出したコードを関数化したものです。

#include "spi_ex.h"
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"

#define SPI_PORT spi0
#define PIN_MISO 16
#define PIN_CS   17
#define PIN_SCK  18
#define PIN_MOSI 19

void spi_ex_init() {
    spi_init(SPI_PORT, 1000*1000);
    gpio_set_function(PIN_MISO, GPIO_FUNC_SPI);
    gpio_set_function(PIN_CS,   GPIO_FUNC_SIO);
    gpio_set_function(PIN_SCK,  GPIO_FUNC_SPI);
    gpio_set_function(PIN_MOSI, GPIO_FUNC_SPI);
    
    // Chip select is active-low, so we'll initialise it to a driven-high state
    gpio_set_dir(PIN_CS, GPIO_OUT);
    gpio_put(PIN_CS, 1);
}

次に、uart_ex.c を以下の内容で作成します。
uart_ex.c は split\src\io の下に置きます。
自動生成された UART の初期化部分を抜き出したコードを関数化したものです。

#include "uart_ex.h"
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/uart.h"

#define UART_ID uart1
#define BAUD_RATE 115200

#define UART_TX_PIN 4
#define UART_RX_PIN 5

void uart_ex_init() {
    uart_init(UART_ID, BAUD_RATE);
    gpio_set_function(UART_TX_PIN, GPIO_FUNC_UART);
    gpio_set_function(UART_RX_PIN, GPIO_FUNC_UART);
}

次に、spi_ex.h を以下の内容で作成します。
spi_ex.h は split\include\io の下に置きます。
spi_ex.c のプロトタイプ宣言をしたヘッダファイルです。

#pragma once
void spi_ex_init();

次に、uart_ex.h を以下の内容で作成します。
uart_ex.h は split\include\io の下に置きます。
uart_ex.c のプロトタイプ宣言をしたヘッダファイルです。

#pragma once
void uart_ex_init();

次に、split.c を以下の内容で編集します。
先ほど言いましたが split.c は src の下に移動します。

#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/spi.h"
#include "hardware/timer.h"
#include "hardware/uart.h"

#include "spi_ex.h"
#include "uart_ex.h"

#define UART_ID uart1

int64_t alarm_callback(alarm_id_t id, void *user_data) {
    // Put your timeout handler code in here
    return 0;
}

int main()
{
    stdio_init_all();

    spi_ex_init();
    uart_ex_init();

    add_alarm_in_ms(2000, alarm_callback, NULL, false);
    uart_puts(UART_ID, " Hello, UART!\n");
    
    while (true) {
        printf("Hello, world!\n");
        sleep_ms(1000);
    }
}

2025.8.10 以下を改訂します。

次に、CMakeLists.txt を以下の内容で編集し、src の下に保存します。
中身がないので、1行目にコメントを意味する # を置きました。
ただし、srcの下に CMakeLists.txt のファイル自体が存在しないと正しく動作しませんでした。
そのために形式的にこのファイルを置いています。

src ディレクトリの下は、特に CMakeLists.txt に記述したいことがなければファイル自体も不要です。
確認不足でした、すみません。

CMakeLists.txtを編集する

split の下にあるCMakeLists.txt を以下の通りに編集して保存します。
(注)
srcフォルダ下に CMaleLists.txt 自体に意味がない場合(不要な場合)、一番下の行にある add_subdirectory(src) は 不要でした。

# のある部分が追加・変更点になります。

cmake_minimum_required(VERSION 3.13)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# ここから
set(TARGET_FILES
    ${CMAKE_CURRENT_LIST_DIR}/src/split.c
    ${CMAKE_CURRENT_LIST_DIR}/src/io/spi_ex.c
    ${CMAKE_CURRENT_LIST_DIR}/src/io/uart_ex.c
    )
# ここまで変更する

if(WIN32)
    set(USERHOME $ENV{USERPROFILE})
else()
    set(USERHOME $ENV{HOME})
endif()
set(sdkVersion 2.2.0)
set(toolchainVersion 14_2_Rel1)
set(picotoolVersion 2.2.0)
set(picoVscode ${USERHOME}/.pico-sdk/cmake/pico-vscode.cmake)
if (EXISTS ${picoVscode})
    include(${picoVscode})
endif()
set(PICO_BOARD pico CACHE STRING "Board type")

include(pico_sdk_import.cmake)

project(split C CXX ASM)

pico_sdk_init()

# ここを変更する
add_executable(split ${TARGET_FILES})

pico_set_program_name(split "split")
pico_set_program_version(split "0.1")

pico_enable_stdio_uart(split 0)
pico_enable_stdio_usb(split 0)

target_link_libraries(split
        pico_stdlib)

target_include_directories(split PRIVATE
        ${CMAKE_CURRENT_LIST_DIR}
        ${CMAKE_CURRENT_LIST_DIR}/include/io # この行を追加する
)

target_link_libraries(split 
        hardware_spi
        hardware_timer
        hardware_uart # この行を追加する
        )

pico_add_extra_outputs(split)

# add_subdirectory(src) # この行を追加する

先ほどのディレクトリ・ファイル構成になっていることをご確認ください。

VSCodeを起動する

起動すると画面右下に以下のメッセージが出てしまいます。
このメッセージが出る原因が良くわかっていません。

この場合、すみやかに No を選択します。(Yesを選択する必要はありません)

調べた限りこのメッセージが出るには以下の要因があるようです。

(a) .vscodeのフォルダがない
(b) buildディレクトリが存在しない
(c) CMakeLists.txt にエラーがある

CMakeLists.txtを編集しているので、もしかすると(c)なのかも知れませんが、動作することもあるのでエラーではないようにも思います。

誤って、このメッセージを無視して(YesもNoも選択せずに)「実行とデバッグ」を選択すると先に進みません。
その場合には、実行メニューから一旦デバッグの停止を選択します。

そしてその時には画面右下(下記画像の緑色枠のベル?表示の部分)をクリックし、先ほどのメッセージを表示させて No を選択します。

ひと手間かかりますがデバッグ前に、この工程を踏んでください。

そして実行とデバッグからプログラムを動作させます。
ステップ実行しながら、SPI と UART の初期化関数内を正しく実行できれば成功です。

いかがでしたか?
皆さんの環境では正しく動作させることができたでしょうか?

今回のプロジェクトはやや構成が複雑です。
もしかしたら動かすことができない方がいらっしゃるかも知れません。

GitHubの こちら にプロジェクトを置きましたので参考になさってください。

お疲れさまでした。

Environmentカテゴリの最新記事