RL78 データフラッシュメモリ(EEL)ライブラリの使い方を分かりやすく

当ページのリンクには広告が含まれています。

RL78には「データ・フラッシュ・メモリ」を使用するためのライブラリが有ります。
これを使うとEEPROMの代わりのようなことが可能。
ただしSRAMやFRAMのように、直番地でアクセスできるわけではありません。

興味があるところを読む

ライブラリの中身とインストール

データ・フラッシュ・メモリへのアクセス用のライブラリを試してみました。
さすがのルネサスで、いつも通り分かりにくい。

アクセスには専用のライブラリが必須である。
FDLとEELと言う名前の二種類のライブラリがあり、FDLは低レベルのライブラリで、EELは高レベルのライブラリ。

・FDLは小型高速だけど自分でやることが多い。
・EELはでかいし遅いけどアクセスが簡単になる。

なおEELは下請け処理としてFDLを使っているので、FDLがBIOSでEELは上位ドライバみたいな雰囲気かも。
TIで言えばCSLとBSLみたいな関係。
この辺の事はマニュアルのP5(1.はじめに)辺りに書かれてるけど、例によって役人言葉表現なんで頭が痛くなる。

ライブラリには旧来のCA78K0Rコンパイラ用と新型のCC-RLコンパイラ用の二種類があるので、自分の環境に合わせてDLする。
・「データフラッシュライブラリ」と言うのがFDL(ここでは使わないので不要)。
・「EEPROMエミュレーションライブラリ」がEELだ(EELライブラリにはFDLも含まれている)。

この中にはライブラリの他にマニュアルとインストール方法とサンプルソフトが入っている。
なおググって見付けたEELマニュアルは、サンプルとは異なるので使わないこと。
ここではCA78K0Rコンパイラ用のEELを使ってます。

スポンサーリンク

EELのインストール

CS+で新規プロジェクトを開始し、インストールマニュアルに従ってEELのソースなどを追加する。

ファイルの上で右クリックして既存のファイルを追加(下図では既に追加済み)

DLしたライブラリの中のこの辺のフォルダ内のソースを入れてやる。
Cで作るんでASMやINCのファイルは不要。

ところでここはいじらなくて良いのかな?
アクセス禁止のままでも動いてるから、多分ライブラリがDFLCTL=0x01をやってるんだと思う。
動かないようならここを操作してみてください。

EELの操作

EEL単独で使う事はあり得ないんで、ここでは当然CS+のコード生成と組み合わせて使います。
まずはコード生成のクロック発生回路を適当に決めてコード生成する。

EELのビルド

コード生成したものをビルドするとエラーになる。
エラー原因はmain()関数とWDTの割り込みベクタの重複なので解決する。

// main関数の重複を修正
RA78K0R error E3404: Multiple symbol definition ‘_main’ in file
‘DefaultBuild\r_eel_sample_c.rel’.
First defined in file ‘DefaultBuild\r_main.rel’

r_eel_sample_c.c と r_main..c の両方にmain()がある。
→ r_main.c 内のmain()関数をコメントアウトするか名前を変える。

// 割り込みベクタvect04の重複を修正
RA78K0R error E3404: Multiple symbol definition ‘_@vect04’ in file
‘DefaultBuild\r_cg_wdt_user.rel’.
First defined in file ‘DefaultBuild\r_eel_sample_c.rel’

r_cg_wdt_user.c と r_eel_sample_c.c の両方に同じvect04がある
→ コード生成で「WDTを使わない」設定にする。

もしWDTを使いたいなら r_eel_sample_c.c のWDTベクタ宣言をコメントアウトし
↓r_cg_userdefine.h にプロトタイプ宣言を追加
r_cg_wdt_user.c 内の割込み関数からコールしてやる
スポンサーリンク

EELをいじる

早速動かしてみた。
サンプルでは0x55と0xAAをデータフラッシュに書き込み、それを読み出して確認してる。
サンプルの動きをざっと追ってみた。
コードの流れはこんな感じ。

  • 「/* data write processing */」で、0xAAと0x55を dubWriteBuffer にセットしてからデータフラッシュに書き込む。
  • 「/* data read processing */」で、データフラッシュから dubReadBuffer に読み出す。
  • 書いた値と読み出した値が等しいかチェック。
  • Closeなどの後処理をする。
  • 永久ループ。

動きの確認

動きの確認はこんな感じで。
「/* Execute EEL_CMD_READ command */」の下の行にブレークポイントをセットしてからRUNする。
dubReadBuffer を見るとゼロになってる。
永久ループの中にブレークポイントをセットしてからRUNする。
dubReadBuffer を見ると0xAAと0x55になってる。
ふむふむ、確かに読み出せてる。

ソースの変更箇所

ソースで変更したのは fdl_descriptor.h の「#define FDL_SYSTEM_FREQUENCY 32000000」だけで、CPUのクロックをF12用に32MHzにした。
このサンプルはmain()の中で全部やっちゃってるから、OpenとWriteとReadとCloseを関数に分けてやれば使いやすくなりそう。

関数に分ける

では関数に分けてみます。
サンプルでは2byteのR/Wなので、ついでにこれを255byteのR/Wに変更する。
R/Wするbyte数はID番号で指定する(byte数で直接指定することは出来ないのだ。途中にテーブルをかましてるんでここが分かりにくかった)。
例えばID=1なら2byte,ID=2なら3byte・・・ID=8なら255byteのR/Wだ。
ID番号とbyte数の関係は変更することも可能で、eel_descriptor.c に書いてある。
自分の用途では書き換え回数が多くはないのでID=8の255byteのR/Wにしておけば万能だろうと思う。

もし不足するようならID7とかID6もtype_Zにすれば255byteのR/Wが可能になるはずだ。
・1ブロックの最大サイズは255byteまでで、IDの指定範囲(EEL_VAR_NO)は1~64。
・データフラッシュは4Kbyteだけど、半分だけ使えとか書いてあったみたいだから、ID1~ID8全部を255byteにすれば丁度半分の2040byte?
ま、必要になったらその時に調べようと思います。

eel_descriptor.c にID番号とR/Wサイズの関係を指定するテーブルがある。sizeof(type_Z)とか書いてあるけど、直接255とか128とか数値で書いても同じはず。

// type_Aやtype_Zなどの宣言は eel_user_types.h にある
// ここでデータフラッシュをR/Wするbyte数を指定してる
__far const eel_u08 eel_descriptor[EEL_VAR_NO+2] =
{
(eel_u08)(EEL_VAR_NO), /* variable count */ \
(eel_u08)(sizeof(type_A)), /* id=1 2byte */ \
(eel_u08)(sizeof(type_B)), /* id=2 3byte */ \
(eel_u08)(sizeof(type_C)), /* id=3 4byte */ \
(eel_u08)(sizeof(type_D)), /* id=4 5byte */ \
(eel_u08)(sizeof(type_E)), /* id=5 6byte */ \
(eel_u08)(sizeof(type_F)), /* id=6 10byte */ \
(eel_u08)(sizeof(type_X)), /* id=7 20byte */ \
(eel_u08)(sizeof(type_Z)), /* id=8 255byte */ \
(eel_u08)(0x00), /* zero terminator */ \
};
eel_user_types.h

typedef eel_u08 type_A[2];
typedef eel_u08 type_B[3];
typedef eel_u08 type_C[4];
typedef eel_u08 type_D[5];
typedef eel_u08 type_E[6];
typedef eel_u08 type_F[10];
typedef eel_u08 type_X[20];
typedef eel_u08 type_Z[255];

EELの初期化と書き込みと読み出しと終了処理のコール。
データフラッシュへ書き込めたかどうかの確認は、デバッグ状態でメモリウインドウで0x1F000を表示させ、ちょっと下の方を見る方法もあり。

r_main.c のmain()関数はこんな感じ。
void main(void)
{
R_MAIN_UserInit();
/* Start user code. Do not edit comment generated here */
eel_Init(FALSE); // FALSE=強制Formatはしない
eel_Write(); // ここをコメントアウトしてやればデータフラッシュへ保存されたことを確認可能
eel_Read();
eel_Finish();
while (1U)
{
;
}
/* End user code. Do not edit comment generated here */
}

↓R/W関数にデータポインタを渡すなどすれば使いやすいけど、今は未だこれで良いや。
もうちょい動作が確認できてから改造の予定です。

//======================================================================================
//
// データフラッシュ(0xF1000~の4K)をEELでアクセスする 2015/08/12 
// ・どの関数も1秒以内に終わるからWDTのリセットはしていない
// 
//======================================================================================
#include "r_cg_macrodriver.h"
#include "r_cg_cgc.h"
#include "r_cg_port.h"
#include "r_cg_userdefine.h"
#include "r_cg_wdt.h"

#include <string.h>             
#include "fdl.h"                    /* FDL library header file               */
#include "fdl_types.h"              /* FDL types definition header file      */
#include "fdl_descriptor.h"         /* FDL descriptor header file            */
#include "eel.h"                    /* EEL library header file               */
#include "eel_types.h"              /* EEL types definition header file      */
#include "eel_descriptor.h"         /* EEL descriptor header file            */
#include "eel_user_types.h"         /* EEL user types definition header file */

//  IDでR/Wするbyte数を指定する(例えばID=1なら2byte,ID=8なら255byteだ)。
//  変更したいなら eel_descriptor.c を見れ(まぁ255byteにしておけば万能だろうと思う)。
#define IDNUM   8      // 255byteのR/Wを行う

// global variable
#define SAM_WRITE_SIZE  255         // テスト用のR/Wバッファのサイズ
eel_request_t   dtyEelReq;
eel_u08         dubWriteBuffer[ SAM_WRITE_SIZE ];
eel_u08         dubReadBuffer[ SAM_WRITE_SIZE ];
fdl_status_t    fdlStatus = 0; 
eel_u08         err_flag = 0;

//======================================================================================
// EELの初期化
//  formatflag:TRUEなら強制的にFormatを行う
//======================================================================================
bool eel_Init(bool formatflag)
{
    dtyEelReq.address_pu08   = 0;
    dtyEelReq.identifier_u08 = 0;
    dtyEelReq.command_enu    = 0;
    dtyEelReq.status_enu     = 0;
    // 開始
    fdlStatus = FDL_Init( &fdl_descriptor_str );    // FDLの初期化
    if( fdlStatus == FDL_OK ) {                     // OKなら
        FDL_Open();                                 // FDLオープン
        dtyEelReq.status_enu = EEL_Init();          // EELの初期化
        if( dtyEelReq.status_enu == EEL_OK ) {      // OKなら
            EEL_Open();                             // EELオープン
            do {
                // EEL開始コマンド発行
                dtyEelReq.command_enu = EEL_CMD_STARTUP;                    // コマンドをセットし
                EEL_Execute( &dtyEelReq );                                  // 実行し
                while( dtyEelReq.status_enu == EEL_BUSY ) EEL_Handler();    // 実行完了を待つ
                // EELブロック不整合エラーならFormatを実行する
                if (formatflag == true || dtyEelReq.status_enu == EEL_ERR_POOL_INCONSISTENT ) {
                    dtyEelReq.command_enu = EEL_CMD_FORMAT;
                    EEL_Execute( &dtyEelReq );
                    while( dtyEelReq.status_enu == EEL_BUSY ) EEL_Handler();
                    formatflag = false;
                }
            // Formatしたら再度EEL開始コマンドを実行する
            } while((dtyEelReq.command_enu == EEL_CMD_FORMAT) && (dtyEelReq.status_enu  == EEL_OK));
        }
    }
    return ( dtyEelReq.status_enu == EEL_OK )? true:false;
}
//======================================================================================
// EELでの書き込み
//======================================================================================
bool eel_Write(void)
{
    int i;
    do {
        // 適当にデータをセットしてやる
        for (i=0;i<SAM_WRITE_SIZE; ++i) dubWriteBuffer[i] = (uint8_t)i;
        dubWriteBuffer[254] = 0x10;
        dubWriteBuffer[253] = 0x20;
        // EEL書き込みコマンド発行
        dtyEelReq.address_pu08   = dubWriteBuffer;
        dtyEelReq.identifier_u08 = IDNUM;   // IDで書き込みbyte数を指定(ID8=255byte)
        dtyEelReq.command_enu    = EEL_CMD_WRITE;
        EEL_Execute( &dtyEelReq );
        while( dtyEelReq.status_enu == EEL_BUSY ) EEL_Handler();
        // データを書き込める領域が存在しないエラーならRefreshを実行する
        if (dtyEelReq.status_enu == EEL_ERR_POOL_FULL) {
            dtyEelReq.command_enu    = EEL_CMD_REFRESH;
            EEL_Execute( &dtyEelReq );
            while( dtyEelReq.status_enu == EEL_BUSY ) EEL_Handler();
        }
        // Refreshしたら再度EEL書き込みコマンドを実行する
    } while((dtyEelReq.command_enu == EEL_CMD_REFRESH) && (dtyEelReq.status_enu  == EEL_OK));
    return ( dtyEelReq.status_enu == EEL_OK )? true: false;
}
//======================================================================================
// EELでの読み出し
//======================================================================================
bool eel_Read(void)
{
    // EEL読み出しコマンド発行
    dtyEelReq.address_pu08   = dubReadBuffer;
    dtyEelReq.identifier_u08 = IDNUM;   // IDで読み出しbyte数を指定(ID8=255byte)
    dtyEelReq.command_enu    = EEL_CMD_READ;
    EEL_Execute( &dtyEelReq );
    while( dtyEelReq.status_enu == EEL_BUSY ) EEL_Handler();
    return  (dtyEelReq.status_enu == EEL_OK )? true:false;
}
//======================================================================================
// EELの終了処理
//======================================================================================
void eel_Finish(void)
{
    dtyEelReq.command_enu = EEL_CMD_SHUTDOWN;
    EEL_Execute( &dtyEelReq );
    while( dtyEelReq.status_enu == EEL_BUSY ) EEL_Handler();
    EEL_Close();
    FDL_Close();
}
//-----------------------------------------------
// EELのコマンド実行と完了待ち(未使用)
//-----------------------------------------------
eel_status_t _eelCmd(eel_command_t cmd)
{
    dtyEelReq.command_enu = cmd;                            // コマンドをセットし
    EEL_Execute(&dtyEelReq);                                // 実行し
    while(dtyEelReq.status_enu==EEL_BUSY) EEL_Handler();    // 実行完了を待つ
    return dtyEelReq.status_enu;                            // 結果を返す
}
スポンサーリンク
スポンサーリンク
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


興味があるところを読む