- 著者: ジョニー・リュー (ダウウェイ・ビークル社CEO|自動車システム専門家)
- 公開日: 2026年7月1日
- カテゴリ: 組み込みシステム/自動車工学/MCUプログラミング
要約すると
このガイドでは、実装手順を段階的に説明します。 STM32 CAN(コントローラーエリアネットワーク)通信 使用 STM32 HALライブラリ(bxCAN). 自動車エレクトロニクスの専門家によって執筆された本書は、物理的なハードウェア設定、タイムクォンタ(TQ)を用いた正確なボーレート計算、ゼロフィルタの「すべて受け入れ」構成、割り込み駆動型メッセージ受信、および実際の産業現場におけるトラブルシューティングガイドラインについて解説しています。
Table of Contents
1. はじめに
自動車工学および産業オートメーションでは、 コントローラーエリアネットワーク(CAN) CANは、堅牢でノイズ耐性のある通信の標準規格であり続けています。これまでの理論的な記事では、CANの物理層、フレームフォーマット、アービトレーション、およびエラー処理メカニズムについて解説しました。
さて、理論と実践のギャップを埋める時が来ました。STM32マイクロコントローラ上で単一のCANフレームを送受信するための、実用的で本番環境に対応したコードをどのように記述すればよいでしょうか?
この記事では古典的な bxCAN 周辺機器を使用する STM32 HALライブラリ (STM32F1、F4、および類似シリーズに最適)。電気自動車(EV)の制御基板を設計する場合でも、工場自動化センサーノードを設計する場合でも、このステップバイステップのコーディングフレームワークは、信頼性が高く拡張性の高い基盤を提供します。
2. STM32 CAN通信に必要なハードウェア(ソフトウェアだけでは不十分な理由)
UARTやSPIとは異なり、ベンチテストのために2つのMCUのRX/TXピンを接続するだけで済みますが、 CAN通信には外部の物理的なトランシーバーハードウェアが必要です。. MCUの接続 CAN_TX そして CAN_RX トランシーバーを使用せずにピン同士を直接接続すると、故障したり、GPIOピンが損傷したりする可能性があります。
CANノードの主要構成要素
正常に機能するCANノードは、以下の5つの主要要素から構成されます。
- 内部CANコントローラ: STM32マイクロコントローラに内蔵されています。
- 外部CANトランシーバー: MCUのデジタルロジックレベル($3.3\text{V}$)を物理的なバス差動信号($\text{CAN\_H}$と$\text{CAN\_L}$)に変換します。一般的なチップには以下のようなものがあります。 TJA1050、 TJA1051、 SN65HVD230、 または MCP2551。
- 実際のバス路線: 電磁干渉(EMI)を抑制するために、$\text{CAN\_H}$と$\text{CAN\_L}$と指定されたツイストペア線が使用されます。
- $120\オメガ$ 終端抵抗器: 信号の反射を防ぐため、バスの両端に設置する必要があります。
- 保護回路(生産時に推奨): 高電圧サージから保護するために、過渡電圧サプレッサ(TVSダイオード)と光電絶縁を採用しています。
ハードウェア配線リファレンス
試作基板またはカスタム基板の配線が、以下の図と完全に一致することを確認してください。
| STM32 MCUピン | トランシーバー側 | トランシーバーバス側 | 物理的なCANバスライン |
STM32 CAN_TX | $\rightarrow$ TXD | カン | $\rightarrow$ CAN_H ($120\Omega$並列終端付き) |
STM32 CAN_RX | $\leftarrow$ RXD | CANL | $\rightarrow$ CAN_L ($120\Omega$並列終端付き) |
3. 概要:CAN初期化の8つのステップ
STM32上で安定した通信を確立するには、ファームウェアは以下の8つの手順を順番に実行する必要があります。
[1. Init CAN Parameters] ──> [2. Config Baud Rate] ──> [3. Config Working Mode]
│
[6. Enable RX Interrupt] <── [5. Start CAN] <── [4. Config Hardware Filters]
│
[7. Transmit CAN Frame] ──> [8. ISR / Callback Rx Handling]
- CAN周辺機器パラメータを初期化する (ハンドル用のメモリを割り当て、ピンの多重化をマッピングする)。
- ボーレートを設定します (プリスケーラと位相セグメントを設定します。)
- 動作モードを設定する (通常モードは本番運用用、ループバックモードはドライラン用)
- ハードウェアフィルタの設定 (ハードウェアレベルで不要なIDを除外するように、受け入れフィルタを設定します。)
- CAN周辺機器を起動します (CANハードウェアの状態を初期化状態からアクティブ状態に移行させる。)
- RX割り込みを有効にする (NVICに受信CANメッセージを受信するよう指示する。)
- CANフレームを送信する (メッセージをTXメールボックスにロードする)
- ISRでCANフレームを受信する (指定されたFIFO割り込みコールバック内でメッセージの解析処理を行う。)
エンジニアリングにおけるよくある落とし穴: 開発者の悩みのほとんどは、ボーレートタイミング設定の間違い、未設定(したがってブロックしている)フィルタ、呼び出しを忘れたことに起因します。
HAL_CAN_Start()または、バス上に別のアクティブなノードがない状態で送信を試み、確認応答(ACK)信号を生成しようとすること。
4. STM32 CANのボーレートとタイムクォンタ(TQ)の理解
CANボーレートの設定を誤ると、ノードが完全に無音になったり、バス上で連続的にエラーフレームが発生したりします。
数学的公式
CANボーレートは、周辺機器のクロックと、1ビット時間を小さな単位に分割することによって計算されます。 時間量子(TQ):$$\text{CANボーレート} = \frac{\text{CANクロック}}{\text{プリスケーラ} \times \text{ビットあたりの合計時間量子}}$$
ビットあたりの総時間量子は、同期セグメントと2つの位相セグメントによって定義されます。$$\text{ビットあたりの総時間量子(総TQ)} = 1 \text{(同期セグメント)} + \text{BS1} + \text{BS2}$$
どこ:
- 同期セグメント(Sync Seg): 常に$1\text{TQ}$に固定されています。
- BS1(時間セグメント1): サンプル点の位置を定義します。伝搬遅延と位相バッファセグメント1の範囲をカバーします。
- BS2(時間セグメント2): 位相バッファセグメント2は、位相ずれを補正します。
実践的な計算例
CAN周辺機器がマッピングされているSTM32で、目標ボーレート$500\text{kbps}$のパラメータを計算してみましょう。 APB1バスが運行中 36 MHz:
- 設定する プリスケーラー 4ドルまで。
- セット BS1 $15\text{TQ}$まで。
- セット BS2 $2\text{TQ}$まで。
これらの値を式に代入してみましょう。$$\text{合計 TQ} = 1 + 15 + 2 = 18\text{ TQ}$$$$\text{CAN ボーレート} = \frac{36\text{ MHz}}{4 \times 18} = \frac{36,000,000}{72} = 500\text{ kbps}$$
計算結果は完全に一致しています。本番環境では、常に サンプリングポイント ($\frac{1 + \text{BS1}}{1 + \text{BS1} + \text{BS2}}$として計算される)は$75\%$を目標としている。 そして 最適なバス安定性とノイズ耐性を実現するには、$87.5\%$が推奨されます。この例では、サンプリングポイントは$\frac{16}{18} \approx 88.8\%$であり、これは標準的な自動車プロファイルに非常に近い値です。
5. コーディング手順 1: MX_CAN_Init() 関数
初期化関数は、bxCANコントローラの構造特性を以下のように構成します。 CAN_HandleTypeDef ハンドル。
CAN_HandleTypeDef hcan;
/**
* @brief Initializes the CAN peripheral and sets basic operation parameters.
* @note This configuration targets a 500 kbps baud rate assuming a 36 MHz APB clock.
* Always recalculate timing parameters if your system clock changes.
* @retval None
*/
void MX_CAN_Init(void)
{
hcan.Instance = CAN1;
/* Baud Rate Configuration: 36MHz / 4 / (1 + 15 + 2) = 500 kbps */
hcan.Init.Prescaler = 4;
hcan.Init.Mode = CAN_MODE_NORMAL;
hcan.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan.Init.TimeSeg1 = CAN_BS1_15TQ;
hcan.Init.TimeSeg2 = CAN_BS2_2TQ;
/* Advanced Operating Modes */
hcan.Init.TimeTriggeredMode = DISABLE;
hcan.Init.AutoBusOff = ENABLE; /* Automatically recover from Bus-Off state */
hcan.Init.AutoWakeUp = DISABLE;
hcan.Init.AutoRetransmission = ENABLE; /* Retransmit frames if arbitration is lost or an error occurs */
hcan.Init.ReceiveFifoLocked = DISABLE; /* Overwrite oldest message when FIFO is full (instead of locking) */
hcan.Init.TransmitFifoPriority = DISABLE; /* Priority determined by Identifier (lowest ID wins) */
if (HAL_CAN_Init(&hcan) != HAL_OK)
{
/* Initialization Error Handling */
Error_Handler();
}
}
重要な初期化パラメータ分析
CAN_MODE_NORMAL対CAN_MODE_LOOPBACK: トランシーブバスを介した実際の通信には、CAN_MODE_NORMAL. ドライラン(物理的な配線を接続せずに内部ロジックをテストする)の場合は、これを切り替えますCAN_MODE_LOOPBACK。オートバスオフ: 設定すると有効にするエラーカウントの蓄積によりバスオフ状態になった後、CANハードウェアは自動的に復旧し、ネットワークに再接続します。自動再送信衝突が発生した場合、またはACKが受信されなかった場合、ハードウェアは自動的にフレームの再送信を試みます。受信Fifoロック: いつ無効にする、新しい受信フレームは FIFO 内の古い未読フレームを上書きします。有効にするFIFOは満杯になるとロックアップし、それ以降の受信データを破棄します。
6. コーディング手順2:フィルタの設定(「なぜ私のMCUはメッセージを無視するのか?」という問題)
デフォルトでは、STM32のbxCANフィルタは閉じられています。フィルタバンクを明示的に構成しない場合、 ノードは受信メッセージをすべて拒否します。
安定したシステム初期化を確立し、ハードウェアが動作していることを確認するには、 「すべて承認」フィルターマスク. これにより、初期のIDチェックがスキップされ、すべてのパケットが通過できるようになります。
/**
* @brief Configures the CAN Hardware Acceptance Filters to accept all incoming frames.
* @note In production, narrow down filter masks to reduce CPU interrupt overhead.
* @retval None
*/
void CAN_Filter_Config(void)
{
CAN_FilterTypeDef filter;
filter.FilterBank = 0; /* Select Filter Bank 0 */
filter.FilterMode = CAN_FILTERMODE_IDMASK; /* Mask Mode (versus Identifier list mode) */
filter.FilterScale = CAN_FILTERSCALE_32BIT; /* Single 32-bit register scale */
/* Setting IDs and Masks to 0 accepts all standard and extended frames */
filter.FilterIdHigh = 0x0000;
filter.FilterIdLow = 0x0000;
filter.FilterMaskIdHigh = 0x0000;
filter.FilterMaskIdLow = 0x0000;
filter.FilterFIFOAssignment = CAN_RX_FIFO0; /* Route matched frames to Receive FIFO 0 */
filter.FilterActivation = ENABLE; /* Enable filter bank 0 */
filter.SlaveStartFilterBank = 14; /* Boundary for dual-CAN microcontrollers */
if (HAL_CAN_ConfigFilter(&hcan, &filter) != HAL_OK)
{
/* Filter Configuration Error */
Error_Handler();
}
}
7. コーディング手順3:CANの開始と割り込みの有効化
初期化とフィルタ設定がレジスタに書き込まれたら、周辺機器を物理的に起動し、適切な割り込み線を有効にする必要があります。
/**
* @brief Starts the CAN peripheral and enables message-pending interrupts.
* @retval None
*/
void CAN_Start(void)
{
/* Start the CAN peripheral */
if (HAL_CAN_Start(&hcan) != HAL_OK)
{
Error_Handler();
}
/* Activate the Notification for FIFO 0 Message Pending Interrupt */
if (HAL_CAN_ActivateNotification(&hcan, CAN_IT_RX_FIFO0_MSG_PENDING) != HAL_OK)
{
Error_Handler();
}
}
? 開発者チェックリスト: 省略した場合
HAL_CAN_Start()CAN ハードウェアは初期化モードのままです。HAL_CAN_ActivateNotification()受信メッセージはFIFOに留まり、コールバック関数がトリガーされることはありません。どちらのミスも「メッセージが受信されませんでした」という全く同じ症状を示します。
8. コーディング手順4:標準CANフレームの送信
CANメッセージを送信するには、メタデータ(IDの長さ、フォーマット、識別子)を指定するヘッダーを設定し、最大8バイトのペイロードバッファを埋める必要があります。
/**
* @brief Transmits a standard CAN frame over the bus.
* @param id: Standard 11-bit Frame Identifier (Range: 0x000 to 0x7FF)
* @param data: Pointer to the uint8_t array containing transmission payload
* @param len: Data length code (DLC) in bytes (Classic CAN range: 0 to 8)
* @retval HAL_StatusTypeDef: Return status of HAL execution
*/
HAL_StatusTypeDef CAN_Send_StdData(uint16_t id, const uint8_t *data, uint8_t len)
{
CAN_TxHeaderTypeDef tx_header;
uint32_t tx_mailbox;
uint8_t tx_data[8] = {0};
/* Boundary Checks */
if ((id > 0x7FFU) || (len > 8U))
{
return HAL_ERROR;
}
/* Safely copy transmission payload */
for (uint8_t i = 0; i < len; i++)
{
tx_data[i] = data[i];
}
/* Formulate CAN Tx Header Struct */
tx_header.StdId = id; /* 11-bit standard ID */
tx_header.ExtId = 0; /* Extended identifier (unused) */
tx_header.IDE = CAN_ID_STD; /* Configure frame type as Standard */
tx_header.RTR = CAN_RTR_DATA; /* Configure frame type as Data Frame */
tx_header.DLC = len; /* Set transmission payload size */
tx_header.TransmitGlobalTime = DISABLE;
/* Request Tx Mailbox transmission */
return HAL_CAN_AddTxMessage(&hcan, &tx_header, tx_data, &tx_mailbox);
}
9. コーディング手順5:割り込みによるCANフレームの受信と解析
有効なフレームがフィルタを通過してFIFO0に到達すると、割り込みが発生します。MCUは通常の実行を停止し、指定されたHALコールバック関数を実行します。
FIFO0コールバックの実装
割り込みコールバックでは、ハードウェアFIFOをクリアしてレイテンシを最小限に抑えるため、フレームを即座に取得する必要があります。
/**
* @brief Rx FIFO 0 message pending callback override.
* Fires automatically whenever a new message lands in FIFO 0.
* @param hcan_ptr: Pointer to the active CAN peripheral handle
* @retval None
*/
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan_ptr)
{
CAN_RxHeaderTypeDef rx_header;
uint8_t rx_data[8];
/* Fetch the message from the hardware FIFO */
if (HAL_CAN_GetRxMessage(hcan_ptr, CAN_RX_FIFO0, &rx_header, rx_data) != HAL_OK)
{
return;
}
/* Sort by Frame Type and route parsing logic */
if (rx_header.IDE == CAN_ID_STD)
{
CAN_Process_StdFrame(rx_header.StdId, rx_data, rx_header.DLC);
}
else
{
CAN_Process_ExtFrame(rx_header.ExtId, rx_data, rx_header.DLC);
}
}
建築におけるベストプラクティス: 遅い複雑な計算やブロッキング遅延(例:
HAL_Delay()割り込みサービスルーチン (ISR) またはコールバック内で ) を使用すると、後続のフレームを見逃すリスクがあります。本番環境レベルのソフトウェアでは、受信したペイロードをスレッドセーフな にすぐにコピーしてください。 リングバッファー または 列 そして、解析処理をメインループまたはRTOSタスクにオフロードします。
10. フレーム処理:IDによる受信データの解析
一度ルーティングされると CAN_Process_StdFrame()ペイロードバイトを、指定されたIDに基づいて解析します。この構造は、標準的な自動車メッセージングマトリックスを反映しています。
/**
* @brief Dispatches standard frames to their target sub-parsing functions.
* @param id: Standard CAN Frame ID
* @param data: Pointer to raw received byte buffer
* @param len: Length of received payload
* @retval None
*/
void CAN_Process_StdFrame(uint16_t id, const uint8_t *data, uint8_t len)
{
switch (id)
{
case 0x100:
CAN_Parse_VehicleSpeed(data, len);
break;
case 0x200:
CAN_Parse_BatteryStatus(data, len);
break;
case 0x300:
CAN_Parse_MotorStatus(data, len);
break;
default:
/* Ignore unrecognized CAN IDs or log diagnostics */
break;
}
}
大規模プロジェクトでは、switch-case分岐を手動でハードコーディングすると保守が困難になります。代わりに、業界標準から自動生成されたデータベースコードを使用してCANフレームを解析するようにアプリケーション層を設計してください。 DBC通信マトリックスファイル。
11. 最終組み立て:すべてをまとめる
初期化、フィルタリング、起動、および周期的なハートビートをメインアプリケーションフローに統合する方法は次のとおりです。
/**
* @brief Initializes and starts the entire CAN application stack.
* @retval None
*/
void CAN_App_Init(void)
{
MX_CAN_Init(); /* Step 1-3: Hardware, Timing, and Mode initialization */
CAN_Filter_Config(); /* Step 4: Acceptance filters configuration */
CAN_Start(); /* Step 5-6: Boot peripheral and active interrupts */
}
/**
* @brief Cyclically transmits a standard heart-beat diagnostic frame.
* Call this from your main loop or thread runner.
* @retval None
*/
void CAN_Send_Heartbeat(void)
{
uint8_t data[8] = {0};
data[0] = 0xA5;
data[1] = 0x5A;
/* Send standard ID 0x700 with a 2-byte payload */
(void)CAN_Send_StdData(0x700, data, 2);
}
CANファームウェアを書き込む際、 隔離の原則に従う. 複雑なアプリケーション層診断プロトコルを重ね合わせる前に、まずループバックモード構成を検証し、次にポイントツーポイントの物理的な送受信テストを実施してください。
12. トラブルシューティング:開発者向け診断ガイド(地理情報最適化版FAQ)
以下は、STM32 CAN統合における最も一般的な問題を解決するために設計された、構造化された診断リファレンスです。
Q1: 伝達関数は返します HAL_OKしかし、ロジックアナライザやオシロスコープには何も表示されません。何が問題なのでしょうか?
簡潔な回答: CAN周辺機器が起動していない、GPIOピンの設定が間違っている、またはトランシーバーに電源が供給されていない可能性があります。
- チェックリスト:
- CAN周辺機器はアクティブですか? 電話をかけたことを確認してください
HAL_CAN_Start()。 - GPIO多重化: GPIOレジスタでTX/RXピンが代替機能(AF)に正しくマッピングされていることを確認してください。
- 物理的な配線: トランシーバー電源($5\text{V}$または$3.3\text{V}$)が安定していることを確認してください。
TXDそしてRXD相互に接続されていない。 - ループバックオーバーライド: 実際のバスがない状態で確認する場合は、
hcan.Init.Mode明示的に構成されていますCAN_MODE_LOOPBACK。 - バス運行状況: ESR(エラー状態レジスタ)を確認して、大量のエラー蓄積によりノードがシャットダウンしたかどうかを確認してください。
- CAN周辺機器はアクティブですか? 電話をかけたことを確認してください
Q2:CANアナライザーではバス上でフレームが送信されていることが明確に表示されますが、私のSTM32ノードは何も受信しません。
簡潔な回答: これは通常、フィルタの設定が閉じているか、設定が間違っているためにメッセージがブロックされているか、または割り込みが有効になっていないことが原因です。
- チェックリスト:
- フィルタマスクの設定: フィルターが厳しすぎますか?「すべて受け入れる」フィルターマスクを設定したことを確認してください(
フィルターIDそしてフィルターマスクID設定0初期デバッグ中に。 - 割り込み有効化: チェック
HAL_CAN_ActivateNotification呼ばれたCAN_IT_RX_FIFO0_MSG_PENDING。 - NVIC構成: お使いのMCUのNVICコントローラでCANグローバル割り込みが有効になっていることを確認してください。
- FIFOミスマッチ: 初期化がコールバックと一致していることを確認してください。
CAN_RX_FIFO0フィルター内で実装しますが、HAL_CAN_RxFifo1MsgPendingCallback(FIFO1)の場合、割り込みが発生してもコードが実行されません。
- フィルタマスクの設定: フィルターが厳しすぎますか?「すべて受け入れる」フィルターマスクを設定したことを確認してください(
Q3:送信しようとすると、バスに「ACKエラー」が連続して表示されるのはなぜですか?
簡潔な回答: ACKエラーは、フレームを承認する他のアクティブなノードがオンラインになっていない場合、または物理的なバス/ボーレートの不一致がある場合に発生します。
- チェックリスト:
- 他にノードはありますか? CANは協調型のバスです。ACKスロット中にメッセージを読み取ってバスをリセッシブからドミナントに切り替えることができる他のノードがオンラインに存在しない場合、送信ノードはACKエラーを報告します。
- ボーレートの不一致: 受信ノードが異なるボーレートに設定されている場合、フレームを読み取ることができず、ACKを発信できません。
- 終端抵抗器: 終端抵抗(両端に120Ω)が欠落していると、信号の反射やエッジの歪みが極端に大きくなり、ACKサンプリングが正しく行われなくなります。
Q4:STM32ノードをネットワークに接続すると、なぜすぐに「バスオフ」状態になるのですか?
簡潔な回答: インスタントバスオフは、通常、不適切なボーレートまたは物理的な短絡によって発生する、大量のエラーフレームの蓄積によって引き起こされます。
- チェックリスト:
- ボーレートの乖離: ボーレートの不一致が深刻になると、エラーフレームが連続的に発生し、送信エラーカウンタ(TEC)のしきい値である255を即座に超えてしまいます。
- フィジカルショーツ: マルチメーターを使用して、物理的な短絡がないことを確認します。
CAN_HそしてCAN_Lまたは地面に接地する。 - トランシーバー電圧: トランシーバーへの電力供給が不十分だと、差動駆動レベルが低下し、重大なローカルハードウェア警告が発生する可能性があります。
13.最終チェックリストと次のステップ
STM32のCAN通信をマスターするには、ハードウェアとソフトウェアの両方の状態機械を同期的に理解する必要があります。
- 正確なクロック構造を確立する そして、安定したボーレートタイミングを実現するための位相セグメントを計算します。
- フィルターのブロックを解除する システム診断テストのための経路を確保するため。
- クリーンで軽量な割り込み駆動型の受信ルーチンを実装する 複雑なネットワークへと拡張するため。
次の記事では、 高度なCANバスデバッグとエンジニアリング診断オシロスコープを使用して波形歪みを解析し、ノイズ関連のエラーフレームを分離し、過酷な産業環境において堅牢なバスオフ自動復旧ルーチンを実装する方法をご紹介します。お楽しみに!




