STM32でCANを使ってみる(メモ)

出典: Wikimura

STM32でCANを使ってみる」の素材ページ。 リファレンスマニュアルを読んで気になった箇所をピックアップ。

目次

22.7.3 Reception handling

FIFOの管理

CANメッセージ受信用に、2つの三段FIFOが用意されている。FIFOの状態は以下のようになる。 メッセージを受け取るたび下の方に行き、メッセージを開放するたび上に行く。 状態の管理はハードウェアがやってくれる。

  1. EMPTY: FIFOが空
  2. PENDING_1: FIFOに1つメッセージが入っている
  3. PENDING_2: FIFOに2つメッセージが入っている
  4. PENDING_3: FIFOに3つメッセージが入っている(満杯)
  5. OVERRUN: FIFOからあふれてしまった

FIFOの状態は受信FIFOレジスタCAN_RFxRのFMPx[1:0]レジスタ(x=0,1)で管理されている。この2ビットでFIFOの使用量を表している。 メッセージが来るたびにカウントアップし、解放するたびにカウントダウンする。

FIFOの解放は、CAN_RFxRのRFOMxレジスタをセットすることで行う。このレジスタをセットすると、一番古いメッセージが解放され、次のメッセージが読み出せるようになる。

オーバーラン

PENDING_3の状態で新しく有効なメッセージを受信してしまうと、オーバーラン状態となる。 個の際の振る舞いは、CANマスタコントロールレジスタCAN_MCRの受信FIFOロックモードレジスタRFLMで設定できる。

  • 受信FIFOロックを有効化: 最新のメッセージは破棄される。一番古い3つのメッセージは守られる。
  • 受信FIFOロックを無効化: 最新のメッセージは直前に受信されたメッセージを上書きする。

受信関係の割り込み

CAN割り込み要求許可レジスタから、各要因の割り込み要求発生を有効化できる。受信の場合、FIFO0/1についてそれぞれ3つの割り込み要因を設定できる。

  • FMPIEx(FIFO Message Pending Interrupt Enable): メッセージが新たに来た時
  • FFIEx(FIFO Full Interrupt Enable): FIFOがフルになった時
  • FOVIEx(FIFO Overrun Interrupt Enable): オーバーランが発生した時


22.7.4 Identifier filtering

CANプロトコルではでは、メッセージIDはIPアドレスのように目的地を示すのではなく、内容を表すものらしい。 全てのノードが放送(broadcast)に耳を傾けていて、自分に必要な内容だけ聞き取るという形態だという。

必要な内容を選別するために使われるのがフィルタであり、STM32では14個のフィルタバンクでフィルタを設定できるようになっている。 フィルタバンクはそれぞれ2つの32ビットレジスタ、CAN_FxR0およびCAN_FxR1を持っている(x=0..13)。 なお、STM32のコネクティビティラインという製品群ではCANモジュールが2基、フィルタバンクは28個ある。ここではMedium Densityを主に取り扱う。

フィルタバンクの使い方は個別に設定できる。 リファレンスマニュアルによると、FSCx(Filter Bank Scale Config. Bit: CAN_FS0R)でフィルタのサイズ選択、 FBMx(Filter Bank Mode: CAN_FM0R)でマスクモード・IDリストモードの選択を行えるようになっている。 そのため、それぞれのバンクで4通りのフィルタ設定ができる。


フィルタモードについて

マスクモードでは、マスクとIDをセットにして1つのフィルタを定義する。 受信IDの中で、一致しなくてはならないビットをマスク側で指定しておき、ID側で一致すべき値を指定する。 (マスクで除外された場所は異なっていても構わないが、それ以外は一致していなくてはならない)

IDリストモードでは、マスクとして使っていた場所もIDとして使用することで、2つのIDを表す。 マスクモードと異なり、完全に一致しなくてはならないが、倍のIDを定義できる。

フィルタ幅について

フィルタバンクは32ビットレジスタが2つ1組になっているため、フィルタ幅によって1つのバンクで定義できるフィルタ数が変わる。

32ビットフィルタ幅の場合、1つのIDまたはマスクを定義するのに32ビットレジスタを1つ使用する。 16ビットフィルタ幅の場合、32ビットレジスタ1つでID+マスクまたはID2つを定義できる。 従って、フィルタモードとフィルタ幅の組み合わせで、1つのバンクから以下の4通りのフィルタ構成が作られる。

  • 32bit + マスクモード  : 1フィルタ
  • 32bit + IDリストモード: 2フィルタ
  • 16bit + マスクモード  : 2フィルタ
  • 16bit + IDリストモード: 4フィルタ


フィルタインデックス

受信FIFOのデータはフィルタを通過してきたものだが、どのフィルタを通過してきたかを調べなくてはならない。 STM32にはこれを簡単化する機能が付いている。

STM32では、フィルタを通ったメッセージの割り振り先をFIFO0とFIFO1の2つに設定できる。 それぞれのFIFOごとに、それを割り振り先としたフィルタに対して番号が0から割り当てられる。

メールボックスにメッセージが到着すると、FIFOにはメッセージと共に、通過したフィルタ番号(Filter Match Index)が格納される。このFilter Match Indexを使うことで、IDの比較をソフトウェアで行うことなく、通過したフィルタを識別できるようになっている。


ただし、フィルタ番号は直接指定することはできない。それぞれのFIFOについて、フィルタバンクが若い順に0番から通しで番号が振られる。フィルタバンク構成(4通り)では、1つのフィルタバンクで1から4つのフィルタを作れる。それぞれの場合について番号の振り方は、「Example of filter numbering」に載っている。

図がないので分かりにくいが、フィルタ番号の増え方は以下のようになる。

32ビット幅 ID + Mask

1つのフィルタバンクで1つのフィルタを作る。

  • Index: n
    • ID: CAN_FxR0[31:0]
    • Mask: CAN_FxR1[31:0]

32ビット幅 IDリスト

1つのフィルタバンクで2つのフィルタを作る。

  • Index: n
    • ID: CAN_FxR0[31:0]
  • Index: n+1
    • ID: CAN_FxR1[31:0]

16ビット幅 ID + Mask

1つのフィルタバンクで2つのフィルタを作る。

  • Index: n
    • ID: CAN_FxR0[15:0]
    • Mask: CAN_FxR0[31:16]
  • Index: n+1
    • ID: CAN_FxR1[15:0]
    • Mask: CAN_FxR1[31:16]

16ビット幅 IDリスト

1つのフィルタバンクで4つのフィルタを作る。

  • Index: n
    • ID: CAN_FxR0[15:0]
  • Index: n+1
    • ID: CAN_FxR0[31:16]
  • Index: n+2
    • ID: CAN_FxR1[15:0]
  • Index: n+3
    • ID: CAN_FxR1[31:16]

フィルタの優先度

1つのメッセージが複数のフィルタにマッチしうる場合、優先度の高い方にマッチしたことになる。 リファレンスマニュアルによれば、フィルタスケール、モード、フィルタ番号の順で比較される。

  1. フィルタスケール: 32ビット幅フィルタ > 16ビット幅フィルタ
  2. フィルタモード: IDリストモード > Maskモード
  3. フィルタ番号: 番号が小さい方が優先


CANのフレーム構造

フィルタを正しく設定するには、フィルタレジスタの各ビットが、CANで使われるIDのどれに相当するかのマッピングを知っていなくてはならない。 CANでは、データフレーム・拡張IDデータフレーム・リモートフレームと、エラーなどの特殊なフレームがある。 それぞれ構造が異なる。リファレンスマニュアルの「CANフレーム」の図や、以下のリンクを参照するとよい。

データフレーム

データフレームは、標準IDしか使わないデータフレームで、以下の構造をしている。 リモート送信要求(別の危機にデータを要求する)を示すRTRビットは0である。 拡張IDであることを示すIDEビットも0である。

従って、フィルタを定義するときは、STIDを設定し、RTRとIDEを0にする。 EXIDは使用しないのでフィルタ定義レジスタのフィールドがどのように扱われるかは不明。Don't careで実装されていれば良いが...

  • SOF
  • STID[11:0]
  • RTR: リモートか?(=0)
  • IDE: 拡張IDか?(=0)
  • r0: 予約
  • DLC: データ数
  • データ
  • CRC
  • ACK
  • EOF


拡張IDデータフレーム

標準IDに加え、拡張IDを使用するデータフレームで、以下の構造をしている。 リモート送信要求(別の危機にデータを要求する)を示すRTRビットは0である。 拡張IDであることを示すIDEビットは1である。

従って、フィルタを定義するときは、STIDとEXID設定し、RTRを0、IDEを1にする。

  • SOF
  • STID[11:0]
  • SSR: Substitute Remote Request Bit
  • IDE: 拡張IDか?(=1)
  • EXID: 拡張ID
  • RTR: リモートか?(=0)
  • r1: 予約
  • r0: 予約
  • DLC: データ数
  • データ
  • CRC
  • ACK
  • EOF


リモートフレーム

データを持たず、相手にデータを要求するだけのフレーム。拡張IDはない。 フィルタを定義するときは、STIDを設定し、RTRを1、IDEを0にする。

  • SOF
  • STID[11:0]
  • SSR: Substitute Remote Request Bit
  • RTR: リモートか?(=1)
  • IDE: 拡張IDか?(=0)
  • r0: 予約
  • DLC: データ数
  • CRC
  • ACK
  • EOF

フィルタレジスタの意味

フィルタレジスタの各ビットは、CANフレームでのSTIDやEXIDフィールドだけでなく、RTRとIDEにもマップされている。 つまり、フィルタはメッセージID部分だけでなく、リモートか、拡張IDかの部分もチェックできるようになっている。

フィルタレジスタは32ビットか16ビット幅で設定可能だが、それぞれマッピングが変わる。 レジスタ上位から以下のようにマッピングされている。

  • 32ビットスケールの場合
    • STID[10:0]
    • EXID[17:0]
    • RTR
    • IDE
  • 16ビットスケールの場合
    • STID[10:0]
    • EXID[17:15]
    • RTR
    • IDE

32ビットスケールの場合、拡張IDも含めてフィルタ可能である。16ビットスケールの場合、EXID[14:0]を指定することができない。


疑問:データフレーム・リモートフレーム時のEXIDの取り扱い

RTR=1のリモートフレームか、IDE=0のデータフレームの場合、フィルタレジスタでEXIDにマッピングされている部分がどのように扱われるかが明記されていない。

IDリストの場合、無視されないと困る気がする。

22.9.3 CAN mailbox registers

CANではメッセージサイズ8バイトが上限なので、32ビットレジスタ2つがメッセージ用に用意されている。 その他にIDやタイムスタンプなどを合わせて、合計4ワード(16バイト)のレジスタで1つのメールボックスが構成されている。

送信メールボックス

送信には3つのメールボックスが用意されている。1つのメールボックスは4ワードのレジスタで構成される(x=0..2)。

  • CAN_TIxR: CAN TX mailbox identifier register
  • CAN_TDTxR: CAN TX mailbox data length control and time stamp register
  • CAN_TDLxR: CAN TX mailbox data low register
  • CAN_TDHxR: CAN TX mailbox data high register


受信メールボックス

受信には2つの3段FIFOメールボックスが用意されている(x=0,1)

  • CAN_RIxR: CAN RX FIFO mailbox identifier register
  • CAN_RDTxR: CAN RX FIFO mailbox data length control and time stamp register
  • CAN_RDLxR: CAN RX FIFO mailbox data low register
  • CAN_RDHxR: CAN RX FIFO mailbox data high register



CAN_InitTypeDef

サンプルのLoopBackでは、ループバックモードを使用して、送り出したフレームをそのまま自分で受け取るテストをしている。CAN Cellの設定は以下の様に行われていた。

    CAN_InitTypeDef        CAN_InitStructure;
    CAN_InitStructure.CAN_TTCM=DISABLE;
    CAN_InitStructure.CAN_ABOM=DISABLE;
    CAN_InitStructure.CAN_AWUM=DISABLE;
    CAN_InitStructure.CAN_NART=DISABLE;
    CAN_InitStructure.CAN_RFLM=DISABLE;
    CAN_InitStructure.CAN_TXFP=DISABLE;
    CAN_InitStructure.CAN_Mode=CAN_Mode_LoopBack;
    CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
    CAN_InitStructure.CAN_BS1=CAN_BS1_8tq;
    CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
    CAN_InitStructure.CAN_Prescaler=5;

CAN_ABOM

自動バスオフ管理(Automatic Bus-Off Management)の有効化・無効化を設定する。

  • ENABLE: 11個の連続したリセッシブ(アイドル状態の条件)を128回検出したとき、自動的にバスオフにする
  • DISABLE: ソフトウェアでバスオフを行う。

CAN_AWUM

自動再起動モードの有効化・無効化を設定する。

  • ENABLE: スリープモードでメッセージを受け取った時、自動的にスリープモードから抜け出す。CAN_MCRのSLEEPビットとSLAKビットは自動的にクリアされる。
  • DISABLE: ソフトウェアでSLEEPビットをクリアすることで、スリープモードから抜ける。SLAKビットはスリープモードから抜けると自動的にクリアされる。

補足

SLEEPビット
スリープモードに切り替えるためのビット(CAN_MCR)。これをセットするとモードの移行が行われる。
SLAKビット
スリープモードに切り替わったことを示す(SLeep mode AcKnowledge)。SLEEPモードから抜けると、自動的にクリアされる。


CAN_BS1

CANビットタイミングレジスタ(CAN_BTR)のTS1[3:0]を設定する。 タイムセグメント1の長さを、時間単位(time quanta)の数で指定する。 記号の意味についてはリファレンスマニュアル参照。


t_{BS1} = t_{CAN} \times ( TS1[3:0] + 1)

とり得る値:

  • CAN_BS1_1tq
  • CAN_BS1_2tq
  • ...
  • CAN_BS1_16tq


CAN_BS2

CANビットタイミングレジスタ(CAN_BTR)のTS2[2:0]を設定する。 タイムセグメント2の長さを、時間単位(time quanta)の数で指定する。 記号の意味についてはリファレンスマニュアル参照。


t_{BS2} = t_{CAN} \times ( TS1[2:0] + 1)

とり得る値:

  • CAN_BS2_1tq
  • CAN_BS2_2tq
  • ...
  • CAN_BS2_8tq


CAN_Mode

CANの動作モードを指定する。とり得る値は以下の通り。 各モードについてはリファレンスマニュアル参照。

  • CAN_Mode_Normal
  • CAN_Mode_LoopBack
  • CAN_Mode_Silent
  • CAN_Mode_Silent_LoopBack


CAN_NART

自動再送信禁止を有効化・無効化する。

  • DISABLE: 自動再送信禁止を無効化(つまり再送信は有効)。CAN規格に準じて、正しく送信できるまで再送を行う。
  • ENABLE: 自動再送信禁止。正常に送信されなくても送信は1回だけ行われる。


CAN_Prescaler

CAN_BTRのボーレートプリスケーラBRP[9:0]を指定し、時間単位tqを決定する。 有効な設定値は1..1024。 関数内で-1されて格納される。tPCLKは、ペリフェラルに供給されるクロックの周期。 CANはAPB1に供給されている。


t_q = ( BRP[9:0] + 1) \times t_{PCLK}


CAN_RFLM

受信FIFOロックモードの有効化・無効化を設定する。

  • DISABLE: 受信FIFOはロックされず、オーバーランが生じたとき、次のメッセージは最後のメッセージを上書きする。
  • ENABLE: 受信FIFOがロックされる。FIFOが一杯になると、次のメッセージは破棄される。


CAN_SJW

CAN_BTRのSJWレジスタで、再同期ジャンプ幅(CANハードウェアが再同期を行う際のビット幅)を時間単位の数で設定する。

  • CAN_SJW_1tq
  • CAN_SJW_2tq
  • CAN_SJW_3tq
  • CAN_SJW_4tq


CAN_TTCM

タイムトリガ通信モードの有効化・無効化を設定する。 タイムトリガ通信モードとは、CANハードウェアのカウンタからタイムスタンプを生成するモード。

  • DISABLE: タイムトリガ通信モード無効化
  • ENABLE: タイムトリガ通信モード有効化


CAN_TXFP

送信FIFOの送信順序を指定する。

  • DISABLE: メッセージIDで送信順序が決定される
  • ENABLE: ソフトウェアで送信要求が発生された順番で送信される


CAN_FilterInitStructure

    CAN_FilterInitTypeDef  CAN_FilterInitStructure;
    CAN_FilterInitStructure.CAN_FilterNumber=0;
    CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
    CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;
    CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;
    CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;
    CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment=0;
    CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;

CAN_FilterActivation

CAN_Filter_Numberで指定するフィルタの有効化・無効化を設定する。

  • ENABLE
  • DISABLE


CAN_FilterFIFOAssignment

フィルタを通過したメッセージをどちらのFIFOに割り振るかを設定する。FIFO0かFIFO1に割り振ることができる。。

  • CAN_FilterFIFO0
  • CAN_FilterFIFO1


CAN_FilterIdHigh

フィルタの


CAN_FilterIdLow

CAN_FilterMaskIdHigh

CAN_FilterMaskIdLow==

CAN_FilterMode

フィルタモードを指定する。フィルタマスクモードとフィルタリストモードがある。


  • CAN_FilterMode_IdList
  • CAN_FilterMode_IdMask


CAN_FilterNumber

設定対象のフィルタ番号を指定する。0から13までが有効。


CAN_FilterScale

フィルタスケールを設定する。デュアル16ビットまたはシングル32ビットが選べる。これはフィルタサイズのところで説明があった通り、フィルタバンクの使い方を決定する。

  • CAN_FilterScale_16bit
  • CAN_FilterScale_32bit

注意事項

受信FIFO ペンディング割り込みのチェックとクリアについて

STM32のStandard Peripheral Libraryでは、ペリフェラル全体で一貫した割り込み操作関数を提供している。 割り込みハンドラでは普通、GetITStatusを使用して割り込み要因をチェックする。 割り込みフラグのクリアには、ClearITPendingBitで対応するビットをクリアする。

しかし、CANの「受信FIFOペンディング割り込み」ではこれが成り立たないことに注意しなくてはならない。 「受信FIFOペンディング割り込み」は、受信FIFOにメッセージが1つ以上(ただし満杯ではない)入っているときに発生する割り込みである。 FIFOからメッセージを取り出し終え、空になった時点で割り込みの発生が停止する。 他の割り込みと異なり、割り込み遅延ビット(ペンディングビット)をクリアすれば停止するタイプではない。

メッセージを取り出すにはCAN_Receive関数を使用する。この関数では、内部でCAN_FIFORelease関数を呼び出すことで、FIFOの解放を行っている。 CAN_Receive関数でメッセージを取り出し、正しく処理し終えて初めて、この割り込みは発生しなくなる。

個人用ツール