STM32 CAN通信:ハードウェアとソフトウェアの解説
はじめに
現代の組み込みシステムにおいて、複数のデバイス間で効率的かつ信頼性の高い通信を行うことは不可欠です。特に、自動車産業や産業オートメーションの分野では、リアルタイム性、堅牢性、そしてマルチマスタ機能を兼ね備えたController Area Network (CAN) プロトコルが広く利用されています。
STMicroelectronics社製のSTM32マイクロコントローラーは、その高性能、多様なペリフェラル、そして豊富な製品ラインナップにより、組み込み開発者の間で非常に人気があります。多くのSTM32ファミリは、CANまたはその後継であるCAN FD (Flexible Data-rate) インターフェースを内蔵しており、これらの強力な通信機能を容易にシステムに組み込むことができます。
この記事では、STM32を用いたCAN通信の実装に焦点を当て、その基礎となるハードウェアと、具体的なソフトウェア開発手法について詳細に解説します。CANプロトコルの仕組みから、STM32のCANペリフェラルの機能、外部ハードウェアの接続、そしてSTM32CubeMX/CubeIDEとHALライブラリを用いたプログラミングまで、一連の流れを追って説明します。この記事を読むことで、STM32とCAN通信を用いたシステムの設計および実装に必要な知識を習得できるでしょう。
1. CAN通信の基礎
STM32での実装に入る前に、まずはCAN通信自体の基本的な仕組みを理解することが重要です。
1.1. CANの歴史と目的
CANは、1980年代にRobert Bosch GmbHによって自動車内の電子制御ユニット (ECU) 間でデータを効率的に交換するために開発されました。従来のポイント・ツー・ポイント配線に代わり、単一のバスラインで複数のECUが通信することで、配線数の削減、システム全体の軽量化、そして信頼性の向上を実現しました。その後、その堅牢性と信頼性から、産業機器、医療機器、航空宇宙など、幅広い分野に応用されています。
1.2. CANの特徴
CAN通信の主な特徴は以下の通りです。
- マルチマスタ方式: バス上のどのノードも、他のノードからの許可なくいつでもデータの送信を開始できます。
- メッセージ指向: 通信相手を指定するアドレスではなく、送信するデータの内容を示すメッセージIDに基づいて通信を行います。
- 調停 (Arbitration): 複数のノードが同時に送信を開始しようとした場合、メッセージIDの値の低いメッセージが優先され、衝突を回避します。IDが低いほど優先度が高いとみなされます。
- エラー検出と処理: 高度なエラー検出メカニズム(CRC、ビットモニタリングなど)を持ち、エラー発生時にはそのフレームを破棄し、再送を試みます。エラーカウンタにより、ノードの状態(Active Error, Passive Error, Bus-off)を管理し、問題のあるノードをバスから隔離することも可能です。
- 堅牢性: 差動信号方式を採用しており、外部ノイズに強い特性を持ちます。
- ブロードキャスト通信: 一つのノードから送信されたメッセージは、バス上の全てのノードに受信されます(ただし、各ノードはフィルタによって必要なメッセージを選択的に受信できます)。
1.3. CANの物理層
CANの物理層は、主に以下の要素で構成されます。
- CAN-High (CANH) および CAN-Low (CANL) 信号線: 2本のツイストペア線が使用されます。ノードはこれらの線間の電位差(差動電圧)によってデータを送受信します。
- 差動信号: データはCANHとCANLの電位差によって表現されます。
- リセッシブ (Recessive) 状態: CANHとCANLの電位差がほぼ0Vに近い状態。論理値 ‘1’ に相当します。バスは通常リセッシブ状態です。
- ドミナント (Dominant) 状態: CANHがCANLよりも高い電位(例: 約2V)を持つ状態。論理値 ‘0’ に相当します。ドミナントはリセッシブよりも優先されます(複数のノードが同時に送信した場合、一つでもドミナントを出力すればバスはドミナントになります)。
- 差動信号は、同相ノイズ(CANHとCANLに同時に同じノイズが乗る場合)に対して高い耐性を持ちます。
- ターミネータ抵抗: バスラインの両端には、通常120Ωの終端抵抗が必要です。これは信号の反射を防ぎ、バスの信号品質を維持するために不可欠です。抵抗がない、または間違った場所に設置されていると、信号反射によるエラーが多発し、正常な通信ができなくなります。
- バスのトポロジー: CANバスはリニアバス(直線状のバス)として構成されます。ノードはバスラインにT字型に接続されます。スター型やリング型は推奨されません。
- CANトランシーバー: マイクロコントローラーのCANコントローラーとCANバスラインを接続する際に使用されるICです。マイコンのロジックレベル信号(通常0V/3.3Vまたは5V)を、CANバスの差動信号レベルに変換(送信時)し、逆にバスの差動信号をマイコンが解釈できるロジックレベルに変換(受信時)する役割を担います。また、バスラインからの過電圧やESD(静電放電)に対する保護機能も提供します。
1.4. CANのデータリンク層
CAN通信は、メッセージを「フレーム」という単位で送受信します。主要なフレーム形式は「データフレーム」と「リモートフレーム」です。
データフレームの構造 (標準フォーマット 11-bit ID):
データフレームは、実際のデータを運ぶためのフレームです。標準フォーマットの場合、以下のフィールドで構成されます。
- SOF (Start of Frame): 1ビットのドミナント(‘0’)。フレームの開始を示します。
- Arbitration Field: メッセージID (11ビット) と RTR (Remote Transmission Request) ビット (1ビット) から構成されます。
- メッセージID (11-bit): 0x000 ~ 0x7FF の値を取り、メッセージの内容や送信元ノードなどを識別します。IDの値が小さいほど優先度が高くなります。このフィールドでバス調停が行われます。
- RTR (Remote Transmission Request): 1ビット。通常はドミナント(‘0’)で、データフレームであることを示します。リモートフレームの場合はリセッシブ(‘1’)になります。
- Control Field: 6ビット。予約ビットとDLC (Data Length Code) から構成されます。
- DLC (Data Length Code): 4ビット。データフィールドのバイト長 (0~8バイト) を示します。
- Data Field: 0~8バイトの実際のデータ。
- CRC Field (Cyclic Redundancy Check): 15ビットのCRCシーケンスと1ビットのCRCデリミタから構成されます。受信ノードはCRC計算を行い、送信ノードのCRCシーケンスと比較することでエラーを検出します。
- ACK Field (Acknowledge Field): 2ビット。ACKスロット (1ビット) と ACKデリミタ (1ビット) から構成されます。受信側のノードがエラーなくフレームを受信した場合、ACKスロットをドミナント(‘0’)にすることで送信側に正常受信を通知します。送信側は、ACKスロットがリセッシブ(‘1’)のままであった場合(ACKエラー)、受信ノードがいない、またはエラーが発生したと判断します。
- EOF (End of Frame): 7ビットのリセッシブ(‘1’)。フレームの終了を示します。
- Interframe Space: フレーム間の最小間隔。
拡張フォーマット (Extended Format 29-bit ID):
拡張フォーマットは、標準フォーマットよりも長い29ビットのメッセージIDを使用します。IDの範囲は 0x00000000 ~ 0x1FFFFFFF です。標準フォーマットとの主な違いは、Arbitration Fieldが拡張され、SRR (Substitute Remote Request) と IDE (Identifier Extension) ビットが追加される点です。IDEビットがリセッシブ(‘1’)であれば拡張フォーマット、ドミナント(‘0’)であれば標準フォーマットと区別されます。
データフレーム vs リモートフレーム:
- データフレーム (Data Frame): 実際にデータを含むフレーム。通常はこのフレームを使用します。
- リモートフレーム (Remote Frame): 特定のメッセージIDを持つデータフレームの送信を要求するためのフレーム。データフィールドは持ちません。RTRビットがリセッシブ(‘1’)になります。リモートフレームを受信したノードは、そのIDに対応するデータフレームを送信する責任があります。
ビットスタッフィング (Bit Stuffing):
CANプロトコルでは、同じビット値が5ビット連続した場合、強制的にその次のビットを反対の値(スタッフィングビット)として挿入します(例: 00000 -> 000001, 11111 -> 111110)。受信側はこのスタッフィングビットを認識して取り除きます。これは、フレーム内の同期を維持し、長い連続した同じビット値によって引き起こされるタイミングの問題を防ぐために行われます。スタッフィングエラーはエラーの一つとして検出されます。
メッセージ調停 (Arbitration):
複数のノードが同時に送信を開始した場合、バスはリセッシブ状態(’1’)からSOFのドミナント状態(’0’)に遷移します。Arbitration Fieldの送信中に、各ノードは自分が送信したビットとバス上の実際のビット値を常にモニタリングします。
もしノードがリセッシブ(‘1’)を出力したにも関わらず、バスがドミナント(‘0’)になった場合(別のノードがドミナントを出力したため)、そのノードは「負けた」と判断し、直ちに送信を中断して受信に切り替えます。
メッセージIDの値が低いほど、Arbitration Fieldの早い段階でドミナント(‘0’)が出現するため、優先度が高くなります。IDフィールド全体を送信し終えてもバス上で「負け」なかったノードだけが、その後のControl Field以降を送信し続けることができます。
エラー検出と処理:
CANは、以下のエラー検出メカニズムを備えています。
- Stuff Error: ビットスタッフィング規則に違反した場合(同じビット値が6ビット以上連続した場合)。
- Form Error: 固定フォーマットのフィールド(SOF, CRCデリミタ, ACKデリミタ, EOF)が規定の値と異なる場合。
- ACK Error: 送信ノードがACKフィールドでドミナント信号を確認できなかった場合(受信ノードがいない、またはエラーでACKを返せない場合)。
- Bit Monitoring Error: 送信ノードが、自分が送信したビットとバス上の実際のビット値が異なることを検出した場合(調停での負けを除く)。
- CRC Error: 受信ノードが計算したCRC値と、受信したフレームのCRC値が一致しない場合。
エラーを検出したノードは、エラーフレームを送信してバス上の他のノードにエラー発生を通知します。各ノードは送信エラーカウンタ (TEC) と受信エラーカウンタ (REC) を持ち、エラーの種類に応じてこれらのカウンタが増減します。
- Active Error State: TECまたはRECが128未満。エラー検出時にアクティブエラーフラグ(6ビットのドミナント列)を送信します。他のノードもこれを受信してエラーカウンタを増やします。
- Passive Error State: TECまたはRECが128以上、256未満。エラー検出時にパッシブエラーフラグ(6ビットのリセッシブ列)を送信します。これはバス上の他のノードへの影響を最小限に抑えます。自身のエラーカウンタは増加し続けます。
- Bus-off State: TECが256以上。そのノードはバスから切り離され、通信に参加できなくなります。Bus-off状態からの回復には、一定時間のリセッシブ状態のモニタリングなど、特定の条件が必要です(通常、マイコンのリセットやソフトウェアによる復帰処理が必要)。
このエラー管理メカニズムにより、一時的なノイズによるエラーや、問題のあるノードがバス全体を停止させてしまうことを防ぎ、高い可用性を実現しています。
1.5. CANの速度とバス長
CANの通信速度(ボーレート)は、ビットタイミングの設定によって決まります。ボーレートとバス長にはトレードオフの関係があります。一般的に、ボーレートが高いほどバス長は短くなります。
- 1 Mbps: 最大バス長 約40m
- 500 Kbps: 最大バス長 約100m
- 250 Kbps: 最大バス長 約250m
- 125 Kbps: 最大バス長 約500m
- 10 Kbps: 最大バス長 約1000m
これらの値はあくまで目安であり、ケーブルの種類、ノード数、接続品質などによって変動します。
2. STM32におけるCANペリフェラルの概要
多くのSTM32マイクロコントローラーには、CANまたはCAN FD (FDCAN) ペリフェラルが内蔵されています。これらのペリフェラルは、CANプロトコルのデータリンク層機能の大部分をハードウェアで処理します。
2.1. CAN (Classic CAN) vs FDCAN (CAN FD)
- CAN (Classic CAN): ISO 11898-1に準拠した従来のCANプロトコルです。メッセージIDは11ビットまたは29ビット、データフィールドは最大8バイト、通信速度は最大1 Mbpsです。
- FDCAN (CAN FD – Flexible Data-rate): CANの改良版で、ISO 11898-1/2015および BoschのCAN FD仕様に準拠しています。
- 可変データレート: Arbitration FieldやControl Fieldなどのプロトコル制御部分はClassic CANと同じ速度で通信しますが、Data Fieldの部分だけをより高速(最大8 Mbps以上、設定による)で通信できます。これにより、スループットが向上します。
- 拡張データ長: データフィールドが最大64バイトまで拡張されました(Classic CANは8バイトまで)。
- 改良されたCRC: データ長が増加したことに伴い、CRCも17ビットまたは21ビットに拡張・改良されています。
多くの新しいSTM32ファミリ(例: G4, L4+, F7, H7, U5など)はFDCANペリフェラルを搭載しており、Classic CANモードでの動作も可能です。一部の古いまたはローエンドのファミリ(例: F0, F1, L0など)はClassic CANペリフェラルのみを搭載しています。どちらのペリフェラルが搭載されているかは、使用するSTM32のデータシートやリファレンスマニュアルで確認する必要があります。
2.2. STM32 CAN/FDCANペリフェラルの主な機能ブロック
STM32のCAN/FDCANペリフェラルは、以下の主要な機能ブロックから構成されます(FDCANはより高機能ですが、基本的な概念はClassic CANと共通しています)。
- クロックソース: ペリフェラルの動作クロックを提供します。通常、APBバスのクロックが使用されます。正確なボーレート設定のためには、安定したクロック供給が重要です。
- ビットタイミングレジスタ: CAN通信のボーレートとビットサンプリングポイントを設定するためのレジスタ群です。
- BRP (Baud Rate Prescaler): ペリフェラルクロックを分周して、CANビットを構成する基本時間単位である「タイムクァンタム (Time Quantum, tq)」を生成します。
- TS1 (Time Segment 1): プロパゲーションセグメント (Prop_Seg) とフェーズセグメント1 (Phase_Seg1) を合わせた長さ (tq単位) を設定します。
- TS2 (Time Segment 2): フェーズセグメント2 (Phase_Seg2) の長さ (tq単位) を設定します。
- SJW (Synchronization Jump Width): 同期時に調整できる最大幅 (tq単位) を設定します。
- 1ビットの時間は、 Sync_Seg (固定1 tq) + Prop_Seg + Phase_Seg1 + Phase_Seg2 の合計時間となります。Sync_Segは通常TS1に含まれます。ビットタイミングの設定は、通信速度、バス遅延、ノード間のタイミングオフセットなどを考慮して慎重に行う必要があります。一般的には、サンプルポイントをビットの終わり近く(75%~85%あたり)に設定するのが推奨されます。
- 送信機能:
- 送信バッファ (Tx Mailboxes / Tx Buffers): 送信待ちのメッセージを一時的に保持するバッファです。STM32のCANペリフェラルは通常3つの送信バッファを持ちます。ソフトウェアはここに送信したいメッセージ(ID, データなど)を書き込みます。
- 調停: 複数のバッファにメッセージが書き込まれた場合、ハードウェアがIDに基づいて自動的に調停を行い、優先度の高いメッセージから順にバスへ送信します。
- 受信機能:
- 受信FIFO (Receive FIFOs): バスから受信したメッセージを格納するFIFO(First-In, First-Out)バッファです。STM32のCANペリフェラルは通常2つの受信FIFO (FIFO0, FIFO1) を持ちます。フィルタを通過したメッセージがここに格納されます。
- FIFOがいっぱいになると、新しいメッセージは失われる可能性があります(オーバーランエラー)。定期的にメッセージをFIFOから読み出す必要があります。
- フィルタ (Filters): 受信ノードがバス上の全てのメッセージを受信するのではなく、特定のメッセージIDを持つメッセージだけを選択的に受信するための機能です。
- STM32のCANペリフェラルは複数のフィルタバンクを持ちます。各フィルタバンクは、特定のメッセージIDまたはID範囲を設定して、それをFIFO0またはFIFO1に紐付けることができます。
- フィルタモードには、「マスクモード (Mask mode)」と「リストモード (List mode)」があります。マスクモードでは、IDとマスク値を設定し、「(受信ID & マスク) == (フィルタID & マスク)」となるメッセージを受信します。リストモードでは、特定の複数のIDを指定し、それらのIDに一致するメッセージを受信します。
- フィルタスケールは、フィルタを11ビットID用または29ビットID用として構成するかを選択できます。
- エラー管理:
- エラーカウンタ(TEC, REC)をハードウェアで保持・更新します。
- バスエラー検出(Stuff Error, Form Error, ACK Error, Bit Monitoring Error, CRC Error)を行います。
- エラー状態(Active Error, Passive Error, Bus-off)を管理します。
- 割り込み制御:
- 送信関連(送信完了、空き送信バッファありなど)
- 受信関連(FIFOにメッセージあり、FIFOオーバーランなど)
- エラー関連(各種エラー発生、Bus-off状態遷移など)
- スリープ/ウェイクアップ関連
- これらのイベント発生時にCPUへの割り込み要求を生成できます。効率的な通信処理のためには、受信割り込みを積極的に利用することが推奨されます。
- DMAサポート: 一部のSTM32ファミリでは、CAN/FDCANの送信および受信にDMA(Direct Memory Access)を使用できます。これにより、CPUの介在なしにメモリとCANペリフェラル間でデータを転送でき、CPU負荷を軽減できます。
3. ハードウェア接続
STM32マイコンとCANバスを接続するためには、外部にCANトランシーバーICが必要です。
3.1. STM32のCAN/FDCANピン
STM32マイコンのデータシートやリファレンスマニュアルを参照し、使用するCAN/FDCANペリフェラルに割り当てられているGPIOピン(CANx_TX, CANx_RX または FDCANx_TX, FDCANx_RX)を確認します。これらのピンは、CAN/FDCAN Alternate Functionとして設定する必要があります。
3.2. CANトランシーバーの役割
CANトランシーバーICは、STM32のCANペリフェラルのロジックレベル信号(TTL/CMOSレベル、通常3.3V)とCANバスの差動信号レベル(ISO 11898-2で規定されたレベル)の間で信号変換を行います。
- 送信時: STM32のCAN_TXピンからのロジック信号(ドミナント=’0’、リセッシブ=’1’)を受け取り、CANHとCANLの差動信号としてバスに出力します。
- 受信時: バス上のCANHとCANLの差動信号を受け取り、その電位差をSTM32のCAN_RXピンが解釈できるロジック信号に変換して出力します。
- 保護機能: バスラインからの過渡電圧、ESD、ショート、過熱などからマイコンのCANペリフェラルを保護する機能を持つトランシーバーが多くあります。
- スタンバイ/スリープモード: 一部のトランシーバーは、低消費電力モード(スタンバイ/スリープ)を備えており、CANバス上のアクティビティを検出してマイコンをウェイクアップさせる機能を持つものもあります。
3.3. 代表的なCANトランシーバー IC
様々なメーカーからCANトランシーバーICが提供されています。代表的な例としては以下のようなものがあります。
- Texas Instruments (TI): SN65HVD230, SN65HVD231, TCAN1042など
- Microchip Technology: MCP2551, MCP2561/2など
- NXP Semiconductors: TJA1050, TJA1051, TJA1057, TJA1145 (CAN FD) など
- STMicroelectronics: VNI2140J (CAN FD), L99C07など
トランシーバー選定の際は、サポートするCANバージョン(Classic/FD)、通信速度、電源電圧、保護機能、消費電力、パッケージなどを考慮します。
3.4. STM32とトランシーバーの接続方法
一般的な接続は非常にシンプルです。
- STM32 CAN_TX → トランシーバー TXD (Transmit Data Input)
- STM32 CAN_RX → トランシーバー RXD (Receive Data Output)
- トランシーバー CANH → CANバス CANH
- トランシーバー CANL → CANバス CANL
- トランシーバーのVCC, GNDピンを適切に接続します。多くの場合、STM32と同じ3.3Vまたは5Vで動作します。
- 一部のトランシーバーには、Low Powerモードを制御するためのSTB (Standby) ピンや、スロープ制御を行うRS (Slope Control) ピンなどがあります。これらのピンも必要に応じて接続・制御します。
3.5. ターミネータ抵抗の実装
バスラインの両端に120Ωのターミネータ抵抗を接続します。これはバスの信号品質に決定的な影響を与えます。
- 実装箇所: 基板上に実装することが最も一般的です。バスラインの物理的な両端に位置するノードの基板に搭載します。コネクタに内蔵された抵抗や、外部に取り付け可能な抵抗を使用する場合もあります。
- バスの中間ノード: バスの物理的な中間にあるノードには、ターミネータ抵抗を接続してはいけません。
- 抵抗値: 標準は120Ωですが、場合によってはケーブルインピーダンスに合わせて調整が必要なこともあります。
3.6. 物理的な配線の注意点
- ツイストペア: CANHとCANLは必ずツイストペアケーブルを使用してください。ツイストすることで、両線に誘導されるノイズが打ち消され、差動信号のノイズ耐性が向上します。
- シールド: ノイズが多い環境では、シールド付きツイストペアケーブルを使用し、シールドを適切にグランドに接続することで、さらにノイズ耐性を高めることができます。
- バス長: 前述のように、通信速度に応じた最大バス長を守ってください。
- スタブ長: 各ノードがバスラインに接続される際の「スタブ」と呼ばれる枝線の長さは、可能な限り短く(理想的には30cm以下)してください。長いスタブは信号反射の原因となります。
- 電源・GND: ノード間で共通のGNDを確保することが重要です。電源ラインも適切に配線し、各ノードで十分なデカップリングコンデンサを配置してください。
4. ソフトウェア実装 (STM32CubeMX/CubeIDE と HAL)
STM32CubeMXやSTM32CubeIDEは、STM32マイクロコントローラーの設定とコード生成を支援するツールです。HAL (Hardware Abstraction Layer) ライブラリは、ペリフェラルの操作を抽象化し、異なるSTM32ファミリ間での移植性を高めます。ここでは、これらのツールとライブラリを用いたCAN通信の実装手順を解説します。
4.1. STM32CubeMX/CubeIDE を使用した設定
- 新しいプロジェクトの作成: 使用するSTM32マイコンを選択し、新しいプロジェクトを開始します。
- RCC (Clock Configuration):
- System Clockの設定を行います。CAN/FDCANペリフェラルに供給されるクロック(通常APB1またはAPB2クロック)が、設定したいボーレートに対して十分な周波数であることを確認します。
- CAN/FDCANのソースクロックを選択します(システムクロックまたは外部水晶/RCなど)。FDCANでは、より柔軟なクロック設定が可能な場合があります。
- GPIO (Pinout & Configuration):
- CAN/FDCAN ペリフェラルのピン (CANx_TX, CANx_RX または FDCANx_TX, FDCANx_RX) を選択します。
- これらのピンのAlternate Functionを設定します。CubeMX/CubeIDEでは、CAN/FDCANペリフェラルを有効にすると自動的に適切なAFが割り当てられます。
- ピン設定を確認します(Push-Pull出力、No Pull-up/Pull-down推奨)。
- CAN/FDCAN ペリフェラル設定:
- System CoreまたはConnectivityの項目から、使用するCAN/FDCANペリフェラルを選択し、Enabledに設定します。
- Mode:
Normal: 通常の通信モード。Loopback: 送信データが外部バスに出力されず、内部的に受信側に入力されるテストモード。外部ハードウェアなしで自身の送信機能と受信機能をテストできます。Silent: 送信は行わず、バス上のデータを受信のみ行うモード(リスナー)。Silent & Loopback: 送信は行わないが、自分の送信データを内部的に受信するモード。
通常はNormalモードを選択します。デバッグ時にはLoopbackモードが有用です。
- Time Settings (Bit Timing):
- Prescaler: ペリフェラルクロックを分周してタイムクァンタム (tq) の周期を決定します。
tq = Peripheral_Clock_Period * (Prescaler + 1)。 - Time Segment 1 (TS1): Prop_Seg + Phase_Seg1 の長さを設定します。
- Time Segment 2 (TS2): Phase_Seg2 の長さを設定します。
- SJW (Resynchronization Jump Width): 再同期ジャンプ幅を設定します。
Number of time quanta in a bit time = 1 (Sync_Seg) + TS1 + TS2Baud rate = Peripheral_Clock_Frequency / (Prescaler + 1) / (1 + TS1 + TS2)- CubeMX/CubeIDEでは、希望するボーレート (例: 500 kbit/s) を入力すると、Prescaler, TS1, TS2, SJW の推奨値を計算してくれます。ただし、環境に合わせて手動で調整が必要な場合もあります。サンプルポイントがビットの終わり近く(通常75%~85%)になるようにTS1とTS2を設定するのが良いとされています。
- FDCANの場合、Nominal bit timing(制御部用)とData bit timing(データ部用)の両方を設定します。
- Prescaler: ペリフェラルクロックを分周してタイムクァンタム (tq) の周期を決定します。
- Receive FIFOs:
- 使用する受信FIFO (FIFO0, FIFO1) を選択します。通常両方使用可能に設定します。
- FDCANでは、Rx FIFO 0 WatermarkやRx FIFO 1 Watermarkなどの設定もあります。
- Interrupts:
- CAN/FDCAN Global Interruptを有効にします。
- 必要に応じて、以下の割り込みを有効にします。
Tx Mailbox Empty Interrupt(送信バッファが空いたとき)Rx FIFO 0 Message Pending Interrupt(FIFO0にメッセージが到着したとき)Rx FIFO 1 Message Pending Interrupt(FIFO1にメッセージが到着したとき)Fifo 0 Full Interrupt(FIFO0がいっぱいになったとき)Error Interrupt(各種エラーが発生したとき)Bus-Off Interrupt(Bus-off状態になったとき)Wakeup Interrupt(CANアクティビティでウェイクアップしたとき)
受信処理にはRx FIFO x Message Pending Interruptの有効化を強く推奨します。
- DMA Settings (Optional):
- Tx/RxにDMAを使用したい場合は、DMA設定タブでADDボタンを押し、CAN/FDCANのTx/Rxリクエストに対応するDMAチャンネルを選択・設定します。
- フィルタ設定 (Filters):
- CAN/FDCANペリフェラルのフィルタ設定タブで、フィルタバンクを設定します。
- Filter Activation: フィルタバンクを有効/無効にします。使用するフィルタバンク全てを有効にします。
- Filter Mode:
Identifier MaskまたはIdentifier Listを選択します。 - Filter Scale:
Single 32-bitまたはDual 16-bitを選択します。11ビットIDのみを使用する場合はDual 16-bit、29ビットIDを使用する場合はSingle 32-bitを選択するのが一般的です。 - Filter FIFO Assignment: このフィルタを通過したメッセージを格納する受信FIFO (FIFO0またはFIFO1) を選択します。
- Filter ID / Mask / List: 選択したモードとスケールに応じて、フィルタリングするIDやマスク値を設定します。
- マスクモード:
Filter IDとFilter Maskを設定します。Filter IDはフィルタリングしたいID(またはIDの一部)、Filter MaskはFilter IDのどのビットを比較対象とするかを示すマスク(1: 比較する、0: 比較しない)を設定します。 - リストモード: フィルタリングしたい複数の特定のID (
Filter ID 1,Filter ID 2…) を設定します。
- マスクモード:
Filter Bankの数だけ設定できます。複数のフィルタバンクを使用して、異なる基準でフィルタリングし、それぞれ異なるFIFOに振り分けることも可能です。例えば、一部のIDをFIFO0へ、別のID範囲をFIFO1へ、といった振り分けが可能です。- デフォルトでは全てのメッセージを受信しない設定になっていることが多いので、少なくとも全てのメッセージを受信したい場合は、マスクモードでFilter ID=0, Filter Mask=0 を設定するか、必要なIDを受信できるようにフィルタを設定する必要があります。
- コード生成: 設定が完了したら、Project -> Generate Code を実行します。これにより、初期化コードを含むCソースファイルが生成されます。
4.2. HALライブラリを使用したプログラミング
CubeMX/CubeIDEで生成されたコードを基に、アプリケーションコードを記述します。主要なHAL関数の使い方を以下に示します。
- ヘッダーファイルのインクルード:
stm32fxxx_hal.h(または特定のファミリのヘッダー) とstm32fxxx_hal_can.h(またはstm32fxxx_hal_fdcan.h) をインクルードします。 - CANハンドル構造体:
CANペリフェラルの設定や状態を保持するための構造体変数を宣言します。
c
CAN_HandleTypeDef hcan1; // または FDCAN_HandleTypeDef hfdcan1;
CubeMXが生成するmain.cのMX_CAN1_Init()関数などで、この構造体が初期化されます。 - 初期化:
CubeMXが生成するMX_CAN1_Init()関数をmain()から呼び出すことで、CANペリフェラルが初期化されます。この関数内でHAL_CAN_Init()が呼び出されます。 -
フィルタ設定:
フィルタは、CANバスを使い始める前に設定する必要があります。
“`c
CAN_FilterTypeDef sFilterConfig; // または FDCAN_FilterTypeDef sFilterConfig;sFilterConfig.FilterBank = 0; // 使用するフィルタバンク番号 (0から始まる)
sFilterConfig.FilterMode = CAN_FILTERMODE_IDMASK; // モード (IDMASK or IDLIST)
sFilterConfig.FilterScale = CAN_FILTERSCALE_32BIT; // スケール (16BIT or 32BIT)
sFilterConfig.FilterFIFOAssignment = CAN_FILTER_FIFO0; // 割り当てるFIFO (FIFO0 or FIFO1)
sFilterConfig.FilterIdHigh = 0x0000; // フィルタIDの上位16ビット
sFilterConfig.FilterIdLow = 0x0000; // フィルタIDの下位16ビット
sFilterConfig.FilterMaskIdHigh = 0x0000; // マスクIDの上位16ビット
sFilterConfig.FilterMaskIdLow = 0x0000; // マスクIDの下位16ビット
sFilterConfig.Activate = ENABLE; // フィルタ有効化
sFilterConfig.SlaveStartFilterBank = 14; // (Dual CAN構成の場合のみ、Slave CANの開始フィルタバンク番号)if (HAL_CAN_ConfigFilter(&hcan1, &sFilterConfig) != HAL_OK)
{
Error_Handler(); // エラー処理
}
``FilterIdHigh
*注: フィルタID/マスクの設定方法は、Standard ID (11-bit) と Extended ID (29-bit) で異なります。HAL関数の引数(,FilterIdLow`など)への格納方法については、リファレンスマニュアルやHALドライバのソースコードを参照してください。一般的に、11ビットIDは32ビットレジスタの上位11ビットに左詰めして格納します。* -
CANペリフェラルの開始:
c
if (HAL_CAN_Start(&hcan1) != HAL_OK) // または HAL_FDCAN_Start(&hfdcan1)
{
Error_Handler(); // エラー処理
}
フィルタ設定後、通信を開始するためにこの関数を呼び出します。 -
メッセージ送信:
送信するメッセージを準備し、送信バッファに追加します。
“`c
CAN_TxHeaderTypeDef TxHeader; // または FDCAN_TxHeaderTypeDef TxHeader;
uint8_t TxData[8]; // 最大8バイトのデータ (FDCANは最大64バイト)
uint32_t TxMailbox; // 送信に使用されたメールボックス番号を取得する変数// 送信ヘッダー設定
TxHeader.StdId = 0x123; // 標準ID (11ビット)
TxHeader.ExtId = 0x0; // 拡張ID (使用しない場合は0)
TxHeader.IDE = CAN_ID_STD; // ID形式 (標準 or 拡張)
TxHeader.RTR = CAN_RTR_DATA; // フレームタイプ (データ or リモート)
TxHeader.DLC = 8; // データ長 (バイト)// 送信データ準備
TxData[0] = 0x01;
TxData[1] = 0x02;
// …// 送信バッファにメッセージを追加 (ノンブロッキング)
// HAL_CAN_AddTxMessage は、空きがあればTxMailboxにメッセージをキューイングし、送信をハードウェアに任せる。
// 空きがない場合は HAL_BUSY を返す。ポーリングするか、Tx Mailbox Empty割り込みを使用する。
if (HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox) != HAL_OK) // または HAL_FDCAN_AddMessageToTxFifoQ(&hfdcan1, &TxHeader, TxData)
{
// 送信バッファが一杯などのエラー処理
}// 必要であれば、送信完了を待つ(ポーリングまたは割り込み)
// while (HAL_CAN_IsTxMessagePending(&hcan1, TxMailbox)); // ポーリング例
``HAL_CAN_AddTxMessage(またはHAL_FDCAN_AddMessageToTxFifoQ) はノンブロッキング関数です。呼び出し後、ハードウェアがバックグラウンドで送信処理を行います。送信が完了したかどうかは、Tx Mailbox Empty割り込みを利用するか、HAL_CAN_IsTxMessagePending` 関数でポーリングして確認できます。 -
メッセージ受信:
受信FIFOにメッセージがあるかどうかを確認し、あればメッセージを読み出します。通常は受信割り込みハンドラ内でこの処理を行います。まず、割り込みハンドラ (
CANx_RX0_IRQHandlerやCANx_RX1_IRQHandler) が呼び出されるように、NVICでCAN/FDCANの受信割り込みを有効にしておく必要があります(CubeMXで設定すれば自動生成されます)。HALドライバは、割り込みが発生すると自動的にHALの共通ハンドラ (
HAL_CAN_IRQHandler) を呼び出し、そこから適切なコールバック関数が呼び出される仕組みになっています。受信メッセージがある場合、HAL_CAN_RxFifo0MsgPendingCallbackまたはHAL_CAN_RxFifo1MsgPendingCallbackが呼び出されます。“`c
// 受信メッセージペンディング割り込みコールバック関数(ユーザー定義)
// stm32fxxx_hal_can.h で weak 属性で定義されているので、ユーザーコードで再定義する
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) // または HAL_FDCAN_RxFifo0Callback
{
CAN_RxHeaderTypeDef RxHeader; // または FDCAN_RxHeaderTypeDef RxHeader;
uint8_t RxData[8]; // または 64// 受信FIFOからメッセージを取得 if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK) // または HAL_FDCAN_GetRxMessage(&hfdcan1, FDCAN_RX_FIFO0, &RxHeader, RxData) { // メッセージ取得失敗時のエラー処理(通常は発生しないはずだが) Error_Handler(); } // 受信したメッセージの処理 // RxHeader に ID, DLC, IDE, RTR などが含まれる // RxData に受信データが含まれる // 例: 受信データを利用した処理を実行 processReceivedMessage(RxHeader.StdId, RxHeader.DLC, RxData); // 受信割り込みを再度許可する必要がある場合がある(HALのバージョンや設定による) // HAL_CAN_ActivateNotification(hcan, CAN_IT_RX_FIFO0_MSG_PENDING, 0); // 例}
// FDCANの場合の例 (Rx FIFO 0 Message Pending Callback)
/
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef hfdcan, uint32_t RxFifo0ITs)
{
FDCAN_RxHeaderTypeDef RxHeader;
uint8_t RxData[64]; // FDCANは最大64バイトif ((RxFifo0ITs & FDCAN_IT_RX_FIFO0_NEW_MESSAGE) != 0) { // 受信FIFOからメッセージを取得 if (HAL_FDCAN_GetRxMessage(hfdcan, FDCAN_RX_FIFO0, &RxHeader, RxData) != HAL_OK) { Error_Handler(); } // 受信したメッセージの処理 processReceivedMessageFDCAN(&RxHeader, RxData); }}
*/
``HAL_CAN_GetRxMessage(またはHAL_FDCAN_GetRxMessage`) を呼び出すことで、FIFOから最も古いメッセージが取り出されます。この関数を呼び出すまで、メッセージはFIFOに保持されます。FIFOオーバーランを防ぐために、割り込みハンドラ内で迅速にメッセージを処理するか、より大きなバッファにコピーしてから後で処理することが重要です。 -
CANペリフェラルの停止:
通信を停止したい場合に呼び出します。
c
if (HAL_CAN_Stop(&hcan1) != HAL_OK) // または HAL_FDCAN_Stop(&hfdcan1)
{
Error_Handler();
} -
エラー処理:
CAN通信でエラーが発生した場合、Error Interruptが発生します。HALドライバはHAL_CAN_ErrorCallback(またはHAL_FDCAN_ErrorCallback) を呼び出します。このコールバック関数内で、HAL_CAN_GetError関数を使ってエラーコードを取得し、適切なエラー処理(ログ記録、状態表示、Bus-offからの復帰処理など)を実装します。
c
void HAL_CAN_ErrorCallback(CAN_HandleTypeDef *hcan)
{
uint32_t error = HAL_CAN_GetError(hcan);
// エラーの種類に応じた処理
if (error & HAL_CAN_ERROR_EWG) { /* Error Warning State */ }
if (error & HAL_CAN_ERROR_EPV) { /* Error Passive State */ }
if (error & HAL_CAN_ERROR_BOF) { /* Bus-Off State */
// Bus-Offからの復帰処理が必要な場合
// 例: 一定時間待機後、HAL_CAN_Startを再度呼び出すなど
}
if (error & HAL_CAN_ERROR_STF) { /* Stuff Error */ }
// ...その他のエラービットを確認...
}
4.3. Low-Level (LL) ライブラリを使用したプログラミング (オプション)
HALライブラリは抽象度が高く、使いやすい反面、細かなレジスタ制御や最高のパフォーマンスを引き出すには向かない場合があります。STM32Cubeパッケージには、HALよりも低レベルで、レジスタ操作に近い操作を提供するLLライブラリも含まれています。
LLライブラリを使用する場合、CAN/FDCANペリフェラルの各レジスタ(CAN_CR, CAN_BTR, CAN_TIxR, CAN_RFIxRなど)を直接操作するような関数が提供されます。これにより、HALよりもオーバーヘッドを減らし、特定の要件に合わせて柔軟な制御が可能になります。しかし、プログラミングはより複雑になり、ハードウェアレジスタの詳細な理解が必要になります。特定のアプリケーションで極限の性能や低遅延が求められる場合、あるいはコードサイズを最小限に抑えたい場合に検討する価値があります。
5. 実践的な考慮事項とデバッグ
CAN通信の実装において、ハードウェアとソフトウェアの連携は非常に重要です。特に複数のノードが接続されるシステムでは、予期しない問題が発生しやすいため、デバッグ手法を知っておくことが役立ちます。
5.1. ボーレートの選定とビットタイミング
- システム全体の要件に基づき、適切なボーレートを選択します。バス長やノード数も考慮に入れる必要があります。
- CubeMXが提示するビットタイミング設定は良い出発点ですが、特に高速通信や長いバスでは、実際の波形を確認しながら微調整が必要になることがあります。
- 全てのノードが正確に同じボーレート、そして互換性のあるビットタイミング設定である必要があります。わずかな違いでも通信エラーの原因となります。
- サンプルポイントの位置は非常に重要です。通常、ビットの終わり近く(80%あたり)に設定することで、信号の安定した部分でサンプリングでき、ジッタや遅延に対する耐性が高まります。
- SJW (Synchronization Jump Width) は、バスの同期がずれた場合に許容される再同期の最大幅を決定します。適切なSJWを設定することで、ある程度のタイミングオフセットを吸収できます。
5.2. クロックソースの安定性
CANペリフェラルの動作クロックの精度と安定性は、ボーレートの精度に直結します。外部水晶発振子 (HSE) を使用するなど、可能な限り正確で安定したクロックソースを選択することが推奨されます。内蔵RC発振子 (HSI/MSI) を使用する場合、温度や電圧変動による周波数ドリフトがボーレートの不一致を引き起こす可能性があります。
5.3. CANトランシーバーの選定
使用する通信速度、必要な保護機能(過電圧、ESD、ショートなど)、動作温度範囲、消費電力などを考慮して、適切なCANトランシーバーを選定します。特に車載や産業用途では、厳しい環境条件に対応できるトランシーバーが必要です。
5.4. 複数のノードを持つバスでのトラブルシューティング
CANバスで通信がうまくいかない場合、様々な原因が考えられます。
- 物理層の問題:
- ターミネータ抵抗: バス両端に120Ωが正しく接続されているか?中間ノードに接続されていないか?抵抗値は正しいか?
- 配線: CANH/CANLがツイストペアになっているか?極性は正しいか?断線、ショート、接触不良はないか?スタブは長すぎないか?
- 電源/GND: 各ノードに安定した電源が供給されているか?ノード間で共通のGNDが確保されているか?
- ノイズ: 周囲に強力なノイズ源(モーター、インバータなど)はないか?適切なシールドやGND処理がされているか?
- 設定の問題:
- ボーレート不一致: バス上の全てのノードが正確に同じボーレートで設定されているか?ビットタイミングパラメータ(Prescaler, TS1, TS2, SJW)は互換性があるか?
- GPIO設定ミス: STM32のCAN_TX/RXピンがAlternate Functionとして正しく設定されているか?ピン設定(プッシュプル、プルアップ/ダウンなし)は適切か?
- フィルタ設定ミス: 受信したいメッセージIDをフィルタが通過するように正しく設定されているか?意図しないメッセージをフィルタが通過していないか?
- ソフトウェアの問題:
- 初期化: CANペリフェナルは正しく初期化されているか?
HAL_CAN_Startは呼び出されているか? - 送信処理: 送信バッファにメッセージが追加できているか? (
HAL_BUSYが返されていないか?) 送信完了を待つ処理は正しいか? - 受信処理: 受信割り込みが有効になっているか?割り込みハンドラやコールバック関数は正しく実装されているか?
HAL_CAN_GetRxMessageは正しく呼び出されているか?受信FIFOからメッセージを読み出す速度は十分か?(FIFOオーバーランが発生していないか?) - エラー処理: エラー割り込みは有効か?エラーコールバックは実装されているか?Bus-off状態になっていないか?
- 初期化: CANペリフェナルは正しく初期化されているか?
5.5. デバッグツール
CAN通信のデバッグには専用のツールが非常に有効です。
- ロジックアナライザ: CANプロトコルデコード機能を備えたロジックアナライザを使用すると、CANH/CANL信号の波形を確認し、各ビットの時間や値、フレーム構造、ビットスタッフィング、エラーフレームなどを解析できます。物理層の問題とデータリンク層の問題を切り分けるのに役立ちます。
- CANアナライザ: PCにUSBなどで接続するタイプのCANインターフェースと、専用ソフトウェアの組み合わせです。CANバス上のメッセージをリアルタイムにモニタリングし、ID、データ、タイムスタンプなどを表示できます。メッセージの送受信や、エラーカウンタの確認なども可能です。システム開発で最もよく使用されるデバッグツールの一つです。
- オシロスコープ: CANH/CANL信号の波形品質(信号レベル、エッジの立ち上がり/立ち下がり、リンギング、ノイズレベル)を確認するのに使用します。ターミネータの問題やノイズ問題を特定する際に有効です。差動プローブがあると、差動信号を直接観測できて便利です。
- STM32のデバッグ機能: SWD/JTAGインターフェースを使用して、STM32のデバッグセッションを開始します。ブレークポイントを設定してコードの実行を停止させ、CAN/FDCANペリフェラルのレジスタ値、送信/受信バッファの内容、エラーカウンタなどを確認することで、ソフトウェアの問題を特定できます。HAL関数が返すステータスコードをチェックすることも重要です。
6. 応用例
STM32のCAN通信は、様々なアプリケーションで利用されています。
- 車載ネットワーク: 車内のECU間通信(エンジン制御、ABS、エアバッグ、ボディ制御、インフォテインメントなど)。CAN FDの採用も進んでいます。
- 産業用オートメーション: センサー、アクチュエータ、PLC (Programmable Logic Controller) 間のデータ交換。CANopenやDeviceNetなどの上位プロトコルが使用されることもあります。
- ロボティクス: 各関節のモータードライバやセンサーからのデータ収集、制御指令の伝達。
- 医療機器: デバイス間通信、センサーデータ取得。
- ビルディングオートメーション: HVAC (暖房・換気・空調)、照明制御、セキュリティシステムなど。
- 家電: 高機能家電の内部通信。
- 研究開発: CANバスを持つ機器とのインターフェース、プロトコル解析。
STM32をCANノードとして組み込むことで、これらのシステムにおいて特定の機能を担うコントローラーとして動作させることができます。例えば、STM32がセンサーデータを収集し、CANバス経由でマスターコントローラーに送信したり、マスターコントローラーからの指示をCANバス経由で受信し、アクチュエータを制御したりといった応用が考えられます。
7. まとめ
この記事では、STM32マイクロコントローラーを用いたCAN通信の実装について、ハードウェアとソフトウェアの両面から詳細に解説しました。
CAN通信は、そのマルチマスタ性、堅牢なエラー検出・処理機能、そして効率的なメッセージ伝達方式により、多くの分野で信頼性の高い通信手段として利用されています。STM32は、高性能なCAN/FDCANペリフェラルを内蔵しており、この強力な通信機能をシステムに容易に組み込むことが可能です。
実装にあたっては、CANプロトコルの基礎(物理層、データリンク層、調停、エラー処理)を理解することが出発点となります。次に、STM32のCAN/FDCANペリフェラルの機能ブロック(ビットタイミング、送信バッファ、受信FIFO、フィルタなど)を把握し、外部のCANトランシーバーとの適切なハードウェア接続を行う必要があります。特に、ターミネータ抵抗の設置や物理的な配線品質は、通信の安定性に大きく影響します。
ソフトウェア開発では、STM32CubeMX/CubeIDEを用いたペリフェラル設定が効率的です。クロック設定、GPIO設定、そして最も重要なビットタイミング設定やフィルタ設定を正確に行うことが成功の鍵となります。生成されたHALコードをベースに、HAL_CAN_Init, HAL_CAN_ConfigFilter, HAL_CAN_Start といった関数を用いてペリフェラルを初期化・開始し、HAL_CAN_AddTxMessage でメッセージを送信、HAL_CAN_GetRxMessage と受信割り込みコールバック (HAL_CAN_RxFifoXMsgPendingCallback) を組み合わせてメッセージを受信します。効率的な受信処理のためには、割り込みを適切に利用することが不可欠です。エラー発生時には、エラー割り込みハンドラと HAL_CAN_ErrorCallback を利用して適切な対処を実装します。
CAN通信のデバッグは、特にバス上のノード数が増えるにつれて複雑になる傾向があります。ロジックアナライザ、CANアナライザ、オシロスコープなどのツールは、物理層やプロトコルレベルでの問題を特定する上で非常に強力な味方となります。
この記事で解説したハードウェアとソフトウェアの知識、そしてデバッグのポイントを押さえることで、STM32を用いたCAN通信システムの設計・実装を成功させることができるはずです。
さらに高度なアプリケーションでは、CANopenやJ1939のようなCANの上位プロトコルを理解し、実装する必要が出てくるかもしれません。また、データレートやデータ長がさらに要求される場合には、CAN FDの詳細な仕様やSTM32 FDCANペリフェラルのより高度な機能(例: Gateway機能、Protocol Exception Handlingなど)について掘り下げて学習することも有用でしょう。
CAN通信は、その信頼性と柔軟性から今後も多くの分野で重要な役割を担い続けると考えられます。STM32はその実装において優れたプラットフォームを提供しており、この記事が皆様のCAN通信を用いた組み込みシステム開発の一助となれば幸いです。