マイコンからSDカードへの書き込みについて

こんにちは。softmです

以前,タイマーの使い方を教えていただいたものですが,そちらの問題は

解決いたしましたので,新たに質問をさせていただきます。

これまではシリアル通信でデータをパソコンに送信していました。

それを,パソコンではなくSDカードに保存したい(可能であればCSVファイルで)のですが,やり方が全く分かりません。

ちなみにマイクロSDカードスロットDIP化キット(秋月電子:K-05488)を使用して書き込みを行うつもりです。

どなたかわかる方ご教授願います。

使用しているマイコンはRX65N,開発環境はCS+です。

Parents
  • こんにちは。NoMaYです。

    >> RX65N,開発環境はCS+
    > のOSかBIOSでもあれば簡単なのですが、たぶん無いと思いますので

    RXマイコンですと、RXスマートコンフィグレータが生成したコードが、それ相当ですね。RX65NならLCD表示コードも生成出来たと思いますし、USBホスト機能でキーボード入力も出来る筈ですね。(もっとも、SCIでの入出力コードの方がコトが簡単ですけれど。) FAT機能もありますね。RX65Nの話では無いですけれども、RX72Nなら、出来合いのSDカードコネクタ付きハードウェアとしてRX72N Envision Kitがありますね。

    思うにFITを選択して生成したコードのAPIなんて、とてもBIOS的だと私は思います。あとは動的なプログラムロード機能があればDOSっぽいものですね。でも、他方、RTOSコードも生成出来ますので、マルチタスクBIOSみたいなもの、なんていう言い回しも許容範囲内かも知れません。(更にそこに動的なプログラムロード機能を足せばマルチタスクDOSなんて言い回しも当たらずとも遠からずなところかも知れません。)

    RXマイコンにはINT命令がありますので、かつてのMS-DOS/PC-DOSのint XXhもどきインターフェースといったものも作成出来るかも知れないです。(今、私の本棚を見たら、ピーターノートン著のPROGRAMMER'S GUIDE TO THE IBM PC & PS2なんていう大昔の本が残ってました。IBM PC & PS2のBIOS APIとDOS APIの解説書です。)

    [追記]

    そういえば、太古の昔、16bitプロセッサのi8088の4.77MHzとかでIBM PCは動いていたようです。

    IT管理者のためのPCエンサイクロペディア
    -基礎から学ぶPCアーキテクチャ入門-
    第7回 PCのエンジン「プロセッサ」の歴史(1)~i8088からIntel386までの道のり
    1. IBM PCシリーズに採用された86系16bitプロセッサたち
    元麻布春男
    2002/08/17
    www.atmarkit.co.jp/fsys/pcencyclopedia/007procs_hist01/procs_hist01.html
     

  • こんにちは,NoMaYさん。

    返信ありがとうございます。

    すみません,後半私の知識が乏しく,理解が追い付きませんでしたが,

    スマートコンフィグレータを使用すれば,SDカードを制御できるということですか?

  • softmさん、こんにちは。NoMaYです。

    出来る筈ですよ。すみません、現在、情報を検索中です。

    ルネサス検索: SDカード
    www.renesas.com/jp/ja/search?keywords=SDカード

    RXファミリ用 FATファイルシステム [M3S-TFAT-Tiny]
    www.renesas.com/jp/ja/software-tool/fat-file-system-m3s-tfat-tiny-rx-family
     

  • softmさん、こんにちは。NoMaYです。

    RXファミリ用 FATファイルシステムのページのドキュメント一覧の以下のドキュメントが取っ掛かりではないかな、と思いました。でも、8年前のドキュメントですので、かなり不安はありますけど。あと、SDカードは相性があるとか無いとか、あったような無かったような、そんな記憶があります。(あいまいで、ごめんなさい。) 他の皆様、私が勘違いしていたら、指摘して下さい。

    RXファミリ オープンソースFATファイルシステム M3S-TFAT-TinyへのSPIモードMMC/SDメモリカード・ドライバ・ソフトウェアの組み込み例
    www.renesas.com/jp/ja/document/apn/820326?language=ja

    RXファミリ SPIモードマルチメディアカードドライバ: 導入ガイド
    www.renesas.com/jp/ja/document/apn/rx-family-spi-mode-multimediacard-driver-introduction-guide?language=ja

     

  • softmさん、こんにちは。NoMaYです。

    先ほどのドキュメントは、ポイントを外してしまったような気がします。こちらかなぁ、と思い直しました。

    RXファミリ オープンソース FATファイルシステムM3S-TFAT-Tinyモジュール
    www.renesas.com/jp/ja/document/apn/open-source-fat-file-system-m3s-tfat-tiny-module-firmware-integration-technology?language=ja

    RXファミリ M3S-TFAT-Tiny メモリドライバインタフェースモジュール
    www.renesas.com/jp/ja/document/apn/m3s-tfat-tiny-memory-driver-interface-module-firmware-integration-technology?language=ja
     

  • こんにちは,NoMaYさん。

    返信ありがとうございます。

    たくさん調べてくださってありがとうございます。

    自分なりにも調べてみたのですが,スマートコンフィグレータで

    SPI動作モードというものがございました。

    https://www.renesas.com/us/ja/document/mat/smart-configurator-users-manual-rx-api-reference?language=ja

    これはSDカードの制御と関係あるのでしょうか?

  • softmさん、こんにちは。NoMaYです。

    いえ、今回の場合(SDカードの場合)、RXスマートコンフィグレータが生成するソースコードのもうひとつのカテゴリであるFITというフレームワークでひとつの連邦国家(という表現は良くないかも知れませんけれど)を形成していまして、その一員を使いますので、FITというフレームワークを使った方が良いです。(他方、今までsoftmさんが使われていたのはCGという連邦国家です。)

  • こんにちは,NoMaYさん。

    返信ありがとうございます。

    なるほど,理解しました。

    まずは,そのFITモジュールをCS+に組み込んでみます。

  • softmさん、こんにちは。NoMaYです。

    SDカードという呼称の他に、SDメモリカードという呼称も使われているようですね。

    ルネサス検索: SDメモリカード
    www.renesas.com/us/ja/search?keywords=SDメモリカード

    それはそれとして、何かの協会に申請しなくても自由に使えるのはSDカードのSPIモードというモードでしたっけ?SDカードのSPIモードのサンプルプログラムって、見付かりましたか?無いですよね、、、

    [追記]

    すみません、8年前のドキュメントがSPIモードでしたね。ごめんなさい。

  • NoMaYさん、こんにちは。Sugachanceです。

    昔調べた時の私の理解では、
    SPIモードだろうと、SDカードを製品に使う時点で
    SDアソシエーションへの入会が必要だった気がするのですが・・・
    (SDの表記無しでカードスロットの設置だけだったらセーフだったかなぁ・・・)

    SDカードではないマルチメディアカード(MMC)にSPIで書き込むのはノンライセンスでいけた
    とかだったような。

    経験ある方の解説があるとうれしいです(他力本願)


    ちなみにライセンス付き(?)の書き込みアダプタみたいなものを出してたところもあったはずです。

  • Sugachanceさん、こんにちは。NoMaYです。

    > SDカードではない マルチメディアカード(MMC)にSPIで書き込むのはノンライセンスでいけた とかだったような。

    あっ、きっと、それです。ありがとうございました。

    > 経験ある方の解説があるとうれしいです(他力本願)

    たしか、昔、スレッドが立っていたのですよ。今、見付けられるかな、、、

Reply
  • Sugachanceさん、こんにちは。NoMaYです。

    > SDカードではない マルチメディアカード(MMC)にSPIで書き込むのはノンライセンスでいけた とかだったような。

    あっ、きっと、それです。ありがとうございました。

    > 経験ある方の解説があるとうれしいです(他力本願)

    たしか、昔、スレッドが立っていたのですよ。今、見付けられるかな、、、

Children
  • Sugachanceさん、NoMaYさん

    SDをSPIモードでアクセスするのは、2GBまでの制限があると思います。

    (MMC互換と言えばライセンス不要と思います)

    最近の大容量カードでなく、小さい容量のカードを使うことをお勧めします

    個人的には保存だけならEEPROM、データを抽出するときはシリアルを使う、

    などにすれば、処理が楽だと思います。

    SDへの書き込みはArduino、ラズパイ、ドライバレベルからポーティング

    するのではなく、ライブラリが揃っていない環境だと私も厳しいです。

    以上、よろしくお願いします

  • 鈴木さん、こんにちは。NoMaYです。

    > MMC互換と言えばライセンス不要と思います

    アドバイスありがとうございます。そういうことなので、知っている人は当然知っている(知らない人は全く知らない)ということかな、ということであったわけでしたので、MMC、というキーワードでも検索しないといけなかったのですね。

    ルネサス検索: MMC
    www.renesas.com/us/ja/search?keywords=MMC

    RXファミリ用マルチメディアカード/e・MMCドライバ
    www.renesas.com/us/ja/software-tool/multimediacard-emmc-driver-rx-family

    [追記]

    すみません、この後で気付いたのですが、今は、MMCというキーワードを知らなくても、SD Simplified Specificationというのを知っていれば事足りるようですね、、、

    [追記その2]

    そうでもないのかな、、、SD Simplified Specificationというものの使用許諾条件がどういうものかによるのかな、、、

  • 鈴木さん、こんにちは。Sugachanceです。

    解説ありがとうございます。

    そうでした!2GB制限がありましたね。

  • こんにちは、NAKAといます。

    SDをSPIモードでアクセスするのは、2GBまでの制限があると思います。

    SDカードをただのメモリとして使用するなら、SD(~2GB)でもSDHC(~32GB)でも制限はないです。規格によってアドレスへのアクセスが違うだけです、SDは書き込みのアドレスを直接指定しますが、SDHCは書き込むブロックを指定します。NAKAの印象はSDカードの中にもマイコンが入っていて、そのマイコンに対してSPI通信でコマンドを投げてやることで、書き込みや読出しができるものです。全ての規格に対応しようとすると、コマンドを投げてそのSDカードがどの規格かを判断し、制御を変える必要があります。

    大分前なので、細かいことはほとんど忘れちゃったけど、webとかインターフェースやトラ技とかSDカードのコマンドを調べながらやった記憶があります。

    FATはデバイスのRAM容量の関係で入れられませんでした。R78/I1E

    GR-SAKURA(RX63N)等にSDカードからRS232Cでパソコンに送信して、CSVにしてました。

    こんな感じで初期化して(SDHC(~32GB)のみ対応)

    /*******************************************************************************
    // 関数名 : init_MicroSDHC(void)
    // 動作 : SDカードの初期化(SDHC) 
    // 引数 : 戻り値: 最後のCMD1のリターン値
    //    0x00:正常終了         
    //    0x01:初期化中のまま終了       
    //    0xFF:ビジーのまま終了
    // 作成 : NAKA  17.05.29
    // ****************************************************************************/
    unsigned char init_MicroSDHC(void)
    {
     unsigned short i;
     unsigned short j;
     unsigned char error_code,res,f_res;
     unsigned char f_NG;
     
     __DI();         //割り込み 禁止☆070801

     error_code = 0xFF;
     f_NG = 0;
     CSI00_CS = 1;       //終了
     //(2)80クロックのダミークロック
     for(i=0; i<10; i++)
     {
      WriteSPI0(0xFF);  //80クロック送信(値は0xFF)
     }
     //(3)CSをLにしてCMD0(CRC付き)を発行
     CSI00_CS = 0;       //開始
        WriteSPI0(0x40);   //CMD0:リセット命令 SPIモードへ移行
        WriteSPI0(0x00);    //引数1
        WriteSPI0(0x00);    //引数2
        WriteSPI0(0x00);    //引数3
        WriteSPI0(0x00);    //引数4
        WriteSPI0(0x95);    //CRC7
     //(4)データラインにレスポンス(x01)
     //while(ReadSPI0()!=0x01);  //0x01待ち *要タイムアウト設定
     while(ReadSPI0()!=0x01)   //0x01待ち *要タイムアウト設定
     {
      j++;
      if(j >=6500)    //タイムアウト
      {
       j = 0;
       f_NG = 1;
       break;
      }
     }

     if(f_NG == 0)
     {
     //(5)CMD1を発行。レスポンス(0x01) *SDHCタイプのカードの場合はCMD8を発行。レスポンスが0x01か0x00ならSDHCの可能性あり、以下の処理が異なるので注意
     //(6)レスポンスが0x00になるまでCMD1を発行。初期化完了。
     f_res = 1;
     do{
      CSI00_CS = 1;   //同期を取る(以下3行)
      WriteSPI0(0xFF);
      CSI00_CS = 0;
      WriteSPI0(0x48);  //CMD8:初期化&状況確認(レスポンス0x01は初期化中、0x00は終了)
      WriteSPI0(0x00);   //引数1
         WriteSPI0(0x00);   //引数2
         WriteSPI0(0x01);   //引数3
         WriteSPI0(0xAA);   //引数4
         WriteSPI0(0x87);   //CRC7(実際にはSPIモードなので無視される)
      for(i=0; i<1000; i++) //レスポンスチェック(通常は数回でコマンドレスポンスが返る)
      {
       res = ReadSPI0();
       if (res != 0xFF) break; //レスポンス評価、ビジー(0xFF)検出ならリトライ
      }
      if(res == 0x01){f_res = 0;}
      if(res == 0x05){f_res = 0;}
     }while(f_res);  //*要タイムアウト設定

     do{
      CSI00_CS = 1;   //同期を取る(以下3行)
      WriteSPI0(0xFF);
      CSI00_CS = 0;   //【10】
      WriteSPI0(0x7A);  //CMD58:初期化&状況確認
      WriteSPI0(0x00);   //引数1
         WriteSPI0(0x00);   //引数2
         WriteSPI0(0x00);   //引数3
         WriteSPI0(0x00);   //引数4
         WriteSPI0(0xFD);   //CRC7(実際にはSPIモードなので無視される)
      for(i=0; i<1000; i++) //レスポンスチェック(通常は数回でコマンドレスポンスが返る)
      {
       res = ReadSPI0();
       if (res != 0xFF) break; //レスポンス評価、ビジー(0xFF)検出ならリトライ
      }
     }while(res != 0x01);  //*要タイムアウト設定

     res = ReadSPI0();    //res = 0x00?
     res = ReadSPI0();    //res = 0xFF?
     res = ReadSPI0();    //res = 0x80?
     res = ReadSPI0();    //res = 0x00?

     f_res = 1;
     do{
      CSI00_CS = 1;   //同期を取る(以下3行)
      WriteSPI0(0xFF);
      CSI00_CS = 0;   //【13】
      WriteSPI0(0x77);  //CMD55:初期化&状況確認
      WriteSPI0(0x00);   //引数1
         WriteSPI0(0x00);   //引数2
         WriteSPI0(0x00);   //引数3
         WriteSPI0(0x00);   //引数4
         WriteSPI0(0x65);   //CRC7(実際にはSPIモードなので無視される)
      for(i=0; i<1000; i++) //レスポンスチェック(通常は数回でコマンドレスポンスが返る)
      {
       res = ReadSPI0();
       if (res == 0x01) break; //レスポンス評価、ビジー(0xFF)検出ならリトライ
      }

      res = ReadSPI0();  //ダミーREADこれが必要!
      res = ReadSPI0();  //ダミーREADこれが必要!

      WriteSPI0(0x69);  //CMD41:初期化&状況確認
      WriteSPI0(0x40);   //引数1
         WriteSPI0(0xFF);   //引数2
         WriteSPI0(0x80);   //引数3
         WriteSPI0(0x00);   //引数4
         WriteSPI0(0x17);   //CRC7(実際にはSPIモードなので無視される)
      for(i=0; i<1000; i++) //レスポンスチェック(通常は数回でコマンドレスポンスが返る)
      {
       res = ReadSPI0();
       if(res == 0x00){f_res = 0;}
       if(res != 0xFF) break;
      }
     }while(f_res);  //*要タイムアウト設定


     do{
      CSI00_CS = 1;   //同期を取る(以下3行)
      WriteSPI0(0xFF);
      CSI00_CS = 0;   //【10】
      WriteSPI0(0x7A);  //CMD58:初期化&状況確認
      WriteSPI0(0x00);   //引数1
         WriteSPI0(0x00);   //引数2
         WriteSPI0(0x00);   //引数3
         WriteSPI0(0x00);   //引数4
         WriteSPI0(0xFD);   //CRC7(実際にはSPIモードなので無視される)
      for(i=0; i<1000; i++){ //レスポンスチェック(通常は数回でコマンドレスポンスが返る)
       res = ReadSPI0();
       if (res != 0xFF) break; //レスポンス評価、ビジー(0xFF)検出ならリトライ
      }
     }while(res != 0x00);  //*要タイムアウト設定

     res = ReadSPI0();    //res = 0xC0?
     res = ReadSPI0();    //res = 0xFF?
     res = ReadSPI0();    //res = 0x80?
     res = ReadSPI0();    //res = 0x00?

        //(8)通信速度再設定         ボーレート変更!!
     ST0 |= 0x0001;    //ch0を停止
     SOE0 &= ~0x0001;   //シリアル通信動作による出力禁止
     SDR00 = 0x0200;    //ボーレート分周(1/?)
            //153600bps=0xCE00
            //312500bps=0x6400
            //2500000bps=0x0600
            //★5000000bps=0x0200
     SOE0 |= 0x0001;    //シリアル通信動作による出力許可
     SS0 |= 0x0001;    //ch0を開始

     }
     else
     {
      res = 0xFF;
     }

        error_code = res;
     
     __EI();      //割り込み 許可☆070801
     
        return error_code;
    }

    こんな感じでブロック書き込みします。

    /****************************************************/
    /* MicroSD 1ブロック書込み       */
    /*  あらかじめd_SD_WR_BUF[]に書かれたデータを  */
    /* MicroSDに書き込む。        */
    /* 戻り値: 書き込みまで行った場合はデータレスポンスを返す。それ以外はコマンドレスポンスを返す  */
    /* 0xFF:コマンド発行中ビジーのまま終了    */
    /* 0x?5:データは受け入れられた      */
    /* 0x?B:CRCエラーで拒否された      */
    /* 0x?D:ライトエラー        */
    /****************************************************/
    unsigned char CMD24(long dataAddress)
    {
     unsigned char a1,a2,a3,a4;
     unsigned short i;
     unsigned char cmd24_res;

     //(1)ブロック数を4バイトの引数に変換
     a1 = (unsigned char)((dataAddress&0xFF000000)>>24);
     a2 = (unsigned char)((dataAddress&0x00FF0000)>>16);
     a3 = (unsigned char)((dataAddress&0x0000FF00)>>8);
     a4 = (unsigned char)(dataAddress&0x000000FF);
     
     __DI();         //割り込み 禁止☆070801

     //(2)CMD発行~レスポンス
     cmd24_res = CMD(24,a1,a2,a3,a4,0xFF); //CMD24発行、レスポンス取得
     if(cmd24_res != 0x00)     //レスポンスが0x00(エラーなし)以外だったら即終了。
     {
      WriteSPI0(0xFF);     //終了処理
         CSI00_CS = 1;      //終了
      return cmd24_res;
     }
     //(3)データトークンスタートバイト(0xFE)書き込み
     WriteSPI0(0xFE);
     //(4)データ書き込み
     for (i=0; i<512; i++)     //WRバッファの前半を書く
     {
      WriteSPI0(d_SD_WR_BUF[i]);   //データ書き込みxブロック長
     }

     //(5)CRC送信
     WriteSPI0(0x00);      //CRCを2バイト書き込み(SPIモードなので意味なし)
     WriteSPI0(0x00);

     //(6)データレスポンス受信
     cmd24_res = ReadSPI0();     //データレスポンス

     //(7)ビジー待ち

     for(i=0; i<10; i++){     //すぐにビジーが解除されない場合があるので多めに判定。呼び出し側で書き込み後すぐに読み込んでもデータが反映されない場合はココでビジーが解除されなかった可能性が高い。
      if(ReadSPI0() != 0x00) break;  //デフォルトは i<10000 だった!
     }

     //(8)終了処理
     WriteSPI0(0xFF);      //ダミークロック送信
        CSI00_CS = 1;       //終了
     
     __EI();         //割り込み 許可☆070801

     return cmd24_res;      //CMD27のレスポンス結果を返信
    }