##はじめに
電子工作界隈で注目されているRaspberry Pi Pico(以後、ラズパイPico)でSDカード上のテストファイルやバイナリファイルへアクセスできるようオープンソースのFatFsを用いて実装することにしました。
以前に「Cypress FM4でFatfsを使う」で FatFsとCypress製マイコンのFM4へ SPI経由での接続、実装は経験済みで、基本的にSPIへアクセスする以下の6つの関数やマクロをラズパイPico用に改造すればよいことが既に分かっています。
・SPI初期化:init_spi()
・SPI1バイト送受信:xchg_spi()
・SPI複数バイト受信:rcvr_spi_multi()
・SPI複数バイト送信:xmit_spi_multi()
・チップセレクト・High:CS_HIGH()
・チップセレクト・Low:CS_LOW()
※Cypress FM4でFatfsを使う
https://qiita.com/Yukiya_Ishioka/items/77cd75ca03fb5c7c5c0b
ラズパイPicoでは開発用にPico-SDKという各種デバイスへのアクセス用サブルーチン群が提供されていて、この中にSPI用のアクセスルーチンがあることから、Pico-SDKのルーチンを使って実装しました。
##ラズパイPicoのビルド環境
私はインターフェース誌 2021年8月号でFreeRTOSを紹介した記事内に記してある Ubuntu上に必要なパッケージをインストールした環境を使っていますが、Pico-SDKを使ってビルドできる環境であれば問題ないと思います。
※インターフェース誌 2021年8月号
https://interface.cqpub.co.jp/magazine/202108/
##ファイルの取得
ビルド環境の構築ができたら、今回使用するソース類をネットワークから取得します。
###FatFs
FatFsは以下のページからソースをダウンロードできます。
使用するファイルやダウンロード場所の詳細は、FatFsは先に紹介したQiitaの記事「Cypress FM4でFatfsを使う」を参照してください。
ここでも stm32f用のコードをラズパイPico用に改造して使用します。
※FatFsのページ
http://elm-chan.org/fsw/ff/00index_e.html
FatFs本体と併せ、以下の2つのファイルをダウンロードしてください。
・ff14b.zip
・ffsample.zip
###Pico-SDK
Pico-SDKは以下のページからソースをダウンロードできます。
ダウロードや展開方法は、やはりインターフェース誌 2021年8月号のFreeRTOSの記事内に記してあるので参考にしてください。
※Raspberry Pi Pico SDKのページ
https://github.com/raspberrypi/pico-sdk
##FatFsの展開
FatFs本体を展開します。
ff14b.zipファイル内から sourceディレクトリを取り出し、PC内の任意のディレクトリへコピーします。
今後の操作を考えると、以下のようにPico-SDKを展開した同じディレクトリにsrcディレクトリを作成し、その中に展開するとよいと思います。
|--pico-sdk
|
|--src
次に、ffsample.zipファイル内から**\stm32\mmc_stm32f1_spi.c**ファイルを取り出し、先のFatFs本体のファイルと同じディレクトリへ置きます。
その後、mmc_pico_spi.cというファイル名に置き替えます。
以下のファイルが置かれていると思います。
00history.txt
00readme.txt
diskio.c
diskio.h
ff.c
ff.h
ffconf.h
ffsystem.c
ffunicode.c
mmc_pico_spi.c
展開後のディレクトリ構成は以下のようになっていると思います。
|--pico-sdk
|
|--src
| |--00history.txt
| |--00readme.txt
| |--diskio.c
| |--diskio.h
| |--ff.c
| |--ff.h
| |--ffconf.h
| |--ffsystem.c
| |--ffunicode.c
| |--mmc_pico_spi.c
##FatFs試行用のハードウェア
・SDカードモジュールとの結線
PicoのPin | GPIO No. | SD Card-Module |
---|---|---|
21 | GPIO 16 | MISO (SDO) |
25 | GPIO 19 | MOSI (SDI) |
24 | GPIO 18 | SCK |
22 | GPIO 17 | /CS |
・USB-シリアル モジュールとの結線
PicoのPin | GPIO No. | USB-シリアル |
---|---|---|
25 | GPIO 20 | TxD |
26 | GPIO 21 | RxD |
##Pico用コードの作成
コピーした状態では mmc_pico_spi.c はSTM32用の制御コードとなっているため、14行目から208行目までを以下のコードへ置き替えます。
#include "pico/stdlib.h"
#include "hardware/spi.h"
/*
GPIO 16 (pin 21) MISO/spi0_rx-> SDO
GPIO 17 (pin 22) Chip select -> !CS
GPIO 18 (pin 24) SCK/spi0_sclk -> SCK
GPIO 19 (pin 25) MOSI/spi0_tx -> SDI
*/
#define DEF_SPI_TX_PIN 19
#define DEF_SPI_RX_PIN 16
#define DEF_SPI_SCK_PIN 18
#define DEF_SPI_CSN_PIN 17
#define SPIDEV spi0
#define FCLK_FAST() { }
#define FCLK_SLOW() { }
#define CS_HIGH() { gpio_put( DEF_SPI_CSN_PIN, 1 ); /* HIGH */ }
#define CS_LOW() { gpio_put(DEF_SPI_CSN_PIN , 0 ); /* LOW */ }
#define MMC_CD 1 /* Card detect (yes:true, no:false, default:true) */
#define MMC_WP 0 /* Write protected (yes:true, no:false, default:false) */
volatile int conter1;
void Delay( int ms )
{
for( conter1=0; conter1<125*ms ; conter1++ ) ;
}
/*--------------------------------------------------------------------------
Module Private Functions
---------------------------------------------------------------------------*/
#include "ff.h" /* Obtains integer types */
#include "diskio.h" /* Declarations of disk functions */
/* MMC card type flags (MMC_GET_TYPE) */
#define CT_MMC3 0x01 /* MMC ver 3 */
#define CT_MMC4 0x02 /* MMC ver 4+ */
#define CT_MMC 0x03 /* MMC */
#define CT_SDC1 0x02 /* SDC ver 1 */
#define CT_SDC2 0x04 /* SDC ver 2+ */
#define CT_SDC 0x0C /* SDC */
#define CT_BLOCK 0x10 /* Block addressing */
/* MMC/SD command */
#define CMD0 (0) /* GO_IDLE_STATE */
#define CMD1 (1) /* SEND_OP_COND (MMC) */
#define ACMD41 (0x80+41) /* SEND_OP_COND (SDC) */
#define CMD8 (8) /* SEND_IF_COND */
#define CMD9 (9) /* SEND_CSD */
#define CMD10 (10) /* SEND_CID */
#define CMD12 (12) /* STOP_TRANSMISSION */
#define ACMD13 (0x80+13) /* SD_STATUS (SDC) */
#define CMD16 (16) /* SET_BLOCKLEN */
#define CMD17 (17) /* READ_SINGLE_BLOCK */
#define CMD18 (18) /* READ_MULTIPLE_BLOCK */
#define CMD23 (23) /* SET_BLOCK_COUNT (MMC) */
#define ACMD23 (0x80+23) /* SET_WR_BLK_ERASE_COUNT (SDC) */
#define CMD24 (24) /* WRITE_BLOCK */
#define CMD25 (25) /* WRITE_MULTIPLE_BLOCK */
#define CMD32 (32) /* ERASE_ER_BLK_START */
#define CMD33 (33) /* ERASE_ER_BLK_END */
#define CMD38 (38) /* ERASE */
#define CMD55 (55) /* APP_CMD */
#define CMD58 (58) /* READ_OCR */
static volatile DSTATUS Stat = STA_NOINIT; /* Physical drive status */
static volatile UINT Timer1, Timer2; /* 1kHz decrement timer stopped at zero (disk_timerproc()) */
static BYTE CardType; /* Card type flags */
/*-----------------------------------------------------------------------*/
/* SPI controls (Platform dependent) */
/*-----------------------------------------------------------------------*/
/* Initialize MMC interface */
static void init_spi (void)
{
spi_init( SPIDEV, 5 * 1000 * 1000 ); /* 5Mbps */
gpio_set_function( DEF_SPI_TX_PIN, GPIO_FUNC_SPI );
gpio_set_function( DEF_SPI_RX_PIN, GPIO_FUNC_SPI );
gpio_set_function( DEF_SPI_SCK_PIN, GPIO_FUNC_SPI );
/* CS# */
gpio_init( DEF_SPI_CSN_PIN );
gpio_set_dir( DEF_SPI_CSN_PIN, GPIO_OUT);
CS_HIGH(); /* Set CS# high */
#if 0
for (Timer1 = 10; Timer1; ) ; /* 10ms */
#else
Delay( 10 );
#endif
}
/* Exchange a byte */
static BYTE xchg_spi (
BYTE dat /* Data to send */
)
{
uint8_t src, dst;
src = dat;
spi_write_read_blocking( SPIDEV, &src, &dst, 1 );
return (BYTE)dst;
}
/* Receive multiple byte */
static void rcvr_spi_multi (
BYTE *buff, /* Pointer to data buffer */
UINT btr /* Number of bytes to receive (even number) */
)
{
spi_read_blocking( SPIDEV, 0xff, buff, btr );
}
/* Send multiple byte */
static void xmit_spi_multi (
const BYTE *buff, /* Pointer to the data */
UINT btx /* Number of bytes to send (even number) */
)
{
spi_write_blocking( SPIDEV, buff, btx );
}
####1)定義
修正プログラムの先頭部分は ラズパイPico用のヘッダファイルの読み込みやデバイスアクセスに必要な定数やマクロを定義しています。
####2)init_spi()
今回、SDカードのアクセスに SPIを用いるので、SPIデバイスの初期化と使用するピンの設定を行っています。
SPIの信号は以下のピンを用いています。
GPIO 16 (pin 21) MISO/spi0_rx-> SDO
GPIO 17 (pin 22) Chip select -> !CS
GPIO 18 (pin 24) SCK/spi0_sclk -> SCK
GPIO 19 (pin 25) MOSI/spi0_tx -> SDI
####3)xchg_spi()
SPIへのデータの送信と受信を行う関数です。
SDK内の spi_write_read_blocking()を使って書き込み、読み出しを行っています。
####4)rcvr_spi_multi()
複数データの受信を行う関数です。
SDK内の spi_read_blocking()を使って読み出しを行っています。
####5)xmit_spi_multi()
複数データの送信を行う関数です。
SDK内の spi_write_blocking()を使って書き込みを行っています。
###Fatfsのコンフィグレーション
Fatfsをどのように使うかのコンフィグレーションをFatfs本体内のファイル ffconf.hで設定することができます。
今回はファイルのタイムスタンプに必要な時間を取得する機能がなたいめ、ファイル更新日時をマクロ固定で取得することにしました。
コンフィグレーションを以下のように変更します。
#define FF_FS_NORTC 0
↓
#define FF_FS_NORTC 1
##cmake用の定義ファイル
cmakeでビルドするための定義ファイルをsrcディレクトリ内に追加します。
**add_executable()**へビルド対象のCソースファイルを列記します。
diskio.c は今回使用しないため、記述に含みません。
main()関数を含むサンプルプログラムを記述するためのsd-test.cというファイル名を追記します。
ビルド後、 sd-testという名称のファイル群が出力されます。
add_executable(sd-test
sd-test.c
ff.c
ffsystem.c
ffunicode.c
mmc_pico_spi.c
)
# Pull in our pico_stdlib which pulls in commonly used features
target_link_libraries(sd-test pico_stdlib hardware_spi)
# create map/bin/hex file etc.
pico_add_extra_outputs(sd-test)
# add url via pico_set_program_url
example_auto_set_url(sd-test)
##サンプルプログラム
以下のサンプルプログラムを示します。
SDカード上の /TEST.TXT ファイルの内容を読み出し、/W_TEST1.TXT と W_TEST2.TXT を生成して読み出した内容を書き込みます。
プログラムの動作状況は UARTへ出力します。
最後に TEST.TXT の先頭10バイトを UARTへ出力します。
#include <stdio.h>
#include <string.h>
#include "ff.h"
#include "diskio.h"
#include "hardware/gpio.h"
#include "hardware/uart.h"
#define UART_ID uart1
#define BAUD_RATE 115200
#define UART_TX_PIN 20
#define UART_RX_PIN 21
char sbuff[64];
#define DEF_FATBUFF 1024
char buff_fattest[ DEF_FATBUFF ];
int fat_test_init( void )
{
DSTATUS ret;
int result = 0;
ret = disk_initialize( 0 );
if( ret & STA_NOINIT ) {
result = -1;
}
return result;
}
int fat_test_read( char *buff, int bsize )
{
FRESULT ret;
FATFS fs;
FIL fil;
UINT rdsz ;
ret = f_mount( &fs, "", 0 );
if( ret != FR_OK ) {
return -1;
}
ret = f_open( &fil, "test.txt", FA_READ );
if( ret != FR_OK ) {
return -2;
}
ret = f_read( &fil, buff, (UINT)bsize, &rdsz );
if( ret != FR_OK ) {
return -3;
}
f_close( &fil );
return (int)rdsz;
}
int fat_test_write( char *filename, char *buff, int size )
{
FRESULT ret;
FATFS fs;
FIL fil;
UINT wsize ;
ret = f_mount( &fs, "", 1 );
if( ret != FR_OK ) {
return -1;
}
ret = f_open( &fil, filename, FA_WRITE|FA_CREATE_ALWAYS );
if( ret != FR_OK ) {
return -2;
}
ret = f_write( &fil, buff, (UINT)size, &wsize );
if( ret != FR_OK ) {
return -3;
}
f_close( &fil );
return (int)wsize;
}
int main( void )
{
int ret;
int wsize;
uart_init( UART_ID, BAUD_RATE );
gpio_set_function( UART_TX_PIN, GPIO_FUNC_UART );
gpio_set_function( UART_RX_PIN, GPIO_FUNC_UART );
uart_puts( UART_ID, "\n----- START fatfs on pico -----\n" );
ret = fat_test_init();
if( ret != 0 ) {
uart_puts( UART_ID, "fat_test_init() ERROR!\n" );
}
ret = fat_test_read( buff_fattest, DEF_FATBUFF );
sprintf( sbuff, "fat_test_read() ret = %d\n", ret );
uart_puts( UART_ID, sbuff );
if( ret > 0 ) {
wsize = ret;
ret = fat_test_write( "w_test1.txt", buff_fattest, wsize );
sprintf(sbuff, "fat_test_write() ret = %d\n", ret );
uart_puts( UART_ID, sbuff );
ret = fat_test_write( "w_test2.txt", buff_fattest, wsize );
sprintf(sbuff, "fat_test_write() ret = %d\n", ret );
uart_puts( UART_ID, sbuff );
}
buff_fattest[ 10 ] = 0xd;
buff_fattest[ 11 ] = 0xa;
buff_fattest[ 12 ] = 0x0;
uart_puts( UART_ID, buff_fattest );
return 0;
}
以下の内容の test.txt を使った場合の出力結果です。
ABCDEFGHTJKLMNOPQRSTUVWXYZ
abcdefghtjklmnopqrstuvwxyz
0123456789
##さいごに
ラズパイPicoを使った工作はいろいろネット上に公開され、SDカードを使った例もあるようです。
ただ、複数の機能を組み合わせた工作品として組み込まれているため、SDカードのみを使う場合には他の機能を削除するなどの作業が必要そうだったので、FatFs単一でSDカードを使うプログラムを公開してみました。
配線もSDカードモジュールだけなら6本、UARTを含めても9本を接続することで使えるので、一度、試してみてはいかがでしょうか。
- 以上 -