3.  SDカードの読み出しについて

【前書き】
   本ページではmiqnnet環境のアクセスライブラリを利用したSDカード読み出しを解説しています。
   下記のサンプルプロジェクトでは高速化の為にSDIO以下のフォルダ内容を変更しています。
   また本ページで説明されているコードの変更はサンプルでは既に行われています。
   下記からダウロードして以降の項目の参考としてください。


   サンプルプロジェクトのダウンロード(LCD動画再生プログラムと同じ)


【SDカードの高速読みだし】
 大量のデータを読み込み、記録する場合にはストレージデバイスが必要となります。
 その中でもSDカードは市場に大量に出回っており容量あたりの単価も安い為
 電子工作でよく使用されます。

 本ページではSDカードから大量のデータアクセスを高速に行う為の
 ヒントを紹介します。

 SPI接続によるSDカード読み出しは他サイト様で行われているので
 当ページではSTM32のペリフェラルであるSDIOを利用した転送を紹介します。
 うまく使えば10Mbyte/sec近い高速読み込みが可能になります。

【4つのポイント】
   SDカードからの高速読み出しに考慮する主な項目は下記の通りです。
   1.SDIOを利用する。
   2.スピードクラスの表記があるSDカードを利用する。
   3.専用のフォーマッタを用いてフォーマットする。
   4.一度の通信でなるべく多くのセクタを読み込む。
   
   以下にその理由と具体的な対処方法を項目ごとに解説していきます。


1.SDIOを利用する。

 ○SDIOの概要
   SDIOはSTM32F1大容量モデルに搭載されているペリフェラルです。
   SDカードにとっての特徴を下記に示します。

 1.SDカードアクセスは4bitバスによる25MHzの高速通信。理論上は100Mbpsの通信が可能。
 2.通信状態に応じてステートマシンがハードウェアで自動制御される。CRCの生成とチェックも自動。
   最小限の操作で通信が可能になる。
 3.32ワードのFIFOバッファを持っている。
   バッファに一定のデータが溜まるとDMAリクエストが自動で生成されるため
   読み込みに掛かるCPUの負担が少ない。
 4.公開された仕様。アクセス用のライブラリもSTマイクロエレクトロニクスから用意されている。
   (ただしSDカードはライセンス規約がある為、製作は研究や個人の学習用途に限られる。)

   上記のようにSDカードとの高速通信の為に多くの機能が備わっている為
   活用しない手は有りません。
  
   ちなみにSDIOはMMCカードver4.2との組み合わせで8bitバスで48MHz,
   つまり理論上384Mbpsという恐ろしい速度のアクセスも可能なようですが、
   資料が見当たらないので使えていません。

 ○ハードウェアの製作
     
 実際のSDIOとSDカードの接続は下記のように行いました。(クリックで拡大)

 

   SDカードコネクタとマイコンボード間は10cm程のケーブルで接続している為
   高速通信による信号反射の対策にフェライトコアを使用しています。
   (詳細はマイコン徹底入門で解説されています)
   CMDピンのみSTBeeボード上で物理的に他のピンより遠いので
   念のためにダンピング抵抗を入れています。

 ○アクセスモードの設定
   先に述べたようにSDIOはDMAリクエストを生成する為これを利用して高速化を図ります。
   ライブラリ「sdcard.c」の105行目を下記のように変更します。

static uint32_t DeviceMode = SD_DMA_MODE;

    SD_DMA_MODEは割り込みを利用します。マイコンの初期化コード中にNVICの設定を行います。

 /* Configure the NVIC Preemption Priority Bits */
NVIC_InitStructure.NVIC_IRQChannel = SDIO_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

    以下の割り込みハンドラを「stm32f10x_it.c」に記述します。

 /**
* @brief This function handles SDIO global interrupt request.
* @param None
* @retval None
*/
void SDIO_IRQHandler(void)
{
    /* Process All SDIO Interrupt Sources */
    SD_ProcessIRQSrc();
}

2.スピードクラスの表記があるSDカードを使用する。
  SDカードライブラリは『SDカード』と容量2GB以上の『SDHCカード』の両方を使用可能です。
 
  ただし高速読み出しの為にはスピードクラス表記があるものを使用する必要があります。
  スピードクラスとは転送速度を保証するための表記で、下図のような刻印で表されます。 

    
赤丸の数字が転送速度の保証値。
写真のカードは4Mbyte/sec。
SDIOによる実測の転送速度は6.1Mbyte/sec
購入先

   『SDHCカード』では必ず表記のあるスピードクラスですが、
   『SDカード』では表記の無いものもあります。

   スピードクラスと転送速度に関して経験(SDカード5枚でテスト)と
   ネットで調べた情報を元に分かった事は以下の通りです。

   ○スピードクラスの表記が無いカードは非常に低速(1~2Mbyte/sec程度)
   ○スピードクラス2のカードは表記以上の速度が出る。(5~7Mbyte/sec程度)
   ○パッケージ(microSDとSD)は転送速度にあまり関係が無い。
   ○スピードクラス10のカードとはいえ10Mbyte/secの速度は出ない事が多い。(7~10Mbyte/sec程度)
   ○スピードクラス4~10の転送速度は当てにならない。
     5~10Mbyte/sec程度の速度の中でどれが出るかは博打。

   ややこしい事にPCでベンチマークした転送速度とSDIOで読み出した速度も違う為、
   SDIOで読み込んでみるまで分かりません。

   なお下記サイト様で実績のあるSDカードが紹介されていますのでご参考ください。

とんすけさん
   とんすけぶろぐ「SDカードのベンチ比較 マイコン工作で速いカードはどれ?
   様々なSDカードを試されています。とても参考になります。

ねむいさん
   ねむいさんのぶろぐ「STM32 Primer2をBARE-METALで使ってみる5 -FatFsの実装-
   10Mbyte/secという理論値に近い転送速度を叩き出されています。

3.専用のフォーマッタを使用してフォーマットする。
   windowsで使用できるフォーマット機能でSDカードをフォーマットしてしまうと
   転送速度が大きく低下します。

   SDカードの規格を策定している「SDアソシエーション」が配布する
   専用のフォーマットソフトがあるのでこれを利用しましょう。
   「SDフォーマッタ

 
  私の環境では上図のフォーマットの設定で問題なく使用できました。



4.一度の通信でなるべく多くのセクタを読み込む。
  ○用語
    本章の説明の前に用語を説明します。
     A.セクタとブロック
       セクタはSDカードのデータの管理単位です。
       ブロックはSDカードへ読み書きを行う際の単位です。
       多くの場合は1セクタ=1ブロック=512バイトです。

     B.SDカード内のコントローラ
       SDカードの内部にはコントローラが入っています。
       このコントローラは主に下記の役割を果たしています。

        1.論理アドレスと物理アドレスを変換しSDカードへの
          局所的な書き込みを防いでいる。(ウェア・レベリング)
        2.SDコマンドを解析して設定や読み書き動作を行う。

  ○SDカード読み込みプロセス
    SDカードからデータを読み取る時は、SDIOからコマンドを送信します。
    コマンドには1セクタのみ読み出すシングルブロックリードと
    複数のセクタを読み出すマルチブロックリードが有ります。

    マルチブロックリードコマンドの送信とデータ読み込みの概要を示します。(クリックで拡大)  

 

    ※シングルブロックリードコマンドの場合はストップコマンド無しで1ブロックのみの読み出しが行われます。

    マルチブロックリードコマンドを受けたSDカードはストップコマンドを受け取るまで
    データをSDIOに送信し続けます。
    ここで見ていただきたいのはコマンド応答期間です。
    SDカードは応答期間中に論理アドレス変換を行い、データをSDIOに送り出す準備をします。
    この期間が読み込み時間を左右する大きなボトルネックになります。

    SDカードへ送るクロックは25MHzで同じであるにも関わらず、カードごとに
    読み出しの速度に差が生じるのは、この応答期間の長さが異なる事が最も大きな要因です。

    高速読み出しの為には応答期間がなるべく少なくなるように読み出し方を工夫します。
  
    図をもう一度見ると最初の1セクタ目の受信から次のセクタの受信までの間が非常に
    短い事が分かります。
    つまり、一度のマルチブロックリードコマンドでなるべく多くのセクタを読み込む事で
    データサイズ辺りのコマンド応答期間の回数を減らす事ができる事が分かります。


  ○マルチブロックリードコマンドを使用する設定
    まずSDカードライブラリを書き換えてマルチブロックリードコマンドが使用されるようにします。
    disk_io.cのdisk_read関数の内容を以下のように書き換えます。  

{
    switch (drv)
    {
    case SDIO_DRIVE:
    {
        SD_Error status = SD_OK;

        if(count >= 2)    //multi block read
        {
            status = SD_ReadMultiBlocks((sector*512), (uint32_t*)(buff), 512, count);
        }
        else                //single block read
        {
            status = SD_ReadBlock((sector*512), (uint32_t*)(buff), 512);
        }

        if (status == SD_OK)
        return RES_OK;
        else
        return RES_ERROR;
        }
    }
    return RES_PARERR;
}

 ○FatFSを高速転送が可能なワードアクセスモードにする。
    「FSMCによるQVGA液晶制御」と重複しますが
    効率のいい転送の為にFatFSとバッファのメモリアクセスも最適化します。
    8bit→32bitでデータアクセスを行うように設定します。
    設定方法:FatFSの設定情報で下記項目を「1」に設定する。     

 #define _WORD_ACCESS 1 /* 0 or 1 */

     バージョン0.09であれば「ffconf.h」に設定項目が有ります。

 ○大きなサイズでデータを読み込む
   マルチブロックリードコマンドで多くのセクタを読み込む為には
   大きなサイズでデータを読み込む必要があります。
   
   FatFSを使用した場合、一度の読み出しサイズによって転送速度が下記のように変化します。
   SDカードは先ほどの「TOSHIBA microSDカード」を使用しました。

 f_read関数における一回の読み込みサイズ(byte) 読み出し速度(Mbyte/sec)
 512(=1セクタ)  1.5
2560  4
5120  4.6
10240  6.1
19200  6.1
153600
(QVGA液晶のGRAMへ
FatFS→FSMCにより直接データ送信
FSMCによるQVGA液晶制御」を参照)
 7.5

  読み出しサイズが大きくなればその分大きなバッファが必要となるので
  転送速度とトレードオフで考えて決定してください。


説明は以上です。

ボトルネックを無くして転送速度を向上させる為には
多くのポイントに注意しなければなりませんがその分得られるメリットも大きいと思います。


○参考文献
  1.STM32マイコン徹底入門
  2.interface誌 2006年3月号 特集「フラッシュ・メモリカードの組み込み機器への活用」
  3.interface誌 2009年6月号 「MMCカード・コントローラの使い方」
  4.interface誌 2010年9月号 特集「FATファイル・システムでファイルを読み書きしよう」