Raspberry Pi PicoでSDXCカード/exFATを使う (SPI I/F)

2021年3月13日土曜日

Raspberry Pi Pico

t f B! P L

Raspberry Pi PicoでexFATフォーマットのmicroSDXCカードにアクセスしてみました。

Raspberry Pi Pico microSD アクセステスト風景 

FatFsモジュール

今回はFAT, FAT32のみならずexFATまで認識させたかったので、pico-examplesにあるサンプルは使用せずにFatFsモジュールを使用しました。FatFsは個人利用から商用まで利用目的を問わず、自己責任の下で自由に使用・改変・配布ができ実装例も豊富なので、自作マイコンユーザーにとっては非常にありがたいモジュールです。すでにSipeed Longan Nanoの実装においてFatFsモジュールでのexFATの動作を確認済みでしたので、そこからの修正という形をとりました。使用したバージョンはR0.14a_p2です。

配線図 / ピン設定

配線図は以下の通りです。
DAT0ラインの10KΩのプルアップ抵抗は必須で、これがないと正常認識しません。
配線図

接続対応表も記載しておきます。

Pico
Pin #
Pico Pin
Name
FunctionmicroSD
Pin #
microSD
Pin Name
4GP2SPI0_SCK5CLK
5GP3SPI0_TX3CMD
6GP4SPI0_RX7DAT0
7GP5SPI0_CSn2CD/DAT3
8GNDGND6VSS
363V3(OUT)3.3V4VDD

注意点

Raspberry Pi PicoとmicroSDカードスロット間の配線は非常にシビアで、通常のブレッドボード用のmicroSDカードモジュールとピン配線では安定させて動作させることができませんでした(特にSDXCカード)。Raspberry Pi PicoのGPIOライブラリにはないがレジスタには存在する出力パッドのドライブ強度やSlew Rateを調整できる機能を追加実装して、これらを様々に調整したり、周波数を変更したり、ダンピング抵抗を挿入したり様々にカットアンドトライを試みましたがあまり改善効果はなく、最終的には以下の写真のような最短で4~8ピンに直挿しできる接続モジュールを作成することで安定動作を確認しました。SPIアクセスタイミングのマージン確保に関しては今後の改善の余地がありそうです。
microSDカード直挿モジュール

動作確認カード

以下、動作確認に使用したカードの情報を記載しておきます。microSDカードはFAT, microSDHCカードはFAT32, microSDXCカードはexFATフォーマットです。手持ちのmicroSDカードで認識できなかったカードはなく、エラーが生じる場合はすべて上記注意点のアクセスタイミングの問題でした。
  • PQI microSD 1GB
  • Memorex microSD 2GB
  • Transcend microSDHC 32GB (C4)
  • Toshiba microSDXC 64GB (UHS-I C10)
  • SanDisk microSDXC Ultra A1 64GB (UHS-I C10/U1)
  • SanDisk microSDXC Ultra A1 512GB (UHS-I C10/U1)
  • SanDisk microSDXC Ultra A2 1TB (UHS-I U3/V30)

pico_fatfs_testプロジェクト

https://github.com/elehobica/pico_fatfs_test に今回作成したプロジェクトを置いておきます。機能は以下の通りです。
  • シリアル端末で何らかのキーを入力するとスタート
  • カードをマウントして認識。ファイルシステム(FAT, FAT32, exFAT)と容量を表示
  • Write / Readのアクセス速度を計測して表示(各2回)
アクセス速度測定の方式はTeensy4.0のbenchプロジェクトに準じて作成しました。カードのRootフォルダ上にbench.datファイルが上書きされますので、念のためテストに使用するカードはバックアップを取るようしてください。
シリアル端末表示例


以下簡単にコードを説明します。

ffconf.h

FatFsの機能的なコンフィグレーションをffconf.hにて行います。今回はアクセス速度のベンチマークのため、比較的「広め」にコンフィグレーションしていますが、実際のプロジェクトで使用する際には例えば以下のパラメータ等について検討すればよいと思います。

今回の設定と他プロジェクトの場合の検討事項
  • FF_FS_READONLY 0: リードオンリーの場合は1で良い
  • FF_FS_MINIMIZE 0: 使用するAPIの範囲に応じて1, 2等に設定しても良い
  • FF_USE_EXPAND 1: PreAllocationを使用しないなら0で良い
  • FF_USE_LFN 1: Long File Nameを使用しないなら0で良い
  • FF_FS_RPATH 1: 相対パスを使用しない、または外部管理なら0で良い
  • FF_FS_EXFAT 1: exFAT使わないなら0で良い
  • FF_FS_NORTC 1: タイムスタンプをRTCにより正しく反映させるなら0に設定し必要な関数を実装する

tf_card.c / tf_card.h

FatFsモジュールのデバイス依存部分はtf_card.c / tf_card.hにすべて記述しています。
  • SPI0のピンアサインはtf_card.hで定義
  • SPIのカード初期化クロックは100KHz、高速動作クロックは50MHzに設定(状況に応じて変更)
  • SPIのアクセス方式は8bit, CPOL = 0, CPHA = 0, MSB First

参考までにSPIアクセスの実装部分を抜粋して記載します。1バイトの双方向アクセス、複数バイトのリードアクセス、複数バイトのライトアクセスをpico-sdkのhardware/spi.hの関数を使用して実装しました。
/* Exchange a byte */
static
BYTE xchg_spi (
	BYTE dat	/* Data to send */
)
{
	uint8_t *buff = (uint8_t *) &dat;
	spi_write_read_blocking(spi0, buff, buff, 1);
	return (BYTE) *buff;
}


/* Receive multiple byte */
static
void rcvr_spi_multi (
	BYTE *buff,		/* Pointer to data buffer */
	UINT btr		/* Number of bytes to receive (even number) */
)
{
	uint8_t *b = (uint8_t *) buff;
	spi_read_blocking(spi0, 0xff, b, btr);
}

...

/* Transmit multiple byte */
static
void xmit_spi_multi (
	const BYTE *buff,		/* Pointer to data buffer */
	UINT btx		/* Number of bytes to transmit (even number) */
)
{
	const uint8_t *b = (const uint8_t *) buff;
	spi_write_blocking(spi0, b, btx);
}

hardware_gpio_ex

今回結局使用しませんでしたが、GPIO 出力PADのドライブ強度、Slew Rate設定、および入力PADのSchmitt Trigger設定を行う関数をhardware_gpio_exディレクトリ以下に実装しました。
プロジェクトのCmakeLists.txtに
add_subdirectory(hardware_gpio_ex)

// ... (other settings)

target_link_libraries(${bin_name} PRIVATE
    pico_stdlib
    // ... (other libraries)
    hardware_gpio_ex
)
と記載して、実際に使用するソースにて
#include "hardware/gpio_ex.h"
とすれば以下の3関数が使用できます。(使用例はtf_card.cのコメントアウトされている部分を参照)
void gpio_set_drive_strength(uint gpio, uint value);
void gpio_set_schmitt(uint gpio, uint value);
void gpio_set_slew_rate(uint gpio, uint value);

アクセススピード計測結果

アクセススピードとしてはかなり見劣りのする数値しか出ていません。もちろんこの程度のスピードでも問題なく使えるアプリケーションは数多くありますのでその範囲内で使用するか、高速化のチューニングをして使用するかはケースバイケースになると思います。
  • PQI microSD 1GB

    Type is FAT16
    Card size:    1.02 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    72.8148, 188153, 3175, 7028
    72.7216, 188240, 3177, 7037
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    740.6934, 1591, 665, 690
    740.8031, 1589, 665, 690
        
  • Memorex microSD 2GB

    Type is FAT16
    Card size:    2.00 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    79.2543, 222555, 2452, 6456
    78.1408, 229029, 2457, 6549
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    757.2978, 2044, 533, 675
    757.4125, 2039, 533, 675
        
  • Transcend microSDHC 32GB (C4)

    Type is FAT32
    Card size:   31.90 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    273.3709, 209797, 947, 1871
    287.3379, 208416, 968, 1780
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    851.7343, 1192, 539, 600
    851.8794, 1189, 539, 600
        
  • Toshiba microSDXC 64GB (UHS-I C10)

    Type is EXFAT
    Card size:   61.89 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    149.3869, 180241, 2065, 3424
    155.4869, 173010, 2069, 3291
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    620.7698, 960, 621, 824
    620.4617, 959, 621, 824
        
  • SanDisk microSDXC Ultra A1 64GB (UHS-I C10/U1)

    Type is EXFAT
    Card size:   63.83 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    464.5679, 19560, 881, 1101
    471.6234, 19881, 761, 1084
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    1238.1575, 422, 394, 412
    1238.1575, 422, 399, 412
        
  • SanDisk microSDXC Ultra A1 512GB (UHS-I C10/U1)

    Type is EXFAT
    Card size:  511.80 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    430.8212, 24998, 951, 1187
    428.5318, 24974, 953, 1193
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    1195.5237, 443, 413, 427
    1195.5237, 444, 415, 427
        
  • SanDisk microSDXC Ultra A2 1TB (UHS-I U3/V30)

    Type is EXFAT
    Card size: 1023.74 GB (GB = 1E9 bytes)
    
    FILE_SIZE_MB = 5
    BUF_SIZE = 512 bytes
    Starting write test, please wait.
    
    write speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    426.9217, 6290, 963, 1198
    427.0676, 32321, 958, 1197
    
    Starting read test, please wait.
    
    read speed and latency
    speed,max,min,avg
    KB/Sec,usec,usec,usec
    1192.9563, 445, 409, 428
    1192.9563, 445, 413, 428
        

自己紹介

自分の写真
電子工作&プログラミング、オーディオ・音楽

注目の投稿

Raspberry Pi Pico Wで電波時計を合わせる (JJY標準電波エミュレータ)

Raspberry Pi Pico Wのアプリケーションとして 最少の周辺部品で電波時計むけJJYエミュレータ(時刻合わせ用)を製作しました。 ※2023年6月6日: ソースコード修正の内容を反映させました。 時刻合わせ風景 概要 電波時計は電波が届くところで使...

QooQ