This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
O principal objectivo destas folhas de apoio à disciplina de Introdução aos Microprocessadores é dar ao aluno um texto de apoio sintético mas que foque os aspectos considerados importantes e essenciais sobre cada um dos tópicos do programa da disciplina. De modo, a que o aluno se sinta apoiado nas aulas teóricas e na realização dos trabalhos práticos.
Este texto não pretende substituir de forma alguma a bibliografia recomendada para a disciplina. E, deve ser entendido como um texto de apoio à matéria leccionada nas aulas teóricas da disciplina, como tal, o aluno deverá sempre ser assíduo e estudar continuamente a matéria leccionada para concretizar com êxito a realização da disciplina.
Nota ao aluno: Como este conjunto de folhas encontra-se ainda numa fase embrionária é natural que contenha alguns erros ortográficos e também técnicos. Portanto, agradece-se a colaboração activa de todos os alunos interessados que ao detectarem qualquer erro, ou ao lerem certo tópico do texto acharem-no menos esclarecedor que contactem o autor via e-mail [email protected] com as vossas criticas e sugestões.
• Memória de Acesso Aleatório - RAM (random access memory)
Para se compreender o funcionamento de uma memória de acesso aleatório, nada melhor que projectar em primeiro lugar a estrutura interna de um dispositivo destes recorrendo aos dispositivos digitais registo bidireccional e demultiplexer. No entanto, algumas considerações devem ser feitas sobre a sua estrutura interna. A RAM é constituída por um conjunto de registos de n bits cada e sendo cada registo identificado internamente através de um endereço personalizado. Assim, o número de bits de cada registo define a dimensão do conjunto de sinais por onde flui a informação de e para a RAM, este conjunto de sinais é designado por barramento de dados (data bus). A quantidade de registos internos define o número de bits necessários para o endereço de modo a que cada registo tenha um endereço distinto dos outros. Este conjunto de bits é designado por barramento de endereços (address bus). Existem ainda sinais de controlo que definem o fluxo de informação de e para a RAM que se designam respectivamente por OE/ (output enable) ou WE/ (write enable), ambos activos a zero. Também deve ser disponibilizado um sinal de controlo CE/ (chip enable) que função do seu nível lógico deve permitir realizar ou não operações sobre a RAM consoante esteja ou não activo.
Por exemplo, a seguinte figura ilustra a arquitectura interna de uma RAM 4x8, ou seja, um dispositivo com 4 registos de 8 bits cada. E/
Depois de se compreender o modo de funcionamento e a estrutura interna de uma RAM pode-se então proceder ao projecto de um sistema simples que permita ler e escrever dados de uma RAM 16x8.
No sistema sugerido existem dois registos com os seguintes objectivos:
• Sistema de acesso à memória
Memory Address Register (MAR) define um endereço a 4 bits seleccionando a posição de memória a aceder. O seu conteúdo pode ser estabelecido muito simplesmente através de um thumbweel switch hexadecimal quando o sinal Address/Data/ está a 1 e houver a transição ascendente do sinal Strobe, ou então, por acção do sinal Next quando se pretenda aceder ao endereço seguinte;
Memory Buffer Register (MBR) contém os 8 bits de dados a escrever na RAM. O seu conteúdo pode ser definido através de dois thumbweel switch quando o sinal Address/Data/ está a 0 e houver a transição ascendente do sinal Strobe.
Instrução Funcionalidade MOV A, end8 A= (end8) MOV end8, A (end8)= A SJMP rel4 PC= PC + rel4 LJMP end8 PC= end8 ADDC A, end8 A= A + (end8) + C SUBB A, end8 A= A – (end8) - C CJNC A, end8, rel4 A-(end8), Se (Cy=0) PC+= rel4 CJNE A, end8, rel4 Se (A-(end8)0) PC+= rel4 SETB C C= 1 CLR C C= 0
Exercício: Pretende-se desenhar a estrutura interna do CPU especificado bem como a sua interligação à memória.
Para a realização do exercício vamos fazer uma breve revisão do formalismo gráfico Basic Schemata que nos vai servir como ferramenta para o projecto de hardware complexo. Também vamos rever o conceito de instrução e a sua decomposição/composição em subcampos como auxiliar importante para a codificação em binário.
Considere uma unidade de processamento central (CPU – Central Processing Unit) como uma arquitectura de Von Newman, na qual os dados e o código são acedidos através dos mesmos barramentos de endereço e dados. Os barramentos têm ambos 8 bits. O CPU suporta o seguinte conjunto de instruções:
• Projecto da Estrutura Interna de um CPU e sua Interligação à Memória
• Esquema de fluência de informação - EFIEste esquema disponibiliza dois mecanismos. Um mecanismo chamado registo que serve para
armazenar informação e que é representado por um rectângulo, tal e qual o ilustrado na seguinte figura:
Um outro mecanismo é designado acção e representa operações sobre um ou mais registos, sejam elas de transferência de informação, aritméticas ou lógicas. Este mecanismo gráfico tem a sintaxe ilustrada na seguinte figura:
•Esquema de Sequência de Acções – ESAEste esquema é composto por quatro mecanismos.
O mecanismo acção onde se evoca o nome de uma acção existente no EFI e que obedece à seguinte sintaxe gráfica:
Nome da acção
Operaçãoou
Nome da acção
Operação - símbolo de acção.
Nome da acção
Nome Nº bits - símbolo de registo.
•Basic Schemata
O basic schemata é um formalismo gráfico constituído por dois esquemas.
No exemplo da figura anterior, quando a avaliação da expressão lógica for verdadeira (V) o conjunto de acções a executar é o referido por “acções1”, caso contrário são executadas as acções identificadas por “acções2”. Quando terminado um conjunto de acções o fluxo de acções continua pelo ramo central.
Um outro mecanismo é designado decisão simples e representa um teste sobre uma expressão lógica englobando uma ou mais entidades existentes no EFI e cujo resultado define o conjunto de acções a executar. O seu grafismo é:
acções1 acções2
V FE.L.
Expressão Lógica
O terceiro mecanismo é designado por repetição e consiste na avaliação de uma expressão lógica cujo resultado pode originar a execução de um conjunto de acções de um modo repetitivo. O grafismo inerente ao mecanismo de repetição é:
O último mecanismo é designado por decisão múltipla e consiste na avaliação de uma expressão cujo resultado pode originar múltiplos valores e no qual cada valor conduz à execução de um conjunto exclusivo de acções. A sintaxe gráfica é representada na seguinte figura:
Expressão a avaliar
valor1 valor3valor2 valorN
Acções 1 Acções 2 Acções 3 Acções N
- decisão múltipla
• Instruções
As instruções são em geral agrupadas em classes ou famílias. As classes mais relevantes de instruções são as designadas por transferência de informação, aritméticas, lógicas e de controlo.
As instruções de transferência de informação são responsáveis pelas as acções de leitura ou escrita em registos, contadores e flags da estrutura interna do CPU bem como pela a alteração ou leitura de valores em dispositivos externos ao CPU mas que partilhem todos os seus barramentos.
As instruções aritméticas são aquelas que desempenham operações aritméticas e em geral são realizadas por uma máquina designada por unidade aritmética e lógica (ALU – arithmetic logic unit) e que é parte integrante da estrutura interna do CPU.
As instruções lógicas implementam as funções lógicas comuns e em geral são realizadas na ALU.
As instruções de controlo são todas as que podem alterar, de alguma forma, a execução sequencial de instruções.
Depois de uma breve revisão dos tipos de instruções vamos de seguida analisar o problema da codificação das instruções tendo como exemplo o exercício proposto.
De uma forma pragmática podemos dizer que o CPU a projectar tem dez instruções logo necessito de pelo menos 4 bits para a sua codificação. O modo de codificar as instruções pode ser realizado utilizando 2 abordagens completamente distintas que conduzem a complexidades de hardware e de estruturação muito diversas.
A primeira abordagem pode ser designada como força bruta e consiste em atribuir um código binário distinto a cada instrução sem qualquer tipo de critério. Por exemplo, se o CPU tem 10 instruções então uma possível codificação a 4 bits é atribuir valores entre 0000B e 1001B a cada uma das instruções, resolvendo-se este problema.
A segunda abordagem é uma abordagem estruturada onde os critérios são perfeitamente bem definidos quanto à funcionalidade de cada um dos bits da codificação. A figura seguinte ilustra esta abordagem para o exercício proposto.
Código da instrução
D C1 C0 T
D – Dimensão0 1 byte1 2 bytes
C1 C0 – Classe 0 0 Transf. de Informação 0 1 Aritmética 1 X Controlo
T – TipoExemplo para C1= 0 e C0=1:0 ADDC1 SUBB
Portanto, estamos a assumir que as instruções do nosso CPU têm duas dimensões possíveis, de 1 ou 2 bytes. Então, de seguida vamos esclarecer como se calcula a dimensão de cada uma das instruções. Basicamente a dimensão depende do número de bits do código de identificação e do número de bits dos operandos da instrução.
Depois de termos projectado o CPU, utilizando o formalismo basic schemata, precisamos de verificar a correcção do desenho. Para isso, precisa-se de realizar um programa de prova de correcção numa qualquer linguagem de alto nível (C, C++, Java, etc).
De seguida, apresenta-se o programa de prova da correcção nas linguagens C, C++ e JAVA. Um aspecto importante, é que o programa não tem interface com o utilizador, sendo esta realizada utilizando a opção watch dos ambientes de desenvolvimento utilizados.
• Programa em C /////////////////////////// Inicio do programa em C //////////////////////////////
// definir tipo byte e word#define byte unsigned char#define word unsigned int
// definição da sintaxe das instruções para introdução do programa#define MOVA 0x08#define MOVM 0x09#define _SJMP 0x04#define SJMP(R) (_SJMP | (R << 4))#define LJMP 0x0C#define ADDC 0x0A
void InitRam(){ // Exemplo: Colocar na posição de memória com o endereço 12H
// o maior valor das posições de memória 10H e 11H// código em 0RAM[0x00]= MOVA;RAM[0x01]= 0x10;RAM[0x02]= CJNC(+2);RAM[0x03]= 0x11;RAM[0x04]= MOVA;RAM[0x05]= 0x11;RAM[0x06]= MOVM;RAM[0x07]= 0x12;RAM[0x08]= SJMP(-1);// dados inicializados em 0x10RAM[0x10]= 0xFF;RAM[0x11]= 0x01;// variável de saídaRAM[0x12]= 0;
InitRam(); // faz load do programa em RAMResetCPU(); // simula a activação do reset do CPUCpuRun(); // run ao CPU
}///////////////////// FIM do programa em C /////////////////////////
• Programa em C++///////////////////////////////////////////////////// ficheiro CPU.H ////////////////////////////////////////////// tipo byte e word#define byte unsigned char#define word unsigned int
// definição da sintaxe das instruções para introdução do programa#define MOVA 0x08#define MOVM 0x09#define _SJMP 0x04#define SJMP(R) (_SJMP | ((R & 0x0F) << 4))#define LJMP 0x0C#define ADDC 0x0A#define SUBB 0x0B#define _CJNC 0x0D
void RAM::LoadPrograma(){ // Exemplo: Colocar na posição de memória com o endereço 0CH
// o maior valor das posições de memória 0AH e 0BH
// código em 0ram[0x00].PL(MOVA);ram[0x01].PL(0x10);ram[0x02].PL(CJNC(+2));ram[0x03].PL(0x11);ram[0x04].PL(MOVA);ram[0x05].PL(0x11);ram[0x06].PL(MOVM);ram[0x07].PL(0x12);ram[0x08].PL(SJMP(-1));// dados inicializados em 0x10ram[0x10].PL(0xFE);ram[0x11].PL(0xFF);// variável de saída em 0x12
public class Registo { // variáveis protected int valor; // construtor public Registo() { valor= 0; } // métodos public int OE() { return valor; } public void PL(int v) { valor= v & 0xFF; }} /// fim de ficheiro
/// início do ficheiro Instrucao.javapackage CPUX;
public interface Instrucao { // definição da código das instruções para introdução do programa int MOVA= 0x08; int MOVM= 0x09; int SJMP= 0x04; int LJMP= 0x0C; int ADDC= 0x0A; int SUBB= 0x0B; int CJNC= 0x0D; int CJZ= 0x0F; int SETC= 0x00; int CLRC= 0x01;
public abstract int CJZ(int op); public abstract int CJNC(int op); public abstract int SJMP(int op);} /// fim de ficheiro
///início do ficheiro Contador.javapackage CPUX;
// import Registo.*;public class Contador extends Registo { public Contador() { valor= 0; } public void CLR() { valor= 0; } public void INC() { ++valor; } public void DEC() { --valor; }} /// fim de ficheiro
/// início do ficheiro Flag.javapackage CPUX;
public class Flag { // variáveis private boolean valor; // construtor public Flag() { valor= false; } // métodos public boolean OE() { return valor; } public void CLR() { valor= false; } public void SET() { valor= true; } public void LD(boolean v) { valor= v; }} /// fim de ficheiro
/// continuaçãopublic void LoadPrograma() { // Exemplo: Colocar na posição de memória com o endereço 12H // o maior valor das posições de memória 10H e 11H
// código em 0 mem.Write(0x00, MOVA); mem.Write(0x01, 0x10); mem.Write(0x02, CJNC(+2)); mem.Write(0x03, 0x11); mem.Write(0x04, MOVA); mem.Write(0x05, 0x11); mem.Write(0x06, MOVM); mem.Write(0x07, 0x12); mem.Write(0x08, SJMP(-1)); // dados inicializados em 0x10 mem.Write(0x10, 0xFE); mem.Write(0x11, 0xFF); // variável de saída em 0x12 } public void Run() {
for(;;){
FasePreparacao();FaseExecucao();
} }}/// fim de ficheiro
/// início do ficheiro KitXXX.javapackage CPUX;
public class KitXXX { public static void main(String args[]) {
// criação da memória Memoria m= new Memoria();
// criação do CPU associando-lhe uma memória CPU x= new CPU(m);
// carregar o programa de teste na memoria do Kit x.LoadPrograma();
• Simulação do CPU, DMA e RAM com interface gráfica em JAVAAlgumas considerações devem ser realizadas antes da apresentação do código do programa em Java devido à simulação aqui descrita diferir nalguns conceitos e em complexidade relativamente ao anteriormente apresentado.
Em primeiro lugar o que é um DMA? DMA é a sigla abreviada do termo anglo-saxónico Direct Memory Access que em português traduz um dispositivo de acesso directo a uma memória. Por outras palavras é uma máquina hardware (previamente descrita no ponto “sistema de acesso a uma memória”) que permite a leitura ou a escrita numa memória.
A segunda questão que se coloca é: como dois dispositivos físicos podem aceder à mesma memória? O problema do acesso múltiplo de duas máquinas hardware a um mesmo dispositivo de memória consiste na partilha física dos barramentos de dados, endereço e controlo que em caso de simultaneidade temporal no acesso poria em risco a integridade física das respectivas máquinas.
A solução simples deste problema passa por estabelecer um protocolo físico entre as duas máquinas designado por HOLD/HLDA (abreviatura de hold/hold acknowledge). A ideia deste protocolo é baseada no facto de existir uma máquina que por defeito acede à memória, designada por sistema Mestre (Master em inglês) e a outra máquina que pretende o acesso à memória é designada por sistema Escravo (Slave em inglês). Este protocolo estabelece uma hierarquia de acesso à memória por hardware designada por Mestre-Escravo. O sinal físico Hold é uma entrada do sistema Mestre e é uma saída do sistema Escravo, permitindo ao Escravo indicar ao Mestre que pretende aceder à memória. Existe o sinal HLDA (HoLD Acknowledge) que é uma saída do sistema Mestre e uma entrada no sistema Escravo que indica ao sistema Escravo que o Mestre libertou os barramentos e que este pode aceder aos barramentos. Quando o sistema Escravo deixa de aceder aos barramentos por ter executado todas as suas tarefas descativa o sinal Hold e o sistema Mestre ao detectar a desactivação deste sinal também desactiva o sinal HLDA passando a aceder novamente à memória.
Nota: Na figura, o ESA do CPU foi redesenhado tendo em atenção que originará uma máquina de estados hardware o que faz corresponder a cada nível de acções um número que é o identificador de estado.
Neste ponto, apresenta-se um sistema software desenvolvido em Java que implementa a arquitectura ilustrada. A interface é gráfica e é composta por 3 janelas, cada uma das quais ilustra ao “raio-x” o conteúdo de cada dispositivo permitindo o utilizador interagir com o DMA e com o modo de funcionamento do CPU. O sistema é composto por três componentes Memória, DMA e CPU que são implementados utilizando os seguintes ficheiros e classes:
•SistemaKitXXX.java – define a classe KitXXX que tem o método “main” e que aglotina todos os dispositivos físicos do sistema.
• MemóriaMemoria.java - define a classe Memória e as acções de leitura e escrita sobre o dispositivo. E também inclui o suporte da interface gráfica.
•DMADMA.java – define um DMA e todos os registos e sinais que o utilizador pode manipular para concretizar a escrita e a leitura da memória.
•CPU•CPUComInterface.java – define a classe com o mesmo nome que implementa a interface gráfica do CPU.Flag.java – define a classe Flag que implementa um flip-flop e os respectivos métodos de manipulação.Registo.java – define a classe Registo bem como os métodos de operação.Contador.java – a classe Contador aumenta a operacionalidade do Registo tal como em hardware.ALU.java – define uma classe ALU que permite realizar operações aritméticas ou lógicas.InstrucaoRegistos.java – define uma interface abstracta designada InstrucaoRegistos aonde se identificam os recursos físicos do CPU, tais como flags, registos, contadores...CPU.java – define a classe CPU que contém toda a arquitectura interna do CPU definida pelo projectista em Basic Schemata e que serve para provar a sua correcção.
Nota: O aluno só deve alterar ou interferir na classe CPU e na interface InstrucaoRegistos.
public class KitXXX { public static void main(String args[]) { // criação da memória Memoria m= new Memoria();
// criação do DMA associando-lhe uma memória DMA d= new DMA(m);
// criação do CPU associando-lhe uma memória CPUComInterface x= new CPUComInterface(m);
for (;;){ x.Hold(d.Hold()); // ligar o hold do DMA ao hold do CPU d.Holda(x.Holda()); // ligar o holda do CPU ao holda do DMA } }}// fim de ficheiro
// continuação do ficheiro Memoria.javaprivate String Int2Hex(int v){ return new String(Integer.toHexString(v)); }
public Memoria() { // inicialização das posições de memória m= new Registo[DIM_MEM]; for(int i= 0 ; i<DIM_MEM ; ++i) // reserva de espaço em memória m[i]= new Registo();
//inicialização dos grafismos lista= new List(16, false); Font font= new Font("Monospaced", Font.PLAIN, 12); lista.setFont(font); lista.setBackground(Color.yellow); for (int adr= 0 ; adr<DIM_MEM ; ++adr) lista.add("M(" + Int2Hex(adr) + "):" + Int2Hex(m[adr].OE()), adr); janela= new Frame("Memória"); janela.setBackground(Color.yellow); janela.add(lista); janela.setBounds(0, 0, 140, 280); janela.show(); }
public void refresh(int end, int dados) { lista.replaceItem("M(" + Int2Hex(end) + "):" + Int2Hex(m[end].OE()), end); janela.show(); }
public int Read(int end) { return m[end].OE(); }
public void Write(int end, int dados) { m[end].PL(dados); refresh(end, dados); }}// fim de ficheiro
public void run(Registo a, Registo mbr, Registo ir, Flag cy) { if ((ir.OE() & 1) == 1) { aux= a.OE() - mbr.OE() - (cy.OE()? 1 : 0); cy.LD(aux < 0); } else { aux= a.OE() + mbr.OE() + (cy.OE()? 1 : 0); cy.LD(aux > 0xFF); } a.PL(aux); }}
// fim de ficheiro
// ficheiro InstrucaoRegistos.javapackage CPUX;
public interface InstrucaoRegistos { /* definição da código das instruções existentes no CPU int MOVA= 0x08; int MOVM= 0x09; int SJMP= 0x04; int LJMP= 0x0C; int ADDC= 0x0A; int SUBB= 0x0B; int CJNC= 0x0D; int CJZ= 0x0F; int SETC= 0x00; int CLRC= 0x01;
*/
// descodificador de instrução int [] DecoderInstrucao={27,28,0,0,13,0,0,0,9,11,15,15,14,17,0,22};
// identificação dos registos int MBR= 0; int REL= 1; int IR= 2; int TMP= 3; int A= 4; int NREGISTOS= 5; // identificação dos contadores int ESTADO= 0; int MAR= 1; int PC= 2; int NCONTADORES= 3; // identificação de flags int CY= 0; int TCY= 1; int HOLD= 2; int HOLDA= 3; int NFLAGS= 4;}// fim de ficheiro
protected void CriaRecursos(Memoria m){ mem= m; au= new ALU(); // criação de flags f= new Flag[NFLAGS]; f[CY]= new Flag("CY"); f[TCY]= new Flag("TCY"); f[HOLD]= new Flag("HOLD"); f[HOLDA]= new Flag("HOLDA"); // criação de contadores c= new Contador[NCONTADORES]; c[ESTADO]= new Contador("ESTADO"); c[MAR]= new Contador("MAR"); c[PC]= new Contador("PC"); // criação de registos r= new Registo[NREGISTOS]; r[MBR]= new Registo("MBR"); r[REL]= new Registo("REL"); r[IR]= new Registo("IR"); r[TMP]= new Registo("TMP"); r[A]= new Registo("A");}
public void Reset(){ CLRPC(); CLRESTADO();}
// continua
// continuação de CPU.java public void Run(){ switch (RDESTADO()) { case 0: // Fase de Preparação if (RDHOLD()) SETHOLDA(); else{ CLRHOLDA(); IESTADO(); }break; case 1: RDPC(); IESTADO(); break; case 2: RDM(); IESTADO(); break; case 3: IMAR(); WRIR(); WRREL(); if (IR3()) IESTADO(); else WRESTADO(6); break; // 2 bytes case 4: RDM(); IESTADO(); break; case 5: IMAR(); IESTADO(); break; case 6: WRPC(); IESTADO(); break; case 7: WRMAR(); WRESTADO(DecoderInstrucao[RDIR()]); break; case 8: WRPC(); WRESTADO(DecoderInstrucao[RDIR()]); break; case 9: // Fase de Execução RDM(); IESTADO(); break; // MOV A, M case 10: WRA(); CLRESTADO(); break; // MOV A, M case 11: RDA(); IESTADO(); break; // MOV M, A case 12: WRM(); CLRESTADO(); break; // MOV M, A case 13: ADDREL(); CLRESTADO(); break; // SJMP rel4 case 14: WRPC(); CLRESTADO(); break; // LJMP end8 case 15: RDM(); IESTADO(); break; // ADDC A, M : SUBB A, M case 16: au.run(r[A], r[MBR], r[IR], f[CY]); CLRESTADO(); break; // ADDC A, M : SUBB A, M case 17: RDM(); WRTMP(); WRTCY(); IESTADO(); break; // CJNC A, M, rel4 case 18: CLRCY(); IESTADO(); break; // CJNC A, M, rel4 case 19: au.run(r[A], r[MBR], r[IR], f[CY]); if (RDCY()) WRESTADO(21); else IESTADO(); break; // CJNC A, M, rel4 case 20: ADDREL(); IESTADO(); break; // CJNC A, M, rel4 case 21: RDTMP(); RDTCY(); CLRESTADO(); break; // CJNC A, M, rel4 case 22: RDM(); WRTMP(); WRTCY(); IESTADO(); break; // CJZ A, M, rel4 case 23: CLRCY(); IESTADO(); break; // CJZ A, M, rel4 case 24: au.run(r[A], r[MBR], r[IR], f[CY]); IESTADO(); if (AZERO()) IESTADO(); else WRESTADO(26); break; // CJZ A, M, rel4 case 25: ADDREL(); IESTADO(); break; // CJZ A, M, rel4 case 26: RDTMP(); RDTCY(); CLRESTADO(); break; // CJZ A, M, rel4 case 27: SETCY(); CLRESTADO(); break; // SETB C case 28: CLRCY(); CLRESTADO(); break; // CLR C }}}// fim de ficheiro
Exercício: Acrescente ao CPU projectado as seguintes instruções.
Proceda às alterações da estrutura interna do CPU projectado de modo a que este suporte todo o conjunto de instruções.
No programa simulador apenas deve modificar os ficheiros CPU.java e InstrucaoRegistos.java de modo a que respeitem o projecto realizado em termos de ESA e EFI..
Instrução Funcionalidade MOV B, A B= A DJNZ A, rel4 - -A, Se (A 0) PC+= rel4 ANL A, B A= A . B ORL A, B A= A + B CPL A A= A/ CJNE A, #const8, rel4 Se (A const8) PC+= rel4
Exemplo de programa: Colocar como conteúdo da posição de memória de endereço 12H o maior valor existente entre os conteúdos das posições de memória com endereços 10H e 11H.
endereço código hexadecimal instrução0: 08, 10 MOV A, 10H2: 2D, 11 CJNC A, +2, 11H4: 08, 11 MOV A, 11H6: 09, 12 MOV M, 12H8: F4 SJMP -1
Nesta secção pretende-se estudar o CPU80535 numa perspectiva de programador. Assumindo-se que este já tem ao seu dispor uma arquitectura de desenvolvimento baseada no CPU80535. Deste modo, o programador tem de conhecer os aspectos principais da estrutura interna do CPU, tais como, a organização de memória, os modos de endereçamento e as instruções para lhe permitir o começo do desenvolvimento de programas de aplicação. De seguida vamos abordar todos estes aspectos.
• Organização de memória
MemóriaExterna
Memória de Código
0H
FFFFH
• Algumas características a ter em conta:Address Bus externo – 16 bitsData Bus – 8 bitsInstruções de 1, 2 ou 3 bytes de dimensão.
Mnemónica Funcionalidade Nº bytes Nº ciclos ADD A, Rn A= A + Rn 1 1 ADD A, directo A= A + (directo) 2 1 ADD A, @Ri A= A + (Ri) 1 1 ADD A, #constante8 A= A + constante8 2 1 ADDC A, Rn A= A + Rn + C 1 1 ADDC A, directo A= A + (directo) + C 2 1 ADDC A, @Ri A= A + (Ri) + C 1 1 ADDC A, #constante8 A= A + constante8 + C 2 1 SUBB A, Rn A= A – Rn - C 1 1 SUBB A, directo A= A – (directo) - C 2 1 SUBB A, @Ri A= A – (Ri) - C 1 1 SUBB A, #constante8 A= A – constante8 - C 2 1 INC A A= A + 1 1 1 INC Rn Rn = Rn + 1 1 1 INC directo (directo) = (directo) + 1 2 1 INC @Ri (Ri) = (Ri) + 1 1 1 INC DPTR DPTR= DPTR + 1 1 2 DEC A A= A - 1 1 1 DEC Rn Rn= Rn - 1 1 1 DEC directo (directo) = (directo) - 1 2 1 DEC @Ri (Ri) = (Ri) - 1 1 1 MUL AB A= A x B 1 4 DIV AB A= A / B 1 4 DA A A= BCD(A) 1 1
ANL A, Rn A= A and Rn 1 1 ANL A, directo A= A and (directo) 2 1 ANL A, @Ri A= A and (Ri) 1 1 ANL A, #constante8 A= A and constante8 2 1 ANL directo, A (directo)= (directo) and A 2 1 ANL directo, #constante8 (directo)= (directo) and constante8 3 2 ORL A, Rn A= A or Rn 1 1 ORL A, directo A= A or (directo) 2 1 ORL A, @Ri A= A or (Ri) 1 1 ORL A, #constante8 A= A or constante8 2 1 ORL directo, A (directo)= (directo) or A 2 1 ORL directo, #constante8 (directo)= (directo) or constante8 3 2 XRL A, Rn A= A xor Rn 1 1 XRL A, directo A= A xor (directo) 2 1 XRL A, @Ri A= A xor (Ri) 1 1 XRL A, #constante8 A= A xor constante8 2 1 XRL directo, A (directo)= (directo) xor A 2 1 XRL directo, #constante8 (directo)= (directo) xor constante8 3 2 CLR A A= 0 1 1 CPL A A= A/ 1 1 RL A An=An-1 : n[1, 7], A0=A7 1 1 RLC A An =An-1 : n[1, 7], A0=C, C=A7 1 1 RR A An-1=An : n[1, 7], A7=A0 1 1 RRC A An-1 =An : n[1, 7],A7=C, C=A0 1 1 SWAP A A0-3
Instruções para Manipulação de Bits Mnemónica Funcionalidade Nº bytes Nº ciclos
CLR C C= 0 1 1 CLR bit bit= 0 2 1 SETB C C= 1 1 1 SETB bit bit= 1 2 1 CPL C C= C/ 1 1 CPL bit bit= bit/ 2 1 ANL C, bit C= C and bit 2 2 ANL C, /bit C= C and bit/ 2 2 ORL C, bit C= C or bit 2 2 ORL C, /bit C= C or bit/ 2 2 MOV C, bit C= bit 2 1 MOV bit, C bit= C 2 2
Algumas notas:Rn – qualquer registo desde R0 até R7.Ri – registos R0 e R1.Directo, directod, directof – RAM interna desde 00H até 7FH, Portos 0 a 6, qualquer registo de status e control.bit – qualquer dos 128 bits da memória interna, qualquer bit dos Portos, qualquer bit de status e control.A – acumulador.MX – posição de memória externa.MC – posição de memória de código (externa ou interna).relativo8 – valor a 8 bits entendido a código de complementos.endereço16 – valor a 16 bits entendido como um endereço.endereço11 – valor a 11 bits entendido como um endereçoconstante8 – valor a 8 bits.
Instruções que afectam as flags devido à sua operação
Mnemónica CY OV AC ADD ADDC SUBB MUL 0 DIV 0 DA CJNE
Instruções para Manipulação de Bits Mnemónica Funcionalidade
CLR C C= 0 CLR bit bit= 0 SETB C C= 1 SETB bit bit= 1 CPL C C= C/ CPL bit bit= bit/ ANL C, bit C= C and bit ANL C, /bit C= C and bit/ ORL C, bit C= C or bit ORL C, /bit C= C or bit/ MOV C, bit C= bit MOV bit, C bit= C
Instruções de Controlo Mnemónica Funcionalidade
ACALL endereço11 (++SP)= PCL, (++SP)= PCH, PC0-10= endereço11 LCALL endereço16 (++SP)= PCL, (++SP)= PCH, PC0-10= endereço16 RET PCH= (SP), PCL=(-- SP), -- SP RETI PCH= (SP), PCL=(-- SP), -- SP, repõe interrupts AJMP endereço11 PC0-10= endereço11 LJMP endereço16 PC= endereço16 SJMP relativo8 PC= PC + relativo8 JMP @A+DPTR PC= DPTR + A JZ relativo8 Se (A=0) PC= PC + relativo8 JNZ relativo8 Se (A0) PC= PC + relativo8 JC relativo8 Se (C) PC= PC + relativo8 JNC relativo8 Se (C=0) PC= PC + relativo8 JB bit, relativo8 Se (bit) PC= PC + relativo8 JNB bit, relativo8 Se (bit=0) PC= PC + relativo8 JBC bit, relativo8 Se (bit) PC= PC + relativo8, bit= 0 CJNE A, directo, relativo8 Se (A(directo)) PC= PC + relativo8 CJNE A, #constante8, relativo8 Se (Aconstante8) PC= PC + relativo8 CJNE Rn, #constante8, relativo8 Se (Rnconstante8) PC= PC + relativo8 CJNE @Ri, #constante8, relativo8 Se ((Ri)constante8) PC= PC + relativo8 DJNZ Rn, relativo8 -- Rn, Se (Rn0) PC= PC + relativo8 DJNZ directo, relativo8 --(directo), Se ((directo)0) PC= PC + relativo8 NOP Não faz nada, apenas consome tempo
Instruções de Transferência de Informação Mnemónica Funcionalidade
MOV A, Rn A= Rn MOV A, directo A= (directo) MOV A, @Ri A= (Ri) MOV A, #constante8 A= constante8 MOV Rn, A Rn= A MOV Rn, directo Rn= (directo) MOV Rn, #constante8 Rn= constante8 MOV directo, A (directo)= A MOV directo, Rn (directo)= Rn MOV directod, directof (directod)= (directof) MOV directo, @Ri (directo)= (Ri) MOV directo, #constante8 (directo)= constante8 MOV @Ri, A (Ri)= A MOV @Ri, directo (Ri)= (directo) MOV @Ri, #constante8 (Ri)= constante8 MOV DPTR, #constante16 DPTR= constante16 MOVC A, @A+DPTR A= MC(A+DPTR) MOVC A, @A+PC A= MC(A+PC) MOVX A, @Ri A= MX(Ri) MOVX A, @DPTR A= MX(DPTR) MOVX @Ri, A MX(Port2, Ri)= A MOVX @DPTR, A MX(DPTR)= A PUSH directo SP= SP +1, (SP)= (directo) POP directo (directo)= (SP), SP= SP - 1 XCH A, Rn A Rn XCH A, directo A (directo) XCH A, @Ri A (Ri) XCHD A, @Ri A0-3 (Ri)0-3
Instruções Aritméticas Mnemónica Funcionalidade
ADD A, Rn A= A + Rn ADD A, directo A= A + (directo) ADD A, @Ri A= A + (Ri) ADD A, #constante8 A= A + constante8 ADDC A, Rn A= A + Rn + C ADDC A, directo A= A + (directo) + C ADDC A, @Ri A= A + (Ri) + C ADDC A, #constante8 A= A + constante8 + C SUBB A, Rn A= A – Rn - C SUBB A, directo A= A – (directo) - C SUBB A, @Ri A= A – (Ri) - C SUBB A, #constante8 A= A – constante8 - C INC A A= A + 1 INC Rn Rn = Rn + 1 INC directo (directo) = (directo) + 1 INC @Ri (Ri) = (Ri) + 1 INC DPTR DPTR= DPTR + 1 DEC A A= A - 1 DEC Rn Rn= Rn - 1 DEC directo (directo) = (directo) - 1 DEC @Ri (Ri) = (Ri) - 1 MUL AB A= A x B DIV AB A= A / B DA A A= BCD(A)
ANL A, Rn A= A and Rn 1 1 ANL A, directo A= A and (directo) 2 1 ANL A, @Ri A= A and (Ri) 1 1 ANL A, #constante8 A= A and constante8 2 1 ANL directo, A (directo)= (directo) and A 2 1 ANL directo, #constante8 (directo)= (directo) and constante8 3 2 ORL A, Rn A= A or Rn 1 1 ORL A, directo A= A or (directo) 2 1 ORL A, @Ri A= A or (Ri) 1 1 ORL A, #constante8 A= A or constante8 2 1 ORL directo, A (directo)= (directo) or A 2 1 ORL directo, #constante8 (directo)= (directo) or constante8 3 2 XRL A, Rn A= A xor Rn 1 1 XRL A, directo A= A xor (directo) 2 1 XRL A, @Ri A= A xor (Ri) 1 1 XRL A, #constante8 A= A xor constante8 2 1 XRL directo, A (directo)= (directo) xor A 2 1 XRL directo, #constante8 (directo)= (directo) xor constante8 3 2 CLR A A= 0 1 1 CPL A A= A/ 1 1 RL A An=An-1 : n[1, 7], A0=A7 1 1 RLC A A0=C, C=A7, An =An-1 : n[1, 7], 1 1 RR A An-1=An : n[1, 7], A7=A0 1 1 RRC A A7=C, C=A0, An-1 =An : n[1, 7], 1 1 SWAP A A0-3
Exercício 1: Faça um programa que determine o maior de 2 números do tipo byte. i. Os 2 números encontram-se respectivamente nas posições de memória externa 10H e 11H. O
resultado deve ser colocado na posição de memória externa 12H.
ii. Os 2 números encontram-se respectivamente nas posições de memória interna 10H e 11H. O resultado deve ser colocado na posição de memória interna 12H.
Exercício 2: Faça um programa que inicializa uma tabela de 200 bytes. Cada byte da tabela deve ser inicializado com um valor igual ao do seu índice na tabela.
Exercício 3: Faça um programa que simule uma Unidade Aritmética e Lógica com a funcionalidade ilustrada na seguinte tabela:
Antes de se realizar um programa numa qualquer linguagem de programação seja ela de alto nível (Pascal, C, C++, Java) ou de baixo nível (Assembly51, Assembly86, etc), em primeiro lugar deve-se sempre realizar o algoritmo utilizando uma linguagem gráfica tipo fluxograma ou program graph e depois então implementar o algoritmo na linguagem de programação pretendida. Vamos então seguir esta estratégia na resolução dos nossos exercícios utilizando em primeiro lugar a linguagem gráfica program graph para a descrição do algoritmo e em segundo lugar então proceder-se-á à codificação do algoritmo em Assembly51.
Exercício 1:
(10H) > (11H)
(12H) = (11H)
F T
(12H) = (10H)
Programa1_i SEGMENT CODERSEG Programa1_i
Maiorde2:MOV DPTR, #10HMOVX A, @DPTRMOV R7, A ; R7 = (10H)INC DPTRMOVX A, @DPTR ; A= (11H)CLR CSUBB A, R7 ; A= (11H)-(10H)JNC ELSE_IF
THEN_IF: MOV A, R7INC DPTRMOVX @DPTR, ASJMP FIM_IF
ELSE_IF: ADDC A, R7 ; repor o valor de AINC DPTRMOVX @DPTR, A
O programa anterior introduz algumas directivas novas do programa tradutor ASM51.EXE que passaremos a descrever.
SEGMENT – define um segmento ou um troço de código ou dados consoante seja do tipo CODE ou XDATA e IDATA. A sintaxe é:
NOME SEGMENT TIPO_SEGMENTNo qual,
NOME – identifica um conjunto de caracteres ou dígitos começados por letra.TIPO_SEGMENT – pode ser uma das seguintes palavras chave:
CODE : identifica o segmento como de código.XDATA : o segmento é de dados (DATA) existentes na memória externa ao CPU.IDATA: as variáveis aqui definidas existem na memória interna do CPU.
RSEG – define o segmento como relocalizavel, ou seja, o endereço do código ou das variáveis definidas num segmento com este atributo não têm um endereço absoluto. A sintaxe é:
RSEG NOME
Esclarecidas as directivas e analisado o programa “Programa_i” é fácil verificar que este poderia ser optimizado pois as instruções finais do THEN_IF e do ELSE_IF são comuns logo poderiam ser retiradas e passadas para o ramo FIM_IF.
Maiorde2:MOV A, 11HCLR CSUBB A, 10H ; A= (11H)-(10H)JNC ELSE_IF
THEN_IF: MOV 12H, 10HSJMP FIM_IF
ELSE_IF: MOV 12H, 11HFIM_IF: SJMP $ ; forever: fim do programa
A solução para o exercício 1.ii é dado pelo programa “Programa1_ii”. O código é consideravelmente mais simples e mais fácil de perceber. Assim, sempre que se puder utiliza-se variáveis definidas na memória interna.
Tendo realizado os programas anteriores,como é que se poderá testá-los no kit SAB80535. Em primeiro lugar tem de se saber aonde está disponível a memória de desenvolvimento RAM. Sem grandes preocupações dos porquês, admita que a RAM está disponível entre os endereços 0000H e 7FFFH. Pois, em breve iremos estudar a arquitectura interna do kit SAB80535.
Em segundo lugar terá de se perceber o ambiente de desenvolvimento a utilizar, que é composto por um qualquer editor de texto e pelos programas ASM51, L51 e OH51.
A seguinte figura pretende elucidar os passos requeridos até termos produzido um ficheiro com extensão .HEX que deve ser carregado na memória do kit.
Segue-se uma breve explicação da figura anterior. Em primeiro lugar deve-se criar um ou mais ficheiros de texto com extensão .A51 utilizando um editor. De seguida, deve-se utilizar o programa tradutor e analisador sintáctico ASM51.EXE de Assembly51 para gerar os ficheiros com extensão .OBJ e .LST, a sintaxe é:
A51 FICHEIRO.A51 DEBUG
Depois de gerarmos todos os ficheiros .OBJ sem erros sintácticos vamos agrupar todos os segmentos existentes nos nossos ficheiros assim como localizá-los em determinados endereços. Um exemplo possível é o seguinte comando:
aonde se está a agrupar todos os segmentos XDATA, IDATA e CODE existentes nos n+1 ficheiros do comando. Cada segmento tem como endereço inicial o valor hexadecimal dado como parâmetro. Assim, o segmento XDATA começa em 2000H, IDATA em 30H, CODE em 4000H, etc. De notar que este comando é apenas um exemplo possível, podendo-se variar os valores de inicio de cada segmento. Este comando gera um ficheiro sem extensão com nome igual ao do primeiro ficheiro dado como parâmetro.Por fim para gerarmos o ficheiro com extensão .HEX temos de executar o comando:
OHS51 FICHEIRO
terminando-se então todos os passos necessários para se obter um ficheiro que se pode carregar na memória RAM do kit 80535.
• Distinção entre o módulo principal e os restantes módulos
No módulo principal deve-se colocar no início a directiva,$TITLE (‘ Texto do título do programa’)
Nos módulos secundários deve-se usar a directiva NAME para sua identificação,NAME Nome_do_Módulo
Em termos práticos o módulo principal deve conter sempre o entry point ou o endereço inicial do programa.
Quando se programa utilizando módulos, tem de se indicar em cada módulo as funções ou variáveis que estão definidas noutros módulos e que são utilizadas no módulo corrente. Para isso, utiliza-se a directiva EXTRN que tem a seguinte sintaxe:
EXTRN segmento(lista de símbolos)Exemplo:
EXTRN CODE(write_string, read_string)
Estamos a indicar que no módulo corrente vamos utilizar duas funções chamadas write_string e read_string que pertencem ao segmento CODE e que estão definidas noutro módulo.Para que estas directivas não indiquem erro no processo de ligação a executar pelo programa l51.exe então deve-se nos módulos aonde estas funções estão definidas utilizando a directiva PUBLIC que tem a seguinte sintaxe,
PUBLIC símbolo [, símbolo, ...]
No nosso exemplo teríamos que definir,PUBLIC write-string, read_string
O segmento de stack é localizado na RAM interna. Este é usado para guardar o Program Counter (PC) durante as chamada e retorno de funções e rotinas de interrupção. As leituras e escritas no stack são controladas por um registo chamado stack pointer (SP) que faz uma gestão First In Last Out (FILO) ou Last In First Out (LIFO) do segmento de stack. O SP é um registo interno de 8 bits que contém o endereço do último byte a ser guardado no stack. É responsabilidade do programador que o SP tenha um valor válido no espaço de endereçamento do stack (menor que 80H) e que este espaço não é utilizado para outras variáveis. Um exemplo da definição do stack e correspondente inicialização do registo SP poderá ser,
DSEG AT 40H ; DATA SEGMENT identifica a memória interna de 00H-7FHDIM_PILHA EQU 40Hpilha: DS DIM_PILHA
.
.
.codigo SEGMENT CODE RSEG codigo MOV SP, #pilha-1 ; porque o 80535 primeiro incrementa o SP e depois é que guarda
De notar que após uma acção de reset ao CPU o SP fica com o valor 7 que coincide com o R7 do banco 0.
• Convenção para Parâmetros de Entrada passados por RegistoTipo char, 1 byte pointer int, word pointer long, float Pointer1º parâmetro R7 R6, R7 R4-7 R1, R2, R32º parâmetro R5 R4, R5 R1, R2, R33º parâmetro R3 R2, R3
Quando o nº de parâmetros excede os indicados na anterior tabela então os parâmetros são passados em posições de memória fixas.
Convenção para os Parâmetros de Saída de FunçõesValor devolvido Registo SignificadoBit CarryChar R7Int R6, R7 R6 (MSB) e R7 (LSB)Long R4-7 R4 (MSB) e R7 (LSB)Float R4-7 R4(MSB) e R7 (LSB)Pointer R1, R2, R3 R1 – Tipo de memória, R2 – High Offset, R3 – Low Offset
Tipo de memória: 1 – IDATA ; 2 – XDATA ; 3 – PDATA ; 4 – DATA ; 5 – CODE
Exercício i: Realize uma função que recebe como parâmetro de entrada o endereço de uma StringC residente em XDATA faça a visualização da stringC na consola. Assuma que dispõe de uma função CO com um parâmetro de entrada tipo char e que faz a visualização do carácter na consola.
ii. Generalize o parâmetro de entrada de modo à stringC poder residir em qualquer memória.
Significado das flagsCY – carryAC – auxiliary carryF0 e F1 – a usar pelo utilizadorOV – overflowP - paridade parRS1 RS0 - Bits de selecção do banco de registos R0-7. 0 0 - banco 0, endereços entre 00H e 07H 0 1 - banco 1, endereços entre 08H e 0FH 1 0 - banco 2, endereços entre 10H e 17H 1 1 - banco 3, endereços entre 18H e 1FH
Exercício iii : Faça uma função Read_String que tem como parâmetro de entrada o endereço da StringC a ser afectada com o conjunto de caracteres introduzidos pelo teclado. Assuma que tem uma função CI que devolve um valor tipo char correspondente ao último carácter introduzido pelo teclado.
• Registo de flags
Este registo no 80535 chama-se Program Status Word (PSW) e pode ser acedido em termos de bits ou ao byte. A informação que contém é a seguinte:
CY AC F0 RS1 RS0 OV F1 PPSW D0HD7H D6H D5H D4H D3H D2H D1H D0H
O CPU 80535 disponibiliza 48 sinais digitais de entrada/saída agrupados em 6 portos bidireccionais de 8 bits cada. Os acessos de leitura ou escrita a estes 6 portos são realizados através dos registos de função especial P0 a P5. Genericamente, cada bit consiste num flip-flop tipo D, num driver de saída e num buffer tri-state de entrada, tal como se ilustra na seguinte figura:
Q
Q/
D
Escrita
driverde
saídaPinodo
porto
Ler Pino
Ler flip.flop
Bus InternoR
N-FET
Circuito genérico associado a cada bit dos portos de entrada/saída.
Os drivers de saída dos portos 0 e 2 são diferentes pois também são usados para acesso à memória externa tal como se exemplifica na seguinte figura:
O porto 0 multiplexa no tempo a parte baixa do endereço A0-7 com o byte a ser lido ou escrito D0-7, enquanto que o porto 2 define a parte alta do endereço A8-15 sempre que o endereçamento é a 16 bits. Caso contrário, em ambos os portos aparece o conteúdo do registo correspondente.
Os portos 1 e 3 são multifuncionais, ou seja, permitem implementar além da funcionalidade de entrada/saída incondicional também os seguintes sinais de entrada/saída alternativos:
Porto 1 Pino Função AlternativaP1.0 /INT3/CC0 Entrada da interrupção 3; Entrada de captura 0; Saída de comparação 0.P1.1 INT4/CC1 Entrada da interrupção 4; Entrada de captura 1; Saída de comparação 1.P1.2 INT5/CC2 Entrada da interrupção 5; Entrada de captura 2; Saída de comparação 2.P1.3 INT6/CC3 Entrada da interrupção 6; Entrada de captura 3; Saída de comparação 3.P1.4 /INT2 Entrada da interrupção 2.P1.5 T2EX Entrada de reload trigger do timer 2.P1.6 CLKOUT Clock de saída.P1.7 T2 Entrada externa de clock do contador realizado com o timer 2.
Porto 3P3.0 RXD Entrada série de dados em modo assíncrono; entrada/saída série de dados em modo síncrono.P3.1 TXD Saída série de dados em modo síncrono; saída de clock em modo síncrono.P3.2 /INT0 Entrada da interrupção 0; gate de controlo do timer 0.P3.3 /INT1 Entrada da interrupção 1; gate de controlo do timer 1.P3.4 T0 Entrada externa de clock do contador realizado com o timer 0.P3.5 T1 Entrada externa de clock do contador realizado com o timer 1.P3.6 /WR Sinal de escrita (write/) para a memória externa.P3.7 /RD Sinal de leitura (read/) para a memória externa.
Na versão MYMOS do CPU80535 o porto 6 define uma interface analógica para o conversor analógico-digital.Na versão ACMOS além da interface analógica também se pode definir entradas digitais. Nesta versão, o porto pode ser tratado como um porto de entrada digital cujo endereço é P6 (0DBH).
O circuito completo correspondente a um bit de entrada/saída dos portos 1 e 3 é mostrado na seguinte figura:
Após Reset todos os flip-flops dos portos 1 a 5 ficam com o valor lógico 1.
• Características Eléctricas dos Portos de Entrada/Saída
Os drivers de saída dos portos 1 a 5 podem ser ligados directamente a entradas TTL pois as suas características eléctricas garantem valores lógicos correctos. Para mais informações deve consultar o data sheet do SAB80535 e verificar na secção de características DC os valores correspondentes aos parâmetros VOL e VOH. Os bits do porto 0 quando programados como saída também partilham esta característica. No entanto quando programados como entrada é necessário ligar-lhes pullups para estas não ficarem em alta impedância. A excepção é feita quando os bits do porto 0 estão sendo usados como sinais de endereço ou de dados (address/data bus).
• Instruções Read-Modify-Write dos portos 0 a 5 Todas as instruções, que têm como destino um porto de entrada/saída ou um bit de um porto, lêem em primeiro lugar o flip-flop de informação correspondente ao bit e não a entrada física do porto. Por exemplo, a instrução ORL P4, #99H lê a informação presente nos 8 flip-flops (latch do porto), modifica o valor e coloca o resultado no latch. Às instruções que em primeiro lugar lêem um ou mais flip-flops depois modificam o seus valores consoante a operação e finalmente actualizam os respectivos flip-flops são chamadas instruções de read-modify-write. Estas instruções são as seguintes:ANL ex: ANL P4, #01HORL ex: ORL P3, AXRL ex: XRL P5, #0FFHJBC ex: JBC P3.1, LOOPCPL ex: CPL P3INC ex: INC P5DEC ex: DEC P5DJNZ ex: DJNZ P4, LOOPMOV ex: MOV P3.2, CCLR ex: CLR P3.1SETB ex: SETB P4.0
De salientar que este conjunto de instruções não lê os valores presentes nos pinos físicos dos portos mas sim os respectivos flip-flops.
Esta técnica de endereçamento que é talvez a mais utilizada no desenvolvimento de microsistemas consiste em dividir o espaço de endereçamento total do CPU, definido pelo seu número de bits de endereçamento externo, em blocos de endereços de semelhante dimensão que são designadas por páginas. Nas páginas inserem-se os dispositivos de memória do microsistema e a dimensão de cada página deve ser concordante com a dimensão do dispositivo de memória nela inserido. Por exemplo, num CPU que tem um barramento de endereços de 16 bits, a organização de páginas é criada pensando no endereçamento total de 64K, dividido em 2 páginas de 32K pelo bit de endereçamento externo A 15 e depois cada página de 32K pode ser dividida em 2 páginas de 16K pelo bit A14, e assim sucessivamente até obtermos páginas com a dimensão desejada. Esta técnica pode ser ilustrada na seguinte figura:
No projecto hardware de microsistemas tem particular importância a atribuição de endereços aos módulos de memória que partilham os barramentos de endereços e dados estabelecidos pelo CPU. Assim, devemos saber qual a dimensão (nº de bits) dos barramentos de endereço e de dados do CPU para depois podermos começar o projecto hardware do microsistema.Os módulos de memória são caracterizados pela dimensão (nº de palavras), pela dimensão das palavras (nº de bits) e por serem memória volátil (RAM) ou não volátil (ROM, PROM, EPROM, EEPROM). De notar que a memória não volátil de um microsistema deve incluir sempre o endereço de reset do CPU.As técnicas de descodificação de endereços num microsistema que iremos estudar são as seguintes:
• Técnicas para a Descodificação de Endereços num Microsistema
• Paginação
64K
32K
16K
8K
A15
A14
A13
0
0
0
0
0 0 0
1
1 1
1 1 1 1
Dimensãodas
páginas
Bits deendereçamento
externo
Bits responsáveis pela selecção de um dispositivo de memória.
Exemplo: Projecte um circuito descodificador de endereços para um microsistema baseado num CPU de 8 bits com 16 bits de endereços que contém os dispositivos de memória apresentados no seguinte mapa de memória:
EPROM 8K
RAM 8K
RAM 8K
Não utilizado
0000H
FFFFH
2000H
6000H
4000H
1FFFH
3FFFH
5FFFH
End. inicial End. final
ES1
S0
0
1
2
3A15
A14
A13
CSEPROM/
CSRAM#1/
CSRAM#2/
Circuito descodificador de endereços
Exercício: Acrescente ao microsistema anterior 2 RAMs de 4K cada.
• Atribuição ContíguaEsta técnica utiliza-se quando se pretende introduzir um dispositivo de memória de pequena dimensão contiguamente a outros dispositivos de memória com dimensão muito superior. Por exemplo, suponha que pretende realizar o circuito de descodificação de endereços para o seguinte mapa de memória:
EPROM 8K
ROM 256
RAM 8K
0000H1FFFH
20FFH
40FFH
2000H
2100H
4100HNão utilizado
Solução:Os sinais de selecção da EPROM e ROM são obtidos descodificando os bits de endereçamento externo de cada dispositivo. A solução sem atribuição repetitiva de endereços (chamada tecnicamente de foldback), será:
O problema está na realização do CSRAM/ pois a RAM é um dispositivo com 8K que se encontra localizado no início de uma página de 256. Assim sendo, a única solução é relocalizar o dispositivo em termos de endereço. Como? Utilizando um subtractor que permita passar o valor do endereço inicial definido pelos 8 bits de maior peso do endereço cujo valor é 21H para o valor 00H, endereçando-se à RAM com os bits do resultado, como o apresentado na seguinte figura:
X0-7
Y0-7
8
S0-7
BW
A8-15
821H
RAM 8K
A8-A12
A0-A7 D0-D7
CS/
OE/
WE/
D0-D78
8A0-A7
8 A’8-15
5A’8-12
3 A’13-15
WR/RD/
Nota: O BW do subtractor entra na geração do CS/ da RAM e de eventuais dispositivos de memória que se venham a acrescentar ao sistema para distinguir estes dispositivos dos dispositivos que não são afectados com o problema de estarem inseridos em páginas com dimensão inferior à sua (ex: EPROM e ROM).
Este método de endereçamento consiste em activar o CS/ de cada dispositivo com um bit de endereçamento externo. Este método tem como grande vantagem a simplificação do hardware para a descodificação de endereços. Embora também apresente graves desvantagens tais como: 1. A não contiguidade de endereços atribuídos a cada dispositivo; 2. Limitação no número de dispositivos a inserir que são tantos quantos os bits de endereçamento externo; 3. As instruções do CPU para acesso à memória externa ficam passíveis de seleccionar vários dispositivos simultaneamente.De referir que este tipo de endereçamento utiliza-se em sistemas vocacionados numa fase posterior ao seu desenvolvimento com o objectivo de reduzir os custos de produção.
Exemplo: Suponha um microsistema baseado num CPU com 16 bits de endereço e que dispõe de dispositivos RAM e EPROM de 4K. Realize o circuito de descodificação de endereços para o microsistema inserindo o número máximo de dispositivos de memória possível utilizando a técnica de selecção linear.
Bits de endereçamento interno: A0-11Bits de endereçamento externo: A12-15.
Deste modo, o número máximo de dispositivos permitido são 4. Cada um deles é seleccionado com cada um dos bits de endereço entre A12 e A15.
Exercício 1: Projecte um microsistema baseado no 80535 com 32Kbytes de RAM para dados e código, e com 32Kbytes de EPROM só para código. Utilize na resolução do exercício 1 RAM 62256(32Kx8) e 1 EPROM 27256(32Kx8).
Exercício 2: Projecte um microsistema baseado no 80535 com 32Kbytes de RAM e 32Kbytes de EPROM mas tendo as seguintes particularidades: 1. após reset o CPU só consegue aceder à EPROM que está localizada em qualquer endereço externo para acesso a código; 2. após ter-se acedido a um qualquer endereço com o bit A15=1 o microsistema deve passar a localizar a RAM no endereço base 0000H e a EPROM no endereço base 8000H. A EPROM deve continuar a aceitar acesso só para código e a RAM deve permitir acesso tipo código e dados.
• Circuito de reset para o 80535 com botão
reset/ +
R
C
C= [4.7uF, 10uF]Treset= 24xTosc
O conteúdo dos registos mais utilizados após e durante a acção de reset são os seguintes:
O 80535 tem três temporizadores/contadores (em linguagem anglo-saxónica são designados por timer/counters) de 16 bits chamados timer 0, timer 1 e timer 2.
•Timer 0 e 1
Estes dois dispositivos podem funcionar ou como temporizadores(timers) ou como contadores de eventos (counters). Quando em modo temporizador (timer), o registo é incrementado em todos os ciclos máquina. Cada ciclo máquina inclui 12 ciclos de clock, o que para um clock de 12MHz implica incrementar o registo em todos os 1uS.Quando em modo contador, o registo é incrementado nas transições descendentes existentes nos pinos de entrada T0(P3.4) e T1(P3.5). Neste modo a entrada externa é amostrada durante o ciclo de clock número 11 de todos os ciclos máquina. Quando os testes exibem o valor 1 num ciclo e o valor 0 no ciclo seguinte, o contador é incrementado. O novo valor aparece no registo durante o terceiro ciclo de clock do próximo ciclo máquina. Como este demora 2 ciclos máquina a reconhecer uma transição descendente então o ritmo máximo de contagem é de 1/24 a frequência do oscilador ou seja de 2uS.Cada temporizador consiste de dois registos de 8 bits cada. TH0 (parte alta) e TL0 (parte baixa) para o timer 0. TH1 e TL1 para o timer 1. As funções de cada um dos timers são controladas através dos registos TCON e TMOD como se pode constatar nas seguintes figuras e descrições:
TF1 TR1 TR0TF0 TCON (88H)
8CH8DH8EH8FH
TR0 – Se a 1 permite o timer 0 contar. Caso contrário o timer está parado.TF0 – Timer 0 overflow flag. Esta flag é colocada a 1 sempre que há overflow no processo de contagem do timer, ou seja, quando o registo do temporizador passa de tudo a 1’s para tudo a 0’s. Esta flag é colocada a 0 por hardware quando o processador é vectorizado para a rotina de atendimento desta interrupção.TR1 – idem TR0 mas agora para o timer 1.TF1 – idem TF0 mas agora para o timer 1.
GATE – Quando a 1 permite contagem sempre que o pino INTX/ e o bit TRX estão ambos a 1.
C-T/ – Se a 1 fica em modo contador com a entrada no pino TX. Quando a 0 funciona em modo temporizador.
M1 M00 0 - Modo 0: temporizador/contador de 13bits. O THX funciona como contador de 8 bits de maior
peso e o TLX serve como contador de 5 bits de menor peso.
0 1 - Modo 1: temporizador/contador de 16 bits. O THX funciona como contador de 8 bits de maior
peso e TLX como contador de 8 bits de menor peso.
1 0 - Modo 2: temporizador/contador de 8 bits recarregável. THX contém um valor que é atribuído a TLX sempre que neste ocorre o overflow de contagem.
1 1 - Modo 3: timer 0: TL0 funciona como um temporizador/contador de 8 bits controlado pelos bits de controlo destinados ao timer 0. TH0 funciona como um temporizador de 8 bits comandado pelos bits de controlo destinados ao timer 1.
A comunicação série consiste no envio e recepção, de forma sincronizada, de 1 bit de informação em cada intervalo temporal designado por tempo de bit (bit time).O nível lógico 1 costuma designar-se por “mark” e o nível lógico 0 designa-se por “space”. A nível físico, os dois níveis lógicos correspondem a níveis de tensão diferentes ou à existência ou não de corrente na linha. Por exemplo, apresenta-se de seguida alguns protocolos para comunicação série e os respectivos níveis de tensão:
Protocolo Mark (1) Space (0) TTL > 2,4 V < 0,8 V
Loop de Corrente 20 mA 0 mA RS232 < -3 V > 3 V
Em termos históricos, o primeiro protocolo adoptado foi o “loop de corrente de 40mA” que se adaptava perfeitamente às características electromecânicas dos dispositivos da época, em que os relés eram directamente excitados pela corrente de 40mA pulsando ao ritmo de mark-space dos sinais enviados.
A conversão de níveis de tensão TTL para RS232 são realizados através de circuitos SSI desenvolvidos para o efeito, caso dos MC1488 e MC1489 da Motorola e os 75188 e 75189 da Texas Instruments.
Usualmente costuma dizer-se que existem dois tipos de comunicação série, a comunicação síncrona e a comunicação assíncrona. Dentro da classe da comunicação síncrona englobam-se os protocolos Bisync, SDLC, HDLC, etc. Na classe da comunicação dita assíncrona temos o protocolo start-stop bit. É sobre este protocolo que vamos dirigir o nosso estudo.
A comunicação designa-se assíncrona quando existe unicamente sincronismo ao nível do bit. Enquanto se houver sincronismo ao nível do carácter e da trama(conjunto de caracteres) a comunicação designa-se por síncrona.
A composição deste protocolo é ilustrada na seguinte figura:
Espera(idle)
MarkSpace
Start bit
dados
Bit de paridade
Stop bit
Campo start bit dados bit paridade stop bit Dimensão 1 5 0 1
em 6 1 1,5 Bits 7 2
8
O bit de paridade pode ou não existir. Caso exista, a paridade designa-se por “par” quando o número de bits de dados mais o bit de paridade têm no conjunto um número par de 1’s. O inverso designa-se por paridade “ímpar”.
Na comunicação série assíncrona o débito da comunicação ou o número de bits por segundo designa-se por baud rate. Os valores usuais deste parâmetro são: 110, 300, 600, 1200, 2400, 4800, 9600, 14400(14K), 28800(28K), 57600(56K).
D0 D2 D3 D4 P
Exercício 1: Realize uma rotina para transmissão de dados série através do bit P5.6. Esta rotina tem como parâmetro de entrada o carácter a enviar.
Exercício 2: Realize uma rotina para recepção de dados série através do bit P5.7. Esta rotina tem como parâmetro de saída o carácter recebido.
• Resolução do exercício 1: • Resolução do exercício 2:
; o carácter a enviar é passado no registo R7; comunicação série: 1 start bit, 8 data bits, 1 Stop bit; baud rate= 1/ Delay_1_bit_timeCO: CLR P5.6 ; start bit
LCALL DELAY_1_BIT_TIMEMOV R0, #8
TX_DATA_BITS:MOV A, R7RRC AMOV R7, AMOV P5.6, C ; 8 data bitsLCALL DELAY_1_BIT_TIMEDJNZ R0, TX_DATA_BITS SETB P5.6 ; stop bitLCALL DELAY_1_BIT_TIMERET
; o carácter a receber é devolvido no registo R7; comunicação série: 1 start bit, 8 data bits, 1 Stop bit; baud rate= 1/ Delay_1_bit_timeCI: JB P5.7, $ ; espera start bit
LCALL DELAY_MEIO_BIT_TIMEJB P5.7, CI ; falso start bitMOV R0, #8
RX_DATA_BITS:LCALL DELAY_1_BIT_TIMEMOV C, P5.7 ; 8 data bitsMOV A, R7RRC AMOV R7, ADJNZ R0, RX_DATA_BITSLCALL DELAY_1_BIT_TIMEJNB P5.7, CI ; falso stop bitRET
Só falta fazer as funções de delay referidas nas anteriores funções. Então, como se faz um delay com um mínimo de rigor temporal?Em primeiro lugar, temos de recordar que um ciclo máquina no 80535 para uma frequência de 12MHz demora 12 ciclos de clock ou seja 1uS. Em segundo lugar tem de se repetir tantos uS ou ciclos máquinas quantos os desejados. Por exemplo se quiséssemos receber e transmitir com um baud rate= 9600, isto significa que cada bit tem de permanecer durante 1/9600= 104uS, daí o bit time= 104us e meio bit time= 52uS. Assim, a estrutura das rotinas será idêntica à parte de uma valor constante que chamaremos respectivamente UM_BIT_TIME e MEIO_BIT_TIME.
Este CPU disponibiliza internamente um canal série que suporta comunicação série bidireccional em simultâneo (dita tecnicamente comunicação em full duplex). Este canal de comunicação pode funcionar em 4 modos diferentes, um modo síncrono e três modos assíncronos como se irá descrever de seguida
• Modo 0 – Canal Síncrono de 8 bits
Neste modo de funcionamento os bits de dados são transmitidos e recebidos através do sinal RxD. O sinal TxD opera como saída, aonde é definido o clock do ritmo da transferência de informação. O campo de dados é de 8 bits e o baud rate é 1/12 da frequência do oscilador do CPU.
A transmissão é iniciada sempre que uma instrução utiliza o registo SBUF(99H) como registo destino.A recepção é iniciada quando os bits de controlo REN= 0 (Receiver Enable) e RI= 0 (Receiver Interrupt).
• Modo 1 – Canal Assíncrono de 8 bits
Neste modo de funcionamento são transmitidos e recebidos 10 bits: 1 start bit, 8 bits de dados(bit de menor peso em primeiro lugar) e 1 stop bit.Na recepção os bits de dados ficam no registo interno SBUF(99H) e o stop bit é registado no bit RB8 do registo SCON(98H).O baud rate do canal neste modo é programável através do timer 1 ou através do gerador de baud rate interno.
Utilizando o timer 1 como gerador de baud rate, temos:Timer 1 em modo 1: baud rate= 2SMOD /32 * Timer1_Overflow
Timer 1 em modo 2: baud rate= 2SMOD /32 * (fXTALL / 12*(256-TH1)) Utilizando o gerador de baud rate interno, o baud rate depende do bit SMOD(bit 7 do registo PCON(87H)) e da frequência do oscilador, através da seguinte fórmula:
Neste modo, o canal série transmite através do TxD ou recebe através do RxD 11 bits, distribuidos da seguinte forma: 1 start bit, 8 bits de dados, 1 bit de paridade e 1 stop bit. Na transmissão o bit de paridade pode ser afectado com 1 ou 0 e é o bit TB8(end. de bit 9BH) do registo SCON(98H).Na recepção o nono bit é afectado ao bit RB8(9AH) do registo SCON(98H). O baud rate é programável para 1/32 ou 1/64 de fXTALL função do valor do bit SMOD do registo PCON estar respectivamente a 1 ou a 0.
Modo funcionalmente idêntico ao modo 2 com a diferença do baud rate poder ser programável através do timer 1 ou do gerador interno de baud rate. As fórmulas para o cálculo do baud rate obtido à custa do timer 1 ou do gerador de baud rate são idênticas às apresentadas na descrição do funcionamento do canal série em modo 1.
• Registos para parametrização do canal série do 80535
98H99H9AH9BH9CH9DH9EH9FH
RITIRB8TB8RENSM2SM1SM0 SCON - 98H
Modo 0 - 0 0 Modo 1 - 0 1Modo 2 - 1 0Modo 3 - 1 1
Permite o funcionamento em modo 2 e 3, quando a 1. Em modo 1 e SM2= 1 implica RI=1 se o stop bit recebido for válido. Em modo 0, SM2 deverá estar sempre a 0.
Se a 1 permite recepção.
Bit 8 da transmissão.
Bit 8 da recepção.
Flag de interrupção da transmissão. É colocada a 1 por hardware no 8º bit time quando em modo 0, ou no início do stop bit nos outros modos. Deve ser afectado a 0 por programação.
Flag de interrupção da recepção. É colocada a 1 por hardware no 8º bit time quando em modo 0, ou no início do stop bit nos outros modos. Deve ser afectado a 0 por programação.
• Registos para parametrização do canal série do 80535 (continuação)
D0D1D2D3D4D5D6D7 SBUF - 99H
Registo que define a interface entre o canal de comunicação série do 80535 e o programa. Escrever no registo SBUF implica escrever no registo de transmissão correspondendo a iniciar-se o processo de transmissão. Ler o SBUF implica ler o registo de recepção do canal.
—SMOD PCON - 87H— — — — — —
Bits não utilizados na comunicação série.
Quando a 1, duplica o baud rate do canal série nos modos 1, 2 e 3.
—BD ADCON – 0D8H— — — — — —
Bits não utilizados na comunicação série.
Quando a 1, o baud rate do canal série nos modos 1 e 3 são obtidos a partir de um divisor de frequênciasdedicado. Valores de baud rate de 4800 e 9600 podem ser obtidos a partir de fXTALL= 12MHz.
; -------- ISEL -----------;; por Jorge Pais;; Rotinas que usam a UART do 80535 para enviar; e receber dados série por pooling; ------------------------
PUBLIC CI, CO, INIT_SERIE;PUBLIC CO ; TI EQU SCON.1; RI EQU SCON.0
Consultando o anteriormente descrito, estamos em condições para desenvolver um módulo para manipular o canal série do 535 composto por uma rotina de iniciação chamada INI_SERIE, uma rotina de recepção chamada CI e uma rotina de transmissão chamada CO.
; continuaçãoPROG SEGMENT CODE RSEG PROG; inicialização do UART do 535INI_SERIE:
CLR P5.5 ; seleccionar o UART do 535; seleccionar 8 bits modo 1 9600
Timer0, Timer1, Timer2 – Interrupções internas geradas pelos temporizadores existentes no CPU. No caso do Timer2 existem modos de funcionamento para os quais a interrupção é gerada externamente e outros em que a interrupção é interna.
Canal Série 0 – Interrupção interna gerada pelos eventos de recepção ou transmissão série de um carácter.
Conversor A/D – Interrupção interna gerada pelo conversor analógico/digital aquando do fim de uma conversão.
O mecanismo de controlo das interrupções no 80535 envolve variadas flags localizadas nos seguintes registos de função especial:
TCON.0TCON.1TCON.2TCON.3TCON.4TCON.5TCON.6TCON.7
IT0IE0IT1IE1TF0 TCON - 88HTF1
88H89H8AH8BH8DH8FH Endereço aonível do bit
IT0 – Define a característica da entrada INT0/. Quando a 1 indica pedido de interrupção nas transições descendentes de INT0/. Se a 0 implica pedido de interrupção quando INT0/ estiver a 0.
IE0 – Flag de detecção de transições na entrada INT0/.Quando a 1 indica que houve um pedido de interrupção por transição descendente na entrada INT0/.É colocada a 0 quando a interrupção é atendida.
IT1 – Idem IT0 mas agora o significado é para a entrada INT1/.IE1 – Idem IE0 mas respeitante à entrada INT1/.TF0 – Flag de overflow do timer 0. Colocada a 1 por hardware quando o temporizador/contador 1 passa da configuração binária definida por tudo a 1’s para tudo a 0’s. Esta flag é colocada a 0 por hardware sempre que o processador atende o pedido.TF1 – Idem TF0 mas agora para o timer 1.
I2FR – Define se a entrada INT2/ afecta na transição ascendente ou descendente a flag de pedido de interrupção IEX2. Se a 1, a entrada INT2/ afecta IEX2 quando houver uma transição ascendente. Se a 0, INT2/ afecta IEX2 quando houver uma transição descendente.
I3FR – Idem I2FR mas agora respeitante à entrada INT3/.
IADC – Flag de pedido de interrupção do conversor analógico/digital. É colocado a 1 no fim de uma conversão e deve ser colocado a 0 por software.
IEX2-6 – Flag de transição para a entrada de interrupção INTx/. Colocada a 1 por hardware quando for detectado um pedido de interrupção. Fica a zero quando é atendido o pedido de interrupção.
TF2 – Flag de overflow do timer 2.Fica a 1 quando o timer 2 passa a sua configuração binária de todos os bits a 1 para todos os bits a 0. Deve ser colocada a 0 por software.
EXF2 – Flag de recarregamento (reload) do timer 2.Fica a 1 quando existe uma transição descendente no sinal T2EX enquanto EXEN2= 1. Quando é permitido a
interrupção do timer 2, EXF2=1 implica uma vectorização para a rotina de atendimento da interrupção timer 2.Deve ser colocado a 0 por software.
Cada entrada de interrupção pode ser mascarada ou desmascarada através dos seus bits de permissão, existentes nos registos IEN0 e IEN1. Existe também um bit global de permissão para todas as fontes de interrupção que quando a 0 não permite qualquer pedido de interrupção independentemente dos bits de permissão individuais.
IEN0.0IEN0.1IEN0.2IEN0.3IEN0.4IEN0.5IEN0.7
EX0ET0EX1ET1ET2 IEN0 – 0A8HEAL
A8HA9HAAHABHADHAFH Endereço do bitACH
ES
IEN1.0IEN1.1IEN1.2IEN1.3IEN1.4IEN1.5IEN1.7
EADCEX2EX3EX4EX6 IEN1 – 0B8HEXEN2
B8HB9HBAHBBHBDHBFH Endereço do bitBCH
EX5
EX0 – Se a 1 permite INT0/.ET0 – Quando a 1 permite interrupção gerada pelo timer 0.EX1 – Permite INT1/ sempre que esteja este bit a 1.ET1 – Se a 1 permite interrupção gerada pelo timer 1.ES – Quando a 1 permite interrupção gerada pelo canal série, tanto na transmissão como na recepção.ET2 – Permite interrupção do timer 2 quando a 1.EAL – Permite ou não interrupções. Se a 0 este bit não permite atendimento aos pedidos de interrupção.
EADC – Se a 1 permite interrupção gerada pelo conversor analógico/digital.EX2-6 – Quando a 1 permite o respectivo pedido de interrupção correspondente à entrada INTx.EXEN2 – Não permite interrupção por recarregamento automático do timer 2 enquanto este bit estiver a 0..
Todas as fontes de interrupção mencionadas obedecem à priori a uma hierarquia quanto às suas prioridades que podem ir do nível 0 a 3 e que são definidas através de bits dos registos IP0 e IP1. Uma rotina de atendimento de interrupção correspondente a uma entrada de interrupção de baixa prioridade só pode ser interrompida por outra interrupção de maior prioridade. Uma interrupção de nível 3 não poder ser interrompida por qualquer outra fonte de interrupção. A definição dos níveis de prioridade das várias fontes de interrupção realizam-se aos pares segundo a seguinte lista:
INT0/ e Conversor A/DTimer0 e INT2/INT1/ e INT3/Timer1 e INT4/Canal Série 0 e INT5/Timer2 e INT6/
• Registos para definição dos níveis de prioridade IP0, IP1
IP0.0IP0.1IP0.2IP0.3IP0.4IP0.5IP0.6IP0.7
IP1.0IP1.1IP1.2IP1.3IP1.4IP1.5IP1.6IP1.7
IE0IADC
TF0IEX2
IE1IEX3
TF1IEX4
RI+TIIEX5
TF2+EXF2IEX6
——
— —IE0
IADCTF0
IEX2IE1
IEX3TF1
IEX4RI+TIIEX5
TF2+EXF2IEX6
IP0 - 0A9H
IP1- 0B9H
IP1.X IP0.X Nível de Prioridade 0 0 0 0 1 1 1 0 2 1 1 3
O nível de prioridade 0 é o menor e o 3 é o maior.
Quando as fontes de interrupção estão todas com a mesma prioridade e existem simultâneos pedidos de atendimento, estes pedidos são atendidos segundo um critério de prioridades pré-definidos no 80535 e ilustrados na seguinte tabela:
Prioridade Alta Baixa Fonte de Interrupção
Alta INT0/ Conversor A/D Timer 0 INT2/ INT1/ INT3/ Timer 1 INT4/ Canal Série INT5/
• Atendimento de InterrupçõesAs flags correspondentes às entradas de interrupção são testadas em todos os ciclos máquina no 10º ciclo de clock. Se alguma destas flags está activa existe um ciclo máquina seguinte durante o qual estas flags são obsessivamente testadas. Caso continuem activas o sistema de interrupções gerará uma instrução LCALL Endereço16, na qual o Endereço16 é um valor a 16 bits que especifica o endereço absoluto do início da respectiva rotina de atendimento de interrupção. Os valores dos endereços absolutos correspondentes às rotinas de atendimento das interrupções são ilustrados na seguinte tabela:
Flag de Pedido de Interrupção Endereço da Rotina de Atendimento Fonte de Interrupção IE0 0003H INT0/ TF0 000BH Timer 0 IE1 0013H INT1/ TF1 001BH Timer 1
Uma rotina de atendimento de interrupção começa no endereço especificado e termina quando é cumprida a instrução RETI. A instrução RETI indica que a rotina de atendimento terminou a execução afectando o PC com os dois bytes que se encontram no topo do stack e também informa a máquina de atendimento de interrupções do CPU que pode voltar a atender interrupções de igual ou menor prioridade. Portanto, nunca se deve terminar uma rotina de interrupção com uma instrução RET porque para o CPU a rotina de atendimento da interrupção ainda está sendo realizada nunca mais atendendo interrupções de igual ou menor prioridade.
Uma característica importante das rotinas de atendimento de interrupções é que devem ser sempre invisíveis, do ponto de vista do programa que foi interrompido. Ou seja, não devem alterar o estado do CPU nem demorar “demasiado” tempo de execução. O estado do CPU é definido como sendo os valores dos seus registos internos e stack. O tempo de execução de uma rotina de atendimento de interrupções poderá ser definido como “demasiado” quando este tempo degradar substancialmente o tempo de execução dos programas interrompidos.
• Desenho das Rotinas para Atendimento de Interrupções
Para garantir a invisibilidade da rotina para atendimento da interrupção de nível 1, a sua estrutura genérica deve ser:
ROTINA_DE_INTERRUPCAO:PUSH DPH ; salvar os registos que vão ser utilizados na rotinaPUSH DPL ; de interrupção. Uma das condições para se garantirPUSH B ; a invisibilidade desta rotina de interrupção.PUSH ACCPUSH PSW
SETB RS0 ; nível 1 de interrupção implica no 535 trabalhar com o ; banco 1 de registos por convenção. Ao nível do programa ; deve-se garantir que se trabalha com o banco 0 de registos. ; Assim garante-se a não destruição dos registos R0-7.
LCALL Fazer_Accoes ; destrói A, B e DPTR. Acções a efectuar na rotina ; de interrupção.
POP PSW ; reposição dos valores nos registosPOP ACCPOP BPOP DPLPOP DPHRETI ; assinala ao processador o fim do atendimento da interrupção
; e afecta o PC com a word que se encontra no topo do stack.Fazer_Accoes:
• Desenho das Rotinas para Atendimento de Interrupções (continuação)
O módulo assembly535 para o atendimento de interrupções no kit didáctico SD535 deve ter a seguinte estrutura:
; entry point da interrupção do Canal Série Rx/TxCSEG AT 23HLJMP INT_RXTX_ROT
; entry point da interrupção do Overflow do Timer 0 CSEG AT 0BHLJMP INT_T0_ROT
; rotina de tratamento da interrupção do Overflow do Timer 1CSEG AT 1BHLJMP INT_T1_ROT
; entry point da interrupção do Overflow do Timer 2 ; ou recarregamento externo do Timer 2
CSEG AT 2BHLJMP INT_T2_ROT
; entry point da interrupção do Conversor Analogico-Digital CSEG AT 43HLJMP INT_ADC_ROT
; entry point da interrupção 0 externaCSEG AT 03HLJMP INT0_ROT
; entry point da interrupção 1 externaCSEG AT 13HLJMP INT1_ROT
; entry point da interrupção 2 externaCSEG AT 4BHLJMP INT2_ROT
; entry point da interrupção 3 externaCSEG AT 53HLJMP INT3_ROT
; entry point da interrupção 4 externaCSEG AT 5BHLJMP INT4_ROT
; entry point da interrupção 5 externaCSEG AT 63HLJMP INT5_ROT
; entry point da interrupção 6 externaCSEG AT 6BHLJMP INT6_ROT
Nota importante: As chamadas às rotinas de interrupção devem ser realizadas através da instrução LJMP ROTINA_INTERRUPÇÃO porque não nos podemos esquecer que o processador chega ao endereço de entry point da rotina de atendimento da interrupção através de uma instrução LCALL ENDEREÇO16 produzida internamente pelo CPU, a qual garante a memorização do endereço de retorno em stack.
Deste modo, o conjunto de instruções que compõem o atendimento da interrupção deve ser sempre terminado pela instrução RETI, como o exemplificado na página anterior.
• Desenho das Rotinas para Atendimento de Interrupções (continuação)
Exercício: Faça um programa que simule um contador crescente de 16 bits com clear assíncrono. O contador deve contar 20 unidades por segundo. O valor do contador deve ser visualizado na consola.
Opções tomadas:1. O timer 0 vai gerar o sinal de relógio (20Hz). Como trabalha a 1MHz tem de dividir-se esta frequência por 50000.2. O sinal de clear do contador vai ser a entrada INT3 do CPU.
$TITLE('Contador crescente de 16 bits com clear'); ******************************************; * feito por Jorge Pais *; * 2001 *; ******************************************
NAME INTERRUPTS; ****************************; * ISEL *; * por Jorge Pais *; * 2001 *; ****************************EXTRN DATA(CONTADOR)EXTRN BIT(VER_CONTADOR)
; ****************************************************; * definição dos registos e bits para tratamento das interrupções *; ****************************************************IEN0 EQU 0A8HIEN1 EQU 0B8HT2CON EQU 0C8HIRCON EQU 0C0H; BITS DO IEN0EAL EQU IEN0.7; BITS DO IEN1EX3 EQU IEN1.2; BITS DO T2CONI3FR EQU T2CON.6; BITS DO IRCONIEX3 EQU IRCON.2
; ***********************************************; *** entry points das rotinas de interrupção ***; ***********************************************; entry point da interrupção do Overflow do Timer 0
CSEG AT 0BHLJMP INT_T0_ROT
; entry point da interrupção 3 externaCSEG AT 53HLJMP INT3_ROT
;continua
; continuação; ************************************************; *** rotinas para tratamento das interrupções ***; ************************************************INTS SEGMENT CODE
RSEG INTS; rotina de tratamento da interrupção do Overflow do Timer 0 INT_T0_ROT:
MOV TL0, #LOW(0-50000) MOV TH0, #HIGH(0-50000) ; reinicialização do TimerPUSH PSWPUSH ACCINC CONTADORMOV A, CONTADORCJNE A, #0, IT0_FIMINC CONTADOR+1
IT0_FIM:SETB VER_CONTADORPOP ACCPOP PSWRETI
; rotina de atendimento da entrada de interrupção externa 3 - Clear do contadorINT3_ROT: MOV CONTADOR, #0
MOV CONTADOR+1, #0SETB VER_CONTADORCLR IEX3 ; clear ao isr3 - para aceitar novos interruptsRETI
; rotina para inicialização das interrupçõesPUBLIC INIT_INTINIT_INT: CLR EAL ; disable a todos os interrupts
; ****************************; * ISEL *; * por Jorge Pais *; * 2001 *; ****************************
; ************************************************; * definição dos registos e bits para os timers *; ************************************************; peso dos BITS DO TMODM0_T0 EQU 01HM1_T0 EQU 02HCT_T0 EQU 04HGATE_T0 EQU 08HM0_T1 EQU 10HM1_T1 EQU 20HCT_T1 EQU 40HGATE_T1 EQU 80H
; ********************************************; *** rotinas para incialização do Timer 0 ***; ********************************************TIMERS SEGMENT CODE
RSEG TIMERS
; rotina de inicialização do timer 0 que não destrói a programação do timer 1; Timer 0 programado em modo 1, e; existe Timer Overflow passados 50000uSPUBLIC INIT_TIMER0INIT_TIMER0:
Algumas considerações devem ser feitas acerca da solução apresentada anteriormente.
A rotina de tratamento de interrupção de Timer Overflow, chamada INT_T0_ROT, faz a reinicialização do Timer0 nas suas duas primeiras instruções para minimizar o erro temporal relativamente ao que é pedido no enunciado. E que é, passo a citar, “O contador deve contar 20 unidades por segundo.”. No entanto, existe um erro de tempo que é derivado do tempo que decorre entre existir Timer Overflow e a completa execução das 2 instruções de reinicialização de TL0 e TH0.
Na figura seguinte, tenta-se ilustrar o erro temporal que não é exactamente quantificável, porque um pedido de interrupção é assíncrono relativamente à normal execução de instruções.Timer Overflow
A questão que se coloca perante este problema é:Qual a solução para a correcta contabilização de tempo?A resposta é: não existe uma forma exacta de contabilizar este tempo. No entanto, o próprio temporizador, que estamos a utilizar, é um contador de tempo então porque não utilizá-lo para a contagem do tempo ilustrado na figura anterior.Como?Se pensarmos um pouco, o tempo de execução das várias instruções a cumprir vai fazer com que o par de registos (TH0, TL0) fique com um valor diferente de zero. Logo a operação a realizar não será:
(TH0, TL0) = 0-50000
mas sim,
(TH0, TL0)= (TH0, TL0) – 50000
De referir, que para o problema simples do contador o erro temporal é quase contabilizavel pela figura anterior, mas geralmente e pensando num problema com vários níveis de interrupção, o tempo pode-se tornar perfeitamente incontabilizavel.Considere a figura seguinte em que a interrupção do Timer Overflow 0 tem prioridade 0 e existem mais fontes de interrupção com prioridades superior, então o erro temporal é ilustrado pela figura seguinte:
As setas bidireccionais da figura anterior representam a chamada e retorno das rotinas de tratamento de interrupção com prioridade crescente. A figura é confusa e esta confusão ilustra devidamente a inaptidão para se medir um tempo correcto e concreto.
Nota FinalEspero sinceramente que esta versão, ainda em construção, das folhas de apoio lhe tenham sido particularmente úteis na concretização teórico-prática da disciplina de Introdução aos Microprocessadores.
O docente Jorge Pais
Bibliografia• SAB80535 Quick ReferenceSecção de Sistemas DigitaisDEETC – ISEL
• SAB80535 8-Bit Single-Chip Microcontroller Family Users Manual 08.95Microcomputer ComponentsSiemens
Deste modo, o conjunto de instruções que devem ser realizadas no início da rotina de tratamento da interrupção Timer Overflow, (INT_T0_ROT), para uma nova inicialização do temporizador com vista a minimizar o erro temporal do à precisão do temporizador que é de 1us, será:
VALOR EQU 50000 ; 20 vezes por segundo
; rotina de tratamento da interrupção gerada por; Overflow do Timer 0INT_T0_ROT:
PUSH PSWPUSH ACC; reinicialização do timerCLR TR0 ; pára o timerMOV A, TL0ADD A, #LOW(-VALOR+7) ; 7 é o nº de instruções MOV TL0, A ; da reinicializaçãoMOV A, TH0ADDC A, #HIGH(-VALOR+7)MOV TH0SETB TR0; fim da reinicializaçãoINC CONTADORMOV A, CONTADORCJNE A, #0, IT0_FIMINC CONTADOR+1