MM – Microprocessadores e Microcontroladores – Roteiro da Experiência 17 EXPERIÊNCIA 17 – USO DO TEMPORIZADOR INTERNO Parte I – Fundamentos Teóricos O que diferencia um microcontrolador (como o 8051) de um microprocessador é o fato de que o primeiro pode apresentar, integrados na mesma pastilha, além da UCP, outros dispositivos componentes de um sistema computacional, como memória, conversores A/D e D/A, interfaces de entrada/saída, etc. Os componentes da família do 8051 apresentam circuitos internos para temporização e contagem de eventos. São os chamados “timers” – o “timer0” e o “timer1”, no caso do 8051 – aos quais estão associados diversos registradores especiais, cujas funções serão estudadas a seguir. Cada “timer” tem 4 possíveis modos de operação. A seleção do modo de operação de cada “timer” é feita por 2 bits do registrador especial TMOD (endereço 89h). Cada “timer” tem sua configuração independente (isto é, os “timers” não precisam operar no mesmo modo). A tabela a seguir apresenta a maneira de se configurar o modo de operação de cada “timer”. “timer1” M1 M0 “timer0” M1 M0 (TMOD.5) (TMOD.4) (TMOD.1) (TMOD.0) 0 0 1 1 0 1 0 1 0 0 1 1 0 1 0 1 modo de operação modo 0: temporizador/contador de 13 bits modo 1: temporizador/contador de 16 bits modo 2: temporizador/contador de 8 bits, com recarga automática modo 3: 2 temporizadores/contadores de 8 bits (apenas “timer0”) Nesta experiência, utilizaremos apenas o “timer0”, configurado no modo 1. A descrição dos demais modos pode ser vista na documentação do 8051. No modo 1, os registradores especiais TH0 (endereço 8Ch) e TL0 (endereço 8Ah), de 8 bits cada, se associam para formar um único registrador de 16 bits, utilizado pelo “timer0” (analogamente, o “timer1” irá se utilizar dos registradores TH1, endereço 8Dh, e TL1, endereço 8Bh). Nesse registrador de 16 bits, os 8 bits mais significativos são os do registrador TH0 (“H” de “high”), enquanto os demais 8 bits, menos significativos, são os do registrador TL0 (“L” de “low”). Nesses 16 bits, é armazenado um valor de contagem, compreendido entre 0 e 65.535 (= 2161). A qualquer momento, o valor da contagem do “timer0” (ou do “timer1”) pode ser verificado pelo programa, através da leitura dos registradores TH0 e TL0 (ou TH1 e TL1). Da mesma forma, esse valor pode ser alterado, por uma escrita nesses mesmos registradores. A restrição é a de que esses acessos precisam ser feitos em duas etapas: não existe instrução capaz de acessar os 2 registradores simultaneamente. No modo 1 de operação (bem como nos demais modos), o valor de contagem é incrementado automaticamente, de acordo com a forma com que o “timer” foi configurado: se como contador de eventos ou se como temporizador. Se, por exemplo, o “timer0” for configurado como contador de eventos, o incremento do seu registrador de 16 bits ocorre a cada borda de descida de um sinal externo, presente no pino T0, que é o pino 4 da porta P3 (analogamente, o registrador do “timer1” é incrementado nas bordas de descida do pino T1, que é o pino 5 da porta P3). Já se o “timer0” for configurado como temporizador, o valor de contagem é incrementado a uma taxa fixa, a cada “ciclo de máquina”, que corresponde a 12 períodos do “clock” do oscilador, o que significa taxa de contagem é 1/12 da freqüência do “clock” do oscilador (como, no nosso caso, o “clock” é de 12MHz, a taxa de contagem é de 1MHz = 106Hz, isto é, a contagem é incrementada a cada 1µs = 106s, quando o “timer” é configurado como temporizador). Portanto, configuração contagem é incrementada ... freqüência contador de eventos temporizador ... a cada borda de descida do sinal no pino T0 (ou T1) ... a cada ciclo de máquina (12 períodos de “clock”) variável (depende do sinal no pino T0 (ou T1)) constante (=1MHz) Exemplo de aplicação do “timer” como contador ou como temporizador: o computador de bordo de bicicleta O computador de bordo de bicicleta é um dispositivo capaz de mostrar a distância percorrida (função odômetro) e a velocidade (função velocímetro). Num dos raios de uma das rodas, é colocado um ímã. Um sensor instalado no garfo é capaz de, a cada passagem do ímã, (isto é, a cada revolução da roda), gerar um pulso elétrico, transmitido por um fio até o computador de bordo. Previamente, o computador de bordo é programado com a medida do raio da roda. Assim, para poder funcionar como odômetro, bastalhe um contador de eventos: o número de pulsos registrado durante um certo percurso, multiplicado por 2π e pelo raio da roda, fornece a distância percorrida. MM – Microprocessadores e Microcontroladores – Roteiro da Experiência 17 Já a determinação da velocidade exige um temporizador, pois é preciso dividir a distância percorrida pelo tempo de percurso, e este pode ser determinado pelo número de ciclos de máquina, multiplicado pelo tempo de cada ciclo (no nosso caso, 1µs) MM – Microprocessadores e Microcontroladores – Roteiro da Experiência 17 A seleção para a configuração do “timer0” como contador de eventos ou como temporizador é feita por outro bit do registrador especial TMOD: o bit C/T0 (bit 2) (analogamente, C/T1, o bit 6 de TMOD, seleciona a configuração do “timer1”). Com esse bit em “0”, o “timer” fica configurado como temporizador (incrementado a cada ciclo de máquina). Com o bit C/T em “1”, o “timer” passa a atuar como contador de eventos, isto é, de bordas de descida aplicadas ao pino T0 (ou T1, no caso do “timer1”). Independentemente de sua configuração como contador de eventos ou como temporizador, cada “timer” pode ser ligado ou desligado por programa, através de um bit do registrador especial TCON (cujo endereço de registrador é 88h): o bit TR0 (TCON.4, cujo endereço de bit é 8Ch), que liga o “timer0” ao ser colocado em “1”, e o desliga quando em “0”. Analogamente, o bit TR1 (TCON.6, cujo endereço de bit é 8Eh) controla o “timer1”. Quando o “timer” está desligado, o seu valor de contagem permanece sem ser alterado, mesmo que ocorram bordas de descida no pino T0 ou ciclos de máquina. Notese, portanto, que desligar um “timer” não significa “zerar” seu valor de contagem, mas sim “travar” este valor. Para “zerar” a contagem, é preciso escrever “0” nos registradores de contagem: TH0 (ou TH1) e TL0 (ou TL1). Por ser fornecido em 16 bits, o valor de contagem pode ser, no máximo, de 65.535 (=216−1). Isso significa que, se os registradores de contagem tiverem sido inicialmente “zerados”, poderão ser contadas, no máximo, 65.535 bordas de subida no pino T0 (ou T1), se o “timer” estiver configurado como contador de eventos; ou a passagem de 65.535µs (=0,065535s), se o “timer” estiver configurado como temporizador. Nesse caso, ocorrendo mais uma contagem (outra borda de descida em T0 ou T1, ou mais 1µs), o valor da contagem volta a zero. Nesse momento, porém, ocorre um pedido de interrupção, causada pelo estouro da contagem do “timer”. Como já foi visto em outra experiência, caso a respectiva interrupção esteja habilitada (por bits do registrador especial IE, já vistos), ocorre um desvio do programa para o seu tratamento, a partir do endereço 000Bh (para o “timer0), ou 001Bh (para o “timer1”). Com o “timer0” configurado como temporizador, temporizações de até 0,065535s podem ser indicadas pela ocorrência de um pedido de interrupção de estouro de um dos “timers” (por exemplo, o “timer0”), desde que sejam seguidos os seguintes passos: • “timer0” deve ser configurado como temporizador (C/T=TMOD.2 ← 0), no modo 1 (M1=TMOD.1 ← 0 e M0=TMOD.0 ← 1) • valor inicial de contagem deve ser escrito nos registradores especiais TH0 e TL0 (mais adiante é mostrado como esse valor deve ser calculado) • a interrupção de estouro de contagem do “timer0” deve ser habilitada (ET0=IE.1 ← 1 e EA=IE.7 ← 1) • “timer0” deve ser ligado (TR0=TCON.4 ← 1) A partir desse instante, o valor de contagem do “timer0” passa a ser incrementado a cada 1µs. Ao estourar, voltando a 0 depois de ter passado por 65.535, gera um pedido de interrupção, cujo tratamento deve sinalizar o fim da temporização previamente pretendida. O tempo decorrido (T), em segundos, depende, evidentemente, do valor inicial de contagem do “timer” (TIMER_INICIAL): T = (65.536 − TIMER_INICIAL) x 106 A temporização mais longa possível, Tmax, será obtida para o menor valor inicial de contagem, TIMER_INICIALmin. Fazendo TIMER_INICIALmin ← 0 então Tmax = (65.536 − 0) x 106 = 0,065536s = 65,536ms Para uma temporização T qualquer, menor do que esse valor, temse TIMER_INICIAL ← inteiro (65.536 − 106 x T + 0,5) onde 0 < T ≤ 0,065536s(=65,536ms) Temporizações ainda mais longas do que 65,536ms podem ser obtidas através do uso de uma variável contadora de interrupções, CONTA_INT, cujo valor inicial seja CONTA_INT_INICIAL. O tratamento do pedido de interrupção por estouro do temporizador precisa, agora, decrementar CONTA_INT a cada interrupção, verificando se chegou a 0, caso em que a temporização estaria encerrada. Caso contrário, o “timer” precisa ter seu valor de contagem (que acaba de passar a 0) reiniciado com TIMER_INICIAL, até que estoure, gerando um novo pedido de interrupção. O tempo decorrido (T), em segundos, fica agora sendo: T = CONTA_INT_INICIAL x (65.536 − TIMER_INICIAL) x 106 Agora, para a determinar a temporização mais longa possível, Tmax, é necessário fazer TIMER_INICIALmin ← 0 CONTA_INT_INICIALmax ← 256 (na verdade, o valor atribuído deve ser 0) então Tmax = 256 x (65.536 − 0) x 106 = 16,777216s ≅ 16,8s MM – Microprocessadores e Microcontroladores – Roteiro da Experiência 17 Para uma temporização T qualquer, temse que CONTA_INT_INICIAL x (65.536 − TIMER_INICIAL) = 106 x T ou seja, é necessário determinar um par de inteiros, CONTA_INT_INICIAL e TIMER_INICIAL, que satisfaçam a equação anterior, e tais que 0 < CONTA_INT_INICIAL ≤ 256 0 ≤ TIMER_INICIAL < 65.535 MM – Microprocessadores e Microcontroladores – Roteiro da Experiência 17 Uma possível forma de se determinar esse par de valores é fazer: CONTA_INT_INICIAL ← inteiro ( (106 x T / 65.536) + 1 ) TIMER_INICIAL ← inteiro ( ( (CONTA_INT_INICIAL x 65.536 − 106 x T) / CONT_INT_INICIAL) + 0,5 ) No caso desta experiência, desejase uma temporização de 1s (isto é, T = 1s). Logo: CONTA_INT_INICIAL ← inteiro ( (106 x 1 / 65.536) + 1 ) = inteiro (16,26) = 16 TIMER_INICIAL ← inteiro ( ( (16 x 65.536 − 106 x 1) / 16) + 0,5 ) = 3.036 Finalmente, um último problema: uma vez obtido o valor inicial de contagem (TIMER_INICIAL), é preciso determinar TIMER_INICIAL_H e TIMER_INICIAL_L (bytes mais e menos significativos), a fim de carregálos em TH0 e TL0, respectivamente. Sabendose que TIMER_INICIAL = 256 x TIMER_INICIAL_H + TIMER_INICIAL_L vem que TIMER_INICIAL_H ← inteiro (TIMER_INICIAL / 256) TIMER_INICIAL_L ← TIMER_INICIAL − 256 x TIMER_INICIAL_H No nosso caso, com TIMER_INICIAL = 3.036 vem que TIMER_INICIAL_H ← inteiro (3036 / 256) = inteiro (11,86) = 11 TIMER_INICIAL_L ← 3.036 − 256 x 11 = 220 Parte II – Roteiro Experimental Nesta experiência, você deverá realizar a montagem de um programa já escrito na linguagem de montagem do 8051, editado no arquivo RELUZINT.ASM, cuja listagem é fornecida. Ao ser executado, desde que os LEDs do kit tenham sido convenientemente conectados aos pinos da porta P1, como já foi feito na experiência 13, haverá o mesmo efeito de deslocamento de luzes daquela experiência. Ao mesmo tempo, um relógio digital será apresentado no display, incrementado a cada segundo. Antes de executar o programa, é preciso ajustar o valor inicial do horário. Descreva, sucintamente, o funcionamento do programa. EXPERIÊNCIA 17 – QUESTÕES ADICIONAIS 1) Em qual área da memória são armazenadas as indicações de hora, minuto e segundo? 2) Em quais endereços dessa área essas indicações são armazenadas? 3) Explique o motivo para as instruções PUSH ACC e POP ACC, existentes na rotina R_TRATA_INT_TIMER0. Indique um ponto do programa em que a ocorrência de uma interrupção do “timer0” causaria problemas, caso essas instruções não existissem. 4) Considere o planeta SPEEDY, cujo sistema de marcação de tempo é igual ao da Terra (ou seja, tem dias com 24 horas, horas com 60 minutos, minutos com 60 segundos), mas no qual o segundo é 20 vezes mais rápido do que o segundo na Terra. Altere o arquivo RELUZINT.ASM, gerando o arquivo RELSPEED.ASM, de forma a apresentar o relógio do planeta SPEEDY (mantendo o ritmo dos LEDs). Teste seu programa, lembrando que cada minuto terráqueo corresponde a 20 minutos speedyanos. MM – Microprocessadores e Microcontroladores – Roteiro da Experiência 17 $INCLUDE(REG51.INC) $INCLUDE(MONIT.INC) ; declaracoes de simbolos ; valores calculados para obter uma temporizacao de 1s C_CONTA_INT_INICIAL EQU 16 C_TIMER_INICIAL_H EQU 11 C_TIMER_INICIAL_L EQU 220 DSEG ORG 0070h HORA: DS 1 ; valor das horas MINUTO: DS 1 ; valor dos minutos SEGUNDO: DS 1 ; valor dos segundos CONTA_INT: DS 1 ; numero de interrupcoes do timer0 ; (ao chegar a 0, indica que se passou 1s) CSEG ; desvio para o tratamento da interrupcao do timer0 ORG VETORINT_TIMER0 JMP R_TRATA_INT_TIMER0 ORG 5000h MOV SP, #2Fh ; inicia ponteiro da pilha MOV TMOD, #00000001b ; timer0 no modo 1 MOV TH0, #C_TIMER_INICIAL_H MOV TL0, #C_TIMER_INICIAL_L ; contagem para 1/16 s MOV CONTA_INT, #C_CONTA_INT_INICIAL ; contagem para 1s SETB ET0 ; habilita int. do timer0 SETB EA ; habilita todas ints. ja habilitadas LCALL CLR_DSP ; limpa o display MOV A, #0Ch LCALL DSP_COM ; tira o cursor do display SETB TR0 ; liga timer0 REPETE: MOV R0, #00010000b ; inicia estados dos LEDs da esquerda MOV R1, #00001000b ; inicia estados dos LEDs da direita MOV R2, #4 ; inicia contador de deslocamentos VOLTA: MOV A, R0 ADD A, R1 ; junta estados dos LEDs MOV P1, A ; acende e apaga LEDs LCALL ATRASO ; espera um tempo MOV A, R0 RL A MOV R0, A ; desloca LED aceso para a esquerda MOV A, R1 RR A MOV R1, A ; desloca LED aceso para a direita DJNZ R2, VOLTA ; se ainda nao chegou na ponta, volta LJMP REPETE ; se ja' chegou na ponta, reinicia ; rotina para esperar um tempo ATRASO: PUSH 0 PUSH 1 PUSH 2 MOV R0, #3 SALTO3: MOV R1, #0 SALTO2: MOV R2, #0FFh SALTO1: DJNZ R2, SALTO1 DJNZ R1, SALTO2 ; tratamento da interrupcao do timer0 R_TRATA_INT_TIMER0: PUSH ACC ; salva acumulador na pilha (vai usalo na interr.) MOV TH0, #C_TIMER_INICIAL_H MOV TL0, #C_TIMER_INICIAL_L ; recarrega contador de 1/16s DJNZ CONTA_INT, L_NAO_1S ; verifica se completou 1s LCALL R_INCREMENTA_HORARIO ; atualiza horario, com mais 1s LCALL R_MOSTRA_HORARIO ; mostra horario atualizado MOV CONTA_INT, #C_CONTA_INT_INICIAL ; recarrega contador de 1s L_NAO_1S: POP ACC ; restaura acumulador RETI ; incremento do horario (passou 1s) R_INCREMENTA_HORARIO: MOV A, SEGUNDO ADD A, #1 DA A MOV SEGUNDO, A ; incrementa os segundos CJNE A, #60h, L_FIM_INCREMENTA ; precisa incrementar min? MOV SEGUNDO, #0 ; e zerar segundos? MOV A, MINUTO ADD A, #1 DA A MOV MINUTO, A ; incrementa os minutos CJNE A, #60h, L_FIM_INCREMENTA ; precisa incrementar hora? MOV MINUTO, #0 ; e zerar minutos? MOV A, HORA ADD A, #1 DA A MOV HORA, A ; incrementa as horas CJNE A, #24h, L_FIM_INCREMENTA ; precisa zerar hora? MOV HORA, #0 L_FIM_INCREMENTA: RET ; apresentacao do horario R_MOSTRA_HORARIO: MOV A, #0C4h LCALL DSP_COM ; posiciona cursor na coluna 5 da linha 2 MOV A, HORA LCALL AC_DSP ; mostra hora MOV A, #':' LCALL DSP_DAT MOV A, MINUTO LCALL AC_DSP ; mostra minuto MOV A, #':' LCALL DSP_DAT MOV A, SEGUNDO LCALL AC_DSP ; mostra segundo RET END MM – Microprocessadores e Microcontroladores – Roteiro da Experiência 17