Chciałbym się podzielić moimi spostrzeżeniami i przeróbkami biblioteki Petit FatFs w wersji R0.03. Zasadniczo jest to zbiór kodów oraz informacji znalezionych w sieci, a ja to jedynie posprzątałem i co nieco rozbudowałem część niskopoziomową. Postaram się to przedstawić w prostej i zwięzłej formie.
O sposobach podłączenia karty do mikrokontrolera nie będę się rozpisywał, bo w sieci jest mnóstwo miejsc, w których jest to solidnie opisane.
Przytoczę tu trzy adresy, z których ja skorzystałem:
http://mikrokontrolery.blogspot.com/201 ... ci-sd.html
http://elm-chan.org/docs/mmc/mmc_e.html
http://elm-chan.org/docs/spi_e.html
Gniazdo kart microSD, z którego było mi dane korzystać, znajduje się na wyświetlaczu TFT i podłączone jest do SPI poprzez scalony bufor 74HC245, który zapewnia translację napięć 3.3V <> 5V. Ok, koniec ględzenia... Czas na jakiś kawałek kodu.
Poniżej udostępniam program montujący kartę i wyświetlający przez uart listę plików znajdujących się w katalogu głównym. Program jest bogato skomentowany, ale niektóre drobiazgi omówię i tutaj.
Aby rozpocząć pracę z kartą, należy ją zamontować poleceniem pf_mount(); Polecenie to przyjmuje argument w postaci adresu struktury typu FATFS. W strukturze tej, po udanym zamontowaniu karty, znajdziemy podstawowe dane o systemie plików. Póki co nie miałem potrzeby korzystania z tych danych.
Aby otworzyć plik znajdujący się w katalogu głównym wystarczy polecenie pf_open(), do którego należy przekazać w postaci tekstu nazwę tego pliku wraz z rozszerzeniem. Przykład:
Kod: Zaznacz cały
result = pf_open("BITMAPA1.BMP");
Dlaczego użyłem zmiennej result? Ano po to, by od razu znać wynik próby otwarcia pliku. Jeżeli plik nie istnieje, bo np. błędnie określiliśmy nazwę,
funkcja zwróci nam kod FR_NO_FILE. Jest to prosty i skuteczny sposób na to, by nie próbować czytać z pliku, którego nie dało się otworzyć.
Tutaj uwaga: W pliku pffconf.h jest makro określające, czy biblioteka Petit FatFs ma dopuszczać do korzystania z małych liter w nazwach plików i ścieżkach.
Kod: Zaznacz cały
#define _USE_LCC 0
Jeśli parametr ten ustawiony jest na 0, należy używać wyłącznie wielkich liter. Jeśli wpiszemy nazwę pliku lub ścieżkę małymi literami, biblioteka zwróci nam kod FR_NO_FILE.
Jeśli parametr ustawimy na 1, będziemy mogli swobodnie korzystać z małych i wielkich liter, ale okupimy to zwiększonym zużyciem pamięci RAM o 128 bajtów. Coś za coś.
Ok, to teraz otwórzmy jakiś katalog znajdujący się na karcie. Przypuśćmy, że katalog ten ma nazwę music.
Aby otworzyć jakikolwiek katalog, musimy przygotować sobie strukturkę typu DIR. W przykładowym programie nazwałem tę strukturę katalog.
Przykład:
Kod: Zaznacz cały
result = pf_opendir(&katalog, "/");
Do polecenia pf_opendir() przekazuję adres struktury przechowującej dane o katalogu oraz ścieżkę do katalogu. W przypadku katalogu głównego należy przekazać kod znaku mniejszy od kodu spacji. Specyfika działania tego polecenia powoduje, że ukośnik jest pomijany i odczytywany jest kolejny znak. W naszym przypadku kolejnym znakiem jest null, czyli zero. A zatem warunek spełniamy, bo zero jest kodem niższym od kodu spacji. Dlaczego tak, a nie inaczej? Przecież można by zrobić tak:
Kod: Zaznacz cały
result = pf_opendir(&katalog, 0);
Można... I tak też działa, ale uznałem, ze taki zapis jest mniej czytelny.
Gdy już udało nam się otworzyć katalog, możemy odczytać jego zawartość. Aby móc skorzystać z tego dobrodziejstwa, potrzebujemy strukturę typu FILINFO, w której to znajdować się będzie wynik działania polecenia pf_readdir();
Przykład:
Kod: Zaznacz cały
result = pf_readdir(&katalog, &file
Wynikiem tego polecenia będzie wypełnienie wypełnienie struktury file. Ja z tego skorzystałem w następujący sposób:
Kod: Zaznacz cały
while (pf_readdir(&katalog, &file) != FR_NO_FILE)
{
//wysyłam nazwę znalezionego pliku
uart_puts(0, file.fname);
uart_puts_P(0, PSTR("\r\n"));
}
Czytam tak długo katalog główny, aż polecenie pf_readdir() natrafi na koniec wpisów w tablicy i zwróci FR_NO_FILE. Wtedy wiem, że przeczytałem całą zawartość katalogu (w tym przypadku głównego). Ok, na dziś to chyba wszystko, ale jeszcze dodam kilka słów o konfiguracji samego SPI.
Zasadniczo, o ile zadowolimy się dedykowanym pinem SS w ATmedze, nie trzeba niczego konfigurować, bo przygotowałem automat, który skompiluje odpowiedni kawałek kodu zależnie od wybranego przez nas układu w ustawieniach projektu. Poza tym w pliku AVRlowlevel.h są dwa makra:
Kod: Zaznacz cały
#define _HARDWARE_SPI
//#define _SOFTWARE_SPI
Jeśli z jakichś przyczyn nie możemy skorzystać ze sprzętowego SPI, wystarczy zakomentować makro _HARDWARE_SPI, odkomentować makro _SOFTWARE_SPI oraz określić piny, na których ma pracować programowe SPI:
Kod: Zaznacz cały
#if defined(_SOFTWARE_SPI)
#define SPI_SCK_PORT B
#define SPI_SCK_BIT 5
#define SPI_MISO_PORT B
#define SPI_MISO_BIT 4
#define SPI_MOSI_PORT B
#define SPI_MOSI_BIT 3
#define SPI_CS_PORT B
#define SPI_CS_BIT 2
Oczywiście programowe SPI działa nieco wolniej, ale nie ma tragedii - dużej różnicy nie ma.
No to chyba na tyle... Pozostaje dodać kompletny projekt dla IDE Eclipse:
Pozdrawiam.