Wyprowadzenia pojedynczego wyświetlacza najczęściej wyglądają tak jak na poniższym obrazku.
Na początek podłączyłem jeden wyświetlacz do mikrokontrolera Atmega32. Kolejne wyprowadzenia wyświetlacza podłączyłem następująco:
Teraz kiedy się przyjrzymy widać, że żeby wyświetlać cyfrę 1 należy włączyć segmenty B i C. Żeby wyświetlić 0 należy włączyć segmenty A,B,C,D,E i F.
Wyświetlacze to zestaw diod połączonych ze sobą. Mogą być połaczone ze sobą Anodami. Wtedy do wyprowadzenia wyświetlacza o oznaczeniu COM podłączamy plus zasilania. Włączenie poszczególnych segmentów polega wtedy na podaniu zera (masy) na wymagane wyprowadzenia.
Dlatego w PORTA musimy na tych pinach ustawiać stan niski i ustawienia dla tej konfiguracji (mojego podłączenia) wyglądają następująco:
' *GFEDCBA Porta = &B11000000 '0 Porta = &B11111001 '1 Porta = &B10100100 '2 Porta = &B10110000 '3 Porta = &B10011001 '4 Porta = &B10010010 '5 Porta = &B10000010 '6 Porta = &B11111000 '7 Porta = &B10000000 '8 Porta = &B10010000 '9
Żeby wyświetlić cyfrę jeden ustawiam stan niski na PORTA.1 i PORTA.2
W zapisie dwójkowym "liczymy" niejako od prawej strony, dlatego w zapisie cyfry 1 dwa zera znajdują się po prawej, licząc od zera, na pierwszym i drugim miejscu. Całkiem z brzegu jest pozycja zero.
Czas trochę zautomatyzować procedurę zmiany wyświetlanych cyferek.
Można je umieścić w specjalnej tabeli. Kiedy mikrokontroler będzie musiał wyświetlić cyfrę zajrzy do tabeli by pobrać ustawienia dla PORTA.
$regfile = "m32def.dat" $crystal = 8000000 $hwstack = 80 $swstack = 64 $framesize = 64 Config Porta = Output Dot Alias Porta.7 'przyjazna nazwa dla kropki, pin wydzielony z całego portu Dim Cyfra As Byte ' TE ZAPISY ZOSTAŁY PRZENIESIONE DO TABELI "Znaki" 'Porta = &B11000000 '0 'Porta = &B11111001 '1 'Porta = &B10100100 '2 'Porta = &B10110000 '3 'Porta = &B10011001 '4 'Porta = &B10010010 '5 'Porta = &B10000010 '6 'Porta = &B11111000 '7 'Porta = &B10000000 '8 'Porta = &B10010000 '9 Do Porta = Lookup(cyfra , Znaki) Incr Cyfra If Cyfra > 9 Then Cyfra = 0 Wait 1 Loop End Znaki: Data &B11000000 , &B11111001 , &B10100100 , &B10110000 , &B10011001 Data &B10010010 , &B10000010 , &B11111000 , &B10000000 , &B10010000
Skoro czytasz ten temat to pewnie wiesz, że można sterować większa ilością wyświetlaczy używając tych samych wyprowadzeń mikrokontrolera. Dołączając kolejny wyświetlacz piny A,B,C,D,E,F,G i DP podłączamy równolegle do tych samych pinów, tak samo jak pierwszy wyświetlacz. Jedynie wyprowadzenia COM będziemy musieli zacząć sterować za pomocą kolejnych pinów mikrokontrolera. Będziemy je uruchamiać naprzemiennie. W jednej chwili tylko jeden wyświetlacz będzie aktywny, ale zmiany będą zachodziły tak szybko, że nasze oko będzie widziało jakby świeciły wszystkie wyświetlacze
Teraz trzeba przypomnieć sobie, że ludzkie oko widzi bezwładnie przy zmianie obrazu szybszym niż 50 klatek na sekundę.
Co w drugą stronę oznacza, że potrafi dostrzec mruganie jeśli coś się dzieje wolniej...
Musimy więc przełączać te wyświetlacze minimum 50 razy na sekundę. 50 razy na sekundę to nic innego jak 50Hz
Do odmierzania czasu najlepiej użyć Timera. Napiszę kiedyś osobny artykuł na ich temat. Na razie przedstawię podstawowe obliczenia.
Jeśli mikrokontroler taktujemy wewnętrznym oscylatorem (ten wybrałem żeby każdy mógł spróbować) to :
Wyświetlacze mamy dwa.
Mnożymy 50Hz x 2 = 100Hz
8MHz = 8 000 000 Hz, Podzielone Prescalerem 1024 = 7812,5 , To dzielimy przez 100 i otrzymujemy wynik 78,125 .
Od takiego wyniku odejmujemy jeszcze jeden i mamy gotową wartość jaką Timer musi odliczyć żeby zgłosić się 100 razy na sekundę.
Teraz zejdźmy na Ziemię. Umiejętność policzenia sobie czasów się na pewno przydaje, ale komputery stworzono dlatego, że potrafią wykonywać działania powtarzalnie bez błędów. Proponuję użyć jakiegoś kalkulatora.
Jak widać obliczenia się zgadzają. Możemy więc skonfigurować Timer by odmierzał 10ms ;)
Skonfigurujemy dwa piny które będą włączać na przemian pierwszy lub drugi wyświetlacz.
Żeby prościej było wytłumaczyć jak to działa będę wyświetlał dwie stałe cyfry - 1 i 2.
Program działa tak: Kiedy Timer zgłasza, że pora właczyć kolejny wyświetlacz to wyłączam poprzedni, ustawiam PORTA wartością która ma się pokazać na drugim wyświetlaczu i dopiero włączam ten wyświetlacz. Piny W1 i W2 to piny odpowiedzialne za włączanie wyświetlaczy. Najczęściej sterują tranzystorami które podają plus na wyprowadzenia COM wyświetlaczy.
$regfile = "m32def.dat" $crystal = 8000000 $hwstack = 80 $swstack = 64 $framesize = 64 Config PORTA = Output Dot Alias PORTA.7 'przyjazna nazwa dla kropki, pin wydzielony z całego portu Config PORTC.3 = Output : Set PORTC.3 : W2 Alias PORTC.3 Config PORTC.2 = Output : Set PORTC.2 : W1 Alias PORTC.2 Dim Cyfra As Byte , Mux As Byte , Timer_flag As Byte Config TIMER0 = Timer , Prescale = 1024 , Compare = Disconnect , Clear Timer = 1 COMPARE0 = 77 '100Hz @8MHz Do If TIFR.OCF0 = 1 Then 'timer ustawił flagę TIFR.OCF0 = 1 'flagi kasuje się wpisując jedynkę Incr Mux 'zwieksz o jeden numer wyswietlacza If Mux > 2 Then Mux = 1 'na razie mamy tylko 1 lub 2 Select Case Mux 'na podstawie wartości zmiennej Mux wybierz którym wyświetlaczem się zajmiemy Case 1 Set W1 'wylącz poprzedni wyświetlacz' PORTA = Lookup(1 , Znaki) 'ustaw PORTA wartością dla drugiego wyswietlacza Reset W2 'teraz go wlacz Case 2 'jeśli pora na drugi wyswietlacz 'to powyższe operacje wykonaj dla tego wyswietlacza Set W2 PORTA = Lookup(2 , Znaki) 'szukaj w tabeli wartości by wyswietlic cyfrę "2" Reset W1 End Select End If Loop End Znaki: Data &B11000000 , &B11111001 , &B10100100 , &B10110000 , &B10011001 Data &B10010010 , &B10000010 , &B11111000 , &B10000000 , &B10010000
Jeśli chcemy uruchomić 4 wyświetlacze to musimy przyspieszyć odświeżanie bo kiedy zgasilibyśmy pierwszy wyświetlacz i dalej robili zmiany co 10ms to do 4 wyświetlacza doszlibyśmy dużo później..
Najprościej pomnożyć 50Hz razy ilość wyświetlaczy 50Hz x 4 = 200Hz i dla takiej częstotliwości robimy obliczenia.
Config Timer0 = Timer , Prescale = 256 , Compare = Disconnect , Clear Timer = 1 Compare0 = 155 '200Hz @8MHz
To będzie działać na pewno więc możemy zając się inną sprawą.
Taki wyświetlacz z czterema polami najczęściej przedstawia jakąś liczbę. Żeby ją poprawnie wyświetlić trzeba obliczyć wartości dla każdego wyświetlacza.
Jak wiadomo do bajtu zapisać możemy jedynie 255. Taka czterocyfrowa liczba ma na pewno więcej więc musi być zapisana co najmniej w zmiennej typu Word lub Integer. Tutaj jest pierwsza pułapka czekająca na nieuważnego programistę.
Żeby otrzymać wartości dla poszczególnych wyświetlaczy musimy tę dużą liczbę podzielić. Nie można jednak wyniku dzielenia zmiennej typu Word zapisywać bezpośrednio do zmiennej typu Byte. Należy użyć zmiennej pomocniczej tego samego typu do której zapisujemy wynik, a dopiero potem rzutować wartość na zmienną typu Byte.
Pora na kolejne usprawnienie. Zamiast zadeklarować cztery osobne bajty przechowujące wartości dla poszczególnych wyświetlaczy możemy zadeklarować tablice czterech bajtów. Przyda się i przy obliczaniu wartości i przy samym multipleksowaniu.
Na wszelki wypadek napisze co to jest Modulo
Jest to reszta z dzielenia przez wartość która stoi za wyrażeniem Mod
Przykład 1 : Wynik = 5 Mod 2 Wynikiem będzie jeden, bo pięć podzielone przez dwa to dwa, ale reszta to jeden.
Przykład 2 : Wynik = 12 Mod 10 Wynikiem będzie dwa, bo 12 podzielone przez dziesięć to jeden , ale reszta to dwa.
$regfile = "m32def.dat" $crystal = 8000000 $hwstack = 80 $swstack = 64 $framesize = 64 Config PORTA = Output Dot Alias PORTA.7 'przyjazna nazwa dla kropki, pin wydzielony z całego portu Config PORTC.5 = Output : Set PORTC.5 : W4 Alias PORTC.5 Config PORTC.4 = Output : Set PORTC.4 : W3 Alias PORTC.4 Config PORTC.3 = Output : Set PORTC.3 : W2 Alias PORTC.3 Config PORTC.2 = Output : Set PORTC.2 : W1 Alias PORTC.2 Config Timer0 = Timer , Prescale = 256 , Compare = Disconnect , Clear_timer = 1 COMPARE0 = 155 '200Hz @8MHz Dim Liczba As Word , Help As Word , Wysw(4) As Byte , N As Byte Dim Cyfra As Byte , Mux As Byte , Timer_flag As Byte Liczba = 1234 'przykładowa wartosc by rozróżnić wyswietlacze For N = 1 To 4 Help = Liczba Mod 10 'zapis do pomocniczej zmiennej Wysw(n) = Help 'rzutowanie Word na Byte Liczba = Liczba / 10 Next N Do If TIFR.OCF0 = 1 Then 'timer ustawił flagę TIFR.OCF0 = 1 'flagi kasuje się wpisując jedynkę Incr Mux If Mux > 4 Then Mux = 1 Select Case Mux Case 1 Set W4 Porta = Lookup(wysw(mux) , Znaki) 'załaduj do PORTA wartość dla wyswietlacza o numerze - Reset W1 ' - aktualnej wartosci zmiennej MUX Case 2 Set W1 Porta = Lookup(wysw(mux) , Znaki) Reset W2 Reset Dot Case 3 Set W2 Porta = Lookup(wysw(mux) , Znaki) Reset W3 Case 4 Set W3 PORTA = Lookup(wysw(mux) , Znaki) Reset W4 End Select End If Loop End Znaki: Data &B11000000 , &B11111001 , &B10100100 , &B10110000 , &B10011001 Data &B10010010 , &B10000010 , &B11111000 , &B10000000 , &B10010000
Każdy kto zbudował coś na tych wyświetlaczach wie, że w nocy strasznie "dają po oczach" czyli świecą za jasno. Zegarek oświetliłby pół sypialni :D
Przydałaby się możliwość ściemniania. Oczywiście taka istnieje i to w postaci regulacji czasu świecenia wyświetlaczy.
Zabierając się za budowę warto przemyśleć który Timer użyjemy. Najlepiej taki trick wychodzi kiedy Timer ma dwa rejestry porównania COMPAREA i COMPAREB, ale nawet jeśli ma jeden to nic straconego.
Możemy Timer "puścić" do końca i w przerwaniu generowanym kiedy Timer się przepełnia multipleksować i włączać wyświetlacze. Do tego kiedy uaktywnimy również przerwanie kiedy wartość Timera jest zgodna z rejestrem porównania mamy możliwośc wcześniejszego wyłączenia wyświetlacza... Hmmm jak to działa?
Włączam wyświetlacz kiedy Timer zgłasza przepełnienie, ale wyłączam go już kiedy Timer osiągnie wartość zapisaną w rejestrze COMPARE0. Dodałem też zmianę wartości liczby by coś się działo na wyświetlaczach.
$regfile = "m32def.dat" $crystal = 8000000 $hwstack = 80 $swstack = 64 $framesize = 64 Config PORTA = Output Dot Alias PORTA.7 'przyjazna nazwa dla kropki, pin wydzielony z całego portu Config PORTC.5 = Output : Set PORTC.5 : W4 Alias PORTC.5 Config PORTC.4 = Output : Set PORTC.4 : W3 Alias PORTC.4 Config PORTC.3 = Output : Set PORTC.3 : W2 Alias PORTC.3 Config PORTC.2 = Output : Set PORTC.2 : W1 Alias PORTC.2 Config TIMER0 = Timer , Prescale = 64 , Compare = Disconnect Enable COMPARE0 : On Compare0 Compare0_isr Nosave Dim Liczba As Word , Help As Word , Wysw(4) As Byte , N As Byte Dim Cyfra As Byte , Mux As Byte , Timer_flag As Byte Dim Ticks As Byte , Copy As Word , Brightness As Byte Dim Up_down As Byte Enable Interrupts Liczba = 1234 'przykładowa wartosc by rozroznic wyswietlacze Brightness = 255 Do If TIFR.TOV0 = 1 Then 'timer ustawił flagę TIFR.TOV0 = 1 'flagi kasuje się wpisując jedynkę Incr Mux If Mux > 4 Then Mux = 1 Select Case Mux Case 1 Set W4 PORTA = Lookup(wysw(mux) , Znaki) 'załaduj do PORTA wartość dla wyswietlacza o numerze - Reset W1 ' - aktualnej wartosci zmiennej MUX Case 2 Set W1 PORTA = Lookup(wysw(mux) , Znaki) Reset W2 Case 3 Set W2 PORTA = Lookup(wysw(mux) , Znaki) Reset W3 Case 4 Set W3 PORTA = Lookup(wysw(mux) , Znaki) Reset W4 End Select Incr Ticks If Ticks = 100 Then Ticks = 0 If Liczba > 0 Then Decr Liczba Copy = Liczba For N = 1 To 4 Help = Copy Mod 10 'zapis do pomocniczej zmiennej Wysw(n) = Help 'rzutowanie Word na Byte Copy = Copy / 10 Next N End If If Up_down = 0 Then If Brightness > 1 Then Decr Brightness 'zmniejszaj jasnośc do 1 Else Up_down = 1 End If Else If Brightness < 254 Then Incr Brightness 'zwiekszaj jasność do 254 Else Up_down = 0 End If End If Compare0 = Brightness 'wpisz wartosc do rejestru porównania Timer0 End If End If Loop End Compare0_isr: !PUSH R23 !PUSH R24 !in R24, sreg !PUSH R24 PORTA = 255 'Tuned by NoSave Tool !POP R24 !out sreg, r24 !Pop R24 !POP R23 Return Znaki: Data &B11000000 , &B11111001 , &B10100100 , &B10110000 , &B10011001 Data &B10010010 , &B10000010 , &B11111000 , &B10000000 , &B10010000
Kod który steruje tym rejestrem ma dosłownie cztery linijki. Reszta to konfiguracja.
W kodzie podane są nawet piny do których należy się podłączyć.
$regfile = "m32def.dat" $crystal = 8000000 Dim B As Byte '-----------------------74HC595 Config Porta = Output Ds_ser Alias Porta.0 'Pin (14) 'Pin (13) connect to Ground (when High then outputs Hi-Z) Stcp_rclk Alias Porta.1 'Pin (12) Shcp_srclk Alias Porta.2 'Pin (11) Mr_srclr Alias Porta.3 'Pin (10) 'opcjonalne czyszczenie rejestru po włączeniu zasilania Reset Mr_srclr Set Mr_srclr B = 1 Do Shiftout Ds_ser , Shcp_srclk , B 'wyslij wartosc bajtu B Set Stcp_rclk 'zatwierdź Reset Stcp_rclk Waitms 50 Rotate B , Right , 1 'przesun bit w bajcie B Loop End