最終更新日:2025-03-04
STマイクロエレクトロニクスの開発環境:STM32CubeIDEを使ってアセンブリ言語プログラムだけのビルドとデバッグを行った事例を紹介します。プログラムのターゲットは同じくSTマイクロエレクトロニクスのMCUボード:NUCLEO-H503RBです。
STM32CubeIDEにはプログラムの自動生成機能がありソフトウェア開発には非常に有益です。しかし、今回はCPUの基本的な動作を確認するのが目的なので、リセット解除後の最初の動作を指示するベクターテーブルとリセットハンドラーを含めた処理全体に自分で作ったアセンブリ言語プログラムを使います。
内容
- 1. 背景
- 2. プロジェクト作成
- 3. ソースファイル作成
- 4. リンカースクリプトファイル作成
- 5. ビルド設定
- 6. ビルド実行
- 7. デバッグ設定
- 8. プログラム実行
- 9. デバッグ
- 10. 参考資料
1. 背景
そもそものやりたいことから下図のように考えて、手段(ボードと開発環境)を選択しました。

そもそものやりたいことは、「費用を抑えてCPUの基本的な動作を確認する」環境を手に入れることです。
選択したボードはSTマイクロエレクトロニクスのNUCLEO-H503RBボードです。ARMのCPU:Cortex-M33を内蔵したSTマイクロエレクトロニクスのMCU:STM32H503RBT6とデバッガーのST-Linkを搭載しています。

選択した開発環境はボードに合わせたSTM32CubeIDEです。初めに書いたように、今回はリセット解除後の最初の動作を指示するベクターテーブルとリセットハンドラーを含めた処理全体に自分で作ったアセンブリ言語プログラムを使います。そこで、せっかく用意されている自動生成などの機能を使用しないのは申し訳ないですが、STM32CubeIDEを使って下記の2つを行い、「費用を抑えてCPUの基本的な動作を確認する」ための準備をしました。
2. プロジェクト作成
アセンブリ言語プログラムに使えるまったく自動生成ファイルのないプロジェクトを作成します。
STM32CubeIDEのダウンロードとインストールに関してはWeb上に情報がたくさんありますからここでは触れません。使用したSTM32CubeIDEのバージョンは 1.17.0 です。
- STM32CubeIDEを起動します。
ワークスペースはデフォルトの"C:\Users\<ユーザ名>\STM32CubeIDE\workspace_1.17.0"を使用しましたが、どこでもよいと思います。
STM32CubeIDEを起動後にInformation Centerを閉じるとC/C++ パースペクティブが開きます。もし、既存のプロジェクトが開いていたら閉じておくのが無難です。 - File → New → C/C++ Project をクリックし「Templates for New C/C++ Project」のウィンドウを開きます。ここで「C Managed Build」を選択し「Next」をクリックします。

- 「C Project」のウィンドウで下記を設定し、「Next」をクリックします。
・Project name:LED1
・Project type:Executable - Empty Project ←デフォルトのまま
・Toolchains:MCU ARM GCC - 「Select Configurations」ウィンドウではDebugとReleaseをチェックしたまま、「Next」をクリックします。
- 「Select default target for the project」ウィンドウで、MCUを「STM32H503RBTx」に設定して「Finish」をクリックします。
Project Explorerに「LED1」のプロジェクト(フォルダ)が追加されます。

3. ソースファイル作成
「LED1」のプロジェクト(フォルダ)の下に「src」フォルダを作成し、さらにその下にアセンブリ言語プログラムファイルの「LED1.s」ファイルを作成します。
- Project ExplorerのLED1をクリックして選択しておき、File → New → Folder をクリックして「Folder」ウィンドウを開きます。Folder nameに「src」と入力して「Finish」をクリックします。
Project Explorerに「src」のフォルダが追加されます。
- Project Explorerのsrcをクリックして選択しておき、File → New → Source File をクリックして「Source File」ウィンドウを開きます。下記の設定をして「Finish」をクリックします。
・Souce folder:LED1/src ←デフォルトのまま
・Source File:LED1.s
・Template:<None>
Project Explorerに「LED1.s」のファイルが追加されます。
- STM32CubeIDEの中央のエディタウィンドウにLED1.sが開いているのでソースコードを入力します。入力後、File → Save をクリックして保存します。

入力したアセンブリ言語のソースコードを下記に示します。
NUCLEO-H503RBボードのLD2を1秒周期で点滅します。(0.5秒点灯、0.5秒消灯の繰り返し)// // LED1.s // NUCLEO-H503RBボード用LD2点滅ソフト // .syntax unified .text .global Reset_Handler .equ SP_init,0x20008000 //スタックポインタ初期値 .equ SP_limit,0x20007000 //スタックポインタ下限 .equ RCC,0x44020C00 //RCCレジスタベースアドレス .equ of_AHB2ENR,0x8C //AHB2ENRレジスタ アドレスオフセット .equ v_AHB2ENR,0x40000001 //RCC_AHB2ENR設定値 GPIOAEN=クロック有効 .equ GPIOA,0x42020000 //GPIOAレジスタベースアドレス .equ of_MODER,0x0 //MODERレジスタ アドレスオフセット .equ of_BSRR,0x18 //BSRRレジスタ アドレスオフセット .equ of_BRR,0x28 //BRRレジスタ アドレスオフセット .equ v_A_MODER,0xABFFF7FF //GPIOA_MODER設定値 MODE5=汎用出力 .equ loop500ms,5333333 //Delay_500msのループ回数 // 例外ベクター Vecter_Table: .word SP_init //初期スタックポインタ .word Reset_Handler //初期プログラムカウンタ .word dummy_Handler //NMI_Handler .word dummy_Handler //HardFault_Handler .word dummy_Handler //MemManageFault_Handler .word dummy_Handler //BusFault_Hamdler .word dummy_Handler //UsageFault_Handler .word dummy_Handler //予約 .word dummy_Handler //予約 .word dummy_Handler //予約 .word dummy_Handler //予約 .word dummy_Handler //SVCall_Handler .word dummy_Handler //DebugMonitor_Handler .word dummy_Handler //予約 .word dummy_Handler //PendSV_Handler .word dummy_Handler //SysTick_Handler // リセットハンドラー .type Reset_Handler, %function Reset_Handler: MOVW R0,#:lower16:SP_limit //スタックポインタ下限 MOVT R0,#:upper16:SP_limit MSR MSPLIM,R0 //スタック下限値設定 B Main // ダミーハンドラー .type dummy_Handler, %function dummy_Handler: B . //無限ループ // メイン処理 Main: //IO初期設定 BL IO_init //GPIOAレジスタベースアドレス、PA5ビット設定 MOVW R0,#:lower16:GPIOA //GPIOAレジスタベースアドレス MOVT R0,#:upper16:GPIOA MOVS R1,#0x00000020 //bit5=1 loop1: //LD2点灯 STR R1,[R0,#of_BSRR] //GPIOA_BSRR.BS5=1 //500ms待つ BL Delay_500ms //LD2消灯 STR R1,[R0,#of_BRR] //GPIOA_BRR.BR5=1 //500ms待つ BL Delay_500ms //繰り返し B loop1 // Delay 500ms Delay_500ms: PUSH {R0} //loop500ms回ループを回す MOVW R0,#:lower16:loop500ms //Delay_500msのループ回数 MOVT R0,#:upper16:loop500ms .align 4 //ループの先頭アドレスを16Byte境界に置く loop2: SUBS R0,#1 BNE loop2 //サブルーチンから復帰 POP {R0} BX LR // IO初期設定 IO_init: PUSH {R0,R1} //GPIOAクロック有効化 MOVW R0,#:lower16:RCC //RCCレジスタベースアドレス MOVT R0,#:upper16:RCC MOVW R1,#:lower16:v_AHB2ENR //RCC_AHB2ENR設定値 MOVT R1,#:upper16:v_AHB2ENR STR R1,[R0,#of_AHB2ENR] //RCC_AHB2ENR.GPIOAEN=有効 //PA5 汎用出力モード MOVW R0,#:lower16:GPIOA //GPIOAレジスタベースアドレス MOVT R0,#:upper16:GPIOA MOVW R1,#:lower16:v_A_MODER //GPIOA_MODER設定値 MOVT R1,#:upper16:v_A_MODER STR R1,[R0,#of_MODER] //GPIOA_MODER.MODE5=汎用出力 //サブルーチンから復帰 POP {R0,R1} BX LR // プログラムエンド .endソースコードの説明の記事を追加しました。2025/03/04
4. リンカースクリプトファイル作成
「LED1」のプロジェクト(フォルダ)の下にリンカースクリプトファイルの「LinkerScript.ld」を作成します。
- Project ExplorerのLED1をクリックして選択しておき、File → New → Other... をクリックして「Select a wizard」ウィンドウを開きます。General → File を選択して「Next」をクリックします。
- 「File」ウィンドウでFile nameに「LinkerScript.ld」を設定をして、「Finish」をクリックします。
Project Explorerに「LinkerScript.ld」のファイルが追加されます。
LinkerScript.ldファイルはLED1の直下であることを確認しておきます。もし、srcの下だったら削除して作り直します。
- エディタウィンドウにLinkerScript.ldが開いているのでリンカースクリプトを入力します。入力後、File → Save をクリックして保存します。
入力したリンカースクリプトを下記に示します。
エントリーポイントとプログラムをロードするアドレスを設定しています。
ENTRY (Reset_Handler) MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x20000 } SECTIONS { .text : { KEEP( * ( .text ) ) } >FLASH }リンカースクリプトの説明の記事を追加しました。2025/03/04
5. ビルド設定
リンク時に不要なコードの追加を止めるために標準のシステム スタートアップ ファイルやライブラリの使用を止める設定を行います。
- Project ExplorerのLED1をクリックして選択しておき、File → Properties をクリックして「Properties for LED1」ウィンドウを開きます。C/C++ Build → Settings をクリックし、Tool SettingsタブのMCU/MPU GCC Linker のGeneralをクリックして下記にチェックを入れます。そして「Apply and Close」をクリックします。
・No startup or default libs(-nostdlib)
6. ビルド実行
ソースファイルからターゲットボードにダウンロードするオブジェクトファイルを作成します。
- Project ExplorerのLED1を右クリックしてメニューを開き、「Build Project」をクリックします。
DebugフォルダがLED1の下に作られて、オブジェクトファイルのLED1.elf、逆アセンブルリストのLED1.listなどが作られます。
- LED1.listファイルを開いて見ると、プログラムはリンカースクリプトファイルで指定した通り0x0800 0000番地からロードされていることが分かります。

7. デバッグ設定
下記の2つの設定を行います。
- Project ExplorerのLED1を右クリックしてメニューを開き、Debug As → Debug Configurations... をクリックして「Create, marge, and run configurations」ウィンドウを開きます。
STM32 C/C++ Application を選択してNew Configurationボタン
を押します。STM32 C/C++ Applicationの下にLED1 Debugが作られます。
- Mainタブはデフォルトのままで変更なしです。
- デバッガタブで下記の設定を変更します。
シリアル ワイヤ ビューア(SWV)
・有効にチェックを入れる
・Core Clock(MHz):32 - Startupタブで下記の設定を変更します。
Runtime Options
・Start AddressのSpecify vector tableにチェックを入れ、「8000000」を記入
・Set breakpoint at:のチェックを外す
・Resumeのチェックを外す - Sourceタブ、Commonタブはデフォルトのままで変更なしです。
- 「Apply」をクリックして、「Close」をクリックします。
8. プログラム実行
ターゲットボードにプログラムをダウンロードして実行します。
- NUCLEO-H503RBボードのコネクタCN1:ST-LINK USB Type-Cコネクタとパソコンをつなぎます。パソコンのエクスプローラで「NOD_H503RB」がドライブに接続していることがわかります。
- Project ExplorerのLED1を右クリックしてメニューを開き、Debug As → 2 STM32 C/C++ Application をクリックしてデバッガーを起動します。
内蔵FlashROMへプログラムがダウンロードされます。ボードのLD1が赤と緑の表示を繰り返していてST-Linkが通信中であることを示しています。デバッガーの起動中はいつも赤と緑の表示を繰り返しています。
パースペクティブの切り替えの確認が表示されたら「Switch」をクリックしてデバッグ パースペクティブに切り替えます。 - ツールバーの Resume ボタン
をクリックするとプログラムの実行が始まり、LD2が1秒周期で点滅を繰り返します。
そして、ツールバーの Suspend ボタン
をクリックするとプログラムが停止します。
また、ツールバーの Terminate ボタン
をクリックするとデバッグが終了します。
9. デバッグ
CPUの動作を確認するために使うデバッガーの機能の内、ここでは下記の3つを行います。
- CPUのレジスタ値の確認
- FlashROMに書き込まれたプログラムの確認
- トレースを使った点滅間隔の確認
9.1. CPUのレジスタ値を確認
- デバッガーが起動した状態でしたら、一旦ツールバーの Terminateボタン
をクリックしてデバッグを終了します。そして、「8. プログラム実行 2.」の手順で再度デバッガーを起動してデバッグ パースペクティブに切り替えます。この状態はリセットが解除後でプログラムが停止しています。 - Window → Show View → Registers をクリックすると右上のウィンドウにRegistersビューが表示され、プログラムが停止した状態でCPUの一般レジスタが表示されます。数値に10進数表記が混ざっていますが、Registersビューのメニューアイコン(
)をクリックして Number Format → Hex をクリックすると全部16進数表記になります。
・PCは0x0800 0040になっていて、リセットベクターの値で停止しています。
・SPは0x2000 8000になっていて、初期値が設定されています。
9.2. FlashROMに書き込まれたプログラムを確認
- Window → Show View → Memory をクリックすると下部のウィンドウにMemoryビューが表示されます。プログラムが停止した状態でAdd Memory Monitorボタン
をクリックし、モニターするアドレスとして0x08000000を設定して「OK」をクリックします。 - 0x0800 0000番地からのFlashROMの値が表示されますがレンダリングが「Hex」でビッグエンディアンの表示になっています。

- Cortex-M33はリトルエンディアンなのでNew Renderings...タブで「Hex Integer」を選択して「Add Rendering(s)」をクリックするとリトルエンディアンで表示されたタブが追加されます。

<Hex Integer>のタブでは0x0800 0000番地の値は0x2000 8000です。LED1.listに示されているベクターテーブルの値と同じで正しくプログラムがFlashROMに書き込まれていることがわかります。
9.3. トレース機能を使い、LD2を点灯している時間と消灯している時間を確認
- Window → Show View → SWV → SWV トレースログ をクリックすると中央下部にSWV トレースログ ビューが表示されます。
- プログラムが停止した状態でSWV トレースログ ビューのトレースの設定ボタン
をクリックして、Serial Wire Viewer 設定のウィンドウを開き下記の設定を行い「OK」をクリックします。ここで、Comparater 0はLD2を点灯する書込み、Comparater 1はLD2を消灯する書込みを取り込む設定です。
データトレースの設定値 項目 Comparater 0 Comparater 1 許可する チェックを入れる 同左 変数名/アドレス 0x42020018 0x42020028 アクセス種別 Write 同左 サイズ Word 同左 生成 Data 同左 - SWV トレースログ ビューのStart Traceボタン
をクリックして、トレースを開始します。
ツールバーのReset the chip and restart debug sessionボタン
をクリックするとプログラムが最初から実行します。2、3回LD2が点滅したら、ツールバーのSuspendボタン
をクリックしてプログラムを停止します。
SWV トレースログ ビューでcomp0とcomp1の時間の差を見ると間隔が0.5sであることがわかります。(comp0がLD2を点灯する書込み動作、comp1がLD2を消灯する書き込み動作)
10. 参考資料
| インストールガイド | UM2563: STM32CubeIDE installation guide |
| ユーザーマニュアル | UM2609: STM32CubeIDE ユーザ・ガイド |
| アセンブラ ユーザーガイド | The GNU Assembler Using as |
| リンカー ユーザーガイド | The GNU linker Using ld |
| コンパイラ ユーザーガイド | Using the GNU Compiler Collection |
| データシート | DB2196: STM32 Nucleo-64 boards |
| 回路図 | MB1814-H503RB-C01 Board schematic |
| ユーザーマニュアル | UM3121: STM32H5 Nucleo-64 board (MB1814) |
| データシート | DS14053: STM32H503xx |
| リファレンスマニュアル | RM0492: STM32H503 line Arm®-based 32-bit MCUs |
| プログラミングマニュアル | PM0264: STM32 Cortex®-M33 MCUs programming manual |
| テクニカルリファレンスマニュアル | 100230_0100_03_en: Arm Cortex-M33 Processor Technical Reference Manual |
| アーキテクチャリファレンスマニュアル | DDI0553B.y: Armv8-M Architecture Reference Manual |
| Todotaniのはやり物Log:FM3マイコンをOpenOCD + Eclipseでデバッグ | https://todotani.cocolog-nifty.com/blog/2012/05/fm3openocd-ecli.html |