Kursy • Poradniki • Inspirujące DIY • Forum
Problem konwersji zmiennych
Pisząc programy na mikrokontrolery, w celu optymalizacji programu, decydujemy się na używanie zmiennych różnego typu. Przeważnie informacje zapisywane są na 1 lub 2 bajtach. Problem pojawia się najczęściej podczas komunikacji układu z otoczeniem.
Transmisja danych przeważnie odbywa się pojedynczymi bajtami. Jest to wymuszone przez stosowany format ramki danych lub sam interfejs komunikacyjny.
Dla przypomnienia!
Pojemności najpopularniejszych zmiennych:
- 1 Bajt (char, byte, uint8_t, int8_t), pojemność:
- od -128 do +127 (ze znakiem)
- 255 (bez znaku)
- 2 Bajty (int, integer, uint16_t, int16_t), pojemność:
- od -32 768 do +32 767 (ze znakiem)
- 65 535 (bez znaku)
Przesyłanie jednego bajta danych nie stanowi problemu, kopiujemy go do rejestru nadawczego i wysyłamy. Co w przypadku, gdy nasza zmienna zapisana jest na 2 bajtach? Wtedy konieczne jest podzielenie zmiennej na młodszy i starszy bajt.
Poniższy obrazek ilustruje podział liczby 5357 zapisanej binarnie na młodszy (L) i starszy (H) bajt:
Przykład praktyczny
Zmienne składające się z dwóch bajtów często używane są, gdy konieczne jest przesyłanie liczb ujemnych lub oczywiście, dużych wartości dodatnich. Programując moje LineFollowery musiałem przesyłać zmienne składające się z dwóch bajtów informujące o:
- aktualnych odczytach z enkoderów
- wartościach członów regulatora PD
- wartościach ADC podczas odczytu czujników
Rozwiązanie
Jeśli z konwersji korzystamy często najlepiej stworzyć dwie funkcje. Pierwsza z nich będzie dzieliła zmienną na dwa bajty, natomiast druga robiła operację odwrotną.
Na początek zajmijmy się funkcją dzielącą. Oczywiście całość można mocno skomplikować, ale skuteczne rozwiązanie wymaga od nas jedynie kilku linijek oraz znajomości logiki. Najwygodniejsza funkcja jaką możemy stworzyć pobiera liczbę składającą się z dwóch bajtów jako argument, a następnie zwraca wybrany bajt.
Szkielet funkcji:
uint8_t IntNaChar(uint16_t liczba, uint8_t ktory_bajt){
if (ktory_bajt == 'L') {
//Zwracamy młodszy bajt
} else if(ktory_bajt == 'H') {
//Zwracamy starszy bajt
}
return 1;
}
Pora na najważniejszą operację: podział zmiennej. Najłatwiej wyciągnąć starszy bajt. Do tego wystarczy przesunięcie bitowe o 8 w prawo. Każde przesunięcie w prawo dodaje nam zero z przodu liczby binarnej oraz "kasuje" najmłodszy bit. Jeśli operację wykonamy 8 razy, z liczby
0 0 0 1 0 1 0 0 1 1 1 0 1 1 0 1
otrzymamy:
0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 0
Pamiętajmy, że zera z przodu nic nie znaczą, więc tak naprawdę otrzymaliśmy liczbę 8 bitową:
0 0 0 1 0 1 0 0
Wyciągnięcie młodszego bajtu wymaga trochę sprytu. Początkującym może wydawać się, że odpowiednią operacją będzie przesunięcia w lewo, sprawdźmy!
0 0 0 1 0 1 0 0 1 1 1 0 1 1 0 1
Po przesunięciu:
1 1 1 0 1 1 0 1 0 0 0 0 0 0 0 0
Liczba, którą otrzymaliśmy zapisana jest tylko na starszym bajcie, ale nadal jest to zmienna 2 bajtowa!
Aby wyciągnąć młodszy bajt konieczny jest iloczyn logiczny. Dla przypomnienia funkcja ta zwraca prawdę (jeden), jeśli na wszystkich porównywanych bajtach znajdzie się 1. W naszym przykładzie:
0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
0 0 0 1 0 1 0 0 1 1 1 0 1 1 0 1
---------------------------------------
& 1 1 1 0 1 1 0 1
Dokładnie o to nam chodziło! Teraz dla wygody, zamieniamy 8 jedynek na postać binarną 0xFF:
uint8_t IntNaChar(uint16_t liczba, uint8_t ktory_bajt){
if (ktory_bajt == 'L') {
return (liczba & 0xFF); //Zwracamy młodszy bajt
} else if(ktory_bajt == 'H') {
return (liczba >> 8); //Zwracamy starszy bajt
}
return 1;
}
W ten sposób otrzymaliśmy gotową funkcję, która zwróci wybrany przez nas bajt. Funkcja wykonująca połączenie dwóch zmiennych w jedną jest prostsza i na dobrą sprawę wykonuję, to co powyższa, ale w odwrotnej kolejności:
uint16_t CharNaInt(uint8_t LByte, uint8_t HByte) {
return ((HByte << 8) + LByte);
}
Przykład w moim programie widoczny jest poniżej. Wysłanie wzmocnienia członu P do kontrolera:
dane[0] = IntNaChar(kp, 'L');
dane[1] = IntNaChar(kp, 'H');
[...]
USART_SendFrame(130, &dane, 7);
Odebranie danych odesłanych przez kontroler:
kp = CharNaInt(daneOdebrane[0], daneOdebrane[1]);
Podsumowanie
Mam nadzieję, że rozwiązanie opisałem w sposób zrozumiały nawet dla początkujących. Zasada działania jest identyczna dla dowolnego języka programowania. Jeśli uważasz, że znasz lepszy sposób na rozwiązanie tego problemu - opisz go w komentarzu!
To nie koniec, sprawdź również
Przeczytaj powiązane artykuły oraz aktualnie popularne wpisy lub losuj inny artykuł »
binarny, młodszy, starszy, system
Trwa ładowanie komentarzy...