Faq MCS 51

background image

1

Copyright by tomasz baszczok

http://www.tdv.cad.pl

FAQ, które masz właśnie przed oczyma powstało głównie dla początkujących elektroników amatorów, chcących
zmierzyć się z wyzwaniem jakim jest samodzielne programowanie mikrokontrolerów rodziny MCS ’51. Powstało wiele
klonów tego mikroprocesora, które wyposażono w różne dodatkowe elementy typu przetworniki A/C, C/A, PWM (co
też się może do sprowadzać do przetwornika C/A), dodatkowe porty itd. Ten tekst dotyczy przede wszystkim wersji
podstawowej układu.
Bardzo wygodnymi elementami dla elektronika amatora jest seria µC 89Cxxxx firmy ATMEL. Są to typowe układy
oparte o rdzeń ’51, wyposażone w pamięć FLASH, dzięki czemu idealnie nadają się do uruchamiania prototypów.
Programator dla tej serii można łatwo skonstruować samemu, lub skorzystać z gotowych opracowań dostępnych w
sieci. Jeszcze prostsze w użytku są nowe układy firmy Philips, też z pamięcią Flash, oraz dodatkowo wyposażone w
interfejs SPI aczkolwiek są to już szczegóły techniczne wykraczające poza obszar tego FAQ.
Tekst ten możesz dowolnie rozpowszechniać oczywiście pod warunkiem, że robisz to za absolutne friko;-))))). Jeżeli
masz jakieś pytania lub sugestie napisz do mnie

tdv@cad.pl

.


P: Czego potrzebuję żeby móc napisać i uruchomić własny program?
O:
Zakładam, że chodzi o całkowicie amatorskie pisanie programów. Czemu? O tym za chwilę. Potrzebujesz asemblera
(lub kompilatora jeżeli zamierzasz pisać programy w języku wysokiego poziomu np. C), linkera i wskazany byłby jakiś
symulator – debuger. W sieci można wyszperać sporo oprogramowania tego typu. Ja polecam program ProView32
firmy Franklin Software INC. oraz µVision/51 i dScope-51 firmy Keil. Tu dochodzimy do amatorstwa;-)))). Są to
programy komercyjne, które kosztują sporo dolarów, jednak firmy te zadbały o stworzenie wersji dla amatorów. Z
pewnymi (czasami całkiem sporymi) ograniczeniami można korzystać z tych programów za darmo;-))))))). W
internecie można też znaleźć w pełni darmowe programy. Zajrzyj na moją stronę (

http://www.tdv.cad.pl

), powinno

tam być kilka adresów, gdzie je można znaleźć.
Do tego jest potrzebny jakiś sprzęt do zaprogramowania układu. Tu znowu odsyłam do zasobów sieci;-)))).
No i oczywiście wskazana jest odrobina cierpliwości.

P: Napisałem program (w symulatorze działa), zmontowałem układ, zaprogramowałem procesor, a układ nie
działa.
O:
Powodów może być dużo. Na początek proponuję sprawdzić czy działa generator na końcówce 18 lub 19 (w
zależności od typu procesora) powinno dać się zaobserwować przy pomocy oscyloskopu, lub zmierzyć
częstościomierzem sygnał zegarowy odpowiadający użytemu rezonatorowi. Jeżeli generator działa właściwie należy
sprawdzić czy końcówka EA (pin 31) jest właściwie podłączona. Przy korzystaniu z wewnętrznej pamięci programu
powinna ona być podłączona do napięcia zasilania, przy pamięci zewnętrznej do masy.

P: Zapisuję 1 do portu P0, a na wyjściu się ona nie pojawia. Te same instrukcje użyte na porcie P1 działają –
dlaczego?
O:
Porty P1, P2 i P3 µC ’51 są wyposażone na wyjściach w wewnętrzne rezystory podciągające „pullup” (w
rzeczywistości nie są to zwykłe rezystory), natomiast port P0 ma wyjścia typu otwarty dren (kolektor) dlatego aby móc
go używać jako typowego portu wejścia – wyjścia należy dołączyć zewnętrze rezystory podciągające (np. w postaci
drabinki rezystorowej).

P: Dlaczego kiedy próbuję odczytywać stan portu, to co otrzymuję nie zgadza się z tym co na nim jest w
rzeczywistości.
O:
Możliwości są dwie: pierwsza, używasz portu P0, a nie dołączyłeś oporników pullup. Druga możliwość: aby
odczytać stan końcówki zewnętrznej procesora w rejestrze wyjściowym odpowiadającym danemu portowi musi być
wpisana jedynka. Dopiero kiedy ten warunek jest spełniony można odczytać stan końcówki układu.

P: Czy mogę bezpośrednio z portu procesora wysterować np. diodę LED?
O:
W standardowym µC ’51 nie. Ale np. w procesorach 89C1051, 89C2051 i 89C4051 da się to zrobić. Są to małe
klony (obudowa DIP 20) produkcji Atmel’a, układy posiadają dwa porty i dodatkowo wbudowany komparator (można
za nich łatwo zrobić przetwornik A/C sukcesywnej aproksymacji;-)))). Przy podłączaniu diody pamiętać trzeba, że są
one w stanie ją wysterować ale pod warunkiem, że będą sterowały stanem niskim (prąd wejściowy w stanie niskim
wynosi 20mA). Jest jeszcze kilka innych procesorów z podobnymi możliwościami.

P: Jakie rezonatory powinno się stosować w układach?
O:
Hmm, to zależy. Jest taka zasada if don’t need a speed, don’t make a heat. O co w tym chodzi? O to, że w układach
CMOS (a praktycznie wszystkie nowoczesne układy są produkowane w tej technologii) moc wydzielana na elemencie
zależy w sposób znaczny od jego częstotliwości pracy. Dlatego jeżeli nie ma potrzeby stosowania dużych
częstotliwości (układy firmy Dallas mogą być taktowane nawet 33MHz) powinno się stosować częstotliwości niższe.
Jakie? To zależy od konkretnego zapotrzebowania. Jest jedno szczególne wskazanie, a mianowicie układy, które mają
odmierzać upływ czasu rzeczywistego. Cykl maszynowy podstawowej wersji ’51 trwa 12 taktów zegara. Na pierwszy
rzut oka, do zrobienia zegarka idealnie się nadaje rezonator 12 MHz, ale tylko na pierwszy rzut oka. Znacznie lepszym

background image

2

Copyright by tomasz baszczok

http://www.tdv.cad.pl

rozwiązaniem są tu częstotliwości 11,0592MHz, 7,372800MHz czy 3,686400MHz. Uważny czytelnik zauważył, że
trzecia częstotliwość stanowi 1/3 pierwszej, a druga 2/3... Dlaczego takie?
’51 posiada dwa układy tajmerów (na przyszłość, za każdym razem kiedy piszę o ’51 mam na myśli wersję
podstawową, jeżeli będzie chodziło o jakąś wersję rozbudowaną, zostanie to zaznaczone). Są to liczniki
szesnastobitowe zliczające w górę od zadanej wartości. Liczniki te mogą pracować w czterech trybach (jak ktoś nie wie
o co chodzi niech zajrzy do dokumentacji procesora). Z grubsza licznik działa na tej zasadzie, że wpisuje się do niego
jakąś wartość i on od tej wartości zaczyna zliczać w górę. Po przepełnieniu licznika (czyli po przejściu ze stanu
0x0FFFF (tu druga uwaga, dla odróżnienia kodu szesnastkowego będę się posługiwał notacją zaczerpniętą z języka C
czyli 0x..... oznacza, że te kropeczki to kod szesnastkowy) na 0x00000) następuje wywołanie przerwania sprzętowego.
W programie powinien być odpowiedni podprogram je obsługujący. Znaczy to, że maksymalnie możemy odmierzyć
65536 cykli maszynowych (dla 11,0592MHz daje to około 70ms). Dlatego żeby otrzymać np. czas jednej sekundy
potrzebny jest jeszcze licznik programowy, ale to już nie stanowi problemu. Kłopoty leżą gdzie indziej. Mała tabelka:

Fr=12MHz

Fr=11,0592MHz

Odmierzany czas

10ms

10ms

cykl maszynowy

1µs

1,08506µs

liczba cykli koniecznych do

zliczenia

10 000

0x02710

9216

0x02400

wartość ładowana do licznika

55536

0x0D8F0

56320

0x0DC00

Licznik timera składa się z dwóch ośmiobitowych połówek. THx i TLx, które ładuje się osobno (zakładam tryb 1
tajmera).Dla 11,0592MHz ładujemy 0x0DC do części starszej i nic do młodszej, w przypadku rezonatora 12MHz trzeba
załadować do liczników wartości 0x0D8 do części starszej i 0x0F0 do młodszej. I tu mamy problem. Nie wiemy ile
cykli maszynowych potrwa przyjęcie przerwania (procesor może akurat obsługiwać przerwanie o wyższym
priorytecie), a licznik tajmera ciągle liczy... OK., procesor już przyjął przerwanie i zaczyna jego obsługę, na początku
powinno nastąpić ponowne załadowanie wartości początkowych do obydwóch połówek licznika czyli (12MHz) 0x0D8
do starszej i 0x0F0 do młodszej... I tu ZONK... Bo my owszem możemy je tam władować ale jest pewne, że młodsza
część licznika już zawiera wartość zliczoną od momentu przepełnienia licznika do teraz. I co z tym fantem zrobić?
Trzeba by dodać to co chcemy załadować do tego co już tam jest. Niby to nic skomplikowanego ale jeżeli to nam
przekroczy 255? Znowu trzeba by kontrolować czy starszej części też nie trzeba dodawać... A już szczytem pecha
byłaby sytuacja kiedy podczas dodawania młodszej części licznika nastąpiłoby jego przepełnienie, bo dodawanie też
przecież zajmuje cykle maszynowe... No i taki błąd byłby już całkiem spory bo chodziło by o kilkaset µs.
A dla kwarcu 11,0592 jak to wygląda? Bardzo prosto;-)))). Ładujemy do starszej połówki licznika 0x0DC, a młodszej
nie ruszamy... Bo i tak licznik miał zliczać od zera w części młodszej;-)))). Ktoś może tu powiedzieć np. no dobra, a jak
już naliczył do 255? No to pech. Kto pisze programy obsługi przerwań takie, żeby przyjęcie przerwania trwało 255
cykli maszynowych? W praktyce jeżeli jest dobrze napisany program jest to sytuacja wręcz niespotykana...
Jeszcze jedna uwaga. Częstotliwość 11,0592MHz umożliwia też łatwe dobranie współczynników przy transmisji
szeregowej;-))))). Aby uzyskać szybkość transmisji 38.4kbps należy użyć rezonatora 7,372800MHz.

P: Jak dobierać wartości początkowe dla tajmerów przy transmisji szeregowej?
O:
W trybach 1 i 3 portu szeregowego prędkość transmisji można ustalać programowo, poprzez regulowanie
częstotliwości wywoływania przerwania tajmera (zakładam tajmer T1). Najwygodniej używać licznika w trybie 2
(licznik ośmiobitowy z automatycznym ładowaniem przy przepełnieniu z TH1). Częstotliwość przepełnienia dana jest
zależnością: fp = fx / (12 * (256 – TH1), gdzie fp – częstotliwość przepełnienia, fx – częstotliwość sygnału
zegarowego, a TH1 Wartość wpisana do TH1;-)))))).
W takim trybie pracy licznika nie ma potrzeby obsługiwania go programowo, wystarczy na początku wpisać daną
wartość do TH1 i uruchomić zliczanie, reszta dzieje się automatycznie. Kilka typowych wartości początkowych dla
TH1 zebrano w tabelce (fx = 11.0592MHz):

Szybkość transmisji

600

1200

2400

4800

9600

19200

Wartość w TH1

D0

E8

F4

FA

FD

FD

SMOD

(PCON.7)

0

0 0 0 0 1

Dla 9600 i 19200 wartość jest ta sama, bo szybkość jest podwojona poprzez wpisanie 1 do SMOD.

P: Piszę program w asemblerze na wzór pliku przykładowego. Co oznaczają instrukcje SEGMENT, CODE,
DATA, BIT, RSEG i CSEG używane w tych plikach?
O:
Po kolei. Program konsolidujący musi wiedzieć, co ma umieścić w pamięci danych, co w pamięci programu i w
którym miejscu (ważne np. dla wektorów przerwań). W/w instrukcje mogą być używane w różny sposób (różna
składnia) w różnych programach (zajrzyj do help’a). Generalnie:
SEGMENT jest deklaracją jakiegoś segmentu w pliku źródłowym: twoja_nazwa SEGMENT typ_pamięci parametry,
oczywiście nazwę możesz sobie wpisać dowolną, jako typ pamięci można zazwyczaj zapodać DATA, CODE, BIT,
IDATA, XDATA, co oznacza kolejno: pamięć danych (wewnętrzną, adresowaną bezpośrednio (0..127)), programu,
przestrzeń adresowaną bitowo, wewnętrzną pamięć danych (adresowaną pośrednio (0..255 w procesorach 8xC52)) i

background image

3

Copyright by tomasz baszczok

http://www.tdv.cad.pl

zewnętrzną pamięć danych. Podane dane mogą się nieco różnić dla różnych programów. Parametry zazwyczaj się
pomija, są przydatne w szczególnych okolicznościach.
RSEG (RSEG nazwa_segmentu) to oznaczenie, że następujący po nim fragment kodu lub dane) ma być umieszczony w
odpowiednim segmencie, oczywiście wcześniej zadeklarowanym. RSEG oznacz, że kod (lub dane) są relokowalne.
CSEG (CSEG AT addr) oznacza, że następujący niżej kod ma być umieszczony w pamięci programu (CSEG od
CodeSEGment) począwszy od podanego adresu (addr). Przy czym addr musi dać się rozwinąć do poprawnego adresu w
pamięci programu.

P: Po wejściu do podprogramu chcę odłożyć na stosie akumulator, ale przy kompilacji zgłasza mi się błąd.
O:
Chyba wszystkie asemblery przyjmują w kodzie odwołanie do akumulatora poprzez wywołanie nazwy A. Instrukcje
PUSH i POP nie przyjmują tej składni. Użyj PUSH ACC i POP ACC. Powinno zadziałać.

P: Co oznacza znak $ np. w instrukcji DJNZ R0,$?
O:
Znak $ oznacza bieżący adres programu. W przykładzie powyższym znaczy tyle, że procesor po dekrementacji R0,
jeżeli R0 nie jest równe 0 ma skoczyć pod ten sam adres, czyli wykonać po raz kolejny instrukcję DJNZ R0,$.
Np. AJMP $; to pętla nieskończona;-))))))).

P: Po co są dublowane deklaracje nazw rejestrów pomocniczych np. R0 i AR0?
O:
Podobnie jak wyżej, R0 nie da się odłożyć na stosie, AR0 powinno się dać.

P: W procesorze jest kilka banków rejestrów pomocniczych Rx, jak się pomiędzy nimi przełączać?
O:
Do zmiany pliku rejestrów pomocniczych używanych służą dwa bity w słowie statusu RS0 i RS1. Jednak przy
używaniu w programie więcej niż jednego pliku trzeba być ostrożnym.

P: Używam procesora 87C52, ma on 256 bajtów pamięci danych, w jaki sposób używać tych dodatkowych 128
bajtów?
O:
Wyższe 128 bajtów jest umieszczone w tej samej przestrzeni adresowej co rejestry specjalne. Asembler rozróżnia
czy chcemy korzystać z obszaru pamięci czy rejestrów dzięki innym trybom adresowania. Dostęp do rejestrów
specjalnych jest możliwy tylko poprzez adresowanie bezpośrednie, natomiast do pamięci poprzez adresowanie
pośrednie. Wykorzystuje się do tego celu rejestry pomocnicze R0 i R1.

P: Jak dekrementować wskaźnik DPTR?
O:
Nijak. Nie przewidziano do tego celu instrukcji. Zawartość DPTR’a można zmieniać jedynie instrukcjami MOV i
INC. Jeżeli chcesz jego zawartość zmniejszyć musisz do tego celu użyć kilku instrukcji. W sieci znalazłem kiedyś coś
takiego: XCH A,DPL ; JNZ $+4 ; DEC DPH ; DEC A ; XCH A,DPL;

P: Czy mogę pisać programy w języku wysokiego poziomu?
O:
Możesz. Pod warunkiem, że dysponujesz odpowiednim kompilatorem. Z tego co wiem, są kompilatory większości
popularnych języków programowania dla µC ’51.

P: Czy program napisany w C będzie wystarczająco sprawny?
O:
W zdecydowanej większości tak. Aktualne kompilatory mają możliwość optymalizacji kodu programu pod kątem
szybkości wykonywania lub zajmowanego miejsca. Zazwyczaj optymalizacja jest wielopoziomowa, więc ma się spore
możliwości. W ostateczności proste procedury (np. obsługa przerwania zewnętrznego przyjmująca dane z
przetwornika) można napisać w asemblerze. Choć wcale nie jest pewne, czy nasza „wizja” tej procedury będzie szybsza
od tej napisanej w C i ułożonej przez kompilator.

P: Piszę program w C. Przechodzi symulację, ale procesor milczy...
O:
A to pech... Zacznij od sprawdzenia pliku hex. Kompilatory (właściwie linkery) C mają to do siebie, że nie
koniecznie układają dane w pliku hex według rosnących adresów. Programator może (choć nie musi) mieć z tym
problemy... i wychodzi ZONK. W intelHex adresy są umieszczane jako 3, 4, 5 i 6 cyfra i według tych adresów powinny
być posegregowane sekcje w pliku (zazwyczaj chodzi o 2 – 3 sekcje).

P: Jak deklarować zmienne, żeby zajmowały po 1 bajcie? (int zabiera dwa).
O:
Unsigned char, lub char i włączyć odpowiednią opcję w kompilatorze żeby jego zakres był 0..255.

P: Jak napisać procedurę obsługi przerwania w C?
O:
Przykład: void int0_servis (void) interrupt 0 {/*twoja procedura*/}
Lub void to_servis (void) interrupt 1 {/*twoja procedura*/}; cała kabała w tym interrupt i numer przerwania, resztę robi
kompilator;-))))))).

P: W jaki sposób deklaruje się stałe i zmienne w asemblerze (bit, 8 bit i 16 bit)?

background image

4

Copyright by tomasz baszczok

http://www.tdv.cad.pl

O: Zacznijmy od tych zmiennych 16 bitowych.
uC '51 jest w zasadzie 8 bitowy i jako taki nie ma możliwości operowania na wartościach 16 bitowych. Wszelkie takie
operacje trzeba robić ręcznie, tzn. 16 bitowe zmienne trzeba składać z dwóch 8 bitowych, i na takich zmiennych trzeba
operować. Jest to dosyć żmudne, aczkolwiek nie jest trudne, trzeba tylko panować nad tym co się robi.

Deklaracje stałych:
Konstrukcja wygląda następująco:
Symbol

EQU

wartość

Gdzie symbol to Twoja nazwa dla zmiennej, EQU to dyrektywa asemblera, a w miejscu wartość wstawiasz liczbę. Przy
czym jako wartość można wstawić też nazwę rejestru (np. R5). Przy kompilacji asembler zastępuje wszędzie symbol
wartością zadeklarowaną przez Ciebie. Raz zadeklarowanej wartości nie można zmienić.
Przykład:
Year

EQU

98

W każdym miejscu programu gdzie wystąpi Year, będzie przy kompilacji użyta wartość 98.

Zmienne 8 bitowe:
Nazwa:

DS

wyrażenie

Gdzie Nazwa to nazwa, DS dyrektywa asemblera, a wyrażenie to ilość rezerwowanych bajtów.
Przykład:
Czas:

DS

1

Czyli rezerwujesz 1 bajt na zmienna czas.

Zmienne bitowe:
Nazwa:

DBIT

wyrażenie

Znaczenie poszczególnych oznaczeń podobne jak poprzednio.
Wszystkie te dyrektywy dotyczą rezerwacji pamięci w bieżącym segmencie.
Poniżej przykład deklaracji w pliku programu:

STACK

SEGMENT

IDATA

PROGRAM SEGMENT

CODE

VAR

SEGMENT

DATA

BITVAR

SEGMENT

BIT

T0_H

EQU

03Ch

T0_L

EQU

0B0h

T1_H

EQU

0ECh

T1_L

EQU

078h

ENABLE

EQU

0B0H.0

RW

EQU

0B0H.1

RS

EQU

0B0H.7

R7AD

EQU

007H

R6AD

EQU

006H

R5AD

EQU

005H

R4AD

EQU

004H

R3AD

EQU

003H

R2AD

EQU

002H

R1AD

EQU

001H

R0AD

EQU

000H

RSEG

VAR

CZ_WYM:

DS

8

CZ_BIEZ:

DS

8

KEYS:

DS

1

T_KEYS: DS

1

CZ_STAT:

DS

1

CZ_TMP:

DS

1

STATUS: DS

1

REFRESH:

DS

1

D_SEK:

DS

1

TIME:

DS

1

background image

5

Copyright by tomasz baszczok

http://www.tdv.cad.pl

TMP:

DS

1

AL_NR:

DS

1

RSEG

BITVAR

TMP_F:

DBIT

1

ALARM: DBIT

1

ACTIV:

DBIT

1

S_BY:

DBIT

1

RSEG

STACK

DS 020H


CSEG

AT 00H

AJMP

INIT

CSEG

AT

0BH

AJMP

T0_SERV

CSEG

AT

1BH

AJMP

T1_SERV

RSEG

PROGRAM

ORG

02CH

INIT:

;inicjalizacja procesora i dalszy ciąg programu.


Ten fragment pochodzi z programu działającego urządzenia.


Wyszukiwarka

Podobne podstrony:
fras,systemy wbudowane L, Wstęp do mikrokontrolerów rodziny MCS 51
DataSheet MCS 51 Instruction Intel
Schematic Mcs 51
Asembler dla MCS 51
51 Wypowiedzenie zmieniające
2009 06 15 21;42;51
49 51
Document (51)
51 Kodeks Etyki Sluzby Cywilnej
Conan 51 Conan Pan czarnej rzeki
Cw 10 (51) Pomiar ładunku właściwego e m elektronu
51 54
51
Najpierw przeczytaj FAQ Internetowe grupy dyskusyjne jako środowisko interakcyjne
51 54
11 2003 51 52
51 07 BW Gospodarka wodna
4 4 1 faq dvbpctvstars

więcej podobnych podstron