STM32Butterfly2: obsługa kart SD w trybie SPI

Nieodłącznym elementem, w realizowanych aplikacjach, jest przetwarzanie oraz gromadzenie danych. Zwykle wymaga to użycia dodatkowych układów scalonych, jednak w przypadku dużych ilości danych takie rozwiązanie może stać się mało wygodne i kosztowne, tym bardziej gdy zgromadzone dane mają być przetwarzane przez komputer lub dane są postaci plików. W takim przypadku najlepszym rozwiązaniem okaże się zastosowanie standardu stosowanego w świecie komputerów. Jednym z nich jest gromadzenie danych na kartach pamięci, bardzo popularnym jest stosowanie kart SD, głównym czynnikiem rozpowszechnienia jest dość prosta ich obsługa przy wykorzystaniu magistrali SPI.
 Poniższy projekt przedstawia właśnie komunikację z kartą SD. Zadaniem aplikacji jest uzyskanie dostępu do karty SD oraz przedstawienie na wyświetlaczu informacji o rodzaju karty oraz odczytanie pierwszego sektora zawierającego informację o typie partycji, a następnie wyświetlenie typu partycji, oraz pierwszego sektora LBA. W przypadku braku włożonej karty pojawia się informacja z prośbą o jej włożenie.

 

Fot. 1. Wygląd modułu KAmodMMC

Fot. 1. Wygląd modułu KAmodMMC

 

Aplikacja została przygotowana dla zestawów STM32Butterfly2 oraz STM32Butterfly. W przypadku zestawu STM32Butterfly2 złącze występuje na płytce, natomiast dla STM32Butterfly wymagany jest moduł dla kart SD – np. KAmodMMC (pokazany na fotografii 1), którego schemat elektryczny znajduje się na rysunku 2. W obu przypadkach dodatkowo potrzebny jest moduł z wyświetlaczem KAmodLCD1, na którym przedstawiane są informacje o stanie karty pamięci.

 

Rys. 2. Schemat elektryczny modułu KAmodMMC

Rys. 2. Schemat elektryczny modułu KAmodMMC

 

Ze względu na różnice w obu płytkach projekt wymaga odrębnej kompilacji, która to uwzględnia. W przypadku kompilacji dla zestawu STM32Butterfly wymagane jest aby plik Makefile wywołać z parametrem BOARD=1. Wywołanie to spowoduje, iż koprocesor zdefiniuje odpowiednie wartości określające, które linie są używane przez poszczególne elementy.

 

Rys. 3. Połączenie pomiędzy zestawem STM32Butterfly2 i modułem KAmodMMC

Rys. 3. Połączenie pomiędzy zestawem STM32Butterfly2 i modułem KAmodMMC

 

Na rysunku 3 przedstawiono połączenie zestawu ewaluacyjnego STM32Butterfly2 z modułem KAmodLCD1. Tak jak zaznaczono wcześniej przygotowaną aplikację można wykorzystać także z zestawem STM32Butterfly, w tym przypadku wymagany jest dodatkowo moduł kart pamięci SD. Jako moduł można zastosować KAmodMMC (jak pokazano na rysunku 4).

 

Rys. 4. Połączenia elektryczne pomiędzy STM32Butterfly i modułami KAmodLCD1 oraz KAmodMMC

Rys. 4. Połączenia elektryczne pomiędzy STM32Butterfly i modułami KAmodLCD1 oraz KAmodMMC

 

Poniżej przedstawiono program główny.

 

W pierwszym kroku programu głównego mamy funkcję realizującą inicjalizacje licznika SysTick. Operacja ta konfiguruje priorytet przerwania wywoływanego przez licznik, ustawienie częstotliwości występowania przerwania. Po przeprowadzeniu konfiguracji następuje uruchomienie licznika.

W kolejnym kroku inicjalizowany jest wyświetlacz bazujący na sterowniku PCD8544 lub zgodnym (wyświetlacz z telefonu Nokia 3310). Ze względu na fakt, iż złącze SPI jest używane do komunikacji z kartą SD, magistrala SPI do komunikacji z wyświetlaczem jest emulowana programowo. Funkcja nlcd_init() inicjalizująca wyświetlacz konfiguruje linie używane w komunikacji, ustawia na nich odpowiedni stan oraz przeprowadza inicjalizację wyświetlacza poprzez przesłanie do niego listy komend. Na koniec przeprowadzane jest wyczyszczenie ekranu w celu usunięcia „śmieci”, które mogą wystąpić w wyniku nieokreślonego stanu poszczególnych bitów w pamięci wyświetlacza.

Następnie wywoływana jest funkcja register_printf_putc_handler, która tworzy wskaźnik na funkcję wyświetlającą znak na wyświetlaczu. Wskaźnik ten wykorzystywany jest w dalszej części programu przez bibliotekę formatującą tekst i dane wyświetlane na wyświetlaczu.

Przed przejściem do nieskończonej pętli wywoływana jest funkcja card_insert_init(), która konfiguruje linię wykrywającą włożenie karty do gniazda jako wejściową. W nieskończonej pętli for, pierwszym poleceniem jest wyczyszczenie zawartości wyświetlacza. Później wywoływana jest funkcja card_wait4insert(), wymusza ona oczekiwanie na włożenie karty do gniazda. Poniżej przedstawiono listing funkcji.

 

Działanie funkcji card_wait4insert() polega na zapętleniu w pętli do..while, w czasie której odczytywany jest stan linii wykrywającej włożenie karty pamięci do gniazda. Odczyt stanu linii jest wykonywany dwukrotnie, z pewnym opóźnieniem w celu eliminacji drgań styków. Sam odczyt stanu realizowany jest przez funkcję io_get, która jako pierwszy parametr przyjmuje wartość określającą port, natomiast jako drugi parametr podawany jest numer odczytywanej linii. Funkcja kończy działanie gdy jeden z odczytów przyjmie inną wartość niż 0.

Po wykryciu włożenia karty SD do gniazda następuje wyczyszczenie wyświetlacza oraz inicjalizacja karty pamięci, która zwraca kod do zmiennej io_get. Zwrócony kod informuje o rodzaju karty lub o wystąpieniu błędu. Za inicjalizację odpowiada funkcja mmcInit(), w pierwszej części funkcji realizowana jest konfiguracja linii, do których podłączone jest gniazdo kart pamięci oraz włączenie zegarów portów używanych w transmisji oraz SPI, konfiguracja odbywa się przez wywołanie funkcji mmcHwInit(), której listing przedstawiono poniżej.

 

Podczas wykonywania funkcji mmcHwInit() ustawiany jest odpowiedni stan linii CS oraz przeprowadzana jest konfiguracja kontrolera SPI do pracy w trybie full duplex, jako master z 8 bitami danych. Za konfigurację tą odpowiada funkcja spi_cfg, która jako parametr przyjmuje dzielnik zegara taktującego kontroler SPI.

Po skonfigurowaniu linii realizowana jest inicjalizacja karty poprzez wysyłanie odpowiednich komend oraz reagowanie na zwracane odpowiedzi.

Po zakończeniu inicjalizacji karty SD, wywoływana jest funkcja printp, która wyświetla informacje o rodzaju karty. W rzeczywistości jest to definicja pod, którą ukrywa się kod przedstawiony poniżej.

#define printp(line,…) do { nlcd_set_position(0,line); tiny_printf(__VA_ARGS__); } while (0) 

Przedstawiona definicja, dla początkującego, zapewne wydaje się bardzo skomplikowana. Rozkładając definicje można zauważyć, iż mamy w niej kod pętli do { … } while (0). Pętla ta grupuje oba polecenia, wywoływane w ramach pętli, w całość. Ze względu na zadany warunek pętla wykona się jednokrotnie. Pierwszym działaniem jest ustawienie pozycji kursora na wyświetlaczu, używany jest do tego celu podawany parametr – line. Następnie wywoływana jest funkcja tiny_printf, która przyjmuje jako parametr pozostałe argumenty. Funkcja ta wypisuje sformatowany tekst i dane na wyświetlaczu, wykorzystując do tego celu wcześniej przygotowany wskaźnik na funkcję wypisującą znak na wyświetlaczu.

W przypadku niepowodzenia inicjalizacji karty (zwracana wartość poniżej 0) program przechodzi do nieskończonej pętli blokując dalsze działanie. W przypadku powodzenia inicjalizacji karty SD program wyświetla informacje o pojemności karty SD, wyrażoną w MB. Pojemność uzyskiwana jest dzięki funkcji mmcCapacity().

W kolejnym kroku wywoływana jest funkcja print_partrecord(), która odczytuje pierwszy sektor, zawierającego informacje o partycji, oraz wyświetla informacje: o typie partycji, pierwszym sektorze LBA oraz o rozmiarze sektora.

Po uzupełnieniu wyświetlacza o dodatkowe informacje program przechodzi do oczekiwania na wyjęcie karty z gniazda, realizuje to funkcja card_wait4remove(). Jej działanie sprowadza się do zapętlenia programu do czasu zmiany stanu na odpowiedniej linii na stan wysoki.

Jak można zauważyć w programie głównym brakuje kodu odpowiedzialnego za konfiguracje między innymi zegarów mikrokontrolera. Ta czynność i pozostałe inicjalizujące mikrokontroler do pracy jest wykonywana w ramach obsługi przerwania wywoływanego przez reset mikrokontrolera. Podczas obsługi przerwania wywoływana jest między innymi funkcja main().

Autor projektu: Lucjan Bryndza
Redakcja i przygotowanie: Mariusz Dziębow

Do pobrania

Autor: