Budowa i wykorzystanie statycznej biblioteki.

Tu możesz pisać o swoich problemach z pisaniem programów w języku C dla AVR.
Awatar użytkownika
Antystatyczny
Geek
Geek
Posty: 1123
Rejestracja: czwartek 03 wrz 2015, 22:02

Budowa i wykorzystanie statycznej biblioteki.

Postautor: Antystatyczny » niedziela 04 wrz 2016, 06:10

Cześć!

Ostatnio wspomniałem co nieco o porządkowaniu "libsów" będących na ogół zestawem plików źródłowych i nagłówkowych. Umieściłem "libsy" w jednym wspólnym folderze, a następnie poinformowałem Eclipse o istnieniu takowego folderu. Więcej szczegółów tutaj:

viewtopic.php?f=29&t=554

Dzisiaj skupię się nad stworzeniu, dołączeniu oraz wykorzystaniu statycznie dołączanej biblioteki o zamkniętym kodzie źródłowym. No dobra, ale co to właściwie jest statycznie dołączana biblioteka? Z grubsza można powiedzieć, że jest to przetłumaczony na język maszynowy kod, który potem można dołączać do różnych innych programów. Na potrzeby tego artykułu przygotuję kawałeczek kodu źródłowego, który następnie przekształcę do postaci statycznej biblioteki (tak, taką właśnie nosi nazwę), no i na koniec z niej skorzystam. Najpierw jednak pokażę, jak użyć statycznej biblioteki matematycznej, którą każdy z nas ma na twardym dysku. U mnie znajduje się ona na końcu takiej ścieżki:

C:\Program Files (x86)\Atmel\AVR Tools\AVR Toolchain\avr\lib\libm.a

Jak widać jest ona umiejscowiona w folderze zawierającym toolchain (zestaw, a właściwie łańcuch narzędzi), który jest niezbędny do budowania oprogramowania dla mikrokontrolerów AVR. Po czym rozpoznać bibliotekę statyczną? Jej nazwa rozpoczyna się przedrostkiem lib (od library – biblioteka), a rozszerzenie nazwy to .a (od archive – archiwum). Wygląda więc na to, że właściwa nazwa biblioteki matematycznej składa się z pojedynczej litery, czyli „m”. Zwracam na to uwagę, ponieważ ma to znaczenie podczas wskazywania linkerowi nazwy pliku, który będzie miał statycznie dołączyć do kodu. Również podczas tworzenia biblioteki ma to znaczenie. Jeśli, przykładowo, stworzę sobie jakąś bibliotekę i nazwę ją libmojabiblioteka, to po jej zbudowaniu na dysku znajdę plik o nazwie liblibmojabiblioteka.a. Reasumując: Podczas tworzenia biblioteki oraz wskazywania jej linkerowi zapominamy o przedrostku lib i wpisujemy jedynie nazwę właściwą biblioteki. No dobra, naklepałem literek, a jeszcze nie przekazałem żadnych konkretów. Za moment stworzę prosty projekt jednoplikowy i pokażę, jak użyć biblioteki matematycznej. Zaraz wracam…

Niecierpliwy_programista.JPG



Już jestem, już… ;) Stworzyłem mały programik, by przetestować działanie funkcji konwertującej wartość zmiennej double do postaci tekstowej. Deklaracja tej funkcji znajduje się w stdlib.h. Z jej dokumentacji wynika, że do poprawnego działania funkcji „dtostrf” konieczne jest dołączenie biblioteki matematycznej (czyli libm.a). Na moje nieszczęście program kompiluje się niezależnie od tego, czy dołączę tę bibliotekę, czy nie… i nie potrafię tego wyjaśnić. Tak czy siak postąpię zgodnie z dokumentacją i ją dołączę. W tym celu otwieram okno właściwości projektu, a w nim dodaję nazwę dołączanej biblioteki (m) oraz ścieżkę do niej. Całość wygląda następująco:

link_library.JPG


Cała operacja okazała się banalna i od tej pory mam jawnie dołączoną bibliotekę matematyczną do projektu.

A teraz mój programik testowy:

Kod: Zaznacz cały

#include<avr/io.h>
#include<stdbool.h>
#include<stdlib.h>
#include<math.h>

/* Tablica, w której zostanie zapisana tekstowa postać zmiennej var. Rozmiar tablicy
 * ustaliłem "na oko". */
char str[20];

int main(void)
{
   /* Zmienna, którą chcę przekształcić na postać tekstową, by np. wysłać ją przez uart.
    * Inicjalizuję ją wartością sinusa  33 radianów. */
   double var = sin(33);

   /* Wywołuję funkcję konwertującą double do postaci tekstu.
    * Pierwszy parametr to zmienna var.
    *
    * Drugi parametr to ilość cyfr/tekstu z lewej strony kropki dziesiętnej.
    * Należy uwzględnić znak zmiennej (plus lub minus) oraz kropkę dziesiętną.
    *
    * Trzeci parametr to precyzja, czyli ilość znaków po prawej stronie kropki dziesiętnej.
    *
    * Czwarty parametr to wskaźnik na bufor docelowy, w którym zostanie zapisana tekstowa
    * postać wartości zmiennej var. */
   dtostrf(var, 3, 5, str);

   while(true);
}


Kompilacja, wgranie do mikrokontrolera… Efekt działania programu: BRAK!

Zapomniałem dołączyć obsługę uartu do projektu. Na szczęście to tylko chwilka roboty. Do projektu dołączam ścieżki do folderu z moimi „libsami”. Drobna korekta softu:

Kod: Zaznacz cały

#include<avr/io.h>
#include<avr/interrupt.h>
#include<stdbool.h>
#include<stdlib.h>
#include<math.h>
#include<uart/uart.h>

/* Tablica, w której zostanie zapisana tekstowa postać zmiennej var. Rozmiar tablicy
 * ustaliłem "na oko". */
char str[20];

int main(void)
{
   /* Inicjalizuję uart, by wynik konwersji zobaczyć na ekranie terminala. */
   uart_init(0, 9600);
   sei();

   /* Zmienna, którą chcę przekształcić na postać tekstową, by np. wysłać ją przez uart.
    * Inicjalizuję ją wartością sinusa  33 radianów. */
   double var = sin(33);

   /* Wywołuję funkcję konwertującą double do postaci tekstu.
    * Pierwszy parametr to zmienna var.
    *
    * Drugi parametr to ilość cyfr/tekstu z lewej strony kropki dziesiętnej.
    * Należy uwzględnić znak zmiennej (plus lub minus) oraz kropkę dziesiętną.
    *
    * Trzeci parametr to precyzja, czyli ilość znaków po prawej stronie kropki dziesiętnej.
    *
    * Czwarty parametr to wskaźnik na bufor docelowy, w którym zostanie zapisana tekstowa
    * postać wartości zmiennej var. */
   dtostrf(var, 3, 5, str);

   /* Wysyłam na uart wynik konwersji. */
   uart_puts(0, str);

   while(true);
}



Tym razem efekt działania jest od razu widoczny:
Zrzut ekranu 2016-09-04 05.53.27.png


Ok, dołączenie libm.a okazało się proste. Czas stworzyć jakąś swoją bibliotekę statycznie linkowaną. Naszła mnie jednak chwila refleksji… Do czego właściwie jest mi potrzebna taka biblioteka? Przecież jej zbudowanie i późniejsze linkowanie to dodatkowy nakład pracy!
W mojej ocenie statycznie linkowana biblioteka ma następujące wady i zalety:

Wady:

1. Dodatkowy nakład pracy poniesiony podczas tworzenia biblioteki, a w późniejszym czasie jej ręczne linkowanie.
2. Każda modyfikacja biblioteki wymaga edycji jej źródeł i ponownej kompilacji.
3. Biblioteka niekoniecznie jest przenośna. W przypadku konieczności przeniesienia jej na inną rodzinę mikrokontrolerów, wymagana będzie jej ponowna kompilacja w nowym środowisku.

Zalety:

1. Biblioteka jest już skompilowana, więc czas budowania całej aplikacji ulega skróceniu.
2. Bibliotekę można udostępnić osobom trzecim bez konieczności dodawania kodu źródłowego.
Oznacza to dla osoby trzeciej spore problemy w dokładnym ustaleniu sposobu, w jaki działa ta biblioteka.
3. Trzecią zaletę niech sobie każdy sam wpisze. Jestem pewien, że każdy jakąś znajdzie ;)

Znowu niepotrzebnie się rozgadałem, a czas leci… Dziś zbuduję konwerter temperatur!

Założenia:

Ma przeliczać stopnie Celsjusza na stopnie Fahrenheita i odwrotnie. Tylko tyle...albo aż tyle.

Przede wszystkim przygotuję kod w tradycyjny sposób, przetestuję jego działanie, a gdy okaże się, że konwersja działa poprawnie w obie strony, przeniosę kod do nowego projektu, który będzie mi tworzył wyłącznie bibliotekę.

...kilkadziesiąt minut później…

Program jest gotowy i przetestowany, a efekt jego pracy widzę w oknie terminala:

Zrzut ekranu 2016-09-04 05.49.04.png


Tutaj wrzucę jedynie zawartość main.c i króciutko wyjaśnię, o co chodzi.

Kod: Zaznacz cały


#include<stdbool.h>
#include<stdlib.h>
#include<math.h>
#include<avr/io.h>
#include<avr/interrupt.h>
#include<util/delay.h>
#include<uart/uart.h>
#include"tempcvt/tempcvt.h"

char TmpBuffer[10];/* Buffer for double to string conversion */

#if CONVERSION_PRECISION == 1
int Modifier = 1;
#elif CONVERSION_PRECISION == 2
float Modifier = 0.5;
#elif CONVERSION_PRECISION == 3
double Modifier = 0.25;
#endif

int main(void)
{
   CONVERSION_DATA_TYPE Temp;

   uart_init(0, 57600);
   sei();

   uart_puts(0, "Temperature converter test.\r\n");

   while(true)
   {
      /* Celsius to Fahrenheit test. */
      for(Temp = -100; Temp < 101; Temp += Modifier)
      {
#if CONVERSION_PRECISION == 1
         uart_put_int(0, Temp, 10);
         uart_puts(0, "C = ");
         uart_put_int(0, GetFahrenheit(Temp), 10);
         uart_puts(0, "F\r\n");
#else
         uart_puts(0, dtostrf(Temp, 5, 3, TmpBuffer));
         uart_puts(0, "C = ");
         uart_puts(0, dtostrf(GetFahrenheit(Temp), 5, 3, TmpBuffer));
         uart_puts(0, "F\r\n");
#endif
      }

      /* Fahrenheit to Celsius test. */
      for(Temp = -148; Temp < 214; Temp += Modifier)
      {
#if CONVERSION_PRECISION == 1
         uart_put_int(0, Temp, 10);
         uart_puts(0, "F = ");
         uart_put_int(0, GetCelsius(Temp), 10);
         uart_puts(0, "C\r\n");
#else
         uart_puts(0, dtostrf(Temp, 5, 3, TmpBuffer));
         uart_puts(0, "F = ");
         uart_puts(0, dtostrf(GetCelsius(Temp), 5, 3, TmpBuffer));
         uart_puts(0, "C\r\n");
#endif
      }
      _delay_ms(10000);
   }
}




Ot, zwyczajny programik… Widać inkludy, zmienną tablicową, w której przechowywana jest postać tekstowa zmiennej Temp oraz zmienną Modifier, której wartość zależy od precyzji określonej w pliku tempcvt.h. Tak sobie wymyśliłem, że zależnie od wybranej precyzji funkcje konwertujące będą operowały na typach int, float lub double. Precyzję konwersji określa makro CONVERSION_PRECISION. Możliwe ustawienia to 1, 2 lub 3. W funkcji main widać zwyczajną inicjalizację uartu, włączenie przerwań, a w pętli głównej dwie pętelki „for”, których ilość iteracji zależna jest od wartości zmiennej Modifier. Jedna z pętli konwertuje stopnie Celsjusza na Fahrenheita, a druga w przeciwną stronę. Każdy wynik konwersji wysyłany jest w postaci tekstowej na uart.

Przede mną ostatni etap podróży, czyli przeniesienie plików tempcvt.c oraz tempcvt.h i stworzenie z nich statycznie linkowanej biblioteki. Do roboty. Stworzę nowy projekt, ale tym razem nie zaznaczę, że ma to być „cross target application”, a „cross target static library”. Kopiuję z poprzedniego projektu tempcvt.c oraz tempcvt.h, a następnie poddam kompilacji. Ok, kompilacja przeszła bez problemu, a w folderze archives pojawił się plik o nazwie libtempcvt.a. Jestem coraz bliżej!

Pozostaje mi teraz stworzyć ostatni projekt oraz podlinkować do niego moją pierwszą statyczną bibliotekę. Najpierw jednak przekopiuję ją do folderu, w którym trzymam swoje libsy. Muszę pamiętać, by skopiować również plik nagłówkowy, bo na podstawie deklaracji w nim zawartych linker będzie szukał ich definicji. Znajdzie je w plikach źródłowych lub w bibliotekach. A będzie ich szukał, jeśli choć raz którejś z tych funkcji użyję w programie. Oczywiście użyję obu ;)

Czas na ostatni projekt. Zakładam, że funkcja main nie ulegnie zmianie. Zmianie ulegnie jedynie sposób inkludowania mojej biblioteki. No i miałem rację. Dołączyłem „libs” uartu oraz pełnoprawną bibliotekę libtempcvt.a do projektu i wszystko pięknie się skompilowało. Program nadal działa a ja cieszę się jak dziecko, bo znowu coś mi się udało ;)

Ok, na koniec objaśnienie projektów zawartych w workspace, które będzie za moment do ściągnięcia:

M_TEST0 - Test funkcji sin() oraz dtostrf().
M_TEST1 - Test funkcji sin() oraz dtostrf(). Wynik wyświetlany w oknie terminala.
M_TEST2 - Test funkcji sin() oraz dtostrf(). Dołączona biblioteka libm.a. Wynik wyświetlany w oknie terminala.
TEMP_CVT_BASIC - Podstawowy program konwersji temperatur.
temp_cvt - Projekt statycznej biblioteki.
TEMP_CVT_LIBRARY - Program wykorzystujący bibliotekę libtempcvt.a

Libs do uartu, wykorzystany w tych projektach, można ściągnąć stąd: viewtopic.php?f=23&t=198

Workspace z wszystkimi dzisiejszymi projektami:

AVR8_static_library.7z


To chyba na tyle... Pozdrawiam!
Nie masz wymaganych uprawnień, aby zobaczyć pliki załączone do tego posta.
"The true sign of intelligence is not knowledge but imagination" Albert Einstein.

Wróć do „Programowanie AVR w C”

Kto jest online

Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 1 gość