stm32f4でのFAT32メディアエミュレーション





最近、このタスクが発生しました-stm32f4でFAT32メディアをエミュレートします。



その異常性は、マイクロコントローラのストラップの中にドライブがまったくないかもしれないという事実にあります。



私の場合、ドライブがありましたが、それを操作するためのルールではファイルシステムを配置できませんでした。 ただし、TKでは、データにアクセスするための大容量記憶装置インターフェイスを整理する必要がありました。



作業の結果は、同じ.hおよび.cファイルで構成される「emfat」というタイトルのモジュールになりました。



モジュールはプラットフォームに依存しません。 添付の例では、stm32f4discoveryボードで実行されます。



このモジュールの機能は、usb-hostが要求するファイルシステムの一部を提供し、ファイルを読み取ろうとするとユーザーデータを置き換えることです。



誰が役に立つか



まず 、デバイスが読み取り専用モードで大容量記憶装置インターフェイスを提供する技術的なソリューションで役立ちます。 この場合、「オンザフライ」でFAT32エミュレーションを使用すると、FSをサポートする必要なく、希望どおりにデータを保存できます。



第二に 、それは麻酔に役立ちます。 物理ドライブを持っていないが、大事な「マイコンピュータ」で自分のデバイスをディスクとして見たい人。 同時に、ディスクのルートには、命令、ドライバー、デバイスバージョンの説明を含むファイルなどが含まれる場合があります。



この場合、メディアをエミュレートする代わりに、準備されたFSの「コンパイル済み」キャストのホスト部分を与えることができます。 ただし、この場合、ほとんどの場合、MKのメモリ消費量は大幅に高くなり、ソリューションの柔軟性はゼロになります。



それで、それはどのように機能しますか。







ユーザーがファイルを読み書きしようとすると、対応する呼び出しがusbリクエストに変換され、デバイスに送信されます。 クエリの本質は単純です-宛先メディアのセクタを読み書きします。



この場合、Windows(または別のOS)はメディア上のストレージを整理するという点でホステスのように振る舞うことに注意する必要があります。 彼女が読んだり、書きたいセクターを知っているのは彼女だけです。 そして、彼は望みます-そして、私たちを完全にデフラグし、混oticとした「ジャグリング」セクターを配置します...したがって、典型的なUSB MSCコントローラーの機能は、512バイトの一部をメディアにシフトして柔和に注ぎ、またはその部分を読み取ることです。



エミュレーション機能に戻りましょう。



メディアへの記録をエミュレートしないことをすぐに警告しなければなりません。 当社のメディアは読み取り専用です。



これは、ファイルテーブルの形成を制御する複雑さが増したためです。



ただし、モジュールAPIにはダミー関数emfat_writeがあります。 おそらく将来的には、レコードを正しくエミュレーションするための解決策が見つかるでしょう。



要求を読み取るときのモジュールのタスクは、有効なデータを「与える」ことです。 これが彼の主な作品です。 要求されたセクターに応じて、このデータは次のようになります。



「どのデータを提供するか」という決定を加速することに重点が置かれたことに注意してください。 したがって、オーバーヘッドは最小化されました。



ドライブへの書き込みを拒否したため、ストレージ構造を自由に編成できます。







いくつかの詳細を除き、すべてが完全に標準です。





当然、この構造は架空のものであることを理解する必要があります。 実際には、RAMには含まれていませんが、読み取られるセクターの数に応じて、それに応じて形成されます。



モジュールAPI



APIは、次の3つの関数のみで構成されています。



bool emfat_init(emfat_t *emfat, const char *label, emfat_entry_t *entries); void emfat_read(emfat_t *emfat, uint8_t *data, uint32_t sector, int num_sectors); void emfat_write(emfat_t *emfat, const uint8_t *data, uint32_t sector, int num_sectors);
      
      





これらのうち、メイン関数はemfat_initです。



そのユーザーは、USBデバイスを接続するとき、またはコントローラーの開始時に1回呼び出します。

関数パラメーター-ファイルシステムインスタンス(emfat)、セクションラベル(label)、およびファイルシステムの要素のテーブル(entries)。



テーブルは、次のようにemfat_entry_t構造体の配列として定義されます。



 static emfat_entry_t entries[] = { // name dir lvl offset size max_size user read write { "", true, 0, 0, 0, 0, 0, NULL, NULL }, // root { "autorun.inf", false, 1, 0, AUTORUN_SIZE, AUTORUN_SIZE, 0, autorun_read_proc, NULL }, // autorun.inf { "icon.ico", false, 1, 0, ICON_SIZE, ICON_SIZE, 0, icon_read_proc, NULL }, // icon.ico { "drivers", true, 1, 0, 0, 0, 0, NULL, NULL }, // drivers/ { "readme.txt", false, 2, 0, README_SIZE, README_SIZE, 0, readme_read_proc, NULL }, // drivers/readme.txt { NULL } };
      
      





テーブルには次のフィールドがあります。



name:要素の表示名。

dir:アイテムがディレクトリ(それ以外の場合はファイル)かどうか。

lvl:要素のネストのレベル(要素を現在のディレクトリに帰属させるか、上記のディレクトリに帰属させるかを理解するにはemfat_init関数が必要です);

offset:カスタムコールバック関数を呼び出してファイルを読み取るときの追加のオフセット。

サイズ:ファイルサイズ

user:この値は、ファイルを読み取るためのユーザーコールバック関数に「そのまま」転送されます。

read:ファイルを読み取るためのユーザーコールバック関数へのポインター。



コールバック関数には次のプロトタイプがあります。



 void readcb(uint8_t *dest, int size, uint32_t offset, size_t userdata);
      
      





ファイルを読み取る「場所」アドレス(destパラメーター)、読み取られるデータのサイズ(サイズ)、オフセット(オフセット)、およびユーザーデータが渡されます。

また、テーブルにはmax_sizeフィールドとwriteフィールドがあります。 max_sizeの値は常にsizeの値と等しくなければならず、writeの値はNULLでなければなりません。



他の2つの関数はemfat_writeとemfat_readです。



前に述べたように、最初はダミーですが、セクターを書き込む要求がOSから来た場合に呼び出します。

2番目は、セクターを読み取るときに呼び出す必要がある関数です。 彼女は、要求されたセクター(セクター)に応じて、自分に与えられたアドレス(データ)にデータを入力します。



ファイルに関連するデータセクターを読み取る場合、emfatモジュールはセクター番号を読み取り中のファイルのインデックスとオフセットに変換し、その後ユーザーのコールバック読み取り関数を呼び出します。 ユーザーは、それぞれ特定のファイルの「ピース」を提供します。 それがどこから来たのかは、図書館にとって興味深いことではありません。 そのため、たとえば、顧客のプロジェクトで、内部フラッシュメモリの構成ファイル、RAMおよびspi-flashのその他のファイルを提供しました。



サンプルコード



 #include "usbd_msc_core.h" #include "usbd_usr.h" #include "usbd_desc.h" #include "usb_conf.h" #include "emfat.h" #define AUTORUN_SIZE 50 #define README_SIZE 21 #define ICON_SIZE 1758 const char *autorun_file = "[autorun]\r\n" "label=emfat test drive\r\n" "ICON=icon.ico\r\n"; const char *readme_file = "This is readme file\r\n"; const char icon_file[ICON_SIZE] = { 0x00,0x00,0x01,0x00,0x01,0x00,0x18, ... }; USB_OTG_CORE_HANDLE USB_OTG_dev; //    emfat_t emfat; // callback    void autorun_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata); void icon_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata); void readme_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata); //   static emfat_entry_t entries[] = { // name dir lvl offset size max_size user read write { "", true, 0, 0, 0, 0, 0, NULL, NULL }, // root { "autorun.inf", false, 1, 0, AUTORUN_SIZE, AUTORUN_SIZE, 0, autorun_read_proc, NULL }, // autorun.inf { "icon.ico", false, 1, 0, ICON_SIZE, ICON_SIZE, 0, icon_read_proc, NULL }, // icon.ico { "drivers", true, 1, 0, 0, 0, 0, NULL, NULL }, // drivers/ { "readme.txt", false, 2, 0, README_SIZE, README_SIZE, 0, readme_read_proc, NULL }, // drivers/readme.txt { NULL } }; // callback    "autorun.inf" void autorun_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata) { int len = 0; if (offset > AUTORUN_SIZE) return; if (offset + size > AUTORUN_SIZE) len = AUTORUN_SIZE - offset; else len = size; memcpy(dest, &autorun_file[offset], len); } // callback    "icon.ico" void icon_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata) { int len = 0; if (offset > ICON_SIZE) return; if (offset + size > ICON_SIZE) len = ICON_SIZE - offset; else len = size; memcpy(dest, &icon_file[offset], len); } // callback    "readme.txt" void readme_read_proc(uint8_t *dest, int size, uint32_t offset, size_t userdata) { int len = 0; if (offset > README_SIZE) return; if (offset + size > README_SIZE) len = README_SIZE - offset; else len = size; memcpy(dest, &readme_file[offset], len); } //       ,     -   //   int main(void) { emfat_init(&emfat, "emfat", entries); #ifdef USE_USB_OTG_HS USBD_Init(&USB_OTG_dev, USB_OTG_HS_CORE_ID, &USR_desc, &USBD_MSC_cb, &USR_cb); #else USBD_Init(&USB_OTG_dev, USB_OTG_FS_CORE_ID, &USR_desc, &USBD_MSC_cb, &USR_cb); #endif while (true) { } }
      
      





StorageMode.cモジュールの重要な部分(USB MSCイベント処理):



 int8_t STORAGE_Read( uint8_t lun, // logical unit number uint8_t *buf, // Pointer to the buffer to save data uint32_t blk_addr, // address of 1st block to be read uint16_t blk_len) // nmber of blocks to be read { emfat_read(&emfat, buf, blk_addr, blk_len); return 0; } int8_t STORAGE_Write(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { emfat_write(&emfat, buf, blk_addr, blk_len); return 0; }
      
      







結論



プロジェクトで大容量記憶装置を使用するために、ファイルシステムが組み込まれたドライブを用意する必要はありません。 FSエミュレーターを使用できます。



ライブラリは基本的な機能のみを実装し、いくつかの制限があります。



制限にもかかわらず、私は個人的にプロジェクトに既存の機能を十分に持っていますが、需要に応じて、将来的には更新バージョンのリリースを認めます。



プロジェクトリポジトリ

プロジェクトアーカイブへのリンク



All Articles