Yotsu Tech

令和最強版 STM32 マイコンの開発環境のすすめ vol.0 基礎知識編

どうもみなさん、こんにちは。
アドカレの時だけ記事を書く人になってるWMMC OBのよつです。

この記事は2025年のWMMC Advent Calendar 10日目の記事です。

昨日の記事はTanaportくんの「魔改造サークル標準機(無限設計編)」でした。
Nucleoマウスが魔改造されてQDD化されるのは楽しみですね。これだけ楽しい用途で使ってもらえるなら、改造元の機体も喜ぶことでしょう。

0. はじめに

「STM32CubeIDE は便利だけど、動作が重い」
もっとイケてる見た目のエディタで編集したい
「AIとの連携をスムーズにしたい」

マイクロマウスやロボット開発を進めていくと、そんな思いから統合開発環境(IDE)を卒業し、モダンな開発環境への移行を考え始める時期がやってきます。私にもその時期が訪れ、VSCodeへと移行することにしました。

開発環境を移すに当たり、学んだことを何回かに分けてまとめようと思います。

そこで本記事は、最強の開発環境を作る前に準備としての 基礎知識編です。
いきなり環境構築の手順に入る前に、まずは「ビルドの仕組み」を解説します。

CubeIDEを使ってる1年生を始めとした多くの人は、IDEの画面上にある「トンカチのマーク(ビルド)」を押して、緑色の「再生マーク(書き込み)」を押していると思います。

「よく分からんけど、ボタンを押したら実行ファイルができて、動く」

なんて思ってる人も多いのではないでしょうか。かくいう私がその一人でした。

IDEではボタンポチだけですみますが、VSCodeに開発環境を移す際には自分でビルドシステムを設定する必要があります。

作業を始める前にこのあたりの全体像を掴み、どのツールがなんのために必要なのかが理解したほうがよいと感じたのでまとめてみました。


1. ビルドとは

なぜビルドが必要なのか?

私たちが書く C/C++ のソースコードは、人間には読めますが、マイコンには理解できません。マイコンが理解できるのは「0と1の羅列(機械語)」だけです。
そのため、ソースコードを機械語に翻訳し、マイコンが実行できる形式にパッケージングする必要があります。この一連の変換工程を「ビルド」と呼びます。 大学1年生の授業で習ったような、習ってないような気がしますね。

全体図:ソースコードから実行ファイルまで

ビルドボタンを1回押すだけで終わるように見えますが、裏側では大きく分けて以下の5つのステップが順番に実行されています。

  1. プリプロセス
  2. コンパイル
  3. アセンブル
  4. リンク
  5. 変換

先に5ステップのフローを図に示します。

以降、各ステップで何が行われているのか順を追って説明していきます。


2. 各ステップの内部で何が起きているか

(1) プリプロセス:展開の下準備

コンパイラが最初に行うのがプリプロセスです。これは # で始まる命令(プリプロセッサディレクティブ)を処理するフェーズです。

  • #include: 指定されたファイルの中身を、その場にコピペします。HAL ライブラリの巨大なヘッダファイルもここで展開されるため、ソースコードは一気に巨大になります。
  • #define: 定義されたマクロを単純置換します。

この段階でのエラーの代表格は「No such file or directory」です。インクルードパスの設定が間違っていると、ここで止まります。

(2) コンパイル:C言語からアセンブリへ

ここで初めて、C言語の文法チェックと翻訳が行われます。
コンパイラは、巨大になったソースコードを読み込み、アセンブリ言語という、より機械に近い言葉(でもまだ文字の状態)に変換します。

  • 文法エラー(セミコロン忘れなど)はここで検出されます。
  • 最適化もここで行われます。「無駄な処理を省く」「ループを展開する」といった処理により、コードサイズや実行速度が変わります。
    (余談ですが私の職場の先輩曰く、昔はこのコンパイラの頭が悪く、非効率的なアセンブリ言語を出してきたそうです。なので人間がゴリゴリにアセンブリ言語を書いて最適化していたとのこと。今では全く考えられないですね。すごい。)

(3) アセンブル:アセンブリから機械語へ

アセンブリ言語(.s ファイル)を、CPUが直接解釈できるバイナリデータ(機械語)に変換します。
この結果生成されるのがオブジェクトファイル(.o)です。

注意すべきは、この段階ではまだ「部品」でしかないという点です。例えば main.c の中で別のファイルの関数 motor_drive() を呼んでいても、この時点では「どこかに motor_drive があるはず」という仮の状態で処理が進みます。

(4) リンク:部品を結合する

バラバラに生成された大量のオブジェクトファイルを一つに合体させ、実行可能なファイル(.elf)を作るのがリンカの仕事です。

リンカは以下のことを行います。

  • 合体: main.o, motor.o, stm32xxxx_hal.o, startup.o などを全て繋げる。
  • 名前解決: 「main から呼ばれている motor_drive はこっちのファイルにあるよ」と紐付ける。
  • 住所決定: プログラムや変数を、マイコンのメモリ(Flash や RAM)のどの番地に配置するかを決める。

よく見るエラー「undefined reference to ...」は、「部品を全部探したけど、呼び出してるその関数が見つからないよ!」というリンク段階のエラーです。

(5) 変換:書き込み用ファイルへ

最後に、デバッグ情報などが含まれた .elf ファイルから、純粋なデータ部分だけを抜き出し、書き込みツールが対応している形式(.hex や .bin)に変換します。
これでようやく、マイコンに書き込めるデータの完成です。


3. 各種ツール の役割分担

仕組みは分かりましたが、数百個あるファイルを毎回手動でコマンドを叩いてコンパイルするのは不可能です。そこで登場するのがビルドシステムです。

私が最初に見たとき、それぞれのツールの違いが分からず特に混乱した部分です。
どんな役割なのか、なんで必要なのかをAIに聞いたら以下のように例えてくれました。

① toolchain (stm32マイコンをビルドするときはgnu-arm-toolchain)

  • 役割: 「職人」
  • 実際にプリプロセス、コンパイル、リンクを行うプログラム群の実体です。

② Make / Ninja

  • 役割: 「現場監督」
  • 職人に指示を出します。「main.c が更新されたから、main.o だけ作り直してくれ。あ、こっちは変更ないからそのままでいいよ」という風に、依存関係を管理して最小限の作業でビルドを完了させます。
  • Ninja は Make よりも現代的で、とにかく仕事(ビルド速度)が速いのが特徴です。

③ CMake

  • 役割: 「設計士」
  • 現場監督(Make/Ninja)のための「指示書(ビルドルール)」を作るツールです。
  • 「このフォルダのソース全部使って」「インクルードパスはここ」といった設定を書くと、それを元に Makefile や build.ninja を自動生成してくれます。


4. まとめ

今回は「ビルド」という処理を分解して解説しました。

  • ビルドは黒魔術ではない:プリプロセス→コンパイル→アセンブル→リンクの積み重ね。
  • エラーの場所を見極める:「関数の定義忘れ」なのか「インクルードパスミス」なのか、発生フェーズが分かれば怖くない。
  • ツールの役割:GCC(職人)、Ninja(監督)、CMake(設計士)の分担を理解する。

これらが分かってしまえば、IDE を使わずに VS Code で開発環境を作るなんてちょちょいのちょいです。

次回は実践編。「VS Code + CMake + Ninja で作る、爆速 STM32 開発環境」の具体的な構築手順を紹介します。

また、アドカレの明日の記事はJamesくんの「またなんか書く」です。投稿おくれてすみません。
3日に引き続き複数日書くの偉すぎる。明日も楽しみにしてます!

よつ

WMMC OB 社会人1年目 のよつです。 マイクロマウス暦3年 よさこいとルービックキューブが趣味です。 クラシックマウス:obsidian