RH850F1KHで自動生成コードをベースでCANドライバーを作る1

前記事では、北斗電子さんHSBRH850F1KH176をターゲットにRH850自動コード生成を行いました。しかしCANドライバは自動コード生成の対象になっていません。そこで自動コード生成とHSBRH850F1KH176 に合わせたCANドライバプログラムを用意することにしました。その覚え書きです。


CANドライバ部は、RH850は設定項目も多く、RH850のCAN機能のアプリケーションノートも無いようです。一からは大変そうなのでサンプルコードを探します。現在、ルネサスから提供されているのは、RH850/U2A 用だけでした。同サンプルコードは、製品コンパイラやマイコン購入者でなくても、ルネサスアカウントを持っていればダウンロードできました。

取り急ぎ RH850F1KHの設定でbuildしてみると、ほぼほぼ通ってしまいます。それはRH850/U2A 専用にSFRを定義が作成しれていて、RH850F1KHiodefine.h は参照していないからです。SFRアドレスもCAN構成も大分違います。RH850F1KH用に移植が必要です。


RH850 CAN機能はこりまでと異なる用語等があります。実用ベースでの見たところ以下のように理解しました。

用語・機能解釈
グローバルCAN「グローバル=世界」 ではないです。RH850のみでのCAN2.0、CAN-FD、複数のCAN UNITを管理するための機構です。
クラシカルCANCAN2.0B のこと。
CAN UNIT所謂CAN コントローラ、この配下に複数のCANチャンネルが連なります。
SFR添え字UINT番号、CH番号等の略記号。マニュアルの表 24.9 添字 に説明あり。
受信ルール従来のCAN Message Box のCAN IDマスクと同意。
送信Buffer従来のCAN Message Box の送信専用版。
受信Buffer従来のCAN Message Box の受信専用版。
データBit Rate少なくとも CAN2.0B では通常Bit Rateと同じ。
FIFO Buffer従来のCAN Message Boxとは別の先入れ先出し方式のMessage Boxの模様。未だ動作未確認です。
割り込みベクタ方式一般的な「テーブル参照方式」と、「優先度に基づいた直接分岐方式」があります。

U2A用サンプルコードのSFR定義: r_rh850_can_sfr.h を変更していきます。ルネサスのマニュアルには、UNIT番号、Channel番号、Buffer番号などぱ可変要素としてSFR名が記載されています。例えば、RCFDCnCFDCmDCFGnはUNIT、mはChannelを示します。実名は RCFDC0CFDC0~7DCFG、iodefine.h はでは RCFDC0.CFDC0~7DCFG となります。よってマニュアルに合わせたSFR名でコードを書いた方が検索性に優れます。 U2A用サンプルコードはそのようになっていますがF1KHと命名が違うため合わせていきます。

  • RSCFDnXXX は、 RCFDCnXXX に一括置換。

これでマニュアルとSFRの対比がつかめるようになります。

U2A用サンプルコードのSFR定義: r_rh850_can_sfr.h では、SFRアドレスは CAN UNIT 毎のベースアドレスからの計算式になっています。そこでまず r_rh850_can_cfg.h にて CAN UNIT のベースアドレスを変更します。U2A用は CAN UNIT は一つと決め打ちです。F1KHの最多pin種では2つのため、ベースアドレスはグローバル変数とし逐次切替式としました。

uint32_t	CAN_START_ADDR;
#define R_CAN_SelectUnit( ch ) 	( CAN_START_ADDR = CAN_START_ADDR [ ch ] )
const uint32_t  CAN_UINT_ADDR[2] = { 0xFFD00000UL /* UNIT 0 */, 0xFFD20000UL /* UNIT 1 */};

あとは SFR名 で マニュアルを検索し、アドレスを変更していきます。アドレスはGlobal系の数個以外は全部違います。50個近くあるので大変です。ついでに日本語名もコメント付与します。計算式のオフセットやCH毎増分など違う場合もあるので修正します。以下 r_rh850_can_cfg.hの一例です。

#define RCFDCnCFDTMCp(p) CAN_REG8 (0x0250 + (p)) // 送信バッファ制御(p)
・・・中略・・・
#define RCFDCnCFDCFCCk(k) CAN_REG32(0x0118 + (0x04 * (k))) // 送受信FIFOバッファ構成/制御(k)
#define RCFDCnCFDCFSTSk(k) CAN_REG32(0x0178 + (0x04 * (k)))	// 送受信FIFOバッファステース(x)

U2AにしかないSFRの削除、F1KHしかないSFRの追加をします。こちらはマニュアルと照らし合わせていくしかありません。手間がかかります。以下 r_rh850_can_sfr.h の一例です。

#define RCFDCnCFDGTINTSTS0  CAN_REG32(0x0610)	// Global TX割り込みステータス0 
#define RCFDCnCFDGTINTSTS1  CAN_REG32(0x0614)	// Global TX割り込みステータス1 ☆
#define RCFDCnCFDGFDCFG     CAN_REG32(0x0624)		// Global FDコンフィグレーション ☆

U2AF1KHで、同じSFRでBit構成が異なるのものがあります。こちらはマニュアルと照らしあわせていくしかありません。弊方ではコメントを付与ついでに調べていきます。幸い数はすくないです。決定的に変更しないとまずいのはBaudrate( RCFDCnCFDCmNCFG, RCFDCnCFDCmDCFG )ですまさかと思い弊方では直ぐに気付けませんでした。トラップです。以下 r_rh850_can_sfr.h の一例です。

#define CAN_DTSEG2_BIT_POS              20U	// Data BitRate Timeセグメント2 (3bit)
#define CAN_DTSEG1_BIT_POS              16U	// Data BitRate Timeセグメント1 (4bit)

U2A用では、CAN構成は r_rh850_can_cfg.[ch] に記述されています。F1KHに構成が異なるため変更が必要です。弊方では CAN UNIT 毎に定義できるようにしています。

#define MAX_CH_NUM_U0					8
#define MAX_CH_NUM_U1					4	// 324pinの場合のみ
#define MAX_CH_NUM(unit)      ( unit == 0 ? MAX_CH_NUM_U0 : MAX_CH_NUM_U1 )
#define USED_UNIT_NUM         1	// 324pinは2
#define MAX_TX_BUF_OF_CH			32	// CH毎の送信Buffer数

送信バッファ数はF1KHが多いので追加します。こちらは r_rh850_can_drv.h。

#define CAN_TXBUF16         16U
#define CAN_TXBUF17         17U
・・・中略・・・
#define CAN_TXBUF31         31U
#define CAN_MAX_TXBUF_NUM   32U

ドライバプログラムで取り扱う構造体もF1KHでは少し違います。CAN Frame と CAN Channel 定義 を修正します。以下CAN Frameの例です。

typedef struct {
・・・中略・・・
	uint32_t TS :16;   /* 受信時刻                           */
	uint32_t RESERVE:12;  /* 空き                            */
	uint32_t DLC :4;	/* DLC(データ部長)                   */
	uint32_t TMESI:1;	/* CAN-FD 0:ERRアクティブNode 1:ERRパッシブNode */
	uint32_t TMBRS:1;	/* CAN-FD DATA領域Bitrate 0:変わらない 1:変わる */
	uint32_t TMFDF:1;	/* CAN-FD 0:CAN2.0 1:CAN-FD */
	uint32_t RESERVE2:13;	/* FD Status(TMESI,TMESI           */
	uint32_t LBL:16;	/* 受信バッファラベル                  */
・・・中略・・・
} can_frame_t;


自動コード生成部を使う部分、セルフテスト部は特に不要です。以下.c は、Build対象外にします。

  • r_rh850_can_int.c
  • r_rh850_can_io.c
  • r_rh850_can_drv2.c

r_rh850_can_drv.c が CANドライバAPI 部です。コメントは少な目で英語なので、内容を確認しながらコメントを追記していきます。全APIで行うとタイヘンなので取り急ぎ使いそうなもののみです。併せてCAN UNIT番号を指定可能にしておきます。以下一部例です。

/******************************************************************************
 * Function Name: R_Can_Init
 * Description  : CAN UNIT毎に初期化を行う。
 *                - CAN用RAMクリア
 *                - Gobal STOPモード解除を待つ。
 *                - CAN2.0 と FD切替え
 *                - ch毎のBaudrate設定
 *                - 受信Ruleの設定
 *                - 受信Bufferの設定
 *                各設定は、r_rh850_can_cfg.c で行う。
・・・中略・・・
 *	              CAN_RTN_ARG_ERROR -
 *	                  パラメータエラー
 ******************************************************************************/
Can_RtnType R_CAN_Init(
	const uint8_t unitNum,	// i : CAN Unit
	const uint8_t chNum,		// i : 使用CAN ch数
	const BOOL useCanFd		  // i : CAN FD使用
) {

修正内容の整合性があっているか、仮Buildします。自己あやまり以外で引っかかる部分が少しあります。RCFDCnCFDTMIECm のポインタ型はU2A用では16bitでしたがF1KHでは32bitにする必要があります。

・・・中略・・・
volatile uint32_t *p_TMIEC;
・・・中略・・・

オリジナルのままでは使いにくそうなので多少機能追加しています。 Callback 機構とそのための割込みに仕込む EVEVT Dispacher です。

/******************************************************************************
 * Function Name: R_CAN_DispatchEevent
 * Description  : CAN ENEVTを処理する
 *                割込ハンドラから呼ばれます
 *                割込ペリフェラルとのI/F部です。
 * Remark:
 * - 現在は、送信Bufferによる送信のみサポート。
 * - RH850では、受信Bufferの割込みはない。
 ******************************************************************************/
void R_CAN_DispatchEevent(
	const uint8_t		unitNum, // i : CAN Unit
	can_ch_t			  ch_idx,	 // i : CAN Ch番号
	const uint8_t		event		 // i : ENEVT No.
) {
・・・中略・・・
/******************************************************************************
 * Function Name: R_CAN_SetRecvCallback
 * Description  : CAN受信Callbckを設定する。
 ******************************************************************************/
void R_CAN_SetRecvCallback(
	const uint8_t		  unitNum,	// i : CAN Unit
	can_ch_t			    ch_idx,		// i : CAN Ch番号
	CAN_RECV_CALLBAK	func		  // i : 受信Callbak
) {
・・・中略・・・
/******************************************************************************
 * Function Name: R_CAN_SetSendCallback
 * Description  : CAN送信Callbckを設定する。
 ******************************************************************************/
void R_CAN_SetSendCallback(
	const uint8_t		 unitNum,	// i : CAN Unit
	can_ch_t			    ch_idx,	// i : CAN Ch番号
	CAN_SEND_CALLBAK	func		// i : 受信Callbak
) {
・・・中略・・・
/******************************************************************************
 * Function Name: R_CAN_SetErrCallback
 * Description  : CANエラーCallbckを設定する。
 ******************************************************************************/
void R_CAN_SetErrCallback(
	const uint8_t		unitNum,	// i : CAN Unit
	can_ch_t			  ch_idx,		// i : CAN Ch番号
	CAN_ERR_CALLBAK	func		  // i : 受信Callbak
) {


自動コード生成でのmainモジュール: r_cg_main.c の実装は以下のような感じです。

・・・中略・・・
/* Start user code for include. Do not edit comment generated here */
#include "r_rh850_can_sfr.h"
#include "r_rh850_can_drv.h"
/* End user code. Do not edit comment generated here */
・・・中略・・・

void main(void)
{
・・・中略・・・
    r_main_userinit();
    r_main_startCan();

    while ( 1U ) {
・・・中略・・・
}

void r_main_startCan(void)
{
	// CAN Controller 初期化
	if ( R_CAN_Init( 0/*UNIT*/, 2/*CH数*/, FALSE/*CANFD*/ ) != CAN_RTN_OK ) {
		return;
	}

	// Ch0 Callbak設定 
	R_CAN_SetErrCallback ( 0/*UNIT*/, 0/*ch.*/, r_main_errCan0  );
	R_CAN_SetRecvCallback( 0/*UNIT*/, 0/*ch.*/, r_main_recvCan0 );
	R_CAN_SetSendCallback( 0/*UNIT*/, 0/*ch.*/, r_main_sendCan0 );

	// Ch1 Callbak設定
	R_CAN_SetErrCallback ( 0/*UNIT*/, 1/*ch.*/, r_main_errCan1  );
	R_CAN_SetRecvCallback( 0/*UNIT*/, 1/*ch.*/, r_main_recvCan1 );
	R_CAN_SetSendCallback( 0/*UNIT*/, 1/*ch.*/, r_main_sendCan1 );

	// Global CAN開始
	if ( R_CAN_GlobalStart( 0/*UNIT*/ ) != CAN_RTN_OK ) {
		return;
	}

	// CAN 0ch 開始
	if ( R_CAN_ChStart( 0/*UNIT*/, 0 /* Ch. */ ) != CAN_RTN_OK ) {
		return;
	}

	// CAN 1ch 開始
	if ( R_CAN_ChStart( 0/*UNIT*/, 1 /* Ch. */ ) != CAN_RTN_OK ) {
		return;
	}

CAN機能と割込は場合、HSBRH850F1KH176 の回路実装から各種割当ては以下のようになります。なお受信Buffer による受信割込みはRH850は無いです。送受信FIFOはFIFO Buffer専用の割込みのようです。

Chanel機能ポート名ポート番号送信完割込番号送受信FIFO割込番号エラー割込番号
0受信CAN0RXP10_02524
0送信CAN0TXP10_1262524
1受信CAN1RXP0_2114113
1送信CAN1TXP0_3115114113

CANペリフェラルの定義は、e2stdioの自動生成で以下のように行います。

上記の設定で、対応するポートの入出力の設定はされるので、以下2つの個別の設定はしないです。

CANペリフェラルの定義は、Pin.cR_Pins_Create に生成されます。しかし生成されるだけで、 Config_PORT.c などからcallされていません。呼出しコードは自分で書く必要がありました。弊方では直ぐ気付けずトラップでした。弊方では、Config_PORT_user.c から以下のように呼出追加しました。

void R_Config_PORT_Create_UserInit(void)
{
    /* Start user code for user init. Do not edit comment generated here */
	  R_Pins_Create();
    /* End user code. Do not edit comment generated here */
}

割込みの設定は一旦以下ようにします。これでハンドラのベクタテーブルへの定義とスケルトン、割込み有効無効のAPIが生成されます。

しかしCAN割込みの場合、割込み優先度割り込みベクタ方式 の設定コードは自動生成されませんでした。タイマ等では自動生成されるので、ここはトラップです。デフォルトは割込み優先度です。そのまま動かすと優先度15割込み未定義で無限loopしました。boot.asmを見ると自動生成デフォルト状態では _Dummy_EI で無限loopになってます。この方式は使わない方が無難なようです。

割込み設定に関するコード例は以下のとおりです。r_main_startCan() の各Ch.初期化後に追記しましたた。

	/* CH0 割込み方式とLEVEL設定 */
    INTC1.ICRCAN0ERR.BIT.TBRCAN0ERR = _INT_TABLE_VECTOR;
    INTC1.ICRCAN0REC.BIT.TBRCAN0REC = _INT_TABLE_VECTOR;
    INTC1.ICRCAN0TRX.BIT.TBRCAN0TRX = _INT_TABLE_VECTOR;
    INTC1.ICRCAN0ERR.UINT16 &= _INT_PRIORITY_LEVEL4;
    INTC1.ICRCAN0REC.UINT16 &= _INT_PRIORITY_LEVEL4;
    INTC1.ICRCAN0TRX.UINT16 &= _INT_PRIORITY_LEVEL4;

	/* CH1 割込み方式とLEVEL設定 */
    INTC2.ICRCAN1ERR.BIT.TBRCAN1ERR = _INT_TABLE_VECTOR;
    INTC2.ICRCAN1REC.BIT.TBRCAN1REC = _INT_TABLE_VECTOR;
    INTC2.ICRCAN1TRX.BIT.TBRCAN1TRX = _INT_TABLE_VECTOR;
    INTC2.ICRCAN1ERR.UINT16 &= _INT_PRIORITY_LEVEL4;
    INTC2.ICRCAN1REC.UINT16 &= _INT_PRIORITY_LEVEL4;
    INTC2.ICRCAN1TRX.UINT16 &= _INT_PRIORITY_LEVEL4;

    // CH0 割込み有効化
	IntCAN0Error_enable_interrupt( );
	IntCAN0TxRxFIFORxCmp_enable_interrupt( );
 	IntCAN0Tx_enable_interrupt( );

    // CH1 割込み有効化
	IntCAN1Error_enable_interrupt( );
	IntCAN1TxRxFIFORxCmp_enable_interrupt( );
 	IntCAN1Tx_enable_interrupt( );


以上おおざっぱなところ、初期化は通過し、送受信スタンパイまで行き着きました。しかし試行錯誤や細かい部分でハードルもあり、ここまで延べて2weekくらいは要したかと思います。

送信編につづきます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です