0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Raspberry Pi Pico C/C++ PetitFatFsでSDカード読み込みしてみる

Last updated at Posted at 2021-06-13

PetitFatFsとは?

PetitFatFsとはChaN氏によるFatFsを最低限の機能に絞り
手軽にFatFileSystemのデータ操作ができるありがたいライブラリです。
このライブラリはハード依存の部分とそうでない部分にわかれており
ユーザーは自分の使うハードに合わせてハード依存の部分(diskio.c)さえ作ってやれば良いだけになっています。

diskio.cをつくる

てことでハード依存の部分(diskio.c)を実装してみました。
(Readで力尽きたのでWriteは実装してません)

とりあえず自分の手持ちのSDカード(2GB)では動作確認できましたが、万能ではないです。
まず、CMD1で初期化できるカードしか使えません。
そして、フォーマットするソフトによっても使えたり使えなかったりします。
win10標準のFAT16フォーマットではダメでDisk Formatterを使うといけました。
(多様なSDカードに対応している世の中の製品ってすごいなあと実感しました。。。)

ピンアサインは下記の通りです。

GPIO 役割 SDカード
2 SCK SCLK/CLK
3 TX DI/CMD
4 RX D0/DAT0
5 CS CS/CD/DAT3
diskio.c

# include "user_define.h"
# include "diskio.h"

/* Definitions for MMC/SDC command */
# define CMD0    (0x40+0)    /* GO_IDLE_STATE */
# define CMD1    (0x40+1)    /* SEND_OP_COND (MMC) */
# define CMD16    (0x40+16)    /* SET_BLOCKLEN */
# define CMD17    (0x40+17)    /* READ_SINGLE_BLOCK */

# define CS_HIGH() {gpio_put(P_SPI_CS, 1);}
# define CS_LOW() {gpio_put(P_SPI_CS, 0);}

static BYTE rcv_spi( void );        /* Send a 0xFF to the SDC/MMC and get the received byte */
static void xmit_spi( BYTE d );    /* Send a byte to the SDC/MMC */
static BYTE send_cmd( BYTE cmd, DWORD arg );

/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive                                                 */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize( void )
{
    DSTATUS stat = STA_NOINIT;
    WORD i;

    /* マイコンリソースの初期設定 */
    {
        spi_init( SPI_CH, 100 * 1000 );
        spi_set_format( SPI_CH, 8, SPI_CPOL_0, SPI_CPHA_0, SPI_MSB_FIRST );
        gpio_set_function( P_SPI_RX, GPIO_FUNC_SPI );
        gpio_set_function( P_SPI_SCK, GPIO_FUNC_SPI );
        gpio_set_function( P_SPI_TX, GPIO_FUNC_SPI );

        gpio_init( P_SPI_CS );
        gpio_set_dir( P_SPI_CS, GPIO_OUT );
    }

    CS_HIGH();

    /* ダミークロックを80発 */
    {
        BYTE i;
        for ( i= 0; i < 10; i++ )
        {
            rcv_spi();
        }
    }

    {
        BYTE cmd_res;

        /* CMD0を0x01が返ってくるまでトライ */
        for ( i = 0; i < 10000; i++ )
        {
            cmd_res = send_cmd( CMD0, 0 );
            if ( cmd_res == 0x01 )
            {
                break;
            }
        }

        if ( cmd_res == 0x01 )/* Enter Idle state */
        {
            /* CMD1を0x00が返ってくるまでトライ */
            for ( i = 0; i < 10000; i++ )
            {
                cmd_res = send_cmd( CMD1, 0 );
                if ( cmd_res == 0x00 )
                {
                    /* 初期化完了 */
                    stat = 0;
                    break;
                }
                sleep_us( 100 );
            }

            /* CMD16でブロックサイズを512バイトに設定 */
            send_cmd( CMD16, 512 );
        }
    }

    CS_HIGH();
    rcv_spi();

    return ( stat );
}



/*-----------------------------------------------------------------------*/
/* Read Partial Sector                                                   */
/*-----------------------------------------------------------------------*/

DRESULT disk_readp (
    BYTE* buff,        /* Pointer to the destination object */
    DWORD sector,    /* Sector number (LBA) */
    UINT offset,    /* Offset in the sector */
    UINT count        /* Byte count (bit15:destination) */
)
{
    DRESULT res;
    BYTE cmd_res;

    res = RES_ERROR;

    cmd_res = send_cmd( CMD17, ( sector * 512 ) );
    if ( cmd_res == 0x00 )
    {
        WORD i;
        /* なんらかの応答が来るまでループ */
        for ( i = 0; i < 40000; i++ )
        {
            cmd_res = rcv_spi();
            if ( cmd_res != 0xFF )
            {
                break;
            }
        }

        /* 応答がデータパケットの先頭0xFE時の処理 */
        if ( cmd_res == 0xFE )
        {
            /* offsetまで読み飛ばす */
            for ( i = 0; i < offset; i++ )
            {
                rcv_spi();
            }

            /* データを読み込む */
            if ( buff != 0 )
            {
                for ( i = 0; i < count; i++ )
                {
                    buff[ i ] = rcv_spi();
                }
            }
            else
            {
                for ( i = 0; i < count; i++ )
                {
                    rcv_spi();
                }
            }

            /* 残りは読み飛ばす */
            for ( i = 0; i < ( 514 - ( offset + count ) ); i++ )
            {
                rcv_spi();
            }

            res = RES_OK;
        }
    }

    CS_HIGH();
    rcv_spi();

    return ( res );
}



/*-----------------------------------------------------------------------*/
/* Write Partial Sector                                                  */
/*-----------------------------------------------------------------------*/

DRESULT disk_writep (
    const BYTE* buff,        /* Pointer to the data to be written, NULL:Initiate/Finalize write operation */
    DWORD sc        /* Sector number (LBA) or Number of bytes to send */
)
{
    DRESULT res;


    if (!buff) {
        if (sc) {

            // Initiate write process

        } else {

            // Finalize write process

        }
    } else {

        // Send data to the disk

    }

    return res;
}


static void xmit_spi( BYTE d )    /* Send a byte to the SDC/MMC */
{
    BYTE src[ 1 ];

    src[ 0 ] = d;
    spi_write_blocking( SPI_CH, src, 1 );
}

static BYTE rcv_spi( void )        /* Send a 0xFF to the SDC/MMC and get the received byte */
{
    BYTE dst[ 1 ];

    spi_read_blocking( SPI_CH, 0xFF, dst, 1 );

    return ( dst[ 0 ] );
}


static BYTE send_cmd(
    BYTE cmd,        /* 1st byte (Start + Index) */
    DWORD arg        /* Argument (32 bits) */
)
{
    BYTE n, res;

    /* Select the card */
    CS_HIGH();
    rcv_spi();
    CS_LOW();
    rcv_spi();

    /* Send a command packet */
    xmit_spi( cmd );                        /* Start + Command index */
    xmit_spi( (BYTE)( arg >> 24 ) );        /* Argument[31..24] */
    xmit_spi( (BYTE)( arg >> 16 ) );        /* Argument[23..16] */
    xmit_spi( (BYTE)( arg >>  8 ) );            /* Argument[15..8] */
    xmit_spi( (BYTE)arg );                /* Argument[7..0] */
    n = 0x01;                            /* Dummy CRC + Stop */
    if ( cmd == CMD0 )
    {
        n = 0x95;            /* Valid CRC for CMD0(0) */
    }
    xmit_spi( n );

    /* Receive a command response */
    n = 10;                                /* Wait for a valid response in timeout of 10 attempts */
    for ( n = 0; n < 10; n++ )
    {
        res = rcv_spi();
        if ( ( res & 0x80 ) == 0x00 )
        {
            break;
        }
    }

    return ( res );            /* Return with the response value */
}

動かしてみる

SDカード内にHelloWorld!SDと書かれたファイルtest.txtをいれておいて
picoでSDカードから読み出しUSBでPCに出力してみます

test.txt
HelloWorld!SD

main()関数はこんな感じ

main.c
void main( void )
{
    FATFS fs;
    BYTE buff[16];
    UINT br;
    FRESULT res;

    stdio_init_all();

    res = pf_mount( &fs );
    if ( res != 0 )
    {
        printf( "Error:pf_mount,%d\n", res );
        sleep_ms( 1000 );
    }
    res = pf_open( "TEST.TXT" );
    if ( res != 0 )
    {
        printf( "Error:pf_open,%d\n", res );
        sleep_ms( 1000 );
    }
    res = pf_read( buff, 16, &br );
    if ( res != 0 )
    {
        printf( "Error:pf_read,%d\n", res );
        sleep_ms( 1000 );
    }

    while ( true )
    {
        printf( buff );
        printf( "\n" );
        sleep_ms( 1000 );
    }
}

これで動かしてみると

image.png

読み込めました!

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?