ルネサス製MCALのLinを使ってみた

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

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

LINインターフェース

HSBRH850F1K100には、LINトランシーバやLINインターフェイスが付いていませんが、拡張I/Oインターフェイスが複数付いているため、以下のLINドライバボードを接続することで、LIN通信が可能になります。

株式会社北斗電子 オプション品のご案内 LINドライバボード

本コラム執筆時は、拡張I/OインターフェイスのJ10を用いて、P10_13(RLIN32RX)とP10_14(RLIN32TX)を、LINドライバボードのRXとTXへ接続しました。各ボードに取り付けるコネクタや接続用のケーブルについても、株式会社北斗電子へ依頼して実装/購入することが可能です。右図は、2台のHSBRH850F1K100およびLINドライバボードを接続した状態です。LINドライバボードには、マスタノード/スレーブノードを切り替えるためのジャンパ等もあるので、注意が必要です。また、LINへの給電のために、どちらかのLINドライバボードに、ACアダプタの接続が必要です。

Lin

コンフィギュレーション

LinのEcucDefsは、<インストールフォルダ>\X1X\F1x\modules\lin\definition\4.2.2\F1K\R422_LIN_F1K.arxmlに定義されています。ルネサス製LinのEcucDefsは、基本的にAUTOSAR仕様通りの構成となっており、RH850/F1KのLINコントローラ(RLIN3)固有の機能に対応するためのパラメータがいくつか追加されています。Linでは、MainFunctionが規定されておらず、コンフィギュレーションに関係なく、割り込みによる送信完了、受信完了の処理が必要です。また、AUTOSAR仕様のLinではマスタノードしかサポートしていませんので、スレーブノードとしては使用できません。HSBRH850F1K100へ接続したRLIN3のユニット2(RLIN32)を使用するためのコンフィギュレーションをYAML形式(※1)で記載すると以下のようになります。(該当パラメータの抜粋)
Lin:
  LinGeneral:
    LinInterruptOutputSelect: false               # 割込み要因はINTRLIN32のみとする

  LinGlobalConfig:
    LinChannel:
      LinChannelBaudRate: 9600                    # ボーレートは9.6kbpsとする
      LinChannelHardwareSelection: RLIN3_CH_ID_2  # RLIN32を選択
      LinChannelId: 0                             # チャネルのID
      LinBreakDelimiterWidth: Tbit_1
      LinHeaderResponseSpaceSelect: Tbit_0
      LinInterByteSpace: Tbit_0
      LinTransmitBreakWidth: Tbits_13
      LinChannelClockRef: /<パッケージ名>/Mcu/McuModuleConfiguration/McuClockSettingConfig/McuIsoLin  # RLIN3への供給クロック(16MHz)への参照
※1 YAML形式によるEcucValsの表記については「ABREXの使い方」をご参照ください。

本コラム執筆時はボーレートを9.6kbpsとしました。/Renesas/EcucDefs_Lin/Lin/LinGlobalConfig/LinChannel/LinChannelClockRefにより、Mcu側のRLIN3側へ供給されるクロックを参照することで、初期化時に適切なレジスタ設定が行われ、意図したボーレートで通信することが可能になります(※2)。フレーム内のBreakDelimiterやHeaderResponseSpaceといった設定については、すべてデフォルト値としました。
RLIN32に対する割り込み要因は、以下のように4つあり、RLIN32では、すべての割り込み要因をINTRLIN32(164)で受け付けるか、割り込み要因に応じてINTRLIN32UR0(165)、INTRLIN32UR1(166)、INTRLIN32UR2(167)でそれぞれ受け付けるかを切り替えることができます。ルネサス製Linでは、/Renesas/EcucDefs_Lin/Lin/LinGeneral/LinInterruptOutputSelectにより、この設定を行うのですが、後述のISR実装の都合から、false(INTRLIN32のみ)としました。

※2 Mcuによるクロック設定については「ATK2のマイコン依存部処理をルネサス製MCALに置き換えてみた①(Mcu/Port)」で説明したものと同じ設定を使用しました。

ジェネレータ実行

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

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

実装

初期化

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

/* Configuration Set Handles */
#define LinGlobalConfig                     (&Lin_GstConfiguration[0])

Port同様、初期化処理にてLin_Initを呼び出すことで初期化を行います。

#include "Lin.h"
    :
sint32
main(void) {
        :
    /* Lin初期化 */
    Lin_Init(LinGlobalConfig);
        :
}

LINスケジュールテーブル

ルネサス製Linによる送受信の確認として、マスタノードと1つのスレーブノードを用いて、以下の4つのフレームを1秒毎に周期的に送受信するスケジュールテーブルを使用しました。

フレームIDレスポンスノードチェックサムデータ長(byte)データ
1マスタクラシック10から送信毎にインクリメントした値
2スレーブクラシック41から送信毎に2倍した値
3マスタエンハンス80x1234567890abcdef
4スレーブエンハンス80xaa55aa55aa55aa55

マスタノード

LINフレームの送信は、Lin_SendFrameにより行います。第2引数となるLin_PduType型のメンバ変数Drcにて、レスポンスを送信するノードを指定します。スレーブノードがレスポンスを送信する場合は、Lin_GetStatusによりマスタノード側でレスポンスを受信します。1秒毎に起動するタスク(TASK1s)を用いて、前述のLINスケジュールテーブルに従った送受信を行う実装例が以下となります。syslog(ATK2が提供するログ送信機能)により、それぞれのフレームに対する送信/受信データを表示しています。

static uint8 fid = 1U;
static uint8 fid1_val = 0U;
TASK(TASK1s)
{
    Std_ReturnType ercd;
    Lin_StatusType lin_ercd;
    Lin_PduType lin_pdu;
    uint8 *p_rcv_data;
    uint8 send_data[8] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef};
    uint32 rcv_data1, rcv_data2;

    if (fid == 1U) {
        lin_pdu.Pid = pid[fid];
        lin_pdu.Cs = LIN_CLASSIC_CS;
        lin_pdu.Drc = LIN_MASTER_RESPONSE;
        lin_pdu.Dl = 1;
        lin_pdu.SduPtr = &fid1_val;

        ercd = Lin_SendFrame(LinConf_LinChannel_LinChannel, &lin_pdu);
        syslog(LOG_INFO, "[TASK1s] Lin_SendFrame: %d, FRAME-ID: %d (PID:0x%x), Data: %d\n", ercd, fid, pid[fid], fid1_val);
        fid1_val++;
    }
    else if (fid == 2U) {
        lin_pdu.Pid = pid[fid];
        lin_pdu.Cs = LIN_CLASSIC_CS;
        lin_pdu.Drc = LIN_SLAVE_RESPONSE;
        lin_pdu.Dl = 4;
        lin_pdu.SduPtr = NULL_PTR;

        ercd = Lin_SendFrame(LinConf_LinChannel_LinChannel, &lin_pdu);
        syslog(LOG_INFO, "[TASK1s] Lin_SendFrame: %d, FRAME-ID: 2 (PID:0x%x)", ercd, pid[fid]);

        do {
            lin_ercd = Lin_GetStatus(LinConf_LinChannel_LinChannel, &p_rcv_data);
        } while (lin_ercd != LIN_RX_OK);

        rcv_data1 = *((uint32*)p_rcv_data);
        syslog(LOG_INFO, "[TASK1s] Lin_GetStatus: %d, FRAME-ID: %d, Data: %d\n", lin_ercd, fid, rcv_data1);
    }
    else if (fid == 3U) {
        lin_pdu.Pid = pid[fid];
        lin_pdu.Cs = LIN_ENHANCED_CS;
        lin_pdu.Drc = LIN_MASTER_RESPONSE;
        lin_pdu.Dl = 8;
        lin_pdu.SduPtr = &send_data[0];

        ercd = Lin_SendFrame(LinConf_LinChannel_LinChannel, &lin_pdu);
        syslog(LOG_INFO, "[TASK1s] Lin_SendFrame: %d, FRAME-ID: %d (PID:0x%x), Data: 0x1234567890abcdef\n", ercd, fid, pid[fid]);
    }
    else {
        lin_pdu.Pid = pid[fid];
        lin_pdu.Cs = LIN_ENHANCED_CS;
        lin_pdu.Drc = LIN_SLAVE_RESPONSE;
        lin_pdu.Dl = 8;
        lin_pdu.SduPtr = NULL_PTR;

        ercd = Lin_SendFrame(LinConf_LinChannel_LinChannel, &lin_pdu);
        syslog(LOG_INFO, "[TASK1s] Lin_SendFrame: %d, FRAME-ID: %d (PID:0x%x)", ercd, fid, pid[fid]);

        do {
            lin_ercd = Lin_GetStatus(LinConf_LinChannel_LinChannel, &p_rcv_data);
        } while (lin_ercd != LIN_RX_OK);

        rcv_data1 = *((uint32*)p_rcv_data);
        rcv_data2 = *((uint32*)(p_rcv_data + 4U));
        syslog(LOG_INFO, "[TASK1s] Lin_GetStatus: %d, FRAME-ID: 4, Data: 0x%x%x\n", lin_ercd, rcv_data1, rcv_data2);
        fid = 0U;
    }
    fid++;

    TerminateTask();
}

Lin_SendFrameの第2引数であるLin_PduType型のメンバ変数Pidに指定するProtected IDは、パリティも含めた値で指定する必要があるため、上記の例では、事前にパリティを含めた値を格納した配列pidを用いて指定しています。また、この例ではLin_GetStatusの返り値がLIN_RX_OKとなることによって、スレーブノードからのレスポンスを受信したことを判定しています。
なお、スレーブノードは、受信したヘッダのフレームIDを確認して、前述のLINスケジュールテーブルに従って、レスポンスの送信/受信を行うだけの実装としました。

ISR

ルネサス製Linでは、RLIN32の送信完了、受信完了に対する割り込み発生時に行う処理が、OsのISRとして<インストールフォルダ>\X1X\common_platform\modules\lin\src\Lin_Irq.cに実装されています。割り込み要因に対して、どのISRを使用すればよいかは、ルネサス製Linのユーザーズマニュアル(R20UT3859EJ0109-AUTOSAR.pdf)のTable13-2に記載されています。しかし、RLIN32に対する割り込み要因は、右図にあるINTRLIN32しか用意されていません。このため、前述したように、/Renesas/EcucDefs_Lin/Lin/LinGeneral/LinInterruptOutputSelectをfalseとして、LIN_CHANNEL18_CAT2_ISRを使用する必要があるわけです。
このISRを有効としないと、スレーブノードがレスポンスを送信する場合に、マスタノードからLin_SendFrameによるヘッダを送信した後、レスポンス受信処理が行われず、Lin_GetStatusでレスポンスを受け取ることができないといった問題が起こります。

Port

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

前述の通り、今回使用した環境では、ポートP10_13、P10_14に、LINドライバボードのRX、TXを接続しています。RH850/F1Kのマニュアル(右図)に書かれているように、P10_13、P10_14は、第2兼用モードとすることで、RLIN32RX、RLIN32TXとして使用することができます。このコンフィギュレーションをYAML形式(※3)で記載すると以下のようになります。(該当パラメータの抜粋)

Port:
  PortConfigSet:
    PortGroup10:
      PortPin13:
        PortPinDirection: PORT_PIN_IN       # 入力モード
        PortPinInitialMode: RLIN32RX_AT2IN  # 第2兼用モード(RLIN32RX/INTP12)
      PortPin14:
        PortPinDirection: PORT_PIN_OUT                      # 出力モード
        PortPinInitialMode: RLIN32TX_AT2OUT_PFC_PFCE_PMCSR  # 第2兼用モード(RLIN32TX)
※3 YAML形式によるEcucValsの表記については「ABREXの使い方」をご参照ください。

Os(ATK2)

前述の通り、割り込み要因INTRLIN32(164)に対応するISRとして、LIN_CHANNEL18_CAT2_ISRを定義する必要がありますので、Osのコンフィギュレーションとして、以下が必要です(該当パラメータの抜粋となります)。ISRの実装は、Lin_Irq.cにありますので、Lin_Irq.cをリンクすれば、対象割り込み発生時に実行されます。

Os:
  LIN_CHANNEL18_CAT2_ISR:
    DefinitionRef: OsIsr
    OsIsrInterruptNumber: 164
    OsIsrCategory: CATEGORY_2
    OsIsrInterruptSource: ENABLE
※ YAML形式によるEcucValsの表記については「ABREXの使い方」をご参照ください。

動作確認

前述の実装を用いて、1秒毎にLINフレームの送受信を行うサンプルプログラムを実行した様子が以下となります。左側がマスタノード、右側がスレーブノードです。

フレームID1では、送信毎にインクリメントした値が、マスタノードからレスポンスとして送信され、スレーブノードで受信しています。フレームID2では、送信毎に2倍した値が、スレーブノードからレスポンスとして送信され、マスタノードで受信しています。フレームID3、4についても、LINスケジュールテーブルの通りに送受信できていることが確認できました。