Micom Car Rally NET
MCR - Micom Car Rally
2015大会 マイコンカーラリーとは? 今から始めるマイコンカーラリー 技術情報 大会記録 MCRファン倶楽部 お問い合せ
マイコンカーラリートップへ マイコンカーラリートップへ
技術情報 >> R8C/M12A 技術情報
技術情報
アプリケーション
R8C/M12A 技術情報
上位入賞マイコンカー
参加者レポート
よくある質問(FAQ)
ダウンロード
R8C/M12A 技術情報
データフラッシュにデータを保存する

内容
R8C/M12Aには、プログラムを記録するプログラムROM(8KB)の他に、2KBのデータフラッシュ(EEP-ROM)が内蔵されています。データフラッシュは、簡単に値を書き替えられるため、パラメータの保存や、情報の記録などに使用することができます。今回は、ディップスイッチの状態を保存します。

ワークスペース

r8cm12a_data_flash_100.zip
ファイルを解凍し、フォルダを「c:\worksapce」に入れてください。

回路
ブレッドボードの回路を下図に示します(「スイッチの入力(I/Oポートの入力)」と同じ回路です)。

回路図

ブレッドボード実装図
ブレッドボードの実装を下図に示します(「スイッチの入力(I/Oポートの入力)」と同じ実装です)。
ブレッドボード実装図

データフラッシュ

R8C/M12Aのメモリマップを下図に示します。

メモリマップ

データフラッシュは、0x3000〜0x37ff番地まで、2048バイトあります。R8C/M12Aマイコンのデータフラッシュへの書き込みについて、下記に示します。

●書き込みは、0x3000〜0x37ff番地のどのアドレスにもできます。
●データフラッシュエリアの初期値は、0xffです。
●一度書き込むと、イレーズ(0xffに戻すこと)するまで同じアドレスには書き込めません。例えば、0x3000番地の値を0xff(初期値)→0x00にすることはできますが、0x00から違う値にはできません。違う値にするためには、いったんイレーズ作業をして0xffに戻す必要があります。(※正確には、書き込みはbitを"1"から"0"にすることはできますが、その逆にはできません。例えば、0xff→0xfe、0xfe→0xfcにすることはできます。0xfc→0xfeにはできません。)
●イレーズ(0xffに戻すこと)は、ブロックごとに行います。具体的には、ブロックA(0x3000〜0x33ff)をイレーズするか、ブロックB(0x3400〜0x37ff)をイレーズするか、しかできません。0x3000番地だけをイレーズする、ということはできません。
●今回、データフラッシュへデータの書き込み、読み込み、イレーズする関数を用意しました(これから説明します)。これらの関数を使って、簡単にデータフラッシュを使用することができます。

プログラム「r8cm12a_data_flash_lib.c」の説明

「r8cm12a_data_flash_lib.c」は、R8C/M12Aのデータフラッシュを制御するための関数をまとめたファイルです。このCファイルをルネサス統合開発環境に組み込んで使用します。このファイルを組み込むと使うことのできる関数を説明します。

データフラッシュへの書き込み

書式 int writeDataFlash(
  unsigned int w_address, signed char *r_address, int count );
内容 データフラッシュへデータを書き込みます。
引数 ●書き込み先アドレス:0x3000〜0x37ff
●書き込むデータがある配列
●書き込むデータ数
戻り値 1:エラーなし 0:エラーあり
使用例

// グローバル変数
signed char data_buff[ 16 ]; /* 一時保存エリア  */

  // 0x3000番地に、data_buff配列の内容を
  // 16バイト分、書き込む
  ret = writeDataFlash( 0x3000, data_buff, 16 );

  if( ret != 0 ) {
    /* 書き込みエラーが無いときのプログラム */
  } else {
    /* 書き込みエラーがあるときのプログラム */
  }
注意 ※ブロックA(0x3000〜0x33ff番地)とブロックB(0x3400〜0x37ff番地)をまたぐ書き込みはできません。次のプログラムは0x33f0番地から16バイトのデータを書き込みます。16バイトということは、0x33f0〜0x340f番地まで書き込むことになり、0x33ffと0x3400番地をまたぐのでエラーになります。このような場合は、2回に分けて書き込んでください。
  // 0x33f0番地に、data_buff配列の内容を
  // 16バイト分、書き込む
  ret = writeDataFlash( 0x33f0, data_buff, 16 );
  ↑ エラーになる!!

データフラッシュから読み込み

書式 void readDataFlash(
  unsigned int r_address, signed char *w_address, int count );
内容 データフラッシュからデータを読み込みます。
引数 ●読み込み元アドレス:0x3000〜0x3fff
●読み込んだデータを格納する配列
●読み込むデータ数
戻り値 無し
使用例
// グローバル変数
signed char data_buff[ 16 ]; /* 一時保存エリア  */

  // 0x3000番地から、data_buff配列に、
  // 16バイト分、読み込む
  readDataFlash( 0x3000, data_buff, 16 );

ブロックイレーズ

書式 int blockEraseDataFlash( unsigned int address );
内容 データフラッシュのブロックをイレーズ(0xffに戻す)します。
引数 イレーズしたいブロックのアドレス
●ブロックAをイレーズしたい場合は、0x3000〜0x33ffのどれかを指定(0x3000〜0x33ffのどの値を設定しても結果は変わりません)
●ブロックBをイレーズしたい場合は、0x3400〜0x37ffのどれかを指定(0x3400〜0x37ffのどの値を設定しても結果は変わりません)
戻り値 1:エラーなし 0:エラーあり
使用例
  // ブロックA(0x3000〜0x33ff番地)をイレーズする
  blockEraseDataFlash( 0x3000 );

プログラム「data_flash.c」の説明

「data_flash.c」は、main関数があるCファイルです。このファイルから、「r8cm12a_data_flash_lib.c」に登録している関数を呼び出してデータフラッシュを制御しています。呼び出し方を説明します。

ヘッダファイルの登録

拡張子が「h」のファイルをヘッダファイルといい、Cファイルに登録している関数を宣言しているファイルです(今回の説明の場合)。「r8cm12a_data_flash_lib.c」と「r8cm12a_data_flash_lib.h」はペアで作られていて、「r8cm12a_data_flash_lib.c」の中にどのような関数があるかを「r8cm12a_data_flash_lib.h」で宣言しています。要は、hファイルはCファイルの目次のようなものです。
今回、main関数がある「data_flash.c」から「r8cm12a_data_flash_lib.c」の関数を呼び出すために、目次である「r8cm12a_data_flash_lib.h」を登録します。

#include "sfr_r8m12a.h"             /*R8C/38A SFRの定義ファイル */
#include "r8cm12a_data_flash_lib.h" /*データフラッシュライブラリ*/

データフラッシュ読み書き用配列の宣言

データフラッシュのイレーズ保証回数は、10,000回です。1回データを書き替えるたびにイレーズしていてはすぐに保証回数を超えてしまうので、作業はRAMエリアで行い、最後にデータフラッシュに書き込み、最初にデータフラッシュから読み込みます。今回、作業エリアとしてdata_buff配列を宣言します。
data_buff配列に関わるシンボルを定義します。

DF_SIZE data_buff配列のサイズを指定します。マイコンの電源を入れたとき、このサイズ分、データフラッシュからdata_buff配列に値を格納します。今回は16にして、16バイト分のデータを読み書きします。
DF_CHECK データフラッシュから値を読み込むとき、その値が正しいかチェックするデータが保存されているdata_buff配列の添字(番号)です。今回は0にしています。
DF_DATA 今回、ディップスイッチの値を保存します。その値を保存する、data_buff配列の添字(番号)です。今回は1にしています。
その他 2〜15番は今回使っていないので、宣言していません。プログラムを改造する場合は、ここで宣言するといいでしょう。

/*======================================*/
/* シンボル定義                         */
/*======================================*/
/* データフラッシュ関連 */
#define DF_SIZE  16  /* 読み書きサイズ               */

#define DF_CHECK 0   /* データフラッシュチェック     */
#define DF_DATA  1   /* データ                       */

/*======================================*/
/* グローバル変数の宣言                 */
/*======================================*/
/* データフラッシュ関連 */
signed char data_buff[ DF_SIZE ];   /* 一時保存エリア   */

main関数(初期化)

init関数で、ポートの入出力設定をします。

void main( void )
{
    int             ret;
    unsigned char   c;

    init();           /* 初期化                       */

main関数(データの読み込み)

readDataFlash関数で、前にデータフラッシュに書き込んだデータを16バイト分、読み込みます。
data_buff配列のDF_CHECK(0)番目の値が0x12かどうかチェックします。0x12でなければ、今回初めてデータフラッシュから値を読み込んだと判断して、配列に初期値をセットします。0x12なら、前回書き込んだ値を読み込めたと判断して、何もしません。
読み込んだデータをポート1のLEDに出力します。

    // 前に書き込んだデータを読み込み
    readDataFlash( 0x3000, data_buff, DF_SIZE );

    // 正しいデータかチェック
    if( data_buff[DF_CHECK] != 0x12 ) {
        // 正しくなければ初期値を入れる
        data_buff[DF_CHECK] = 0x12;
        data_buff[DF_DATA]  = 0;
    }

    // 読み込んだデータをP1に出力
    p1 = data_buff[DF_DATA];

main関数(ディップスイッチのチェック)

ディップスイッチの値が変化したかチェックします。
まず、ディップスイッチにつながっているポート3の値を読み込みます。ポート3は、bit7、5、4、3の4bitだけです。その他のビットは"1"か"0"か分からないので、AND演算で強制的に"0"にします。
次に、先ほど読み込んだ値と、現在のポート3の値を比較します。異なればディップスイッチの状態が変化したと判断して、次に進みます。
次に進む前に、ディップスイッチのチャタリングを防止するために、500msのタイマを入れて、時間稼ぎをします。
この次のプログラムでディップスイッチの値を保存しますが、タイマを入れないとチャタリングの影響でディップスイッチの設定値とは違う値が保存されてしまうことがあります。

    // P3が変化したかチェック
    c = p3 & 0b10111000;             // 今の値を読み込み
    while( c == (p3 & 0b10111000) ); // 変化したかチェック

    // チャタリングが収まるまで待つ
    timer( 500 );

main関数(値の保存)

data_buff配列のDF_DATA(1)番目に、ディップスイッチの値を設定します。
blockEraseDataFlash関数で、0x3000〜0x300f(0x3000+DF_SIZE-1)番地をイレーズします。この番地はブロックA(0x3000〜0x33ff番地)なので、0x3000番地を指定します。
イレーズができたら、writeDataFlash関数で、@0x3000番地から、Adata_buff配列のデータを、BDF_SIZE(16)バイト分、書き込みます。
ret変数に、書き込み結果が格納されています。ret変数が0なら書き込みエラーです。8個のLEDを「1111 0000」と「0000 1111」を100msごとに点灯させて、書き込みエラーであることを知らせます。ret変数が0以外なら書き込み成功です。8個のLEDを「0101 0101」と「1010 1010」を200msごとに点灯させて、書き込み成功であることを知らせます。

    // P3の値を保存
    data_buff[DF_DATA]  = p3 & 0b10111000;
    blockEraseDataFlash( 0x3000 );  /* データフラッシュイレーズ*/
                          @      A         B
    ret = writeDataFlash( 0x3000, data_buff, DF_SIZE );

    if( ret == 0 ) {
        /* 書き込みエラーなら */
        while( 1 ) {
            p1 = 0xf0;
            timer( 100 );
            p1 = 0x0f;
            timer( 100 );
        }
    }

    /* 書き込みが正常にできたなら */
    while( 1 ) {
        p1 = 0x55;
        timer( 200 );
        p1 = 0xaa;
        timer( 200 );
    }


Cファイルの関係
今回のプロジェクトのC source fileには、「startup_r8cm12a.c」、「data_flash.c」、「r8cm12a_data_flash_lib.c」の3つのCファイルが登録されています。

ルネサス統合開発環境

3つのファイルの関係を下図に示します。
3つのファイルの関係

@マイコンの電源が入ると「startup_r8cm12a.c」内のstart関数が実行されます(#pragma entry命令で設定した関数が最初に実行されます)。この関数の中で、マイコン固有の初期化を行います。次にmain関数に移ります。
A「data_flash.c」の最初に「r8cm12a_data_flash_lib.h」を読み込んで、「r8cm12a_data_flash_lib.c」に登録されている関数名を確認します。main関数を実行、この中でデータフラッシュを制御します。
B「r8cm12a_data_flash_lib.c」はデータフラッシュを制御するための関数をまとめたファイルで、main関数などから呼び出されて使います。このCファイル単体では何もしません。


BACK