RX マイコン用 FatFS のバージョンを最新版へ(ff14)

exFAT を有効にする

最近 64GB(SDXC) の SD カードを購入したので、RX72N Envision Kit でも試してみた。

FAT32 では、1つのファイルは最大 4GB までしか対応出来ず、SD カードの場合、32GB を超えるサイズは、標準で exFAT になっている。
exFAT は、もはや FAT32 とは、根本的に異なる構造のようで、新たに設計からやり直したファイルシステムのようだ。

FatFS では、exFAT をかなり前からサポートしており、ffconf.h の「FF_FS_EXFAT」を「1」にするだけだ。
リソースの少ないマイコンでも扱えるように、細かい工夫が色々してある、素晴らしいとしか言いようが無い。
何の苦労もなく利用できる事に、ChaN さんには感謝しかない。

ファイルが 4GB を超えるサイズを扱える事から、seek などの位置指定は 64 ビット仕様になるので、その修正も行った。
※FatFS では、「FSIZE_t」が 64 ビットで定義されている、exFATを使わない場合は、32ビットとなる。

ファイルリストは普通に取れたので、他も動作すると思えたが、ディレクトリの移動が出来ない・・・

調べると、exFAT の場合「f_getcwd」がカレントディレクトリパスを返さない・・・

これは、バグだろうと思い、良い機会でもあるので、ff13c から ff14 へアップグレードした。

しかし・・・

状況は変わらない・・・
何で?
と思い、f_getcwd の説明を読んでみたー

Note: In this revision, this function cannot retrieve the current directory path on the exFAT volume. It always returns the root directory path.

これは、仕様のようだ・・・

さて、どうするか・・・


カレントパスを自分で管理するしか無い

以前の実装では、自分で管理していたが、FatFS にその機能がある事が判り、FatFS の API を使うように変更したのに・・・

先祖返りとは・・・

そもそも、カレントパスの管理が一つのコンテキストなのは多少問題でもある。
※ FAT ではファイル名の文字コードが拡張2バイトで、ロケールに左右される問題点があった。(たとえば、アラビア語とに日本語を同時に利用出来ない)

FreeRTOSなどを使うようになって、複数のタスクから、ファイルシステムにアクセスする場合、カレントパスを共有するのは「少しマズイ」。

そこで、それを見越して、ファイルのアクセス関係を少し整理してみた。

ただ、複雑な相対パスなどを認識する事は出来ない簡易的な解析機能しか無い。


Micron 64GB SDXC カードの速度

※参考速度

Write: 'test.bin'
Write Open:  23 [ms]
Write: 684 KBytes/Sec
Write Close: 4 [ms]
Read: 'test.bin'
Read Open:  1 [ms]
Read: 1622 KBytes/Sec
Read Close: 0 [ms]

読み込み速度は、1.6M バイトもあるー

FreeRTOS 対応

FreeRTOS などで、マルチスレッドでファイルシステムを動かす場合、排他制御を有効にする必要がある。

ffconf.h の主要部分を書き換える。

#include <FreeRTOS.h>
#include <semphr.h>
#define FF_FS_REENTRANT 1
#define FF_FS_TIMEOUT   1000
/// #define FF_SYNC_t       HANDLE
#define FF_SYNC_t       xSemaphoreHandle

※「FF_FS_LOCK」は、「FF_FS_REENTRANT」の制御を行う場合、「0」にしておく必要がある。

ffsystem.c の修正。
このソースには、排他制御の呼び出しが集約されている、標準では、WIN32 関係 API の呼び出しになっているので、それをコメントアウトする。
FreeRTOS 用は、既に実装されており、コメントアウトされているので、そのコメントアウトを外して有効にする。

int ff_cre_syncobj (    /* 1:Function succeeded, 0:Could not create the sync object */
    BYTE vol,           /* Corresponding volume (logical drive number) */
    FF_SYNC_t* sobj     /* Pointer to return the created sync object */
)
{
    /* Win32 */
//  *sobj = CreateMutex(NULL, FALSE, NULL);
//  return (int)(*sobj != INVALID_HANDLE_VALUE);

    /* FreeRTOS */
    *sobj = xSemaphoreCreateMutex();
    return (int)(*sobj != NULL);
}

int ff_del_syncobj (    /* 1:Function succeeded, 0:Could not delete due to an error */
    FF_SYNC_t sobj      /* Sync object tied to the logical drive to be deleted */
)
{
    /* Win32 */
//  return (int)CloseHandle(sobj);

    /* FreeRTOS */
    vSemaphoreDelete(sobj);
    return 1;
}

int ff_req_grant (  /* 1:Got a grant to access the volume, 0:Could not get a grant */
    FF_SYNC_t sobj  /* Sync object to wait */
)
{
    /* Win32 */
//  return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);

    /* FreeRTOS */
    return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
}

void ff_rel_grant (
    FF_SYNC_t sobj  /* Sync object to be signaled */
)
{
    /* Win32 */
//  ReleaseMutex(sobj);

    /* FreeRTOS */
    xSemaphoreGive(sobj);
}

標準では、exFAT は無効にしておく・・

一応、動作するようだが、もう少し、全体をチェックしたいので、標準では「無効」にしてコミットしておく事にする。

gcc-8 系での、strncpy などの警告

gcc-7 系から、strcpy、strncpy などを使う場合に、コピー先の配列サイズが、コピー元サイズより少ない場合、「警告」が出るようになった。
※サイズ不足でコピーが出来ない場合、終端「0」が抜けてしまい、問題が起こる、これを抑止するものだ。
※どのように判断しているのか不明だが、警告が出る場合と出ない場合があり、対処するのが難しい場合がある。
std::string などを使って、動的に配列を作れば良いのだが、組み込み系ではそうもいかない。

警告を完全に抑止するようなオプションを付ける事も方法の一つではあるのだが、それも、少し違う。

「ここは、まず、大丈夫なハズ」でも警告が出る場合は、以下のように抑止するようにした。

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wstringop-truncation"
                strncpy(&dst[l], base, len - l - 1);
#pragma GCC diagnostic pop

また、strncpy の亜種を実装してある、名前空間「str」に置いてある。
「string_utils.hpp」
あらかじめ、コピーする最大サイズを -1 して、最後は「0」で埋める。

        static char* strncpy_(char* dst, const char* src, uint32_t len) noexcept
        {
            if(dst == nullptr || src == nullptr || len == 0) return dst;

            auto tmp = dst;
            --len;
            while(tmp < (dst + len)) {
                auto ch = *src++;
                *tmp++ = ch;
                if(ch == 0) {
                    return dst;
                }
            }
            *tmp = 0;
            return dst;
        }

文字列の扱いは、メモリが少ない場合システムの場合でも、少し考える必要がある。

std::string 専用のアロケーターを実装して、簡単なメモリ管理を実装するのも良さそうだが、RX24T 位のデバイスを考えると、有限のメモリで凌ぐのでも良いかと考えてしまう・・・