前記事では 、北斗電子さんHSBRH850F1KH176  をターゲットにRH850自動コード生成を行いました。しかしCANドライバは自動コード生成の対象になっていません。そこで自動コード生成とHSBRH850F1KH176   に合わせたCANドライバプログラムを用意することにしました。その覚え書きです。
 
サンプルコードの入手 
CANドライバ部は、RH850は設定項目も多く、RH850のCAN機能のアプリケーションノートも無いようです。一からは大変そうなのでサンプルコードを探します。現在、ルネサスから提供されているのは、RH850/U2A   用だけでした。同サンプルコードは、製品コンパイラやマイコン購入者でなくても、ルネサスアカウントを持っていればダウンロードできました。
取り急ぎ RH850F1KH  の設定でbuildしてみると、ほぼほぼ通ってしまいます。それはRH850/U2A   専用にSFRを定義が作成しれていて、RH850F1KH  のiodefine.h   は参照していないからです。SFRアドレスもCAN構成も大分違います。RH850F1KH  用に移植が必要です。
 
RS-CANFDの理解 1
RH850 CAN機能はこりまでと異なる用語等があります。実用ベースでの見たところ以下のように理解しました。
用語・機能 解釈 グローバルCAN 「グローバル=世界」 ではないです。RH850のみでのCAN2.0、CAN-FD、複数のCAN UNITを管理するための機構です。 クラシカルCAN CAN2.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→F1KH  移植: 定義編  
— SFR # d e f i n e 名 を 変 更 – — 
U2A  用サンプルコードのSFR定義: r_rh850_can_sfr.h   を変更していきます。ルネサスのマニュアルには、UNIT番号、Channel番号、Buffer番号などぱ可変要素としてSFR名が記載されています。例えば、RCFDCn CFDCm DCFG  のn  はUNIT、m  はChannelを示します。実名は RCFDC0CFDC0~7DCFG、iodefine.h   はでは RCFDC0.CFDC0~7DCFG  となります。よってマニュアルに合わせたSFR名でコードを書いた方が検索性に優れます。 U2A  用サンプルコードはそのようになっていますがF1KH  と命名が違うため合わせていきます。
RSCFD  nXXX は、 RCFDC  nXXX に一括置換。  
 
これでマニュアルと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)  
 
— 過 不 足 S F R の 追 加 / 削 除 — 
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コンフィグレーション ☆  
 
— B I T 構 成 の 変 更 — 
U2A  とF1KH  で、同じ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)  
 
— CAN構成の変更加  —
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 ;  
 
U2A→F1KH  移植: ロジック編 
— 不要.c の 除 外 — 
自動コード生成部を使う部分、セルフテスト部は特に不要です。以下.c は、Build対象外にします。
r_rh850_can_int.c  
r_rh850_can_io.c  
r_rh850_can_drv2.c  
 
— A P I 部 の 日 本 語 コ メ ン ト 付 与 と CAN UNIT番号の指定化— 
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使用  
) {  
— 仮 B u i l d — 
修正内容の整合性があっているか、仮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  
) {  
 
U2A→F1KH  移植: main実装 
— m a i n へ の 組 込 み — 
自動コード生成での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 ;  
	}  
— C A N ペ リ フ ェ ラ ル と 割 込 み の 追 加 — 
CAN機能と割込は場合、HSBRH850F1KH176   の回路実装から各種割当ては以下のようになります。なお受信Buffer による受信割込みはRH850は無いです。  送受信FIFOはFIFO Buffer専用の割込みのようです。
Chanel 機能 ポート名 ポート番号 送信完割込番号 送受信FIFO割込番号 エラー割込番号 0 受信 CAN0RX P10_0 — 25 24 0 送信 CAN0TX P10_1 26 25 24 1 受信 CAN1RX P0_2 — 114 113 1 送信 CAN1TX P0_3 115 114 113 
 
CANペリフェラルの定義は、e2stdioの自動生成で以下のように行います。
 
上記の設定で、対応するポートの入出力の設定はされるので、以下2つの個別の設定はしないです。
 
CANペリフェラルの定義は、Pin.c    の R_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くらいは要したかと思います。
送信編につづきます。