STM32Butterfly2: obsługa zewnętrznego RTC z I2C

W części realizowanych projektów wymagane jest znanie aktualnej daty i czasu. Zadanie to można zrealizować na trzy sposoby, pierwszym z nich jest użycie standardowych liczników mikrokontrolera. Główną wadą tego rozwiązania jest konieczność pracy mikrokontrolera przez cały czas. Drugim rozwiązaniem jest zastosowanie liczników dedykowanych do obliczania aktualnego czasu i daty, niestety nie wszystkie produkowane mikrokontrolery posiadają taki licznik. Należy tu wspomnieć, iż takie układy wyposażone w dedykowane liczniki posiadają także system podtrzymania działania licznika RTC. Mikrokontrolery rodziny STM32 są wyposażone w układ RTC, jednak w poniższym projekcie zostanie przedstawiona trzecia metoda. Polega ona na użyciu dodatkowego układu RTC, w naszym przypadku będzie to układ M41T56C64. Atutem jego jest wbudowany w niewielką obudowę rezonator kwarcowy oraz układ pamięci NVRAM i EEPROM.

 

Fot. 1. Wygląd modułu KAmodRTC

Fot. 1. Wygląd modułu KAmodRTC

 

Działanie programu polega na zapisaniu ustalonej w programie daty i czasu do układu RTC, a następnie cyklicznemu odczytywaniu tych danych i prezentacji na wyświetlaczu. Do działania wymagane jest podłączenie dodatkowych modułów: KAmodRTC (fotografia 1, schemat pokazano na rysunku 2) i KAmodLCD1 zgodnie z rysunkiem 3, połączenie z modułem RTC można dokonać przy użyciu przewodu CAB_HU04-30. Przedstawiona aplikacja została przygotowana dla płytek STM32Butterfly2 i STM32Butterfly. Jako, że używane elementy obu zestawów są w identycznej konfiguracji projekt nie wymaga żadnej ingerencji w przypadku zmiany stosowanej platformy.

 

Rys. 2. Schemat elektryczny modułu KAmodRTC

Rys. 2. Schemat elektryczny modułu KAmodRTC

 

Rys. 3. Schemat połączeń pomiędzy STM32Butterfly2 i modułami dodatkowymi

Rys. 3. Schemat połączeń pomiędzy STM32Butterfly2 i modułami dodatkowymi

 

Poniżej przedstawiono listing programu głównego.

 

Pierwszym działaniem w programie głównym jest skonfigurowanie licznika SysTick, realizuje to funkcja time_init(). Jej działanie sprowadza się do określenia częstotliwości występowania przerwania od licznika SysTick, ustawienia priorytetu przerwania oraz włączenia licznika.

Drugim działaniem jest wywołanie funkcji nlcd_init(), która opowiada za konfigurację wyświetlacza pochodzącego z telefonu Nokia 3310. Jej działanie polega na włączeniu zegarów dla wykorzystywanych portów GPIO i kontrolera SPI i skonfigurowaniu linii używanych w transmisji przez magistralę SPI oraz dodatkowych sterujących wyświetlaczem. Funkcja konfigurująca wyświetlacz po przeprowadzeniu konfiguracji linii wysyła cykl komend do wyświetlacza, które go inicjalizują.

Następnym działaniem jest konfiguracja kontrolera I2C. Odpowiada za to funkcja i2cm_init, która jako parametr przyjmuje częstotliwość z jaką transmitowane są dane. Poniżej przedstawiono listing funkcji.

 

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.

 

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.

Autor projektu: Lucjan Bryndza
Redakcja i przygotowanie: Mariusz Dziębowski

Do pobrania

Autor: