wersja mobilna | kontakt z nami

STM32 - sprzętowe sterowanie multipleksowanego wyświetlacza LED

Numer: Styczeń/2018

Multipleksowane wyświetlacze 7-segmentowe należą do najbardziej popularnych elementów urządzeń mikroprocesorowych służących do komunikacji z użytkownikiem. Istnieje wiele sposobów przyłączania takich wyświetlaczy do mikrokontrolera. Może do tego posłużyć np. gotowy układ sterownika wyświetlacza, dołączony do interfejsu SPI lub I2C mikrokontrolera. Najtańszym sposobem jest sterowanie wyświetlacza przez mikrokontroler. Zaletą takiego rozwiązania, oprócz niskiego kosztu, jest również możliwość uzyskania efektów optycznych niedostępnych w standardowych układach sterowników, w tym płynnej zmiany jasności stosownie do natężenia oświetlenia zewnętrznego oraz ?miękkiej? zmiany zawartości wyświetlacza.

Pobierz PDFMateriały dodatkowe

W zależności od wymaganej jasności świecenia i natężenia prądów sterujących wyświetlacz, może być niezbędne użycie układów wzmacniających w postaci kluczy tranzystorowych bipolarnych lub MOSFET albo specjalizowanych układów scalonych. Zazwyczaj stosuje się klucze dla wspólnych elektrod cyfr. Jeśli wymagane natężenie prądu poszczególnych segmentów przekracza dopuszczalną obciążalność prądową wyjść mikrokontrolera, należy również zastosować wzmacniacze segmentów.

Wyświetlanie multipleksowane zazwyczaj polega na kolejnym, naprzemiennym wyświetlaniu poszczególnych cyfr z taką częstotliwością, aby obserwator nie zauważył migotania. Poprawnie zaprojektowane wyświetlacze są odświeżane z częstotliwością od ok. 120 Hz do 2 kHz. Większa częstotliwość odświeżania ogranicza niepożądane efekty wizualne, zwłaszcza, gdy obserwator przemieszcza się względem wyświetlacza. Zbyt mała częstotliwość odświeżania powoduje, że obserwator ma wrażenie falowania lub drżenia obrazu. Jeżeli dopuszczamy możliwość ruchu obserwatora względem wyświetlacza, częstotliwość odświeżania nie powinna być mniejsza od 400 Hz. Górna wartość częstotliwości odświeżania jest uwarunkowana parametrami czasowymi elementów przełączających oraz – przy programowym sterowaniu wyświetlaniem – zajętością czasu procesora.

Zasady programowego sterowania wyświetlacza

Programowa obsługa odświeżania wyświetlacza jest powszechnie znana i stosowana, jednak warto zwrócić uwagę na zasady jej poprawnej realizacji.

W celu utrzymania stałej i jednakowej jasności cyfr przełączanie cyfr musi zachodzić w stałych odstępach czasu, wyznaczanych przez sprzętowy timer dostępny w mikrokontrolerze. W typowych rozwiązaniach zmianami sterowania wyświetlacza zajmuje się fragment programu stanowiący część obsługi przerwania timera. Należy zauważyć, że wymagana częstotliwość przerwań timera jest iloczynem częstotliwości odświeżania wyświetlacza i liczby cyfr. Proste odświeżanie 4-cyfrowego wyświetlacza z częstotliwością 400 Hz wymaga zgłaszania przerwań z częstotliwością 1600 Hz.

Zawartość wyświetlacza powinna być przygotowana przez oprogramowanie i przechowywana w wektorze danych, którego poszczególne elementy odpowiadają wyświetlanym cyfrom. Informacja ta może mieć postać obrazu bitowego segmentów poszczególnych cyfr lub stanów wszystkich sygnałów sterujących wyświetlaczem (sterowania cyfr i segmentów). W ten sposób unikamy zbędnego przekodowania symboli na ich obrazy przy każdym wyświetleniu cyfry.

Przy odświeżaniu programowym linie sterujące segmentami i cyframi nie muszą należeć do jednego portu GPIO, chociaż takie rozwiązanie jest najwygodniejsze do obsługi programowej. Jeżeli sterowania segmentów nie są zgrupowane w jednym porcie, wyświetlany obraz może być przechowywany w postaci wektora struktur, których poszczególne pola zawierają stany wyjść portów, z których są wyprowadzone sygnały sterujące.

Podczas zmiany wyświetlanej cyfry musimy zadbać o zachowanie właściwych zależności czasowych pomiędzy sygnałami sterującymi. Jeżeli wszystkie sygnały sterujące wyświetlaczem nie pochodzą z jednego portu lub nie jest możliwa równoczesna zmiana stanu wszystkich sygnałów przez pojedynczy zapis danej do portu, należy zwrócić uwagę na poprawną kolejność poszczególnych zmian. W takim przypadku oprogramowanie realizujące odświeżanie musi wykonać kolejno trzy czynności:

  • wyłączenie bieżącej cyfry,
  • ustawienie obrazu nowej cyfry na wyjściach sterujących segmentami,
  • włączenie nowej cyfry.

Częstym błędem popełnianym przez początkujących programistów jest pominięcie pierwszej z tych czynności lub niewłaściwa kolejność czynności. Skutkuje to błędnym wyświetlaniem „cienia” cyfry na sąsiedniej pozycji wyświetlacza.

Poszczególne czynności związane z wysterowaniem wyświetlacza wymagają selektywnej zmiany stanu wyjść, co w prostszych mikrokontrolerach realizuje się poprzez operacje logiczne na rejestrach wyjściowych portów. W przypadku rodziny STM32 można i należy użyć mechanizmu modyfikacji wybranych linii portów, dostępnego poprzez rejestry BSRR i BRR portów GPIO. W ten sposób możemy dowolnie zmienić stan grupy wyjść pojedynczą operacją zapisu bez narażania się na problemy wynikające z operacji logicznych na portach.

Przy takim rozwiązaniu sterowania wyświetlaczem wygodnie jest, gdy oprogramowanie przygotowuje dane do wyświetlenia w postaci wartości przesyłanych do rejestru BSRR. Dane odpowiadające poszczególnym cyfrom są umieszczone w wektorze, którego zawartość jest cyklicznie przesyłana do rejestru BSRR portu używanego do sterowania wyświetlaczem.

Przykładowy program

Do zademonstrowania technik sterowania wyświetlacza posłuży prosty program odliczający czas w sekundach i wyświetlający go w postaci liczby 4-cyfrowej na multipleksowanym wyświetlaczu LED. Program został napisany w dwóch wersjach, różniących się sposobem realizacji odświeżania.

Uruchomiony go na płytce L476RG-Nucleo, zawierającej mikrokontroler STM32L476RGT. Do płytki dołączono moduł KA-NUCLEO-MULTISENSOR zawierający m.in. multipleksowany, 4-cyfrowy wyświetlacz LED. Ze względu na planowane użycie modułu w warunkach laboratoryjnych i wynikający stąd brak wymagania na dużą jasność, wyświetlacz jest sterowany z wyjść mikrokontrolera, bez użycia wzmacniaczy segmentów ani kluczy tranzystorowych cyfr. Wspólne elektrody cyfr są sterowane z linii portów mikrokontrolera bezpośrednio, a segmenty – poprzez rezystory ograniczające natężenie prądu do wartości ok. 5 mA. Wyświetlacz podłączono w taki sposób, że wszystkie linie sterujące nim należą do portu GPIOC; linie PC0…PC7 sterują świeceniem segmentów, a PC8…PC11 – wyborem cyfr.

Każda wersja programu składa się z dwóch plików źródłowych. Korzystają one z kilku plików nagłówkowych. Plik 7seg_common.c, wspólny dla obu wersji zawiera dwie procedury. Pierwsza z nich, common_init() jest wywoływana przy starcie i służy do inicjowania zegara mikrokontrolera i portu sterującego wyświetlaczem. Druga, run_every_10ms() ma za zadanie aktualizację wyświetlanego czasu i reagowanie na naciśnięcie przycisku. Zgodnie z nazwą powinna ona być wywoływana co 10 ms przez procedurę obsługi przerwania timera.

Pliki 7seg-dma.c-soft.c7seg-dma.c zawierają części oprogramowania specyficzne dla obu wersji projektu. Zostały one opisane w dalszej części artykułu.

Plik board.h zawiera definicje zasobów płytki, w tym linii portów, do których jest podłączony wyświetlacz. Zastosowany w pliku sposób zdefiniowania sterowań wyświetlacza umożliwia użycie tego samego kodu źródłowego niezależnie od polaryzacji sygnałów sterujących segmentów i cyfr. Ponadto, w skład projektu wchodzą trzy własne pliki zawierające definicje przydatne przy programowaniu mikrokontrolerów rodziny STM32. Stanowią one uzupełnienie pliku definicji zasobów mikrokontrolera dostarczonego przez producenta. Oba projekty są dostępne w pliku ep_l476_7seg.zip w materiałach dodatkowych do projektu.

Inicjowanie mikrokontrolera

Mikrokontroler STM32L476 może być taktowany z jednego z kilku dostępnych źródeł przebiegu zegarowego, w tym trzech wewnętrznych generatorów RC o niskiej dokładności oraz generatorów wewnętrznych lub zewnętrznych synchronizowanych rezonatorami kwarcowymi. Ciekawą i rzadko spotykaną cechą dostępną w L476 jest możliwość synchronizacji wewnętrznego generatora RC o dużej częstotliwości przez generator używający „zegarkowego” rezonatora kwarcowego 32768 Hz. Taki właśnie sposób generowania przebiegu zegarowego wybrano dla projektów przykładowych. Jest to jedyny dostępny sposób na uzyskanie dokładnej częstotliwości roboczej mikrokontrolera na płytce Nucleo bez konieczności modyfikacji sprzętowej konfiguracji płytki.

Domyślnym źródłem przebiegu zegarowego aktywnym po zainicjowaniu mikrokontrolera jest generator wewnętrzny MSI o częstotliwości około 4 MHz. W celu uzyskania maksymalnej dozwolonej częstotliwości taktowania równej 80 MHz i synchronizacji generatora częstotliwością 32768 Hz należy kolejno:

  • włączyć moduł PWR poprzez zapis do rejestru RCC ’ AHB2ENR i odblokować możliwość zapisu rejestru RCC ’ BDCR,
  • włączyć generator LSE 32 kHz i poczekać na jego uruchomienie,
  • włączyć synchronizację generatora MSI przebiegiem z generatora LSE,
  • skonfigurować parametry głównej pętli PLL dla uzyskania częstotliwości 80 MHz z częstotliwości wejściowej 4 MHz, pochodzącej z MSI,
  • skonfigurować parametry dostępu do pamięci Flash odpowiednie dla docelowej częstotliwości pracy,
  • poczekać na synchronizację PLL i włączyć taktowanie mikrokontrolera z PLL.

Wymienione powyżej czynności są wykonywane przez procedurę common_setup() z pliku 7seg-common.c.

Ponieważ maksymalne dozwolone częstotliwości pracy wszystkich szyn wewnętrznych mikrokontrolera L476 wynoszą 80 MHz, nie ma potrzeby konfigurowania dzielników częstotliwości poszczególnych szyn – wszystkie peryferiale, w tym timery będą taktowane częstotliwością 80 MHz.

Obsługa wyświetlacza w przerwaniu timera

Pierwsza wersja programu, zawarta w projekcie 7seg-soft (listing 1), obsługuje wyświetlacz programowo, w przerwaniu timera. Aby ograniczyć liczbę źródeł przerwań, w wersji tej użyto jednego przerwania timera zarówno do obsługi wyświetlania, jak i do odliczania czasu.

Konfiguracja timera

Do obsługi wyświetlacza użyto timera TIM4. Okres timera odpowiada czasowi świecenia pojedynczej cyfry. Dodatkowo kanał porównania CCR1 został użyty do regulacji jasności wyświetlacza. W celu zaprogramowania timera należy kolejno:

  • włączyć taktowanie modułu timera w rejestrze APB1ENR1,
  • ustawić preskaler (rejestr PSC) i okres timera (rejestr ARR),
  • ustawić początkową jasność wyświetlacza (rejestr CCR1),
  • włączyć generowanie przerwać na końcu okresu i przy porównaniu z CCR1 (rejestr DIER),
  • uruchomić timer (rejestr CR1).

Wartości wszystkich parametrów potrzebnych do zaprogramowania timera zostały zdefiniowane w pliku 7seg-common.h jako wyrażenia stałe rozwijane przez preprocesor języka C na podstawie dwóch stałych zdefiniowanych przez programistę: częstotliwości odświeżania wyświetlacza MPX_FREQ oraz liczby dostępnych poziomów jasności MPX_STEPS. Dla zapewnienia dokładnego odliczania czasu iloczyn tych stałych powinien być równocześnie wielokrotnością częstotliwości testowania stanu przycisku (50 Hz) i dzielnikiem częstotliwości zegara mikrokontrolera. Do sprawdzenia tego warunku można użyć stosownych dyrektyw preprocesora.

Po zainicjowaniu mikrokontrolera następuje jego uśpienie. Cała funkcjonalność urządzenia została zrealizowana w przerwaniu timera TIM4.

Procedura obsługi przerwania timera składa się z dwóch bloków, wykonywanych odpowiednio przy końcu okresu timera i przy osiągnięciu przez timer wartości zapisanej w rejestrze CCR1.

Wyświetlenie cyfry

Obsługa przerwania końca okresu timera składa się z kilku kolejnych czynności. Rozpoczyna się ona od wyświetlenia kolejnej cyfry, co następuje przy każdym przerwaniu. Wyświetlenie polega na zapisie wcześniej przygotowanej wartości do rejestru BSRR portu GPIOC. Wartość ta jest pobierana z wektora display[], którego każdy element odpowiada jednej cyfrze wyświetlacza. Zawartość wektora jest modyfikowana przy zmianie danych, które mają być wyświetlane. Ponieważ pojedynczy zapis rejestr BSRR powoduje równoczesną zmianę stanu wszystkich sygnałów sterujących wyświetlaczem, nie ma potrzeby wykonywania opisanej wcześniej sekwencji czynności związanych z przejściem do kolejnej cyfry.

Regulacja jasności

Zawartość rejestru CCR1 określa wypełnienie, a tym samym jasność wyświetlacza. Obraz cyfry jest wyświetlany przez przerwanie końca okresu, a wygaszany w przerwaniu porównania CCR1. Jedyną akcją wykonywaną przy obsłudze tego przerwania jest wyłączenie wszystkich cyfr i segmentów, które następuje w wyniku zapisu odpowiedniej stałej wartości do rejestru BSRR portu sterującego wyświetlaczem.

Odliczanie czasu i sterowanie jasnością

Sto razy na sekundę procedura obsługi przerwania timera wywołuje procedurę run_every_10ms() służącą do sterowania jasnością wyświetlacza i odliczania czasu. Okres 10 ms zapewnia poprawne ignorowanie drgań styków przycisku.

Wykrycie naciśnięcia przycisku następuje, gdy przy poprzednim teście przycisk był zwolniony, a obecnie jest wciśnięty. W takim przypadku następuje zmiana zadanej jasności wyświetlacza poprzez przełączenie pomiędzy dwiema dostępnymi jasnościami. W celu uzyskania płynnej zmiany jasności kolejny fragment kodu stopniowo modyfikuje bieżące wypełnienie przebiegów sterujących wyświetlaniem tak, by osiągnęło ono wartość zadaną.

Raz na 100 wejść do procedury, a więc jeden raz na sekundę, następuje wejście do bloku aktualizacji czasu. Licznik sekund jest w nim inkrementowany, a następnie jego stan jest zamieniany na reprezentację odpowiednią do sterowania wyświetlacza. Informacja dla każdej cyfry przyjmuje postać słowa 32-bitowego, przeznaczonego do przesłania do rejestru BSRR portu GPIOC. Do wyznaczenia zawartości tego słowa służą makrodefinicje umieszczone w pliku board.h. Zostały one skonstruowane w taki sposób, by łatwo można było zdefiniować polaryzację sygnałów sterujących aktywacją cyfr i segmentów, zależną od typu wyświetlacza (wspólna anoda/katoda) i sposobu jego podłączenia (wzmacniacze odwracające lub ich brak). Służą do tego dwa symbole preprocesora: DigActLevelSegActLevel, określające poziom logiczny wyjścia, przy którym następuje odpowiednio załączenie cyfry lub segmentu (listing 2).

Sprzętowa obsługa wyświetlacza przy użyciu DMA

Dostępny we wszystkich modelach mikrokontrolerów rodziny STM32 moduł bezpośredniego dostępu do pamięci umożliwia obsługę odświeżania wyświetlacza bez udziału oprogramowania, pod warunkiem, że wszystkie sygnały sterujące pochodzą z jednego portu GPIO.

Druga wersja programu demonstracyjnego, zawarta w projekcie 7seg-dma i korzystająca z modułu bezpośredniego dostępu do pamięci, realizuje sprzętowo funkcje, które w poprzednim przykładzie były wykonywane w procedurze obsługi przerwania timera. Eliminujemy w ten sposób konieczność zgłaszania przerwań przez timer sterujący wyświetlaniem i zmniejszamy obciążenie procesora obsługą przerwań. Dzięki temu możemy zmniejszyć częstotliwość przerwań timera do niezbędnej dla uzyskania pozostałej funkcjonalności urządzenia. W naszym przypadku będzie to częstotliwość 100 Hz, potrzebna do sprawdzania stanu przycisku i odmierzania czasu. Do zgłaszania przerwań z tą częstotliwością użyjemy timera SysTick. Akcje związane z obsługą przerwania realizuje opisana wcześniej procedura run_every_10ms(). Aby uniknąć dodatkowego narzutu czasu potrzebnego na jej wywołanie z procedury obsługi przerwania SysTick można zmienić jej nazwę na SysTick_Handler przy użyciu odpowiedniej definicji dla preprocesora.

W celu zapewnienia odświeżania wyświetlacza należy najpierw zaprogramować moduł DMA. Potrzebne do tego symbole – kanały i numery żądań DMA – zostały zdefiniowane w pliku board.h. W projekcie użyto dwóch kanałów DMA, z których jeden służy do wyświetlania kolejnych cyfr, a drugi – do regulacji jasności poprzez ich wygaszania po upłynięciu określonego czasu od wyświetlenia. Podobnie jak przy realizacji programowej, obie te czynności będą wyzwalane przez odpowiednie zdarzenia generowane przez timer. Inicjowanie modułu DMA umieszczono w funkcji main() (listing 3).

W celu zaprogramowania DMA wykonujemy kolejno następujące czynności:

  • Wybieramy źródła zgłoszeń dla obu używanych kanałów DMA (rejestr DMA1_CSELR).
  • Ustawiamy adres docelowy danych dla kanału wyświetlania – rejestr BSRR portu wyświetlacza.
  • Ustawiamy adres źródłowy danych – wektor sterowań dla poszczególnych cyfr.
  • Ustawiamy długość bufora równą 4.
  • Ustawiamy tryb pracy kanału DMA (transmisja powtarzana danych 32-bitowych z inkrementacją adresu źródła) i włączamy kanał.
  • Ustawiamy adres docelowy danych dla kanału wygaszania – rejestr BSRR portu wyświetlacza.
  • Ustawiamy adres źródłowy danej – stałej powodującej wygaszenie całego wyświetlacza.
  • Ustawiamy długość bufora równą 1.
  • Ustawiamy tryb pracy kanału DMA (transmisja powtarzana danych 32-bitowych) i włączamy kanał.

Po zaprogramowaniu moduł DMA czeka na żądania transmisji, które będą generowane przez timer po jego zaprogramowaniu. Timer odświeżania wyświetlacza programujemy podobnie jak dla odświeżania programowego, jednak nie włączamy przerwań końca okresu i porównania CCR1. Zamiast tego włączamy w rejestrze TIM4 ’ DIER żądania transmisji DMA wynikające z obu tych zdarzeń. Transmisja danych jest wykonywana przez moduł DMA na podstawie żądań zgłaszanych przez timer.

Uwaga: w mikrokontrolerach serii STM32F4 i STM32F7, z powodu struktury połączeń wewnętrznych, dostęp do portów GPIO ma jedynie moduł DMA2. W przypadku użycia mikrokontrolerów z tych serii żądanie odświeżania musi być zgłaszane przez timer połączony z modułem DMA2.

Grzegorz Mazur

Pozostałe artykuły

STM32 - interfejs QuadSPI

Numer: Marzec/2018

Niemal wszystkie nowe modele mikrokontrolerów serii STM32L4 i STM32F4 są wyposażone w interfejs QSPI, przeznaczony do współpracy z szybkimi pamięciami z interfejsem szeregowym. Interfejs QSPI (QuadSPI) stanowi rozwinięcie znanego interfejsu SPI. Główną różnicą pomiędzy SPI i QSPI jest zwiększona liczba i zmodyfikowana funkcjonalność linii danych.

SDC_One - komputer zdefiniowany programowo z klasycznym mikroprocesorem (3). Komputer docelowy

Numer: Marzec/2018

W pierwszym dwóch artykułach serii przedstawiliśmy ogólną koncepcję komputera zdefiniowanego programowo oraz szczegóły implementacji przez mikrokontroler STM32 krytycznych czasowo operacji związanych z realizacją protokołu szyny mikroprocesora. W trzeciej części cyklu, przedstawimy zasoby dostępne dla komputera docelowego - pamięć i wejście-wyjście, zrealizowane przez sprzęt i oprogramowanie mikrokontrolera.

NIOS II na maXimatorze, czyli mikroprocesor w układzie FPGA (4). Nowości i powrót do świata timerów i przerwań

Numer: Marzec/2018

Docierają do nas kolejne nowinki od Intel FPGA - została wydana aktualizacja środowiska Quartus do wersji 17.1, w której będą prowadzone kolejne części niniejszego kursu. Ponadto kontynuować będziemy zgłębianie w praktyczny sposób tematyki timerów oraz innych przerwań w naszym systemie.

NIOS II na maXimatorze, czyli mikroprocesor w układzie FPGA (3). Przerwania, timery i obsługa wyświetlaczy

Numer: Luty/2018

Do tej pory wspólnymi siłami udało nam się wbudować w pełni funkcjonalny system mikroprocesorowy, który odbierał i generował cyfrowe sygnały (powiedzmy dumnie, że przetwarzał sygnały cyfrowe!). Kilkakrotnie wspominałem przy tej okazji, że stosowanie opóźnień jest rozwiązaniem nagannym, jednak dotychczas nie mieliśmy alternatywy - czas ją poznać i wzbogacić system oraz swoją wiedzę o timery i system przerwań.

Systemy dla Internetu Rzeczy (14). Podglądanie ruchu w sieci radiowej z protokołem IEEE 802.15.4

Numer: Luty/2018

Największym problemem podczas pracy z układami komunikacji radiowej jest brak pewności czy nadajnik wysłał to co trzeba i czy odbiornik odbiera poprawnie. Jedynym sposobem pokonania tych kłopotów jest ?podsłuchiwanie? transmisji radiowej. Stosowane są do tego sniffery, zwane też analizatorami sieciowymi lub analizatorami pakietów. Pozwalają śledzić (?wąchać?, ang. sniffing) pakiety przesyłane przez wybrany interfejs sieciowy. Jednym ...

Mobilna
Elektronika
Praktyczna

Ze świata  Kobiety w elektronice    ...

Elektronika Praktyczna

Maj 2019

PrenumerataePrenumerataKup w kiosku wysyłkowym

Elektronika Praktyczna Plus

lipiec - grudzień 2012

Kup w kiosku wysyłkowym