# STM32マイコンのUART・I2C・SPI・CAN通信 実装ガイド｜株式会社テクノスフィア

> STM32マイコンのペリフェラル設定から実装まで解説。UART/I2C/SPI/CAN通信の使い分けとHALライブラリを使ったC言語サンプルコードを掲載。組込み開発歴20年超のエンジニアが在籍するテクノスフィアが解説。

URL: https://technosphere.co.jp/blog/stm32-uart-i2c-can

** 目次
1. はじめに：STM32が産業機器に選ばれる理由
2. STM32CubeMXによるペリフェラル設定
3. UART通信の実装
4. I2C通信の実装
5. SPI通信の実装
6. CAN通信の実装
7. 通信方式の使い分けガイド
8. よくあるトラブルと対策
9. まとめ

## はじめに：STM32が産業機器に選ばれる理由

 STMicroelectronics（ST社）の **STM32シリーズ**は、産業機器・医療機器・車載システム・IoT機器など、あらゆる組込みシステムで広く採用されているARM Cortex-Mベースのマイコンです。弊社でも多数のSTM32を用いた[組込み受託開発実績](https://technosphere.co.jp/embedded-achievements)があります。

STM32が選ばれる主な理由を以下にまとめます。
- **豊富なペリフェラル：**UART・I2C・SPI・CAN・USB・Ethernet・ADC・DAC・TIMERなどをワンチップに搭載
- **HALライブラリ：**ST公式のHardware Abstraction Layerにより、ハードウェアを意識せずに開発できる
- **STM32CubeMX：**GUIでピン割り当てや初期化コードを自動生成。開発効率が大幅に向上
- **コストパフォーマンス：**高性能ながら低コスト。量産時のコスト最適化がしやすい
- **エコシステムの充実：**ST-LINK/V2などのデバッガ、Keil MDK・IAR・STM32CubeIDE などの開発環境が揃っている
  ** 弊社での活用実績

弊社では STM32F4・STM32G4・STM32H7 シリーズを中心に、センサーデータ収集装置・モーター制御・産業用通信ゲートウェイなど多数の組込み開発を手がけています。本記事はその実案件で得たノウハウをもとに作成しています。

## STM32CubeMXによるペリフェラル設定

 STM32CubeMXはST公式の無償ツールで、GUIでマイコンのピン配置・クロック設定・ペリフェラル設定を行い、HAL初期化コードを自動生成できます。従来はレジスタを直接叩いていた初期化作業が大幅に短縮されます。

### 基本的な使い方
1. 使用するSTM32マイコンを選択（例：STM32F446RE）
2. 「Pinout & Configuration」タブでペリフェラルを有効化（例：USART2 → Asynchronous）
3. ピンアサインを確認・変更（コンフリクトがあれば自動警告）
4. 「Clock Configuration」タブでシステムクロックを設定
5. 「Project Manager」でプロジェクト名・IDEを選択（STM32CubeIDE / Keil / IAR）
6. 「Generate Code」でHAL初期化コードを自動生成

**生成されるファイル構成（STM32CubeIDE の場合）**

```
MyProject/
├── Core/
│   ├── Inc/
│   │   ├── main.h          # ペリフェラルのdefine定義
│   │   ├── usart.h
│   │   └── i2c.h
│   └── Src/
│       ├── main.c          # メイン処理（ユーザーコードをここに追加）
│       ├── usart.c         # UART初期化コード（自動生成）
│       └── i2c.c           # I2C初期化コード（自動生成）
└── Drivers/
    ├── STM32F4xx_HAL_Driver/  # HALライブラリ本体
    └── CMSIS/
```

   ** 注意点

CubeMXで再生成すると、`/* USER CODE BEGIN */`〜`/* USER CODE END */` の間に書いたコードは保持されますが、それ以外の箇所に直接書いたコードは上書きされます。必ずユーザーコード領域に記述してください。

## UART通信の実装

 UART（Universal Asynchronous Receiver/Transmitter）は、シリアル通信の中でも最もシンプルな方式です。デバッグ出力・PCとの通信・GPSモジュール・Bluetoothモジュール・各種センサーとの通信に広く使われます。

### 基本送受信（ポーリング方式）

最もシンプルな実装です。送信完了または受信完了まで CPU がブロックされます。

```
/* UART送信（ポーリング）*/
char tx_buf[] = "Hello STM32!\r\n";
HAL_UART_Transmit(&huart2, (uint8_t*)tx_buf, strlen(tx_buf), HAL_MAX_DELAY);

/* UART受信（ポーリング）*/
uint8_t rx_buf[64];
HAL_StatusTypeDef ret = HAL_UART_Receive(&huart2, rx_buf, 10, 1000); // 10byte, 1000msタイムアウト
if (ret == HAL_OK) {
    // 受信成功
}
```

### 割り込み方式（推奨）

実案件では受信タイミングが不定なため、割り込み方式を使います。CPUをブロックせず効率的に処理できます。

```
/* main.c – 受信割り込み開始 */
uint8_t rx_byte;
HAL_UART_Receive_IT(&huart2, &rx_byte, 1);  // 1バイト受信割り込みを有効化

/* stm32f4xx_it.c または main.c に追加 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART2) {
        // rx_byte に受信データが入っている
        process_received_byte(rx_byte);

        // 次の受信に備えて再度セット
        HAL_UART_Receive_IT(&huart2, &rx_byte, 1);
    }
}

/* DMA方式（大量データの高速受信）*/
uint8_t dma_rx_buf[256];
HAL_UART_Receive_DMA(&huart2, dma_rx_buf, 256);
// 256バイト受信完了でHAL_UART_RxCpltCallbackが呼ばれる
```

### printfのリダイレクト（デバッグ用）

標準のprintfをUART経由でPCのターミナルに出力するよう設定できます。デバッグ作業が大幅に楽になります。

```
/* syscalls.c または main.c に追加 */
#include <stdio.h>

int __io_putchar(int ch)
{
    HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, HAL_MAX_DELAY);
    return ch;
}

/* 使用例 */
printf("温度: %.1f ℃, 電圧: %.3f V\r\n", temp, voltage);
```

## I2C通信の実装

 I2C（Inter-Integrated Circuit）は、SDA（データ）とSCL（クロック）の2線式シリアル通信です。センサー・EEPROM・RTC・LCD・DAC/ADCなど、低〜中速のデータ転送が必要なデバイスとの通信に適しています。7bitアドレスでは予約アドレスを除いた最大112台のデバイスを同一バスに接続できます。

### メモリ書き込み・読み出し（EEPROMの例）

```
#define EEPROM_ADDR  0xA0  // 7bitアドレス 0x50 を左シフト → 0xA0

/* EEPROMへの書き込み（HAL_I2C_Mem_Write） */
uint8_t write_data[4] = {0x01, 0x02, 0x03, 0x04};
HAL_StatusTypeDef ret = HAL_I2C_Mem_Write(
    &hi2c1,          // I2Cハンドル
    EEPROM_ADDR,     // デバイスアドレス
    0x0000,          // メモリアドレス（書き込み先）
    I2C_MEMADD_SIZE_16BIT, // メモリアドレスサイズ
    write_data,      // 送信データ
    4,               // データ長
    HAL_MAX_DELAY
);

/* EEPROMからの読み出し（HAL_I2C_Mem_Read） */
uint8_t read_data[4];
HAL_I2C_Mem_Read(
    &hi2c1,
    EEPROM_ADDR,
    0x0000,
    I2C_MEMADD_SIZE_16BIT,
    read_data,
    4,
    HAL_MAX_DELAY
);
```

### 汎用デバイスの読み書き（レジスタアクセス）

```
/* センサーのレジスタ1バイトを読み出す */
uint8_t reg_addr = 0x75;  // WHO_AM_I レジスタ（例：MPU-6050）
uint8_t reg_data;

HAL_I2C_Master_Transmit(&hi2c1, 0xD0, ®_addr, 1, HAL_MAX_DELAY);  // レジスタアドレス送信
HAL_I2C_Master_Receive (&hi2c1, 0xD1, ®_data, 1, HAL_MAX_DELAY);  // データ受信

/* あるいは Mem_Read で簡潔に書ける */
HAL_I2C_Mem_Read(&hi2c1, 0xD0, 0x75, I2C_MEMADD_SIZE_8BIT, ®_data, 1, HAL_MAX_DELAY);
```

   ** 実案件でのポイント

STM32のI2C実装では**クロックストレッチ問題**に注意が必要です。一部のセンサーはクロックストレッチを使用するため、STM32F1系ではドライバのバグが報告されています。STM32F4以降では改善されていますが、センサーのデータシートを必ず確認してください。プルアップ抵抗は4.7kΩを推奨します。

## SPI通信の実装

 SPI（Serial Peripheral Interface）は、MOSI・MISO・SCLK・NSS（CS）の4線式フルデュプレックス通信です。I2Cより高速（最大数十MHz）で、高サンプリングレートが必要なADC・加速度センサー・フラッシュメモリ・ディスプレイドライバなどに適しています。

### フラッシュメモリ（W25Qシリーズ）への読み書き

```
#define FLASH_CS_PORT  GPIOA
#define FLASH_CS_PIN   GPIO_PIN_4

/* CS をアサート（Low）してSPI送受信 */
static void SPI_CS_Select(void)   { HAL_GPIO_WritePin(FLASH_CS_PORT, FLASH_CS_PIN, GPIO_PIN_RESET); }
static void SPI_CS_Deselect(void) { HAL_GPIO_WritePin(FLASH_CS_PORT, FLASH_CS_PIN, GPIO_PIN_SET); }

/* Manufacture/Device IDの読み出し（JEDEC ID: 0x9F） */
uint8_t tx[4] = {0x9F, 0x00, 0x00, 0x00};
uint8_t rx[4];

SPI_CS_Select();
HAL_SPI_TransmitReceive(&hspi1, tx, rx, 4, HAL_MAX_DELAY);
SPI_CS_Deselect();

// rx[1]:Manufacturer ID, rx[2]:Memory Type, rx[3]:Capacity
// W25Q64: 0xEF, 0x40, 0x17

/* ページ書き込み（256バイト単位） */
void Flash_PageWrite(uint32_t addr, uint8_t *data, uint16_t len)
{
    uint8_t cmd[4];
    cmd[0] = 0x02;            // Page Program コマンド
    cmd[1] = (addr >> 16) & 0xFF;
    cmd[2] = (addr >>  8) & 0xFF;
    cmd[3] = (addr      ) & 0xFF;

    SPI_CS_Select();
    HAL_SPI_Transmit(&hspi1, cmd, 4, HAL_MAX_DELAY);   // コマンド＋アドレス送信
    HAL_SPI_Transmit(&hspi1, data, len, HAL_MAX_DELAY); // データ送信
    SPI_CS_Deselect();
}
```

### DMAによる高速SPI転送

ADCデータの高速収集やディスプレイのフレームバッファ転送など、大量データを扱う場合はDMAを使ってCPUをフリーにします。

```
/* DMA送信（ノンブロッキング）*/
HAL_SPI_Transmit_DMA(&hspi1, frame_buffer, FRAME_SIZE);

/* 転送完了コールバック */
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
    if (hspi->Instance == SPI1) {
        SPI_CS_Deselect();
        // 次のフレームの準備など
    }
}
```

## CAN通信の実装

 CAN（Controller Area Network）は、自動車・産業機器・医療機器などで標準的に使われる堅牢な差動通信プロトコルです。マルチマスター方式でノイズ耐性が高く、**長距離・高ノイズ環境での多ノード通信**に最適です。STM32のCANコントローラはシリーズによって2種類あり、**STM32F1/F2/F4/F7/L4 などは bxCAN**、**STM32G0/G4/H7/L5/U5 などは FDCAN（CAN FD対応）**を内蔵します。本記事のコードは bxCAN（HAL_CAN_*）を例にしています。FDCAN搭載品では `HAL_FDCAN_*` 系のAPI（`FDCAN_HandleTypeDef` / `HAL_FDCAN_AddMessageToTxFifoQ` 等）を使用し、データ構造も異なる点にご注意ください。

### CAN送信の実装（bxCAN）

```
/* CANの初期化（CubeMXで設定後、手動で有効化）*/
HAL_CAN_Start(&hcan1);
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING); // 受信割り込み有効化

/* CAN送信 */
CAN_TxHeaderTypeDef tx_header;
uint8_t tx_data[8];
uint32_t tx_mailbox;

tx_header.StdId = 0x123;         // 標準IDフレーム（11bit ID）
tx_header.ExtId = 0;
tx_header.IDE   = CAN_ID_STD;
tx_header.RTR   = CAN_RTR_DATA;  // データフレーム
tx_header.DLC   = 8;             // データ長（最大8バイト）
tx_header.TransmitGlobalTime = DISABLE;

// 送信データのセット
tx_data[0] = 0x01;
tx_data[1] = 0x02;
tx_data[2] = (uint8_t)(temperature * 10);  // 温度×10（小数点1桁）
// ...

HAL_CAN_AddTxMessage(&hcan1, &tx_header, tx_data, &tx_mailbox);
```

### CAN受信（割り込み方式）

```
/* 受信割り込みコールバック */
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    CAN_RxHeaderTypeDef rx_header;
    uint8_t rx_data[8];

    if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data) == HAL_OK) {
        // 受信IDで処理を振り分け
        switch (rx_header.StdId) {
            case 0x100:
                handle_motor_command(rx_data);
                break;
            case 0x200:
                handle_sensor_data(rx_data);
                break;
            default:
                break;
        }
    }
}
```

### CANフィルタ設定（受信IDのフィルタリング）

CANバス上の全フレームをすべて受信すると処理負荷が増大します。受信したいIDだけを通過させるフィルタを設定します。

```
/* IDリストモード（特定のIDのみ受信） */
CAN_FilterTypeDef filter;

filter.FilterBank           = 0;
filter.FilterMode           = CAN_FILTERMODE_IDLIST;  // リストモード
filter.FilterScale          = CAN_FILTERSCALE_16BIT;
filter.FilterIdHigh         = 0x100 << 5;  // 受信するID1
filter.FilterIdLow          = 0x200 << 5;  // 受信するID2
filter.FilterMaskIdHigh     = 0x300 << 5;  // 受信するID3（マスクモード時はマスク値）
filter.FilterMaskIdLow      = 0x400 << 5;  // 受信するID4
filter.FilterFIFOAssignment = CAN_RX_FIFO0;
filter.FilterActivation     = ENABLE;

HAL_CAN_ConfigFilter(&hcan1, &filter);
```

   ** 産業現場での活用例

弊社では複数の製造設備をCAN接続したシステムを開発した実績があります。PLCとSTM32間のCAN通信で設備状態をリアルタイム収集し、生産管理システムへ転送するゲートウェイ装置です。CANの堅牢性により、工場の電気ノイズ環境でも安定稼働しています。重機向けCAN通信や産業IoTを含む事例は [組込み開発実績ページ](https://technosphere.co.jp/embedded-achievements) でご紹介しています。

## 通信方式の使い分けガイド

実案件では複数の通信方式を組み合わせるケースがほとんどです。それぞれの特徴を理解して適切に選択することが重要です。
|  方式 |  速度 |  接続数 |  線数 |  主な用途 |
|  **UART** |  最大数Mbps |  1:1 |  2線（TX/RX） |  PCデバッグ、GPS、Bluetooth、各種モジュール |
|  **I2C** |  100k / 400k / 1Mbps |  1:112 |  2線（SDA/SCL） |  センサー、EEPROM、RTC、LCD（近距離・多デバイス） |
|  **SPI** |  数十Mbps |  1:N（CS切替） |  4線＋CS |  ADC、フラッシュ、ディスプレイ（高速・高精度） |
|  **CAN** |  最大1Mbps |  マルチマスター |  差動2線 |  車載・産業機器・長距離・高ノイズ環境 |
|  **Ethernet** |  100M / 1Gbps |  多数 |  4線〜 |  クラウド連携・大量データ転送・Web通信 |
|  **Bluetooth（BLE）** |  最大2Mbps |  多数 |  無線 |  スマホ連携・ウェアラブル・IoT端末 |       ** 典型的な組み合わせ例
- **センサー収集 + クラウド送信：**I2C/SPI（センサー） + UART（デバッグ） + Ethernet（クラウド）
- **産業用制御装置：**CAN（設備間通信） + SPI（ADC） + I2C（表示器） + UART（設定用）
- **ウェアラブル機器：**I2C（センサー） + BLE（スマホ連携） + SPI（フラッシュログ）

## よくあるトラブルと対策
| 症状 | 原因 | 対策 |
|  UARTでデータ化け |  ボーレート不一致 / ノイズ |  両デバイスのボーレートを再確認。過誤率（Error Rate）を計算して許容範囲内か確認。ケーブルを短くしシールド線を使用 |
|  I2CがHAL_BUSYを返す |  バスロックアップ（前回通信が異常終了） |  `HAL_I2C_DeInit(&hi2c1); HAL_I2C_Init(&hi2c1);` でリセット。またはGPIOをトグルしてSCLに9クロック印加してスレーブをリセット |
|  SPIで受信データが0xFF / 0x00になる |  CS信号のタイミングミス / SPIモード不一致 |  データシートでSPIモード（CPOL/CPHA）を確認。NSS管理をソフトウェア制御（SSM=1）に変更して手動でCS操作 |
|  CANがACKエラーを出す |  終端抵抗なし / ノード数1台 |  CANバスの両端に120Ω終端抵抗を必ず接続。テスト時は最低2ノード必要（ループバックモードで単体テスト可能） |
|  DMA転送が途中で止まる |  DMAストリームの競合 / バッファオーバーフロー |  CubeMXでDMAストリームの競合がないか確認。ダブルバッファモードを使用して連続転送を安定化 |       ** デバッグツールの活用
- **ロジックアナライザ（例：Saleae Logic）：**UART/I2C/SPIの信号を可視化。プロトコルデコード機能でデータ内容も確認できる
- **オシロスコープ：**信号の波形品質（立ち上がり・ノイズ）を確認
- **ST-LINK/V2 + STM32CubeIDE：**ブレークポイント・変数ウォッチでリアルタイムデバッグ
- **CAN Analyzer（例：PEAK PCAN-USB）：**CANバスのトラフィックをモニタリング

## まとめ

###  この記事のまとめ
- STM32はUART・I2C・SPI・CANを全搭載し、産業機器から民生品まで幅広く対応できる
- STM32CubeMXでペリフェラルの初期化コードを自動生成し、開発工数を大幅に削減できる
- UART：デバッグ・外部モジュール接続、I2C：センサー・多デバイス接続、SPI：高速ADC・フラッシュ、CAN：産業・車載の多ノード通信 と使い分ける
- 本番運用では割り込み・DMA方式を活用してCPUの処理効率を上げる
- ロジックアナライザとオシロスコープを使ったハードウェアデバッグが通信トラブル解決の近道

STM32を使った組込みシステム開発・通信設計でお困りのことがあれば、弊社にお気軽にご相談ください。要件定義から設計・実装・量産対応まで、一貫してサポートします。

STM32 を使った UART/I2C/SPI/CAN 通信機器の試作・量産設計、産業 IoT ゲートウェイ開発、既存基板からの移行や量産時の通信トラブル調査まで、[組込み制御・マイコン開発](https://technosphere.co.jp/embedded) として受託対応しています。   ／この内容の受託開発・PoC相談はこちら＼

### STM32 通信ペリフェラル実装・産業IoTの受託開発

UART・I2C・SPI・CAN を組み合わせた産業機器・医療機器・車載・IoTゲートウェイの設計と量産対応まで、テクノスフィアにご相談ください。要件定義から PoC・量産まで一貫対応します。  [組込み開発サービスを見る](https://technosphere.co.jp/embedded) [UART・CAN通信トラブル相談](https://technosphere.co.jp/contact)

## よくある質問（FAQ）   ** STM32のUART・I2C・SPI・CANはどう使い分ければよいですか？

UARTはデバッグ出力やGPS・無線モジュールなど1対1のシリアル接続、I2Cは2線でセンサーやEEPROMを複数ぶら下げる用途、SPIは高速ADCやフラッシュ・LCDなど高スループットが必要な近距離接続、CANは産業機器・車載で複数ノードをノイズに強く接続する用途に向いています。   ** STM32CubeMXを使うとUART/I2C/SPI/CANの実装はどのくらい楽になりますか？

STM32CubeMXはピン割り当て・クロック設定・ペリフェラル初期化のCコードを自動生成するため、HALベースの初期化を手書きする工数を大幅に削減できます。生成後は `HAL_UART_Transmit()` などの送受信APIを呼ぶだけで通信を開始できます。   ** 通信が動かないときはどこを確認すればよいですか？

ボーレート／クロック設定の不一致、I2Cのプルアップ抵抗の有無、SPIのモード（CPOL/CPHA）違い、CANのビットレートと終端抵抗120Ωを確認します。ロジックアナライザやオシロスコープで実波形を観測するのが解決の近道です。   ** 本番運用ではポーリングと割り込み・DMAのどちらを使うべきですか？

CPU負荷を抑え取りこぼしを防ぐため、本番では割り込みまたはDMA方式を推奨します。特に高速・大容量転送や、複数処理を並行させる場合はDMAを使うとCPUを解放でき、リアルタイム性が向上します。詳しくは [STM32 DMA 完全ガイド](https://technosphere.co.jp/blog/stm32-dma-complete-guide) を参照してください。      # STM32 # UART # I2C # SPI # CAN通信 # マイコン開発 # 組込み制御 # HAL

##   次に読む｜STM32 シリーズ記事
-  [STM32 シリーズ STM32 + FreeRTOS 入門 タスク・キュー・セマフォを使ったRTOS実装ガイド](https://technosphere.co.jp/blog/stm32-freertos-intro)
-  [STM32 シリーズ STM32 DMA 完全ガイド｜UART・SPI・ADCとの連携 CPU負荷を最小化するDMA活用の決定版](https://technosphere.co.jp/blog/stm32-dma-complete-guide)
-  [STM32 シリーズ STM32 低消費電力モード完全ガイド バッテリー駆動を年単位で実現する設計ノウハウ](https://technosphere.co.jp/blog/stm32-low-power-battery)
-  [開発実績 組込み制御・マイコン開発 実績紹介 CAN通信・カメラミドル・産業IoTなど多数の受託開発事例](https://technosphere.co.jp/embedded-achievements)
    [技術コラム一覧に戻る](https://technosphere.co.jp/blog/)
