niedziela, 14 lipca 2019

STM32F4 - Unit Test - Unity

W tym poście chciałbym opisać implementację Unit Test w projekcie dla układu STM32F4.

[Źródło: http://www.st.com/en/evaluation-tools/stm32f4discovery.html]

Testowanie na układu STM32:


Tutaj przedstawię wykonanie instrukcji testowych na platformie sprzętowej STM32F4.

Na samym początku należy pobrać pliki źródłowe (link).

Następnie trzeba dołączyć do projektu pliki źródłowe zawierające frameworka testowego Unity. W skład projektu podstawowego wchodzą trzy pliki:

  • unity.h
  • unity_internals.h
  • unity.c

Po dołączeniu plików testowych można przejść do przygotowania programu testowego.

  1. int main(void)
  2. {
  3.   //Start test
  4.   UnityBegin("test/TestProductionCode.c");
  5.   //Run test procedure
  6.   RUN_TEST(addFuntion_Test_CheckIfProperResponse);
  7.   RUN_TEST(multiplicationFunction_Test_CheckIfProperResponse);
  8.    
  9.   //Sum Up test resoult
  10.   return (UnityEnd());
  11. }

Można go oczywiście ustawić w definicjach preprocesora przez co będzie on używany tylko gdy programowi na to pozwolimy. Co pozwoli elastycznie wgrywać różne projekty na płytę z układem STM32.

W projekcie testowym sprawdzam poprawność działania prostych funkcji matematycznych jedna ma za zadanie dodać dwie liczy 8 bitowe, druga natomiast pomnożyć dwie wartości.

Przykładowe testy wyglądają następująco:

  1. void addFuntion_Test_CheckIfProperResponse(void)
  2. {
  3.   TEST_ASSERT_EQUAL(7, addFunction(2, 5));
  4.   TEST_ASSERT_EQUAL(13, addFunction(5, 8));
  5. }
  6.  
  7. void multiplicationFunction_Test_CheckIfProperResponse(void)
  8. {
  9.   TEST_ASSERT_EQUAL(4, multiplicationFunction(2, 2));
  10.   TEST_ASSERT_EQUAL(64, multiplicationFunction(8, 8));
  11. }

Powyższe Asserty sprawdzają czy wynik zwracany przez funkcję jest zgodny z oczekiwanymi wartościami. 

Dodatkowym elementem są funkcje setUp, tearDown które są wywoływane odpowiednio przed i po każdym z testów.

Poprawny wynik operacji zwróci na ekran konsoli programu STM32 ST-Link Utility następującą odpowiedź:


Wygenerowanie błędu w testach spowoduje wyświetlanie np. takiej informacji:


W podsumowaniu dostajemy informację ile testów nie przeszło pozytywnie, natomiast bezpośrednio w wiadomości wygenerowanej przez błąd w teście można odczytać jaka była oczekiwana wartość oraz jaka wartość była wymagana w celu uzyskania poprawnego testu.

Dodatkowym plusem przeprowadzania testów na docelowej maszynie (czyli bezpośrednio na mikrokontrolerze) jest możliwość sprawdzenia działania funkcji sprzętowych tzn. np. ustawienie pinów, można to zweryfikowane przez sprawdzenie stanu pinu zamiast polegać na odpowiedzi zwracanej przez funkcję. Ta sama sytuacja dotyczy komunikacja z wykorzystaniem peryferiów.

Dostępne makra testowe zostały zdefiniowane w pliku unity.h. Jest ich dosyć sporo, poniżej wypiszę tylko kilka z dostępnych testów:

  • TEST_ASSERT, TEST_ASSERT_TRUE - sprawdza czy podany warunek testowy jest wartością true. 
  • TEST_ASSERT_FALSE, TEST_ASSERT_UNLESS - sprawdza czy podany warunek testowy jest wartością true.
  • TEST_ASSERT_EQUAL_INT, TEST_ASSERT_EQUAL_UINT8 itp. - porównywanie wartości danego typu. 
  • TEST_ASSERT_BITS, TEST_ASSERT_BITS_HIGH itp. - porównywanie bitów w danej zmiennej.
  • TEST_ASSERT_GREATER_THEN, TEST_ASSERT_GREATER_UINT16 itp. - sprawdzanie czy dana wartość jest większa od podanej.
  • TEST_ASSERT_LESS_THEN, TEST_ASSERT_LESS_UINT16 itp. - sprawdzanie czy dana wartość jest mniejsza od podanej.
  • TEST_ASSERT_GREATER_OR_EQUAL, TEST_ASSERT_GREATER_OR_EQUAL_UINT16 itp. - sprawdzanie czy dana wartość jest większa lub równa wartości zadanej.


Dodatkowo są tam assercje do porównywania tablic, wartości zmiennoprzecinkowych itp. itd.

Printf:


W tej części chciałem opisać przekierowanie funkcji printf na debugger ST zamontowany na płycie Discovery w celu obsługi otrzymanych danych w programie STM32 ST-LINK Utility. Co pozwoli na debuggowanie systemu bez konieczności podłączania zewnętrznego UARTU.

Przesłanie pojedynczego znaku może zostać wysłać za pomocą funkcji ITM_SendChar zdefiniowanej w pliku core_cm4.h:

  1. __STATIC_INLINE uint32_t ITM_SendChar (uint32_t ch)
  2. {
  3.   if (((ITM->TCR & ITM_TCR_ITMENA_Msk) != 0UL) &&      /* ITM enabled */
  4.       ((ITM->TER & 1UL               ) != 0UL)   )     /* ITM Port #0 enabled */
  5.   {
  6.     while (ITM->PORT[0U].u32 == 0UL)
  7.     {
  8.       __NOP();
  9.     }
  10.     ITM->PORT[0U].u8 = (uint8_t)ch;
  11.   }
  12.   return (ch);
  13. }

Można ją używać bezpośrednio w celu przesłania pojedynczego znaku:

  1. ITM_SendChar('s');
  2. ITM_SendChar('x');

Samo przekierowanie funkcji printf opiera się na dodaniu następującej funkcji do pliku głównego:

  1. int _write(int file, char *ptr, int len)
  2. {
  3.   /* Implement your write code here, this is used by puts and printf for example */
  4.   int i=0;
  5.   for(i=0 ; i<len ; i++)
  6.   {
  7.       ITM_SendChar((*ptr++));
  8.   }
  9.   return len;
  10. }

Teraz można bezproblemów używać printf'a do wyświetlenia informacji przez programator w programie STM32 STLink Utility.

Projekt testowy można pobrać z dysku Google pod tym linkiem.