![[Rozmiar: 10329 bajtów]](naglowek.gif)
![[Rozmiar: 37989 bajtów]](7seg_resize.jpg)
Wyprowadzenia pojedynczego wyświetlacza najczęściej wyglądają tak jak na poniższym obrazku.
![[Rozmiar: 6507 bajtów]](wysw7seg.gif)
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.
![[Rozmiar: 62588 bajtów]](calc_50hz.jpg)
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
![[Rozmiar: 86271 bajtów]](12.jpg)
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
![[Rozmiar: 101533 bajtów]](1234.jpg)
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
![[Rozmiar: 376862 bajtów]](74hc595.gif)
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