STM32Butterfly2: obsługa zewnętrznego RTC z I2C
Pierwszym działaniem jest włączenie zegara dla portu GPIO oraz kontrolera I2C, proces ten realizowany jest przez ustawienie odpowiedniego bitu rejestru APB1EN i APB2ENR. Następnym działaniem jest skonfigurowanie linii SDA i SCL oraz podciągnięcie ich do potencjału dodatniego. Proces konfiguracji realizuje funkcja io_config, która przyjmuje jako pierwszy parametr port, jako drugi numer konfigurowanej linii, natomiast trzeci i czwarty parametr określają tryb działania linii. W tym przypadku linie są konfigurowane jako wyjściowe, działające z prędkością 50MHz, w funkcji alternatywnej z otwartym drenem. Natomiast podciągnięcie do VCC realizuje funkcja io_set, która ustawia linię ,wskazaną przez parametr drugi, portu określonego przez pierwszy parametr funkcji.
W następnym kroku realizowane jest włączenie kontrolera I2C oraz jego zresetowanie. Proces ten odbywa się poprzez ustawianie i zerowanie bitów rejestrów, które określają działanie kontrolera.
Kolejnym działaniem jest określenie częstotliwości taktowania kontrolera, zmienna ta określana jest w pięciu najmłodszych bitach rejestru CR2 kontrolera I2C. W celu zachowania bez zmian pozostałych bitów proces ten realizowany jest poprzez odczyt wartości rejestru i działanie na kopii. Po wprowadzeniu zmian w kopii zapisywana jest ona do rejestru.
Następnie ustawiana jest częstotliwość wysyłania danych, proces ten zależy od większej liczby czynników i jest bardziej skomplikowany. W związku z tym realizuje to odrębna funkcja i2cm_set_speed, która jako parametr przyjmuje prędkość z jaką mają być wysyłane dane.
Analogicznie dla konfigurowania częstotliwości z jaką działa kontroler odbywa się konfiguracja rejestru CR1 kontrolera I2C – działanie na kopii w celu zachowania wartości pozostałych bitów. W ramach modyfikacji tego rejestru ustawiany jest tryb działania. Następnie konfigurowany jest adres dostępu oraz zerowane są rejestry SR1 i SR2, które informują o stanie kontrolera.
Ostatnim elementem wykonywanym w ramach funkcji jest skonfigurowanie priorytetu dwóch przerwań wywoływanych przez kontroler I2C i ich włączenie. Działanie funkcji kończone jest przez zwrócenie wartości 0 informującej o bezbłędnemu przeprowadzeniu konfiguracji kontrolera.
Następnym dzianiem w ramach programu głównego jest wyświetlenie na wyświetlaczu komunikatu powitalnego. Realizuje to funkcja nlcd_put_string, która jako pierwszy parametr przyjmuje ciąg znaków do wyświetlenia, natomiast pozostałe dwa parametry określają położnie napisu.
Po wyświetleniu napisu następuje zdefiniowanie struktury składającej się dziewięciu elementów. Pierwszy element struktury opisuje numer rejestru, od którego będzie odbywać się zapis do układu RTC. Natomiast pozostałe elementy struktury zawierają wartości kolejnych rejestrów, przy czym pierwsze siedem opisuje wprowadzany czas i datę. W następnych dwóch linijkach mamy zdefiniowane dwie zmienne: pomocniczą sw_addr oraz tablicę buf. Pierwsza z nich która służy w czasie odbierania danych do wybrania numeru rejestru, od którego zaczyna się transmisja. Natomiast tablica jest używana jako bufor odbieranych danych.
W tym momencie mamy realizowany zapis aktualnego czasu do układu RTC. Jest on realizowany przy użyciu funkcji buf. Do poprawnego działania wymaga podania pięciu parametrów: pierwszy z nich określa adres docelowy urządzenia, drugi jest wskaźnikiem na bufor danych do wysłania, trzeci określa ilość danych do wysłania, czwarty jest buforem danych odbieranych, ostatni parametr określa ilość danych do odebrania. W przypadku braku potrzeby odbierania danych w pozycji bufora zapisywana jest wartość NULL, a ilość danych przyjmuje wartość 0.
Funkcja odpowiedzialna za odbiór wysyłanie danych po magistrali I2C zwraca wartość, która informuje o ewentualnym błędzie. W przypadku jego wystąpienia zwracana jest wartość poniżej zera, która sprawia, iż program przechodzi do wyświetlenia na wyświetlaczu informacji o błędzie i przechodzi do nieskończonej pętli for.
Jeśli podczas inicjalizacji daty i czasu nie wystąpił błąd program rozpoczyna nieskończoną pętlę for w ramach, której aktualizowane są dane i następuje ich wyświetlenie na wyświetlaczu. Pobranie nowej daty i czasu realizowane jest przez funkcję i2cm_transfer_7bit, która została już przedstawiona powyżej. W przypadku wystąpienia błędu przy odbiorze następuje wyświetlenie informacji o błędzie i zatrzymanie działania programu. W przypadku bezbłędnego odbioru danych program przy użyciu funkcji print_date wyświetla aktualny czas i datę na wyświetlaczu. Jako parametr przyjmuje ona bufor odebranych danych. Poniżej został przedstawiony listing funkcji.
static void print_date(const uint8_t *rtc) { char buf[24]; //Display hour tiny_snprintf( buf,sizeof(buf),"%02x:%02x:%02x", rtc[RTC_HOUR_CENTURY_REG] & RTC_HOUR_MASK, rtc[RTC_MIN_REG]& RTC_MIN_MASK, rtc[RTC_SEC_REG] & RTC_SEC_MASK ); nlcd_put_string(buf,0,1); //Display date tiny_snprintf( buf,sizeof(buf),"%02x-%02x-%02x", rtc[RTC_DATE_REG] & RTC_DATE_MASK, rtc[RTC_MONTH_REG] & RTC_MONTH_MASK, rtc[RTC_YEAR_REG] ); nlcd_put_string(buf,0,2); }
W pierwszym kroku definiowany jest bufor o długości 24 bajtów. Następnie wywoływana jest funkcja tiny_snprintf, której zadaniem jest odpowiednie sformatowanie danych liczbowych do postaci ciągu znaków. Pierwszym parametrem jest bufor, do którego nastąpi zapis ciągu, drugi parametr określa długość podanego bufora, następnie podawany jest sformatowany ciąg, ostatnim elementem są zmienne. W naszym przypadku przedstawiają one aktualny czas. Po utworzeniu sformatowanego ciągu znaków następuje wyświetlenia go na wyświetlaczu. Realizuje to funkcja nlcd_put_string, która przyjmuje trzy parametry: pierwszy jest ciągiem znaków, natomiast drugi i trzeci określają położenie wyświetlanego tekstu. Analogiczne postępowanie realizowane jest przy działaniu z datą.
Na końcu pętli przy użyciu funkcji wait_ms realizowane jest opóźnienie. W ramach działania funkcja wykorzystuje licznik SysTick. Jak można zauważyć w programie głównym brakuje fragmentu odpowiedzialnego za inicjalizację układu zegarów mikrokontrolera oraz przygotowanie go do pracy. Wynika to z faktu, że działania te realizowane są w ramach obsługi przerwania reset_handler, wywoływanego jako pierwsze po uruchomieniu mikrokontrolera.