Buscar

programacao do 8051 em assember

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 59 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 59 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 59 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

Programação do Microcontrolador i8051 
 em Assembly 
 
 
 
 
 
 
Departamento de Engenharia Electrotécnica do Instituto Superior de Engenharia de Coimbra 
 
 
Material de Apoio para a disciplina de Microprocessadores 
Realizado por Fernanda Madureira Coutinho 
 
 1
Índice 
1 - INTRODUÇÃO 3 
2 - INTRODUÇÃO AO ASSEMBLY E COMPILADORES 4 
2.1 – Introdução 4 
2.2 - Programação em assembly versus programação em alto nível 4 
2.3 - Assembler e compiladores 5 
3 - REGISTOS 8051 6 
3.1 – Introdução 6 
3.2 - Registos Genéricos 6 
3.3 - Registos de Função Específica (SFR) 6 
4 - DESCRIÇÃO DOS MODOS DE ENDEREÇAMENTO 8 
4.1 – Introdução 8 
4.2 - Endereçamento com valor imediato 8 
4.3 - Endereçamento directo 8 
4.4 - Endereçamento indirecto 8 
4.5 - Endereçamento indirecto indexado 9 
5 - OPERAÇÕES BÁSICAS EM ASSEMBLY 10 
5.1 - MOV – MOVe (instrução de atribuição) 10 
5.2 - Instruções aritméticas 10 
5.3 – Instruções para teste da flag de carry 12 
5.4 - Operações sobre bits 12 
5.5 - Operações no espaço de dados externo 12 
5.6 - Operações no espaço de código 13 
6 - ESTRUTURAS DE CONTROLO DE FLUXO DE EXECUÇÃO 15 
6.1 – Introdução 15 
6.2 - Instruções de salto incondicional 15 
6.3 - Instruções de salto condicional aplicadas a bit 16 
 2
6.4 - Instruções de salto condicional aplicadas a registos 17 
6.5 - Transferência de dados entre espaços de endereçamento de dados 20 
6.5.1 - Memória interna/memória interna 20 
6.5.2 - Memória externa/interna 20 
6.5.3 - Memória interna/externa 20 
7 - SUBROTINAS 22 
7.1 – Introdução 22 
7.2 - Chamada e retorno de subrotinas (A/LCALL, RET) 22 
7.3 - Pilha 22 
8 - TEMPORIZADORES 25 
8.1 - Descrição do temporizador e seus SFRs e modos de funcionamento 25 
8.2 - Programação dos SFRs do temporizador 25 
8.3 - Modos de operação 27 
8.4 - Exemplos 28 
9 - CANAL SÉRIE 37 
9.1 – Introdução 37 
9.2 - Registos de Função Específica 37 
9.3 - Modos de funcionamento 38 
9.4 - Exemplos 40 
10 - PROCESSAMENTO DE INTERRUPÇÕES 43 
10.1 - Introdução 43 
10.2 - Tabela de Vectores de Interrupção 44 
10.3 - Registos de Função Específica 45 
10.4 - Interrupção externas 47 
10.5 - Pooling vs interrupções 48 
10.6 – Exemplos 50 
11 - SISTEMAS MULTIPROCESSADOR BASEADOS EM 8051´S 53 
11.1 – Introdução 53 
11.2 - Arquitectura Mestre/Escravo 53 
 
 3
 
 
Capítulo 1 
Introdução 
 
A família MCS-51 é uma família de microcontroladores desenvolvida originalmente pela 
Intel em 1980. Devido ao sucesso que estes µCs tiveram no mercado, actualmente existem 
várias empresas que desenvolvem e comercializam microcontroladores desta família. 
O 8051 foi o primeiro microcontrolador (µC) da família MCS-51 a ser produzido e 
comercializado, sendo por isso o µC de referência desta família. 
As principais características do µC 8051 são as seguintes: 
8 Espaço de Dados Interno (00h-FFh) 
RAM (00-7Fh = 128 bytes) 
(00h - 1Fh) 4 Bancos de Registos = 4x (R0,R1,R2,R3,R4,R5,R6,R7). 
(20h - 2Fh) 210 bits endereçáveis individualmente. 
(30h – 7Fh)128 Bytes de RAM interna. 
Registos de Função Específica (80h-FFh) 
8 Espaço de Código Interno (00h - 1000h) 
ROM (00-1000h = 4 Kbytes) 
8 Espaço de Dados Externo (0000h-FFFFh) 
64K de espaço de dados externo 
8 Espaço de Código Externo (0000h-FFFFh) 
64K de espaço de código externo 
8 Buses: 
Bus de dados de 8 bits 
Bus de endereço de 16 bits 
8 Interface série 
8 2 Temporizadores de 16 bits cada 
8 4 Portos bidireccionais 
Os outros microcontroladores da família MCS-51 apresentam algumas variações, tais 
como o tamanho de ROM e RAM interna e a existência de temporizadores adicionais, 
EPROM interna ou periféricos adicionais. 
 4
 
 
Capítulo 2 
Introdução ao Assembly e Compiladores 
 
 
2.1 – Introdução 
 
Todos os µC/µP funcionam através da execução de programas que consistem num 
conjunto de instruções atómicas. O conjunto de todas estas instruções que um dado µC/µP é 
capaz de executar é a linguagem máquina desse µC/µP. Estas instruções são codificadas e 
guardadas como números inteiros. A linguagem assembly consiste num conjunto de 
mnemónicas (palavras chave) que são associadas a cada uma das instruções de linguagem 
máquina, para simplificar a programação dos µCs e tornar os programas legíveis a um leitor 
humano. Uma característica normal de uma “família” de µC é a utilização de uma linguagem 
assembly comum (ou, pelo menos, a utilização de um subconjunto de instruções assembly 
comum)1. Por exemplo, todos os µCs da família MCS-51 são normalmente capazes de 
executar um programa que funcione no µC 8051 (apesar do inverso poder não ser verdade, 
porque estes podem ter instruções adicionais que não existem no 8051). 
 
2.2 - Programação em assembly versus programação em alto nível 
A linguagem assembly é normalmente designada como sendo uma liguagem de baixo 
nível, porque cada instrução de assembly corresponde exactamente a uma instrução executada 
pelo µC. Por oposição, as linguagens de alto nível (como o C, Pascal, Fortran, PLM-51 e 
BASIC entre outras) são compostas por instruções mais complexas, as quais poderão 
necessitar de múltiplas instruções de código máquina para serem executadas. A decisão de 
programar em baixo nível ou em alto nível é tomada levando em conta o compromisso 
existente entre as características de cada uma das linguagens e a aplicação alvo a que se 
destina: 
 
1 Na verdade, a compatibilidade verifica-se quase sempre ao nível do código máquina, ou seja, à codificação 
utilizada para armazenar cada instrução. 
 5
8 Programação em baixo nível – Permite gerar código mais compacto2 e mais rápido3. 
Podem ser manipuladas todas as as características particulares do µC. O tamanho do 
ficheiro de código fonte é muito superior, porque é necessário discriminar 
exactamente todas as instruções que o µC vai executar. O programa é menos legível, 
porque a função de cada linha de código só se torna perceptível se analisarmos o 
contexto em que se insere. O desenvolvimento do programa é mais complexo, 
demorando mais tempo a desenvolver e estando mais sujeito a erros. 
8 Programação em alto nível – Gera código menos compacto e mais lento. Pode não 
estar disponível o acesso a determinadas características particulares do µC, porque só 
podem ser utilizadas as instruções existentes na linguagem, as quais podem não estar 
preparadas para determinadas tarefas particulares. O tamanho do ficheiro de código 
fonte é menor. O programa é mais legível porque cada instrução tem normalmente um 
significado facilmente perceptível a um leitor humano. O desenvolvimento do 
programa é mais simples, sendo o desenvolvimento mais rápido e estando menos 
sujeito a erros. 
 
2.3 - Assembler e compiladores 
O assembler é um compilador da linguagem assembly. Um compilador é uma ferramenta 
de software que efectua a tradução do código desenvolvido pelo projectista (linguagem de alto 
ou baixo nível) para código máquina. 
Normalmente, a designação “compilador” usa-se quando se está a falar de linguagens de 
alto nível. Para baixo nível, em vez de se falar de compilador de assembly, fala-se 
simplesmente de assembler. No caso de um assembler, a compilação consiste na tradução 
directa das mnemónicas para as instruções de código máquina correspondentes. 
Para cada par “microprocessador – linguagem” existe um compilador específico. Por 
exemplo: se quisermos programar o 8051 na linguagem C teremos de usar um compilador de 
C para o 8051 (ex: c51 da Keil Software); se quisermos programar o 8051 em assembly 
teremos de usar um assembler para o 8051 (ex: ses51 da Scientific Educational SystemsLtd.). 
 
2 Um programa é mais compacto (mais pequeno) do que outro quando o número total de instruções máquina que 
o compõem é menor. 
3 Um programa é mais rápido do que outro quando demora menos tempo a executar. Note que um programa 
maior pode ser mais rápido do que outro mais pequeno, e vice-versa. Note ainda que a velocidade de execução 
não é directamente proporcional ao número de instruções de código máquina executadas, porque instruções 
diferentes podem ter tempos de execução diferentes. 
 6
 
 
Capítulo 3 
Registos 8051 
 
3.1 – Introdução 
Um registo é uma unidade de memória na qual o µC pode guardar valores numéricos 
inteiros. O tamanho de um registo é dado pelo número de bits que o compõem. Por exemplo, 
um registo de 8 bits é capaz de guardar 256 valores distintos (0-255), enquanto que um registo 
de 16 bits é capaz de armazenar 65536 valores distintos (0-65535). No µC 8051, todos os 
registos são mapeados no espaço de dados interno. 
 
3.2 - Registos Genéricos 
O µC 8051 tem 32 registos de utilização genérica, mapeados nas primeiras 32 posições do 
espaço de dados interno (00-1Fh). Estes registos são subdivididos em 4 bancos de 8 registos 
cada (R0,…,R7). Devemos confugurar qual o banco de registos que está a ser utilizado, 
especificação essa que é válida até haver uma nova especificação. A configuração de qual o 
banco de registos que está a ser utilizado num dado instante é feita no registo PSW. Por 
omissão, o 8051 considera que se está a usar o banco 0. 
 
3.3 - Registos de Função Específica (SFR) 
O µC8051 tem 18 registos de 8 bits e 2 registos de 16 bits designados por registos de 
função específica (special function registers - SFRs). Alguns destes registos são endereçáveis 
bit a bit. 
Em seguida vamos descrever alguns desses registos, nomeadamente os registos dos portos 
(P0, P1, P2 e P3), o acumulador (A), o contador de programa (PC) e o DPTR (data pointer). 
 
8 Portos Px, com x={0,1,2,3} 
 
Px.7 Px.6 Px.5 Px.4 Px.3 Px.2 Px.1 Px.0 
 
 7
Cada um dos portos está associado a um conjunto de 8 pinos do microcontrolador. Um 
programa pode receber ou enviar dados para o exterior através da leitura ou escrita, 
respectivamente, em cada um dos registos de portos (P0, P1, P2, P3 e P4). 
Cada um destes registos é endereçável bit a bit. Cada bit corresponde ao mapeamento 
do respectivo pino do respectivo porto. Por exemplo, se quisermos ler o estado do 4º 
pino do porto 1, bastará aceder e ler o bit P1.3. 
Todos os portos permitem o Input/Output de dados (interface com periféricos 
externos). No entanto, todos os portos, excepto o porto 1, podem ser eventualmente 
utilizados para outras funções pré-definidas. No caso de ser utilizada memória externa, 
o porto P0 serve para multiplexar o bus de dados com o bus de endereços (8 bits menos 
significativos). Quando a necessidade de memória externa é superior a 256 bytes, o 
porto P2 assegura o bus de endereços para os 8 bits mais significativos. Os pinos do 
porto 3 englobam um leque vasto de funções desde os pinos para comunicação série, 
pinos de controlo para leitura e escrita externa, entre outros. 
 
8 Registo A (acumulador) 
O registo A é um registo não endereçável bit a bit, sendo utilizado por muitas instruções 
(aritméticas, lógicas ou de transferência de dados) para guardar o resultado das suas 
operações. 
 
8 PC (Program Counter) 
O registo PC é um registo de 16 bits que indica o endereço em memória da instrução 
que vai ser executada. O seu valor vai sendo automaticamente actualizado (pelo 
processador) à medida que as instruções vão sendo executadas. Não deve nunca ser 
alterado pelo programador. 
 
8 DPTR (Data Pointer) 
O registo DPTR é um registo de 16 bits que deve ser usado unicamente no acesso ao 
espaço externo de dados ou de código. É no DPTR que deve estar o endereço externo 
(16 bits) ao qual se pretende aceder. 
 8
 
Capítulo 4 
Descrição dos Modos de Endereçamento 
 
 
4.1 – Introdução 
Os modos de endereçamento são as várias formas através das quais podem ser 
especificados os argumentos das instruções de código máquina. 
Segue-se uma descrição sucinta dos modos de endereçamento disponíveis no µC 8051. 
 
4.2 - Endereçamento com valor imediato 
Neste caso, os dados, precedidos por um ‘#’, são disponibilizados imediatamente para a 
execução da instrução. Estes dados não podem exceder o tamanho de 8 bits. 
 
Exemplo 
 ADD A,#55h ;Soma ao conteúdo do acumulador o valor 55 hex. 
MOV R0,#20 ;Carrega o registo R0 com o valor 20 decimal. 
 
4.3 - Endereçamento directo 
Este tipo de endereçamento é utilizado para aceder a variáveis da memória interna ou a 
registos de hardware quando o seu endereço é conhecido e constante. 
 
Exemplo 
MOV P0,A ;Envia pelo porto P0 o conteúdo do acumulador 
MOV A,34h ;Carrega no registo A o conteúdo da célula de 
;memória de endereço 34h 
 
4.4 - Endereçamento indirecto 
Utilizado para aceder a dados localizados no endereço indicado por R0 ou R1 (endereço de 
1 byte). São utilizados os símbolos ‘@R0’ ou ‘@R1’ para indicar que está a ser usado 
endereçamento indirecto com R0 ou R1, respectivamente. 
 
Exemplo: 
 9
MOV R0,#23H 
MOV A,@R0 ;Coloca em A o valor armazenado no endereço 23h 
 
4.5 - Endereçamento indirecto indexado 
Utilizado para aceder ao espaço de endereçamento externo quando o endereço é de 2 bytes. 
O endereço da posição do espaço externo à qual se pretende aceder é carregado previamente 
no registo de 16 bits, DPTR. Os dados são então acedidos utilizando o símbolo ‘@DPTR’. 
Exemplo 
MOV DPTR,#1234h 
MOVX A,@DPTR ;guarda no acumulador o conteúdo do endereço 
;1234h pertencente ao espaço de dados externo 
 10
 
Capítulo 5 
Operações Básicas em Assembly 
 
 
Vão ser descritas algumas instruções de assembly usadas na manipulação de registos, 
nomeadamente operações de transferência de dados e aritméticas. A descrição destas e das 
restantes instruções assembly do 8051 podem ser consultadas no Anexo C o qual faz parte do 
material de apoio da disciplina. 
 
5.1 - MOV – MOVe (instrução de atribuição) 
Esta instrução permite a transferência de dados entre posições de memória e/ou registos. O 
primeiro parâmetro é o destino e o segundo a origem dos dados. 
 
Exemplo 
No endereço de RAM interna 20h está o valor 15h. O valor do endereço 
15h é 10h. Então, as instruções: 
 MOV R2,#20h ;R2Å20h 
MOV A,@R2 ;AÅ15h 
MOV R3,A ;R3Å15h 
MOV B,@R3 ;BÅ10h 
deixam o acumulador com 15h e o registo B com 10h. 
 
Não é permitida a escrita entre dois registos, excepto se um deles for o acumulador. 
 
Exemplo 
MOV R1,R2 deverá ser por exemplo MOV A,R2 
 MOV R1,A 
 
A instrução MOV possui ainda as variantes MOVX e MOVC, explicadas mais à frente. 
 
5.2 - Instruções aritméticas 
Vão ser descritas algumas instruções aritméticas, nomeadamente instruções de adição, 
subtracção, incremento, decremento, divisão e multiplicação. Todas elas actuam sobre o 
 11
acumulador e deixam o resultado nesse mesmo registo. As instruções aritméticas podem 
originar overflows. Quando ocorre um overflow, a flag de carry C é posta a 1. Para verificar se 
ocorreu ou não overflow tem de se analisar qual o estado da flag de carry após a operação 
aritmética, usando para o efeito as instruções de teste da flag de carry (ver mais à frente). 
 
8 ADD – ADDition (adição) 
Exemplo 
MOV A,#03 
ADD A,#55 ;adiciona ao conteúdo do acumulador o valor 55, 
;ficando o acumulador com o valor 58. 
 
8 INC – INCrement (incremento de uma unidade) 
Exemplo 
 MOV A,#03 
INC A ;Incrementa o conteúdo do acumulador de uma 
;unidade,ficando o acumulador com o valor 4. 
 
8 DEC – DECrement (decremento de uma unidade) 
Exemplo 
MOV A,#03 
DEC A ;decrementa de uma unidade o valor armazenado no 
;acumulador, ficando o acumulador com o valor 2. 
 
8 DIV - DIVide (divide o valor de A por B, colocando o quociente em A e o resto em B) 
Exemplo 
MOV A,#31 ;AÅ31 
MOV B,#2 ;BÅ2 
DIV AB ;AÅ15, BÅ1 
 
8 MUL - MULtiply (multiplica o valor de A por B, guarda o byte menos significativo do 
resultado em A e o byte mais significativo do resultado em B. 
Exemplo 
 MOV A,#80 ;AÅ80(50H) 
MOV B,#160 ;BÅ160(0A0H) 
MUL AB ; Resultado de 80*160=12800 (3200H) 
 ;AÅ00h, BÅ32h 
 12
 
5.3 – Instruções para teste da flag de carry 
 
8 JC – Jump if Carry (salta se a flag de carry está a 1) 
8 JNC – Jump if Not Carry (salta se a flag de carry está a 0) 
Estas são as duas únicas instruções que existem para teste da flag de carry. O argumento desta 
instrução é a label de salto, isto é, a label para onde o programa salta no caso de se verificar a 
respectiva condição. 
 JC label_1 ; salta para label_1 se Carry=1 
 JNC label_0 ; salta para label_0 se Carry=0 
 
5.4 - Operações sobre bits 
O valor dos bits endereçáveis individualmente pode ser manipulado directamente. Para 
esse efeito existem duas instruções complementares: SETB (SET Bit - coloca um bit a 1) e 
CLR (CLeaR bit - coloca um bit a 0). Existe ainda a instrução a CPL (ComPLement bit) que 
complementa o estado do bit. 
 
Exemplo 
SETB P1.0 ;Coloca o primeiro pino do porto P1 a 1 (nível de 
 ;tensão Vcc) 
CLR P1.0 ;Coloca o primeiro pino do porto P1 a 0 (nível de 
 ;tensão GND) 
SETB 07H ;Coloca a 1 o bit de RAM interna de endereço 7h 
CPL P1.0 ;O estado de P1.0 estava a 0 passando agora a 1 
 
 
5.5 - Operações no espaço de dados externo 
Vai exemplificar-se como é feita a escrita e leitura no espaço de dados externo. Todos os 
acessos ao espaço externo tem necessariamente de ser feitos através da indicação do 
respectivo endereço. Se esse endereço fôr de 8 bits bastará um registo de 8 bits para guardar o 
endereço, mas, se o endereço fôr de 2 bytes, então já se torna necessário usar um registo de 2 
bytes; esse registo é o DPTR. 
O registo DPTR é um registo de 16 bits usado para guardar o endereço do espaço de dados 
externo assim como também ao espaço de código (seja ele externo ou interno) ao qual se 
pretende aceder. 
 13
Todos os acessos ao espaço de dados externo, sejam de leitura ou de escrita, têm de ser 
feitos usando a instrução MOVX. A sintaxe da instrução MOVX é a seguinte: 
 
MOVX @registo,A ou MOVX A,@registo 
 
consoante se trate de uma operação de escrita ou de leitura, respectivamente, onde registo 
poderá representar: 
8 R1…R7, se o endereço fôr menor ou igual a 8 bits 
8 DPTR, se o endereço fôr superior a 8 bits 
 
Note que todos os valores que são lidos ou escritos na memória externa são sempre guardados 
no acumulador, porque não é possível utilizar outro registo para esse efeito. 
 
Exemplo 
Escrever no espaço externo de dados no endereço 23h (8 bits) o valor 
3 decimal. 
MOV A,#3 ou MOV A,#3 
MOV R0,#23h MOV DPTR,#23h 
 MOVX @R0,A MOVX @DPTR,A 
 
Exemplo 
Lêr o valor do endereço 2033h (16 bits), do espaço externo de dados, 
para o acumulador. 
 
MOV DPTR,#2033h 
 MOVX A,@DPTR 
 
5.6 - Operações no espaço de código 
Vai ser exemplificado a forma como pode ser feita a leitura em memória de código, seja 
ela externa ou interna. 
Todos os acessos ao espaço de código têm de ser feitos usando a instrução MOVC. A 
sintaxe da instrução MOVC é a seguinte: 
MOVC A,A+@DPTR 
Exemplo 
Lêr o valor do endereço 2033h, do espaço de código. 
 14
MOV DPTR,#2033h 
MOV A,#0 
 MOVC A,A+@DPTR 
 15
Capítulo 6 
Estruturas de Controlo de Fluxo de Execução 
 
 
6.1 – Introdução 
As instruções de um programa são normalmente executadas sequencialmente. No entanto, 
é por vezes desejável alterar o percurso normal de execução destas instruções, especificando 
uma outra ordem de execução. 
As instruções de salto controlam o fluxo de execução de um programa, isto é, a ordem pela 
qual as instruções são executadas. Existem saltos condicionais e saltos incondicionais. Os 
saltos condicionais, contrariamente ao que acontece com os saltos incondiconais, são aqueles 
cuja execução está condicionada pelo valor lógico de um registo ou bit. 
As instruções de salto re-direcionam o fluxo do programa para uma instrução identificada 
por uma etiqueta (label). Essa label coloca-se antes da instrução para a qual se pretende fazer 
o salto. 
 
6.2 - Instruções de salto incondicional 
Esta secção descreve três instruções de salto incondicional: AJMP, LJMP e SJMP. O 
comportamento destas instruções é idêntico, estando a diferença na distância e/ou localização 
possível da instrução para a qual se pretende saltar e tamanho em bytes da instrução de salto 
(em código máquina). 
A instrução de salto mais compacta é o SJMP, seguida do AJMP e por fim o LJMP. Por 
essa razão, a ordem de escolha preferencial deve ser essa. Por vezes, por uma questão de 
comodidade, utiliza-se sempre o LJMP durante a fase de desenvolvimento do programa, 
sendo este substituído quando o desenvolvimento está concluído pelas instruções mais 
eficazes se tal for possível. 
 
xJMP label da instrução destino com x={A,L,S} 
 
8 SJMP – Short JuMP (Salta para instrução localizada a menos de 127 bytes de distância) 
Exemplo 
Ciclo: SETB P1.0 
 CLR P1.0 
 16
 SJMP Ciclo ;Salta para instrução identificada pela label 
;“Ciclo”. 
 
8 LJMP – Long JuMP (Salta para instrução localizada em qualquer endereço de 
memória) 
Exemplo 
Ciclo: SETB P1.0 
 CLR P1.0 
 LJMP Ciclo2 ;Salta para instrução identificada pela 
;label “Ciclo2”. 
 … 
Ciclo2: SETB P1.1 
 CLR P1.1 
 LJMP Ciclo ;Salta para instrução identificada pela 
;label “Ciclo”. 
 
8 AJMP – Absolute JuMP (Salta para instrução dentro do mesmo bloco de 2 kbytes 4,5) 
Exemplo 
Inicio: MOV A,#00 ;instrução no endereço 0800h (2kbytes=2048 
;bytes) 
 … 
 AJMP Inicio 
 … 
Bloco2: MOV A,#42 ;instrução no endereço 1000h (4kbytes). Não 
;seria possível saltar para esta instrução 
;com o AJMP (em vez de saltar para a label 
;Inicio) porque já está dentro de outro bloco 
;de 2 kbytes 
 
6.3 - Instruções de salto condicional aplicadas a bit 
Vão ser descritas três instruções de salto condicional aplicadas a bit: JB, JNB e JBC. Na 
grande maioria dos casos, os bits que interessa manipular são os bits pertencentes aos SFR´s. 
 
4 Estes blocos de 2 kbytes são contabilizados a partir do inicio do espaço de endereçamento e não a partir da 
instrução de salto (como é o caso no SJMP). Ou seja, são os blocos com endereços 0000h-07FFh; 0800h-0FFFh; 
… 
5 Para se ser mais preciso, a instrução para a qual se salta tem de estar no bloco de 2 kbytes que inclui a 
instrução seguinte ao AJMP (o qual não é necessariamente o mesmo bloco no qual está a instrução de salto). 
 
 17
instrução bit de teste, label de salto, com instrução={JB,JNB,JBC} 
 
 
8 JB - Jump if Bit (salta se bit a 1) 
Exemplo 
SETB P1.0 
label_x: JB P1.0,label_x ;Se P1.0=1, salta para a label 
;‘label_x’, ou seja, o programa 
;fica retido nesta instrução 
;enquanto P1.0 fôr 1 
 
8 JNB - Jump if Not Bit (salta se o bit está a 0) 
Exemplo 
CLR TF0 ;Apaga flag 
label_y: JNB TF0,label_y ;Salta enquanto a flag é zero, ou 
;seja, o programa fica retido 
;nesta instrução enquanto TF0 fôr 
;igual a 0 
 
8 JBC - Jump if Bit and Clear (salta se bit a 1 e apaga-o em seguida) 
Repare-se que esta instrução tem uma diferençasignificativa relativamente às anteriores: 
testa o bit, e se fôr 1 apaga-o para 0, se fôr 0 não faz nada. 
Exemplo 
 
label_z: JBC P1.0,label_z ;Se P1.0=1, salta para a label 
;‘label_z’ e coloca P1.0=0. Se 
;P1.0=0 não faz o salto e continua 
;para a instrução seguinte 
 
6.4 - Instruções de salto condicional aplicadas a registos 
Vão ser descritas as principais instruções de salto condicional aplicadas a registos: JZ, 
JNZ, DJNZ e CJNE. As instruções JZ, JNZ e CJNE actuam exclusivamente sobre o registo 
acumulador, contrariamente ao que acontece com a instrução DJNZ. 
 
8 JZ – Jump if Zero (salta se o conteúdo do acumulador fôr zero) 
 18
Esta instrução testa de forma implícita o registo acumulador. Se o valor deste fôr zero, 
então a instrução de salto é executada, caso contrário não é. 
Exemplo 
 MOVX A,@DPTR 
 JZ Turn_On 
 
8 JNZ – Jump if Not Zero (salta se o conteúdo do acumulador fôr diferente de zero) 
Esta instrução testa de forma implícita o registo acumulador. Se o valor deste não fôr zero, 
então a instrução de salto é executada, caso contrário não é. 
 
Exemplo 
 MOV A,#13h 
Decrementa:DEC A 
 JNZ Decrementa ;A instrução de salto será 
;executada 19 vezes até que o 
;acumulador esteja a zero 
 
8 DJNZ – Decrement and Jump if Not Zero 
Esta instrução pode ser aplicada a qualquer registo de 8 bits. A instrução primeiro 
decrementa e só depois testa o conteúdo do registo. A instrução de salto é efectuada se o 
conteúdo do registo ainda não fôr zero. Esta instrução é normamente utilizada para o controlo 
de ciclos. 
Exemplo 
Suponhamos que se pretende copiar o valor 20h para as células de RAM 
interna compreendidas entre os endereços 40h e 50h. 
 
 MOV A,#20h ;Valor a copiar 
 MOV R1,#40h ;Endereço inicial 
 MOV R0,#10h ;Nº de vezes que o ciclo de cópia vai 
;ser executado 
Copia: MOV @R1,A 
 INC R1 ; Passa para o endereço seguinte 
 DJNZ R0,Copia ; Se R0 ainda não é zero, isto é, se 
;ainda não se atingiu o enedereço 50h, 
;o ciclo de cópia continua. 
 
 19
8 CJNE – Compare and Jump if Not Equal 
Esta instrução compara o conteúdo do registo acumulador com um valor. Se forem iguais a 
instrução de salto é executada, caso contrário não é. Esta é a única instrução que existe para 
fazer a comparação entre dois valores. Esta instrução mexe na flag de carry da forma que se 
exmplifica a seguir. 
CJNE A,#valor,label ; se A>valor, a flag C->0 
 ; se A<valor, a flag C->1 
 ; se A=valor, não salta para label 
 ; se A≠valor, salta para label 
 
Exemplo 
Vamos ler o valor de um periférico mapeado no endereço externo de 
dados 3450h. Se o valor lido for 0FFh, então o programa deve 
escrever para esse periférico o valor 00h. 
 
 MOV DPTR,#3450h 
 MOV A,@DPTR ; AÅvalor enviado pelo periférico 
 CJNE A,#0FFh,Escrita 
 … 
Escrita: MOV A,#00h 
 MOV @DPTR,A 
 
Exemplo 
Vamos comparar o valor do acumulador com o valor 45h. Se o valor do 
acumulador for superior deve ligar o pino P1.0, se for inferior deve 
ligar o pino P1.1. Se forem iguais deve ligar o pino P1.2. 
 
 CJNE A,#45h,Diferentes 
 SETB P1.2 
 LJMP Fim 
Diferentes:JNC Superior ; se A>45h (C=0) 
 JC Inferior ; se A<45h (C=1) 
Superior: SETB P1.0 
 LJMP Fim 
Inferior: SETB P1.1 
Fim: LJMP Fim 
 20
 
6.5 - Transferência de dados entre espaços de endereçamento de dados 
 
6.5.1 - Memória interna/memória interna 
 
Exemplo 
Como transmitir um bloco de dados com 20 bytes de tamanho do 
endereço 30h para o endereço 60h 
 
Inicio: MOV R0,#30H 
 MOV R1,#60H 
 MOV R3,#20 
CopiaByte: MOV A,@R0 ; este ciclo é repetido 20 vezes 
 MOV @R1,A 
 INC R0 
 INC R1 
 DJNZ R3, CopiaByte 
 
6.5.2 - Memória externa/interna 
 
Exemplo 
Como transmitir um bloco de dados com 20 bytes de tamanho do 
endereço externo 3F0Ah para o endereço interno 60h. 
 
Inicio: MOV R0,#60H 
 MOV DPTR, #3F0AH 
CopiaByte: MOVX A,@DPTR 
 MOV @R0,A 
 INC DPTR 
 INC R0 
 CJNE R0,#75H,CopiaByte ;60h+14h = 74H 
 
6.5.3 - Memória interna/externa 
 
Exemplo 
Como transmitir um bloco de dados com 20 bytes de tamanho do 
endereço interno 60h para o endereço externo 3F0Ah. 
 
 21
Inicio: MOV R0,#60H 
 MOV DPTR, #3F0AH 
CopiaByte: MOV A,@R0 
 MOVX @DPTR,A 
 INC DPTR 
 INC R0 
 CJNE R0, #75 ,CopiaByte ;60h+14h = 74H 
 
 22
Capítulo 7 
Subrotinas 
 
 
7.1 – Introdução 
Subrotinas são secções de código que, devido ao facto de serem necessárias em várias 
zonas distintas do programa, ou porque desempenham uma função específica e bem definida, 
são separadas do programa principal. As subrotinas são chamadas através de instruções 
específicas e quando terminam o seu processamento, retornam a execução ao ponto onde 
foram chamadas. A utilização correcta de subrotinas reduz o tamanho do código e torna a 
estrutura do programa mais clara e de compreensão mais fácil. 
 
7.2 - Chamada e retorno de subrotinas (A/LCALL, RET) 
 
8 A/LCALL nome da rotina – Instrução para chamar uma subrotina. As diferenças entre as 
duas formas da instrução são semelhantes às diferenças entre as instruções de salto AJMP 
e LJMP, aplicando-se as mesmas restrições e recomendações. Estas instruções funcionam 
essencialmente como um salto, mas apresentam a uma diferença. A subrotina necessita de 
saber o local onde deve continuar a execução uma vez que termine. Este local, designado 
por endereço de retorno, corresponde usualmente à instrução seguinte ao A/LCALL, pelo 
que esta instrução armazena o endereço dessa instrução numa estrutura de dados chamada 
Pilha. 
 
8 RET – Instrução para terminar a subrotina e continuar a execução a partir do ponto onde a 
subrotina foi chamada. 
 
7.3 - Pilha 
A pilha é uma zona de dados que representa uma fila na qual são colocados e retirados 
dados segundo uma política LIFO: Last In First Out. Isto significa que quando é retirado 
(lido) um valor da pilha, este é sempre o valor que lá foi colocado mais recentemente. Uma 
vez lido um valor, ele é normalmente removido da pilha, pelo que o próximo valor a ser 
removido será o segundo mais recente, e assim sucessivamente. Uma boa analogia para pilha 
 23
é, efectivamente, uma pilha de objectos como tijolos: tijolos novos são colocados no topo da 
pilha, e serão estes também os primeiros a serem removidos. 
A pilha é uma estrutura de dados especialmente útil para efectuar a gestão dos endereços 
de retorno das subrotinas, pelo que muitos µCs e µPs têm mecanismos de hardware 
especialmente dedicados à sua implementação. 
No caso dos µCs 8051, existe um registo, o SP (Stack Pointer) dedicado a guardar o 
endereço do topo da pilha. Sempre que é colocado um valor na pilha, este valor é 
incrementado, e sempre que é retirado um valor da pilha, este valor é decrementado, para 
apontar para o novo topo. 
As instruções PUSH e POP podem ser utilizadas pelo programador para colocar e retirar 
explicitamente valores da pilha: 
 
8 PUSH endereço – Guarda o valor que está no endereço interno na pilha. O SP é 
incrementado. 
 
8 POP endereço – Lê e remove valor da pilha. Guarda esse valor no endereço indicado e 
decrementa o SP. 
 
A pilha é utilizada automaticamente pelas funções A/LCALL e RET para armazenar o 
endereço de retorno da função. A instrução de chamada à subrotina coloca o endereço da 
instrução seguinte na pilha antes de saltar para a subrotina. A instrução RET continua a 
execução no endereço que está guardado no topo da pilha. Note-se que este sistema permite 
efectuar não só várias chamadas à mesma função em zonas de código distintas como também 
permite a utilização de funções recursivas. 
Uma outra utilização típica das pilhas é para efectuar a salvaguarda de valores deregistos 
de uma função. Como uma função pode, teoricamente, ser chamada de qualquer sítio, esta 
deve sempre garantir que nenhum registo sai alterado quando termina (a não ser, claro, 
quando este é usado para devolver o resultado da função). Se não fosse esse o caso, poderia 
haver conflitos entre os registos usados dentro e fora da subrotina. Imaginando que uma 
função altera o conteúdo de R1 e R2, por exemplo, então teríamos o seguinte código para 
preservar esses valores entre a entrada e saída: 
 
SubRotina: 
 PUSH R1 
 24
 PUSH R2 
 … ;código da função (utiliza R1 1e R2) 
 POP R2 
 POP R1 
 RET 
 
Neste caso o valor dos registos R1 e R2 é guardado na pilha antes de estes serem alterados, 
e são restaurados aos valores originais antes de se sair da subrotina. Note que a ordem dos 
POP’s necessita de ser inversa relativamente à ordem dos PUSH’s (Porquê?). 
Por vezes é necessário inicializar a pilha convenientemente durante a fase de arranque do 
sistema. Isto é feito através da escolha de um valor inicial adequado para o SP. Quando se 
escolhe este valor, deve ter-se em atenção que no 8051 a pilha cresce para cima, isto é, desde 
os endereços menores para os maiores (há processadores nos quais se verifica o contrário). O 
espaço de memória alocado para a pilha deve ser suficiente para armazenar todos os valores 
que lá possam ser colocados, se tal valor for possível de determinar à partida. 
 25
 
Capítulo 8 
Temporizadores 
 
 
8.1 - Descrição do temporizador e seus SFRs e modos de funcionamento 
 
Um temporizador é um circuito que pode ser usado para contabilizar a passagem de tempo 
ou contabilizar o número de ocorrências de acontecimentos. O temporizador contabiliza o nº 
de transições descendentes do sinal presente à sua entrada. Esse sinal pode ser proveniente de 
duas fontes possíveis: relógio interno ou do exterior do chip através do pino Tx{x=0,1}. Se o 
sinal de entrada do temporizador for proveniente do sinal de relógio interno (1/12 freq. 
relógio externo), diz-se que está a funcionar como temporizador, se o sinal for proveniente de 
uma fonte externa (um sensor, por exemplo) diz-se que funciona como contador de 
acontecimentos externos. 
Note-se que quando um temporizador está a funcionar como “temporizador”, o nº de 
transições contabilizadas corresponde ao nº de períodos de relógio ocorridos até então (por 
cada período de relógio há uma transição descendente), pelo que se multiplicarmos o nº de 
transições pelo período do relógio interno, ficamos com a informação do tempo... 
O µC 8051 tem dois temporizadores internos de 16 bits cada e 4 modos de funcionamento 
possíveis. Quer isto dizer que cada um é capaz de contabilizar, no máximo, 65536 (216) 
transições descendentes do sinal presenta à sua entrada. Quando essa contagem “ultrapassa” a 
capacidade dos registos usados na contagem, dá-se o “overflow” e o temporizador assinala 
isso activando um bit dedicado para esse efeito, normalmente denominado por “flag de 
overflow”. Os temporizadores são independentes entre si e portanto cada um tem a sua própria 
flag de overflow. 
 
8.2 - Programação dos SFRs do temporizador 
Para usar um temporizador é necessário, primeiro de tudo, programá-lo. Essa programação 
passa pela inicialização adequada dos seus registos de controlo, isto é, dos SFRs do 
temporizador. 
Em seguida são descritos os SFRs dos temporizadores do µC8051: TMOD, TCON, TH1, 
TL1, TH0 e TL0. 
 26
 
8 TCON (Timer CONtrol) 
Este registo é endereçável bit a bit e está mapeado no endereço 88h do espaço de dados 
interno. 
8Fh 8Eh 8Dh 8Ch 8Bh 8Ah 89h 88h 
TF1 TR1 TF0 TR0 - - - - 
 
 Timer 1 Timer 0 
 
Os bits assinalados com ‘-‘ são bits não relevantes para a operação dos temporizadores. 
 
TF1 → flag de overflow do temporizador 1 
TR1 → flag de arranque/paragem do temporizador 1. Quando é 0 o temporizador 1 
não contabiliza a passagem de tempo ou os eventos, isto é, a contagem não se 
faz, está parada. 
TF0 → flag de overflow do temporizador 0 
TR0 → flag de arranque/paragem do temporizador 0. Idêntica a TR1, mas para o 
temporizador 0. 
 
8 TMOD (Timer MODe) 
Este registo é endereçável bit a bit e está mapeado no endereço 89h do espaço de dados 
interno. Este registo serve para definir o modo de funcionamento dos temporizadores. 
 
90h 8Fh 8Eh 8Dh 8Ch 8Bh 8Ah 89h 
Gate C/T M1 M0 Gate C/T M1 M0 
 
 Timer 1 Timer 0 
 
A definição do modo de funcionamento para os temporizadores 1 e 0 faz-se de igual forma, 
a diferença está na localização dos respectivos bits de controlo no registo TMOD. 
 
Gate → Quando TRx, com x={0,1}=1 e Gate=1, o respectivo temporizador funciona 
apenas quando e enquanto o pino INTx estiver a 1. Este tipo de controlo 
 27
chama-se de controlo por Hardware. 
Quando Gate=0, o respectivo temporizador funciona apenas quando e 
enquanto TRx estiver a 1. Este tipo de controlo chama-se de controlo por 
Software. 
C/T → Se C/T=0, o temporizador fica em modo “temporizador”, isto é, contador de 
impulsos temporais. Cada impulso corresponde ao período do relógio 
interno do microcontrolador. 
Se C/T=1, o temporizador fica em modo “contador”, isto é, contador de 
acontecimentos externos. Os impulsos relativos aos acontecimentos 
externos estão à entrada do pino Tx 
M1 e M0→ Estes dois bits definem o modo de funcionamento (ver adiante) 
 
M1 M0 Modo 
0 0 Modo 0 
0 1 Modo 1 
1 0 Modo 2 
1 1 Modo 3 
 
8 THx (Timer High), TLx (Timer Low), x={0,1} 
TL0 e TH0 → Estes dois registos são utilizados pelo temporizador 0 para armazenar o 
tempo/acontecimentos decorridos. TL0 é o byte menos significativo e TH0 é o byte mais 
significativo desse valor. 
TL1 e TH1→ Análogos a TL0 e TH0, mas respeitantes ao temporizador 1. 
 
8.3 - Modos de operação 
Tal como já foi referido, cada temporizador pode funcionar em 4 modos de operação 
possíveis, conforme o valor das flags M1 e M0: 
 
8 Modo 0: Temporizador de 13 bits (M1=0, M0=0) 
Este modo de funcionamento existe para manter compatibilidade com o predecessor do 
8051, o uC 8048. Neste modo de funcionamento, são utilizados os registos THx e os 5 bits 
menos significativos de TLx para efectuar a contagem. Quando esta contagem efectua a 
transição FF1Fh (11111111-xxx11111)→0h (00000000-xxx00000), a flag TFx é activada. 
 28
Os três bits mais significativos de TLx são ignorados. Este modo de operação é raramente 
utilizado em projectos actuais, que não necessitam de funcionar no 8048. 
 
8 Modo 1: Temporizador de 16 bits (M1=0, M0=1) 
Neste modo de operação, semelhante ao modo de operação anterior, são utilizados todos 
os 16 bits disponíveis em TLx e THx para efectuar a contagem. A flag TFx é activada na 
transição FFFFh→0000h, o que possibilita a contagem máxima de 65536 (216) transições. 
 
8 Modo 2:Temporizador de 8 bits, com recarregamento automático (M1=1, M0=0) 
A contagem é feita apenas no registo TLx. O registo THx contém um valor que é utilizado 
para recarregar o TLx quando ocorre o overflow. Por exemplo, se o valor do registo THx 
for FCh, o registo TLx vai contar a sequência FCh, FDh, FEh, FFh, FCh, FDh, .... O 
overflow ocorre, isto é, a flag de overflow (TFx) é activada, quando a contagem actual é 
FFh e se tenta incrementar mais uma contagem, “rebentando” assim com a capacidade do 
registo de 8 bits. Automaticamente o registo de contagem TLx é reinicializado com o 
valor presente em THx que é o valor de recarregamento. 
 
8 Modo 3: (M1=1, M0=1) 
Neste modo, o timer 0 é decomposto em dois timers independentes: um em TL0 com a 
flag de overflow TF0, e outro em TH0 com a flag de overflow TF1. Portanto, neste modo, 
o timer 1vê-se privado da sua flag TF1. Pode no entanto ser usado desde que esta flag não 
seja necessária. 
 
8.4 - Exemplos 
 
Técnica de programação - Pooling 
O pooling consiste em amostrar consecutivamente, por S/W, um dado 
valor que é alterado por uma fonte externa (p.ex um pino ou porto do 
processador ou uma flag do temporizador) até ser encontrado o valor 
pretendido. Nos exemplos seguintes é utilizado pooling para detectar 
a passagem dos intervalos de tempo indicados. 
 
Programação de temporizador para 200 µs: este programa envia um 
valor (FFh) pelo porto P2 em cada intervalo de 200 µs. 
 
 29
• Período de clock interno? 
Supondo que: freq. clock externo = 12Mhz. 
 ⇓ 
freq.clock interno=freq.clock externo/12=1Mhz ⇒período interno=1µs 
 
• Nº de contagens? 
Nº de contagens= intervalo de contagem/ período clock interno 
 = 200x10-6(s)/ 1x10-6(s) = 200 contagens 
 
• Tamanho do Temporizador? 
Temporizador de 8 bits ⇒ permite até 256 contagens 
Como 200<256, podemos usar um temporizador de 8 bits, o timer 0, 
por exemplo. 
 
• Programação do registo TMOD = xxxx0010b (2d) 
Gate C/T M1 M0 Gate C/T M1 M0 
- - - - 0 0 1 0 
Gate=0 → controlo de ON↔ OFF feito por SW 
C/T=0 → vai funcionar com temporizador, i.é, contador de tempo 
Modo de funcionamento → M1=1, M0=0 
Temporizador de 8 bits ⇒ Modo 2 
⇓ 
 Valor de Recarregamento? TH0 =256-200=56 
 
• Programação do registo TCON=xx01xxxxb (16d) 
TF1 TR1 TF0 TR0 - - - - 
- - 0 1 
TF0=0 → limpar a flag de overflow 
TR0=1 → para dar início à contagem 
 
• Código 
… 
Inicio: MOV TH0,#56; 
 MOV TL0,#56; 
 MOV TMOD,#2; 
 30
 CLR TF0 
 SETB TR0 ;Este registo deve sempre ser o último 
;a ser inicializado pois é neste 
;registo (através do bit TR0 que se 
;inicia a contagem 
Espera: JNB TF0,Espera ;O programa fica retido nesta instrução 
;(200µs) até que TF0→1, isto é, espera 
;pela ocorrência de overflow. 
 CLR TF0 ;Apaga flag de overflow para poder ser 
;possível detectar o próximo overflow. 
Escreve: MOV P2,#0FFh 
 LJMP Espera 
 
Programação de temporizador para activação em cada 20 ms: este 
programa envia um valor (FFh) pelo porto P2 em cada intervalo de 20 
ms. 
 
• Período de clock interno? 
Supondo que: freq. clock externo = 15Mhz. 
 ⇓ 
freq. clock interno= freq. clock externo/12 = 1,25Mhz ⇒ 
período interno = 0,8µs 
 
• Nº de contagens? 
Nº de contagens= intervalo de contagem/ período clock interno 
 = 20x10-3(s)/ 0,8x10-6(s) = 25000 contagens 
 
• Tamanho do Temporizador? 
Temporizador de 16 bits ⇒ permite até 65536 contagens 
Como 25000<65536, vamos usar um temporizador de 16 bits, o timer 
1, por exemplo. 
 
• Programação do registo TMOD = 0001xxxxb (10h) 
Gate C/T M1 M0 Gate C/T M1 M0 
0 0 0 1 - - - - 
Gate=0 → controlo de ON↔ OFF feito por SW 
 31
C/T=0 → vai funcionar com temporizador, i.é, contador de tempo 
Modo de funcionamento → M1=0, M0=1 
Temporizador de 16 bits ⇒ Modo 1 
⇓ 
 Valor de Recarregamento=65536-25000=40536d=9E58h 
 ⇓ 
TH1=9Eh e TL1=58h 
 
• Programação do registo TCON=01xxxxxxb (40h) 
TF1 TR1 TF0 TR0 - - - - 
0 1 - - - - - - 
TF1=0 → limpar a flag de overflow 
TR1=1 → para dar início à contagem 
 
• Código 
… 
Inicio: MOV TH1,#9Eh 
 MOV TL1,#58h 
 MOV TMOD,#10h 
MOV TCON,#40h 
Espera: JNB TF1,Espera ;O programa fica retido nesta instrução 
;(20ms) até que TF1→1, isto é, espera 
;pela ocorrência de overflow. 
MOV TH1,#9Eh ;O re-carregamento não é automático… 
 MOV TL1,#58h 
 CLR TF1 ;Apaga flag de overflow para poder ser 
;possível detectar o próximo overflow. 
Escreve: MOV P2,#0FFh 
 LJMP Espera 
 
Programação de temporizador para activação em cada 200 ms: este 
programa lê um valor do porto P2 escreve-o na célula de memória 
interna 20h, em intervalos de 200 ms. 
 
• Período de clock interno? 
Supondo que: freq. clock externo = 15Mhz. 
 32
 ⇓ 
freq. clock interno= freq. clock externo/12 = 1,25Mhz ⇒ 
período interno = 0,8µs 
 
• Nº de contagens? 
Nº de contagens= intervalo de contagem/ período clock interno 
 = 200x10-3(s)/ 0,8x10-6(s) = 250000 contagens 
 
• Tamanho do Temporizador? 
Temporizador de 16 bits ⇒ permite até 65536 contagens 
Mas 250000>65536 !!, como resolver a situação? Vamos obrigar um 
temporizador (por exemplo o temporizador 1) a fazer várias 
contagens sucessivas até totalizar o intervalo desejado. 
Optando, por exemplo, por 5 ciclos de contagem, onde cada 
contagem corresponderá a 250000/5=50000 contagens. 
 
• Programação do registo TMOD = 0001xxxxb (10h) 
Gate C/T M1 M0 Gate C/T M1 M0 
0 0 0 1 - - - - 
Gate=0 → controlo de ON↔ OFF feito por SW 
C/T=0 → vai funcionar com temporizador, i.é, contador de tempo 
Modo de funcionamento → M1=0, M0=1 
Temporizador de 16 bits ⇒ Modo 1 
⇓ 
 Valor de Recarregamento=65536-50000=15536d=3CB0h 
 ⇓ 
TH1=3Ch e TL1=B0h 
 
• Programação do registo TCON=01xxxxxxb (40h) 
TF1 TR1 TF0 TR0 - - - - 
0 1 - - - - - - 
TF1=0 → limpar a flag de overflow 
TR1=1 → para dar início à contagem 
 
• Código 
 33
… 
Inicio: MOV TH1,#3Ch 
 MOV TL1,#B0h 
 MOV TMOD,#10h 
MOV TCON,#40h 
MOV R0,#5 
Espera: JNB TF1,Espera ;O programa fica retido nesta instrução 
;(50ms) até que TF1→1, isto é, espera 
; pela ocorrência de overflow. 
MOV TH1,#3Ch ;O re-carregamento não é automático… 
 MOV TL1,#B0h 
 CLR TF1 
 DJNZ R0,Espera ;Obriga à execução de 5 ciclos de 
;contagem sucessivos 
Escreve: MOV A,P2 
MOV 20h,A 
MOV R0,#5 
 LJMP Espera 
 
Programação do temporizador para contar 100 impulsos externos: este 
programa espera que ocorram 100 impulsos no pino T0 e envia o valor 
FFh pelo porto P2 quando tal acontece. 
 
• Período de clock interno? 
Não interessa, dado que o timer não vai funcionar como 
temporizador mas sim como contador de acontecimentos externos. 
• Nº de contagens? 
Nºcontagens é eexcatamente igual ao nº de acontecimentos externos 
que se pretende contabilizar, neste caso = 100 impulsos. 
• Tamanho do Temporizador? 
Temporizador de 8 bits ⇒ permite até 256 contagens 
• Programação do registo TMOD = xxxx0010b (2d) 
Gate C/T M1 M0 Gate C/T M1 M0 
- - - - 0 1 1 0 
Gate=0 → controlo de ON↔ OFF feito por S/W 
C/T=1 → vai funcionar como contador de impulsos externos 
 34
Modo de funcionamento → Modo 2 (M1=1,M0=0) 
Temporizador de 8 bits ⇒ Modo 2 
⇓ 
 Valor de Recarregamento? TH0 =256-100=156d 
 
• Programação do registo TCON=xx01xxxxb (16d) 
TF1 TR1 TF0 TR0 - - - - 
- - 0 1 
TF0=0 → limpar a flag de overflow 
TR0=1 → para dar início à contagem 
 
• Código 
… 
Inicio: MOV TH0,#156; 
 MOV TL0,#156; 
 MOV TMOD,#6; 
 MOV TCON,#16 ;Este registo deve sempre ser o último 
;a ser inicializado pois é neste 
;registo (através do bit TR0 que se 
;inicia a contagem 
Espera: JNB TF0,Espera ;O programa fica retido nesta instrução 
;(100 impulsos) até que TF0→1, isto é, 
;espera pela ocorrência de overflow. 
CLR TF0 
Escreve: MOV P2,#0FFh 
 LJMP Espera 
 
Programação do temporizador para determinar a duração de um impulso 
aplicado ao pino P3.3 (INT1). Deve ser determinada a duração de um 
impulso aplicado ao pino P3.3 sabendo que a sua duração máxima é de 
20 ms. A duração deve ser enviada pelos portos P1 e P2 quando o 
impulso termina (P1=>mais significativo, P2 =>menos significativo). 
 
• Período de clock interno? 
Supondo que: 
freq. clock externo = 15Mhz 
 35
 ⇓ 
freq. clock interno= freq. clock externo/12 = 1,25Mhz ⇒ 
período interno = 0,8µs 
 
• Nº de contagens? 
Nº de contagens máximo=duração do impulso / período clock interno 
 = 20x10-3(s)/ 0,8x10-6(s) = 25000 contagens 
 
• Tamanho do Temporizador? 
Temporizador de 16 bits ⇒ permite até65536 contagens 
Como 25000<65536, vamos usar um temporizador de 16 bits, o timer 
1, por exemplo. 
 
• Programação do registo TMOD = 1001xxxxb (90h) 
Gate C/T M1 M0 Gate C/T M1 M0 
1 0 0 1 - - - - 
Gate=1 → controlo de ON↔ OFF feito por H/W 
C/T=0 → vai funcionar com temporizador, i.é, contador de tempo 
Modo de funcionamento → M1=0, M0=1 
Temporizador de 16 bits ⇒ Modo 1 
⇓ 
 Valor de Recarregamento=65536-25000=40536d=9E58h 
 ⇓ 
TH1=9Eh e TL1=58h 
 
• Programação do registo TCON=xxxxxxxxb 
TF1 TR1 TF0 TR0 - - - - 
- - - - - - - - 
 
Não interessa… 
• Programação adicional 
É necessário inibir a interrupção externa 1 (ver secção sobre 
interrupções), porque ela também é activada pelo pino P3.3. Para 
esse efeito, devemos garantir que o bit EX1 do SFR IE é colocado a 
0. 
 36
 
• Código 
… 
Inicio: MOV TH1,#9Eh 
 MOV TL1,#58h 
 MOV TMOD,#90h 
CLR EX1 
EsperaImp: JNB P3.3,EsperaImp ;Esperamos que o pino P3.3 seja 
;colocado externamente a 1. A 
;partir deste momento, o 
;temporizador começa a contar 
EsperaFim: JB P3.3,EsperaFim ;Esperamos que o pino P3.3 seja 
;colocado externamente a 0. 
;Quando isso acontece, o 
;temporizador deixa de estar 
;activado, e podemos saber 
;durante quantos impulsos do 
;temporizador o pino esteve 
;activado analisando o valor de 
;TL1 e TH1 
Escreve: MOV P2,TL1 
 MOV P1,TH1 
 
MOV TH1,#9Eh ;O re-carregamento não é automático… 
 MOV TL1,#58h 
 LJMP EsperaImp 
 37
 
Capítulo 9 
Canal Série 
 
 
9.1 – Introdução 
 
O 8051 tem uma UART (Universal Asynchronous Receiver Transmitter) integrada para 
comunicação série. Permite a comunicação em Full Duplex pois tem dois buffers 
independentes: um para a recepção e outro para a transmissão de dados. Esses dois registos 
têm a mesma designação SBUF, no entanto trata-se de dois registos físicos distintos: quando é 
feita uma escrita em SBUF é utilizado o buffer de envio, quando é feita uma leitura o valor é 
lido do buffer de recepção. A taxa de transmissão pode ser variável (ver “Modos de 
funcionamento”). 
8 Transmissão: Quando se pretende enviar um dado pelo canal série, escreve-se esse valor 
(máximo 8 bits) para o registo SBUF (buffer de envio). De cada vez, os dados a 
enviar/receber podem ter no máximo 8 bits, que é o tamanho do registo SBUF. Os LSB 
são recebidos/transmitidos primeiro. Quando a UART tiver enviado todo o conteúdo do 
SBUF pelo pino TXD, e após ter enviado o stop bit, assere a flag TI (Transmission 
Interrupt), isto é, põe a flag TI a 1 (envio concluído). 
 
8 Recepção: Quando se pretende receber um dado que chega pelo canal série, é necessário, 
antes de mais, certificarmo-nos de que de facto existe informação válida para ler do 
SBUF. O que acontece é que quando a UART do 8051 recebe um byte de dados pelo 
canal série, escreve-o no SBUF (buffer de recepção) e, após receber o stop bit, assere a 
flag RI (Reception Interrupt), isto é, põe a lag RI a 1 (recepção concluída). 
 
9.2 - Registos de Função Específica 
 
8 SCON (Serial port CONtrol register) 
 
9Fh 9Eh 9Dh 9Ch 9Bh 9Ah 99h 98h 
SM0 SM1 SM2 REN TB8 RB8 TI RI 
 38
 
Os bits SM0 e SM1 definem o modo de funcionamento do canal série. A definição dos 
modo de funcionamento está exposta na tabela seguinte: 
 
 
SM0 SM1 Modo 
0 0 0 
0 1 1 
1 0 2 
1 1 3 
 
O bit SM2 é normalmente utilizado em sistemas de comunicação multiprocessador. Para 
comunições normais, este bit pode ser colocado a 0. 
O bit REN (Reception ENable) deve estar a 1 para que a UART possa receber valores do 
exterior. Quando REN=0, a UART ignora todas as transmissões de dados vindas do exterior. 
As flags TB8 e RB8 são utilizadas conjuntamente com os modos de operação 2 e 3. Em 
sistemas multiprocessador com a arquitectura master-slave, estes bits são fundamentais para 
identificar se a informação que está a circular no sistema de comunicação é um dado ou um 
endereço de um escravo.Quando não estamos perante um sistema multiprocessador, o 9º bit é 
normalmente utilizado para implementar um esquema de validação da paridade dos dados 
transmitidos/recebidos. Nesse caso, o nono bit indica se os 8 bits do caracter têm um número 
par ou impar de ‘1’. O emissor dos dados preenche este bit conforme os dados que vai enviar, 
e o receptor verifica se o bit de paridade que recebe está de acordo com os dados que recebeu. 
Se não estiver, isso significa que existiu um erro na transmissão. 
 
As flags TI e RI são utilizadas pela UART, tal como já se referiu, para assinalar a 
transmissão/recepção com sucesso de um caracter (8 bits), respectivamente. 
Adicionalmente, uma flag do registo PCON também é utilizada para controlo da UART. A 
flag SMOD do registo PCON serve para seleccionar, em cada modo, qual o divisor de 
frequência usado. 
 
9.3 - Modos de funcionamento 
 
8 Modo 0 – Registo de deslocamento de 8 bits 
 39
Neste modo de transmissão, a comunicação é do tipo half-duplex. O pino RXD é utilizado 
para recepção e envio de caracteres de 8 bits, enquanto que o pino TXD transmite o sinal de 
relógio ou de sincronismo. A entrada/saída de bits por RXD está sincronizada com o relógio 
emitido em TXD, que é fixo em 1/12 da frequência externa do µC. 
Para transmitir um dado neste modo deve fazer-se o seguinte: 
MOV SBUF,#valor 
CLR TI ;condição necessária e suficiente para ser enviado o sinal por TXD, 
;aquando da transmissão. 
JNB TI,$ ;quando a flag TI vai a 1, o sinal de relógio deixa de ser transmitdo. 
 
Para receber um dado neste modo deve fazer-se o seguinte: 
CLR RI ;estas duas condições são necessárias e suficientes para ser enviado o 
SETB REN ;sinal de relógio por TXD. 
JNB RI,$ ;quando a flag RI vai a 1, o sinal de relógio deixa de ser transmitdo. 
MOV xpto,SBUF ; xpto representa um endereço de RAM interna ou um registo. 
Na recepção, compete ao circuito que está ligado ao µC detectar o sinal de relógio e enviar 
os dados de forma sincronizada. 
 
8 Modo 1 – UART de 8 bits com taxa de transmissão variável 
Neste modo de transmissão, caracteres de 8 bits são recebidos (por RXD)/ transmitidos 
(por TXD) precedidos por um start bit (de valor 0) e seguidos de um stop bit (usualmente 1). 
Ou seja são transmitidos 10 bits por cada caracter de 8 bits. O bit menos significativo do 
caracter é transmitido primeiro. 
Start 
Bit 
LSB 
Bit 0 
 
Bit 1 
 
Bit 2 
 
Bit 3 
 
Bit 4 
 
Bit 5 
 
Bit 6 
MSB 
Bit 7 
stop 
bit 
1 * * * * * * * * 0 
 
Se a flag SM2 =1, então o stop bit deve ser 1. Se a flag for 0, então o stop bit pode ter 
qualquer valor. Se o stop bit tiver um valor incorrecto, então o caracter recebido é ignorado. 
A taxa de transmissão é calculada através da taxa de overflow do timer 1, a qual é dividida por 
32 ou 16, conforme PCON.SMOD seja igual a 0 ou 1, respectivamente. 
 A transmissão de caracteres é feita pelo escrita do valor a transmitir em SBUF. Quando a 
transmissão está completa, o bit TI é colocado a 1. 
 40
A linha RXD é colocada a 1 externamente quando não estão a ser transmitidos dados. 
Desta forma, a recepção de caracteres é iniciada (se REN=1) quando existe uma transição 1-
>0 no pino RXD. O contador de 4 bits é sincronizado para começar a contar neste instante, 
sendo o valor TXD amostrado no meio da contagem de 16 impulsos. 
Quando o caracter é recebido, os seus 8 bits são guardados em SBUF, o valor do stop bit é 
armazenado na flag RB8 e a flag RI é asserida. 
 
8 Modo 2 – UART de 9 bits com taxa de transmissão fixa 
Neste modo de funcionamento, são transmitidos/recebidos caracteres de 9 bits. Como o 
registo SBUF tem apenas 8 bits, sãoutilizadas duas flags (TB8 e RB8) para armazenar o valor 
do nono bit aquando da transmissão ou recepção, respectivamente. São utilizados start/stop 
bits, portanto o número total de bits transmitidos por caracter é 11. 
A taxa de transmissão neste modo é 1/64 ou 1/32 da frequência externa do µC, conforme o 
valor configurado no bit SMOD. 
 
8 Modo 3 - UART de 9 bits com taxa de transmissão variável 
Este modo de funcionamento é idêntico ao modo 2, na medida em que estes dois modos 
têm um 9ºbit, sendo a a fórmula de cálculo da taxa de transmissão idêntica à do modo 1. 
 
9.4 - Exemplos 
 
Programe o canal série para enviar dados com uma taxa de transmissão 
de 1 Mbps, utilizando um µC 8051 a 12 MHz. Impeça a recepção de 
dados, envie o caracter 55h e espere pelo término do envio. 
 
É necessário saber se pode ser utilizado um modo com taxa de 
transmissão fixa ou se é necessário utilizar um modo com taxa de 
transmissão variável. Se for utilizado um modo com taxa variável 
(modo 1 ou modo 3) é preciso também programar o temporizador para 
que este forneça a taxa de transmissão adequada. Portanto, quando é 
possível, deve optar-se por um modo com taxa de transmissão fixa. 
A taxa de transmissão no modo 0 é 1/12 da frequência do µC. Como a 
frequência de operação do µC é 12 MHz, a taxa de transmissão irá ser 
12Mhz/12=1MHz (1Mbps), que é a taxa de operação pretendida. Um 
 41
problema do modo 0 é que é apenas half-duplex, mas isso não é 
relevante neste caso. 
… 
;Inicialização de SMOD 
 CLR SM0 
CLR SM1 ;(SM=0;SM1=0) = modo 0 
CLR SM2 ;Não é relevante para este problema,pode ficar a 0 
CLR REN ;REN=0) impede a recepção. 
CLR TI 
CLR RI ;Coloca as flags de recepção e transmissão a 0. 
;Importante! 
;Alternativamente, em vez de inicializar cada bit separadamente, as 
;instruções anteriores podiam ser substituídas pela instrução: 
MOV SCON, #00000000b 
MOV SBUF,#55h 
Espera:JNB TI,Espera ;Espera que a UART coloque a flag TI a 1, o 
;que indica fim de transmissão. 
 
Programe o canal série para enviar dados com uma taxa de transmissão 
de 187,5Kbps, utilizando um µC8051 a 12MHz. Espere pelo caracter AAh 
e envie como resposta o caracter 55h. 
 
É necessário determinar o modo de funcionamento: 
O modo 0 transmite a 1/12 da frequência externa do µC, sendo a taxa 
de transmissão de 1Mbps para um relógio de 12 MHz. 
O modo 2 transmite a 1/32 ou 1/64 da frequência externa do µC, sendo 
a taxa de transmissão resultante de 375000bps ou 187500bps, 
respectivamente. Sendo assim, é possível utilizar o modo 2 com um 
factor de divisão de 1/64. 
 
 MOV SCON,#10010000b ;Modo 2, REN=1 
MOV PCON,#80h ;SMOD=1 
EsperaR: JNB RI,EsperaR ;Espera por recepção de caracter 
 MOV A,SBUF 
 CJNE A,#AAh,EsperaR ;Se caracter recebido ≠ Aah, espera 
;pelo próximo, senão envia resposta 
CLR TI 
 42
MOV SBUF,#55 ;Transmite resposta e espera por 
EsperaT: JNB TI,EsperaT ;fim de transmissão 
 SJMP EsperaR 
 
Determine o modo de operação do canal série para enviar dados à 
tx.transmissão de 1250bps, utilizando um µC8051 a 12MHz. 
 
Para um relógio de 12 MHz, os modos de transmissão fixa oferecem uma 
taxa de transmissão de 1 Mbps, 375Kbps ou 187.5Kbps, conforme 
determinado nos exemplos anteriores. Sendo assim, é necessário 
utilizar um modo com taxa de transmissão variável. 
A taxa de overflow do timer é dividida conforme o modo e SMOD por 16 
(modo 1) , 32 (modo 1 ou 3) ou 64 (modo 3). Sendo assim, é 
necessário programar o timer 1 para que a taxa de overflow seja 16, 
32 ou 64 vezes maior do que a taxa que queremos. Deve escolher-se o 
valor que mais simplifica a configuração do timer 1 (que deve 
necessariamente ser configurado no modo 2 quando é usado para este 
efeito, i.é, para a geração da baudrate). 
Para programar o timer para uma taxa de overflow de 16x1250=20000 
overflows/s, é necessário obter um período de 1/20000 segundos entre 
overflow. Se o timer for alimentado com o relógio interno de 1Mhz, 
então existem 1000000/20000=50 transições de relógio entre cada 
overflow. 
Desta forma, pode ser utilizado o timer 1 em modo 2 (timer de 8 bits 
com recarregamento automático) com um valor de recarregamento de 
256-50=206. 
 43
 
Capítulo 10 
Processamento de Interrupções 
 
 
10.1 - Introdução 
 
Uma interrupção é uma suspensão temporária da execução do programa, para execução de 
uma rotina, designada normalmente por rotina de serviço à interrupção (RSI). 
Contrariamente ao pooling no qual o uC precisa testar por S/W (JNB flag,$) a flag de 
interrupção e aguardar que esta seja asserida, com interrupções esse teste é feito de forma 
“transparente”. Com efeito, no final da execução de cada instrução máquina, o uC vai testar, 
por H/W, todas as flags (permitidas no registo IE) de interrupção. Se alguma estiver asserida, 
a respectiva RSI será executada de imediato. 
No 8051, uma interrupção pode ser proveniente de 5 fontes distintas: 
8 Interrupção do Timer 0 (qd ocorre overflow da contagem do timer 0 ⇔ TF0→1) 
8 Interrupção do Timer 1 (qd ocorre overflow da contagem do timer 1 ⇔ TF1→1) 
8 Interrupção do Canal Série (quando RI→1 ou TI→1) 
8 Interrupção Externa 0 (quando IE0→1) 
8 Interrupção Externa 1 (quando IE1→1) 
Para permitir o atendimento correcto quando várias interrupções são activadas 
simultaneamente, a cada interrupção é atribuído um nível de prioridade (alta →1 ou baixa 
→0). No caso de duas interrupções com prioridade distinta ocorrerem simultaneamente, a 
interrupção de maior prioridade é atendida primeiro. No caso de ocorrerem 2 ou mais 
interrupções simultaneamente e com a mesma prioridade, então o uC vê-se obrigado a 
recorrer à sequência natural para ordenar o atendimento. 
Existe um espaço de 8 bytes reservado por cada uma destas interrupções, em endereços 
predefinidos na memória de código para a localização das RSI´s. Este conjunto de endereços 
predefinidos chama-se vectores de interrupção, pois é para um destes “vectores” que o uC é 
direccionado quando ocorre uma interrupção. Como este espaço é normalmente demasiado 
pequeno para conter a RSI completa, geralmente coloca-se aí uma instrução de salto para uma 
outra zona da memória que contém a RSI completa. 
 44
O salto para uma dada RSI é efectuado quando a flag associada a essa interrupção (flag de 
interrupção) é colocada a 1. Estas flags são apagadas pelo uControlador quando é efectuado o 
salto para a RSI respectiva, excepto no caso da interrupção do canal série e das interrupções 
externas quando são configuradas por nível. Para a interrupção do canal série, existe apenas 
um único vector de interrupção e existem duas flags possíveis que podem desencadear a 
interrupção. Uma delas indica o fim de transmissão de um dado (TI) enquanto a outra indica o 
fim de recepção (RI). O microcontrolador não apaga automaticamente as duas flags para que a 
RSI possa determinar se a interrupção foi provocada pela recepção ou transmissão de um 
caracter. 
 
10.2 - Tabela de Vectores de Interrupção 
 
Flag de Interrupção Designação da Interrupção Endereço do Vector de Interrupção 
- Interrupção do RESET 0000h 
IE0 Interrupção Externa 0 0003h 
TF0 Interrupção do Timer 0 000Bh 
IE1 Interrupção Externa 1 0013h 
TF1 Interrupção do Timer 1 001Bh 
TI ou RI Interrupção do Canal Série 0023h 
 
A interrupção de RESET é gerada quando se prime o botão de Reset. Tem prioridade 
máxima sobre qualquer outro processamento em curso. Não existe nenhuma RSI associada a 
esta interrupção. Quando ocorre a interrupção, o PC é automaticamente carregado com o 
endereço 0, no qual deverá estar, necessariamente, localizada a primeira instrução do 
programa ou um salto para o início do programa. Veja-seo exemplo seguinte: 
Exemplo 
 ORG 00h ou ORG 00h 
 LJMP inicio inicio: .... 
 ... 
inicio: ... 
 
No final da execução de cada instrução máquina e antes de iniciar a execução da próxima, 
o µC vai analisar o estado de todas as flags de interrupção (permitidas no registo IE). Se tiver 
 45
ocorrido um pedido de interrupção, isto é, se estiver asserida uma flag de interrupção, então 
acontece o seguinte: 
 1º- o conteúdo do registo PC é guardado em RAM interna (pilha). 
 2º- o registo PC é carregado com o endereço do vector de interrupção relativo à 
interrupção que está a ser processada/atendida. 
Quando acaba a execução da RSI (quando o microcntrolador executa a instrução RETI), o 
PC é novamente carregado com o endereço da instrução que estava para ser executada quando 
surgiu a interrupção. 
 
10.3 - Registos de Função Específica 
 
8 IE (Interrupt Enable) 
Este registo (endereçável bit a bit) serve para indicar ao microcontrolador quais as 
interrupções que ele deve considerar e quais as que deve ignorar. 
 
AFh AEh ADh ACh ABh AAh A9h A8h 
EA - - ES ET1 EX1 ET0 EX0 
 
8 EA (Enable All) → funciona como uma máscara: se estiver a zero inibe todas as 
interrupções; se estiver a um permite as interrupções cujos bits respectivos 
estiverem a 1 no registo IE. 
8 ES (Enable Serial) → se estiver a zero inibe a interrupção do canal série; se estiver 
a um permite a interrupção. 
8 ET1 (Enable Timer 1) → se estiver a zero inibe a interrupção do timer 1; se estiver 
a um permite a interrupção. 
8 EX1 (Enable External 1) → se estiver a zero inibe a interrupção externa 1; se 
estiver a um permite a interrupção. 
8 ET0 (Enable Timer 0) → se estiver a zero inibe a interrupção do timer 0; se estiver 
a um permite a interrupção. 
8 EX0 (Enable External 0) → se estiver a zero inibe a interrupção externa 0; se 
estiver a um permite a interrupção. 
 
8 IP (Interrupt Priority) 
 46
Este registo (endereçável bit a bit) serve para indicar a prioridade de cada uma das 
interrupções autorizadas na configuração do registo IE. 
 
BFh BEh BDh BCh BBh BAh B9h B8h 
- - - PS PT1 PX1 PT0 PX0 
 
8 PS (Priority Serial) → se estiver a zero dá prioridade baixa à interrupção do canal 
série; se estiver a um dá prioridade alta. 
8 PT1 (Priority Timer 1) → se estiver a zero dá prioridade baixa à interrupção do 
timer 1; se estiver a um dá prioridade alta. 
8 PX1 (Priority External 1) → se estiver a zero dá prioridade baixa à interrupção 
externa 1; se estiver a um dá prioridade alta. 
8 PT0 (Priority Timer 0) → se estiver a zero dá prioridade baixa à interrupção do 
timer 0; se estiver a um dá prioridade alta. 
8 PX0 (Priority External 0) → se estiver a zero dá prioridade baixa à interrupção 
externa 0; se estiver a um dá prioridade alta. 
 
No caso de duas ou mais interrupções serem lançadas simultaneamente e tiverem todas a 
mesma prioridade, então a ordem de atendimento respeitará a sequência natural: 
 Externa 0 → Timer 0 → Externa 1 → Timer 1 → Canal Série 
 
Exemplo 
Se os registos IE e IP forem inicializados da forma que se segue, 
indicar a ordem pela qual as interrupções são atendidas pelo 
processador, no caso de todas ocorrerem simultaneamente: 
 MOV IE,#10010111b 
 MOV IP,#01001101b 
Analisando o registo IE vê-se que: 
 - interrupções permitidas são: canal série, externa1, timer0 e 
externa0. 
No registo IP, e considerando apenas as interrupções validadas no IE 
temos: 
 - com prioridade alta: externa1 e externa0. 
 - com prioridade baixa: canal série e timer 0. 
 47
Recorrendo à sequência natural, e analisando primeiro o grupo de 
interrupções de prioridade alta e em seguida o grupo das que têm 
prioridade baixa, obtém-se a seguinte ordem de atendimento: 
1º - Externa0; 2ª - Externa1; 3ª - Timer0; 4ª - Canal Série. 
 
10.4 - Interrupção externas 
Existem duas interrupções causadas por fontes externas. Essas fontes externas estão ligadas 
ao microcontrolador através dos pinos P3.2(INT0) e P3.3(INT1), para a interrupção externa 0 
e interrupção externa 1, respectivamente. 
As interrupções externas podem ser configuradas para serem activadas por nível (nível 0) 
ou por transição (1→0) do sinal alimentado a esses pinos. 
O controlo das interrupções externas é feito através do registo de função específica TCON. 
 
8 TCON (Timer CONtrol) – 88h 
 
8Fh 8Eh 8Dh 8Ch 8Bh 8Ah 89h 88h 
- - - - IE1 IT1 IE0 IT0 
 
IEx, com x={0,1} → Flags de interrupção; quando é detectada uma interrupção externa, a 
respectiva flag é colocada a 1. 
ITx, com x={0,1} → Indica se a interrupção é activada por nível ou por transição (descendente). 
 
Exemplo: 
Fazer um programa que liga uma lâmpada(P1.0) quando um botão ligado 
no pino INT0 é premido (gera uma transição descendente). 
 ORG 00h 
 LJMP Inicio 
 ORG 03h 
 LJMP RsiExt0 
Inicio: MOV IE,#10000001b 
 SETB IT0 ; por transição 
 LJMP $ 
RsiExt0: SETB P1.0 
 RETI 
 
 48
10.5 - Pooling vs interrupções 
As interrupções permitem fazer sistemas que processam de forma muito eficaz as 
entradas/saídas de dados. Considere-se, por exemplo, a operação do canal série: quando é 
recebido um caracter, a flag RI á colocada a 1. Se a interrupção do canal série não estiver 
permitida no registo IE, o microcontrolador necessita de executar um ciclo que testa 
repetidamente o valor desta flag para detectar a recepção de um caracter, ficando “retido” 
nessa instrução (JNB RI,$) até a flag ser asserida pela UART. Este tipo de acesso a 
entrada/saída é designado por pooling (do inglês pool – sondagem). Se existirem outras fontes 
de dados que precisem de ser amostradas (por exemplo, o overflow temporizador ou um valor 
recebido externamente através de um porto), então é necessário testar também o valor das 
respectivas flags ou portos dentro desse mesmo ciclo. O funcionamento básico do pooling é 
demonstrado no seguinte programa em pseudo-código. 
 Ciclo: 
 Se RI=1, chama rotina RecepçãoSérie( ) 
 Se TI=1, chama rotina TransmissãoSérie( ) 
 Se TF0=1, chama rotina OverflowTimer0() 
 …. 
 Salta para Ciclo 
Existem vários inconvenientes na utilização de pooling. Por exemplo, não existe nenhuma 
forma simples de indicar que o processamento do overflow do timer é mais importante do que 
o processamento da transmissão série, por exemplo. Se o overflow acontecer imediatamente 
após o teste da flag TF0, todas as outras rotinas podem ser executadas antes do processamento 
do overflow ocorrer. Isto conduz-nos ao segundo problema: o tempo de resposta. Quanto 
maior o número de fontes de dados que precisam de ser amostrados, maior o tempo que 
demora cada volta do ciclo. Isso significa que o tempo decorrido entre o assinalar da flag e o 
tratamento dos dados respectivos pode ser demasiado grande. 
Por fim, note-se que nem tudo o que o µC faz é ler ou escrever dados em periféricos. 
Normalmente, é necessário efectuar processamentos adicionais, tais como o cálculo de valores 
para controlo, estatísticas, correcção e filtragem de erros, ou qualquer outro tipo de operação 
que não envolva directamente a leitura ou escrita de dados. Isto é um problema quando se 
utiliza looping, porque o processador está sempre a “queimar” tempo no ciclo à espera de 
dados de entrada. A solução mais simples é colocar esses cálculos dentro do próprio ciclo: 
 Ciclo: 
 Se RI=1, chama rotina RecepçãoSérie( ) 
 49
 Se TI=1, chama rotina TransmissãoSérie( ) 
 Se TF0=1, chama rotina OverflowTimer0() 
 … 
 Efectua calculos necessários 
 Salta para Ciclo 
 
Mas esta abordagem tem vários problemas óbvios: o tempo de resposta aos periféricos 
torna-se ainda maior e o tempo disponível para efectuar os cálculos é muito pequeno (sob 
pena de o tempode resposta aos periféricos ser incomportavelmente longo). 
A utilização de interrupções resolve estas questões de forma simples e eficaz. Em vez de 
testar continuamente a existência de dados para enviar/receber dos periféricos, utilizam-se 
RSIs que são executadas apenas quando existe realmente algo para tratar. No tempo restante, 
o µC está livre e pode dedicar-se a efectuar quaisquer cálculos que necessite. 
 Ciclo: 
 Efectua calculos necessários 
 Salta para Ciclo 
RSI do canal série: 
 Se RI=1, chama rotina RecepçãoSérie( ) 
 Senão chama rotina TransmissãoSérie( ) 
 
RSI do timer 0; 
 Chama rotina OverflowTimer0() 
 
Note que nesta nova versão da rotina com interrupções: 
8 As rotinas de tratamento de dados são chamadas imediatamente quando os dados estão 
disponíveis, ou seja, o atraso da resposta é mínimo. 
8 É possível atribuir uma prioridade elevada à RSI do timer 0, o que nos permite indicar que 
o processamento do overflow é mais urgente do que o processamento do canal série. 
8 Quando não está a ser feito tratamento de entrada/saída, o µC dedica-se exclusivamente a 
efectuar os cálculos que necessita, sem se preocupar em amostrar o estado das flags. Isto 
significa que sobra mais tempo ao µC para fazer os cálculos e que pode demorar tanto 
tempo quanto necessita (porque se for necessário, esses calculos serão interrompidos 
temporariamente para que seja efectuado o tratamento de entrada/saída) 
 
 50
Resumindo, para sistemas práticos, que necessitem de efectuar um tratamento intensivo e 
rápido de Entrada/Saída (E/S) e/ou que necessitem de efectuar processamento de cálculos 
demorados, a única opção plausível é a utilização de RSI para efectuar o processamento de 
E/S. 
 
10.6 – Exemplos 
 
A instrução ORG é uma directiva do compilador SES51 (usado nas aulas laboratoriais), 
que indica ao compilador qual o endereço inicial de memória de código a partir do qual deve 
ser armazenado o código. 
 
Programação de temporizador – interrupções 
Suponhamos que se pretende enviar uma onda quadrada, pelo pino P1.0, 
com período 400µs e duty cycle de 50%. Use o timer 0, supondo que o 
clock externo é de 12Mhz. 
 
ORG 00h 
LJMP Inicio ;Primeira instrução que vai ser executada 
ORG 0Bh 
LJMP Rsi_Timer0 
Inicio: 
MOV TL0,#56 ; há 200 contagens para cada metada da onda 
 MOV TH0,#56 
 MOV TMOD,#06h ;Programação como temporizador e no modo 2 
 MOV IP,#02h ;Prioridade alta 
 MOV IE,#82h ;Permissão de interrupção pelo timer 0 
 CLR TF0 
 SETB P1.0 ;Inicializa estado da onda quadrada 
 SETB TR0 ;Arranca temporização 
Repete:SJMP Repete ;Execução em ciclo infinito, sendo apenas 
;interrompida quando ocorre a interrupção 
;desencadeada pelo overflow ; ; do timer 0. 
Rsi_Timer0:CPL P1.0 
 RETI 
 
Programação de canal série usando interrupções 
 51
Programe o canal série para enviar dados com uma taxa de transmissão 
de 4200 bps, utilizando um µC 8051 a 12 MHz. Quando o µC receber o 
valor 1Bh pelo porto série, deverá escrever esse valor para o porto 
P1. 
 ORG 00h 
 LJMP Inicio 
 ORG 23h 
 LJMP Rsi_Cserie 
Inicio: MOV SCON,#01010000 ;Modo1, REN=1 
MOV PCON,#00h ;divisor de 64 
MOV TMOD,#20h; 
MOV TH1,#252 
MOV TL1,#252 
CLR TF1 
SETB TR1 
Repete: LJMP Repete 
 
Rsi_CSerie:CLR RI 
 MOV A,SBUF 
 CJNE A,#1Bh,Fim 
 MOV P1,A 
Fim: RETI 
 
Programação de um contador para contar 100 impulsos externos usando 
interrupções 
Este programa espera que ocorram 100 impulsos externos e envia o 
valor FFh pelo porto P2 quando tal acontece. 
 
Trata-se de uma contagem de acontecimentos externos pelo que será de 
supôr que a fonte desses impulsos externos estará ligada ao pino Tx 
{com x=1 ou 0} do porto P3. Vamos usar, por exemplo, o timer 0. 
• Período de clock interno? 
Não interessa, pois o timer vai funcionar como contador e não 
como temporizador. 
• Nº de contagens? 
Precisamos de contar 100 impulsos. 
• Tamanho do Temporizador? 
 52
Temporizador de 8 bits ⇒ permite até 256 contagens (<100). 
• Programação do registo TMOD = xxxx0010b (2d) 
Gate C/T M1 M0 Gate C/T M1 M0 
- - - - 0 1 1 0 
Gate=0 → controlo de ON↔ OFF feito por SW 
C/T=1 → vai funcionar como contador de impulsos externos 
Modo de funcionamento → M1=1, M0=0 
Temporizador de 8 bits ⇒ Modo 2 
⇓ 
 Valor de Recarregamento? TH0 =256-100=156 
 
• Programação do registo TCON=xx01xxxxb (16d) 
TF1 TR1 TF0 TR0 - - - - 
- - 0 1 
TF0=0 → limpar a flag de overflow 
TR0=1 → para dar início à contagem 
 
• Código 
ORG 00h 
LJMP Inicio 
ORG 0Bh 
LJMP Rsi_Timer0 
Inicio: MOV TH0,#156 
 MOV TL0,#156 
 MOV TMOD,#6 
MOV TCON,#16 
Espera: LJMP Espera 
Rsi_Timer0:MOV P2,#0FFh ;A Rsi_Timer0 é chamada quando o timer 0 
 RETI ;tiver atingido o overflow (o que 
 ;acontece a cada 100 contagens). O 
 ;apagamento da flag TF0 e o 
 ;recarregamento do registo TL0 são feitos 
 ;automaticamente. 
 53
 
Capítulo 11 
Sistemas Multiprocessador baseados em 8051´s 
 
 
11.1 – Introdução 
 
Um sistema distribuído é composto por vários sistemas distintos interligados, sendo cada 
um dos sistemas componentes baseado num microprocessador/microcontrolador. A 
comunicação entre os vários sistemas pode ser efectuada de formas distintas. No caso de 
sistemas baseados no µC 8051, é comum a utilização do porto série para esse efeito. O canal 
série dos µC 8051 está preparado para ser utilizado numa arquitectura distribuída chamada 
mestre/escravo. 
 
11.2 - Arquitectura Mestre/Escravo 
 
Na arquitectura Mestre/Escravo, como o próprio nome sugere, existe um sistema (Mestre) 
que “manda” nos restantes (Escravos). Todos os sistemas estão interligados da forma que se 
vÊ na figura. É o processador Mestre que “disciplina” toda a comunicação. Uma mensagem 
enviada pelo Mestre é recebida por todos. 
 
TXD RXD
Mestre Escravo 0 Escravo 1 Escravo n
TXD RXD TXD RXD TXD RXD
...
 
 
Naturalmente, é necessário impedir que haja problemas devido à utilização simultânea do 
canal série por mais do que um sistema, ao mesmo tempo. Na arquitectura Mestre-Escravo, 
cada um dos sistemas Escravo só pode comunicar quando recebe uma ordem para o fazer do 
sistema Mestre. 
Cada sistema Escravo está identificado com um endereço único. Este endereço tem 8 bits, 
é constante e é determinado de forma fixa pelo projectista do sistema. O Mestre indica a um 
dos sistemas Escravo que pretende comunicar com ele enviando o endereço desse sistema 
 54
pelo canal série. Todos os sistemas escravo estão à escuta da informação enviada pelo canal 
série do mestre. Quando um destes sistemas detecta que o seu endereço foi transmitido pelo 
sistema mestre, então sabe que: 
 
8 Todos os dados enviados até à próxima transmissão de um endereço são-lhe 
destinados. 
8 Nenhum outro sistema Escravo irá escrever no canal enquanto não for enviado outro 
endereço pelo sistema mestre. 
 
Isto é, o sistema escravo sabe que pode ler/escrever com exclusividade no canal série até 
que seja transmitido outro endereço. 
Para que este esquema de comunicação funcione, é necessário que exista alguma forma de 
distinguir entre dados e endereços. Tal como já se referiu, o canal série do µC 8051 suporta a 
arquitectura Mestre/Escravo. Para possiblitar a distinção entre endereços e dados, é 
disponibilizado um 9º bit adicional nos modos de transmissão 2 e 3. Este bit pode ser ligado 
ou desligado pelo sistema Mestre para indicar se o byte que vai enviar corresponde a um 
endereço de um Escravo ou se é um byte de dados (se o TB8 é colocado a 0 então é uma 
transmissão de dados, se for colocado a 1 é o endereço de um sistema Escravo). 
O bit TB8 é configurado (1 ou 0) pelo Mestre e é

Outros materiais