Tym razem chciałbym przedstawić sposób rozpoczęcia pracy z kartą SD na mikrokontrolerze STM32F7 i środowisku CubeMx oraz System Workbench. Cały proces jest bardzo szybki i mało problematyczny. Po wygenerowaniu projektu w środowisku, prace można rozpocząć odrazu, nie trzeba wykonywać żadnych dodatkowych operacji na bibliotece, zostaje tylko przygotowanie podstawowych funkcji odpowiedzialnych za obsługę.
Przygotowanie projektu w CubeMx
Jak zwykle projekt należy najpierw wygenerować w środowisku CubeMx. Można to zrobić zarówno dla płytki jak i dla mikrokontrolera.
Ja wybrałem płytkę discovery, i nie czyściłem przygotowanego projektu, natomiast nic nie stoi na przeszkodzie aby to wykonać. Procedurę przedstawiłem na rysunku 1 oraz na rysunku 2.
Rys. 1. Wstępne włączenie projektu
Rys. 2. Czyszczenie wyprowadzeń
Teraz można przejść do włączenia opcji dla programatora oraz skonfigurowania zegara. Ja wybrałem kwarc zamontowany na płytce, o wartości 25MHz.
Poniżej na rysunku 3 włączenie ustawień debuggera, kwarcu oraz SDIO dla karty SD. Można wybrać tryb czterobitowy bądź jednobitowy. Tak czy tak projekt będzie wyglądał tak samo, zmieniająć odpowiednią definicje będzie można wykorzystywać jedną lub cztery linia. Co oczywiście wpłynie na szybkość zapisu oraz odczytu danych z układu.
Rys. 3. Włączenie karty SD
Rys. 4. Włączenie programatora, kwarcu oraz ustawienie zegara
Następnie w ustawieniach projektu nie trzeba właściwie nic zmieniać, dodatkowo do debbugowania można uruchomić układ USART, np. USART'1. Jest on najwygodniejszym rozwiązaniem ponieważ jest podłączony do portu microUSB na płytce Discovery.
Ze zmian w zakładce configuration to przede wszystkim trzeba ustawić parametry mikrokontrolera:
Następny etap to już wygenerowanie projektu. Z błędów jakie się mogą pojawić to np. niezdefiniowane zmienne, to można rozwiązać to poprzez ponowne wgranie projektu. Drugą rzeczą jaka może się pojawić to problem z wygenerowanie projektu. To natomiast spowodowane może być poprzez obsługiwane niedostępnej wersji oprogramowania, która jest dodana natomiast nie można jej pobrać na komputer, przynajmniej ja na taki problem się natknąłem.
Ze zmian w zakładce configuration to przede wszystkim trzeba ustawić parametry mikrokontrolera:
Rys. 5. Ustawienie pamięci i Cortex 7
Rys. 6. Ustawienie parametrów biblioteki
Następny etap to już wygenerowanie projektu. Z błędów jakie się mogą pojawić to np. niezdefiniowane zmienne, to można rozwiązać to poprzez ponowne wgranie projektu. Drugą rzeczą jaka może się pojawić to problem z wygenerowanie projektu. To natomiast spowodowane może być poprzez obsługiwane niedostępnej wersji oprogramowania, która jest dodana natomiast nie można jej pobrać na komputer, przynajmniej ja na taki problem się natknąłem.
Projekt zostanie wygenerowany, natomiast nie zostanie automatycznie dodany do kompilatora. Aby go dodać należy postępować tak jak na rysunku poniżej:
Rys. 8. Wgrywanie wygenerowanego projektu
W oknie programu System Workbench Ac6 należy kliknąć prawym przyciskiem myszy i wybrać import projekt. Następnie z okna general wybrać existing projekt po czym w kolenym oknie kliknąć browse. Następnie gdy on się już załaduje można go z=przekopiować do głównego okna katalogu, bądź operować na oryginale.
Dalej to zostaje już obsługa funkcji odbierających i wysyłających dane. Ogólnie operacje na plikach bądź folderach są dosyć schematyczne. Całość rozpoczyna się od włączenia biblioteki z BSP, zamontowania karty, dalej wykonuje się odpowiednie operacje na plikach odczytanie, zapisanie, stworzenie pliku, katalogu, dopisanie danych itp. Po dokonaniu operacji plik trzeba zamknąć a dysk odmontować. Każdą z tych operacji należy sprawdzać poleceniem ErrorHandler, która wyłapie błąd jak wystąpi niespodziewana sytuacja podczas wykonywania sekwencji.
Teraz krótki program demonstracyjny:
Jeśli chodzi o wykorzystanie jest tego całe mnóstwo, od loggerów danych, oddtwarzanie obrazów, muzyki, programatorów i całe mnóstwo innych.
Całość należy rozpocząć od zdefiniowania kilku zmiennych, bądź do obsługi biblioteki i zapisywania stanów od funkcji przez buffory danych dla wprowadzenia czy odczytania ich.
- /* USER CODE BEGIN PV */
- /* Private variables ---------------------------------------------------------*/
- FRESULT res; //Resoult
- FATFS SDFatFs; //Do karty SD
- FIL FileSDCard; //Do pliku
- DIR dir
- char buffer_write[100] = "Dane wprowadzone do pliku tekstowego\n\r";
- char buffer_read[100]; //Bufor danych odczytanych
- char SDPath_to_File[20]; //Ścieżka do pliku
- uint32_t bytes_to_write, bytes_to_read;
- /* USER CODE END PV */
Teraz przykładowa część montowania, stworzenia pliku i katalogu:
- /* USER CODE BEGIN 2 */
- res = BSP_SD_Init(); //Wlaczenie biblioteki
- if(res != FR_OK) { Error_Handler(); }
- res = f_mount(&SDFatFs, "", 1); //Montowanie
- if(res != FR_OK) { Error_Handler(); }
- res = f_mkdir("Folder1"); //Stworzenie folderu
- if(res != FR_OK) { Error_Handler(); }
- res = f_mkdir("Folder2"); //Stworzenie projektu
- if(res != FR_OK) { Error_Handler(); }
- //res = f_opendir(&dir, "/"); //Otwarcie katalogu glownego
- //if(res != FR_OK) { Error_Handler(); }
- //Plik zostanie stworzony badz otwarty, z uprawnieniami zapisu oraz odczytu
- res = f_open(&FileSDCard, "plik_do_otwarcia.txt", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
- if(res != FR_OK) { Error_Handler(); }
- //Przeunięcie wskaźnika na koniec pliku
- res = f_lseek(&FileSDCard, f_size(&FileSDCard));
- if(res != FR_OK) { Error_Handler(); }
- //Wprowadzenine danych do pliku
- f_printf(&FileSDCard, "%s", buffer_write);
- if(res != FR_OK) { Error_Handler(); }
- //Po operacjach na nim przed ponowną obsluga nalezy go zapisac
- f_close(&FileSDCard);
- //Otwarcie pliku, jesli istanieje
- res = f_open(&FileSDCard, "plik_do_otwarcia.txt", FA_READ);
- if(res != FR_OK) { Error_Handler(); }
- //Odczytanie danych do buffora
- res = f_read(&FileSDCard, buffor_read, 100, &bytesread);
- if(res != FR_OK) { Error_Handler(); }
- //Zamkniecie pliku
- f_close(&FileSDCard);
- //Odmontowanie dysku
- f_mount(0, "", 1);
- /* USER CODE END 2 */
Drugi sposób otwarcia pliku wygląda następująco:
- char nazwapliku[20] = "przykladowyplik.txt"; //moze byc bmp, jpg itp
- //Potrzebne biblioteki inicjalizacja
- MX_SDMMC1_SD_Init();
- MX_FATFS_Init();
- HAL_SD_MspInit(&hsd1);
- BSP_SD_Init(); //Inicjalizacja karty SD,
- //sprawdzenie czy jest wlozona,
- //konfiguracja trybu 1 czy 4 bit SDIO
- //Zwraca: MSD_ERROR_SD_NOT_PRESENT, MSD_ERROR, MSD_OK
- if(f_mount(&SDFatFs, "", 1) == FR_OK)
- {
- if(f_open(&FileSDCard, nazwapliku , FA_CREATE_ALWAYS | FA_READ | FA_WRITE) == FR_OK)
- {
- //operacje na pliku
- }
- f_close(&FileSDCard); //zamkniecie pliku
- f_mount(0, "", 1); //odmontowanie karty
- }
- else
- {
- //Blad montowania karty
- }
Jest on moim zdaniem lepszy od tego zaprezentowanego wcześniej, ponieważ w przypadku wystąpienia błędu i problemu z otwarciem pliku bądź samej karty nie nastąpi wejście do obsługi błędu czyli nieskończonej pętli while. Dzięki temu program w przypadku nieudanej operacji na pliku się nie zawiesi.
Gdy ma się do czynienia z istniejącym plikiem i chce się dopisywać dane na jego końcu, to można wykorzystać następujące operacje:
- char nazwapliku[20] = "plik.txt"
- char buffer[100];
- int dane = 43;
- uint8_t dane1 = 23;
- uint32_t dane2 = 74392;
- if(f_mount(&SDFatFs, "", 1) == FR_OK)
- {
if(f_open(&FileSDCard, nazwapliku , FA_OPEN_EXISTING | FA_WRITE | FA_READ)
== FR_OK)
- {
- sprintf(buffer, "Dane: %d \t %u \t %lu",
- res = f_lseek(&FileSDCard, f_size(&FileSDCard));
- res = f_printf(&FileSDCard, buffer, sizeof(buffer));
- }
- f_close(&FileSDCard);
- f_mount(NULL, "", 1);
- }
- else
- {
- //blad
- }
Kolejną rzeczą jest otwieranie plików bez parametru FA_CREATE_ALWAYS. To co prawda nie zawiesi systemu, natomiast warto jakoś to sprawdzać. W takim celu należy stworzyć zmienną typu FILE *przyklad; po czym wpisać do niej parametr zwracany przez funkcję f_open. Jeśli będzie on równy NULL czyli 0 to będzie to oznaczać, że plik nie został znaleziony.
Jeśli chodzi o bufor danych to może on być dowolnie zmniejszany bądź zwiększany w zależności od potrzeby. Jeśli byłby on dużo większy to dobrym rozwiązaniem byłoby stosowanie CRC, bądź porównywania danych pomiędzy dwoma buforami. Na końcu przed ponownym odczytem nie zaszkodzi też taki bufor wyczyścić. Dzięki temu nie wprowadzi się niepotrzebnych śmieci do układu.