Twitter
Facebook
Hatena
ルネサス製MCALのCanを使ってみた

本コラムでは、ルネサス製MCAL(Microcontroller Abstraction Layer)のCanを使って、2台のHSBRH850F1K100間でクラシックCAN/CAN-FD通信する事例を紹介します。
ルネサス製MCALに関する基本情報や開発環境については「ルネサスエレクトロニクス社製MCALの使用方法」をご参照ください。

※本コラムは、ルネサスエレクトロニクス社の許可を得て公開しています。

CANインターフェース

HSBRH850F1K100には、CANトランシーバ(TJA1044)が2つ搭載されており、CANインターフェースも2つ(J7、J8)付いています。各トランシーバは、それぞれRH850/F1KのP10_0/P10_1(CAN0)、P0_2/P_3(CAN1)に接続されています(※1)。また、終端抵抗もボード上に搭載されていますので、2台のHSBRH850F1K100を直接接続することで、2台のボード間でCANおよびCAN-FDを用いた通信をすることが可能です。本コラムでは、CAN0を使用して2台のボード間でCAN通信を行います。

インターフェース端子番号端子名機能名
CAN098P10_0CAN0RX
99P10_1CAN0TX
CAN18P0_2CAN1RX
9P0_3CAN1TX
※1 ジャンパショートにより、デイジーチェーン接続として使用することも可能です。

Can

コンフィギュレーション

CanのEcucDefsは、<インストールフォルダ>\X1X\F1x\modules\can\definition\4.2.2\F1K内のarxmlファイルに定義されています。以下の4ファイルがありますが、チップに対応するarxmlは、ルネサス製Canのユーザーズマニュアル(R20UT4640EJ0101-AUTOSAR.pdf)のTable13.2に記載されています。本コラムで使用するチップはR7F7015813AFPですので、R422_CAN_F1K_580_to_83_620_to_23.arxmlになります。

  • R422_CAN_F1K_46_47_57_66_67_77.arxml
  • R422_CAN_F1K_542_43_60_to_63_602_03_10_to_13.arxml
  • R422_CAN_F1K_580_to_83_620_to_23.arxml
  • R422_CAN_F1K_86_87_97.arxml

ルネサス製CanのEcucDefsは、基本的にAUTOSAR仕様通りの構成となっており、RH850/F1KのCANコントローラ(RS-CAN)固有の機能に対応するためのパラメータがいくつか追加されています。Canでは、送信完了と受信完了を処理する方法として、割込みかポーリングかを選択できますが、Canの場合、動作モードの遷移確認処理(Can_MainFunction_Mode)をポーリングで行う必要があるため、Can_MainFunction_Modeを呼び出すための周期起動タスクが必須となります。せっかく周期起動タスクがありますので、本コラムでは送信完了と受信完了もポーリングで行うこととします。

送信(HTH)

AUTOSARのCanでは、CANコントローラ上のメッセージバッファなど、CANメッセージを送信するハードウェア上の単位をHTH(Hardware Transmit Handle)として定義します。ルネサス製Canでは、HTH毎に使用するバッファの種類を指定します。RS-CANがサポートする4つの送信バッファの種類を、4つのHTHでそれぞれ送信するためのコンフィギュレーションをYAML形式(※2)で記載すると以下のようになります。(該当パラメータの抜粋)

Can:
  CanConfigSet:
    HTH1_TX_BUFF:
      DefinitionRef: CanHardwareObject
      CanIdType: STANDARD
      CanObjectId: 0              # HTHのID
      CanObjectType: TRANSMIT
      CanMemoryMode: BUFFER_MODE  # 送信バッファ(マージモード禁止)

    HTH2_TX_MERGE_BUFF:
      DefinitionRef: CanHardwareObject
      CanIdType: STANDARD
      CanObjectId: 1                    # HTHのID
      CanObjectType: TRANSMIT
      CanMemoryMode: BUFFER_MERGE_MODE  # 送信バッファ(マージモード許可)

    HTH3_TX_QUEUE:
      DefinitionRef: CanHardwareObject
      CanIdType: STANDARD
      CanObjectId: 2                      # HTHのID
      CanObjectType: TRANSMIT
      CanMemoryMode: TRANSMIT_QUEUE_MODE  # 送信キュー
      CanTransmitQueueConfiguration:
        CanTransmitQueueBufferDepth: 3    # キューの段数

    HTH4_TXRX_FIFO_BUFF:
      DefinitionRef: CanHardwareObject
      CanIdType: STANDARD
      CanObjectId: 3                             # HTHのID
      CanObjectType: TRANSMIT
      CanMemoryMode: TRANSMIT_RECEIVE_FIFO_MODE  # 送受信FIFOバッファ
      CanTxRxFIFOConfiguration:
        CanTxRxFIFOBufferDepth: BUFFER_4         # FIFOバッファの段数
        CanTxRxFIFOPayloadLength: PAYLOAD_64     # ペイロードの最大サイズ
※2 YAML形式によるEcucValsの表記については「ABREXの使い方」をご参照ください。

上記のように、送信キューや送受信FIFOバッファでは、それぞれの設定を行うための独自コンテナである/Renesas/EcucDefs_Can/Can/CanConfigSet/CanHardwareObject/CanTransmitQueueConfiguration/Renesas/EcucDefs_Can/Can/CanConfigSet/CanHardwareObject/CanTxRxFIFOConfigurationの設定が必要です。

受信(HRH)

受信では、CANメッセージを受信するハードウェア上の単位をHRH(Hardware Receive Handle)として定義します。HTH同様、HRH毎に使用するバッファの種類を指定します。RS-CANがサポートする3つの受信バッファの種類を、3つのHRHでそれぞれ受信するためのコンフィギュレーションをYAML形式(※3)で記載すると以下のようになります。(該当パラメータの抜粋)

Can:
  CanConfigSet:
    HRH1_RX_BUFF:
      DefinitionRef: CanHardwareObject
      CanIdType: STANDARD
      CanObjectId: 0              # HRHのID
      CanObjectType: RECEIVE
      CanMemoryMode: BUFFER_MODE  # 受信バッファ
      CanHwFilter:
        CanHwFilterCode: 0x0000   # 下位2ビットが00の場合、受信する
        CanHwFilterMask: 0x0003   # 下位2ビットをマスク

    HRH2_RX_FIFO_BUFF:
      DefinitionRef: CanHardwareObject
      CanIdType: STANDARD
      CanObjectId: 1                             # HRHのID
      CanObjectType: RECEIVE
      CanMemoryMode: RECEIVE_FIFO_MODE           # 受信FIFOバッファ
      CanHwFilter:
        CanHwFilterCode: 0x0001                  # 下位2ビットが01の場合、受信する
        CanHwFilterMask: 0x0003                  # 下位2ビットをマスク
      CanReceiveFIFOConfiguration:
        CanReceiveFIFOBufferDepth: BUFFER_4      # FIFOバッファの段数
        CanReceiveFIFOPayloadLength: PAYLOAD_64  # ペイロードの最大サイズ

    HRH3_TXRX_FIFO_BUFF:
      DefinitionRef: CanHardwareObject
      CanIdType: STANDARD
      CanObjectId: 2                             # HRHのID
      CanObjectType: RECEIVE
      CanMemoryMode: TRANSMIT_RECEIVE_FIFO_MODE  # 送受信FIFOバッファ
      CanHwFilter:
        CanHwFilterCode: 0x0000
        CanHwFilterMask: 0x0000                  # マスクしない(フィルタしない)
      CanTxRxFIFOConfiguration:
        CanTxRxFIFOBufferDepth: BUFFER_4         # FIFOバッファの段数
        CanTxRxFIFOPayloadLength: PAYLOAD_64     # ペイロードの最大サイズ
※3 YAML形式によるEcucValsの表記については「ABREXの使い方」をご参照ください。

上記のように、受信FIFOバッファや送受信FIFOバッファでは、それぞれの設定を行うための独自コンテナである/Renesas/EcucDefs_Can/Can/CanConfigSet/CanHardwareObject/CanReceiveFIFOConfiguration/Renesas/EcucDefs_Can/Can/CanConfigSet/CanHardwareObject/CanTxRxFIFOConfigurationの設定が必要です。

ジェネレータ実行

Port同様、Canのジェネレータ実行時も、入力情報として以下の4つが必要です。
※入力情報の詳細な説明はPortの説明をご参照ください。

  • [1] ECU Configuration Description File
    • Can向けのEcucValsを指定します。
    • 複数モジュール向けのEcucValsを1つのarxmlで定義しても問題ありません。
  • [2] Translation XML File
    • ジェネレータを実行するフォルダからの相対パスが同じであれば、Portのジェネレータで使用したものと同じファイルを使用できます。
  • [3] BSWMDT File
    • Can用のBSWMDT Fileは、以下に用意されています。
      • <インストールフォルダ>\X1X\F1x\modules\can\generator\R422_CAN_F1x_BSWMDT.arxml
  • [4] Configuration XML File
    • Port同様、省略可能です。

実装

初期化

まず、Can_Initにより初期化を行います。Can_Initの引数は、ハードウェアやMCAL実装に依存して定義される構造体型であるCan_ConfigTypeへのポインタです。ルネサス製Canでは、ジェネレータで生成したCan_PBcfg.cに対象の構造体が生成されます。また、Can_Cfg.hに以下のマクロが定義されますので、こちらをCan_Initの引数として使用することができます。

/* Controller ID offset */
    :(略)
#define CanConf_CanConfigSet_CanConfigSet    (&Can_GaaConfig[0])

Port同様、初期化処理にてCan_Initを呼び出すことで初期化を行います。ただし、Can_Init内では、タイムアウト処理のためにOsのカウンタ機能を使用するので、Can_InitはOs起動後に呼び出す必要があります。以下の実装例では、InitTaskというOs起動時に起動するタスク内で呼び出しています。また、Can_Initを呼び出した後のコントローラの動作モードはCAN_T_STOPとなっていますので、Can_SetControllerModeにより対象のコントローラをCAN_T_STARTへ変更します。

#include "Can.h"
    :
TASK(InitTask) {
        :
    Can_Init(CanConf_CanConfigSet_CanConfigSet);
    (void) Can_SetControllerMode(CanConf_CanController_CanController, CAN_T_START);
        :
}

動作モードの遷移確認処理のために、/Renesas/EcucDefs_Can/Can/CanGeneral/CanMainFunctionModePeriodで設定した周期で、Can_MainFunction_Modeを呼び出す必要があります。今回は100ミリ秒周期で呼び出すこととして、100ミリ秒毎に起動するタスク(TASK100ms)から呼び出すことで実現しました。(本来のAUTOSARのメソドロジでは、RTEがコンフィギュレーションに応じてTASK100ms相当のタスクコードを生成します)
さらに、今回は送信完了(Can_MainFunction_Write)と受信完了(Can_MainFunction_Read)の処理もポーリングで行うため、同一のタスクで行うことにしています。こちらは、動作モードの遷移確認処理とは別の/Renesas/EcucDefs_Can/Can/CanGeneral/CanMainFunctionRWPeriods/CanMainFunctionPeriodにより周期を設定することができますが、同じ100ミリ秒へ設定することで、1つのタスクですべて処理しています。
TASK(TASK100ms) {
    Can_MainFunction_Mode();
    Can_MainFunction_Write();
    Can_MainFunction_Read();
    TerminateTask();
}

また、動作モードの遷移が完了した際に、遷移完了をCanIfへ通知するインターフェイスとして、CanIf_ControllerModeIndicationが呼び出されますので、スタブとして以下のコードを用意しました。syslog(ATK2が提供するログ送信機能)により、動作モード遷移が完了したコントローラのIDと、遷移後の動作モードを表示します。

void CanIf_ControllerModeIndication(uint8 Controller, CanIf_ControllerModeType ControllerMode)
{
    syslog(LOG_INFO, "[CanIf_ControllerModeIndication] Controller: %d, ControllerMode: %d\n", Controller, ControllerMode);
}

送信

CANメッセージの送信は、Can_Writeにより行います。送信処理では、1秒毎に起動するタスク(TASK1s)から、CAN-ID等を変化させながら、周期的に送信する実装としました。まず、TASK1sを起動する毎に、cntを0からインクリメントしていき、送信するメッセージのPDU-IDとCAN-IDの値として使用します。そして、cntの下位2ビットを用いて、用意した4つのHTHに対して順番に送信します。HTH1_TX_BUFF、HTH3_TX_QUEUEに対しては、クラシックCANで8byteのメッセージを、HTH2_TX_MERGE_BUFF、HTH4_TXRX_FIFO_BUFFに対しては、CAN-FDで64byteのメッセージをそれぞれ送信します。最後に、送信した内容とCan_Writeの結果をsyslogで表示します。

static uint32 cnt = 0U;
TASK(TASK1s) {
    Can_PduType pdu;
    Can_ReturnType ercd;

    pdu.sdu = &send_data[0];
    pdu.swPduHandle = (PduIdType)cnt;

    if ((cnt & 0x03U) == 0x00U) {
        pdu.id = (Can_IdType)cnt;
        pdu.length = 8U;
        ercd = Can_Write(CanConf_CanHardwareObject_HTH1_TX_BUFF, &pdu);
        syslog(LOG_INFO, "[TASK1s] Can_Write(%d): %d, PDU-ID: %d, CAN-ID: 0x%x", CanConf_CanHardwareObject_HTH1_TX_BUFF, ercd, pdu.swPduHandle, pdu.id);
    }
    else if ((cnt & 0x03U) == 0x01U) {
        pdu.id = (Can_IdType)(cnt | 0x40000000U);
        pdu.length = 64U;
        ercd = Can_Write(CanConf_CanHardwareObject_HTH2_TX_MERGE_BUFF, &pdu);
        syslog(LOG_INFO, "[TASK1s] Can_Write(%d): %d, PDU-ID: %d, CAN-ID: 0x%x", CanConf_CanHardwareObject_HTH2_TX_MERGE_BUFF, ercd, pdu.swPduHandle, pdu.id);
    }
    else if ((cnt & 0x03U) == 0x02U) {
        pdu.id = (Can_IdType)cnt;
        pdu.length = 8U;
        ercd = Can_Write(CanConf_CanHardwareObject_HTH3_TX_QUEUE, &pdu);
        syslog(LOG_INFO, "[TASK1s] Can_Write(%d): %d, PDU-ID: %d, CAN-ID: 0x%x", CanConf_CanHardwareObject_HTH3_TX_QUEUE, ercd, pdu.swPduHandle, pdu.id);
    }
    else {
        pdu.id = (Can_IdType)(cnt | 0x40000000U);
        pdu.length = 64U;
        ercd = Can_Write(CanConf_CanHardwareObject_HTH4_TXRX_FIFO_BUFF, &pdu);
        syslog(LOG_INFO, "[TASK1s] Can_Write(%d): %d, PDU-ID: %d, CAN-ID: 0x%x", CanConf_CanHardwareObject_HTH4_TXRX_FIFO_BUFF, ercd, pdu.swPduHandle, pdu.id);
    }
    cnt++;

    TerminateTask();
}

送信データであるsend_dataは64byteの配列で、予め0x00~0x3fを格納しています。なお、AUTOSAR仕様では、CAN-IDを格納するデータ型であるCan_IdTypeの上位2ビットを用いて、クラシックCAN or CAN-FD、STANDARD or EXTENDEDを指定するように規定されているため、HTH2_TX_MERGE_BUFF、HTH4_TXRX_FIFO_BUFFでは、上位2ビットを"01(CAN-FD/STANDARD)"としています。

さらに、送信を完了した際に、送信完了をCanIfへ通知するインターフェイスとして、CanIf_TxConfirmationが呼び出されますので、スタブとして以下のコードを用意しました。

void CanIf_TxConfirmation(PduIdType CanTxPduId) {
    syslog(LOG_INFO, "[CanIf_TxConfirmation] CanTxPduId: %d\n", CanTxPduId);
}

受信

受信処理は、受信を完了した際に、受信完了をCanIfへ通知するインターフェイスとして、CanIf_RxIndicationが呼び出されますので、スタブとして以下のコードを用意するのみとなります。

void CanIf_RxIndication(const Can_HwType* Mailbox, const PduInfoType* PduInfoPtr) {
    uint8 i;
    syslog(LOG_INFO, "[CanIf_RxIndication] Mailbox->CanId: 0x%x", Mailbox->CanId);
    syslog(LOG_INFO, "[CanIf_RxIndication] Mailbox->Hoh: %d", Mailbox->Hoh);
    syslog(LOG_INFO, "[CanIf_RxIndication] Mailbox->ControllerId: %d", Mailbox->ControllerId);
    syslog(LOG_INFO, "[CanIf_RxIndication] PduInfoPtr->SduLength: %d", PduInfoPtr->SduLength);
    for (i = 0U; i < PduInfoPtr->SduLength; i += 4U) {
        syslog(LOG_INFO, "  0x%02x 0x%02x 0x%02x 0x%02x", PduInfoPtr->SduDataPtr[i],
                                                          PduInfoPtr->SduDataPtr[i + 1],
                                                          PduInfoPtr->SduDataPtr[i + 2],
                                                          PduInfoPtr->SduDataPtr[i + 3]);
    }
}

引数のMailboxには、受信したCAN-ID(CanId)、HRHのID(Hoh)、コントローラのID(ControllerId)といった情報が含まれます。そして、引数PduInfoPtrには、受信したPDUのデータ長(SduLength)とデータ(SduDataPtr)が含まれます。本実装では、正しく受信できていることを確認するために、これらの情報をすべてsyslogで表示しています。

Port

ルネサス製Portの使用方法については、既に「ルネサス製MCALのPortとDioを使ってみた」で説明していますので、ここではHSBRH850F1K100でRS-CANを使用するためのコンフィギュレーションについてのみ説明します。

前述の通り、HSBRH850F1K100では、ポートP10_0、P10_1に、CANトランシーバのRXD、TXDが接続されています。RH850/F1Kのマニュアル(右図)に書かれているように、P10_0、P10_1は、第2兼用モードとすることで、CAN0RX、CAN0TXとして使用することができます。このコンフィギュレーションをYAML形式(※4)で記載すると以下のようになります。(該当パラメータの抜粋)

Port:
  PortConfigSet:
    PortGroup10:
      PortPin0:
        PortPinDirection: PORT_PIN_IN     # 入力モード
        PortPinInitialMode: CAN0RX_AT2IN  # 第2兼用モード(CAN0RX/INTP0)
      PortPin1:
        PortPinDirection: PORT_PIN_OUT     # 出力モード
        PortPinInitialMode: CAN0TX_AT2OUT  # 第2兼用モード(CAN0TX)
※4 YAML形式によるEcucValsの表記については「ABREXの使い方」をご参照ください。

Mcu / ボーレート設定

ルネサス製Mcuの使用方法については、既に「ATK2のマイコン依存部処理をルネサス製MCALに置き換えてみた①(Mcu/Port)」で説明していますので、ここではRS-CANへのクロック供給を行うコンフィギュレーションについてのみ説明します。MainOSCなどのRS-CAN以外のクロック設定についても、「ATK2のマイコン依存部処理をルネサス製MCALに置き換えてみた①(Mcu/Port)」で説明したものと同じ設定を使用しました。

RS-CANでは、RH850/F1Kのマニュアル(右図)に書かれているように、複数のクロック設定が必要です。通信クロックは、CKSCLK_ICANOSC(clk_xincan)とCKSCLK_IPERI2(clkc)から選択可能ですが、本コラム作成時はclk_xincanを選択しました。McuにおけるCKSCLK_ICANOSCとCKSCLK_ICANに対するコンフィギュレーションをYAML形式(※5)で記載すると以下のようになります。(該当パラメータの抜粋)

Mcu:
  McuModuleConfiguration:
    McuClockSettingConfig:
      McuIsoCanosc:
        McuPeripheralClock: 16000000  # 16MHz
        McuDivClockSel: DIVBY_1_ID_1  # MainOSCを分周しない
        McuUnitName: CANOSC
      McuIsoCan:
        McuPeripheralClock: 80000000          # 80MHz
        McuSrcClockSel: PPLLCLK_DIVBY_1_ID_3  # C_ISO_CANにPPLLCLKを選択
        McuUnitName: CAN
※5 YAML形式によるEcucValsの表記については「ABREXの使い方」をご参照ください。

上記のMcuのクロック設定を参照する形で、以下のようにCanのボーレート設定を行うことで、初期化時に適切なレジスタ設定が行われ、意図したボーレートで通信することが可能です。以下の通り、本コラム執筆時は、クラシックCAN:500kbps、CAN-FD:2Mbpsで使用しました。

Can:
  CanConfigSet:
    CanControllerMainClock: /<パッケージ名>/Mcu/McuModuleConfiguration/McuClockSettingConfig/McuIsoCanosc  # McuのCKSCLK_ICANOSC定義への参照
    CanControllerPclkClock: /<パッケージ名>/Mcu/McuModuleConfiguration/McuClockSettingConfig/McuIsoCan     # McuのCKSCLK_ICAN定義への参照
    CanController:
      CanControllerId: 0
      CanControllerSelection: RSCANFD00
      CanControllerDefaultBaudrate: /Ecuc/Can/CanConfigSet/CanController/CanControllerBaudrateConfig
      CanControllerBaudrateConfig:
        CanControllerBaudRate: 500        # クラシックCANでは500kbps
        CanControllerBaudRateConfigID: 0
        CanControllerPropSeg: 0
        CanControllerSeg1: 20
        CanControllerSeg2: 11
        CanControllerSyncJumpWidth: 1
        CanControllerFdBaudrateConfig:
          CanControllerFdBaudRate: 2000   # CAN-FDでは2Mbps
          CanControllerPropSeg: 0
          CanControllerSeg1: 4
          CanControllerSeg2: 3
          CanControllerSyncJumpWidth: 1
          CanControllerTxBitRateSwitch: true
  CanGlobalConfiguration:
    CanClockSourceSelect: CLK_XINCAN  # 通信クロックにclk_xincanを選択する
※ YAML形式によるEcucValsの表記については「ABREXの使い方」をご参照ください。

動作確認

前述の実装を用いて、1秒毎にCANメッセージの送受信を行うサンプルプログラムを実行した様子が以下となります。左側が送信、右側が受信です。

まず、送信/受信ともに、Can_SetControllerModeにより、動作モードがCAN_T_START(3)に切り替わったことが、CanIf_ControllerModeIndicationで通知されます。その後、送信側から各バッファを用いたCANメッセージの送信が始まり、受信側では対応するメッセージの受信が始まります。
送信側では、Can_Writeによる送信時に指定したPDU-IDが、CanIf_TxConfirmationによる送信完了通知で正しく通知されていることが分かります。受信側では、コンフィギュレーションで設定した通り、CAN-IDの下位2ビットが00の場合はHRH1_RX_BUFF(Mailbox->Hoh:0)で、01の場合はHRH2_RX_FIFO_BUFF(Mailbox->Hoh:1)で、それ以外はHRH3_TXRX_FIFO_BUFF(Mailbox->Hoh:2)で受信していることが分かります。また、データ長やデータの値も正しく送受信されていることを確認できました。