SDカード2


ChaNさんの FatFs 汎用FATファイルシステム・モジュールで汎用FATファイルシステムを作ってみました。
サンプル・プロジェクト (FatFs R0.09b) をダウンロードし、avrディレクトリより下記のファイルを使用しました。

diskio.h
ff.c
ff.h
ffconf.h (#define _USE_STRFUNC 1に変更する。 _FS_READONLY = 0, _USE_STRFUNC = 1でf_gets(), f_puts(), f_printf()が使用でき、 \n→\r\n変換はなしです。)
integer.h
mmcbb.c (回路図に合わせてPORTの#defineなどを下記のように変更する。)

mmcbb_c.gif

rtc.c (RTCに合わせ新しく作る。)
rtc.h(新しく作る。)

その他、main()の有るファイルに下記を書き加えます。
#include "ff.h"
#include "rtc.h"

DWORD get_fattime (void)
{
    RTC rtc;


/// if (!RtcOk) return 0;

    /* Get local time */
    rtc_gettime(&rtc);

    /* Pack date and time into a DWORD variable */
    return    ((DWORD)(rtc.year - 1980) << 25)
            | ((DWORD)rtc.month << 21)
            | ((DWORD)rtc.mday << 16)
            | ((DWORD)rtc.hour << 11)
            | ((DWORD)rtc.min << 5)
            | ((DWORD)rtc.sec >> 1);
}
下記にmainプログラム (KINGMAX 2GB SDカード使用) とその結果をのっけておきます。


mainプログラム
/*
 *  sd_card_test.c
 *  atmega644P
 *
 *
 *  Fuses(システムクロック:10MHz)
 *
 *              水晶    
 *  EXTENDED    0xFF
 *  HIGH        0xD9
 *  LOW         0xF7
 */

#include <stdio.h>                  /* sprintf() */
#include <string.h>                 /* strcat(), strtok() */
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 10.0E6                /* 10 MHz */
#include <util/delay.h>
#include "monitor.h"
#include "rtc.h"
#include "timer.h"
#include "usart0.h"

#include "ff.h"

extern unsigned short timerWDT;     /* timer.cより */

static SYSTIM tim;

/*---------------------------------------------------------*/
/* User Provided Timer Function for FatFs module           */
/*---------------------------------------------------------*/
/* This is a real time clock service to be called from     */
/* FatFs module. Any valid time must be returned even if   */
/* the system does not support a real time clock.          */
/* This is not required in read-only configuration.        */

DWORD get_fattime (void)
{
    RTC rtc;


/// if (!RtcOk) return 0;

    /* Get local time */
    rtc_gettime(&rtc);

    /* Pack date and time into a DWORD variable */
    return    ((DWORD)(rtc.year - 1980) << 25)
            | ((DWORD)rtc.month << 21)
            | ((DWORD)rtc.mday << 16)
            | ((DWORD)rtc.hour << 11)
            | ((DWORD)rtc.min << 5)
            | ((DWORD)rtc.sec >> 1);
}

FRESULT scan_files (
    char* path,         /* 開始ノード (ワークエリアとしても使用) */
    char* buffer 
)
{
    FRESULT res;
    FILINFO fno;
    DIR dir;
    int i;
    char *fn;           /* 非Unicode構成を想定 */
#if _USE_LFN
    static char lfn[_MAX_LFN + 1];
    fno.lfname = lfn;
    fno.lfsize = sizeof lfn;
#endif


    res = f_opendir(&dir, path);                            /* ディレクトリを開く */
    if (res == FR_OK) {
        i = strlen(path);
        for (;;) {
            res = f_readdir(&dir, &fno);                    /* ディレクトリ項目を1個読み出す */
            if (res != FR_OK || fno.fname[0] == 0) break;   /* エラーまたは項目無しのときは抜ける */
            if (fno.fname[0] == '.') continue;              /* ドットエントリは無視 */
#if _USE_LFN
            fn = *fno.lfname ? fno.lfname : fno.fname;
#else
            fn = fno.fname;
#endif
            if (fno.fattrib & AM_DIR) {                     /* ディレクトリ */
                sprintf(&path[i], "/%s", fn);
                res = scan_files(path, buffer);
                if (res != FR_OK) break;
                path[i] = 0;
                } else {                                    /* ファイル */
                ///printf_sio("%s/%s\r", path, fn);
                strcat(buffer, path);
                strcat(buffer, fn);
                strcat(buffer, ",");
            }
        }
    }

    return res;
}

///static void delay_us(unsigned short t){while(t--) _delay_us(1);}
static void delay_ms(unsigned short t){while(t--) _delay_ms(1);}

int main(void)
{
    static unsigned long ot[1];

    FATFS fs;
    FIL fp;
    BYTE buffer[512];
    FRESULT res;
    UINT bw;
    UINT br;
    TCHAR *tres;
    FILINFO fno;
    char path[32];
    char seps[] = " ,\t\n\r";
    char *token;
    FATFS *pfs;
    DWORD fre_clust, fre_sect, tot_sect;
    DWORD i;

    DDRA = 0b10001101;      /* SCL   SDA   ----- ----- CS   CLK    SDO   SDI   */
    DDRB = 0b00000001;      /* SCK   MISO  MOSI  ----- ----- ----- ----- LED   */
    DDRC = 0b00000000;      /* ----- ----- ----- ----- ----- ----- ----- ----- */
    DDRD = 0b00000000;      /* ----- ----- ----- ----- ----- ----- TXD0  RXD0  */

    PORTA = 0b01111011;
    PORTB = 0b11111110;
    PORTC = 0b11111111;
    PORTD = 0b11111100;

    initRTC();
    initTIMER();
    initUSART0();

    /* WDT ON (タイムアウト値:1秒) */
    __asm__ __volatile__("WDR"::);              /* WDTクリア */
    WDTCSR |= _BV(WDCE) | _BV(WDE);
    WDTCSR |= _BV(WDE) | _BV(WDP2) | _BV(WDP1); /* 4クロック以内に出す */
    sei();

    print_sio("START\r");

    delay_ms(1);

    /* ドライブ0にワーク・エリアを与える (常に成功する) */
    f_mount(0, &fs);


    /* ドライブ0にバイナリファイルを作成する */
    /* ファイルを作成します。同名のファイルがある場合は、サイズを0にしてから開きます。 */
    for(i=0; i<8; i++)
        buffer[i] = i + 0x10;
    res = f_open(&fp, "0:def.dat", FA_CREATE_ALWAYS | FA_WRITE);
    if(res != 0)
        printf_sio("ファイル 0:def.dat はオープンできません\r");
    else{
        f_write(&fp, buffer, 8, &bw);
        f_close(&fp);
    }

    /* ドライブ0のバイナリファイルを読む */
    /* 既存のファイルを開きます。ファイルが無いときはエラーになります。(デフォルト) */
    for(i=0; i<8; i++)
        buffer[i] = 0;
    res = f_open(&fp, "0:def.dat", FA_OPEN_EXISTING | FA_READ);
    if(res != 0)
        print_sio("ファイル 0:def.dat はオープンできません\r");
    else{
        while(1){
            f_read(&fp, buffer, sizeof(buffer), &br);
            if(br == 0)
                break;
            for(i=0; i<br; i++)
                printf_sio("0x%02x ", buffer[i]);
        }
        f_close(&fp);
    }
    putchar_sio('\r');
    putchar_sio('\r');


    /* ドライブ0にテキストファイルを作成する */
    /* ファイルを作成します。同名のファイルがある場合は、サイズを0にしてから開きます。 */
    res = f_open(&fp, "0:abc.dat", FA_CREATE_ALWAYS | FA_WRITE);
    if(res != 0)
        printf_sio("ファイル 0:abc.dat はオープンできません\r");
    else{
        f_puts("abcdefg\r\n", &fp);
        f_puts("hijklmn\r\n", &fp);
        f_close(&fp);
    }

    /* ドライブ0のテキストファイルを読む */
    /* 既存のファイルを開きます。ファイルが無いときはエラーになります。(デフォルト) */
    res = f_open(&fp, "0:abc.dat", FA_OPEN_EXISTING | FA_READ);
    if(res != 0)
        print_sio("ファイル 0:abc.dat はオープンできません\r");
    else{
        while(1){
            tres = f_gets((TCHAR *)buffer, sizeof(buffer), &fp);
            if(tres == NULL) break;
            print_sio((char *)buffer);
        }
        f_close(&fp);
    }


    /* ドライブ0にテキストファイルを作成する */
    /* 既存のファイルを開きます。ファイルが無いときはファイルを作成します。追記の場合は、 */
    /* この方法でオープンした後、f_lseek()でファイルの最後尾に移動してください。 */
    res = f_open(&fp, "0:zzz.dat", FA_OPEN_ALWAYS | FA_WRITE);
    if(res != 0)
        printf_sio("ファイル 0:zzz.dat はオープンできません\r");
    else{
        /* ファイル終端へ移動(ファイル追記の準備) */
        f_lseek(&fp, fp.fsize);
        f_printf(&fp, "%u, %u\r\n", 12345, 22345);
        f_close(&fp);
    }

    /* 追記 */
    res = f_open(&fp, "0:zzz.dat", FA_OPEN_ALWAYS | FA_WRITE);
    if(res != 0)
        printf_sio("ファイル 0:zzz.dat はオープンできません\r");
    else{
        f_lseek(&fp, fp.fsize);
        f_printf(&fp, "%u, %u\r\n", 32345, 42345);
        f_close(&fp);
    }
    
    /* ドライブ0のテキストファイルを開く */
    /* 既存のファイルを開きます。ファイルが無いときはエラーになります。(デフォルト) */
    res = f_open(&fp, "0:zzz.dat", FA_OPEN_EXISTING | FA_READ);
    if(res != 0)
        print_sio("ファイル 0:zzz.dat はオープンできません\r");
    else{
        while(1){
            tres = f_gets((TCHAR *)buffer, sizeof(buffer), &fp);
            if(tres == NULL) break;
            print_sio((char *)buffer);
        }
        f_close(&fp);
    }


    /* ファイル/ディレクトリの削除 */
    f_unlink( "0:zzz.dat");


    /* ファイル/ディレクトリのタイムスタンプの変更 */
    fno.fdate = (WORD)(((2013 - 1980) * 512U) | 4 * 32U | 3);
    fno.ftime = (WORD)(12 * 2048U | 34 * 32U | 56 / 2U);
    f_utime("0:abc.dat", &fno);


    /* ファイル・ステータスの取得 */
    f_stat("0:abc.dat", &fno);
    printf_sio("%s %lu %04d/%02d/%02d %02d:%02d:%02d 0x%02X\r", fno.fname, fno.fsize,
        (fno.fdate >> 9) + 1980, fno.fdate >> 5 & 0x0f, fno.fdate & 0x1f,
        fno.ftime >> 11, fno.ftime >> 5 & 0x3f, (fno.ftime & 0x1f) * 2,
        fno.fattrib);
    putchar_sio('\r');

    /* ディレクトリの読み出し */
    strcpy(path, "0:");
    buffer[0] = '\0';
    scan_files (path, (char *)buffer);  /* 上にこの関数が有ります */
    token = strtok((char *)buffer, seps);
    while(token != NULL){
        printf_sio("%s\r", token);
        token = strtok(NULL, seps);
    }
    putchar_sio('\r');


    /* ドライブ0のボリューム情報と空きクラスタ数を得る */
    f_getfree("0:", &fre_clust, &pfs);

    /* 全セクタ数と空きセクタ数を計算 */
    tot_sect = (pfs->n_fatent - 2) * pfs->csize;
    fre_sect = fre_clust * pfs->csize;

    /* ボリュームとと空きのサイズをKiB単位で表示 (512バイト/セクタと仮定) */
    printf_sio("%lu KB total drive space.\r%lu KB available.\r", tot_sect / 2, fre_sect / 2);
    printf_sio("Number of FAT entries (= number of clusters + 2) = %lu\r", pfs->n_fatent);
    printf_sio("Sectors per cluster (1,2,4...128) = %u\r", pfs->csize);


    /* ワーク・エリアを開放する */
    f_mount(0, NULL);

    get_tim(&tim);
    ot[0] = tim.ltime;
    while(1){
        get_tim(&tim);
        if((unsigned long)(tim.ltime - ot[0]) >= 500/MSEC){
            ot[0] = tim.ltime;
            PORTB ^= 0b00000001;
        }

        monitor();

        if(timerWDT != 0xffff)
            timerWDT = 0;           /* WDTのリセット */
    }
}


sd_card_gamen.jpg

上記プログラムの結果


sd_card_kairo.gif

回路図


sd_card_bb.JPG

実験風景


真空管
戻る