Metoda EEPROM::write() potrafi zapisać jedynie liczby prostej reprezentacji, typu int. Stąd nie można bezpośrednio zapisać w EEPROM liczby zmiennoprzecinkowej.
Jednak liczba zmiennoprzecinkowa charakteryzuje się swoim formatem, który w ostateczności i tak należy zapisać jako ciąg zer i jedynek... dokładnie tak, jak liczbę każdego innego typu. Żeby tego dokonać, trzeba dostać się do jej reprezentacji binarnej, czyli tego, jak jest ona zapisana w pamięci.
W języku C/C++ można tego dokonać bezpośrednio na przynajmniej dwa sposoby. Posłużyć się wskaźnikami i pewną nieskomplikowaną manipulacją w ich interpretacji, albo użyć unii*.
Żeby móc w ogóle próbować przedstawić typ float za pomocą typu int, musimy mieć pewność, że mają taką samą liczbę bitów. Zakładam, że autor unii zdawał sobie z tego sprawę i wziął pod uwagę możliwości docelowej platformy.
Kod konwertujący zapis bitowy liczby typu float do reprezentacji jako liczba całkowita z wykorzystaniem wskaźnika:
int float_to_int(float f)
{
/* Nie jestem pewny czy kompilator w Arduino IDE wspiera C++11.
Jeśli nie - wypadałoby zamiast static_assert użyć warunku,
działającego w czasie wykonania... lub nie sprawdzać poprawności.
*/
static_assert(sizeof(float) == sizeof(int));
float *p_f;
int *p_i;
int result;
p_f = &f;
p_i = reinterpret_cast<int*>( p_f );
result = *p_i;
return result;
}
float int_to_float(int i)
{
static_assert(sizeof(float) == sizeof(int));
float *p_f;
int *p_i;
float result;
p_i = &i;
p_f = reinterpret_cast<float*>( p_i );
result = *p_f;
return result;
}
Ciała funkcji są tak rozwleczone dla łatwiejszego zrozumienia.
Można to samo zrobić nieco inaczej - za pomocą unii.* Unia to struktura danych, która w danym czasie ma dostępną tylko jedną swoją składową, zaś jej rozmiar jest równy rozmiarowi jej największego elementu składowego. Reasumując - wszystkie dane przechowuje w jednym miejscu w pamięci. Jak wrzucimy do unii jedną składową, pozostałe stają się niepoprawne, unia je nadpisze.
W tym przypadku rozmiar unii txd powinien wynosić tyle samo, co rozmiar typu int oraz tyle samo, co rozmiar typu float. Zapisując do unii liczbę float, unia przechowuje tę liczbę jako float (jak można się było spodziewać). Jeśli jednak sięgniemy po składową int, to "unia zajrzy" w swoje jedyne dostępne miejsce w pamięci i wyjmie stamtąd to, co było przed chwilą wrzucone jako float. Z tym, że każemy jej wyjąć int, więc zobaczymy dokładnie to samo, co z pomocą przedstawionych przeze mnie wyżej funkcji konwersji. Mam nadzieję, że opisałem to dość zrozumiale.*
Nie wiem natomiast, dlaczego przesyła się do funkcji write() highByte() - to pewnie jest związane z fizyczną reprezentacją danej pamięci EEPROM.
A co się stanie jak nic nie będzie w pamięci EEPROM a my ją wczytamy ;)
EEPROM jest to pamięć stała - więc odczytamy coś, co już tam wcześniej ktoś zapisał. Albo zera. Albo śmieci. :)
* - jak słusznie zauważył adrian17, w C++ zaglądanie do "nieaktywnej" składowej unii jest zachowaniem nielegalnym, wedle dokumentacji to jest UB i nie powinno się tego robić