poniedziałek, 5 grudnia 2016

[4] STM32F7 - CubeMx - Obsluga karty SD

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:

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.    

Rys. 7. Okno projektu

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.

  1. /* USER CODE BEGIN PV */
  2. /* Private variables ---------------------------------------------------------*/
  3. FRESULT res;                //Resoult
  4. FATFS SDFatFs;              //Do karty SD
  5. FIL FileSDCard;                 //Do pliku
  6. DIR dir
  7. char buffer_write[100] = "Dane wprowadzone do pliku tekstowego\n\r";
  8. char buffer_read[100];      //Bufor danych odczytanych
  9. char SDPath_to_File[20];            //Ścieżka do pliku
  10. uint32_t bytes_to_write, bytes_to_read;  
  11. /* USER CODE END PV */

Teraz przykładowa część montowania, stworzenia pliku i katalogu:

  1. /* USER CODE BEGIN 2 */
  2.    
  3.   res = BSP_SD_Init(); //Wlaczenie biblioteki
  4.   if(res != FR_OK) { Error_Handler(); }
  5.   res = f_mount(&SDFatFs, "", 1); //Montowanie
  6.   if(res != FR_OK) { Error_Handler(); }
  7.   res = f_mkdir("Folder1"); //Stworzenie folderu
  8.   if(res != FR_OK) { Error_Handler(); }
  9.   res = f_mkdir("Folder2"); //Stworzenie projektu
  10.   if(res != FR_OK) { Error_Handler(); }
  11.   //res = f_opendir(&dir, "/"); //Otwarcie katalogu glownego
  12.   //if(res != FR_OK) { Error_Handler(); }
  13.   //Plik zostanie stworzony badz otwarty, z uprawnieniami zapisu oraz odczytu
  14.   res = f_open(&FileSDCard, "plik_do_otwarcia.txt", FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
  15.   if(res != FR_OK) { Error_Handler(); }
  16.   //Przeunięcie wskaźnika na koniec pliku
  17.   res = f_lseek(&FileSDCard, f_size(&FileSDCard));
  18.   if(res != FR_OK) { Error_Handler(); }
  19.   //Wprowadzenine danych do pliku
  20.   f_printf(&FileSDCard, "%s", buffer_write);
  21.   if(res != FR_OK) { Error_Handler(); }
  22.   //Po operacjach na nim przed ponowną obsluga nalezy go zapisac
  23.   f_close(&FileSDCard);
  24.   //Otwarcie pliku, jesli istanieje
  25.   res = f_open(&FileSDCard, "plik_do_otwarcia.txt", FA_READ);
  26.  if(res != FR_OK) { Error_Handler(); }
  27.  //Odczytanie danych do buffora
  28.  res = f_read(&FileSDCard, buffor_read, 100, &bytesread);
  29.  if(res != FR_OK) { Error_Handler(); }
  30.  //Zamkniecie pliku
  31.  f_close(&FileSDCard);
  32.  //Odmontowanie dysku
  33.  f_mount(0, "", 1);
  34.  /* USER CODE END 2 */

Drugi sposób otwarcia pliku wygląda następująco:

  1.    char nazwapliku[20] = "przykladowyplik.txt"; //moze byc bmp, jpg itp
  2.    //Potrzebne biblioteki inicjalizacja
  3.    MX_SDMMC1_SD_Init();
  4.    MX_FATFS_Init();
  5.    HAL_SD_MspInit(&hsd1);
  6.    BSP_SD_Init();          //Inicjalizacja karty SD,
  7.                            //sprawdzenie czy jest wlozona,
  8.                            //konfiguracja trybu 1 czy 4 bit SDIO
  9.                            //Zwraca: MSD_ERROR_SD_NOT_PRESENT, MSD_ERROR, MSD_OK
  10.    
  11.    if(f_mount(&SDFatFs, "", 1) == FR_OK)
  12.    {
  13.       if(f_open(&FileSDCard, nazwapliku , FA_CREATE_ALWAYS | FA_READ | FA_WRITE) == FR_OK)
  14.       {
  15.             //operacje na pliku
  16.       }
  17.       f_close(&FileSDCard);     //zamkniecie pliku
  18.       f_mount(0, "", 1);    //odmontowanie karty
  19.    }
  20.    else
  21.    {
  22.         //Blad montowania karty
  23.    }

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:

  1.        char nazwapliku[20] = "plik.txt"
  2.        char buffer[100];
  3.        int dane = 43;
  4.        uint8_t dane1 = 23;     
  5.        uint32_t dane2 = 74392;
  6.        
  7.        if(f_mount(&SDFatFs, "", 1) == FR_OK)
  8.        {
  9.         if(f_open(&FileSDCard, nazwapliku , FA_OPEN_EXISTING | FA_WRITE | FA_READ)  
                                                                        == FR_OK)
  10.           {
  11.               sprintf(buffer, "Dane: %d \t %u \t %lu",
  12.               res = f_lseek(&FileSDCard, f_size(&FileSDCard));
  13.               res = f_printf(&FileSDCard, buffer, sizeof(buffer));
  14.           }
  15.           f_close(&FileSDCard);
  16.           f_mount(NULL, "", 1);
  17.        }
  18.        else
  19.        {
  20.            //blad
  21.        }

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.