USB tömegtároló eszközosztály

A fejezet tartalma:
Az USB tömegtároló eszközosztályba (USB mass storage class) azok az USB eszközök tartoznak, amelyek az USB Implementers Forum által definiált kommunikációs protokollt használva fájlok átvitelét teszik lehetővé az általuk kezet tároló és a gazdagép között. Az USB tömegtároló eszköz tehát a gazdagép felől nézve külső USB-s háttértárolónak látszik. Az eszköz felismerését és kezelését a legtöbb operációs rendszer alapértelmezetten támogatja (nem kell hozzá külön meghajtóprogramot telepíteni).

A számítógépekhez használt USB tömegtároló eszközök például:
Az alábbiakban azt nézzük meg, hogy a FRDM-KL25Z kártya MKL25Z128VLK4 mikrovezérlőjével hogyan alakíthatunk ki adattárolásra alkalmas USB tömegtároló eszközt. Ehhez elsősorban az USBDevice programkönyvtárban található USBMSD objektumosztályra lesz szükségünk, ami  az USB-n kereszül zajló kommunikációt kezeli. Emellett azonban  definiálnunk kell azokat a függvényeket, amelyek a tényleges adatmozgatást végzik a tárolóeszközön.

Az USBMSD objektumosztály

Az USBMSD objektumosztály emulálja a tömegtároló eszközt az USB kapcsolaton keresztül. Az USBMSD osztály kezeli az MSD protokollt, tisztán virtuális függvények hívásán keresztül  (mint például disk_initialize, disk_write vagy disk_read)  kezeli a tárolóeszközt. Az USBMSD objektumosztály önmagában nem használható: ez csupán egy generikus objektumosztály, amelynek létre kell hozni egy olyan leszármazottját, amelyben valamilyen konkrét tárolóeszköz (SD kártya, SPI flash memóris stb.) kezelését ténylegesen kidolgozzuk.

A konkrét eszközhöz kidolgozandó objektumoszály tehát az USBMSD leszármazottja legyen, s az abból megörökölt, alább felsorolt virtuális függvényeket kell definiálnunk:

    virtual int disk_read(uint8_t * data, uint64_t block): egy blokk beolvasása
    virtual int disk_write(const uint8_t * data, uint64_t block): egy blokk írása
    virtual int disk_initialize(): a tárolóeszköz inicializálása
    virtual uint64_t disk_sectors(): visszaadja az adatblokkok számát
    virtual uint64_t disk_size(): visszaadja a memória méretét
    virtual int disk_status(): visszaadja a tárolóeszköz állapotát (0: OK, 1: nincs inicializálva,
                                        2: nincs lemez a meghajtóban, 4: írásvédelem be van kapcsolva)

Ezek a függvények kompatibilisek a FAT fájlrendszer kezelésre szolgáló könyvtárral, így ahhoz is használhatjuk  ezeket. Akár az is elképzelhető, hogy ugyanabban a programban az USB-n keresztül és helyileg is kezeljük a tárolóeszközt, csak vigyáznunk kell a kettős hozzáférésből adódó konfliktusok elkerülésére. A disk_status() felhasználásával például kialakíthatunk egy master/slave (mester/szolga) rendszert. Egy ilyen mintapélda az mbed.org oldalán is található SD_USB_FS_HelloWorld néven.

Mihelyt a fentebb felsorolt függvényeket definiáltuk, meghívhatjuk az USBMSD osztály connect() tagfüggvényét (például a leszármazott osztály konstruktorának a végén), hogy csatlakoztassuk az eszközt a gazdagéphez. A connect() meghívásakor először a disk_status() kerül meghívásra, majd a connect() függvény begyűjti a blokkok számára és a memória méretére vonatkozó információt (a megfelelő függvények meghívásával).

Az USBMSD_SD objektumosztály

Az USBMSD_SD objektumosztály egy mintapélda az USBMSD-ből leszármazott osztályra, ami egy, a mikrovezérlőnkhöz kötött SD memóriakártyát kezel. Ennek a példának a felhasználásával akár saját osztálykönyvtárat  is készíthetünk, például egy memóriachip kezeléséhez.

Az USBMSD_SD objektumosztálynak nincsenek olyan tagfüggvényei, amelyeket nekünk kellene meghívni, így kizárólag a konstruktorral kerülünk kapcsolatba.

Függvénynév
Rövid leírás
USBMSD_SD (PinName mosi,
        PinName      miso,
        PinName      sclk,
        PinName      cs
    )    
SD kártya fájlrendszerének kezelése az SPI porton keresztül

A paraméterek:

mosi - az SPI port adatkimenete (Master Out - Slave In), a FRDM-KL25Z kártyánál ez a PTD2 kivezetés.
miso - az SPI port adatbemenete (Master In - Slave Out), a FRDM-KL25Z kártyánál ez a PTD3 kivezetés.
sclk - az SPI port órajele (SPI Clock), a FRDM-KL25Z kártyánál ez a PTD1 kivezetés
cs -  az SPI port eszközkiválasztó jele (Chip Select), tetszőlegesen választott digitális kimenet.

A hardveres SPI használata miatt tehát a négy paraméterből három eleve kötött, a negyediket pedig célszerű a többi szomszédságában felvenni, így természetes választás lehet például a PTD0 kivezetés.

Mintapélda: kártyaolvasó az USBMSD_SD használatával

Az alábbi példában egy SD kártyát kezelő kártyaolvasót eszkábálunk össze, ami jól jöhet olyan esetben, amikor egy SD kártyát kellene kiolvasnunk, de nincs a számítógépünkben kártyaolvasó. A programhoz az USBMSD_SD objektumosztályt használjuk, s a konstruktor meghívásán kívül tulajdonképpen mással nem is kell foglalkoznunk.

A mintaprogramot és az USBMSD_SD objektumkönyvtárat Samuel Mokrani felhasználói lapjáról vettük.

Hardver követelmények:

Bekötési vázlat:


1. ábra: Az SD kártya csatlakoztatása a FRDM-KL25Z kártyához.

Megjegyzések:

1. lista: A 14_USBMSD_SD/main.cpp program listája
#include "mbed.h"
#include "USBMSD_SD.h"
//USBMSD_SD(PinName mosi, PinName miso, PinName sclk, PinName cs);
USBMSD_SD sd(PTD2, PTD3, PTD1, PTD0);

int main() {
while(1);
}

Sikeres programfeltöltés és csatlakozás után első alkalommal a számítógép az új eszköz felismerésére utaló felirat jelenik meg. Felismerés után a megjelenő új meghajtót megnyithatjuk és fájlműveleteket végezhetünk (mintha USB pendrive volna).


2. ábra: Az eszköz sikeres felismerése és használatba vétele

Az USBMSD_Ram objektumosztály

Az USBMSD_SD objektumosztályhoz hasonlóan a RAM_DISK (más néven USBMSD_Ram) is egy, az USBMSD-ből leszármazott osztály. Annyi a különbség, hogy ebben a FRDM-KL25Z kártya MKL25Z128VLK4mikrovezérlőjének RAM memóriájában hozunk létre egy minimális méretű (mindössze 32 x 512 bájt) FAT fájlrendszert, amelyet az USB kapcsolaton keresztül kiajánlunk a gazdagépnek. Gyakorlati haszna természetesen nincs ennek a minimalista megoldásnak, pusztán azért mutatjuk be, mert ennek felhasználásával SD kártya nélkül is kipróbálhatjuk az USBMSD eszközosztály működését.

A teljes forráskód bemutatásra itt nem vállalkozunk. A lényeg a fejezet elején, az USBMSD objektumosztálynál említett virtuális függvények implementálása, ami a a 2. listán látható. A függvények a disk_image nevű adattömböt kezelik, amelyet kezdeti tartalmát inicializáláskor a flash memóriából töltjük fel egy memcpy() függvényhívással. Amint láthatjuk, a virtuális függvények többsége egyetlen programsorral megvalósítható, a szektor írása vagy olvasása, pedig csupán egy-egy memcpy() függvényhívással elintézhető.

2. lista: Az USBMSD_Ram objektumosztály implementációja (részlet a forráskódból)
...
USBMSD_Ram::USBMSD_Ram() {
//no init
_status = 0x01;
memcpy(disk_image, disk, 512*5);
connect();
}

int USBMSD_Ram::disk_initialize() {
// OK
_status = 0x00;
return 0;
}

int USBMSD_Ram::disk_write(const uint8_t * buffer, uint64_t block_number) {
memcpy((void *)&disk_image[block_number*512], buffer, 512);
return 0;
}

int USBMSD_Ram::disk_read(uint8_t * buffer, uint64_t block_number) {
memcpy(buffer, &disk_image[block_number*512], 512);
return 0;
}

int USBMSD_Ram::disk_status() { return _status; }
int USBMSD_Ram::disk_sync() { return 0; }
uint64_t USBMSD_Ram::disk_sectors() { return 32; }
uint64_t USBMSD_Ram::disk_size() { return 32*512;}

Mintapélda: Virtuális "kártyaolvasó" USBMSD_Ram használatával

Az alábbi példában egy virtuális USB kulcsot vagy kártyaolvasót készítünk, amely a FRDM-KL25Z kártya MKL25Z128VLK4 mikrovezérlőjének RAM memóriájában tárol adatokat. Ezt a mintapéldát arra az esetre ajánljuk, ha nincs kéznél SD kártya foglalt, amit a FRDM-KL25Z kártyához csatlakoztatnánk. A RAM természetesen csak addig őrzi a beleírt adatokat, amíg a tápfeszültséget meg nem szüntetjük, vagy a mikrovezérlő újra nem indul. A  nyúlfarknyi főprogram gyakorlatilag semmit sem csinál. Az érdemi tevékenység (az adatok kiajánlása az USB kapcsolaton keresztül) az USBMSD_Ram programkönyvtárban történik.

A mintaprogramot és az USBMSD_Ram objektumkönyvtárat Richard Green felhasználói lapjáról vettük át.

Hardver követelmények:
3. lista: A 14_USBMSD_Ramdisk program listája
#include "mbed.h"
#include "USBMSD_Ram.h"

USBMSD_Ram sd;

int main() {
while(1);
}