Sterowanie rejestrami przesuwnymi z wykorzystaniem interfejsu SPI na przykładzie 6 cyfrowego wyświetlacza LED


http://www.easy-soft.pl/
Sterowanie rejestrami przesuwnymi z wykorzystaniem
interfejsu SPI na przykładzie 6-cyfrowego wyświetlacza LED.
Tym razem, korzystając z modułu wyświetlacza LED już wcześniej opisywanego na tej stronie (np.
http://www.easy-soft.pl/modules.php?name=News&file=article&sid=43), podłączyłem go do mikrokontrolera
ST7. Jednocześnie zastanawiałem się, czy nie można by było wykorzystać do jego obsługi interfejsu SPI, to
znaczy czy rejestr 74HCT595 nadaje się do odbioru danych przesyłanych za pomocą SPI w którymś z
trybów pracy. Przecież SPI posiada linię zegara w takt którego przesyła i odbiera dane. A użycie interfejsu
SPI, zresztą  któregokolwiek ze sprzętowych udogodnień wbudowanych w strukturę mikrokontrolera,
znakomicie wręcz upraszcza program, wpływa na łatwość jego uruchomienia (a co za tym idzie  skraca
czas potrzebny na przetestowanie układu).
Wyświetlacz LED
Pole odczytowe wyświetlacza ma 6 cyfr LED, po 7 segmentów każda. Doliczając kropkę dziesiętną można
powiedzieć, że wyświetlacz wymaga do sterowania 8 bitów, więc same sterowanie segmentami zajmie jeden
pełny port. Dodatkowo sterowanie tranzystorami kluczami załączającymi napięcie na anody cyfr, będzie
wymagać następnych sześciu bitów portu. To już razem 14 linii! A jeśli jeszcze konieczne stanie się jakiś
układów zewnętrznych takich, jak na przykład klawiatura? Może braknąć wyprowadzeń mikrokontrolera.
Schemat połączeń wyświetlacza LED przedstawiono zobaczyć na rysunku 1. Do eksperymentowania z tym
przykładem trzeba sobie taki wyświetlacz zbudować, chociażby na płytce uniwersalnej. Moim zdaniem
przyda on nie tylko do eksperymentów, ale również do wykorzystania w dowolnym innym układzie.
Obsługa wyświetlacza oparta jest o przerwanie generowane przez Lite Timer B. Przy każdym wejściu w
obsługę przerwania wyświetlana jest pojedyncza cyfra. Czas przełączania cyfr jest dobrany tak, aby spełniał
dwa kryteria:
1) aby nie było widoczne migotanie cyfr,
2) aby czas wyświetlania (załączenia) cyfry był maksymalnie długi.
Do konstrukcji rejestru wyświetlacza użyłem układów 74HCT595, które są rejestrami przesuwającymi z
wejściem szeregowym, wyjściem równoległym i zatrzaskami na tychże wyjściach. Do zastosowania w
prezentowanym układzie predysponowała je zwłaszcza ta druga cecha. Dzięki użyciu zatrzasków na
wyjściach, układ nie wyprowadza informacji do momentu pojawienia się osobnego impulsu zegarowego,
który ją tam przepisze. Nie ma więc efektu migotania cyfr w czasie wpisywania danych do rejestrów. Każdy
impuls zegarowy docierający do wejścia zegara przesuwu, powoduje próbkowanie stanu wejścia, jego zapis
do rejestru przesuwnego oraz przesunięcie o 1 bit w lewo. Układy można łączyć w szeregi budując rejestry o
praktycznie nieograniczonej pojemności. Dla potrzeb tej aplikacji połączyłem szeregowo dwa układy
74HCT595 tworząc w ten sposób rejestr o pojemności 16 bitów. Jako pierwszy w szeregu znajduje się rejestr
sterujący załączaniem segmentów cyfr (aktywny stan niski), jako drugi rejestr załączający poszczególne
cyfry poprzez sterowanie kluczami tranzystorowymi (również aktywny stan niski). Wejście szeregowe danych
taktowane jest sygnałem o częstotliwości około 4,65 kHz natomiast cyfry przełączane są z częstotliwością
zbliżoną do 48Hz.
Użyłem wyświetlaczy LED ze wspólną anodą. Zasilanie anod załączane jest przez tranzystory MOS z
kanałem typu P (np. BS250). Wartości rezystorów podłączonych do poszczególnych segmentów
wyświetlacza należy dobrać indywidualnie do posiadanych cyfr, pamiętając jednocześnie o tym, aby nie
przekroczyć dopuszczalnego prądu wyjść rejestrów. Numery wyprowadzeń wyświetlacza LED podane na
schemacie należy traktować jako orientacyjne. Istotne są literowe oznaczenia segmentów.
Sterowanie wyświetlaniem
Do sterowania wymagane są trzy linie  jedna danych i dwie zegarowe. Wykorzystano w tym celu wyjście
danych MOSI, wyjście zegarowe SCK i 4 wyprowadzenie portu B. W skrócie funkcjonowanie wyświetlacza
wygląda następująco: dane przy pomocy opadającego zbocza sygnału zegarowego podawanego na
wyprowadzenie 11 (SRCLK) wpisywane są z wejścia szeregowego na doprowadzeniu 14 (SER) do
wewnętrznego rejestru. Mikrokontroler przesyła pełne słowo 16-to bitowe tak, aby działały oba układy
rejestrów. Następnie, po wpisaniu 16 bitów do rejestrów, na wyprowadzenie PB4 (RCLK) podawany jest
J.Bogusz  Sterowanie rejestrami przesuwnymi z wykorzystaniem SPI strona 2 / 10
http://www.easy-soft.pl/
impuls, którego opadające zbocze powoduje przepisanie danych z wewnętrznego szeregowo 
równoległego rejestru do wyjściowego równoległego rejestru typu zatrzask.
Prezentowana aplikacja wykorzystuje tryb SPI zwany Master, to znaczy mikrokontroler przesyła bajt
generując sygnał zegara. Wyboru trybu pracy SPI dokonuje się poprzez nastawy rejestrów kontrolnych
SPICR i SPICSR (w programie zadeklarowany jako SPISR). Samo przesyłanie danych przez SPI jest bardzo
proste: wystarczy w rejestrze danych SPIDR umieścić bajt i poczekać, aż zostanie wysłany. Nie mniej jednak
procedura wysyłająca zawiera kilka drobnych szczegółów, bez uwzględnienia których po prostu nie działa,
lub zatrzymuje się po wysłaniu pojedynczego bajtu. Podprogram przesyłający dane umieszczono na listingu
1. Wymaga on kilku słów wyjaśnienia.
;-------------------------------------------------------
; funkcja wysyłająca zmienną LEDDATA do wyświetlacza LED
; z wykorzystaniem interfejsu SPI
;-------------------------------------------------------
ledwrite
push X ;zapamiętanie modyfikowanych rejestrów
;mikrokontrolera na stosie
push A
clr X
ld A,(leddata,X) ;pierwszy bajt zmiennej
ld SPIDR,A
btjf SPISR,#7,* ;oczekiwanie na wysłanie bajtu
ld A,SPIDR ;dla wyzerowania SPIF
inc X ;drugi bajt zmiennej
ld A,(leddata,X)
ld SPIDR,A
btjf SPISR,#7,* ;oczekiwanie na wysłanie bajtu
ld A,SPIDR ;dla wyzerowania SPIF
ld A,PBDR ;krótki impuls dodatni na PB4 (przepisanie rejest-
or A,#$10 ;rów szeregowych na wyjścia)
ld PBDR,A
and A,#$EF
ld PBDR,A
pop A ;odtworzenie rejestrów roboczych mikrokontrolera
pop X
ret
Listing 1. Funkcja przesyłająca 2-bajty zmiennej LEDDATA przez interfejs SPI.
Na początku podprogramu zapamiętywane są na stosie modyfikowane przezeń rejestry, bez uwzględnienia
flag mikrokontrolera. Następnie do akumulatora z pamięci RAM pobierany jest pierwszy bajt zmiennej.
Wskaznikiem jest adres zmiennej a indeksem rejestr X, który w tym momencie ma wartość 0. Po pobraniu
bajt zapisywany jest do rejestru danych interfejsu SPI, z którego to jest natychmiast wysyłany przez wyjście
szeregowe MOSI, synchronicznie z zegarem, którego sygnał wyprowadzany jest przez SCK. Wysyłka
kończy się ustawieniem bitu 7 (SPIF) w rejestrze SPISR. Stan wysoki tego bitu pociąga za sobą pewne
reperkusje.
Po pierwsze, jeśli ustawiony jest bit SPIE w rejestrze SPICR, to wygenerowane zostanie przerwanie. Po
drugie, może znacznie dla nas ważniejsze, SPI nie wyśle żadnych danych do momentu, aż bitowi SPIF
programowo zostanie nadana wartość logiczna 0.
Zgodnie z dokumentacją producenta, bit zostaje wyzerowany w pewnym specjalnym cyklu: należy dokonać
odczytu rejestru SPISR a następnie odczytu rejestru SPIDR. W prezentowanym na listingu podprogramie
pierwszy warunek spełniany jest przez rozkaz BTJF testujący stan bitu 7, natomiast drugi przez specjalnie w
tym celu dodany rozkaz ld A,SPIDR. Nie ma on w tym przypadku żadnej innej funkcji poza opisaną. Jeszcze
raz pozwolę sobie przypomnieć: bez wykonania opisanej sekwencji flaga SPIF pozostaje ustawiona i nie
pozwala interfejsowi na przyjmowanie następnych danych. W konsekwencji interfejs po prostu nie działa!
J.Bogusz  Sterowanie rejestrami przesuwnymi z wykorzystaniem SPI strona 3 / 10
http://www.easy-soft.pl/
Obsługa przerwania timera B.
Każde wywołanie procedury obsługi przerwania timera B powoduje przesłanie dwóch bajtów informujących o
załączeniu cyfry oraz jej wzorca do rejestrów. Wartość bieżących, przesyłanych przez SPI dwóch bajtów,
zapamiętywana jest w zmiennej o nazwie leddata. Stan zmiennej jest dokładnym odwzorowaniem
wysyłanych bajtów.
Dwa bajty leddata tworzone są na podstawie tablic z pamięci programu. Starszy bajt zawiera kod załączenia
wyświetlacza. Młodszy bajt zawiera kod załączeń segmentów wyświetlacza na danej pozycji. W jaki sposób
tworzone są oba kody?
Ich wzorce pamiętane są jako stałe umieszczone w pamięci programu (listing 2). Indeksem do tych tablic są:
przy pobieraniu kodu załączenia cyfry  zmienna licznikowa ledcnt zawierająca numer wyświetlanej pozycji,
przy pobieraniu kodu załączenia segmentów  wartość cyfry w kodzie BCD zapamiętana w zmiennej
tablicowej ledbuf.
Przy pobieraniu kodu załączenia, do rejestru X ładowana jest wartość licznika ledcnt o ile ten, nie
przekroczył 5 (licznik liczy od 0 do 5). Jeśli jest on równy 6, to przed załadowaniem do X jest zerowany tak,
aby ponownie wskazywał na pierwszą pozycję tablicy. Rejestr X jest offsetem dla adresu 16-bitowego w
pamięci programu. Część stała adresu, to początek tablicy o nazwie sequence. Jak łatwo domyśleć się, w
ten sposób pobierane będą wartości zależne od stanu licznika. Oczywiście dla potrzeb sterowania
załączaniem kluczy można było na przykład przesuwać w prawo wartość zmiennej tworząc swego rodzaju
 wędrujące 0 . Po pobraniu z tablicy kod załączenia zapisywany jest w starszym bajcie zmiennej leddata.
Kod załączenia segmentów tworzony jest w dwóch etapach. Najpierw, na bazie zmiennej licznikowej,
wyznaczony jest offset do tablicy w pamięci RAM i z niej pobierana jest wartość cyfry w kodzie BCD. Ta
wartość stanowi z kolei offset do tablicy patterns zawierającej wzorce znaków do wyświetlenia. Po pobraniu
wzorzec zapamiętywany jest w młodszym bajcie zmiennej leddata.
Funkcja obsługi przerwania kończona jest wysłanie obu bajtów (leddata) przez SPI. Dzieje się tak na skutek
wywołania podprogramu ledwrite.
;-------------------------------------------------------
; deklaracje tabel związanych z cyframi
;-------------------------------------------------------
;kody załączeń cyfr (0 powoduje załączenie danego klucza tranzystorowego)
sequence DC.B $FE,$FD,$FB,$F7,$EF,$DF
;kody załączeń segmentów cyfr (0 załącza segment)
;d0=G, d1=f, d2=D, d3=dp, d4=C, d5=A, d6=B, d7=E
patterns DC.B %00001001,%10101111,%00011010,%10001010,%10101100 ;0,1,2,3,4,
pat1 DC.B %11001000,%01001000,%10001111,%00001000,%10001000 ;5,6,7,8,9,
pat2 DC.B $FF ;znak wyłączony
Listing 2. Tablice sterujące załączeniem kluczy i segmentów.
J.Bogusz  Sterowanie rejestrami przesuwnymi z wykorzystaniem SPI strona 4 / 10
Rysunek 1. Schemat dołączenia wyświetlacza 6-cyfrowego LED do mikrokontrolera ST7LISTE29.
http://www.easy-soft.pl/
Wyświetlanie wartości  program główny.
Kolejnym przełączaniem wyświetlanych cyfr i ich pobieraniem z pamięci mikrokontrolera zajmuje się
opisywana wyżej procedura obsługi przerwania. Jedyne, co musi zrobić użytkownik, to na odpowiedniej
pozycji zmiennej tablicowej ledbuf wstawić cyfrę w kodzie BCD. Numer pozycji w tablicy ściśle odpowiada
numerowi wyświetlanej cyfry. I tak dla przykładu wykonanie rozkazów:
ld A,#1
ld {ledbuf+1},A
Spowoduje pojawienie się na 2-giej pozycji wyświetlacza cyfry  1 (znaki liczone są od lewej do prawej,
pierwszy znak ma numer 0). Przykład takiego podstawienia zawiera program główny rozpoczynający się od
etykiety main, a umieszczony na listingu 3.
Należy pamiętać jeszcze o tym, aby program główny zawierał załączenie przerwań (rozkaz rim). Bez nich
procedura wyświetlająca nie będzie działać!
W programie użyto kilku makr tak, aby poprawić jego czytelność. Makro nie jest podprogramem
wywoływanym przez CALL czy CALLR. Owszem, może zawierać definicję podprogramu. Upraszczając
można powiedzieć, że makro to zestaw instrukcji asemblera powiązanych z definicją makro, umieszczany
przez kompilator w miejscu pojawienia się jego nazwy. W prezentowanym przykładzie użyto odrębnych makr
dla:
" nastaw trybu pracy portu B (PORTS_INIT),
" nastaw trybu pracy SPI (SPI_INIT),
" nastaw przerwania od timera B (TIMB_INIT).
Wszystkie bity portu PB są wyjściowymi z załączonymi rezystorami zasilającymi, za wyjątkiem bitu PB2.
Wprowadzono następujące nastawy SPI:
" częstotliwość zegara równą 1/8 częstotliwości zegara taktującego pracą CPU,
" CPOL = 1, CPHA = 1,
" tryb master (wymaga nastaw rejestrów SPICR i SPISR),
" przerwania wyłączone.
Timer B inicjowany jest w taki sposób, że częstotliwość sygnału doprowadzonego na jego wejście jest równa
częstotliwości z jaką taktowane jest CPU i zostaje załączone przerwanie.
Jacek Bogusz
J.Bogusz  Sterowanie rejestrami przesuwnymi z wykorzystaniem SPI strona 6 / 10
http://www.easy-soft.pl/
st7/
;Program demonstracyjny demonstrujący użycie
;wyświetlacza LED: 6 znaków 7 segmentowych
MOTOROLA ;format MOTOROLA (.s19)
#include "st7flite29.inc"
;segmenty pamięci
BYTES
segment byte at 80-FF 'ram0'
segment byte at 100-1FF 'stack'
segment byte at 200-27F 'ram1'
segment byte at 1000-10FF 'eeprom'
segment byte at E000-FFDF 'program'
segment byte at FFE0-FFFF 'intvect'
BYTES
;deklaracje zmiennych
segment 'ram0'
ledcnt DS.B ;licznik znaków LED
ledbuf DS.B 6 ;miejsce na 6 znaków
leddata DS.B 2 ;bieżący wyświetlany znak
;deklaracje stałych
WORDS
segment 'program'
;-------------------------------------------------------
; deklaracje tabel związanych z cyframi
;-------------------------------------------------------
sequence DC.B $FE,$FD,$FB,$F7,$EF,$DF
patterns DC.B %00001001,%10101111,%00011010,%10001010,%10101100 ;0,1,2,3,4,
pat1 DC.B %11001000,%01001000,%10001111,%00001000,%10001000 ;5,6,7,8,9,
pat2 DC.B $FF ;znak wyłączony
;-------------------------------------------------------
; inicjacja używanych portów I/O
;port B
PBDR_init EQU %00000000
PBDDR_init EQU %11111011
PBOR_init EQU %11111011
;-------------------------------------------------------
PORTS_INIT MACRO
ld A,#PBDDR_init ;nastawa trybu pracy portu B
ld PBDDR,A
ld A,#PBOR_init
ld PBOR,A
ld A,#PBDR_init
ld PBDR,A
MEND
;-------------------------------------------------------
; inicjacja SPI:
J.Bogusz  Sterowanie rejestrami przesuwnymi z wykorzystaniem SPI strona 7 / 10
http://www.easy-soft.pl/
; CLK = fcpu/3, divider = wyłączony, cpol = idle gdy "H",
; cpha = 1, przerwania = wyłączone, tryb = master
SPISR_init EQU %00000011
SPICR_init EQU %00011100
SPI_enable EQU %01000000
;-------------------------------------------------------
SPI_INIT MACRO
ld A,#SPISR_init
ld SPISR,A
ld A,#SPICR_init
ld SPICR,A
or A,#SPI_enable
ld SPICR,A
MEND
;-------------------------------------------------------
; inicjacja timera Lite (timer B)
;-------------------------------------------------------
TIMB_INIT MACRO
clr A ;f_CPU = f_OSC
ld MCCSR,A
ld A,#$12 ;f_TIMER = f_CPU
ld ATCSR,A
clr A ;konfiguracja 12-bitowego timera
ld ATRL,A
ld A,#$07
ld ATRH,A
clr ledcnt ;zerowanie licznika znaków
MEND
;-------------------------------------------------------
; funkcja obsługi przerwania timera B
;-------------------------------------------------------
timerb
ld A,ledcnt
sub A,#6 ;C=1 dla licznika <= 6
jrc timbskip
clr ledcnt ;jeśli licznik > 5,to zerowanie
timbskip
ld X,ledcnt ;załadowanie wzorca załączenia klucza
;tranzystorowego
ld A,(sequence,X) ;na podstawie wartości licznika
ld leddata,A
ld A,(ledbuf,X) ;pobranie cyfry BCD do akumulatora
ld X,A ;ustawienie rejestru indeksowego
ld A,(patterns,X) ;pobranie wzorca cyfry
ld {leddata+1},A ;załadowanie wzorca cyfry do bufora bieżącej cyfry
call ledwrite ;wysłanie znaków do rejestrów cyfr
inc ledcnt
ld A,ATCSR ;odczyt ATCSR w celu zerowania flagi przerwania
iret
;-------------------------------------------------------
; funkcja wysyłająca zmienną LEDDATA do wyświetlacza LED
J.Bogusz  Sterowanie rejestrami przesuwnymi z wykorzystaniem SPI strona 8 / 10
http://www.easy-soft.pl/
;-------------------------------------------------------
ledwrite
push X ;zapamiętanie modyfikowanych rejestrów
push A
clr X
ld A,(leddata,X) ;pierwszy bajt zmiennej
ld SPIDR,A
btjf SPISR,#7,* ;oczekiwanie na wysłanie bajtu
ld A,SPIDR ;dla wyzerowania SPIF
inc X ;drugi bajt zmiennej
ld A,(leddata,X)
ld SPIDR,A
btjf SPISR,#7,* ;oczekiwanie na wysłanie bajtu
ld A,SPIDR ;dla wyzerowania SPIF
ld A,PBDR ;krótki impuls dodatni na PB4
or A,#$10
ld PBDR,A
and A,#$EF
ld PBDR,A
pop A ;odtworzenie rejestrów roboczych
pop X
ret
;-------------------------------------------------------
; początek programu głównego
;-------------------------------------------------------
main
PORTS_INIT ;inicjalizacja portów I/O
SPI_INIT ;inicjalizacja trybu pracy SPI
TIMB_INIT ;inicjalizacja timera B
ld A,#0 ;umieszczenie w buforze ciągu 012345
ld {ledbuf+0},A
ld A,#1
ld {ledbuf+1},A
ld A,#2
ld {ledbuf+2},A
ld A,#3
ld {ledbuf+3},A
ld A,#4
ld {ledbuf+4},A
ld A,#5
ld {ledbuf+5},A
rim ;załączenie przerwań
jra * ;wyświetlanie w przerwaniu timera B
;-------------------------------------------------------
; "pusta" funkcja, zawiera tylko instrukcje powrotu
; z obsługi przerwania
;-------------------------------------------------------
.it_ret iret
segment 'intvect'
;-------------------------------------------------------
J.Bogusz  Sterowanie rejestrami przesuwnymi z wykorzystaniem SPI strona 9 / 10
http://www.easy-soft.pl/
; wektory przerwań
;-------------------------------------------------------
DC.W it_ret
DC.W it_ret
DC.W it_ret
.sci DC.W it_ret
.timb DC.W timerb
.tima DC.W it_ret
.spi DC.W it_ret
DC.W it_ret
DC.W it_ret
.ext3 DC.W it_ret
.ext2 DC.W it_ret
.ext1 DC.W it_ret
.ext0 DC.W it_ret
DC.W it_ret
.soft DC.W it_ret
.rst DC.W main
END
Listing 3. Program do obsługi wyświetlacza LED przy pomocy SPI.
J.Bogusz  Sterowanie rejestrami przesuwnymi z wykorzystaniem SPI strona / 10


Wyszukiwarka