30代エンジニアの備忘録のカバー画像

30代エンジニアの備忘録

組み込み開発・信号処理・プログラミングの技術メモと短編小説

STM32マイコン入門|GPIO制御・タイマー・割り込みの基礎をHALライブラリで学ぶ

2026年5月時点、ARM Cortex-M 系の STM32 を題材に、HAL を使った GPIO・タイマー・割込コールバックの読みどころを整理します。組み込み初学者や HAL から触る人向けで、シリーズ/型番ごとにクロックやバスが違う前提で、最終確認は STM32CubeMX とリファレンスマニュアルに寄せます。

読み進める前提:クロックツリーが一度、データシートと実機の挙動が一致するところまで通っていること。確認のコツは、UART か SWO で周期・デューティ・GPIO トグルを数値ログで1本取ること。うまくいかない典型は、クロックゲート未解放のまま GPIO だけ触る、ISR に処理を詰め込みすぎる、最適化でウォッチが嘘をつく、などです。

メモリ配置の考え方は [メモリマップ入門|組み込みエンジニアが知るべきメモリ配置とリンカスクリプト](/posts/memory-map-explained/) と通じますが、ベクタ表・リンカシンボルは教材より Cube/BSP 生成物を正にしてください。

STM32のシリーズは何を基準に選べばいい?

STM32 は ST の ARM コア MCU ファミリーで、シリーズごとに性能と周辺 IP が異なります(数値はデータシートで必ず確認)。

  • STM32F0/F1(Cortex-M0/M3):エントリー向け。実効クロック〜数十MHz帯など低〜中性能の構成が多い
  • STM32F4(Cortex-M4):単精度FPU を持つ構成が多く、PWM や処理余力のある処理向き。最大180MHz級などデータシートの範囲は型番依存
  • STM32H7(Cortex-M7):高クロック構成が多く、画像や通信処理の親機向きにも使われる
  • STM32L4(Cortex-M4):抑えめ消費電力寄り。性能と消費のトレードオフはブロック設計依存

開発に最低限そろえるものは?

  • STM32CubeMX:GUI でペリフェラル設定と HAL コード生成を行う無料ツール。ピン配置やクロック設定をビジュアルに行える(公式)
  • STM32CubeIDE:Eclipse ベースの統合開発環境(無料)。CubeMX 統合済み
  • ST-Link:デバッガ/プログラマ。Nucleoボードには搭載済み

GPIO を触る前に何を必ずする?

GPIO は LED やボタンなど外部との入出力口です。STM32 では触る前に必ずクロック有効化が必要、という順番を忘れがちです。

/* GPIO初期化: PA5を出力(プッシュプル)として設定 */
void MX_GPIO_Init(void) {
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  __HAL_RCC_GPIOA_CLK_ENABLE();  // クロック有効化が必須

  GPIO_InitStruct.Pin = GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

/* LED制御 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);    // 点灯
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);  // 消灯
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);                  // 反転
GPIOモード一覧
  • OUTPUT_PP(プッシュプル):LED駆動など一般用途
  • OUTPUT_OD(オープンドレイン):I2Cバスなどプルアップ必要な用途
  • INPUT:ボタン読み取り等のデジタル入力
  • IT_RISING / IT_FALLING:エッジ検出で割り込みを発生
  • AF_PP(オルタネートファンクション):UART/SPI/I2C等のペリフェラル接続用

タイマ周期と PWM の数値はどこで確かめる?

タイマーは時間計測・PWM・イベントカウントに使います。数値の当てはめは次の式と CubeMX のクロック表示をセットで見てください。

周期や PWM の数値は (Prescaler+1)×(Period+1) と、タイマに供給される実クロック(APB プリスケーラ等を踏まえた値)で決まります。下の数字は読みどころ用の一例であり、自分の MCU では CubeMX のクロックツリーで確認してください。

周期 = (Prescaler + 1) × (Period + 1) / タイマークロック

例: 1秒間隔(タイマークロック 84MHz)
  Prescaler = 83       → 84MHz / 84 = 1MHz
  Period    = 999999   → 1MHz / 1000000 = 1Hz(1秒)

例: 1kHz PWM(デューティ比50%)
  Prescaler = 83       → 1MHz
  Period    = 999      → 1kHz
  Pulse     = 500      → 50% duty
PWM出力のコード例(数値もプロジェクト依存)
htim3.Instance = TIM3;
htim3.Init.Prescaler = 84 - 1;     // 1MHz
htim3.Init.Period = 1000 - 1;      // 1kHz PWM
HAL_TIM_PWM_Init(&htim3);

sConfigOC.Pulse = 500;  // 50% duty
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);

割り込みに書く処理の線引きは?

割り込みは、外部イベントやタイマーによって通常の処理を一時中断し、緊急度の高い処理を実行する仕組みです。STM32ではHALのコールバック関数で割り込み処理を記述します。

/* タイマー割り込みコールバック */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
  if (htim->Instance == TIM2) {
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);  // LED反転
  }
}

/* GPIO外部割り込みコールバック */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
  if (GPIO_Pin == GPIO_PIN_13) {
    // ボタン押下時の処理
  }
}
💡
NVICの優先度設定に注意してください。数値が小さいほど優先度が高く、ISR内で長時間処理を行うとシステム全体のリアルタイム性が低下します。ISRではフラグのセットだけにとどめ、実処理はメインループで行うのが基本設計です。

まずどこにログを出して切り分ける?

  • UART経由のprintf:HAL_UART_Transmit()で変数値やステータスを確認
  • SWV(Serial Wire Viewer):ITM_SendChar()でprintf出力をSWO経由でリアルタイム表示
  • ブレークポイント:デバッガでウォッチ(最適化 -O2 では見え方が変わることも)

まとめ:持ち帰りチェック(CubeMX とデータシートを正に)

  • STM32 は CubeMX でクロックとバスを固めてから HAL の数字を信じるのが安全
  • GPIO は クロックゲート有効化が手順の最初
  • タイマーは Prescaler×Period と 実効タイマクロックのセットで読む
  • ISR は短く、重い処理はメインやタスクへ