STM32マイコンでCAN通信

STM32でCAN通信を行った際のまとめ。

開発環境 STM32CubeMX(Version 5.3.0)(無料)

Atollic TrueSTUDIO for STM32(Version: 9.3.0)(無料)

デバッガ J-Link Plus
ボード 自社製品(CanLine-1)用ボード
マイコン STM32F205RCT7
CANトランシーバ SN65HVD232D


目次

CAN初期設定

CubeMXでCAN初期設定を行います。

使用するピンの設定をします。

  • PB9→CAN1_TX
  • PB8→CAN1_RX

STM32 CAN初期設定

次にCANの転送速度とサンプリングポイントを今回は下記のように設定します。

  • 転送速度→500kbps
  • サンプリングポイント→80%

転送速度とサンプリングポイントは下記5個のパラメータで決定します。

  • Prescaler
  • Time Quantum
  • Time Quanta in Bit Segment1 (以下 TSeg1)
  • Time Quanta in Bit Segment2 (以下 TSeg2)
  • ReSynchronization Jump Width (以下 SJW)

「Time Quantum」は1ビットを細分化する際の最小の時間で、下記の計算式で求めます。

Time Quantum = 1sec / (CANのクロック源  /  Prescaler)

CANのクロック源はAPB1なので、CubeMXのClock ConfigurationでAPB1の周波数を確認すると30MHである。

Prescalerを3にして「Time Quantum」を100.0nsecにする。

1sec / (30MHz / 3) = 100.0nsec

STM32 CAN初期設定

転送速度500kbpsでは1ビットの送信時間は2μsecになるので、下記の式で各パラメーターを調整する。

1ビット送信時間 = (TSeg1 + TSeg2 + SJW) × Time Quantum
2000nsec(2μsec) = (15 + 4 + 1) × 100nsec
サンプリングポイント[%] = ( (TSeg1+SJW)  /  (TSeg1+SJW+Tseg2) ) × 100
80[%]=((15 + 1) / (15 + 1 + 4)) × 100

STM32 CAN初期設定

設定例(CANのクロック源30MHz)

転送速度 サンプリングポイント Prescaler TSeg1 TSeg2 SJW
125Kbps 60% 12 11Times 8Times 1Times
70% 12 13Times 6Times 1Times
80% 12 15Times 4Times 1Times
250kbps 60% 6 11Times 8Times 1Times
70% 6 13Times 6Times 1Times
80% 6 15Times 4Times 1Times
500kbps 60% 3 11Times 8Times 1Times
70% 3 13Times 6Times 1Times
80% 3 15Times 4Times 1Times
1Mbps 60% 3 5Times 4Times 1Times
70% 3 6Times 3Times 1Times
80% 3 7Times 2Times 1Times

それ以外のパラメーターは今回はデフォルトのままです。

CAN送信

まずは下記関数をコールしてCAN通信を開始する。

HAL_CAN_Start(&hcan1)

STM32F205はCANの送信メールBOXは3個あり、送信するには少なくとも1個の送信メールBOXが空きである必要がある。HAL_CAN_GetTxMailboxesFreeLevel()で現在空いている送信メールBOXの数を確認して、空いている場合、HAL_CAN_AddTxMessage()でメッセージの送信を開始する。4個目の引数のTxMailboxに使用したメールBOXのナンバー(0~2)が格納される。

CAN_TxHeaderTypeDef TxHeader;
uint32_t TxMailbox;
uint8_t TxData[8];
if(0 < HAL_CAN_GetTxMailboxesFreeLevel(&hcan1)){
    TxHeader.StdId = 0x555;                 // CAN ID
    TxHeader.RTR = CAN_RTR_DATA;            // フレームタイプはデータフレーム
    TxHeader.IDE = CAN_ID_STD;              // 標準ID(11ビット)
    TxHeader.DLC = 8;                       // データ長は8バイトに
    TxHeader.TransmitGlobalTime = DISABLE;  // ???
    TxData[0] = 0x11;
    TxData[1] = 0x22;
    TxData[2] = 0x33;
    TxData[3] = 0x44;
    TxData[4] = 0x55;
    TxData[5] = 0x66;
    TxData[6] = 0x77;
    TxData[7] = 0x88;
    HAL_CAN_AddTxMessage(&hcan1, &TxHeader, TxData, &TxMailbox);
}



CAN受信

STM32F205はCANメッセージ受信FIFOが2個あり(FIFO0とFIFO1)、IDフィルターの設定を行い、どの受信FIFOにメッセージが格納するかを設定する。

IDフィルターは下記のように設定することで、全てのIDのメッセージをFIFO0に格納する。

※詳細はCAN IDフィルター設定を参照

CAN_FilterTypeDef filter;
filter.FilterIdHigh         = 0;                        // フィルターID(上位16ビット)
filter.FilterIdLow          = 0;                        // フィルターID(下位16ビット)
filter.FilterMaskIdHigh     = 0;                        // フィルターマスク(上位16ビット)
filter.FilterMaskIdLow      = 0;                        // フィルターマスク(下位16ビット)
filter.FilterScale          = CAN_FILTERSCALE_32BIT;    // フィルタースケール
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;         // フィルターに割り当てるFIFO
filter.FilterBank           = 0;                        // フィルターバンクNo
filter.FilterMode           = CAN_FILTERMODE_IDMASK;    // フィルターモード
filter.SlaveStartFilterBank = 14;                       // スレーブCANの開始フィルターバンクNo
filter.FilterActivation     = ENABLE;                   // フィルター無効/有効
HAL_CAN_ConfigFilter(&hcan1, &filter);

受信は割込みとコールバック関数を利用しました。

まずCubeMXでFIFO0の受信割込みを有効にします。

STM32 CAN受信

コールバック関数を記述する。
uint32_t id;
uint32_t dlc;
uint8_t data[8];

void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)
{
    CAN_RxHeaderTypeDef RxHeader;
    uint8_t RxData[8];
    if (HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &RxHeader, RxData) == HAL_OK)
    {
        id = (RxHeader.IDE == CAN_ID_STD)? RxHeader.StdId : RxHeader.ExtId;     // ID
        dlc = RxHeader.DLC;                                                     // DLC
        data[0] = RxData[0];                                                    // Data
        data[1] = RxData[1];
        data[2] = RxData[2];
        data[3] = RxData[3];
        data[4] = RxData[4];
        data[5] = RxData[5];
        data[6] = RxData[6];
        data[7] = RxData[7];
    }
}

CAN通信を開始してFIFO受信の割り込みを有効にすることで、上記コールバック関数で受信メッセージを取得できる。

// CANスタート
HAL_CAN_Start(&hcan1);
// 割り込み有効
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);



CAN IDフィルター設定

STM32F205はCANが2チャンネルあり、それぞれ3メッセージ分の受信可能なFIFOが2個ある(FIFO0とFIFO1)。

また28個のフィルターバンクがあり、0~n-1をCAN1で使用して、n~27をCAN2で使用する。(n数は任意に設定可能)

受信FIFOにメッセージを格納するにはフィルターバンクの設定が必要になる。

STM32 CAN IDフィルター

フィルターモードにはIDマスクモードとIDリストモードがある。

  • IDマスクモード は 受信IDとフィルターマスクを論理積した値とフィルターIDとフィルターマスクを論理積した値が一致した場合に指定した受信FIFOに格納する。
(受信ID & フィルターマスク) == (フィルターID & フィルターマスク)
例えば0x750~0x75Fまでの16種類の受信IDを通過させるにはフィルターマスクは0x7F0、フィルターIDは0x750を設定する。
  • IDリストモードは フィルターIDに完全に一致した受信IDを指定した受信FIFOに格納する。
受信ID == フィルターID

またフィルタースケールには32ビットモードと16ビットモードがあり、フィルターモードとの組み合わせで1個のフィルターバンクにつき設定可能なフィルター数が変わってくる。

フィルタースケール フィルターモード
32ビットモード IDマスクモード 1種類のID マスクが可能
32ビットモード IDリストモード 2種類の標準ID又は拡張IDを通過可能にする
 16ビットモード IDマスクモード 2種類のIDマスクが可能
 16ビットモード IDリストモード 4種類の標準IDを通過可能にする

32ビットモードIDマスクモード

32ビットモードの場合、フィルターバンクは下記のようなレジスタ構成なので、標準IDの場合は左へ21ビットシフト、拡張IDの場合は左へ3ビットシフトする必要がある。またフィルターマスクは標準IDか拡張IDかを比較するためIDE(Bit2)は1にする。

CANフィルター設定

0x750~0x75Fの範囲の標準IDを受信FIFO0に格納する例

CAN_FilterTypeDef filter;
uint32_t fId   =  0x750 << 21;        // フィルターID
uint32_t fMask = (0x7F0 << 21) | 0x4; // フィルターマスク 

filter.FilterIdHigh         = fId >> 16;             // フィルターIDの上位16ビット
filter.FilterIdLow          = fId;                   // フィルターIDの下位16ビット
filter.FilterMaskIdHigh     = fMask >> 16;           // フィルターマスクの上位16ビット
filter.FilterMaskIdLow      = fMask;                 // フィルターマスクの下位16ビット
filter.FilterScale          = CAN_FILTERSCALE_32BIT; // 32モード
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;      // FIFO0へ格納
filter.FilterBank           = 0;
filter.FilterMode           = CAN_FILTERMODE_IDMASK; // IDマスクモード
filter.SlaveStartFilterBank = 14;
filter.FilterActivation     = ENABLE;

HAL_CAN_ConfigFilter(&hcan1, &filter);

32ビットモードIDリストモード

0x600と0x700の2個の標準IDを受信FIFO0に格納する例

CANフィルター設定

uint32_t fId1 = 0x600 << 21; // フィルターID1
uint32_t fId2 = 0x700 << 21;  // フィルターID2

filter.FilterIdHigh         = fId1 >> 16;            // フィルターID1の上位16ビット
filter.FilterIdLow          = fId1;                  // フィルターID1の下位16ビット
filter.FilterMaskIdHigh     = fId2 >> 16;            // フィルターID2の上位16ビット
filter.FilterMaskIdLow      = fId2;                  // フィルターID2の下位16ビット
filter.FilterScale          = CAN_FILTERSCALE_32BIT; // 32モード
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;      // FIFO0へ格納
filter.FilterBank           = 0;                     
filter.FilterMode           = CAN_FILTERMODE_IDLIST; // IDリストモード
filter.SlaveStartFilterBank = 14;
filter.FilterActivation     = ENABLE;

HAL_CAN_ConfigFilter(&hcan1, &filter);

16ビットモードIDマスクモード

16ビットモードの場合、フィルターバンクは下記のようなレジスタ構成なので、標準IDの場合は左へ5ビットシフトする必要がある。またフィルターマスクは標準IDか拡張IDかを比較するためIDE(Bit3)は1にする。

0x350~0x35Fの16種類、0x400~0x4FFの256種類の標準IDを 受信FIFO0に格納する例

uint32_t fId1   =  0x350 << 5;        // フィルターID1
uint32_t fMask1 = (0x3F0 << 5) | 0x8; // フィルターマスク1
uint32_t fId2   =  0x400 << 5;        // フィルターID2
uint32_t fMask2 = (0x700 << 5) | 0x8; // フィルターマスク2

filter.FilterIdHigh         = fId1;                  // フィルターID1
filter.FilterIdLow          = fId2;                  // フィルターID2
filter.FilterMaskIdHigh     = fMask1;                // フィルターマスク1
filter.FilterMaskIdLow      = fMask2;                // フィルターマスク2
filter.FilterScale          = CAN_FILTERSCALE_16BIT; // 16モード
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;      // FIFO0へ格納
filter.FilterBank           = 0;                     
filter.FilterMode           = CAN_FILTERMODE_IDMASK; // IDマスクモード
filter.SlaveStartFilterBank = 14;
filter.FilterActivation     = ENABLE;

HAL_CAN_ConfigFilter(&hcan1, &filter);

16ビットモードIDリストモード

0x100、0x200、0x300、0x400の4個の標準IDを受信FIFO0に格納する例

uint32_t fId1 = 0x100 << 5; // フィルターID1
uint32_t fId2 = 0x200 << 5; // フィルターID2
uint32_t fId3 = 0x300 << 5; // フィルターID3
uint32_t fId4 = 0x400 << 5; // フィルターID4

filter.FilterIdHigh         = fId1;                  // フィルターID1
filter.FilterIdLow          = fId2;                  // フィルターID2
filter.FilterMaskIdHigh     = fId3;                  // フィルターID3
filter.FilterMaskIdLow      = fId4;                  // フィルターID4
filter.FilterScale          = CAN_FILTERSCALE_16BIT; // 16モード
filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;      // FIFO0へ格納
filter.FilterBank           = 0;                     
filter.FilterMode           = CAN_FILTERMODE_IDLIST; // IDリストモード
filter.SlaveStartFilterBank = 14;
filter.FilterActivation     = ENABLE;

HAL_CAN_ConfigFilter(&hcan1, &filter);

あとがき

自社製品(CanLine-1)用ボードを製作するにあたり、当初はCAN通信の経験があったルネサス製RL78マイコンで製作しようとしましたが、小ロットでの購入が困難であることが判明したため、急遽STM32マイコンに変更。

STM32マイコンでCAN通信の開発経験はなく、日本語版のドキュメントもなかったため、少し不安でしたが試行錯誤してなんとか制御できるようになりました。フィルター設定のところが理解するのに少し苦労しましたが慣れてしまえばルネサス製マイコンより開発しやすいと感じました。

よろしければシェアを!
  • URLをコピーしました!
目次