Metodické materiály k předmětu MIMP Jednočipový mikropočítač Intel 8051 |
© 1997 Ing. Petr Weissar ZČU, FEL-KAE |
Jednočipový mikropočítač Intel 8051 je základem celé rodiny procesorů kompatibilních jak po stránce instrukční sady, tak po stránce vývodové a signálové. Lepší a výkonnější typy obsahují lépe vybavené či přidané periférie, větší i rychlejší paměť apod., ale programování a práce s obvodem zůstává zachována. Proto stačí se naučit pracovat se základním modelem a znát pak další rozšíření kompatibilních typů.
Zde jsou uvedeny informace potřebné ke psaní programů pro 8051 s nezbytným minimem hardwarové stránky. Podrobnější informace lze v ucelené míře získat na přednáškách MIMP (doc. Pinker).
Další potřebné informace lze nalézt:
Při cvičeních předmětu MIMP se 8051 programuje v jazyce C, jehož znalost je tedy nezbytností. Používaný překladač C51 firmy Keil se drží standardu ANSI C se specifickými rozšířeními pro řadu 8051. Používaná verze 3.2 obsahuje kompilátor C, assembler, linker a některé pomocné “prográmky”. Na počítačích v laboratoři MIMP je k dispozici dávka, které se předloží zdrojový text v “C” a výsledkem jsou buď hlášení o chybách nebo program, který je možno nahrát do vývojové desky s 8051. Ten se může naprogramovat do paměti EEPROM/EPROM a vložit do patice na desce nebo poslat po sériové lince do paměti RAM vývojové desky a tuto paměť pak přepnout jako paměť programu. Toto řeší program monitor, který dokáže pracovat na upravené vývojové desce (jedna z diplomových prací na KAE).
Obsah překládací dávky C51.BAT je možno diskutovat s vyučujícím. “Cesta” zdrojového textu programu pak vypadá asi následovně:
obrázek .....
Pro zjednodušení práce se v laboratoři MIMP používá vývojové prostředí Borland C++ 3.1, ve kterém je možné pohodlně psát programy i s pomocí např. “Syntax Highlighting”. Po vyvolání externího překladače jsou do prostředí vráceny informace o překladu, chybách apod. Je možné samozřejmě používat i jiné vývojové prostředí.
Příklad 1 čítač na bráně P1:
#include <reg51.h> // tento hlavickovy soubor obsahuje
// definici jmen specialnich funkcnich
// registru SFR
int main (void)
{
int i; // pomocna promenna
P1=0; // nastav hodnotu na branu P1
while(1) // nekonecna smycka
{
P1++; // inkrementuj hodnotu na brane,
// odpovidajici registr je pro cteni/zapis
// a chova se jako unsigned char
for(i=0;i<8000;i++) // cekaci smycka pro vizualni kontrolu
;
}
return 0; // doporuceno i v pripade nekonecne smycky
// jinak prekladac muze dat varovani ...
}
Je nezbytně nutné především “includovat” soubor REG51.H, který
obsahuje deklarace jmen speciálních funkčních registrů (SFR). Tyto registry
řídí činnost jednotlivých součástí mikropočítače. Zpravidla jsou přístupné
jako bajtová hodnota, v jazyce C je to pak chápáno jako unsigned char
.
Některé registry jsou přístupné pro čtení i zápis, jiné pouze pro čtení nebo
pro zápis. To je podrobně popsáno v dokumentaci k 8051, používané SFR se
proberou i zde. Některé registry jsou přístupné i po jednotlivých bitech, jejich
jména též obsahuje REG51.H a shodují se s názvy podle Intel-dokumentace.
Uvedený příklad je triviální, tvoří 8-bitový čítač na bráně P1. Základní typ 8051 má celkem 4 brány, P0 - P3. Všechny jsou 8-bitové vstupně/výstupní, ale brány P0 a P2 jsou použity k připojení vnější paměti programu a dat, brána P3 je sdílena s některými speciálními vývody (přerušení, čítač/časovač apod.). Zbývá tedy brána P1. Její vývody jsou vyvedeny na konektor vývojové desky. Vizuální kontrolu by bylo možné provést např. připojením LED diod na výstupy brány P1.
Používaný překladač C51 umožňuje psát poznámky do kódu ohraničené nejen /*
... */
, ale i podle stylu C++ označit jako poznámku cokoliv od značky //
do konce řádku. Dále je důležité si uvědomit, že komentářové závorky /*
... */
nelze vnořovat.
Příklad 2 využití interního čítače
#include <reg51.h> // deklarace registru SFR
#define TH_1MS (65536-1000)/256 // definice hodnot
#define TL_1MS (65536-1000)%256
int main(void)
{
int i=0; // promenna pomocneho citace
TMOD=0x01; // nastaveni rezimu citace CT0
TH0=TH_1MS; // nastaveni pocatecniho stavu
TL0=TL_1MS;
TR0=1; // spusteni citace CT0
while(1)
{
while(!TF0) // dokud citac nedopocita
;
TF0=0; // vynuluj priznak pro pristi pruchod
TH0=TH_1MS; // nastavit hodnotu do CT0
TL0=TL_1MS;
i++; // pomocny citac
if(i>100) // 100 * 1000µ s = 0.1 vteriny
{
i=0;
P1<<=1; // posun vlevo
if(!P1) // je-li 0 (“vysunuto”)
P1=1; // zacni na 0000 0001
}
}
}
Mikroprocesor 8051 obsahuje 2 stejné nezávislé 16-bitové čítače. CT0
je všeobecně použitelný, CT1 je primárně určen ke generování
přenosové rychlosti sériového přenosu, ale pokud není potřeba komunikovat, je také
volný. Čítače mohou pracovat ve 4 režimech, které se nastavují v registru TMOD.
Dolní nibble patří CT0, horní pak CT1. Dolní 2 bity
určují režim čítače. Zde se používá režim 1, což je plně 16-bitový čítač.
Čítače počítají nahoru a při přetečení 0xFFFF -> 0x0000
nastaví
příznak TFx a mohou vyvolat přerušení. V režimu čítače je na
vstup čítačů připojena hodinová frekvence dělená 12, což je pro krystal 12 MHz
přesně 1 MHz. Registry čítače jsou označeny THx (vyšší bajt) a TLx.
Pokud se tedy má za 1 ms provést přetečení, je třeba nastavit hodnotu -1000
(viz program). Ještě je nutno čítač spustit, což řídí bit TRx.
V hlavní smyčce programu se čeká na přetečení čítače (příznak TF0,
pak se čítač znovu naplní, aby mohl znovu počítat. Protože nejdelší interval,
který lze při krystalu 12 MHz změřit, je 65.5 ms, je použito čítání milisekund a
čas je pak prodloužen ještě čítačem programovým (proměnná i
).
Každých 100 ms se proto “posune” rozsvícená LED v řadě 8 diod připojených na
bránu P1.
Mezi přetečením a znovunaplněním čítače uplyne nějaká doba (záleží na konkrétním překladu kódu), proto měřený interval není přesně 1ms. V časově kritické aplikaci by to bylo třeba zohlednit. Druhou možností by bylo využít automatického znovunaplnění čítače (režim 2), kdy lze ale čítač použít pouze jako 8-bitový, což omezuje nastavení intervalu na max. 256µs.
Příklad 3 využití přerušení
#include <reg51.h>
#define T_100US (256-100)
int main(void)
{
TMOD=0x02; // nastav rezim 2 citace CT0
ET0=1; // povol preruseni od CT0
EA=1; // globalne povol zpracovani preruseni
TH0=T_100US; // nastav hodnotu pro reload
TR0=1; // spust citac
while(1) // jen se ceka na provadeni interruptu
;
}
void timer0(void) interrupt 1 // obsluha preruseni
{
static unsigned int pomi=0; // programove prodlouzeni citace
// MUSÍ byt typu STATIC !!
if (P1==0x20) // pokud je nastaven bit 5
T0=~T0; // 5 kHz, strida 1:1
pomi++;
if(pomi<1000) // 1000 * 100µ s = 100 ms
return; // není-li, tak konec obsluhy intu
pomi=0;
P1<<=1; // opet “pohybliva” log. 1 na P1
if(!P1)
P1=1;
return;
}
Tento příklad poskytuje stejný efekt, jako příklad 2, navíc využívá piezo-měniče připojeného na pin T0 (je to součást multifunkční brány P3). Vytváří signál s půlperiodou = 100µ s, tedy 5 kHz, což je blízké rezonačnímu kmitočtu měniče. Zvuk se ozývá tehdy, když je aktivní dioda na bitu 5 brány P1.
Pro využití přerušení je nutno provést několik kroků: povolit akceptování
daného přerušení (zde bit ET0, každý zdroj přerušení má “svůj” bit),
povolit přerušení globálně (bit EA), spustit čítač (bit TR0) a
hlavně napsat obsluhu přerušení. Je to funkce typu void
bez parametrů a
překladači je třeba říci klíčovým slovem interrupt
, pro které
přerušení je obsluha určena (přerušení 1 je od CT0). Překladač sám
řeší úklid proměnných apod.
Režim čítače je 8-bitový s automatickým reloadem (režim 2), čítá se v registru TLx, hodnota pro reload je v THx. Nedochází zde k žádné prodlevě, neboť reload je proveden přímo obvodem čítače okamžitě po přetečení. Ještě je třeba poznamenat, že obsluhou přerušení se nuluje příznak přetečení TFx, což bylo nutno provést “ručně” při programové obsluze v příkladu 2.
Příklad 4 připojení DA převodníku
#include <reg51.h>
int main(void)
{
P1=0;
while(1)
{
while(P1<0xFE)
P1+=2;
while(P1)
P1-=1;
}
}
Tento jednoduchý příklad předpokládá připojení DA převodníku na bránu P1
(LSB převodníku odpovídá bitu 0 brány, MSB bitu 7). Generuje se trojúhelníkový
průběh s náběžnou hranou dvojnásobně strmou než doběžnou (dvojí inkrement
proti jednomu dekrementu). Pozor na podmínku 1. cyklu while
, pokud by bylo <0xFF
,
podmínka se nikdy nesplní, neboť P1 je jako datový typ unsigned char
,
který přetéká 0xFF -> 0x00
. Nelze ani testovat na přetečení (==0
),
pak by vznikl nechtěný zákmit do hodnoty 0. Řešením by zřejmě mohlo být použití
cyklu do - while
. Kmitočet výsledného analogového signálu závisí na
překladu kódu, pro přesné časování je opět vhodné použít čítač.
Příklad 5 sériová komunikace
#include<reg51.h>
#include<stdio.h> // navíc funkce vstupne/vystupni
int main (void)
{
char a;
SCON=0x52; // nastaveni registru seriove komunikace
TMOD=0x20; // nastaveni rezimu citace CT1
TR1=1; // spust citac CT1
TH1=0xF3; // prenosova rychlost 2400 Bd
puts("AHOJ"); // posli retezec
while(1)
{
a=_getkey(); // precti znak (ceka se na nej)
P1=a; // pro kontrolu na branu P1
putchar(a); // a posli zpet
}
}
V tomto příkladu se vypíše po sériové lince úvodní pozdrav a pak se jen přijaté znaky posílají zpět. Standardní funkce vstupu a výstupu deklarované v stdio.h pracují se sériovým kanálem. Je proto nutné použít nějaký terminál (resp. terminálový program na PC), který ukazuje, co se na linku poslalo a odesílá na ní znaky podle stisku kláves. Pro přehlednost je v terminálu vhodné si vypnout lokální echo, jinak se ukazují znaky odchozí i příchozí.
V registru SCON se nastavují parametry sériové komunikace, zde 8-bitový
asynchronní přenos. Výše již bylo řečeno, že čítač CT1 generuje
přenosovou rychlost, proto je nutné ho nastavit do módu 2 (8-bitový reload) a vložit
hodnotu 0xF3
pro rychlost komunikace 2400 Bd. Nesmíme zapomenout čítač CT1
spustit. Pak se pošle řetězec funkcí puts(...)
a ve smyčce se
přijímají znaky z linky a posílají okamžitě zpět. Funkce typicky čekají na
příjem/odeslání znaku. Pro příjem je použita funkce _getkey()
,
protože standardní funkce getchar()
provádí “echování” přijatého
znaku a _getkey()
nikoliv.
Od příjmu i vyslání znaku lze vyvolat přerušení, standardní obsluha toho však nevyužívá, pouze testuje příznakové bity RI (přijat znak) a TI (odeslán znak).
Příklad 6 připojení LCD displeje
#include <reg51.h>
sbit EN=P3^3; // ridici bit displeje (zapis)
sbit RS=P3^5; // ridici bit, registr/data
void sendToDisp(char c, bit reg) // posli data do displeje
{
int i; // promenna pro citac
RS=reg; // pin RS (registr/data)
P1=c; // data na P1
EN=1; // zapisovy puls hrana L-H
EN=0; // hrana H-L
if((reg==0)&&((c&0xFC)==0x00)) // podle druhu dat
for(i=0;i<500;i++) // dlouhe cekani
;
else
for(i=0;i<30;i++) // kratke cekani
;
return;
}
int main(void)
{
char *cptr,*s="Ahoj DISPLEJ";
EN=0; // prvotni nastaveni rizeni LCD
sendToDisp(0x01,0); // Display Clear
sendToDisp(0x06,0); // Entry Mode Set
sendToDisp(0x0f,0); // Display, Cursor, Blinking On/Off
sendToDisp(0x38,0); // Set HW configuration
sendToDisp(0x80+0,0); // nastav pozici kurzoru na 0
for (cptr=s;*cptr;cptr++) // po znacich posli retezec
sendToDisp(*cptr,1); // jako data
while(1)
;
}
Na vývojových deskách s 8051 je připraven konektor pro LCD displej 2 řádky po 16
znacích (typ LM 16255). Datové signály displeje jsou připojeny k bráně P1,
řídící signály pak na bity brány P3. Stačí 2 řídící signály, RS
určuje, zda se bude jednat o operaci s daty (RS=1) nebo o operaci s řídícími
registry (RS=0). Další řídící signál je EN, kdy EN=1 určuje
aktivní data. Signál R/W, který určuje, zda jde o zápis nebo čtení, je
nastaven trvale na zápis, takže z displeje nelze číst. To sice trochu vadí
v kontrole, zda je požadovaná operace dokončena (příznak BUSY), ale protože
jsou známo nejdelší doby provedení operací, je možné čekat určitý čas
programově. Téměř všechny operace trvají 40µs, pouze operace inicializační
(povely 0x01
a 0x02
) trvají 1.64 ms. To je zohledněno ve
funkci sendToDisp(...)
, kde dobu čekání určuje podmínka. Takže pro
zápis povelu (reg==0)
hodnoty 0x01
a 0x02
je
použito delší čekání. Hodnoty jsou odhadnuty empiricky tak, aby dostačovaly pro
krystal 12 MHz. Podmínka by mohla srozumitelněji vypadat: if ((reg==0) &&
((c==1)||(c==2)))
...
V hlavním programu se pak musí nejprve inicializovat displej, aby vůbec zobrazoval.
Doporučuji použít tyto hodnoty, jinak je samozřejmě možno si je upravit v souladu
s dokumentací. Zápis pozice kurzoru má v povelu nastaven nejvyšší bit (D7),
ostatní bity určují adresu v paměti. První řádek začíná na adrese 0x00
,
druhý na adrese 0x40
. Navíc je displej vybaven kromě vlastního
generátoru znaků i možností uživatelsky definovaných znaků (tzv. CG RAM).
Speciální datový typ sbit je možností, jak elegantně pracovat s jednotlivými bity registrů, které tuto bitovou adresaci umožňují. Je to datový typ ekvivalentní typ bit, pouze je pevně svázán se “svým” registrem (zde P3) a pozicí v něm (bit 3 a 5).