Você está na página 1de 204

Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Introdução

Este tutorial foi feito para as pessoas que têm a eletrônica como diversão e
desejam aprender a utilizar microcontroladores em seus projetos.
Também sou um entusiasta da eletrônica e gosto de entender como as coisas
funcionam. Por isso, escrevo os programas para os microcontroladores em linguagem Assembly.
Se você é como eu, creio que gostará deste tutorial.
Boa leitura!

Mulder_Fox
Membro do fórum de Eletrônica do Clube do Hardware
http://forum.clubedohardware.com.br/eletronica/f39

Parte 1
Pisca LED

Nesta primeira parte, vamos montar um circuito para fazer um LED piscar
numa frequência de aproximadamente um Hz.
Vamos utilizar o microcontrolador PIC16F628A, um dos modelos mais
usados hoje em dia.

Figura 1

1
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Os microcontroladores precisam de um sinal de clock para funcionar, mas


muitos modelos possuem a alternativa de usar um circuito oscilador interno para gerar esse sinal.
Este é o caso do PIC16F628A, onde podemos escolher a frequência de 48 KHz ou a de 4 MHz.
A configuração do oscilador interno como fonte do sinal de clock e a sua
frequência são feitos no programa que é gravado no PIC.
Antes de escrevermos o programa para o microcontrolador, vamos desenhar
o esquema do circuito.
Segundo consta no datasheet do PIC16F628A, a sua tensão de alimentação
pode ser de 3 v até 5,5V, sendo que com 3 v, a frequência máxima do sinal de clock é de 10 MHz
enquanto que a partir de 4,5V é de 20Mhz.
Vamos utilizar um regulador de tensão LM7805 para fornecer uma tensão de
5 v a partir de 9 v.
Além do LM7805 e do PIC16F628A iremos utilizar um LED e um resistor
para limitar sua corrente, que serão ligados diretamente no pino do PIC, já que ele tem a capacidade
de fornecer a corrente necessária para o LED.
Mas, em qual pino do PIC iremos ligar o LED?
O PIC16F628A possui 15 pinos que podem ser usados como entrada ou
saída: RA0, RA1, RA2, RA3, RA4, RA6, RA7, RB0, RB1, RB2, RB3, RB4, RB5, RB6 e RB7 e 1
pino que só pode ser usado como entrada: RA5.
O pino RA4 é o único que, quando configurado como saída, é do tipo open
drain, ou seja, a carga conectada a este pino deve estar ligada ao positivo da alimentação.
É possível fazer com que resistores de pull-up integrados no PIC sejam
conectados nos pinos RB0, RB1, RB2, RB3, RB4, RB5, RB6 e RB7, individualmente, para o caso
de um ou mais destes pinos estarem sendo usados como entrada, economizando, desta forma, o uso
de resistores externos. Veremos como fazer isto em outra parte deste tutorial.
Antes de definirmos qual pino iremos utilizar, precisamos saber se as
características do pino atendem às nossas necessidades.
No nosso circuito precisaremos de um pino que possa ser usado como saída
e, portando, temos 15 à nossa disposição. Podemos escolher qualquer um deles, por exemplo, o
RA0.
Portanto, o esquema do circuito ficou assim:

Figura 2

2
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Definido o esquema do circuito, vamos elaborar o fluxograma do programa


que iremos gravar no PIC.
O fluxograma é uma representação gráfica de como o programa se comporta
conforme as possíveis situações.
Com o fluxograma, fica mais fácil escrever o programa.
Eis o fluxograma do nosso programa:

Início

Configuração
dos registradores

Inicialização das
variáveis

Passou 0,5
segundo? não

sim

Acende LED LED está aceso? Apaga LED


não sim

Indica o início

Indica uma subrotina

3
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Indica uma decisão

Indica acesso a um dispositivo de I/O

Agora podemos começar a escrever o programa utilizando o software


MPLAB IDE da Microchip.
Faça o download do MPLAB IDE do site da Microchip e instale em seu
computador. A última versão disponível na data que foi escrito este tutorial é a 8.63.00.00.
A tela inicial do MPLAB IDE pode ser vista na figura 3.
No menu “File”, clique em “New”.
Novamente, no menu “File”, clique em “Save As...” e escolha um nome para
o arquivo, com a extensão .asm. Por exemplo: Pisca LED .asm.

Figura 3

Primeiramente vamos criar um cabeçalho onde irá constar o nome do


programa, sua versão, o nome do autor e a data de conclusão, ficando assim:

4
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

;***********************************************************************************************
; PROGRAMA: PISCA LED
; VERSÃO 1.0
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /2011
;***********************************************************************************************

Tudo o que for digitado na linha após ponto e vírgula será ignorado pelo
MPLAB na hora da montagem do código, portanto, todas as anotações e comentários têm de virem
precedidos de ponto e vírgula.
Repare no ponto e vírgula no início de cada linha que faz com que o
MPLAB ignore o que está escrito após.
Em seguida vamos incluir no nosso programa o arquivo padrão de
definições do PIC16F628A, usando a diretiva #INCLUDE:
;***********************************************************************************************
#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A
;***********************************************************************************************
Repare que antes de #INCLUDE <P16F628A.INC> não há ponto e vírgula
e portanto o MPLAB irá executar esse comando.
Observe que foi deixado um espaço a partir da margem esquerda. Isso
porque se o MPLAB encontra uma diretiva na margem esquerda, ele envia uma mensagem de alerta
na hora de criar o arquivo a ser gravado no microcontrolador.
Observe que na mesma linha, antes do comentário “ARQUIVO PADRAO
MICROCHIP PARA O PIC16F628A” há ponto e vírgula, pois, não queremos que o MPLAB leve
em consideração o que está escrito, pois se trata de um comentário (não faz parte do programa).
Repare que após digitar #INCLUDE a cor da letra ficou azul, O MPLAB faz
isso sempre que reconhece o termo como uma diretiva. Isso ajuda a perceber quando escrevemos a
diretiva de forma errada, pois, aí ela não ficará azul.
No arquivo P16F628A.INC é onde constam, além do modelo do
microcontrolador, as correspondências entre os nomes dos registradores e as respectivas posições
que eles ocupam na memória de dados, bem como, entre os nomes de cada bit e sua posição no
registrador, entre outras informações, como tamanho e endereços válidos da memória e bits de
configuração.
O que ocorre é que, para facilitar a vida do programador, as localidades da
memória de dados, ou seja, os registradores, recebem nomes.
Quando o MPLAB vai traduzir o programa para a linguagem de máquina ele
usa essas correspondências.
Se você quiser ver o arquivo P16F628A.INC, localize-o na pasta MPASM
Suite que é uma subpasta da pasta Microchip, criada no diretório onde foi instalado o MPLAB IDE.
O próximo passo é ajustar os Bits de Configuração, também conhecidos por
“fusíveis” ou “fuses”. Isso é feito com a diretiva __CONFIG.
;***********************************************************************************************
; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF
& _LVP_OFF & _CP_OFF & DATA_CP_OFF

;**********************************************************************************************

5
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

São 2 “underlines” antes do CONFIG, seguido de espaço e, após, as


expressões antecedidas de 1 “underline” separadas pelo símbolo “&” entre espaços.
Nos Bits de Configuração selecionamos o tipo de oscilador que será usado e
se os seguintes recursos serão ativados: WDT (Watch Dog Timer), PWRTE (Power-up Timer),
MCLRE (Master Clear Enable), BOREN (Brown-out Reset), LVP (Low Voltage Program),
DATA_CP (proteção da memória de dados), CP (proteção da memória de programa).
As opções para seleção do oscilador são:

_LP_OSC : Cristal de frequência até cerca de 1MHz ligado entre os pinos


OSI e OSO.
_XT_OSC : Cristal de frequência de cerca de 1MHz a 4 MHz ligado entre
os pinos OSI e OSO.
_HS_OSC : Cristal de frequência superior a 4 MHz ligado entre os pinos
OSI e OSO.
_EXTCLK_OSC : Sinal de clock externo aplicado no pino CLKIN.

_INTOSC_OSC_NOCLKOUT : Oscilador interno.

_INTOSC_OSC_CLKOUT : O mesmo que o anterior, porém com saída do


sinal de clock no pino CLKOUT (¼ da frequência) para fins de sincronização de hardware externo.

_RC_OSC_NOCLKOUT : Oscilador a base de resistor e capacitor ligados


no pino CLKIN.
_RC_OSC_CLKOUT : O mesmo que o anterior, porém com saída do sinal
de clock no pino CLKOUT (¼ da frequência) para fins de sincronização de hardware externo.

No nosso caso, escolhemos a opção _INTOSC_OSC_NOCLKOUT, ou seja,


oscilador interno sem saída do sinal.
O WDT (Watch Dog Timer) é um recurso que reinicia o microcontrolador,
caso o programa travar.
Vamos habilitar esse recuso escrevendo _WDT_ON.
Se não quiséssemos ativar o recurso escreveríamos _WDT_OFF.
O PWRTE (Power-up Timer) é um circuito que mantém o microcontrolador
em reset por 72 ms após a alimentação ser ligada para que dê tempo do oscilador estabilizar.
Vamos habilitar também este recurso escrevendo _PWRTE_ON.
O MCLRE (Master Clear Enable), se estiver ativado, reserva o pino MCLR
para a função de reset do microcontrolador.
Este recurso não nos interessa e, por isso, o deixamos desligado, escrevendo
_MCLRE_OFF.
O BOREN (Brown-out reset) é um recurso que monitora a tensão de
alimentação e quando ela cai abaixo de 4,5V provoca o reset.
Este recurso também não nos interessa e, por isso, escrevemos
_BOREN_OFF.
O LVP (Low Voltage Program) é um recurso que permite que o
microcontrolador seja gravado sem a necessidade de aplicar uma tensão de cerca de 13V no pino
VPP (veremos sobre gravação do PIC no momento oportuno).
Esta função não nos interessa e, por isso, escrevemos _LVP_OFF.

6
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

DATA_CP é um recurso para proteger a memória de dados contra cópia.


CP é um recurso para proteger a memória de programa contra cópias.
Estes recursos interessam a quem fabrica aparelhos eletrônicos e quer evitar
engenharia reversa.
Estes recursos não nos interessam, e, por isso, escrevemos _CP_OFF &
DATA_CP_OFF.
O próximo passo é definir as “labels” para a comutação dos bancos de
memória de dados.
Fazemos isto utilizando a diretiva #DEFINE desta forma:
;**********************************************************************************************
; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA

;**********************************************************************************************
A memória de dados está dividida em quatro bancos (figura 4).
Para ter acesso a qualquer registrador a fim de ler ou alterar o seu valor,
precisamos ajustar os valores dos bits RP0 e RP1 do registrador STATUS, para selecionar o banco
onde se encontra o registrador.
Banco 0: RP0 = 0, RP1 = 0
Banco 1: RP0 = 1, RP1 = 0
Banco 2: RP0 = 0, RP1 = 1
Banco 3: RP0 = 1, RP1 = 1
Neste programa que estamos fazendo, não necessitaremos de acessar os
bancos 3 e 4, pois, os Registradores de Uso Específico que neles se encontram também estão nos
bancos 0 ou 1 e não iremos precisar das posições destinadas a Registradores de Uso Geral que lá se
encontram, pois, nos bastarão os disponíveis no banco 0.
Como o bit RP1 inicializa com o valor 0, basta que alteremos o bit RP0,
para alternar entre os bancos 0 e 1.
Usamos a diretiva #DEFINE para que onde houver a palavra BANCO_0 o
microcontrolador execute a instrução BCF STATUS,RP0.
BANCO_0 é o que chamamos de “label” e poderia ser outra palavra de sua
preferência.
A instrução BCF serve para fazer o valor de um determinado bit igual a 0.
Em BCF STATUS,RP0 STATUS é o nome do registrador e RP0 o
nome do bit deste registrador cujo valor ficará igual a 0.
Nós também usamos a diretiva #DEFINE para que onde houver a palavra
BANCO_1 o microcontrolador execute a instrução BSF STATUS,RP0.
A instrução BSF serve para fazer o valor de um determinado bit igual a 1.
Desta forma, fica mais fácil fazer a comutação entre os bancos de memória,
pois basta escrever a palavra BANCO_0 para que o banco 0 da memória de dados seja selecionado
e a palavra BANCO_1 para o banco 1.
Repare que após digitar BCF e BSF, a cor da letra ficou azul e em negrito. O
MPLAB faz isto com qualquer termo reconhecido como uma instrução. Isto nos ajuda a perceber se
escrevermos uma instrução de forma errada.

7
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 4

O próximo passo é definir as variáveis que iremos utilizar em nosso


programa.
Uma variável é um Registrador de Uso Geral, ou seja, é uma das posições da
memória de dados que podemos usar para armazenar os valores dos dados que vamos manipular no
nosso programa.
À medida que vamos escrevendo o programa, vamos precisando criar

8
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

variáveis. Por esse motivo, minha forma preferida para defini-las é com o uso da diretiva CBLOCK:
;**********************************************************************************************
; VARIAVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

DELAY_0 ;USADO PARA GERAR O TEMPO DE 0,5 SEGUNDO


DELAY_1 ;USADO PARA GERAR O TEMPO DE 0,5 SEGUNDO
DELAY_2 ;USADO PARA GERAR O TEMPO DE 0,5 SEGUNDO
ENDC ;FIM DO BLOCO DE MEMORIA

;**********************************************************************************************

A expressão 0X20 após a diretiva CBLOCK indica o endereço na memória


de dados que assumirá a primeira variável definida (DELAY_0).
Esse endereço está em formato hexadecimal. Veja na figura 4 que ele (lá
representado por 20h) é o primeiro, no banco 0, que podemos usar como Registrador de Uso Geral.
Repare que o último é o 7Fh, totalizando 96 localidades de memória nesse
banco.
Os do endereço 70 até 7F são espelhados nos outros bancos, permitindo que
possam ser acessados sem necessidade de selecionar o banco.
No banco 1 temos outras 80 posições e no banco 2 mais 48.
Portanto, no PIC16F628A temos um total de 224 posições de memória
(registradores) que podemos usar para armazenar valores. Ou seja, podemos criar até 224 variáveis.
Voltando a falar da diretiva CBLOCK, após relacionar os nomes das nossas
variáveis, encerramos a diretiva com ENDC.
Desta forma, definimos o endereço 0X20 para DELAY_0, 0X21 para
DELAY_1 e 0X22 para DELAY_2.
Se posteriormente quisermos adicionar outras variáveis, podemos intercalá-
las sem problemas.
Há outras formas de definir variáveis, através das diretivas #DEFINE e
EQU, mas creio que a mais prática seja através da diretiva CBLOCK.

O próximo passo é definir as constantes.


Constantes são valores numéricos que utilizamos durante o programa.
Por exemplo, uma determinada variável pode precisar ser reiniciada sempre
com o mesmo valor. Este valor é uma constante.
No nosso programa, as variáveis DELAY_0, DELAY_1 c DELAY_2 serão
reiniciadas, cada qual, com valores que serão sempre os mesmos.
Esses valores serão ajustados depois que fizermos a rotina principal do
programa, por isso, os valores que iremos escrever agora serão provisórios.
Para definir as constantes, usamos a diretiva EQU:

;**********************************************************************************************
; CONSTANTES

INI_DELAY_0 EQU .255 ;VALOR QUE DELAY_0 INICIA


INI_DELAY_1 EQU .50 ;VALOR QUE DELAY_1 INICIA
INI_DELAY_2 EQU .13 ;VALOR QUE DELAY_2 INICIA
;***********************************************************************************************

9
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

No momento em que o MPLAB for montar o programa, onde estiver escrito


INI_DELAY_0, ele irá substituir essa expressão pelo valor 255 (decimal); INI_DELAY_1 por 50 e
INI_DELAY_2 por 13.

A vantagem de fazer isso é que se constatarmos que os valores para aquelas


variáveis não estavam satisfatórios, basta alterá-los ali, em vez de modificá-los em todas as linhas
do programa onde forem feitas reinicializações das variáveis.
A propósito, vamos falar de como representar, no programa, os valores numéricos
usando o número 10 como exemplo.
Quando estivermos nos referindo ao número 10 do sistema decimal, escreveremos
assim: D'10' ou ainda .10
Se for o numero 10 do sistema binário (2 do sistema decimal), escreveremos assim:
B'10'
Se for o número 10 do sistema hexadecimal (16 do sistema decimal),
escreveremos assim: H'10' ou assim: 0X10
A seguir vamos atribuir a “label” LED para o bit 0 do registrador PORTA.

;***********************************************************************************************
; SAÍDA

#DEFINE LED PORTA,0 ;LED LIGADO EM RA0

;***********************************************************************************************

A diretiva #DEFINE atribuiu a “label” LED para o bit 0 do registrador


PORTA.
Toda vez que aparecer no programa a palavra LED, o MPLAB saberá que se
trata do bit 0 do registrador PORTA.
Os registradores associados aos pinos de I/O são o PORTA e o PORTB.
O próprio nome do pino já nos diz qual é o registrador e o bit.
Pino RA0 – bit 0 do PORTA
Pino RA1 – bit 1 do PORTA
Pino RA2 – bit 2 do PORTA
Pino RA3 – bit 3 do PORTA
Pino RA4 – bit 4 do PORTA
Pino RA5 – bit 5 do PORTA
Pino RA6 – bit 6 do PORTA
Pino RA7 – bit 7 do PORTA
Pino RB0 – bit 0 do PORTB
Pino RB1 – bit 1 do PORTB
Pino RB2 – bit 2 do PORTB
Pino RB3 – bit 3 do PORTB
Pino RB4 – bit 4 do PORTB
Pino RB5 – bit 5 do PORTB
Pino RB6 – bit 6 do PORTB
Pino RB7 – bit 7 do PORTB

10
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Quando um pino estiver configurado como entrada (veremos depois como


se faz a configuração dos pinos como entradas ou saídas), o nível lógico presente nele poderá ser
verificado fazendo-se a leitura do valor do bit correspondente. Se o valor do bit é igual a 1, então o
nível lógico no pino é alto e vice-versa.
Por exemplo, suponhamos que o pino RB2 esteja configurado como entrada.
Para sabermos qual é o seu nível lógico, fazemos a leitura do bit 2 do
registrador PORTB (veremos no momento oportuno como verificar o nível lógico de um bit).
Para o pino RA6, lemos o bit 6 do PORTA e assim por diante.
Se um pino estiver configurado como saída, e quisermos levar o seu nível
para alto, tornamos o valor do seu bit correspondente igual a 1. Para levar o pino para nível baixo,
tornamos o valor do bit correspondente igual a 0, ou seja, controlando o valor do bit controlamos o
nível no pino.
O próximo passo é o vetor de reset.

;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO


GOTO INICIO ;DESVIA PARA INICIO

;***********************************************************************************************
Após a inicialização e depois de um reset, o microcontrolador executa a
instrução que estiver no endereço 0X00 da memória de programa.
Em seguida ele irá executar a instrução presente no endereço 0X01, depois
0X02 e assim por diante.
A diretiva ORG indica em qual endereço da memória de programa deverá
ser escrita a instrução seguinte. No nosso programa, a instrução GOTO INICIO ocupará o endereço
0X00 da memória de programa, ou seja, será a primeira instrução a ser executada pelo
microcontrolador.
O microcontrolador ao executar esta instrução desvia para o endereço da
memória de programa ocupado pela instrução que estiver após a label INICIO.
Mas, porque fazer esse desvio?
Os microcontroladores possuem um recurso muito útil chamado Interrupção,
que é a interrupção da execução do programa devido a um evento provocado por um periférico do
microcontrolador configurado para isso. Periféricos são os circuitos presentes no microcontrolador
que fazem funções específicas como contadores, geradores de sinal PWM, comparadores, etc...
Quando uma interrupção ocorre, o microcontrolador executa a instrução
presente no endereço 0X04 da memória de programa (no caso do PIC16F628A).
Este é o motivo de fazermos um desvio logo no endereço 0X00. Este desvio
será para depois do fim da rotina de interrupção, pois, o programa não caberia entre o endereço
0X00 e 0X03 e ele não pode ocupar os endereços a partir do 0X04, pois, ali estará a rotina de
interrupção.
Como neste programa não iremos utilizar o recurso da interrupção, iremos
escrever no nosso programa a instrução RETFIE no endereço 0X04 para que se, por acaso, ocorrer
uma interrupção indesejada, o programa possa retornar para o ponto de onde foi desviado:
;**********************************************************************************************
; ROTINA DE INTERRUPÇÃO
ORG 0X04 ;VETOR DAS INTERRUPÇÕES
RETFIE ;RETORNA

11
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

;***********************************************************************************************
Em outra parte deste tutorial falaremos detalhadamente sobre interrupções.
O próximo passo é configurar os Registradores de Uso Específico.

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA

MOVLW B'11111110' ;W = B'11111110'


MOVWF TRISA ;CONFIGURA RA0 COMO SAÍDA E DEMAIS COMO ENTRADAS
MOVLW B'11111111'
MOVWF TRISB ;TODOS OS PINOS DO PORTB COMO ENTRADAS

BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA

MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O

;***********************************************************************************************

Repare que aqui está a label INICIO.


Nesta parte do programa devemos configurar todos os Registradores de Uso
Específico que estejam envolvidos com os recursos do microcontrolador que iremos utilizar.
Por exemplo, se formos utilizar o TIMER 0, teremos de configurar o
registrador relacionado a ele. Se formos utilizar o módulo de PWM, devemos configurar o
registrador a ele relacionado e assim por diante, por isto devemos ler o datasheet do
microcontrolador para sabermos quais registradores deveremos configurar.
No nosso circuito estamos utilizando o pino RA0 do PORTA e por isto,
devemos configurar o registrador TRISA, responsável por definir cada pino do PORTA como
entrada ou saída.
Aqui também há uma relação direta entre o bit e o pino.
O bit 0 do TRISA configura o pino RA0, o bit 1 o pino RA1 e assim por
diante.
Se for atribuído o valor 0 para o bit, o pino é configurado como saída e se
for atribuído o valor 1, o pino é configurado como entrada. Memorize essa regra.
Observe, na figura 4, que o registrador TRISA está no banco 1.
Para termos acesso a esse registrador precisamos selecionar o banco 1,
escrevendo BANCO_1 (label que definimos para isso).
Os demais pinos não serão utilizados e não precisamos configurá-los, mas
aqui vai uma dica:
Configure como entrada os pinos que não estiverem sendo utilizados, pois,
se estiverem configurados como saída e se, por engano, um destes pinos for ligado diretamente ao
VSS ou ao VDD, poderá provocar a queima do microcontrolador. Estando configurados como
entrada, eles assumem alta impedância e não tem problema se forem ligados diretamente no VDD
ou no VSS.
O pino RA0 será configurado como saída e os demais pinos do PORTA
como entrada, então, precisamos escrever o número binário 11111110 no registrador TRISA (os bits
em um registrador estão na seguinte ordem: bit7, bit6, bit5, bit4, bit3, bit2, bit1, bit0).

12
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Não existe uma instrução para escrever diretamente um número num


registrador do PIC16F628A.
No microcontrolador existe um registrador chamado de W (Work).
Quando queremos escrever um número num registrador, primeiramente
devemos escrever esse número no registrador W.
Fazemos isso com a instrução MOVLW, desta forma:
MOVLW B'11111110'
Assim, escreve-se no registrador W o número binário 11111110.
O nome das instruções foram criados de forma a lembrar a sua função.
No caso da instrução MOVLW, MOV lembra mover. L lembra literal. A
instrução MOVLW move um número para o registrador W. Reparou na correspondência? (move L
para W).
Com o número já escrito no registrador W, podemos escrevê-lo no
registrador TRISA, usando a instrução MOVWF, assim:
MOVWF TRISA
A instrução MOVWF move o que estiver no registrador W para o
registrador escrito após a instrução, no caso, o TRISA.
Melhor dizendo, o conteúdo do registrador W é copiado para o TRISA, pois,
depois de executada a instrução, o registrador W continua com o mesmo valor que estava antes.
Pronto, configuramos o pino RA0 para funcionar como saída e os demais
pinos do PORTA para funcionarem como entrada.
Agora vamos configurar todos os pinos do PORTB como entrada, pelo
mesmo motivo que configuramos os outros pinos do PORTA.
O registrador TRISB é onde configuramos os pinos do PORTB como
entrada ou saída:
MOVLW B'11111111'
MOVWF TRISB

No PIC16F628A, bem como noutros modelos de microcontroladores, a


maioria dos pinos são compartilhados por mais de um recurso do microcontrolador.
É o caso do pino RA0 que estamos utilizando.
Além de ele ser um pino que podemos usar como entrada ou saída de sinal,
também é um dos pinos de entrada do módulo comparador.
O módulo comparador é um circuito do PIC16F628A que funciona como
um CI comparador de tensão.
Na inicialização do PIC16F628A, os pinos RA0, RA1, RA2 e RA3, estão
vinculados ao módulo comparador de tensão e, por isso, para que possamos utilizá-los como pinos
de entrada ou saída de sinal, precisamos configurar o registrador CMCON.
O registrador CMCON está no banco 0 de memória, conforme você pode
ver na figura 4 e, então, selecionamos este banco, escrevendo BANCO_0.
Conforme pode ser visto no datasheet do PIC16F628A, se os bits 2, 1 e 0 do
CMCON tiverem os valores 1, 0, e 1, respectivamente, os pinos RA0 e RA3 ficarão disponíveis
para serem usados como entrada ou saída e se os valores desses mesmos bits forem 1, 1 e 1, os
pinos RA0, RA1, RA2 e RA3 ficaram disponíveis.
No nosso caso qualquer das duas alternativas serve, pois, só iremos usar o
pino RA0.
Então, vamos escrever o valor B'00000111' no registador CMCON:
MOVLW B'00000111'

13
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

MOVWF CMCON
São apenas esses os Registradores de Uso Específico que precisamos
configurar nesse programa.

O próximo passo é inicializar as variáveis.


;***********************************************************************************************
; INICIALIZACAO DAS VARIAVEIS

MOVLW INI_DELAY_0 ;W = INI_DELAY_0


MOVWF DELAY_0 ;INICIALIZA DELAY_0
MOVLW INI_DELAY_1 ;W = INI_DELAY_1
MOVWF DELAY_1 ;INICIALIZA DELAY_1
MOVLW INI_DELAY_2 ;W = INI_DELAY_2
MOVWF DELAY_2 ;INICIALIZA DELAY_2

;***********************************************************************************************
Inicializar as variáveis é escrever nesses registradores os valores que eles
devem ter na inicialização do programa.
Nós criamos 3 variáveis: DELAY_0, DELAY_1 e DELAY_2.
A variável DELAY_0 deve ser iniciada com o valor decimal 255. A
DELAY_1 com o valor decimal 50 e a DELAY_3 com o valor decimal 13.
Nós criamos constantes para esses valores, que foram INI_DELAY_0, para
o valor 255, INI_DELAY_1, para o valor 50 e INI_DELAY_2, para o valor 13.
Por isso, quando escrevemos MOVLW INI_DELAY_0, o registrador W
assume o valor 255, ocorrendo o mesmo para os outros dois valores.

O próximo passo é a rotina principal do programa.


Nosso programa aguarda que passe meio segundo e testa o LED para ver se
está aceso ou apagado. Se estiver aceso, apaga e se estiver apagado, acende, voltando a aguardar
meio segundo para testar o LED novamente.
Há mais de uma forma de contar este tempo de meio segundo. Neste
programa vamos fazer isso decrementando os valores de variáveis. No próximo programa iremos
utilizar outra forma mais eficiente.
Nossa rotina irá decrementar a variável DELAY_0 até que ela chegue ao
valor 0.
Quando DELAY_0 chega ao valor 0, ela é reiniciada e a variável DELAY_1
é decrementada.
Quando DELAY_1 chega a 0, ela é reiniciada e a variável DELAY_2 é
decrementada.
Quando DELAY_2 chegar a 0 terá passado aproximadamente meio segundo.
A maioria das instruções no PIC16F628A são executadas em 1 ciclo de
instrução, sendo 1 ciclo de instrução igual a 4 ciclos do sinal de clock. Algumas instruções são
executadas em 2 ciclos de instrução, entre elas GOTO e CALL.
Neste projeto, estamos utilizando o oscilador interno numa frequência de 4
MHz e, portando, o ciclo do sinal de clock é de 250 nanosegundos (1/4.000.000). Sendo assim, o
ciclo de instrução é de 1 microssegundo.
Durante a simulação, iremos medir o tempo gasto até que DELAY_2 chegue
a 0, para então definirmos os valores definitivos das variáveis, pois, os valores que definimos são
uma estimativa.

14
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Vamos lá!
Primeiramente escrevemos a label PRINCIPAL.
Toda vez que quisermos que o programa volte a este ponto escreveremos:
GOTO PRINCIPAL. (goto em Inglês significa vá para).
Há certo preconceito a respeito da instrução GOTO. Algumas pessoas dizem
que se deve evitar usar essa instrução.
O que se deve evitar é usá-la desnecessariamente.
Sempre que for necessário usar essa instrução use-a sem medo. Ela foi feita
para ser usada!
A primeira instrução é DECFSZ DELAY_0,F
Essa instrução decrementa o valor de DELAY_0 e, após, verifica se o valor
ficou igual a 0.
Repare na letra F após o DELAY_0. Ela indica que o resultado será gravado
nele próprio, ou seja, supondo que o valor de DELAY_0 fosse 255, então, ficará igual a 254.
Se a letra fosse W, o resultado seria gravado no registrador W e o registrador
DELAY_0 teria ficado com o mesmo valor de antes, ou seja, W ficaria com o valor de 254 e
DELAY_0 com 255.
Se após decrementar DELAY_0, o seu valor for igual a 0, a próxima linha
do programa será pulada, se não, a próxima linha será executada.
Repare no nome desta instrução: DEC vem de decrementar, F, o registrador
que será decrementado, S, da palavra skip (neste caso, pular, em Inglês) e Z de zero. DECFSZ =
decrementa o registrador F e pula a próxima linha se o resultado for zero.
Se você procurar fazer essas associações entre o nome das instrução e sua
função, irá memorizá-las mais facilmente.
Voltando ao nosso programa, se o resultado da operação for diferente de
zero, a próxima linha do programa será executada.
O que precisamos que ocorra quando a variável DELAY_0 ainda não
chegou a 0?
Precisamos que ela continue a ser decrementada até que o seu valor seja
igual a 0.
Por isso, na próxima linha escrevemos: GOTO PRINCIPAL.
Isso faz com que a variável DELAY_0 seja decrementada novamente até
que seu valor chegue a 0, quando então, a linha após a instrução DECFSZ será pulada.
Neste momento em que DELAY_0 chega a zero, nós iremos reiniciá-la.
Fazemos isto, da mesma forma que fizemos para escrever o seu valor, na
parte de Inicialização das Variáveis:

MOVLW INI_DELAY_0
MOVWF DELAY_0

Após reinicializar DELAY_0, vamos decrementar DELAY_1 e testar se seu


valor chegou a 0:
DECFSZ DELAY_1,F

Se o seu valor não for igual a 0, o programa deverá voltar para decrementar
DELAY_0 e por isto, usamos a mesma instrução de antes:

GOTO PRINCIPAL

15
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Porém, se o valor de DELAY_1 chegou a 0, reiniciaremos DELAY_1 e


iremos decrementar DELAY_2 e testar se o seu valor chegou a 0:

MOVLW INI_DELAY_1
MOVWF DELAY_1
DECFSZ DELAY_2,F

Igualmente, se DELAY_2 não chegou a 0, o programa deverá voltar para


decrementar DELAY_0:

GOTO PRINCIPAL

Mas, se DELAY_2 chegou a 0, iremos reiniciá-la e terá terminado a


contagem do tempo:

MOVLW INI_DELAY_2
MOVWF DELAY_2

Parece complicado? Com a prática isso fica bem simples.


Recapitulando: DELAY_0 é decrementada até que seu valor chegue a 0,
quando então, DELAY_1 é decrementada. Quando DELAY_1 chega a 0, DELAY_2 é
decrementada. Quando DELAY_2 chega a 0, a contagem de tempo terminou.
Com os valores que escrevemos provisoriamente nestas variáveis,
DELAY_0 terá que zerar 50 vezes para que DELAY_1 seja zerada, enquanto que DELAY_1 terá
zerar 13 vezes para que DELAY_2 seja zerada.
Fazendo os cálculos, DELAY_0 será decrementada 165750 vezes para que a
contagem de tempo chegue ao fim.
Parece muito? Lembre-se de que o microcontrolador executa 1 instrução em
1 microssegundo.
Quando fizermos a simulação da execução do programa, iremos medir o
tempo que demorou para DELAY_2 zerar e então, faremos os ajustes nos valores das variáveis, para
que esse tempo seja de aproximadamente meio segundo.

A rotina principal até o momento está assim:

************************************************************************************************

PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

DECFSZ DELAY_0,F ;DECREMENTA DELAY_0. DELAY_0 = 0?


GOTO PRINCIPAL ;NÃO
MOVLW INI_DELAY_0 ;SIM, W = INI_DELAY_0
MOVWF DELAY_0 ;REINICIALIZA DELAY_0
DECFSZ DELAY_1,F ;DECREMENTA DELAY_1. DELAY_1 = 0?
GOTO PRINCIPAL ;NÃO
MOVLW INI_DELAY_1 ;SIM, W = INI_DELAY_1
MOVWF DELAY_1 ;REINICIALIZA DELAY_1
DECFSZ DELAY_2,F ;DECREMENTA DELAY_2. DELAY_2 = 0?
GOTO PRINCIPAL ;NÃO
MOVLW INI_DELAY_2 ;SIM, W = INI_DELAY_2
MOVWF DELAY_2 ;REINICIALIZA DELAY_2

16
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Agora que passou o tempo de meio segundo, devemos testar o LED para ver
se ele está aceso ou apagado.
O LED estará aceso se o pino RA0 estiver em nível alto e apagado se estiver
em nível baixo.
Para testar o estado deste pino, devemos verificar qual o valor do bit 0 do
registrador PORTA.
Para verificar o valor de um bit, existem 2 instruções: BTFSS E BTFSC.
BTFSS testa o bit e pula a próxima linha se o valor for 1. (BTFSS = testa o
bit do registrador F e pula se estiver setado).
BTFSC testa o bit e pula a próxima linha se o valor for 0. (BTFSC = testa o
bit do registrador F e pula se estiver limpo (clean)).
A escolha entre uma ou outra depende das particularidades do trecho do
programa onde serão usadas.
Neste nosso programa não faz diferença e, portanto, vamos escolher BTFSS:
BTFSS LED

Você se lembra de que nós definimos a label LED para o bit 0 do registrador
PORTA. Portanto, quando escrevemos esta instrução, é aquele bit que será testado.
Vamos supor que o valor do bit seja igual a 1 e, neste caso, a próxima linha
do programa será pulada.
Se o valor do bit 0 do PORTA é igual a 1, significa que o LED está aceso e,
então, devemos apagá-lo e para isso devemos fazer o valor do bit 0 do PORTA igual a 0.
Lembre-se de que para fazer o valor de um bit igual a 0, usamos a instrução
BCF.
BCF LED

Com isso o LED irá apagar.


Mas, se quando testamos o bit 0 do PORTA com a instrução BTFSS, o valor
do bit era igual a 0, então, a próxima linha do programa seria executada.
Sendo o valor do bit 0 do PORTA igual a 0, significa que o LED está
apagado e, então, precisamos acendê-lo.
Para isso usamos a instrução BSF para fazer o valor do bit 0 do PORTA
igual a 1:
BSF LED

Com isso acendemos o LED.


Então, este trecho do nosso programa ficou assim:

BTFSS LED ;testa o valor do bit 0 do PORTA


BSF LED ;valor = 0, acende o LED
BCF LED ;valor = 1, apaga o LED

Espere. Temos um problema aí:


No caso do valor do bit ser igual a 0, ele executa a instrução BSF LED e
em seguida executa a instrução BCF LED, ou seja, o LED é aceso e apagado em seguida. Como
faremos para resolver isso?

17
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Faremos assim:

BTFSS LED ;testa o valor do bit 0 do PORTA


GOTO ACENDE_LED ;valor = 0, desvia
BCF LED ;valor = 1, apaga o LED
GOTO PRINCIPAL ;desvia

ACENDE_LED
BSF LED ;ACENDE O LED
GOTO PRINCIPAL ;desvia

END ;Fim do programa

Desta forma, quando o valor do bit for igual a 0, o programa será desviado
para onde está escrito ACENDE_LED, executando a instrução BSF LED.
Repare que depois de acender ou de apagar o LED ele desvia para o começo
da rotina principal, onde, começará novamente a decrementar as rotinas.
Com isso chegamos ao final do nosso programa.
Devemos indicar o fim do programa ao MPLAB através da diretiva END.
Lembre-se de que nós ativamos o WDT nos bits de configuração.
O WDT é um circuito que reinicia o microcontrolador caso o programa
trave.
Ele é um contador que é incrementado continuamente e quando atinge o
valor máximo, provoca o reset do microcontrolador.
Em algum ponto do nosso programa deveremos escrever a instrução
CLRWDT, que reinicia o contador do WDT toda vez que é executada.

Caso o programa trave, esta instrução não será executada, provocando o


reset do microcontrolador.
É assim que o WDT funciona.
Vamos escrevê-la no começo da rotina principal, finalizando o programa:

;***********************************************************************************************
; PROGRAMA: PISCA LED
; VERSÃO 1.0
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /
;***********************************************************************************************

#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A

;***********************************************************************************************
; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF
& _LVP_OFF & _CP_OFF & DATA_CP_OFF

;**********************************************************************************************
; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA
;**********************************************************************************************

18
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

; VARIAVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

DELAY_0 ;USADO PARA GERAR O TEMPO DE 0,5 SEGUNDO


DELAY_1 ;USADO PARA GERAR O TEMPO DE 0,5 SEGUNDO
DELAY_2 ;USADO PARA GERAR O TEMPO DE 0,5 SEGUNDO

ENDC ;FIM DO BLOCO DE MEMORIA

;**********************************************************************************************
; CONSTANTES

INI_DELAY_0 EQU .255 ;VALOR QUE DELAY_0 INICIA


INI_DELAY_1 EQU .50 ;VALOR QUE DELAY_1 INICIA
INI_DELAY_2 EQU .13 ;VALOR QUE DELAY_2 INICIA

;***********************************************************************************************
; SAÍDA

#DEFINE LED PORTA,0 ;LED LIGADO EM RA0

;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO


GOTO INICIO ;DESVIA PARA INICIO

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES


RETFIE ;RETORNA

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11111110' ;W = B'11111110'
MOVWF TRISA ;CONFIGURA RA0 COMO SAÍDA E DEMAIS COMO ENTRADAS
MOVLW B'11111111'
MOVWF TRISB ;TODOS OS PINOS DO PORTB COMO ENTRADAS
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O

;***********************************************************************************************

19
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

;**********************************************************************************************
; INICIALIZACAO DAS VARIAVEIS

MOVLW INI_DELAY_0 ;W = INI_DELAY_0


MOVWF DELAY_0 ;INICIALIZA DELAY_0
MOVLW INI_DELAY_1 ;W = INI_DELAY_1
MOVWF DELAY_1 ;INICIALIZA DELAY_1
MOVLW INI_DELAY_2 ;W = INI_DELAY_2
MOVWF DELAY_2 ;INICIALIZA DELAY_2

;***********************************************************************************************

PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


DECFSZ DELAY_0,F ;DECREMENTA DELAY_0. DELAY_0 = 0?
GOTO PRINCIPAL ;NÃO
MOVLW INI_DELAY_0 ;SIM, W = INI_DELAY_0
MOVWF DELAY_0 ;REINICIALIZA DELAY_0
DECFSZ DELAY_1,F ;DECREMENTA DELAY_1. DELAY_1 = 0?
GOTO PRINCIPAL ;NÃO
MOVLW INI_DELAY_1 ;SIM, W = INI_DELAY_1
MOVWF DELAY_1 ;REINICIALIZA DELAY_1
DECFSZ DELAY_2,F ;DECREMENTA DELAY_2. DELAY_2 = 0?
GOTO PRINCIPAL ;NÃO
MOVLW INI_DELAY_2 ;SIM, W = INI_DELAY_2
MOVWF DELAY_2 ;REINICIALIZA DELAY_2
BTFSS LED ;TESTA O VALOR DO BIT 0 DO PORTA
GOTO ACENDE_LED ;VALOR = 0, DESVIA
BCF LED ;VALOR = 1, APAGA O LED
GOTO PRINCIPAL ;DESVIA
ACENDE_LED
BSF LED ;ACENDE O LED
GOTO PRINCIPAL ;DESVIA

;**********************************************************************************************
END ;FIM DO PROGRAMA

;**********************************************************************************************

Nosso próximo passo é simular a execução do programa para ver se ele se


comporta como esperamos.
No menu “Project”, clique em “Project Wizard”.
Na janela que se abre clique em “Avançar”:

20
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 5

Na janela seguinte (“Step One”), selecione o PIC16F628A e clique em


“Avançar”:

Figura 6

21
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Na próxima janela (“Step Two”), clique em “Avançar”:

Figura 7

Na próxima janela (“Step Three”), clique em “Browse”:

Figura 8

Na janela que se abre, escolha um local e dê um nome para o projeto, por


exemplo Pisca LED e clique em “Salvar”:

22
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 9

Em seguida clique em “Avançar”

Figura 10

23
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Na janela seguinte (“Step Four”), selecione o arquivo Pisca LED.asm,


clique em “Add” e em seguida clique em “Avançar”:

Figura 11

Na janela seguinte (“Summary”), clique em concluir:

Figura 12

24
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

No menu “Project”, clique em “Build All”. Na janela que se abre, clique em


“Absolute”:

Figura 13

A seguir, expanda a janela chamada “Output”:

25
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 14
A mensagem “BUILD SUCCEEDED”, confirma que não ocorreu nenhum
erro na compilação.
Com isto, nós já temos disponível o arquivo Pisca LED.hex para ser gravado
no microcontrolador, criado na mesma pasta onde está o arquivo Pisca LED.asm., mas, antes vamos
simular a execução do programa.
A mensagem “Message[302] E:\PISCA LED.ASM 56 : Register in operand
not in bank 0. “Ensure that bank bits are correct” é um aviso de que o registrador objeto da
instrução presente naquela linha do programa (linha 56), não está no banco 0, afim de que nos
certifiquemos de ter setado corretamente o banco. É uma mensagem que aparece mesmo que o
banco tenha sido selecionado corretamente.
Abra o arquivo Pisca LED.asm, clicando no menu “File” em “Open”.
Clique no menu “Edit”, depois em “Properties” e depois na aba “ASM File
Types” e selecione “Line Numbers”.
Aparecerão os números das linhas à esquerda.
Vá à linha 56 e veja que o registrador em questão é o TRISA, que está no
banco 1. Repare que nós selecionamos esse banco antes e, por isso, não precisamos nos preocupar.
O mesmo ocorre para a mensagem da linha 58.
Agora vamos à simulação:
Clique no menu “Debugger” e depois, em “Select Tool”, selecione “MPLAB
SIM”.
Clique novamente no menu “Debugger” e depois em “Settings”
Na aba “Osc/Trace”, em “Processor Frequency” digite 4 e selecione Mhz.
Na aba “Animation / Real Time Updates”, selecione “Enable Real Time
Watch Updates” e leve o cursor todo para a esquerda “Fastest”.
Clique em “OK”.
No menu “View”, clique em “Watch”.

26
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Na janela “Watch”, clique onde está indicado na figura abaixo, selecione o


PORTA e clique em “Add SFR”:

Figura 15

Depois, clique onde está indicado na figura abaixo e selecione DELAY_0 e


clique em “Add Symbol”.
Faça o mesmo para DELAY_1 e DELAY_2.

Figura 16

27
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Em “Add SFR” adicionamos os Registradores de Uso Específico que


queremos visualizar, enquanto em “Add Symbol” adicionamos as variáveis por nós criadas que
queremos visualizar.
No menu “Window”, clique em “Pisca LED.asm”:

Figura 17

Na imagem abaixo, a seta está apontando para a barra de botões do


simulador.
Aponte o mouse para cada botão para ver seus nomes. São eles: “Run”,
“Halt”, “Animate”, “Step Into”, “Step Over”, “Reset” e “Breakpoints”:

28
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 18
Clique no botão “Reset”.
O cursor vai para a linha 46 onde está a instrução GOTO INICIO.
Esta é a posição 0X00 da memória de programa, e, portanto é a instrução
contida nesta posição que o microcontrolador irá executar em primeiro lugar, quando for ligado ou
resetado.
Repare que apareceu uma seta verde do lado esquerdo:

Figura 19

29
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Esta seta indica qual instrução está para ser executada.


Clique no botão “Step Into”.
Este botão executa uma instrução a cada vez que é pressionado.
A instrução GOTO INICIO foi executada e, portanto, o programa foi
desviado para a linha após a label INICIO.
Clique novamente em “Step Into”.
Agora, a instrução representada pela label BANCO_1, ou seja, BSF
STATUS, RP0 foi executada e o banco 1 foi selecionado.
Repare na parte de baixo da janela, que o banco selecionado é o 1:

Figura 20

Outras informações podem ser vistas nessa barra, como o modelo do


microcontrolador, o valor do contador de programa (0X2), o valor do registrador W, dos bits z, dc e
c do registrador STATUS, etc...
O contador de programa – PC, armazena o endereço na memória de
programa onde está a instrução que será executado pelo microcontrolador.
Veremos o significado dos valores dos bits z, dc e c do registrador STATUS
em outra parte deste tutorial.
Continue clicando no botão “Step Into” e acompanhando a simulação da
execução do programa.
Após ser executada a instrução MOVWF DELAY_0, no menu “Window”
escolha a janela “Watch” e repare que a variável DELAY_0 assumiu o valor 255.
Volte para a janela do programa, escolhendo-a no menu “Window”,
continue clicando em “Step Into” e depois visualize na janela “Watch” que DELAY_1 e DELAY_2,
assumem os valores 50 e 13 respectivamente.
Continue clicando no botão “Step Into” e veja que após a instrução
DECFSZ DELAY_0 ser executada, o valor de DELAY_0 passa a ser 254 e que como o seu valor
é diferente de 0, o programa volta para a linha após a label PRINCIPAL, pois, executa a instrução
GOTO PRINCIPAL.

30
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Se continuar clicando em “Step Into”, você poderá acompanhar a variável


DELAY_0 sendo decrementada.
Ela irá ser decrementada até chegar ao valor 0, quando, então, o programa
irá pular a linha com a instrução GOTO PRINCIPAL.
Para agilizar, na janela “Watch”, dê um duplo clique no valor da variável
DELAY_0, e altere o seu valor para 1.
Depois, volte a clicar em “Step Into” e repare que, como DELAY_0 chegou
ao valor 0, a linha com a instrução GOTO PRINCIPAL é pulada, sendo executadas as instruções
que reiniciam DELAY_0 e depois a que decrementa DELAY_1.

DELAY_1, agora vale 49 e como é diferente de 0, o programa é desviado


para onde está a label PRINCIPAL.
A partir de agora, DELAY_0 voltará a ser decrementada até chegar a 0 de
novo.
Vamos mudar os valores de DELAY_0 e de DELAY_1 para 1 e continuar
clicando em “Step Into”.
Veremos que DELAY_1 chega a 0, é reiniciada e DELAY_2 é decrementada.
Agora vamos mudar o valor das três para 1.
Clicando em “Step Into” veremos que agora DELAY_2 chega a 0, é
reiniciada e o programa irá executar a instrução BTFSS LED para testar o valor do bit 0 do PORTA,
o que é o mesmo que verificar se o LED está aceso ou apagado.
Neste teste ele constata que o valor do bit é igual a 0 e o programa, então, é
desviado para a instrução após a label ACENDE_LED, onde é executada a instrução BSF LED.
Em seguida ele volta para PRINCIPAL.
Vá para a janela “Watch” e veja que o bit 0 do PORTA foi setado, isto é, seu
valor é igual a 1, acendendo o LED.
Agora, o programa voltará a decrementar as variáveis.
Vamos agilizar, alterando o valor das três variáveis para 1.
Desta vez no teste do bit ele verifica que o valor é 1 e executa a instrução
BCF LED, fazendo o valor do bit igual a 0, apagando o LED.
Verifique na janela “Watch” que o bit 0 do PORTA foi apagado (= 0).
Agora vamos medir se o tempo que demora para a variável DELAY_2
chegar a 0 é de aproximadamente 500 milissegundos.
Dê um duplo clique na linha que contêm a instrução BTFSS LED.
Você verá que aparece uma letra B dentro de um círculo vermelho,
conforme a figura abaixo:

31
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 21

Você acaba de inserir um Breakpoint. O programa será interrompido toda


vez que encontrar um Breakpoint ativo.
Clique no botão “Reset” da barra de ferramentas do simulador e depois vá
clicando em “Step Into” até chegar à linha onde está a instrução CLRWDT.
No menu “Debugger”, clique em “StopWatch”.
Eis a janela do “StopWatch”:

32
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 22

Nesta janela, clique no botão “Zero”.


Volte para a janela do programa, selecionando-a no menu “Window” e
clique no botão “Run” da barra de ferramentas do simulador.
O programa será executado até a linha onde está o Breakpoint, ou seja, na
linha onde está a instrução BTFSS.
Volte para a janela do “StopWatch”.
Veja no campo “Time” que se passaram 665 milissegundos desde que o
“StopWatch” foi zerado (quando clicamos em “Zero”):

33
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 23

Ou seja, está demorando 665 milissegundos para que a variável DELAY_2


chegue a 0, mas, nós queremos que demore 500 milissegundos.
Vamos medir de quanto em quanto tempo a variável DELAY_2 é
decrementada.
Para isto, vamos inserir outro Breakpoint na linha onde está a instrução
DECFSZ DELAY_2.
Após inserir o Breakpoint, clique no botão “Reset” do simulador e vá
clicando no botão “Step Into” até chegar à instrução CLRWDT.
Vá para a janela do “StopWatch” e clique em “Zero”.
Volte para a janela do programa e clique em “Run”.
Quando o programa parar na linha onde está o Breakpoint vá para a janela
do “StopWatch”.
Repare que passaram 51 milissegundos, ou seja, a variável DELAY_2 é
decrementada a cada 51 milissegundos.

34
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 24

Estamos com um excesso de 125 milissegundos no tempo total.


Se nós diminuirmos o valor de inicialização da variável DELAY_2 em 3
unidades, ou seja, diminuirmos para 10, o tempo total deverá cair para cerca de 512 milissegundos.
Vamos verificar.
Vá para a janela do programa e na parte “CONSTANTES”, altere a linha
INI_DELAY_2 EQU .13 para INI_DELAY_1 EQU .10
Como alteramos o programa, precisamos compilar de novo e para isso, no
menu “Project” clique em “Build All”.
Retire o Breakpoint da linha onde está a instrução DECFSZ DELAY_2,
dando um duplo clique nessa linha, e deixe o outro que está na linha com a instrução BTFSS LED.
Clique no botão “Reset” do simulador e vá clicando em “Step Into” até
chegar à linha com a instrução CLRWDT. Neste momento, vá para a janela “StopWatch” e clique
em “Zero”.
Volte para a janela do programa e clique no botão “Run” do simulador.
Quando o programa parar no Breakpoint, abra a janela “StopWatch”.
Repare que realmente o tempo total caiu para 512 milissegundos.

35
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 25

Ainda temos um excesso de 12 milissegundos.


Vamos experimentar diminuir o valor de inicialização de DELAY_1 para 49,
da mesma forma que mudamos o valor de DELAY_2.
Lembre-se de que temos que compilar de novo, clicando no menu “Project”
e em “Build All”.
Agora o tempo total é bem próximo de 500 milissegundos:

Figura 26

36
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Vamos experimentar diminuir o valor da variável DELAY_0 para 254.


Agora, o tempo é de praticamente 500 milissegundos.

Figura 27

Ficamos, então com estes valores para as variáveis: DELAY_0 = 254,


DELAY_1 = 49 e DELAY_2 = 10.
Nosso programa está como queríamos e agora é hora de gravá-lo no
microcontrolador.
Você poderá comprar um gravador ou montar o seu próprio gravador.
Há vários modelos à venda e também vários esquemas de gravadores na
Internet para quem quiser montar o seu.
Existem gravadores que são conectados na porta paralela, outros na porta
serial e também os que são conectados na porta USB.
Os melhores são os USB, pela praticidade.
Alguns gravadores funcionam com o MPLAB, enquanto outros necessitam
de outro software.
A figura a seguir é de um esquema de gravador para ser conectado na porta
serial do computador (conector DB9).

37
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 28
Para utilizá-lo é necessário o programa IC-prog:
http://www.ic-prog.com/icprog106B.zip
Também é necessário o driver para Windows XP:
http://www.ic-prog.com/icprog_driver.zip.

Até hoje apenas utilizei este programa no Windows XP, e por isso, não
posso garantir que o mesmo funcione em versões posteriores do Windows.
Descompacte os arquivos do programa e do driver numa mesma pasta.
Na primeira vez que o IC-prog é executado ele apresenta a janela mostrada
na figura a seguir. Clique em OK.

38
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 29

Na próxima janela também clique em OK, deixando como está, pois, este
gravador é baseado no JDM.

Figura 30

Se for exibida a mensagem vista na figura a seguir ou outras de mesmo teor


clique em OK.

39
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 31

A janela do IC-prog é vista na figura a seguir.

Figura 32

No menu “Settings”, clique em “Options”.


Na aba “Language” escolha “Portuguese”.
No menu “Configuração” clique em “Opções”.
Na aba “Diversos”, marque “Activar Driver NT/2000/XP”

40
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Na janela que abre clique em “Yes” para reiniciar o IC-prog.

Figura 33

Na janela que se abre, perguntando se deseja instalar o driver, clique em


“Yes”.

Figura 34

No menu “Configuração”, clique em “Opções” e na aba “Diversos”, em


“Processo Prioritário”, selecione “Alto” e clique em “OK”.

41
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

No menu “Configuração”, aponte para “Dispositivo” e depois para


“Microchip PIC” e escolha o PIC16F628A.
No menu “Arquivo”, clique em “Abrir”.
Localize e selecione o arquivo Pisca LED.hex e clique em “Abrir”.

Figura 35

Repare do lado direito da janela, que o tipo de oscilador e os “Fusíveis” já


estão configurados, pois nós os configuramos no programa, com a diretiva CONFIG.
Certifique-se de que o gravador está conectado na porta serial do
computador.
No menu “Comando”, clique em “Programar Tudo” e na janela de
confirmação, clique em “Yes”.
Será gravado o programa no microcontrolador e depois o programa gravado
será lido e comparado com o arquivo. Se estiverem iguais, será apresentada a mensagem
“Dispositivo verificado com sucesso”, conforme figura a seguir.

42
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED

Figura 36

Agora podemos montar o circuito da figura 2 e constatar o seu


funcionamento.
Aqui termina a primeira parte deste tutorial. Espero que você tenha gostado.
Na próxima parte, vamos criar um programa para a mesma finalidade,
porém, utilizando o TIMER 0 para obter o intervalo de meio segundo entre as piscadas do LED.

43
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II

Parte 2

Pisca LED II
Nesta 2ª parte iremos montar o mesmo circuito da Parte 1, mas utilizaremos
o Timer 0 do PIC16F628A para obter a frequência de cerca de 1 Hz para o LED.
O Timer 0 é um circuito do microcontrolador que incrementa um registrador
chamado TMR0, ou seja, é um circuito que faz com que o valor desse registrador vá aumentando de
1 em 1.
O registrador TMR0 é de 8 bits (como todos os registradores do
PIC16F628A) e, portanto, seu valor pode variar de 0 a 255. O seu valor pode ser lido e também
alterado, ou seja, podemos escrever o valor que quisermos nele (de 0 a 255).
O Timer 0 pode ser configurado para incrementar o registrador TMR0 a
partir do ciclo de instrução ou a partir do ciclo de um sinal externo aplicado no pino T0CKI (pino 3).
Quando ele é incrementado pelo ciclo de instrução, diz-se que ele está sendo
usado como timer e quando é incrementado por um sinal aplicado no pino T0CKI, diz-se que ele
está sendo usado como contador (pois pode ser usado para contar os ciclos do sinal externo).
No nosso caso vamos configurá-lo para que seja incrementado a partir do
ciclo de instrução, pois, desejamos obter um determinado intervalo de tempo, conhecendo o tempo
de duração do ciclo de instrução.
Podemos configurar o Timer 0 para que o registrador TMR0 seja
incrementado a cada ciclo ou para que seja incrementado a cada 2, 4, 8, 16, 32, 64, 128 e 256 ciclos.
Isso é o que se chama de Prescaler. Quando ele estiver configurado para incrementar a cada ciclo,
dizemos que o valor do prescaler é 1:1, quando for incrementado a cada 2 ciclos, 1:2, e assim por
diante.
Quando o registrador TMR0 estiver com o valor 255, o próximo incremento
fará seu valor voltar a 0 e, então, dizemos que ele “estourou”.
Quando o TMR0 estoura, o bit T0IF do registrador INTCON é setado, ou
seja, o valor desse bit passa a ser igual a 1, sendo que ele precisa ser apagado na rotina do programa
para que se detecte nova mudança de seu estado. Ao mesmo tempo uma interrupção é provocada, se
estiver habilitada.
A vantagem de se usar o Timer 0 para obter o tempo que desejamos é que o
programa fica livre para executar outras funções, bastando monitorar o estado do bit T0IF para ver
se o tempo que desejamos já passou. Outra opção é habilitar a interrupção de estouro do Timer 0.
O intervalo de tempo que precisamos é de 500 milissegundos.
Para uma frequência do oscilação de 4 MHz, o ciclo de instrução é de 1
microssegundo, como já vimos na parte 1 deste tutorial.
Dividindo 500 milissegundos por 1 microssegundo, obtemos o valor de
500.000, ou seja, a cada 500.000 ciclos de instrução terão se passado 500 milissegundos.
Se configurarmos o prescaler do Timer 0 para 1:1, ou seja, se o registrador
TMR0 for incrementado a cada ciclo de instrução, ele irá estourar a cada 256 microssegundos.
Como este tempo é muito menor do que o que estamos querendo, vamos
configurar o prescaler para o seu valor máximo, isto é, para 1:256.

Dessa forma, o TMR0 será incrementado a cada 256 ciclos de instrução, ou


seja, a cada 256 microssegundos.
Assim, o TMR0 irá estourar a cada 256 x 256 microssegundos, isto é, a cada
65.536 microssegundos, o que equivale a 65,536 milissegundos.

44
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II

Ou seja, mesmo com o prescaler no máximo, serão necessários mais de 1


estouros do TMR0 para obtermos o tempo que desejamos e, portanto, teremos que contar esses
estouros.
Porém, a divisão de 500 milissegundos por 65,536 milissegundos não
resulta em um número exato.
Temos que, de alguma forma, obter um número exato de estouros que
correspondam ao tempo de 500 milissegundos.
Como vimos, 500 milissegundos correspondem a 500.000 ciclos de
instrução.
Dividindo 500.000 pelos valores de prescaler disponíveis, constatamos que
o maior valor do prescaler que resulta numa divisão exata é 1:32 e este valor é 15.625.
Por sua vez, 15.625 é igual ao produto de 125 por 125.
Com o valor do prescaler definido em 1:32, o TMR0 será incrementado a
cada 32 ciclos de instrução, ou seja, a cada 32 microssegundos.
Se após todas as vezes que o TMR0 estourar, nós o reiniciarmos com o valor
de 131 (escrevendo este valor nele), após 125 incrementos (256 – 131) ele irá estourar, ou seja, irá
estourar a cada 32 x 125 microssegundos = 4.000 microssegundos = 4 milissegundos.
Se contarmos 125 estouros do TMR0, teremos o tempo de 500
milissegundos.
Vamos ao programa!
O fluxograma é o mesmo e o programa é igual até este ponto:

;***********************************************************************************************
; PROGRAMA: PISCA LED II
; VERSÃO 1.0
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /
;***********************************************************************************************

#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A

;***********************************************************************************************
; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF
& _LVP_OFF & _CP_OFF & DATA_CP_OFF

;**********************************************************************************************
; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA

;**********************************************************************************************
Para não ter de escrever tudo de novo, no MPLAB, abra o arquivo Pisca
LED.asm e, no menu “File”, clique em “Save As...” e mude o nome do arquivo para Pisca LED
II.asm e vá fazendo as alterações.
O próximo passo do programa é a definição das variáveis.
Iremos utilizar apenas uma variável, que será usada para contar os estouros
do TMR0.
Vamos nomeá-la de CONT_EST_TMR0:

;**********************************************************************************************
; VARIÁVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

45
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II

CONT_EST_TMR0 ;USADO PARA CONTAR OS ESTOUROS DO TMR0

ENDC ;FIM DO BLOCO DE MEMORIA

;**********************************************************************************************

O próximo passo é a definição das constantes.


Iremos utilizar duas constantes, uma no valor de 131 que será usada para
reinicializar o TMR0 e outra no valor de 125 para a variável que irá contar os estouros do TMR0.
Vamos chamá-las de INI_TMR0 e INI_CONT_EST_TMR0.
A partir daí, o programa é igual até a configuração dos registradores:

;**********************************************************************************************
; CONSTANTES

INI_TMR0 EQU .131 ;VALOR QUE TMR0 INICIA


INI_CONT_EST_TMR0 EQU .125 ;VALOR QUE CONT_EST_TMR0 INICIA

;***********************************************************************************************
; SAÍDA

#DEFINE LED PORTA,0 ;LED LIGADO EM RA0

;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO


GOTO INICIO ;DESVIA PARA INICIO

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES


RETFIE ;RETORNA

;***********************************************************************************************

Como iremos utilizar o Timer 0, precisamos configurar o registrador


OPTION_REG, que está associado a ele.
Esse registrador encontra-se no banco 1 de memória.
Conforme consta no datasheet do PIC16F628A, o bit 5 desse registrador, é o
que define se o TMR0 será incrementado pelo ciclo de instrução ou a partir de um sinal externo.
Para que ele seja incrementado pelo ciclo de instrução, o valor desse bit deve ser igual a 0.

O bit 3 define se o prescaler será usado pelo Timer 0. Para que o Timer 0
use o prescaler, o valor desse bit deve ser igual a 0.
Os bits 2, 1 e 0 definem o valor do prescaler. No nosso caso, iremos utilizar
o valor 1:32, e, portanto, os valores desses bits deverão ser 1, 0 e 0, respectivamente.
Os demais bits não nos interessam e, portanto, vamos deixá-los com o valor
1, valor com o qual eles são inicializados.
Dessa forma, iremos escrever o seguinte número binário no registrador
OPTION_REG: 11010100.
As configurações dos outros registradores são as mesmas.
A seguir, inicializamos a variável e teremos chegado à rotina principal:

46
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11010100' ;W = B'11010100'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO, COM PRESCALER DE 1:32
MOVLW B'11111110' ;W = B'11111110'
MOVWF TRISA ;CONFIGURA RA0 COMO SAÍDA E DEMAIS COMO ENTRADAS
MOVLW B'11111111'
MOVWF TRISB ;TODOS OS PINOS DO PORTB COMO ENTRADAS
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O

;***********************************************************************************************
; INICIALIZACAO DA VARIAVEL

MOVLW INI_CONT_EST_TMR0 ;W = INI_CONT_EST_TMR0


MOVWF CONT_EST_TMR0 ;INICIALIZA CONT_EST_TMR0

;***********************************************************************************************

PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

A primeira instrução da rotina principal é CLRWDT para limpar o WDT.


A seguir, iremos testar o bit T0IF do registrador INTCON para verificarmos
se o TMR0 estourou:
BTFSS INTCON,TOIF ;TMR0 ESTOUROU?

Se o TMR0 não houver estourado, o valor desse bit será igual a 0 e a


próxima linha do programa será executada. Portanto, nessa linha escreveremos a instrução GOTO
PRINCIPAL, para que o bit seja testado novamente.

GOTO PRINCIPAL ;NAO

Se o TMR0 houver estourado, o valor do bit será igual a 1 e a linha com a


instrução GOTO PRINCIPAL será pulada.
Nesse caso, a primeira providência que iremos tomar é zerar o bit TOIF para
que na próxima vez que o TMR0 estourar possamos detectar a mudança do seu valor para 1:
BCF INTCON,T0IF ;SIM

A seguir, iremos reiniciar o TMR0 com o valor 131, usando a constante que
criamos:
MOVLW INI_TMR0 ;W = INI_TMR0
MOVWF TMR0 ;REINICIA TMR0

Em seguida, decrementamos o valor da variável e ao mesmo tempo


verificamos se o seu valor chegou a 0:

DECFSZ CONT_EST_TMR0,F ;DECREMENTA CONT_EST_TMR0. CONT_EST_TMR0 = 0?

Se o seu valor não for igual a 0, a próxima linha será executada e, nesse caso,
desviaremos o programa para o começo da rotina principal:

GOTO PRINCIPAL ;NAO

47
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II

Se o valor da variável houver chegado a 0, iremos reiniciá-la e, nesse caso,


terão se passado cerca de 500 milissegundos.
A partir daqui, o resto da rotina é igual ao do programa da parte I deste
tutorial.
Portanto, o programa ficou assim:

;***********************************************************************************************
; PROGRAMA: PISCA LED II
; VERSÃO 1.0
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /
;***********************************************************************************************

#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A

;***********************************************************************************************
; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF
& _LVP_OFF & _CP_OFF & DATA_CP_OFF

;**********************************************************************************************
; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA

;**********************************************************************************************
; VARIÁVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

CONT_EST_TMR0 ;USADO PARA CONTAR OS ESTOUROS DO TMR0

ENDC ;FIM DO BLOCO DE MEMORIA

;**********************************************************************************************
; CONSTANTES

INI_TMR0 EQU .131 ;VALOR QUE TMR0 INICIA


INI_CONT_EST_TMR0 EQU .125 ;VALOR QUE CONT_EST_TMR0 INICIA

;***********************************************************************************************
; SAÍDA

#DEFINE LED PORTA,0 ;LED LIGADO EM RA0

;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO


GOTO INICIO ;DESVIA PARA INICIO

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES


RETFIE ;RETORNA

48
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11010100' ;W = B'11010100'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO , COM PRESCALER DE 1:32
MOVLW B'11111110' ;W = B'11111110'
MOVWF TRISA ;CONFIGURA RA0 COMO SAÍDA E DEMAIS COMO ENTRADAS
MOVLW B'11111111'
MOVWF TRISB ;TODOS OS PINOS DO PORTB COMO ENTRADAS
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O

;***********************************************************************************************
; INICIALIZACAO DA VARIAVEL

MOVLW INI_CONT_EST_TMR0 ;W = INI_CONT_EST_TMR0


MOVWF CONT_EST_TMR0 ;INICIALIZA CONT_EST_TMR0

;***********************************************************************************************
PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


BTFSS INTCON,T0IF ;TMR0 ESTOUROU?
GOTO PRINCIPAL ;NAO
BCF INTCON,T0IF ;SIM
MOVLW INI_TMR0 ;W = INI_TMR0
MOVWF TMR0 ;REINICIA TMR0
DECFSZ CONT_EST_TMR0,F ;DECREMENTA CONT_EST_TMR0. CONT_EST_TMR0 = 0?
GOTO PRINCIPAL ;NAO
MOVLW INI_CONT_EST_TMR0 ;SIM, W = INI_CONT_EST_TMR0
MOVWF CONT_EST_TMR0 ;REINICIALIZA CONT_EST_TMR0
BTFSS LED ;TESTA O VALOR DO BIT 0 DO PORTA
GOTO ACENDE_LED ;VALOR = 0, DESVIA
BCF LED ;VALOR = 1, APAGA O LED
GOTO PRINCIPAL ;DESVIA
ACENDE_LED
BSF LED ;ACENDE O LED
GOTO PRINCIPAL ;DESVIA

;**********************************************************************************************
END ;FIM DO PROGRAMA

;**********************************************************************************************
Salve o arquivo.
A seguir iremos simular a execução do programa com o MPLAB SIM.
No menu “Project”, clique em “Open”. Localize o projeto de nome Pisca
LED, selecione-o e clique em “Abrir”.
No menu “Project”, em “Remove Files to Project”, clique no arquivo Pisca
LED.asm (o da parte I) para removê-lo.
No menu “Project”, clique em “Add Files to Project...”, localize o arquivo
Pisca LED II.asm (o novo), selecione-o e clique em “Abrir”.
No menu “Project”, clique em “Buid All”.
Verifique se a montagem foi feita com sucesso, selecionando a janela
“Output” no menu “Window”:

49
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II

Figura 1

Selecione a janela com o programa Pisca LED II.asm, no menu “Window” e


execute o programa até a linha que contém a instrução CLRWDT, clicando no botão “Step Into” da
barra do simulador.
Insira um Breakpoint na linha que contém a instrução BTFSS LED, dando
um duplo clique nessa linha:

Figura 2

A seguir, selecione a janela “Stop Watch” no menu “Window” e clique em


“Zero”.
Volte para a janela do programa e clique no botão “Run” do simulador. O
programa irá parar no Breakpoint.

50
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II

Vá para a janela do “Stop Watch” e veja que demorou 505 milissegundos


para o programa chegar neste ponto, ou seja, a cada 505 milissegundos o LED será testado e
mudará de estado.

Figura 3
O erro de 5 milissegundos é devido ao tempo gasto com as instruções que reiniciam o TMR0 com o
valor 131 toda vez que ele estoura.
Experimente mudar este valor para 132 (na constante), monte novamente o
projeto e volte a fazer a simulação e medir o tempo.
Você verá que o tempo agora é de 497 milissegundos:

Figura 4

Como a diferença é menor, ficaremos com esse valor.

51
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II

Este projeto não exige um tempo exato, pois, não se trata, por exemplo, de
um relógio.
Programas de relógio usam cristais cujo valor da frequência é um múltiplo
de 2 e desta forma, não é preciso reiniciar o Timer, deixando que ele rode de 0 a 255.
Em outra parte deste tutorial voltaremos a falar sobre esse assunto.
Grave o programa no microcontrolador e constate o seu funcionamento!
Aqui termina a 2ª parte deste tutorial.
Na próxima parte, vamos continuar com este circuito, mas utilizaremos o
recurso da interrupção provocada pelo estouro do Timer 0.

52
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

Parte 3
Pisca LED III

Nesta terceira parte, continuaremos com o mesmo circuito utilizado até


agora.
O Timer 0 continuará sendo usado para obtermos o tempo de cerca de 500
milissegundos entre as mudanças de estado do LED.
A diferença é que iremos habilitar a interrupção do Timer 0 para que não
seja necessário ficar testando o bit T0IF para sabermos quando o registrador TMR0 estourou.
O recurso de interrupção permite que o microcontrolador possa executar
outras tarefas, sendo interrompido quando ocorre o evento esperado, quando então, os
procedimentos relativos a esse evento são tomados.
Quando ocorre uma interrupção, o microcontrolador salva o endereço da
próxima instrução que seria executada e desvia para o endereço 0X04 da memória de programa,
executando a instrução contida nesse endereço.
Quando o microcontrolador encontra uma instrução chamada RETFIE, ele
recupera o endereço da instrução que seria executada antes de ocorrer a interrupção, voltando a
executar o programa do ponto onde havia sido interrompido.
A interrupção de cada periférico do microcontrolador, como o Timer 0, pode
ser habilitada ou desabilitada individualmente através de um bit específico.
Além disso, também existe um bit (PEIE - bit 6 do registrador INTCON),
que permite habilitar e desabilitar as interrupções de todos os periféricos ao mesmo tempo e outro
bit (GIE – bit 7 do mesmo registrador) que permite habilitar e desabilitar todas as interrupções do
microcontrolador ao mesmo tempo. Isso porque, além das interrupções dos periféricos existem as
interrupções por mudança de estado em determinados pinos do PORTB.
Conforme consta no datasheet do PIC16F628A, para habilitar a interrupção
do Timer 0, precisamos “setar” o bit 5 (T0IE) do registrador INTCON, ou seja, fazer com que o
valor deste bit seja igual a 1.
Como o Timer 0 é um periférico, também precisamos setar o bit PEIE (bit 6
do INTCON) para habilitar as interrupções de periféricos.
Finalmente, setamos o bit GIE (bit 7 do INTCON) para habilitar as
interrupções de forma geral.
Vamos ao programa!
No MPLAB, abra o arquivo Pisca LED II.asm, salve-o com o nome Pisca
LED III.asm e faça as alterações, para não ter de digitar tudo novamente.
O programa é igual ao da Parte 2 até este ponto:

;***********************************************************************************************
; PROGRAMA: PISCA LED III
; VERSÃO 1.0
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /
;***********************************************************************************************

#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A

53
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

;***********************************************************************************************
; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF
& _LVP_OFF & _CP_OFF & DATA_CP_OFF

;**********************************************************************************************
; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA

;**********************************************************************************************

O próximo passo é a definição das variáveis.


Quando estamos trabalhando com interrupções, a primeira providência que
devemos tomar dentro da rotina de interrupção é salvar os conteúdos dos registradores W e
STATUS.
O registrador W, como já vimos, é aquele onde primeiramente escrevemos o
valor que deverá ser gravado em algum registrador, ou para onde copiamos o valor de um
registrador para depois escrevê-lo em outro, pois não é possível escrevermos diretamente nos
registradores, bem como, copiarmos o valor de um registrador diretamente para outro.
No registrador STATUS, é onde selecionamos o banco de memória de dados
(bits RP0 e RP1) e onde o microcontrolador registra informações sobre o estouro do WDT, entrada
em modo de economia de energia (Sleep), bem como, se operações de soma, subtração, rotação de
bits, etc... resultaram em zero e se houve estouro dos registradores envolvidos.
Nos programas que fizemos até agora, não efetuamos nenhuma leitura do
registrador STATUS, porque foram programas bem simples.
É necessário salvar os conteúdos dos registradores W e STATUS porque eles
são usados durante a rotina de interrupção.
No retorno da interrupção, se estes registradores não estiverem com os
valores que estavam antes, ocorrerá um erro.
Criaremos uma variável chamada W_TEMP para salvarmos o valor do
registrador W e outra chamada STATUS_TEMP para o registrador STATUS.
Esses nome são apenas sugestivos.
A variável CONT_EST_TMR0 terá a mesma função do programa anterior,
ou seja, a de contar os estouros do registrador TMR0.
A partir da definição das variáveis, o programa é igual até a rotina de
interrupção:
;**********************************************************************************************
; VARIÁVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

W_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR W


STATUS_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR STATUS
CONT_EST_TMR0 ;USADA PARA CONTAR OS ESTOUROS DO TMR0

ENDC ;FIM DO BLOCO DE MEMORIA

;**********************************************************************************************

54
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

; CONSTANTES

INI_TMR0 EQU .131 ;VALOR QUE TMR0 INICIA


INI_CONT_EST_TMR0 EQU .125 ;VALOR QUE CONT_EST_TMR0 INICIA

;***********************************************************************************************
; SAÍDA

#DEFINE LED PORTA,0 ;LED LIGADO EM RA0

;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO


GOTO INICIO ;DESVIA PARA INICIO

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

Nos programas utilizados nas partes 1 e 2 deste tutorial, havia aqui apenas a
instrução RETFIE, pois não utilizamos o recurso de interrupção, e a instrução RETFIE servia para
se, por acaso, ocorresse uma interrupção inesperada, o programa voltasse ao ponto de onde havia
sido desviado.
Agora iremos habilitar a interrupção de estouro do Timer 0 e, quando ela
ocorrer, o microcontrolador irá executar o código contido a partir daqui.
Primeiramente vamos salvar os valores dos registradores W e STATUS,
copiando-os para as variáveis W_TEMP e STATUS_TEMP.
A Microchip, fabricante do PIC16F628A, recomenda a seguinte sequência
de instruções para salvar os conteúdos dos registradores W e STATUS:

MOVWF W_TEMP ;SALVA W EM W_TEMP


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;CHAVEIA PARA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS

O registrador W é salvo em W_TEMP através da instrução MOVWF.


A seguir, com a instrução SWAPF, inverte-se os nibles (os 4 bits mais
significativos com os 4 bits menos significativos) do registrador STATUS, gravando o resultado no
W.
A seguir, seleciona-se o banco 0 (mesmo que já estivesse selecionado) e,
depois, o conteúdo do registrador STATUS (que estava no W com os nibbles invertidos) é salvo no
STATUS_TEMP, com a instrução MOVWF.
Uma vez salvos os conteúdos dos registradores W e STATUS, testamos o bit
T0IF do registrador INTCON para confirmarmos se a interrupção foi causada pelo estouro do Timer
0:
BTFSS INTCON,T0IF ;TMR0 ESTOUROU?

Se o bit T0IF não estiver setado, significando que a interrupção não foi
causada pelo estouro do Timer 0, a próxima linha do programa será executada, e, nesse caso, temos
de fazer o programa voltar da interrupção.

55
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

Para isso usamos a instrução RETFIE, porém, antes, temos de recuperar os


valores dos registradores W e STATUS.
Faremos isso executando as seguintes instruções, recomendadas pela
Microchip:
SWAPF STATUS_TEMP,W ;W = SWAP EM STATUS_TEMP
MOVWF STATUS ;RECUPERA STATUS
SWAPF W_TEMP,F ;SWAP EM W_TEMP
SWAPF W_TEMP,W ;RECUPERA W
RETFIE ;RETORNA DA INTERRUPÇÃO

Repare que é feito um “swap” no registrador STATUS_TEMP, pois havia


sido feito antes, voltando a apresentar a ordem correta dos nibbles.
A seguir o valor do registrador STATUS é recuperado através da instrução
MOVWF STATUS.
Também é realizado um “swap” no registrador W_TEMP e, como não havia
sido feito antes, faz-se outro em seguida. Ao mesmo tempo, o resultado é salvo no registrador W.
A seguir, vem a instrução RETFIE que faz o microcontrolador retornar da
interrupção.
Lembrando que esse procedimento é recomendado pela Microchip e pode
ser visto no datasheet do PIC16F628A.
Como teremos mais de uma situação em que precisaremos retornar da
interrupção, para evitarmos repetir as instruções acima, deixando nosso código o mais resumido
possível, iremos colocar essas instruções no fim da rotina de interrupção, após a label SAI_INT.
Assim, quando quisermos retornar da interrupção, usaremos a instrução
GOTO SAI_INT:
GOTO SAI_INT ;RETORNA DA INTERRUPÇÃO

Se o bit T0IF estiver setado, indicando que a interrupção foi causada pelo
estouro do Timer 0, a linha com a instrução GOTO SAI_INT será pulada, e, nesse caso, na próxima
instrução apagaremos o bit T0IF:

BCF INTCON,T0IF ;SIM

É necessário apagar esse bit, caso contrário, quando o programa retornar da


interrupção, outra será gerada em seguida, já que o bit ainda estará setado.
Continuando com a rotina, vêm agora as instruções que reiniciam o
registrador TMR0:
MOVLW INI_TMR0 ;W = INI_TMR0
MOVWF TMR0 ;REINICIA TMR0

A seguir decrementamos a variável que conta os estouros do Timer 0 e ao


mesmo tempo verificamos se ela chegou a 0:

DECFSZ CONT_EST_TMR0,F ;DECREMENTA CONT_EST_TMR0. CONT_EST_TMR0 = 0?

Se o valor dessa variável não houver chegado a zero, a próxima linha do


programa será executada.
Nesse caso, novamente iremos retornar da interrupção e, então, escrevemos
a instrução GOTO SAI_INT:

GOTO SAI_INT ;NAO

56
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

Já, se a variável houver chegado a 0, essa linha será pulada e seguem-se as


instruções que reiniciam a variável:
MOVLW INI_CONT_EST_TMR0 ;SIM, W = INI_CONT_EST_TMR0
MOVWF CONT_EST_TMR0 ;REINICIALIZA CONT_EST_TMR0

A seguir, testamos o estado do LED e, após mudá-lo, retornamos da


interrupção:
BTFSS LED ;TESTA O VALOR DO BIT 0 DO PORTA
GOTO ACENDE_LED ;VALOR = 0, DESVIA
BCF LED ;VALOR = 1, APAGA O LED
GOTO SAI_INT ;RETORNA DA INTERRUPCAO

ACENDE_LED
BSF LED ;ACENDE O LED

Recapitulando:
O Timer 0 foi configurado para estourar a cada 4 milissegundos. A cada vez
que o registrador TMR0 estoura, é gerada uma interrupção, onde a variável CONT_EST_TMR0 é
decrementada. Como essa variável é iniciada com o valor 125, quando ela chega a 0, terão se
passado cerca de 500 milissegundos, quando, então, o estado do LED é testado e alterado.
Ou seja, o que era feito na rotina principal do programa na parte 2 deste
tutorial, agora é feito dentro da rotina de interrupção.
O programa da parte 2 fica testando o bit T0IF para detectar quando ele for
setado. Aqui, não precisamos fazer isto, porque, quando o bit é setado, é gerada uma interrupção.
A cada interrupção, a variável CONT_EST_TMR0 é decrementada. Quando
o seu valor chega a 0, mudamos o estado do LED.
Neste programa tão simples, talvez você não veja muita vantagem nisso,
mas, em programas mais complexos, o recurso da interrupção é muito útil.
Nosso objetivo aqui foi explicar como usar o recurso da interrupção do
microcontrolador.
Com isso concluímos a rotina de interrupção que ficou assim:

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

MOVWF W_TEMP ;SALVA W


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;CHAVEIA PARA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS
BTFSS INTCON,T0IF ;TMR0 ESTOUROU?
GOTO SAI_INT ;NAO, SAIR DA INTERRUPÇÃO
BCF INTCON,T0IF ;SIM
MOVLW INI_TMR0 ;W = INI_TMR0
MOVWF TMR0 ;REINICIA TMR0
DECFSZ CONT_EST_TMR0,F ;DECREMENTA CONT_EST_TMR0. CONT_EST_TMR0 = 0?
GOTO SAI_INT ;NAO
MOVLW INI_CONT_EST_TMR0 ;SIM, W = INI_CONT_EST_TMR0
MOVWF CONT_EST_TMR0 ;REINICIALIZA CONT_EST_TMR0
BTFSS LED ;TESTA O VALOR DO BIT 0 DO PORTA

57
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

GOTO ACENDE_LED ;VALOR = 0, DESVIA


BCF LED ;VALOR = 1, APAGA O LED
GOTO SAI_INT ;SAIR DA INTERRUPCAO
ACENDE_LED
BSF LED ;ACENDE O LED

SAI_INT
SWAPF STATUS_TEMP,W ;SWAP EM STATUS_TEMP
MOVWF STATUS ;RECUPERA STATUS
SWAPF W_TEMP,F ;SWAP EM W_TEMP
SWAPF W_TEMP,W ;RECUPERA W
RETFIE ;RETORNA DA INTERRUPÇÃO

;**********************************************************************************************

O próximo passo é a configuração dos registradores de uso específico.


A diferença aqui em relação ao programa da parte 2, é a configuração do
registrador INTCON.
Conforme já havíamos comentado, temos que “setar” os bits 5 (T0IE), 6
(PEIE) e 7 (GIE) do registrador INTCON e, portanto, escreveremos o valor binário 11100000
neste registrador:

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11010100' ;W = B'11010100'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO, COM PRESCALER DE 1:32
MOVLW B'11111110' ;W = B'11111110'
MOVWF TRISA ;CONFIGURA RA0 COMO SAÍDA E DEMAIS COMO ENTRADAS

MOVLW B'11111111'
MOVWF TRISB ;TODOS OS PINOS DO PORTB COMO ENTRADAS
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O
MOVLW B'11100000'
MOVWF INTCON ;HABILITA INTERRUPÇÃO DO TIMER 0

;**********************************************************************************************
A seguir vem a inicialização da variável CONT_EST_TRMR0.
Não é necessário inicializar W_TEMP e STATUS_TEMP, pois, não são
variáveis que precisam ter um determinado valor na inicialização.
Finalmente, vem a rotina principal, que consiste apenas na execução da
instrução CLRWDT.
O programa fica executando a instrução CLRWDT até que ocorra uma
interrupção.
O programa completo ficou assim:

58
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

;***********************************************************************************************
; PROGRAMA: PISCA LED III
; VERSÃO 1.0
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /
;***********************************************************************************************

#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A

;***********************************************************************************************
; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF
& _LVP_OFF & _CP_OFF & DATA_CP_OFF

;**********************************************************************************************
; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA

;**********************************************************************************************
; VARIÁVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

W_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR W


STATUS_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR STATUS
CONT_EST_TMR0 ;USADA PARA CONTAR OS ESTOUROS DO TMR0

ENDC ;FIM DO BLOCO DE MEMORIA

;***********************************************************************************************
; CONSTANTES

INI_TMR0 EQU .131 ;VALOR QUE TMR0 INICIA


INI_CONT_EST_TMR0 EQU .125 ;VALOR QUE CONT_EST_TMR0 INICIA

;***********************************************************************************************
; SAÍDA

#DEFINE LED PORTA,0 ;LED LIGADO EM RA0

;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO


GOTO INICIO ;DESVIA PARA INICIO

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

MOVWF W_TEMP ;SALVA W


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;CHAVEIA PARA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS

59
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

BTFSS INTCON,T0IF ;TMR0 ESTOUROU?


GOTO SAI_INT ;NAO, SAIR DA INTERRUPÇÃO
BCF INTCON,T0IF ;SIM
MOVLW INI_TMR0 ;W = INI_TMR0
MOVWF TMR0 ;REINICIA TMR0
DECFSZ CONT_EST_TMR0,F ;DECREMENTA CONT_EST_TMR0. CONT_EST_TMR0 = 0?
GOTO SAI_INT ;NAO
MOVLW INI_CONT_EST_TMR0 ;SIM, W = INI_CONT_EST_TMR0
MOVWF CONT_EST_TMR0 ;REINICIALIZA CONT_EST_TMR0
BTFSS LED ;TESTA O VALOR DO BIT 0 DO PORTA
GOTO ACENDE_LED ;VALOR = 0, DESVIA
BCF LED ;VALOR = 1, APAGA O LED
GOTO SAI_INT ;SAIR DA INTERRUPCAO

ACENDE_LED
BSF LED ;ACENDE O LED

SAI_INT
SWAPF STATUS_TEMP,W ;W = SWAP EM STATUS_TEMP
MOVWF STATUS ;RECUPERA STATUS
SWAPF W_TEMP,F ;SWAP EM W_TEMP
SWAPF W_TEMP,W ;RECUPERA W
RETFIE ;RETORNA DA INTERRUPÇÃO

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11010100' ;W = B'11010100'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO , COM PRESCALER DE 1:32
MOVLW B'11111110' ;W = B'11111110'
MOVWF TRISA ;CONFIGURA RA0 COMO SAÍDA E DEMAIS COMO ENTRADAS
MOVLW B'11111111'
MOVWF TRISB ;TODOS OS PINOS DO PORTB COMO ENTRADAS
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O
MOVLW B'11100000'
MOVWF INTCON ;HABILITA INTERRUPÇÃO DO TIMER 0

;***********************************************************************************************
; INICIALIZACAO DA VARIÁVEL

MOVLW INI_CONT_EST_TMR0 ;W = INI_CONT_EST_TMR0


MOVWF CONT_EST_TMR0 ;INICIALIZA CONT_EST_TMR0

;***********************************************************************************************

PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


GOTO PRINCIPAL

;**********************************************************************************************
END ;FIM DO PROGRAMA

60
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

Vamos simular a execução do programa.


Abra o MPLAB e no menu “Project”, clique em “Open”, selecione e abra o
projeto de nome Pisca LED.
No menu “Project”, em “Remove File From Project”, clique no arquivo
Pisca LED II para removê-lo.
Em seguida, no menu “Project”, clique em “Add File To Project”, selecione
o arquivo Pisca LED III e clique em “Abrir” para adicioná-lo ao projeto.
No menu “Project”, clique em “Build All”.
Verifique se a montagem foi efetuada com sucesso:

Figura 1

No menu “File”, clique em “Open”, localize e abra o arquivo Pisca LED III.
Na barra de ferramentas do simulador, clique no botão “Reset”:

61
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

Figura 2

Na mesma barra de ferramentas, vá clicando no botão “Step Into”, e observe


que, na rotina principal, ele fica executando a instrução CLRWDT.
Clique duas vezes na linha da primeira instrução da rotina de interrupção
(MOVWF W_TEMP), para adicionar um “breakpoint”.
Abra a janela do “StopWatch”, no menu “Window”.
Clique em “Zero”.
Clique no botão “Run” da barra de ferramentas do simulador.
Na janela do “Stopwatch” repare que demorou cerca de 4 milissegundos
para que ocorresse uma interrupção.
Vá clicando no botão “Step Into” e observando a execução da rotina de
interrupção, e repare que após a execução da instrução RETFIE ele retorna à rotina principal.
Clique duas vezes na linha com o “breakpoint” para removê-lo e adicione
outro na linha com a instrução BTFSS LED.
Na janela do “Stopwatch”, clique em “Zero”, clique no botão “Run” e
depois, verifique no “Stopwatch” que o tempo que passou é de cerca de 500 milissegundos.
Obs.: Se aparecer a mensagem da figura abaixo, no menu “Debugger”,
clique em “Breakpoints...” e remova os outros que estiverem aparecendo.

62
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III

Figura 3

Grave o programa no microcontrolador e teste no circuito para comprovar o


seu funcionamento.
Aqui termina a 3ª parte deste tutorial.
Na próxima parte iremos adicionar um chave do tipo push-button ou seja, do
tipo pulso ao nosso circuito, através da qual iremos ligar e desligar nosso pisca-pisca.

63
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Parte 4

Pisca LED com botão para ligar e desligar

Agora iremos acrescentar ao nosso circuito um botão do tipo pulso N/A


(normalmente aberto) sem trava que a cada vez que for pressionado, alternará o estado do LED
entre piscando e apagado.
Nesse tipo de chave os contatos ficam fechados apenas enquanto o botão
está pressionado.
Primeiramente devemos decidir em qual pino do PIC16F628A iremos ligar
o botão.
Um botão é um dispositivo de entrada de dados e, portanto, devemos
escolher um pino que possa ser configurado como entrada.
Conforme já foi exposto na parte 1 deste tutorial, todos os pinos do PORTA
e do PORTB podem ser configurados como entrada.
Vamos escolher o RA1.
Eis o esquema do nosso circuito com o botão incluído:

Repare que ligamos um resistor entre o pino de entrada e o VDD, enquanto


que o botão foi ligado entre o pino e o VSS.
Todo pino configurado como entrada tem de estar ou em nível alto ou em
nível baixo, ou seja, ou na tensão de VDD ou na de VSS.
No nosso circuito, quando o botão está solto, o resistor mantém o pino em
nível alto e quando o botão é pressionado o pino vai para nível baixo.
Se não houvéssemos ligado o resistor, quando o botão estivesse solto, o pino
ficaria desconectado, ou seja, “flutuando”, ocasionando um comportamento imprevisível do
microcontrolador quando efetuasse a leitura do estado deste pino.

64
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Poderíamos, também, ter ligado o botão entre o pino e o VDD e o resistor


entre o pino e o VSS, mas geralmente se liga o botão no VSS.
Quando utilizamos uma chave num circuito digital, nos deparamos com um
problema:
Quando a chave é acionada, a condução da corrente elétrica em seus
contatos fica instável durante um tempo de alguns milissegundos.
É como se a chave ficasse sendo ligada e desligada até que finalmente
permanecesse ligada.
No nosso circuito, a cada vez que o botão for pressionado, o LED deverá
alternar entre piscando e apagado.
Mas, com o problema da instabilidade da condução da corrente na chave,
nunca se saberia como o LED ficaria, pois, o microcontrolador poderia detectar dois, três, ou mais
apertos do botão a cada vez que ele fosse pressionado, devido ao problema descrito.
Para resolver esse problema, pode-se utilizar junto ao botão um circuito
conhecido como de “de-bouncing”, baseado em um capacitor.
Mas, quando estamos utilizando um microcontrolador, podemos dispensar o
uso deste circuito, pois é possível implementar o “de-bouncing” na rotina do programa.
Isso pode ser feito da seguinte forma:
Quando é detectado que o estado do pino corresponde ao de botão
pressionado, ele é testado por cerca de mais 50 milissegundos e, se o seu estado permanecer o
mesmo por esse tempo, considera-se que o botão foi realmente pressionado.
No nosso circuito a partir do momento em que o estado do pino RA1 for
detectado como sendo baixo, iremos testar esse pino por mais 50 milissegundos e se o estado do
pino permanecer baixo por esse tempo, iremos adotar os procedimentos correspondentes ao
acionamento do botão.
Se o seu estado voltar para alto antes de ter transcorrido o referido tempo,
iremos começar a testar o botão novamente.
Isso elimina o problema da instabilidade da condução da corrente no
momento em que o botão é pressionado.
Há outro procedimento que deve ser adotado quando se utiliza um botão:
Caso se deseje que a ação seja produzida somente a cada aperto do botão (depois de realizada a
ação, mesmo que o botão seja mantido pressionado nada deverá ocorrer), devemos registrar de
alguma forma que a ação já foi realizada e que se deve aguardar o botão ser solto e pressionado
novamente.
Uma forma de fazer isso, é usar um bit de um registrador de uso geral. Este
bit é setado após a ação referente ao aperto do botão ser realizada e só é apagado quando o botão for
solto. Bits usados para esta finalidade são chamados de flags.
Vamos ao programa!
No MPLAB, abra o arquivo Pisca LED III.asm.
No menu “File”, clique em “Save As...” e mude o nome para Pisca LED
IV.asm
A primeira alteração que faremos é na seção “Variáveis”, onde, iremos
adicionar três: FLAGS, DB_BTA e DB_BTB.
A variável FLAGS conterá os bits de flag.
As variáveis DB_BTA e DB_BTB serão usadas para a contagem do tempo
de de-bouncing do botão:

65
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

;********************************************************************************************
; VARIÁVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

W_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR W


STATUS_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR STATUS
CONT_EST_TMR0 ;USADA PARA CONTAR OS ESTOUROS DO TMR0
FLAGS ;REGISTRADOR DE FLAGS
DB_BTA ;PRIMEIRO REGISTRADOR DO DE-BOUNCING DO BOTÃO
DB_BTB ;SEGUNDO REGISTRADOR DO DE-BOUNCING DO BOTÃO

ENDC ;FIM DO BLOCO DE MEMORIA

;********************************************************************************************
A seguir, vamos criar as constantes para a inicialização das variáveis
DB_BTA e DB_BTB:

;**********************************************************************************************
; CONSTANTES

INI_TMR0 EQU .131 ;VALOR QUE TMR0 INICIA


INI_CONT_EST_TMR0 EQU .125 ;VALOR QUE CONT_EST_TMR0 INICIA
INI_DB_BTA EQU .255 ;VALOR QUE DB_BTA INICIA
INI_DB_BTB EQU .50 ;VALOR QUE DB_BTB INICIA

;**********************************************************************************************

Iremos ajustar esses valores durante a simulação, para que tenhamos um


tempo de de-bouncing em torno de 50 milissegundos.
A seguir, depois da seção “Saída”, vamos criar uma seção chamada
“Entrada”, para definirmos a label BOTAO como sendo o bit 1 do PORTA:

;***********************************************************************************************
; ENTRADA

#DEFINE BOTAO PORTA,1 ;BOTAO LIGADO EM RA1

;***********************************************************************************************

A seguir, iremos acrescentar uma seção chamada “FLAGS” onde iremos


atribuir um nome aos bits que iremos utilizar como flags:

;***********************************************************************************************
; FLAGS

#DEFINE SOLTAR_BOTAO FLAGS,0 ;SE = 1 AGUARDA SOLTAR O BOTÃO


#DEFINE ESTADO_DO_LED FLAGS,1 ;SE = 1 LED PISCANDO

;***********************************************************************************************

A função do flag SOLTAR_BOTAO, já foi explicada. A do flag


ESTADO_DO_LED veremos adiante.
Também faremos alterações na rotina de interrupção, mas somente depois
que tenhamos escrito a rotina principal.

66
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

A seguir, na seção “INICIALIZAÇÃO DAS VARIÁVEIS”, vamos incluir as


novas variáveis:
;***********************************************************************************************
; INICIALIZACAO DAS VARIAVEIS

MOVLW INI_CONT_EST_TMR0 ;W = INI_CONT_EST_TMR0


MOVWF CONT_EST_TMR0 ;INICIALIZA CONT_EST_TMR0
CLRF FLAGS ;INICIALIZA FLAGS
MOVLW INI_DB_BTA ;W = INI_DB_BTA
MOVWF DB_BTA ;INICIALIZA DB_BTA
MOVLW INI_DB_BTB ;W = INI_DB_BTB
MOVWF DB_BTB ;INICIALIZA DB_BTB

;***********************************************************************************************
A variável FLAGS foi inicializada com a instrução CLRF. Esta instrução faz
todos os bits de um registrador iguais a 0.
Na rotina principal, após a instrução CLRWDT, temos a instrução BTFSS
SOLTAR_BOTAO que testa o estado desse bit de flag.
Se o valor desse bit for 0, significando que não está se aguardando soltar o
botão, a próxima linha será executada e programa será desviado para onde está a label
TESTA_BOTAO, onde será testado o estado do pino onde o botão está ligado (RA1) para ver se
está pressionado.
Porém, se o bit estiver setado, significando que está se aguardando soltar o
botão, devemos verificar se o botão já foi solto. Nesse caso, a próxima linha será pulada e será
executada a instrução BTFSS BOTAO, para testar o RA1.
Se o estado do RA1 for igual a 0, significa que o botão ainda não foi solto e
por isso o programa é desviado para o início da rotina “PRINCIPAL”, para que voltemos a testá-lo
até que ele tenha sido solto.
Se o estado do RA1 for igual a 1, significa que o botão foi solto e, então,
apagamos o flag SOLTAR_BOTAO.
Após a label TESTA_BOTAO, temos a instrução BTFSC BOTAO, onde
testamos o estado do bit RA1 para verificarmos se o botão está pressionado.
Se ele não estiver pressionado, o estado deste bit será igual a 1, e, portanto, a
próxima linha será executada, desviando o programa para REINC_CONT_DEB, onde as variáveis
DB_BTA e DB_BTB são reinicializadas e depois o programa é desviado para o início da rotina
principal.
Se o botão estiver pressionado (estado do bit RA1 igual a 0), a próxima
linha será pulada e a instrução DECFSZ DB_BTA ,F será executada, começando o de-bouncing.
A instrução DECFSZ DB_BTA,F decrementa a variável e ao mesmo tempo
verifica se o seu valor chegou a 0. Se não chegou, o programa retorna para o início da rotina
PRINCIPAL e, se o botão permanecer pressionado, esta variável será novamente decrementada.
Toda vez que o valor de DB_BTA chega a 0, ela é reinicializada e a variável DB_BTB é
decrementada. Quando o valor de DB_BTB chegar a 0, o de-bouncing terá terminado.
Mas, para que isso ocorra, é necessário que o estado do pino RA1 se
mantenha em nível baixo durante todo esse tempo, que iremos ajustar para cerca de 50
milissegundos, através dos valores de inicialização de DB_BTA e DB_BTB.
Se antes disso o nível de RA1 voltar a 1, o programa será desviado para a
subrotina REINC_CONT_DEB, onde as variáveis DB_BTA e DB_BTB são reinicializadas, para
que se comece uma nova contagem de 50 milissegundos, quando for detectado nível baixo em RA1.

67
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Tendo o de-bouncing terminado com a variável DB_BTB chegando a 0, ela


é reiniciada.
Nesse momento podemos considerar que o botão foi pressionado e devemos
adotar as providências decorrentes disso.
No nosso circuito, queremos que a cada vez que o botão seja pressionado, o
LED mude de estado. Se ele estiver apagado deverá começar a piscar e se estiver piscando deverá
parar de piscar. Portanto, quando o programa chega nesse ponto, devemos saber em que estado está
o LED.
Uma forma de sinalizar o estado atual do LED é utilizar um bit de flag. É
para isso que servirá o bit que denominamos de ESTADO_DO_LED.
Convencionaremos que quando o valor desse bit for igual a 0, o LED estará
apagado e, quando for igual a 1, ele estará piscando.
A próxima instrução é a BTFSS que verifica o valor do bit
ESTADO_DO_LED. Se ele for igual a 0, desviamos o programa para onde está a label
PISCAR_O_LED, onde, ele é setado.
Se o valor do bit ESTADO_DO_LED for igual a 1, ele é zerado.
Após zerar ou setar o bit ESTADO_DO_LED, setamos o bit de flag
SOLTAR_BOTAO, que como já vimos fará com que se aguarde o botão ser solto.

;***********************************************************************************************
PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


BTFSS SOLTAR_BOTAO ;AGUARDA SOLTAR O BOTAO?
GOTO TESTA_BOTAO ;NAO, DESVIA
BTFSS BOTAO ;SIM, O BOTÃO ESTÁ SOLTO?
GOTO PRINCIPAL ;NAO, DESVIA
BCF SOLTAR_BOTAO ;SIM, APAGA FLAG

TESTA_BOTAO
BTFSC BOTAO ;O BOTÃO ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB ;NÃO, DESVIA
DECFSZ DB_BTA,F ;SIM, DECREMENTA DB_BTA. DB_BTA = 0?
GOTO PRINCIPAL ;NAO, DESVIA
MOVLW INI_DB_BTA ;SIM, W = INI_DB_BTA
MOVWF DB_BTA ;INICIALIZA DB_BTA
DECFSZ DB_BTB,F ;DECREMENTA DB_BTB. DB_BTB = 0?
GOTO PRINCIPAL ;NAO, DESVIA
MOVLW INI_DB_BTB ;SIM, W = INI_DB_BTB
MOVWF DB_BTB ;INICIALIZA DB_BTB
BTFSS ESTADO_DO_LED ;LED ESTÁ PISCANDO?
GOTO PISCAR_O_LED ;NAO, DESVIA
BCF ESTADO_DO_LED ;SIM, APAGA O LED
BSF SOLTAR_BOTAO ;SETA O FLAG PARA AGUARDAR SOLTAR O BOTAO
GOTO PRINCIPAL ;DESVIA

PISCAR_O_LED
BSF ESTADO_DO_LED ;FAZ O LED PISCAR
BSF SOLTAR_BOTAO ;SETA O FLAG PARA AGUARDAR SOLTAR O BOTAO
GOTO PRINCIPAL ;DESVIA
REINC_CONT_DEB
MOVLW INI_DB_BTA ;W = INI_DB_BTA
MOVWF DB_BTA ;INICIALIZA DB_BTA
MOVLW INI_DB_BTB ;W = INI_DB_BTB

68
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

MOVWF DB_BTB ;INICIALIZA DB_BTB


GOTO PRINCIPAL ;DESVIA

;*********************************************************************************************
END ;FIM DO PROGRAMA

;**********************************************************************************************

Agora vamos fazer as alterações na rotina de interrupção.


É na rotina de interrupção onde o valor do bit de flag ESTADO_DO_LED,
será testado e o estado do LED alterado.
Há mais de uma maneira de implementar isso. Vamos fazer da seguinte
forma:
Depois da instrução BCF INTCON,T0IF, vamos testar o valor do bit
ESTADO_DO_LED. Se o seu valor for igual a 0, significando que o LED deverá ficar apagado,
vamos apagá-lo com a instrução BCF LED e em seguida sairemos da interrupção. Se o valor do bit
ESTADO_DO_LED for igual a 1, significando que o LED deverá piscar, deixaremos que a rotina
de interrupção prossiga:

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

MOVWF W_TEMP ;SALVA W


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;CHAVEIA PARA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS
BTFSS INTCON,T0IF ;TMR0 ESTOUROU?
GOTO SAI_INT ;NAO, SAIR DA INTERRUPÇÃO
BCF INTCON,T0IF ;SIM
BTFSC ESTADO_DO_LED ;LED DEVERÁ PISCAR?
GOTO CONT_INTERRUPCAO ;SIM, DESVIA
BCF LED ;NAO, APAGA O LED
GOTO SAI_INT ;SAIR DA INTERRUPCAO

CONT_INTERRUPCAO
MOVLW INI_TMR0 ;W = INI_TMR0
MOVWF TMR0 ;REINICIA TMR0
DECFSZ CONT_EST_TMR0,F ;DECREMENTA CONT_EST_TMR0. CONT_EST_TMR0 = 0?
GOTO SAI_INT ;NAO
MOVLW INI_CONT_EST_TMR0 ;SIM, W = INI_CONT_EST_TMR0
MOVWF CONT_EST_TMR0 ;REINICIALIZA CONT_EST_TMR0
BTFSS LED ;TESTA O VALOR DO BIT 0 DO PORTA
GOTO ACENDE_LED ;VALOR = 0, DESVIA
BCF LED ;VALOR = 1, APAGA O LED
GOTO SAI_INT ;SAIR DA INTERRUPCAO

ACENDE_LED
BSF LED ;ACENDE O LED

SAI_INT
SWAPF STATUS_TEMP,W ;SWAP EM STATUS_TEMP

69
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

MOVWF STATUS ;RECUPERA STATUS


SWAPF W_TEMP,F ;SWAP EM W_TEMP
SWAPF W_TEMP,W ;RECUPERA W
RETFIE ;RETORNA DA INTERRUPÇÃO

;***********************************************************************************************

Nosso programa está pronto para ser simulado, tendo ficado assim:

;***********************************************************************************************
; PROGRAMA: PISCA LED IV
; VERSÃO 1.0
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /
;***********************************************************************************************

#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A

;***********************************************************************************************
; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF
& _LVP_OFF & _CP_OFF & DATA_CP_OFF

;**********************************************************************************************
; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA

;**********************************************************************************************
; VARIÁVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

W_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR W


STATUS_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR STATUS
CONT_EST_TMR0 ;USADA PARA CONTAR OS ESTOUROS DO TMR0
FLAGS ;REGISTRADOR DE FLAGS
DB_BTA ;PRIMEIRO REGISTRADOR DO DE-BOUNCING DO BOTÃO
DB_BTB ;SEGUNDO REGISTRADOR DO DE-BOUNCING DO BOTÃO

ENDC ;FIM DO BLOCO DE MEMORIA

;**********************************************************************************************
; CONSTANTES

INI_TMR0 EQU .131 ;VALOR QUE TMR0 INICIA


INI_CONT_EST_TMR0 EQU .125 ;VALOR QUE CONT_EST_TMR0 INICIA
INI_DB_BTA EQU .255 ;VALOR QUE DB_BTA INICIA
INI_DB_BTB EQU .50 ;VALOR QUE DB_BTB INICIA

;***********************************************************************************************
; SAÍDA

70
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

#DEFINE LED PORTA,0 ;LED LIGADO EM RA0

;***********************************************************************************************
; ENTRADA

#DEFINE BOTAO PORTA,1 ;BOTAO LIGADO EM RA1

;***********************************************************************************************
; FLAGS

#DEFINE SOLTAR_BOTAO FLAGS,0 ;SE = 1 AGUARDA SOLTAR O BOTÃO


#DEFINE ESTADO_DO_LED FLAGS,1 ;SE = 1 LED PISCANDO

;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO


GOTO INICIO ;DESVIA PARA INICIO

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

MOVWF W_TEMP ;SALVA W


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS
BTFSS INTCON,T0IF ;TMR0 ESTOUROU?
GOTO SAI_INT ;NAO, SAIR DA INTERRUPÇÃO
BCF INTCON,T0IF ;SIM
BTFSC ESTADO_DO_LED ;LED DEVERÁ PISCAR?
GOTO CONT_INTERRUPCAO ;SIM, DESVIA
BCF LED ;NAO, APAGA O LED
GOTO SAI_INT ;SAIR DA INTERRUPCAO

CONT_INTERRUPCAO
MOVLW INI_TMR0 ;W = INI_TMR0
MOVWF TMR0 ;REINICIA TMR0
DECFSZ CONT_EST_TMR0,F ;DECREMENTA CONT_EST_TMR0. CONT_EST_TMR0 = 0?
GOTO SAI_INT ;NAO
MOVLW INI_CONT_EST_TMR0 ;SIM, W = INI_CONT_EST_TMR0
MOVWF CONT_EST_TMR0 ;REINICIALIZA CONT_EST_TMR0
BTFSS LED ;TESTA O VALOR DO BIT 0 DO PORTA
GOTO ACENDE_LED ;VALOR = 0, DESVIA
BCF LED ;VALOR = 1, APAGA O LED
GOTO SAI_INT ;SAIR DA INTERRUPCAO

ACENDE_LED
BSF LED ;ACENDE O LED

SAI_INT
SWAPF STATUS_TEMP,W ;SWAP EM STATUS_TEMP
MOVWF STATUS ;RECUPERA STATUS
SWAPF W_TEMP,F ;SWAP EM W_TEMP
SWAPF W_TEMP,W ;RECUPERA W
RETFIE ;RETORNA DA INTERRUPÇÃO

71
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11010100' ;W = B'11010100'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO, COM PRESCALER DE 1:32
MOVLW B'11111110' ;W = B'11111110'
MOVWF TRISA ;CONFIGURA RA0 COMO SAÍDA E DEMAIS COMO ENTRADAS
MOVLW B'11111111'
MOVWF TRISB ;TODOS OS PINOS DO PORTB COMO ENTRADAS
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O
MOVLW B'11100000'
MOVWF INTCON ;HABILITA INTERRUPÇÃO DO TIMER 0

;***********************************************************************************************
; INICIALIZACAO DAS VARIAVEIS

MOVLW INI_CONT_EST_TMR0 ;W = INI_CONT_EST_TMR0


MOVWF CONT_EST_TMR0 ;INICIALIZA CONT_EST_TMR0
CLRF FLAGS ;INICIALIZA FLAGS
MOVLW INI_DB_BTA ;W = INI_DB_BTA
MOVWF DB_BTA ;INICIALIZA DB_BTA
MOVLW INI_DB_BTB ;W = INI_DB_BTB
MOVWF DB_BTB ;INICIALIZA DB_BTB

;***********************************************************************************************

PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


BTFSS SOLTAR_BOTAO ;AGUARDA SOLTAR O BOTAO?
GOTO TESTA_BOTAO ;NAO, DESVIA
BTFSS BOTAO ;SIM, O BOTÃO ESTÁ SOLTO?
GOTO PRINCIPAL ;NAO, DESVIA
BCF SOLTAR_BOTAO ;SIM, APAGA FLAG

TESTA_BOTAO
BTFSC BOTAO ;O BOTÃO ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB ;NÃO, DESVIA
DECFSZ DB_BTA,F ;SIM, DECREMENTA DB_BTA. DB_BTA = 0?
GOTO PRINCIPAL ;NAO, DESVIA
MOVLW INI_DB_BTA ;SIM, W = INI_DB_BTA
MOVWF DB_BTA ;INICIALIZA DB_BTA
DECFSZ DB_BTB,F ;DECREMENTA DB_BTB. DB_BTB = 0?
GOTO PRINCIPAL ;NAO, DESVIA
MOVLW INI_DB_BTB ;SIM, W = INI_DB_BTB
MOVWF DB_BTB ;INICIALIZA DB_BTB
BTFSS ESTADO_DO_LED ;LED ESTÁ PISCANDO?
GOTO PISCAR_O_LED ;NAO, DESVIA
BCF ESTADO_DO_LED ;SIM, APAGA O LED
BSF SOLTAR_BOTAO ;SETA O FLAG PARA AGUARDAR SOLTAR O BOTAO
GOTO PRINCIPAL ;DESVIA

72
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

PISCAR_O_LED
BSF ESTADO_DO_LED ;FAZ O LED PISCAR
BSF SOLTAR_BOTAO ;SETA O FLAG PARA AGUARDAR SOLTAR O BOTAO
GOTO PRINCIPAL ;DESVIA

REINC_CONT_DEB
MOVLW INI_DB_BTA ;W = INI_DB_BTA
MOVWF DB_BTA ;INICIALIZA DB_BTA
MOVLW INI_DB_BTB ;W = INI_DB_BTB
MOVWF DB_BTB ;INICIALIZA DB_BTB
GOTO PRINCIPAL ;DESVIA

;*********************************************************************************************
END ;FIM DO PROGRAMA

;**********************************************************************************************

No MPLAB, no menu “Project” clique em “Open...”


Abra o projeto “Pisca LED”.
No menu “Project”, posicione o ponteiro do mouse em “Remove File From
Project”.
O Arquivo “Pisca LED III.asm”, deve aparecer à direita.
Clique nele para removê-lo.
No menu “Project”, clique em “Add Files do Project...”
Selecione o arquivo Pisca LED IV.asm e clique em abrir.
No menu “Project”, clique em “Build All”.
Verifique se o projeto foi montado corretamente, sendo exibida a mensagem
“BUILD SUCCEEDED”:

73
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Figura 1

No menu “Debugger”, posicione o mouse sobre “Select Tool” e clique em


“MPLAB SIM”.
No menu “File”, clique em “Open”.
Selecione o arquivo Pisca LED IV.asm e clique em abrir.
No menu “View”, clique em “Watch”.
Deixe somente o registrador PORTA, excluindo os demais que estejam
aparecendo, referentes aos programas anteriores.
Adicione o registrador FLAGS, selecionando-o na caixa à direita do botão
“Add Symbol” e depois clicando nesse botão.
No menu “Debugger”, aponte o mouse para “Stimulus” e clique em “New
Wordbook”.
Na caixa de seleção do campo “Pin/SFR”, selecione RA1:

74
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Figura 2

Na caixa de seleção do campo “Action” ao lado, selecione “Set High”:

Figura 3

75
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Na próxima linha, selecione novamente RA1, porém desta vez, em “Action”,


selecione “Set Low”:

Figura 4

Clique em “Save”, atribuindo um nome qualquer, por exemplo Pisca LED.


No menu “Window”, vá para a janela com o programa (Pisca LED.asm).
Clique no botão reset da barra de ferramentas do simulador.
Vá clicando no botão “Step Into”, até que o cursor esteja na linha com a
instrução CLRWDT .
Caso apareça esta janela, clique em “SIM”:

76
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Figura 5

No menu “Window”, alterne para a janela do “Stimulus”.


Clique no botão da primeira linha ao lado do RA1:

Figura 6

Clicando nesse botão estará fazendo com que o pino RA1 fique em nível
alto, ou seja, que o seu valor seja igual a 1.
Volte para a janela do programa e clique mais uma vez no botão “Step Into” .

77
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Em seguida, vá para a janela do “Watch” e repare que o RA1 assumiu o valor 1:

Figura 7

No “Stimulus”, clique agora no botão da linha de baixo.


Clique mais uma vez no botão “Step Into” e repare, no “Watch” que o RA1
voltou para 0.
Através desses botões iremos simular que o botão foi pressionado e que foi
solto.
Aproveitando que o RA1 está em nível baixo, simulando que o botão está
pressionado, vamos medir o tempo do “de-bouncing”.
No menu “Debugger”, clique em “Breakpoints”.
Se houver “breakpoints” listados, exclua-os e clique em OK.
Em seguida, na janela do programa, dê um duplo clique na linha que contém
a instrução BTFSS ESTADO_DO LED (rotina principal) para inserir um “Breakpoint”, já que
quando o “de-bouncing” terminar, o programa irá executar essa instrução.
Abra a janela do “Stopwatch”, que deverá estar disponível no menu
“Window” e clique em “Zero”. Se a janela do “Stopwatch” não estiver disponível no menu
“Window”, abra-o através do menu “Debugger”.
Clique no botão “Run” da barra de ferramentas do simulador.
O programa irá parar na linha onde está o “breakpoint”.
Vá para a janela do “Stopwatch” e veja que o tempo de “de-bouncing” foi de
115 milissegundos:

78
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Figura 8

Como queremos um tempo de cerca de 50 milissegundos para o “de-


bouncing”, vamos experimentar diminuir o valor da variável DB_BTB para 20.
Fazemos isso na seção “CONSTANTES” alterando o valor de INI_DB_BTB.
Após alterar, temos de salvar, clicando no menu “File”, em “Save” e montar
novamente o projeto, clicando no menu “Project” em “Build All”.

Após fazer isso, vá clicando no botão “Step Into” até que o cursor chegue à
linha da instrução CLRWDT.
Abra a janela do “Stopwatch” e clique em “Zero”.
Clique no botão “Run” do simulador e quando o programa parar no
“breakpoint”, verifique o tempo indicado pelo “Stopwatch”.
Repare que o tempo do “de-bouncing” agora foi de cerca de 46
milissegundos o que está bom, pois, não precisa ser de exatos 50 milissegundos.
Vá clicando no botão “Step Into” e acompanhe a execução do programa.
Quando ele retornar para o início da rotina “PRINCIPAL”, terão sido
setados os dois bits de flag: o que indica que se está aguardando soltar o botão e o que indica que o
LED deve piscar. Repare, no “Watch”, que esses dois bits estão setados.
Vá clicando no botão “Step Into” e repare que o programa está aguardando o
botão ser solto.
Vamos simular que o botão foi solto clicando, no “Stimulus”, no botão da
primeira linha, fazendo com que o RA1 vá para nível alto.
Vá clicando em “Step Into” e repare que ele apaga o flag
“SOLTAR_BOTAO”.
Continue clicando no botão “Step Into” e acompanhando a execução do
programa e repare que ele ficará em loop até que o botão seja novamente pressionado.
Como o flag que indica que o botão deve piscar foi setado, vamos ver se o
pino onde o LED está ligado (RA0) está alternando de nível.

79
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Restaure a janela do programa, de forma que possamos ver as outras janelas:

Figura 9

Arraste a janela do programa para baixo para liberar espaço:

80
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

Figura 10
Posicione as janelas de forma que possa visualizar os valores do PORTA, na
janela do “Watch” e ao mesmo tempo possa clicar nos botões do “Stimulus”:

Figura 11

81
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV

No menu “Debugger”, clique em “Settings...” e na aba “Animation/Real


Time Updates”, selecione “Enable Real Time Watch Updates”, ajuste o seu cursor todo para a
esquerda (“Fastest”) e clique em OK.
Clique no botão “Run” do simulador e observe na janela do “Watch” que o
bit 0 do PORTA, fica alternando de estado ou seja, o LED está piscando:

Figura 12

Agora vamos simular que o botão foi pressionado.


Ainda com o botão “Run” ativado, no “Stimulus”, clique no botão da
segunda linha para levar o RA1 para nível baixo.
Repare que o flag SOLTAR_BOTAO (bit 0 do registrador FLAGS), foi
setado, o flag ESTADO_DO_LED (bit 1 do registrador FLAGS) foi apagado e que o bit RA0 parou
de alternar de estado ficando em nível 0, ou seja, o LED está apagado.
No “Stimulus” clique no botão da primeira linha para simular que o botão
foi solto.
Repare que o flag SOLTAR_BOTAO foi apagado.
Simule que o botão foi pressionado novamente através do “Stimulus” e
perceba que o RA0 voltou a alternar de estado.
Uma vez que o programa está se comportando como esperávamos, grave-o
no microcontrolador e monte o circuito para comprovar o seu funcionamento.
Aqui termina a 4ª parte deste tutorial.
Espero que você tenha gostado.
Na próxima parte, vamos montar um circuito onde haverá um display de 7
segmentos e dois botões: um incrementará o número exibido no display, enquanto o outro o
decrementará.
Até lá.

82
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Parte 5

Contador Crescente e Decrescente

Desta vez iremos montar o circuito da figura abaixo onde o botão 1 irá
incrementar o número exibido no display, enquanto o botão 2 o decrementará.

Figura 1

83
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

O display que utilizaremos é do modelo HD-K123 ou HS-5202AG ou outro


modelo compatível de dois dígitos, portanto o número exibido estará entre 00 e 99.
Para comandar os 14 segmentos dos dois dígitos do display a partir de
apenas 7 pinos do PIC16F628A, iremos ativar, a cada vez, somente um dos dígitos, através de seu
pino de catodo comum.
Quando o pino RA2 estiver em nível alto, a junção base-emissor do
transistor T1 estará diretamente polarizada, ativando o dígito 1.
Da mesma forma, quando o pino RA3 estiver em nível alto, será o dígito 2
que estará ativo.
Levaremos aos pinos RB0 a RB6 os níveis lógicos adequados para que seja
exibido o número desejado no dígito que estiver ativo no momento.
Desta forma, enquanto um dígito estiver exibindo o seu número, o outro
estará apagado.
Cada dígito estará ativo por cerca de 5 milissegundos, ficando apagado pelos
próximos 5 milissegundos (enquanto o outro estará ativo), voltando após este tempo a estar ativo
por mais 5 milissegundos e assim por diante.
Devido à persistência da visão, os dois dígitos parecerão estar acesos o
tempo todo.
O fluxograma do programa pode ser visto na figura 2, enquanto na figura 3
o da rotina de interrupção.

84
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Figura 2

85
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Figura 3

No MPLAB, abra o arquivo Pisca LED IV.asm e salve-o com o nome de


Contador.asm.
A primeira alteração que faremos é na seção “VARIÁVEIS”, onde vamos
excluir a “CONT_EST_TMR0” .
Como teremos dois botões, vamos renomear as variáveis “DB_BTA” e
DB_BTB” para “DB1_BTA” e “DB1_BTB” e criar outras duas: “DB2_BTA” e “DB2_BTB”.
Criaremos uma variável chamada “UNIDADE” para a unidade do número
exibido no display e outra chamada “DEZENA” para a dezena.

86
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

;***********************************************************************************************
; VARIÁVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

W_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR W


STATUS_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR STATUS
FLAGS ;REGISTRADOR DE FLAGS
DB1_BTA ;PRIMEIRO REGISTRADOR DO DE-BOUNCING DO BOTÃO 1
DB1_BTB ;SEGUNDO REGISTRADOR DO DE-BOUNCING DO BOTÃO 1
DB2_BTA ;PRIMEIRO REGISTRADOR DO DE-BOUNCING DO BOTÃO 2
DB2_BTB ;SEGUNDO REGISTRADOR DO DE-BOUNCING DO BOTÃO 2

UNIDADE ;UNIDADE DO NUMERO EXIBIDO NO DISPLAY


DEZENA ;DEZENA DO NUMERO EXIBIDO NO DISPLAY

ENDC ;FIM DO BLOCO DE MEMORIA

;***********************************************************************************************

Na seção “CONSTANTES”, vamos excluir “INI_CONT_EST_TMR0” e


“INI_TMR0”, renomear as que inicializam as variáveis de “de-bouncing” do botão 1 e criar as que
inicializam as do botão 2:

;**********************************************************************************************
; CONSTANTES

INI_DB1_BTA EQU .255 ;VALOR QUE DB1_BTA INICIA


INI_DB1_BTB EQU .20 ;VALOR QUE DB1_BTB INICIA
INI_DB2_BTA EQU .255 ;VALOR QUE DB2_BTA INICIA
INI_DB2_BTB EQU .20 ;VALOR QUE DB2_BTB INICIA

;***********************************************************************************************
Na seção “SAÍDAS”, vamos definir a label “DIGITO_1” para o pino RA2 e
a label “DIGITO_2” para o pino RA3.
Relembrando que quando RA2 estiver em nível alto, o dígito 1 do display
estará ativo e quando RA3 estiver em nível alto, o dígito 2 estará ativo:

;***********************************************************************************************
; SAÍDAS

#DEFINE DIGITO_1 PORTA,2 ;SE = 1, DIGITO 1 DO DISPLAY ATIVADO


#DEFINE DIGITO_2 PORTA,3 ;SE = 1, DIGITO 2 DO DISPLAY ATIVADO

;***********************************************************************************************
Na seção “ENTRADAS”, vamos definir a label “BOTAO_1” para o pino
RA0 e a label “BOTAO_2” para o pino RA1, conforme o esquema do circuito:
;***********************************************************************************************
; ENTRADAS

#DEFINE BOTAO_1 PORTA,0 ;BOTAO 1 LIGADO EM RA0


#DEFINE BOTAO_2 PORTA,1 ;BOTAO 2 LIGADO EM RA1

;***********************************************************************************************

87
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Na seção “FLAGS”, vamos excluir o flag “ESTADO_DO_LED”, renomear


o flag “SOLTAR_BOTAO” para “SOLTAR_BOTAO_1” e criar o flag “SOLTAR_BOTAO_2”:

;***********************************************************************************************
; FLAGS

#DEFINE SOLTAR_BOTAO_1 FLAGS,0 ;SE = 1 AGUARDA SOLTAR O BOTÃO 1


#DEFINE SOLTAR_BOTAO_2 FLAGS,1 ;SE = 1 AGUARDA SOLTAR O BOTÃO 2

;***********************************************************************************************

Chegamos à rotina de interrupção.


O Timer 0 será configurado para que gere uma interrupção a cada 5
milissegundos aproximadamente. Isto será feito depois, na seção de configuração dos registradores.
Na rotina de interrupção, iremos:
 Verificar qual dígito está ativo e desativá-lo;
 Ativar o outro dígito;
 Converter o valor numérico a ser exibido no display para o formato de 7
segmentos;
 Enviar este valor convertido para o PORTB, onde está ligado o display.
A rotina é igual até este ponto:

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

MOVWF W_TEMP ;SALVA W


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;CHAVEIA PARA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS
BTFSS INTCON,T0IF ;TMR0 ESTOUROU?
GOTO SAI_INT ;NAO, SAIR DA INTERRUPÇÃO
BCF INTCON,T0IF ;SIM

A seguir, vamos verificar qual o dígito que está ativo, desativá-lo, e ativar o
outro:

BTFSS DIGITO_1 ;DIGITO 1 ESTA ATIVADO?


GOTO DESATIVA_DIGITO_2 ;NAO, DESVIA
BCF DIGITO_1 ;SIM, DESATIVA DIGITO 1
CLRF PORTB ;TODOS OS BITS DO PORT B = 0
BSF DIGITO_2 ;ATIVA DIGITO 2
GOTO COPIAR_UNIDADE ;DESVIA

DESATIVA_DIGITO_2
BCF DIGITO_2 ;DESATIVA DIGITO 2
CLRF PORTB ;TODOS OS BITS DO PORT B = 0
BSF DIGITO_1 ;ATIVA DIGITO 1

Com a instrução BTFSS, testamos se o dígito 1 está ativado e, se estiver,


desativamo-lo com a instrução “BCF DIGITO_1” e ativamos o dígito 2 com a instrução
“BSF DIGITO_2”.

88
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Porém se o dígito 1 estiver desativado, significa que o dígito 2 está ativo e,


então, o programa é desviado para a label “DESATIVA_DIGITO_2”, onde o dígito 2 é desativado e
o dígito 1 é ativado.
Repare que antes de ativarmos o dígito, fazemos todos os bits do PORTB =
0 com a instrução CLRF, para que o dígito que será ativado não receba os valores do dígito que
acaba de ser desativado.
Os segmentos do display estão conectados no PORTB, da seguinte forma:

Segmento Bit
a RB0
b RB1
c RB2
d RB3
e RB4
f RB5
g RB6

Tabela 1
O display utilizado é do tipo catodo comum, portanto os segmentos são
ativados com nível alto. Sendo assim, os valores a serem escritos no PORTB para cada algarismo a
ser exibido no display são os constantes na tabela abaixo. O bit 7 do PORTB está desconectado,
portanto, seu valor é indiferente, pelo que lhe atribuímos o valor 0 para todos os algarismos.

Algarismo PORTB
0 00111111
1 00000110
2 01011011
3 01001111
4 01100110
5 01101101
6 01111101
7 00000111
8 01111111
9 01101111

Tabela 2

As figuras a seguir ilustram os valores da tabela anterior.

89
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

00111111 00000110

01011011 01001111

90
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

01100110 01101101

01111101 00000111

91
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

01111111 01101111

Após o dígito ser ativado, devemos enviar para o PORTB o valor referente
ao algarismo que deve ser exibido.
O dígito 1 do display exibe a dezena do número e o dígito 2, a unidade.
O algarismo da dezena está gravado na variável “DEZENA”, enquanto que
o da unidade está gravado na variável “UNIDADE”, porém, esses algarismos estão gravados nessas
variáveis no formato binário, conforme a tabela abaixo:

Algarismo Valor binário


0 00000000
1 00000001
2 00000010
3 00000011
4 00000100
5 00000101
6 00000110
7 00000111
8 00001000
9 00001001
Tabela 3

92
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Esses valores são diferentes dos que deve assumir o PORTB para que os
algarismos sejam exibidos no display de 7 segmentos, conforme a tabela comparativa abaixo:

Algarismo Valor binário PORTB


0 00000000 00111111
1 00000001 00000110
2 00000010 01011011
3 00000011 01001111
4 00000100 01100110
5 00000101 01101101
6 00000110 01111101
7 00000111 00000111
8 00001000 01111111
9 00001001 01101111

Tabela 4

Por isso, não podemos simplesmente copiar o valor da variável para o


PORTB.
Existe uma maneira prática de converter o valor binário para o
correspondente a 7 segmentos, que consiste em somar o valor binário ao Contador de Programa (PC
- Program Counter) para provocar um desvio no programa (Computed GOTO) para uma localidade
onde exista uma instrução que faça o programa retornar com o número já convertido no registrador
W.
O Contador de Programa (PC), armazena o endereço da próxima instrução
que será executada.
Quando o microcontrolador é ligado e também depois de um reset, o valor
do PC é igual a 0h (número 0 no formato hexadecimal), o que quer dizer que ele irá executar a
instrução gravada nessa localidade da memória de programa.
A cada instrução que é executada, o PC é incrementado em 1 unidade,
portanto, depois de executar a instrução contida no endereço 0h, ele irá executar a instrução contida
no endereço 1h, depois a do endereço 2h e assim por diante. Isso se as instruções contidas nesses
endereços não provocarem o desvio do programa para outra localidade da memória.
Um exemplo de instrução que provoca o desvio para outra localidade de
memória é a GOTO.
O PC do PIC16F628A é composto de 13 bits, sendo que os 8 bits menos
significativos (do 0 ao 7) compõe o registrador PCL. Este registrador pode ser acessado diretamente
para ser alterado. Os 5 bits mais significativos do PC (do 8 ao 12), chamados de PCH só podem ser
alterados indiretamente através do registrador PCLATH.
Quando uma instrução GOTO é executada, o endereço para onde o
programa deverá ser desviado consta na própria instrução sendo copiado para o PC; portanto, a
próxima instrução que será executada é aquela que consta na instrução GOTO.
Outro tipo de instrução que provoca um desvio no programa é a instrução
CALL.

93
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Quando uma instrução CALL é executada, o valor do PC (endereço da


próxima instrução após CALL) é salvo no “Stack” e em seguida o PC assume o endereço que
consta na instrução CALL (que é para onde o programa deve ser desviado).
Uma vez desviado o programa, assim que uma instrução RETURN ou
RETLW for executada, o endereço que estava salvo no “Stack” é recuperado para o PC e, portanto,
o programa retorna para que seja executada a instrução que estava após a instrução CALL.
A instrução GOTO serve para provocar um desvio no programa quando não
se pretende que ele retorne, enquanto que a instrução CALL desvia o programa para que ele possa
retornar posteriormente.
A instrução RETURN simplesmente provoca o retorno, enquanto a instrução
RETLW faz com que o programa retorne com o valor escrito após a instrução no registrador W. Por
exemplo: RETLW .10: retorna com o valor 10 decimal no registrador W.
O “Stack” tem 8 níveis. Se, por exemplo, uma instrução CALL tiver sido
executada e outra instrução CALL for executada antes de uma instrução RETURN ou RETLW, o
endereço de retorno da primeira instrução passa para o nível 2 do “Stack” enquanto o endereço de
retorno da segunda instrução assume o nível 1.
Quando for executada uma instrução RETURN ou RETLW, o programa
voltará para o endereço constante no nível 1, ou seja, para depois da segunda instrução CALL. Ao
mesmo tempo o endereço de retorno da primeira instrução CALL volta a assumir o nível 1. Quando
for executada outra instrução RETURN ou RETLW o programa voltará para depois da primeira
instrução CALL.
Se nós somarmos um número ao registrador PCL, estaremos alterando o PC
e, portanto, o microcontrolador irá executar a instrução que esteja localizada no endereço que o PC
assumir.
Por exemplo, suponhamos que o valor do PC seja: 0010h. Isto significa que
a próxima instrução a ser executada será a que está localizada neste endereço da memória de
programa.
Nesse número hexadecimal, os dois algarismos menos significativos (10)
são o PCL e os dois mais significativos são o PCH (00).
Se, por exemplo, nós somarmos o número 5h ao PCL, o PC irá assumir o
valor: 0015h e, então, ao invés de ser executada a instrução contida no endereço 0010h será
executada a contida no endereço 0015h.
Para somarmos um número a um registrador qualquer, precisamos,
primeiramente, fazer com que o registrador W assuma o valor desse número, através da instrução
MOVLW. Em seguida, através da instrução ADDWF, somamos o número ao registrador.
No exemplo citado, para somar o número 5 ao registrador PCL, fazemos
assim:
MOVLW .5 ;W = 5
ADDWF PCL,F ;PCL = PCL + 5

A instrução ADDWF soma o valor do registrador W ao registrador indicado,


no caso, o PCL. A letra F após a vírgula indica que o resultado da soma será salvo no próprio
registrador PCL.
O PCL é um registrador de 8 bits, e, portanto, o seu valor pode assumir, no
máximo, o valor 255 decimal.
Isto quer dizer que se o seu valor for, por exemplo, igual a 252 e somarmos
a ele o número 5, ele irá assumir o valor 1, pois, terá estourado (252 + 1 = 253 + 1 = 254 + 1 = 255
+ 1 = 0 + 1 = 1).

94
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Esse estouro do PCL provocado quando somamos um número a ele não faz
com que o PCH seja incrementado, como ocorre quando o PC é incrementado pelo ciclo de
instrução.
Desta forma, antes de somarmos um número ao PCL, devemos ter o cuidado
de verificar se isso não irá provocar o estouro do PCL, pois, se provocar, o programa será desviado
para uma localidade que não é a que queremos.
Por exemplo, vamos supor que o valor do PC seja 00FCh, onde o PCL está
com o valor hexadecimal FC que corresponde ao valor decimal 252.
Se nesse momento, somarmos ao PCL o valor 5, o PC irá assumir o seguinte
valor hexadecimal: 0001h. Mas, o correto seria que ele assumisse o valor 0101h, o que não ocorreu
porque o estouro do PCL não provocou o incremento do PCH.
Ou seja, o programa seria desviado para o endereço 0001h quando
queríamos que fosse desviado para 0101h.
Se virmos que a soma ao PCL irá provocar o seu estouro, devemos, antes de
efetuar a soma, incrementar manualmente o PCH, através do registrador PCLATH, fazendo o seu
valor igual ao que deverá assumir o PCH após a soma. No momento da soma ao PCL, o PCH
assumirá o mesmo valor do PCLATH, pois, toda vez que ocorre uma alteração no PCL, o conteúdo
do PCLATH é copiado para o PCH.
Para verificarmos se uma soma ao PCL irá provocar o seu estouro,
verificamos as localidades que assumiram as instruções, depois que o código esteja montado.
Veremos como fazer isso ainda nesta parte do tutorial, quando o programa estiver pronto.
Voltemos ao programa!
Após o dígito 2 ser ativado, o programa é desviado para onde se encontra a
label “COPIAR_UNIDADE”.
Com a instrução MOVF, copiamos o valor da variável “UNIDADE” para o
registrador W:
COPIAR_UNIDADE
MOVF UNIDADE,W ;W = UNIDADE

Em seguida usamos a instrução CALL para desviar o programa para onde


está a label “CONVERTE_BINARIO_7_SEGMENTOS”:

CALL CONVERTE_BINARIO_7_SEGMENTOS ;CHAMA SUBROTINA

Nesta subrotina, usamos a instrução ADDWF para somar o valor da variável


“UNIDADE”, que está copiada no registrador W, ao PCL:

CONVERTE_BINARIO_7_SEGMENTOS
ADDWF PCL,F ;PCL = PCL + W
RETLW B'00111111' ;W = 0
RETLW B'00000110' ;W = 1
RETLW B'01011011' ;W = 2
RETLW B'01001111' ;W = 3
RETLW B'01100110' ;W = 4
RETLW B'01101101' ;W = 5
RETLW B'01111101' ;W = 6
RETLW B'00000111' ;W = 7
RETLW B'01111111' ;W = 8
RETLW B'01101111' ;W = 9

95
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Se o valor do registrador W for igual a 0, o valor do PCL e


consequentemente o do PC não mudará e, portanto, a próxima instrução que será executada é
RETLW B'00111111'. O programa, então, retornará com o este valor no registrador W, que
corresponde ao algarismo 0 no formato de 7 segmentos.
Se o valor da unidade for igual a 1, o valor do PCL aumentará 1 unidade e,
portanto, a instrução que será executada será a de baixo, ou seja, a que faz com que o programa
retorne com o valor B'00000110' no registrador W correspondente ao número 1 e assim por diante.
Após retornar, será executada a instrução que vem depois da instrução
CALL e como já temos o número convertido no registrador W, basta copiá-lo para o PORTB:

MOVWF PORTB ;PORTB RECEBE O VALOR CONVERTIDO

O mesmo procedimento é adotado quando o dígito 1 for ativado, com a


diferença de que é o valor da variável “DEZENA” que é copiado e convertido.
Em seguida, desviamos o programa para a subrotina “SAI_INT” para que
retorne da interrupção.
Concluímos, assim, a rotina de interrupção:

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

MOVWF W_TEMP ;SALVA W


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;CHAVEIA PARA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS
BTFSS INTCON,T0IF ;TMR0 ESTOUROU?
GOTO SAI_INT ;NAO, DESVIA
BCF INTCON,T0IF ;SIM, APAGA FLAG
BTFSS DIGITO_1 ;DIGITO 1 ESTA ATIVADO?
GOTO DESATIVA_DIGITO_2 ;NAO, DESVIA
BCF DIGITO_1 ;SIM, DESATIVA DIGITO 1
CLRF PORTB ;TODOS OS BITS DO PORT B = 0
BSF DIGITO_2 ;ATIVA DIGITO 2
GOTO COPIAR_UNIDADE ;DESVIA

DESATIVA_DIGITO_2
BCF DIGITO_2 ;DESATIVA DIGITO 2
CLRF PORTB ;TODOS OS BITS DO PORT B = 0
BSF DIGITO_1 ;ATIVA DIGITO 1
MOVF DEZENA,W ;W = DEZENA
CALL CONVERTE_BINARIO_7_SEGMENTOS ;CHAMA SUBROTINA
MOVWF PORTB ;PORTB RECEBE O VALOR CONVERTIDO
GOTO SAI_INT ;DESVIA

COPIAR_UNIDADE
MOVF UNIDADE,W ;W = UNIDADE
CALL CONVERTE_BINARIO_7_SEGMENTOS ;CHAMA SUBROTINA
MOVWF PORTB ;PORTB RECEBE O VALOR CONVERTIDO
GOTO SAI_INT ;DESVIA

CONVERTE_BINARIO_7_SEGMENTOS
ADDWF PCL,F ;PCL = PCL + W
RETLW B'00111111' ;W = 0

96
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

RETLW B'00000110' ;W = 1
RETLW B'01011011' ;W = 2
RETLW B'01001111' ;W = 3
RETLW B'01100110' ;W = 4
RETLW B'01101101' ;W = 5
RETLW B'01111101' ;W = 6
RETLW B'00000111' ;W = 7
RETLW B'01111111' ;W = 8
RETLW B'01101111' ;W = 9

SAI_INT
SWAPF STATUS_TEMP,W ;SWAP EM STATUS_TEMP
MOVWF STATUS ;RECUPERA STATUS
SWAPF W_TEMP,F ;SWAP EM W_TEMP
SWAPF W_TEMP,W ;RECUPERA W
RETFIE ;RETORNA DA INTERRUPÇÃO

;***********************************************************************************************
O próximo passo é configurar os registradores de uso específico.
Devemos configurar o Timer 0 para estourar a cada 5 milissegundos,
aproximadamente.
Como ele será incrementado pelo ciclo de instrução, que dura 1
microssegundo, se definirmos o seu prescaler para 1:16, ele será incrementado a cada 16
microssegundos e portanto irá estourar a cada 4 milissegundos aproximadamente (16 x 256).
Esse valor é próximo o suficiente daquele que precisávamos.
Para configurar o prescaler do Timer 0 para 1:16, devemos ajustar os valores
dos bits 2, 1 e 0 do registrador OPTION_REG para 0,1 e 1, respectivamente, conforme consta no
datasheet do PIC16F628A.
Temos de configurar também o registrador TRISA para definirmos os pinos
RA0 e RA1 como entrada e os pinos RA2 e RA3 como saída. Os demais pinos do PORTA,
deixaremos como entrada.
Devemos configurar ainda o registrador TRISB para definirmos os pinos
RB0 a RB6 como saída deixando o RB7 como entrada, pois não é usado.
As demais configurações são as mesmas do programa anterior:

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11010011' ;W = B'11010011'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO, PRESCALER 1:16
MOVLW B'11110011' ;W = B'11110011'
MOVWF TRISA ;CONFIGURA RA2 E RA3 COMO SAÍDAS E DEMAIS COMO ENTRADAS
MOVLW B'10000000'
MOVWF TRISB ;CONFIGURA O PINO RB7 COMO ENTRADA E DEMAIS COMO SAIDAS
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O
MOVLW B'11100000'
MOVWF INTCON ;HABILITA INTERRUPÇÃO DO TIMER 0

;***********************************************************************************************

97
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

A seguir, em “INICIALIZAÇÃO DAS VARIÁVEIS”, inicializamos as


variáveis de de-bouncing dos botões com os seus devidos valores e as variáveis “FLAGS”,
“UNIDADE” e “DEZENA” com o valor 0 (CLRF):

;***********************************************************************************************
; INICIALIZACAO DAS VARIAVEIS

CLRF FLAGS ;INICIALIZA FLAGS


MOVLW INI_DB1_BTA ;W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
MOVLW INI_DB1_BTB ;W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
MOVLW INI_DB2_BTA ;W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
MOVLW INI_DB2_BTB ;W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
CLRF UNIDADE ;INICIALIZA UNIDADE
CLRF DEZENA ;INICIALIZA DEZENA

;***********************************************************************************************
Chegamos à rotina principal do programa.
Aqui vamos introduzir um conceito conhecido como “Programação
Estruturada”, onde, na rotina principal, chamamos através da instrução CALL as subrotinas do
programa.
Teremos duas subrotinas que serão as que tratam os dois botões.
Nossa rotina “PRINCIPAL” ficará assim:

;***********************************************************************************************
PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


CALL TRATA_BOTAO_1 ;CHAMA SUBROTINA
CALL TRATA_BOTAO_2 ;CHAMA SUBROTINA
GOTO PRINCIPAL ;DESVIA

;***********************************************************************************************

Na subrotina “TRATA_BOTAO_1”, primeiramente testamos se o flag


“SOLTAR_BOTAO_1” está setado. Se estiver, testamos o botão para verificar se ele está solto. Se
estiver, apagamos o flag. Se não estiver solto, retornamos através da instrução RETURN.
Se o flag “SOLTAR_BOTAO_1” não estiver setado, testamos o botão. Se
ele não estiver pressionado, retornamos. Se estiver pressionado, decrementamos a variável
“DB1_BTA” dando início ao de-bouncing e em seguida retornamos.
O retorno depois de decrementar as variáveis do de-bouncing é para que o
programa não fique “preso” dentro da rotina de de-bouncing.
Quando terminar o de-bouncing, teremos chegado a este ponto:

;***********************************************************************************************

TRATA_BOTAO_1

BTFSS SOLTAR_BOTAO_1 ;AGUARDA SOLTAR O BOTAO 1?


GOTO TESTA_BOTAO_1 ;NAO, DESVIA
BTFSS BOTAO_1 ;SIM, O BOTÃO 1 ESTÁ SOLTO?

98
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

RETURN ;NAO, RETORNA


BCF SOLTAR_BOTAO_1 ;SIM, APAGA FLAG

TESTA_BOTAO_1
BTFSC BOTAO_1 ;O BOTÃO 1 ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB_1 ;NÃO, DESVIA
DECFSZ DB1_BTA,F ;SIM, DECREMENTA DB1_BTA. DB1_BTA = 0?
RETURN ;NAO,RETORNA
MOVLW INI_DB1_BTA ;SIM, W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
DECFSZ DB1_BTB,F ;DECREMENTA DB1_BTB. DB1_BTB = 0?
RETURN ;NAO, RETORNA
MOVLW INI_DB1_BTB ;SIM, W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB

Já podemos setar o flag para aguardar soltar o botão 1:

BSF SOLTAR_BOTAO_1 ;SETA FLAG PARA AGUARDAR SOLTAR O BOTÃO 1

Como ficou definido, a cada vez que o botão 1 for pressionado o número
exibido no display será incrementado.
O número é composto de dois dígitos: unidade e dezena. Para incrementar
esse número, devemos incrementar a unidade, o que fazemos com a instrução INCF:

INCF UNIDADE,F ;INCREMENTA UNIDADE

Essa instrução aumenta em 1 unidade o valor do registrador “UNIDADE”.


A letra F depois do nome do registrador faz com que o resultado seja salvo no próprio registrador.
O valor da unidade deve variar entre 0 e 9 e, portanto, depois de
incrementá-la, devemos verificar se o seu valor é igual a 10. Se não for, retornamos e, se for,
devemos zerá-la e incrementar a dezena.
Para verificar se o valor do registrador “UNIDADE” é igual a 10, podemos
usar a instrução XORWF.
Essa instrução efetua a operação lógica XOR entre o registrador W e outro
registrador. Essa operação é realizada entre cada bit equivalente dos dois registradores (bit0 com
bit0, bit1 com bit1, etc...). Se os bits equivalentes forem iguais, o bit equivalente do resultado será
igual a 0 e se forem diferentes será igual a 1, de forma que se todos os bits equivalentes do
registrador W e do outro registrador forem iguais, todos os bits do resultado serão iguais a 0 e,
portanto, o valor numérico do resultado é igual a 0.
Ou seja, se os valores do registrador W e o do outro registrador forem iguais,
o valor do resultado da operação XOR entre eles será igual a 0.
Começamos por fazer o valor do registrador W igual a 10:
MOVLW .10 ;W = 10
Em seguida, efetuamos a operação XOR entre o registrador “UNIDADE” e
o W, salvando o resultado no W (para não alterar o valor do “UNIDADE”):

XORWF UNIDADE,W ;W = W XOR UNIDADE

Se o valor da variável “UNIDADE” for igual a 10 (valor do W), o resultado


da operação será igual a 0.
Para sabermos se o resultado da operação foi igual a 0, testamos o bit “Z”
do registrador “STATUS”:

99
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

BTFSS STATUS,Z ;UNIDADE = 10?

O bit ”Z” serve para indicar quando o resultado de determinadas operações,


entre elas a XOR, foi igual a 0, sendo que, nesse caso, o valor deste bit será igual a 1, caso contrário
será igual a 0.
Então, se o valor do bit “Z” for igual a 0, significa que o resultado da
operação XOR entre o registrador “UNIDADE” e o registrador W, não foi igual a 0, e portanto, o
valor do registrador “UNIDADE” é diferente de 10. Nesse caso, a próxima linha do programa, onde
está a instrução RETURN, será executada.

RETURN ;NAO, RETORNA

Mas, se o bit “Z” for igual a 1, então, o valor da unidade é igual a 10 e,


então, a linha com a instrução RETURN é pulada.
Como o valor da unidade chegou a 10, devemos zerá-la e incrementar o
valor da dezena:

CLRF UNIDADE ;SIM, UNIDADE = 0


INCF DEZENA,F ;INCREMENTA DEZENA

Da mesma forma que fizemos com a unidade, após incrementarmos a


dezena, devemos verificar se o seu valor é igual a 10. Se não for, basta retornar e, se for igual a 10,
devemos zerá-la e depois retornar, pois não temos a centena para incrementar:
MOVLW .10 ;W = 10
XORWF DEZENA,W ;W = W XOR DEZENA
BTFSS STATUS,Z ;DEZENA = 10?
RETURN ;NAO, RETORNA
CLRF DEZENA ;SIM, DEZENA = 0
RETURN ;RETORNA

Com isso, concluímos a subrotina “TRATA_BOTAO_1” que ficou assim:

;***********************************************************************************************

TRATA_BOTAO_1

BTFSS SOLTAR_BOTAO_1 ;AGUARDA SOLTAR O BOTAO 1?


GOTO TESTA_BOTAO_1 ;NAO, DESVIA
BTFSS BOTAO_1 ;SIM, O BOTÃO 1 ESTÁ SOLTO?
RETURN ;NAO, RETORNA
BCF SOLTAR_BOTAO_1 ;SIM, APAGA FLAG

TESTA_BOTAO_1
BTFSC BOTAO_1 ;O BOTÃO 1 ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB_1 ;NÃO, DESVIA
DECFSZ DB1_BTA,F ;SIM, DECREMENTA DB1_BTA. DB1_BTA = 0?
RETURN ;NAO,RETORNA
MOVLW INI_DB1_BTA ;SIM, W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
DECFSZ DB1_BTB,F ;DECREMENTA DB1_BTB. DB1_BTB = 0?
RETURN ;NAO, RETORNA
MOVLW INI_DB1_BTB ;SIM, W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
BSF SOLTAR_BOTAO_1 ;SETA FLAG PARA AGUARDAR SOLTAR O BOTÃO 1

100
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

INCF UNIDADE,F ;INCREMENTA UNIDADE


MOVLW .10 ;W = 10
XORWF UNIDADE,W ;W = W XOR UNIDADE
BTFSS STATUS,Z ;UNIDADE = 10?
RETURN ;NAO, RETORNA
CLRF UNIDADE ;SIM, UNIDADE = 0
INCF DEZENA,F ;INCREMENTA DEZENA
MOVLW .10 ;W = 10
XORWF DEZENA,W ;W = W XOR DEZENA
BTFSS STATUS,Z ;DEZENA = 10?
RETURN ;NAO, RETORNA
CLRF DEZENA ;SIM, DEZENA = 0
RETURN ;RETORNA

REINC_CONT_DEB_1
MOVLW INI_DB1_BTA ;W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
MOVLW INI_DB1_BTB ;W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
RETURN ;RETORNA

;**********************************************************************************************

A seguir, vem a subrotina “TRATA_BOTAO_2” que é igual à do botão 1


(porém fazendo referência ao botão 2) até este ponto:

;***********************************************************************************************

TRATA_BOTAO_2

BTFSS SOLTAR_BOTAO_2 ;AGUARDA SOLTAR O BOTAO 2?


GOTO TESTA_BOTAO_2 ;NAO, DESVIA
BTFSS BOTAO_2 ;SIM, O BOTÃO 2 ESTÁ SOLTO?
RETURN ;NAO, RETORNA
BCF SOLTAR_BOTAO_2 ;SIM, APAGA FLAG

TESTA_BOTAO_2
BTFSC BOTAO_2 ;O BOTÃO 2 ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB_2 ;NÃO, DESVIA
DECFSZ DB2_BTA,F ;SIM, DECREMENTA DB2_BTA. DB2_BTA = 0?
RETURN ;NAO,RETORNA
MOVLW INI_DB2_BTA ;SIM, W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
DECFSZ DB2_BTB,F ;DECREMENTA DB2_BTB. DB2_BTB = 0?
RETURN ;NAO, RETORNA
MOVLW INI_DB2_BTB ;SIM, W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
BSF SOLTAR_BOTAO_2 ;SETA FLAG PARA AGUARDAR SOLTAR O BOTÃO 2

O botão 2 a cada vez que for pressionado decrementará o número mostrado


no display.
Devemos, portanto, decrementar a unidade e para isso usaremos a instrução
DECF, que diminui em 1 unidade o valor de um registrador:

DECF UNIDADE,F ;DECREMENTA UNIDADE

101
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Precisamos verificar qual o valor do registrador UNIDADE, após ser


decrementado.
Se o valor do registrador antes de ser decrementado era 0, seu valor após o
decremento será 255, já que se trata de um registrador de 8 bits, cujo valor varia de 0 a 255, porém,
nesse caso, a unidade deverá assumir o valor 9.
O que faremos é verificar se o valor da unidade é igual a 255. Se não for,
retornamos e, se for 255, tornamos o valor da unidade igual a 9:
MOVLW .255 ;W = 255
XORWF UNIDADE,W ;W = W XOR UNIDADE
BTFSS STATUS,Z ;UNIDADE = 255?
RETURN ;NAO, RETORNA
MOVLW .9 ;SIM, W = 9
MOVWF UNIDADE ;UNIDADE = 9

Em seguida decrementamos a dezena, e adotamos o mesmo procedimento se


o seu valor for igual a 255:

DECF DEZENA,F ;DECREMENTA DEZENA


MOVLW .255 ;W = 255
XORWF DEZENA,W ;W = W XOR DEZENA
BTFSS STATUS,Z ;DEZENA = 255?
RETURN ;NAO, RETORNA
MOVLW .9 ;SIM, W = 9
MOVWF DEZENA ;DEZENA = 9
RETURN ;RETORNA

Concluímos assim a subrotina “TRATA_BOTAO_2”:

;**********************************************************************************************

TRATA_BOTAO_2

BTFSS SOLTAR_BOTAO_2 ;AGUARDA SOLTAR O BOTAO 2?


GOTO TESTA_BOTAO_2 ;NAO, DESVIA
BTFSS BOTAO_2 ;SIM, O BOTÃO 2 ESTÁ SOLTO?
RETURN ;NAO, RETORNA
BCF SOLTAR_BOTAO_2 ;SIM, APAGA FLAG

TESTA_BOTAO_2
BTFSC BOTAO_2 ;O BOTÃO 2 ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB_2 ;NÃO, DESVIA
DECFSZ DB2_BTA,F ;SIM, DECREMENTA DB2_BTA. DB2_BTA = 0?
RETURN ;NAO,RETORNA
MOVLW INI_DB2_BTA ;SIM, W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
DECFSZ DB2_BTB,F ;DECREMENTA DB2_BTB. DB2_BTB = 0?
RETURN ;NAO, RETORNA
MOVLW INI_DB2_BTB ;SIM, W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
BSF SOLTAR_BOTAO_2 ;SETA FLAG PARA AGUARDAR SOLTAR O BOTÃO 2
DECF UNIDADE,F ;DECREMENTA UNIDADE
MOVLW .255 ;W = 255
XORWF UNIDADE,W ;W = W XOR UNIDADE
BTFSS STATUS,Z ;UNIDADE = 255?

102
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

RETURN ;NAO, RETORNA


MOVLW .9 ;SIM, W = 9
MOVWF UNIDADE ;UNIDADE = 9
DECF DEZENA,F ;DECREMENTA DEZENA
MOVLW .255 ;W = 255
XORWF DEZENA,W ;W = W XOR DEZENA
BTFSS STATUS,Z ;DEZENA = 255?
RETURN ;NAO, RETORNA
MOVLW .9 ;SIM, W = 9
MOVWF DEZENA ;DEZENA = 9
RETURN ;RETORNA

REINC_CONT_DEB_2
MOVLW INI_DB2_BTA ;W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
MOVLW INI_DB2_BTB ;W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
RETURN ;RETORNA

;**********************************************************************************************

O nosso programa está pronto, tendo ficado assim:

;***********************************************************************************************
; PROGRAMA: CONTADOR CRESCENTE E DECRESCENTE
; VERSÃO 1.0
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /
;***********************************************************************************************

#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A

;***********************************************************************************************
; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF
& _LVP_OFF & _CP_OFF & DATA_CP_OFF

;**********************************************************************************************
; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA

;***********************************************************************************************
; VARIÁVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

W_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR W


STATUS_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR STATUS
FLAGS ;REGISTRADOR DE FLAGS
DB1_BTA ;PRIMEIRO REGISTRADOR DO DE-BOUNCING DO BOTÃO 1
DB1_BTB ;SEGUNDO REGISTRADOR DO DE-BOUNCING DO BOTÃO 1
DB2_BTA ;PRIMEIRO REGISTRADOR DO DE-BOUNCING DO BOTÃO 2
DB2_BTB ;SEGUNDO REGISTRADOR DO DE-BOUNCING DO BOTÃO 2

103
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

UNIDADE ;UNIDADE DO NUMERO EXIBIDO NO DISPLAY


DEZENA ;DEZENA DO NUMERO EXIBIDO NO DISPLAY

ENDC ;FIM DO BLOCO DE MEMORIA

;***********************************************************************************************
; CONSTANTES

INI_DB1_BTA EQU .255 ;VALOR QUE DB1_BTA INICIA


INI_DB1_BTB EQU .20 ;VALOR QUE DB1_BTB INICIA
INI_DB2_BTA EQU .255 ;VALOR QUE DB2_BTA INICIA
INI_DB2_BTB EQU .20 ;VALOR QUE DB2_BTB INICIA

;***********************************************************************************************
; SAÍDAS

#DEFINE DIGITO_1 PORTA,2 ;SE = 1, DIGITO 1 DO DISPLAY ATIVADO


#DEFINE DIGITO_2 PORTA,3 ;SE = 1, DIGITO 2 DO DISPLAY ATIVADO

;***********************************************************************************************
; ENTRADAS

#DEFINE BOTAO_1 PORTA,0 ;BOTAO 1 LIGADO EM RA0


#DEFINE BOTAO_2 PORTA,1 ;BOTAO 2 LIGADO EM RA1

;***********************************************************************************************
; FLAGS
#DEFINE SOLTAR_BOTAO_1 FLAGS,0 ;SE = 1 AGUARDA SOLTAR O BOTÃO 1
#DEFINE SOLTAR_BOTAO_2 FLAGS,1 ;SE = 1 AGUARDA SOLTAR O BOTÃO 2

;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO

GOTO INICIO ;DESVIA PARA INICIO

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

MOVWF W_TEMP ;SALVA W


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;CHAVEIA PARA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS
BTFSS INTCON,T0IF ;TMR0 ESTOUROU?
GOTO SAI_INT ;NAO, DESVIA
BCF INTCON,T0IF ;SIM, APAGA FLAG
BTFSS DIGITO_1 ;DIGITO 1 ESTA ATIVADO?
GOTO DESATIVA_DIGITO_2 ;NAO, DESVIA
BCF DIGITO_1 ;SIM, DESATIVA DIGITO 1
CLRF PORTB ;TODOS OS BITS DO PORT B = 0
BSF DIGITO_2 ;ATIVA DIGITO 2
GOTO COPIAR_UNIDADE ;DESVIA

DESATIVA_DIGITO_2
BCF DIGITO_2 ;DESATIVA DIGITO 2

104
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

CLRF PORTB ;TODOS OS BITS DO PORT B = 0


BSF DIGITO_1 ;ATIVA DIGITO 1
MOVF DEZENA,W ;W = DEZENA
CALL CONVERTE_BINARIO_7_SEGMENTOS ;CHAMA SUBROTINA
MOVWF PORTB ;PORTB RECEBE O VALOR CONVERTIDO
GOTO SAI_INT ;DESVIA

COPIAR_UNIDADE
MOVF UNIDADE,W ;W = UNIDADE
CALL CONVERTE_BINARIO_7_SEGMENTOS ;CHAMA SUBROTINA
MOVWF PORTB ;PORTB RECEBE O VALOR CONVERTIDO
GOTO SAI_INT ;DESVIA

CONVERTE_BINARIO_7_SEGMENTOS
ADDWF PCL,F ;PCL = PCL + W
RETLW B'00111111' ;W = 0
RETLW B'00000110' ;W = 1
RETLW B'01011011' ;W = 2
RETLW B'01001111' ;W = 3
RETLW B'01100110' ;W = 4
RETLW B'01101101' ;W = 5
RETLW B'01111101' ;W = 6
RETLW B'00000111' ;W = 7
RETLW B'01111111' ;W = 8
RETLW B'01101111' ;W = 9

SAI_INT
SWAPF STATUS_TEMP,W ;SWAP EM STATUS_TEMP
MOVWF STATUS ;RECUPERA STATUS
SWAPF W_TEMP,F ;SWAP EM W_TEMP
SWAPF W_TEMP,W ;RECUPERA W
RETFIE ;RETORNA DA INTERRUPÇÃO

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11010011' ;W = B'11010011'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO, PRESCALER 1:16
MOVLW B'11110011' ;W = B'11110011'
MOVWF TRISA ;CONFIGURA RA2 E RA3 COMO SAÍDAS E DEMAIS COMO ENTRADAS
MOVLW B'10000000'
MOVWF TRISB ;CONFIGURA O PINO RB7 COMO ENTRADA E DEMAIS COMO SAIDAS
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O
MOVLW B'11100000'
MOVWF INTCON ;HABILITA INTERRUPÇÃO DO TIMER 0

;***********************************************************************************************
; INICIALIZACAO DAS VARIAVEIS

CLRF FLAGS ;INICIALIZA FLAGS


MOVLW INI_DB1_BTA ;W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA

105
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

MOVLW INI_DB1_BTB ;W = INI_DB1_BTB


MOVWF DB1_BTB ;INICIALIZA DB1_BTB
MOVLW INI_DB2_BTA ;W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
MOVLW INI_DB2_BTB ;W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
CLRF UNIDADE ;INICIALIZA UNIDADE
CLRF DEZENA ;INICIALIZA DEZENA

;***********************************************************************************************
PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


CALL TRATA_BOTAO_1 ;CHAMA SUBROTINA
CALL TRATA_BOTAO_2 ;CHAMA SUBROTINA
GOTO PRINCIPAL ;DESVIA

;***********************************************************************************************
TRATA_BOTAO_1

BTFSS SOLTAR_BOTAO_1 ;AGUARDA SOLTAR O BOTAO 1?


GOTO TESTA_BOTAO_1 ;NAO, DESVIA
BTFSS BOTAO_1 ;SIM, O BOTÃO 1 ESTÁ SOLTO?
RETURN ;NAO, RETORNA
BCF SOLTAR_BOTAO_1 ;SIM, APAGA FLAG

TESTA_BOTAO_1
BTFSC BOTAO_1 ;O BOTÃO 1 ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB_1 ;NÃO, DESVIA
DECFSZ DB1_BTA,F ;SIM, DECREMENTA DB1_BTA. DB1_BTA = 0?
RETURN ;NAO,RETORNA
MOVLW INI_DB1_BTA ;SIM, W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
DECFSZ DB1_BTB,F ;DECREMENTA DB1_BTB. DB1_BTB = 0?
RETURN ;NAO, RETORNA
MOVLW INI_DB1_BTB ;SIM, W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
BSF SOLTAR_BOTAO_1 ;SETA FLAG PARA AGUARDAR SOLTAR O BOTÃO 1
INCF UNIDADE,F ;INCREMENTA UNIDADE
MOVLW .10 ;W = 10
XORWF UNIDADE,W ;W = W XOR UNIDADE
BTFSS STATUS,Z ;UNIDADE = 10?
RETURN ;NAO, RETORNA
CLRF UNIDADE ;SIM, UNIDADE = 0
INCF DEZENA,F ;INCREMENTA DEZENA
MOVLW .10 ;W = 10
XORWF DEZENA,W ;W = W XOR DEZENA
BTFSS STATUS,Z ;DEZENA = 10?
RETURN ;NAO, RETORNA
CLRF DEZENA ;SIM, DEZENA = 0
RETURN ;RETORNA

REINC_CONT_DEB_1
MOVLW INI_DB1_BTA ;W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
MOVLW INI_DB1_BTB ;W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
RETURN ;RETORNA

106
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

;********************************************************************************************
TRATA_BOTAO_2

BTFSS SOLTAR_BOTAO_2 ;AGUARDA SOLTAR O BOTAO 2?


GOTO TESTA_BOTAO_2 ;NAO, DESVIA
BTFSS BOTAO_2 ;SIM, O BOTÃO 2 ESTÁ SOLTO?
RETURN ;NAO, RETORNA
BCF SOLTAR_BOTAO_2 ;SIM, APAGA FLAG

TESTA_BOTAO_2
BTFSC BOTAO_2 ;O BOTÃO 2 ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB_2 ;NÃO, DESVIA
DECFSZ DB2_BTA,F ;SIM, DECREMENTA DB2_BTA. DB2_BTA = 0?
RETURN ;NAO,RETORNA
MOVLW INI_DB2_BTA ;SIM, W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
DECFSZ DB2_BTB,F ;DECREMENTA DB2_BTB. DB2_BTB = 0?
RETURN ;NAO, RETORNA
MOVLW INI_DB2_BTB ;SIM, W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
BSF SOLTAR_BOTAO_2 ;SETA FLAG PARA AGUARDAR SOLTAR O BOTÃO 2
DECF UNIDADE,F ;DECREMENTA UNIDADE
MOVLW .255 ;W = 255
XORWF UNIDADE,W ;W = W XOR UNIDADE
BTFSS STATUS,Z ;UNIDADE = 255?
RETURN ;NAO, RETORNA
MOVLW .9 ;SIM, W = 9
MOVWF UNIDADE ;UNIDADE = 9
DECF DEZENA,F ;DECREMENTA DEZENA
MOVLW .255 ;W = 255
XORWF DEZENA,W ;W = W XOR DEZENA
BTFSS STATUS,Z ;DEZENA = 255?
RETURN ;NAO, RETORNA
MOVLW .9 ;SIM, W = 9
MOVWF DEZENA ;DEZENA = 9
RETURN ;RETORNA

REINC_CONT_DEB_2
MOVLW INI_DB2_BTA ;W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
MOVLW INI_DB2_BTB ;W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
RETURN ;RETORNA
;**********************************************************************************************

END ;FIM DO PROGRAMA

Vamos montar o programa.


No MPLAB, no menu “Project”, clique em “Project Wizard...”.
Na janela “Welcome”, clique em “Avançar”.
Na janela “Step One”, selecione o PIC16F628A e clique em “Avançar”.
Na janela “Step Two”, clique em “Avançar”.
Na janela “Step Three”, clique em “Browse”. Na janela que se abre, em
“Nome do arquivo”, escreva: “Contador” e clique em salvar.
Agora, na janela “Step Three”, clique em “Avançar”.

107
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Na janela “Step Four”, selecione o arquivo Contador.asm, clique em “Add”


e depois clique em “Avançar”.
Na janela “Summary”, clique em “Concluir”.
No menu “Project”, clique em “Build All”.
Na janela que se abre, clique em “Absolute”.
Verifique se a mensagem “BUILD SUCCEEDED” apareceu na janela
“Output” e, caso contrário, confira se o programa está exatamente igual.

Figura 4

Quando falamos sobre a operação de soma ao registrador PCL, dissemos


que após montar o programa, deveríamos verificar se a soma não iria estourar o PCL.
No menu “View”, clique em “Disassembly Listing”.
A primeira coluna corresponde ao endereço, em hexadecimal, que a
instrução ocupará na memória de programa. A segunda coluna corresponde à instrução em
hexadecimal. A terceira coluna é a instrução em Assembly com os nomes dos registradores
substituídos pelos seus endereços na memória de dados.

108
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Figura 5

Repare que a instrução ADD PCL,F está localizada no endereço 01C.


Os dois dígitos menos significativos desse número (1C) correspondem ao
valor do registrador PCL, enquanto que o dígito mais significativo (0) é o valor do registrador PCH.
A última instrução RETLW está localizada no endereço 026.
Repare que não há estouro do PCL nesse trecho, pois, o valor do PCH é o
mesmo (0).
É isso que temos que verificar, ou seja, se o valor do PCH na localidade
onde está a instrução que efetua a soma de um valor ao PCL (no nosso caso, ADD PCL,F) e o valor
do PCH na última instrução da tabela são iguais.
Se houvesse estouro do PCL justamente nesse trecho, a maneira mais fácil
de resolver o problema seria reposicionando a tabela no programa, de forma que ficasse numa
posição onde não houvesse o estouro.
Agora vamos simular o programa.
No menu “File”, clique em “Open...”, localize e abra o arquivo
Contador.asm.
No menu “Debugger”, posicione o mouse em “Select Tool” e clique em
“MPLAB SIM”.
No menu “Debugger”, clique em “Settings...”. Na aba “Osc/Trace”, em
“Processor Frequency”, digite 4 e, em “Units”, selecione Mhz. Na Aba “Animation/Real Time
Updates”, selecione “Enable Realtime watch updates” e mova o cursor todo para a esquerda
(“Fastest”). Clique em “OK”.
No menu “View”, clique em “Watch”.
Adicione os registradores PORTA e PORTB em “Add SFR” e os
registradores FLAGS, UNIDADE e DEZENA em “Add Symbol”:

109
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Figura 6

No menu “Debugger”, posicione o mouse sobre “Stimulus” e clique em


“New Workbook”.
Na primeira linha, em “Pin/SFR”, selecione RA0, em “Action”, selecione
“Pulse Low”, em “Width”, escreva 100 e em “Units” selecione “ms” (millisecond). Faça o mesmo
na segunda linha para o RA1.
Na terceira linha, selecione o RA0 e, em “Action”, selecione “Set High”.
Faça o mesmo na quarta linha para o RA1. Na quinta linha, selecione o RA0 e, em “Action”,
selecione “Set Low”. Faça o mesmo na sexta linha para o RA1:

110
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Figura 7

Dessa forma quando clicarmos no botão “Fire” da primeira linha estaremos


simulando que o botão 1 foi pressionado e solto após 100 milissegundos. Clicando no botão “Fire”
da segunda linha, estaremos simulando que o botão 2 foi pressionado e solto após 100
milissegundos.
Definimos o tempo de 100 milissegundos para que o pino se mantenha em
nível baixo, pois esse tempo precisa ser superior ao tempo de de-bouncing que é de cerca de 50
milissegundos.
Os outros botões usaremos quando quisermos que os pinos fiquem fixos nos
níveis 0 ou 1.
Clique em “Save” e atribua um nome, por exemplo: Contador.
No menu “Window”, selecione a janela do programa (Contador.asm).
Vá clicando no botão “Step Into” da barra de ferramentas do simulador até o
cursor chegar à instrução CLRWDT.
Vá para a janela do “Stimulus”, selecionando-a no menu “Windows” e
clique nos botões “Fire” das terceiras e quarta linhas para levar RA0 e RA1 para nível alto,
simulando que os botões estão soltos.
Volte para a janela do programa, clique uma vez no botão “Step Into”.
Vá para a janela do “Watch” e confirme se RA0 e RA1 estão em nível alto:

111
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Figura 8

Volte para a janela do programa e clique novamente no botão “Step Into”.


Repare que o cursor foi para a subrotina “TRATA_BOTAO_1”, pois foi
executada a instrução CALL que chamou essa subrotina.
Continue clicando no botão “Step Into” e acompanhando a simulação do
programa. Repare que, como o botão 1 está solto, ele reinicia as variáveis do de-bouncing e tendo
encontrado uma instrução RETURN, retorna para a rotina principal, na próxima linha após a
instrução CALL.
Em seguida, ele irá executar a instrução que chama a subrotina do botão 2.
Depois de retornar para a rotina principal ele executa a instrução GOTO
PRINCIPAL, e recomeça o ciclo.
Vamos medir os tempos de “de-bouncing”.
Insira um “breakpoint” na linha que contem a instrução BSF
SOLTAR_BOTAO_1, na subrotina do botão 1.
No “Stimulus”, clique no botão “Fire” da quinta linha para fixarmos o nível
do RA0 em 0.
No menu “Debugger”, clique em “Stopwatch”.
Clique no botão “Run” da barra do simulador e quando o programa parar no
breakpoint, repare, no “Stopwatch” que o tempo que o “de-bouncing” demorou foi de 138
milissegundos, não sendo necessário um tempo tão longo.
Nem é necessário medir o tempo de “de-bouncing” do botão 2, pois, será o
mesmo.
Vamos ver se deixamos esse tempo próximo de 50 milissegundos,
diminuindo o valor de inicialização das variáveis DB1_BTB e DB2_BTB para 10 na seção
constantes.

112
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Após alterar o valor, devemos montar novamente o programa, clicando em


“Build All”, no menu “Project”.
Toda vez que montamos o projeto, o programa é resetado, portanto vá
clicando no botão “Step Into” até chegar à instrução CLRWDT.
Como os bits RA0 e RA1 são inicializados com o valor 0, clique no botão
“Fire” que fixa o valor do RA1 em 1, para simular que o botão 2 está solto.
Como vamos medir o tempo do “de-bouncing” do botão 1, podemos deixar
o valor do RA0 em 0, simulando que ele está pressionado.
No “Stopwatch”, clique em “Zero” e depois clique no botão “Run” da barra
do simulador. Quando o programa parar no “breakpoint”, veja, no “Stopwatch” que o “de-bouncing”
agora durou 69 milissegundos.
Vamos diminuir o valor das variáveis DB1_BTB e DB2_BTB, para 8 e
medir o tempo novamente.
Com este valor, o tempo do “de-bouncing” foi de 55 milissegundos, o que
está bom.
Vamos verificar se o contador está sendo incrementado e decrementado
corretamente.
Remova o “breakpoint”. No “Stimulus”, clique no botão “Fire” da 3ª linha
para setar o RA0.
Posicione as janelas de modo a poder ver as janelas do “Stimulus” e do
“Watch” ao mesmo tempo:

Figura 9

Clique no botão “Run” da barra do simulador.


No “Stimulus”, clique no botão “Fire” da primeira linha para simular que o
botão 1 foi pressionado e em seguida solto.

113
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Repare, no “Watch”, que o valor da unidade foi incrementado para 1:

Figura 10

Clique novamente no botão “Fire” da primeira linha e repare que a unidade


foi incrementada novamente, passando para 2.
Continue clicando nesse botão e repare que a unidade passa do valor 9 para
o valor 0 enquanto a dezena é incrementada para 1, ou seja, o valor do contador é igual a 10.
Agora, clique no botão “Fire” da segunda linha para simular que o botão 2
foi pressionado e solto em seguida. Repare que agora o contador é decrementado, voltando a
unidade para 9 e a dezena para 0.
Continue decrementando-o e repare que de 00 passa a 99.
Agora o incremente e veja que o seu valor volta a 00.
Incremente-o até que o seu valor seja igual a 19 e a seguir clique no botão
“Halt” do simulador.
Vamos conferir a rotina de interrupção.
Insira um breakpoint na linha da primeira instrução da rotina de interrupção
(MOVWF WTEMP).
Clique no botão “Run” da barra do simulador e quando o programa parar no
“breakpoint”, vá para a janela do “Stopwatch” e clique em “Zero”.
Clique no botão “Run” novamente e quando o programa parar outra vez no
“breakpoint”, verifique no “Stopwatch” que o tempo entre uma interrupção e outra foi de 4
milissegundos.
Vá clicando no botão “Step Into” e observando a execução da rotina de
interrupção.

114
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente

Repare que quando é a vez do dígito 1 estar ativo, o valor do registrador


DEZENA é somado ao PCL.
Como o valor da dezena é igual a 1, o programa é desviado para a linha que
contem a instrução RETLW referente a este valor.
Quando é a vez do dígito 2 estar ativo, é o valor do registrador UNIDADE
que é somado ao PCL e como o seu valor é igual a 9, o programa é desviado para a linha onde está a
instrução RETLW correspondente.
Depois de executar a instrução RETLW, o programa retorna e, em seguida,
copia o valor do W para o PORTB. Observe, no “Watch”, que o PORTB assume o valor referente ao
número que deve ser exibido no display de 7 segmentos.
Com isso concluímos a simulação do programa e podemos gravá-lo no
microcontrolador e testar o circuito na protoboard:

Figura 11

Aqui termina a 5ª parte deste tutorial. Na próxima parte, vamos utilizar a


memória EEPROM do PIC16F628A para que o valor do contador não seja perdido quando o
circuito é desligado.

115
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Parte 6

Contador Crescente e Decrescente II

O contador da parte anterior não retinha o seu valor quando a alimentação era
desligada, pois ele era salvo apenas na memória de dados, que é uma memória volátil, isto é, cujo
conteúdo só é mantido enquanto há energia elétrica.
Desta vez, vamos utilizar a memória EEPROM do PIC16F628A para manter
salvo o valor do contador mesmo que a alimentação seja desligada.
O PIC16F628A possui 128 localidades de memória EEPROM, cada uma com
8 bits. Essas localidades devem ser lidas ou escritas uma de cada vez, como ocorre na memória de
dados. Segundo informa a Microchip, fabricante desse microcontrolador, cada localidade dessas
suporta ser regravada cerca de um milhão de vezes, não havendo indicação do limite de número de
leituras. Iremos utilizar duas localidades: uma para o valor da unidade e outra para o valor da dezena.
Poderíamos escolher duas localidades para serem sempre as mesmas onde
seriam salvos os valores da unidade e da dezena e, dessa forma, essas localidades estariam
deterioradas após cerca de um milhão de vezes que os valores fossem salvos. Quando isso
acontecesse, teríamos de trocar o microcontrolador ou reescrever o programa para definir outras duas
localidades. Porém, se todas as vezes que salvarmos aqueles valores, alterarmos as localidades,
estaremos prolongando esse tempo.
As localidades da memória EEPROM do PIC16F628A não são acessadas
diretamente como as da memória de dados. Para ler ou escrever na memória EEPROM, utilizamos
quatro registradores de uso específico:
EEADR: Nesse registrador, devemos escrever o endereço da localidade da
memória EEPROM, que queremos ler ou escrever;
EEDATA: É o registrador onde devemos escrever o valor que queremos que
seja escrito na localidade que consta no EEADR. É também no EEDATA onde o valor escrito na
localidade da memória EEPROM aparece quando efetuamos uma leitura.
EECON1: É o registrador de controle de leitura e escrita.
EECON2: Esse registrador não existe fisicamente, ou seja, não é uma
localidade da memória. Seu nome é usado para prover uma forma de segurança contra escrita
indesejada, conforme veremos adiante.
O tempo necessário para se efetuar uma gravação na memória EEPROM é
tipicamente de 4 milissegundos. Já, no caso de leitura, ela ocorre em apenas 1 ciclo de instrução.
Para efetuarmos a leitura de uma localidade da memória EEPROM, devemos
adotar os seguintes procedimentos:

116
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

1º – Escrever no EEADR o endereço da localidade que queremos ler;


2º – Setar o bit “RD” (bit 0 do EECON1), para iniciar o processo de leitura;
O valor da localidade da memória EEPROM constará no EEDATA após um
ciclo de instrução, e o bit “RD” será zerado pelo microcontrolador.
Os registradores EEADR, EECON1 e EEDATA estão no banco 1 de memória
e, portanto, devemos selecionar esse banco antes de ler ou escrever nesses registradores.
Para efetuarmos a escrita de uma localidade da memória EEPROM, devemos
adotar os seguintes procedimentos:
1º – Escrever no EEADR o endereço da localidade que queremos escrever;
2º – Escrever no EEDATA, o valor que queremos escrever na localidade da
memória EEPROM;
3º – Setar o bit “WREN” (bit 2 do EECON1);
4º – Zerar o bit “GIE” (bit 7 do INTCON) para desabilitar as interrupções;
5º – Testar se o bit “GIE” foi zerado (recomendação da Microchip) e, se não
houver sido, repetir a instrução para zerá-lo;
6º – Escrever o número 55 (hexadecimal) no registrador EECON2;
7º – Escrever o número AA (hexadecimal) no registrador EECON2;
8º – Setar o bit “WR” (bit 1 do EECON1) para iniciar a escrita;
9º – Setar o bit “GIE” para habilitar as interrupções.
As interrupções são desabilitadas para evitar que interfiram durante o
processo de escrita.
Escrever 55h e depois AAh no EECON2 é uma senha para que ocorra a
gravação na EEPROM. Serve para evitar gravações indesejadas.
A gravação demora cerca de 4 milissegundos. Quando ela houver sido
concluída, o microcontrolador irá zerar o bit “WR” e irá setar o bit “EEIF” (bit 7 do registrador PIR1)
gerando uma interrupção, caso esteja habilitada, o que é feito setando o bit EEIE (bit 7 do registrador
PIE1).
Há duas formas de sabermos se a gravação terminou: testando o estado do bit
“WR” ou habilitando a interrupção. Seja qual for a forma escolhida, quando terminar a gravação,
devemos, por precaução, verificar se o valor gravado na localidade da memória EEPROM
corresponde ao que queríamos. Para isso, efetuamos uma leitura daquela localidade e comparamos
com o valor que mandamos gravar. Se a gravação não terminar dentro do tempo esperado, devemos
reiniciar o processo de gravação.
O circuito que iremos utilizar é o mesmo da parte anterior deste tutorial.
No MPLAB, abra o arquivo Contador.asm e salve-o como ContadorII.asm.
A primeira alteração que faremos é na seção “VARIÁVEIS”.

117
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Como neste programa iremos acessar com frequência registradores que se


encontram no banco 1 da memória de dados, e para que não precisemos trocar constantemente de
banco, vamos criar as variáveis a partir do endereço 0x70, pois, as variáveis que ocupam esse
endereço até o 0x7F podem ser acessadas a partir de qualquer banco. Repare que são apenas 16
localidades que tem esse atributo.
Iremos adicionar as variáveis ESC_EEPROM_A e ESC_EEPROM_B para a
contagem do tempo de espera pelo fim da escrita na EEPROM e a variável LOCAL_DA_UNIDADE,
que conterá o endereço da localidade da EEPROM onde está salvo o valor da unidade:
;***********************************************************************************************
; VARIÁVEIS

CBLOCK 0X70 ;ENDERECO DA PRIMEIRA VARIÁVEL

W_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR W


STATUS_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR STATUS
FLAGS ;REGISTRADOR DE FLAGS
DB1_BTA ;PRIMEIRO REGISTRADOR DO DE-BOUNCING DO BOTÃO 1
DB1_BTB ;SEGUNDO REGISTRADOR DO DE-BOUNCING DO BOTÃO 1
DB2_BTA ;PRIMEIRO REGISTRADOR DO DE-BOUNCING DO BOTÃO 2
DB2_BTB ;SEGUNDO REGISTRADOR DO DE-BOUNCING DO BOTÃO 2
UNIDADE ;UNIDADE DO NUMERO EXIBIDO NO DISPLAY
DEZENA ;DEZENA DO NUMERO EXIBIDO NO DISPLAY
ESC_EEPROM_A ;CONTA O TEMPO DE ESPERA DO FIM DA ESCRITA NA EEPROM
ESC_EEPROM_B ;CONTA O TEMPO DE ESPERA DO FIM DA ESCRITA NA EEPROM
LOCAL_DA_UNIDADE ;CONTEM O ENDEREÇO DA EEPROM ONDE ESTÁ SALVO O VALOR DA UNIDADE

ENDC ;FIM DO BLOCO DE MEMORIA

;***********************************************************************************************

Na seção “CONSTANTES”, acrescentaremos as de inicialização das variáveis


ESC_EEPROM_A e ESC_EEPROM_B:
;***********************************************************************************************
; CONSTANTES

INI_DB1_BTA EQU .255 ;VALOR QUE DB1_BTA INICIA


INI_DB1_BTB EQU .8 ;VALOR QUE DB1_BTB INICIA
INI_DB2_BTA EQU .255 ;VALOR QUE DB2_BTA INICIA
INI_DB2_BTB EQU .8 ;VALOR QUE DB2_BTB INICIA
INI_ESC_EEPROM_A EQU .255 ;VALOR QUE ESC_EEPROM_A INICIA
INI_ESC_EEPROM_B EQU .2 ;VALOR QUE ESC_EEPROM_B INICIA
;***********************************************************************************************

A seguir, na seção “FLAGS”, acrescentaremos os seguintes:


APAGAR_UNIDADE, GRAVAR_UNIDADE, GRAVAR_DEZENA, ESP_FIM_GRV_EEPROM,
CONF_GRV_EEPROM que serão usados na subrotina de gravação da EEPROM.

;***********************************************************************************************
; FLAGS

#DEFINE SOLTAR_BOTAO_1 FLAGS,0 ;SE = 1, AGUARDA SOLTAR O BOTÃO 1

118
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

#DEFINE SOLTAR_BOTAO_2 FLAGS,1 ;SE = 1, AGUARDA SOLTAR O BOTÃO 2


#DEFINE APAGAR_UNIDADE FLAGS,2 ;SE = 1, APAGAR A LOCALIDADE ANTERIOR DA UNIDADE
#DEFINE GRAVAR_UNIDADE FLAGS,3 ;SE = 1, GRAVAR UNIDADE NA EEPROM
#DEFINE GRAVAR_DEZENA FLAGS,4 ;SE = 1, GRAVAR DEZENA NA EEPROM
#DEFINE ESP_FIM_GRV_EEPROM FLAGS,5 ;SE = 1, ESPERA O FIM DA GRAVAÇÃO NA EEPROM
#DEFINE CONF_GRV_EEPROM FLAGS,6 ;SE = 1, CONFERE A GRAVAÇÃO NA EEPROM

;***********************************************************************************************
Para verificarmos se a gravação da EEPROM terminou, podemos escolher
entre testar o estado do bit “WR” até que ele seja zerado ou habilitar a interrupção de gravação
completa na EEPROM. Ficaremos com o teste do bit, por ser mais simples e, portanto, não iremos
fazer nenhuma alteração na rotina de interrupção.
A próxima alteração é na seção de Inicialização das Variáveis, onde
incluímos as instruções para a inicialização das variáveis criadas para a contagem do tempo de espera
pelo fim da escrita na EEPROM.
As variáveis UNIDADE e DEZENA não serão inicializadas com o valor 0,
como fizemos na parte anterior deste tutorial, mas, sim, com os valores que houverem sido salvos na
memória EEPROM, pois, queremos que quando o circuito seja ligado, ele mostre o valor que tinha
antes de ser desligado.
Como havíamos comentado, todas as vezes que formos salvar os valores da
unidade e da dezena, iremos alterar as localidades da memória EEPROM, a fim de prolongar sua vida
útil. Assim sendo, quando o programa for iniciado, ele terá de encontrar as localidades onde os
valores estão salvos.
Como o valor da unidade varia entre 0 e 9, apenas os quatro bits menos
significativos (bits 0 a 4) da localidade da memória são necessários para salvá-lo:

Valor em decimal Valor em binário Valor em decimal Valor em binário


0 0000 0000 5 0000 0101
1 0000 0001 6 0000 0110
2 0000 0010 7 0000 0111
3 0000 0011 8 0000 1000
4 0000 0100 9 0000 1001

Tabela 1

Sendo assim, usaremos os bits 5 a 8 para gravar um código que fará com que
o programa identifique a localidade onde está armazenado o valor da unidade.
O código que utilizaremos será este: 0110, ou seja, esses serão os valores dos
bits 5 a 8 da localidade onde o valor da unidade estiver salvo. Esse código será gravado todas as
vezes que salvarmos o valor da unidade, porém, na gravação do programa no microcontrolador,
devemos escrever o código na primeira localidade da memória EEPROM, caso contrário, não haverá
nenhuma localidade com o código para que o programa encontre na primeira vez que o
microcontrolador for ligado. Isso é feito com o uso da diretiva “DE”, desta forma:

119
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

;**********************************************************************************************
; INICIALIZAÇÃO DA EEPROM
ORG 2100 ;APONTA PARA O ENDEREÇO 00H DA EEPROM
DE B'01100000', .0 ;ESCREVE O NÚMERO BINÁRIO 01100000 NO ENDEREÇO 00H DA EEPROM
;E O NÚMERO DECIMAL 0, NA LOCALIDADE 01H
;**********************************************************************************************
A diretiva “DE” serve para escrevermos nas localidades da memória
EEPROM durante a gravação do programa no microcontrolador. Para o PIC16F628A, devemos nos
referir à primeira localidade da memória EEPROM como 2100. Se quisermos escrever em localidades
sequenciais, basta separar os valores por vírgula. Por exemplo, se escrevêssemos assim:
DE .1, .5, .3 escreveríamos os números decimais 1, 5 e 3, nas três localidades iniciadas pela que foi
apontada na diretiva “ORG”.
No nosso caso, o número binário 01100000 será escrito na localidade 00h e o
número decimal 0 na localidade seguinte, ou seja, a 01h. Assim, na primeira vez que o programa for
executado, ele encontrará o código na primeira localidade da memória EEPROM, iniciando a unidade
com o valor 0. Na localidade seguinte, escrevemos 0 para que a dezena também seja iniciada com o
valor 0.
Insira a seção “INICIALIZAÇÃO DA EEPROM”, entre as seções “BITS DE
CONFIGURAÇÃO” e “PAGINAÇÃO DE MEMÓRIA”.
Voltando à seção “INICIALIZAÇÃO DAS VARIÁVEIS”, o programa irá ler
a primeira localidade da memória EEPROM (00h) em busca do código. Se encontrar, ele saberá que
o valor da unidade está salvo naquela localidade e, portanto, que o valor da dezena está salvo na
próxima (01h). Se não encontrar, ele irá ler a terceira localidade (02h). Se encontrar o código, ele
saberá que o valor da unidade está salvo naquela localidade, e a dezena na próxima (03h) e assim por
diante até encontrar onde estão salvos os valores:

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW .254 ;W = 254
MOVWF EEADR ;INICIALIZA EEADR
INICIALIZA_UNIDADE_E_DEZENA
INCF EEADR,F ;INCREMENTA EEADR
INCF EEADR,F ;INCREMENTA EEADR
BSF EECON1,RD ;INICIA LEITURA
MOVF EEDATA,W ;W = VALOR DA LOCALIDADE DA EEPROM
ANDLW B'11110000' ;ZERA BITS 0 A 4 E MANTEM BITS 5 A 8
XORLW B'01100000' ;W = W XOR 01100000
BTFSS STATUS,Z ;W = 01100000?
GOTO INICIALIZA_UNIDADE_E_DEZENA ;NAO
MOVF EEADR,W ;SIM, W = EEADR
MOVWF LOCAL_DA_UNIDADE ;INICIALIZA A VARIÁVEL LOCAL_DA_UNIDADE
MOVF EEDATA,W ;W = VALOR DA LOCALIDADE DA EEPROM
ANDLW B'00001111' ;W = W AND B'00001111'
MOVWF UNIDADE ;INICIALIZA UNIDADE
INCF EEADR,F ;INCREMENTA EEADR
BSF EECON1,RD ;INICIA LEITURA
MOVF EEDATA,W ;W = VALOR DA LOCALIDADE DA EEPROM
MOVWF DEZENA ;INICIALIZA DEZENA
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA

120
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Primeiramente, inicializamos o EEADR com o valor 254.


A seguir, ele é incrementado duas vezes, assumindo o valor 0 (254 + 1 = 255
+ 1 = 0).
Após isso, o bit “RD” é setado, iniciando a leitura da localidade 00h da
memória EEPROM (valor do EEADR).
Em seguida, o conteúdo dessa localidade que estará disponível no EEDATA, é
copiado para o registrador W.
A seguir, com a instrução ANDLW, efetuarmos a operação lógica AND entre
o W e o número binário 11110000, para que os bits 0 a 4 sejam zerados e os bits 5 a 8 permaneçam
com o mesmo valor. O resultado é salvo no próprio W.
A operação AND é efetuada bit a bit entre os bits equivalentes do registrador
W e o número 11110000. O bit do resultado será igual a 1 somente se os dois bits forem iguais a 1.
Nas demais situações, o bit do resultado será 0.
Como no número binário 11110000, os bits 0 a 4 são iguais a zero, os bits 0 a
4 do resultado serão iguais a zero, não importando o valor destes bits no W.
Já os bits 5 a 8 do número binário 11110000 são iguais a 1, e, portanto, os
valores dos bits 5 a 8 do W serão mantidos, pois, se o bit do W é igual a 0, o bit do resultado será
igual a 0 e se o bit do W for igual a 1, o bit do resultado será igual a 1.
A seguir, através da instrução XORLW, efetuamos a operação lógica XOR
entre o registrador W e o número binário 01100000, salvando o resultado no W. Repare que os bits 5
a 8 desse número contém o código que indica a localidade de memória EEPROM onde está salvo o
valor da unidade.
A operação lógica XOR também é realizada bit a bit. Nela, o bit do resultado
será igual a 0 se os bits envolvidos tiverem o mesmo valor e será igual a 1 se forem diferentes. Se
todos os bits forem iguais, ou seja, se o registrador W tiver o valor 01100000, todos os bits do
resultado da operação XOR serão iguais a 0, o que significa que o resultado da operação foi igual a 0.
Para saber se o resultado da operação foi igual a 0, testamos o bit Z do
registrador STATUS. Se o resultado houver sido igual a 0, esse bit estará setado, caso contrário, estará
zerado.
A instrução BTFSS STATUS,Z testa o valor do bit Z do registrador STATUS.
Se o seu valor for 0, significa que o resultado da operação XOR não foi igual a 0, ou seja, que o
registrador W é diferente de 01100000, e, então, não é na localidade testada que está salvo o valor da
unidade. Neste caso, a próxima linha é executada, e o programa é desviado para onde está a label
“INICIALIZA_UNIDADE_E_DEZENA”, onde o EEADR é incrementado duas vezes, assumindo o
valor 02h. O processo se repete para verificar se é nessa localidade que está salvo o valor da unidade
e assim por diante até que ela seja encontrada.
Quando a localidade onde estiver salvo o valor da unidade for encontrada, o
resultado da operação XOR entre o registrador W e o número binário 01100000 será igual a 0,
portanto o bit Z do registrador STATUS estará setado. Com isso, a próxima linha depois da instrução
BTFSS STATUS,Z será pulada. Nesse caso, o valor do EEADR é copiado para o W e, em seguida,
deste para a variável LOCAL_DA_UNIDADE, inicializando essa variável.

121
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

A seguir, copiamos o valor do EEDATA para o W e, após isso, realizamos a


operação lógica AND entre o W e o número binário 00001111 para zerar os bits 5 a 8, onde está o
código, deixando apenas o valor da unidade nos bits 0 a 4. Então, copiamos o W para o registrador
UNIDADE, inicializando essa variável.
A seguir, o EEADR é incrementado, a fim de apontar para a próxima
localidade da memória EEPROM. Em seguida, o bit “RD” é setado, iniciando a leitura, e, na próxima
instrução, o valor do EEDATA, que agora contém a dezena, é copiado para o W. Depois, copiamos o
valor do W para o registrador DEZENA, inicializando essa variável.
Repare, que antes de acessarmos os registradores EEADR, EECON1 e
EEDATA, selecionamos o banco 1, pois eles estão localizados nesse banco.
Com isso, concluímos a seção de inicialização das variáveis, que ficou assim:
;***********************************************************************************************
; INICIALIZACAO DAS VARIAVEIS

CLRF FLAGS ;INICIALIZA FLAGS


MOVLW INI_DB1_BTA ;W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
MOVLW INI_DB1_BTB ;W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
MOVLW INI_DB2_BTA ;W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
MOVLW INI_DB2_BTB ;W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
MOVLW INI_ESC_EEPROM_A ;W = INI_ESC_EEPROM_A
MOVWF ESC_EEPROM_A ;INICIALIZA ESC_EEPROM_A
MOVLW INI_ESC_EEPROM_B ;W = INI_ESC_EEPROM_B
MOVWF ESC_EEPROM_B ;INICIALIZA ESC_EEPROM_B
BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA
MOVLW .254 ;W = 254
MOVWF EEADR ;INICIALIZA EEADR
INICIALIZA_UNIDADE_E_DEZENA
INCF EEADR,F ;INCREMENTA EEADR
INCF EEADR,F ;INCREMENTA EEADR
BSF EECON1,RD ;INICIA LEITURA
MOVF EEDATA,W ;W = VALOR DA LOCALIDADE DA EEPROM
ANDLW B'11110000' ;ZERA BITS 0 A 4 E MANTEM BITS 5 A 8
XORLW B'01100000' ;W = W XOR 01100000
BTFSS STATUS,Z ;W = 01100000?
GOTO INICIALIZA_UNIDADE_E_DEZENA ;NAO
MOVF EEADR,W ;SIM, W = EEADR
MOVWF LOCAL_DA_UNIDADE ;INICIALIZA A VARIÁVEL LOCAL_DA_UNIDADE
MOVF EEDATA,W ;W = VALOR DA LOCALIDADE DA EEPROM
ANDLW B'00001111' ;W = W AND B'00001111'
MOVWF UNIDADE ;INICIALIZA UNIDADE
INCF EEADR,F ;INCREMENTA EEADR
BSF EECON1,RD ;INICIA LEITURA
MOVF EEDATA,W ;W = VALOR DA LOCALIDADE DA EEPROM
MOVWF DEZENA ;INICIALIZA DEZENA
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA

;***********************************************************************************************

122
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Na rotina principal, acrescentaremos uma instrução CALL que chamará a


subrotina de gravação da EEPROM, ficando assim:

;***********************************************************************************************
PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


CALL TRATA_BOTAO_1 ;CHAMA SUBROTINA
CALL TRATA_BOTAO_2 ;CHAMA SUBROTINA
CALL GRAVA_EEPROM ;CHAMA SUBROTINA
GOTO PRINCIPAL ;DESVIA

;***********************************************************************************************

Na subrotina do botão 1, após a unidade ser incrementada, iremos setar os


flags APAGAR_UNIDADE, GRAVAR_UNIDADE e GRAVAR_DEZENA, pois todas as vezes que
o valor da unidade for alterado, iremos salvar os valores da unidade e da dezena, sendo que, também,
precisamos apagar a localidade anterior da unidade para limpar o código que a identificava.
;***********************************************************************************************
TRATA_BOTAO_1

BTFSS SOLTAR_BOTAO_1 ;AGUARDA SOLTAR O BOTAO 1?


GOTO TESTA_BOTAO_1 ;NAO, DESVIA
BTFSS BOTAO_1 ;SIM, O BOTÃO 1 ESTÁ SOLTO?
RETURN ;NAO, RETORNA
BCF SOLTAR_BOTAO_1 ;SIM, APAGA FLAG

TESTA_BOTAO_1
BTFSC BOTAO_1 ;O BOTÃO 1 ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB_1 ;NÃO, DESVIA
DECFSZ DB1_BTA,F ;SIM, DECREMENTA DB1_BTA. DB1_BTA = 0?
RETURN ;NAO,RETORNA
MOVLW INI_DB1_BTA ;SIM, W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
DECFSZ DB1_BTB,F ;DECREMENTA DB1_BTB. DB1_BTB = 0?
RETURN ;NAO, RETORNA
MOVLW INI_DB1_BTB ;SIM, W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
BSF SOLTAR_BOTAO_1 ;SETA FLAG PARA AGUARDAR SOLTAR O BOTÃO 1
INCF UNIDADE,F ;INCREMENTA UNIDADE
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
MOVLW .10 ;W = 10
XORWF UNIDADE,W ;W = W XOR UNIDADE
BTFSS STATUS,Z ;UNIDADE = 10?
RETURN ;NAO, RETORNA
CLRF UNIDADE ;SIM, UNIDADE = 0
INCF DEZENA,F ;INCREMENTA DEZENA
MOVLW .10 ;W = 10
XORWF DEZENA,W ;W = W XOR DEZENA
BTFSS STATUS,Z ;DEZENA = 10?
RETURN ;NAO, RETORNA
CLRF DEZENA ;SIM, DEZENA = 0
RETURN ;RETORNA

123
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

REINC_CONT_DEB_1
MOVLW INI_DB1_BTA ;W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
MOVLW INI_DB1_BTB ;W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
RETURN ;RETORNA

;********************************************************************************************

Na subrotina do botão 2, também iremos setar esses mesmos flags após a


unidade ser decrementada:
;********************************************************************************************
TRATA_BOTAO_2

BTFSS SOLTAR_BOTAO_2 ;AGUARDA SOLTAR O BOTAO 2?


GOTO TESTA_BOTAO_2 ;NAO, DESVIA
BTFSS BOTAO_2 ;SIM, O BOTÃO 2 ESTÁ SOLTO?
RETURN ;NAO, RETORNA
BCF SOLTAR_BOTAO_2 ;SIM, APAGA FLAG

TESTA_BOTAO_2
BTFSC BOTAO_2 ;O BOTÃO 2 ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB_2 ;NÃO, DESVIA
DECFSZ DB2_BTA,F ;SIM, DECREMENTA DB2_BTA. DB2_BTA = 0?
RETURN ;NAO,RETORNA
MOVLW INI_DB2_BTA ;SIM, W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
DECFSZ DB2_BTB,F ;DECREMENTA DB2_BTB. DB2_BTB = 0?
RETURN ;NAO, RETORNA
MOVLW INI_DB2_BTB ;SIM, W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
BSF SOLTAR_BOTAO_2 ;SETA FLAG PARA AGUARDAR SOLTAR O BOTÃO 2
DECF UNIDADE,F ;DECREMENTA UNIDADE
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
MOVLW .255 ;W = 255
XORWF UNIDADE,W ;W = W XOR UNIDADE
BTFSS STATUS,Z ;UNIDADE = 255?
RETURN ;NAO, RETORNA
MOVLW .9 ;SIM, W = 9
MOVWF UNIDADE ;UNIDADE = 9
DECF DEZENA,F ;DECREMENTA DEZENA
MOVLW .255 ;W = 255
XORWF DEZENA,W ;W = W XOR DEZENA
BTFSS STATUS,Z ;DEZENA = 255?
RETURN ;NAO, RETORNA
MOVLW .9 ;SIM, W = 9
MOVWF DEZENA ;DEZENA = 9
RETURN ;RETORNA

REINC_CONT_DEB_2
MOVLW INI_DB2_BTA ;W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
MOVLW INI_DB2_BTB ;W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
RETURN ;RETORNA
;**********************************************************************************************

124
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

A seguir, vamos escrever a subrotina de gravação da EEPROM.


Quando o valor da unidade for alterado, seja por ter sido incrementado, seja
por ter sido decrementado, iremos salvar os valores da unidade e da dezena em duas novas
localidades da memória EEPROM.
Antes de salvá-los, iremos apagar a localidade onde, anteriormente, estava
escrito o valor da unidade, pois, precisamos apagar o código que identificava aquela localidade.
Portanto, são três etapas de escrita na EEPROM:
1) Escrever o número 0 na localidade anterior da unidade;
2) Escrever o valor da unidade com o código na nova localidade;
3) Escrever o valor da dezena na nova localidade.
Nas rotinas dos botões, depois que o valor da unidade é alterado, setamos os
flags APAGAR_UNIDADE, GRAVAR_UNIDADE e GRAVAR_DEZENA.
Na rotina de gravação da EEPROM, depois de escrevermos o número 0 na
localidade anterior da unidade, iremos zerar o flag APAGAR_UNIDADE. Depois de escrevermos o
valor da unidade na nova localidade, iremos zerar o flag GRAVAR_UNIDADE e depois de
escrevermos o valor da dezena na nova localidade, iremos zerar o flag GRAVAR_DEZENA.
Depois de iniciada cada gravação setaremos o flag
ESP_FIM_GRV_EEPROM, para que o programa seja desviado para onde está a label
“TESTA_FIM_DA_GRAVACAO” onde iremos verificar se a gravação terminou, testando o bit
“WR”, já que uma gravação demora cerca de 4 milissegundos e só poderemos iniciar outra gravação
depois de que a anterior esteja concluída.
Depois de efetuar as três gravações, setamos o flag CONF_GRV_EEPROM
para que o programa seja desviado para onde está a label “CONFERE_DEZENA” a partir de onde
iremos conferir se os valores foram gravados corretamente.
A primeira instrução testa o flag que informa se o programa está aguardando a
conclusão de uma gravação na EEPROM:
GRAVA_EEPROM
BTFSC ESP_FIM_GRV_EEPROM ;AGUARDA O FIM DA GRAVACAO NA EEPROM?

Se esse flag estiver setado, o programa é desviado para onde está a label
“TESTA_FIM_DA_GRAVACAO”:
GOTO TESTA_FIM_DA_GRAVACAO ;SIM
Se o flag estiver zerado, testamos o flag que informa se já podemos conferir a
gravação:
BTFSC CONF_GRV_EEPROM ;NAO, CONFERIR A GRAVAÇÃO?
Se esse flag estiver setado, o programa é desviado para onde está a label
“CONFERE_DEZENA”:
GOTO CONFERE_DEZENA ;SIM
Se o flag estiver zerado, testamos o flag que informa se é a vez de apagar a
localidade anterior da unidade:

125
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

BTFSC APAGAR_UNIDADE ;NAO, APAGAR A LOCALIDADE ANTERIOR DA UNIDADE?


Se esse flag estiver setado, o programa é desviado para onde está a label
APAGA_UNIDADE_ANTERIOR:
GOTO APAGA_UNIDADE_ANTERIOR ;SIM
Se o flag estiver zerado, testamos o flag que informa se é a vez de gravar a
unidade na nova localidade:
BTFSC GRAVAR_UNIDADE ;NAO, GRAVAR UNIDADE?
Se esse flag estiver setado, o programa é desviado para onde está a label
GRAVA_A_UNIDADE:
GOTO GRAVA_A_UNIDADE ;SIM
Se o flag estiver zerado, testamos o flag que informa se é a vez de gravar a
dezena na nova localidade:
BTFSC GRAVAR_DEZENA ;NAO, GRAVAR DEZENA?
Se esse flag estiver setado, o programa é desviado para onde está a label
GRAVA_A_DEZENA:
GOTO GRAVA_A_DEZENA ;SIM
Se o flag estiver zerado, retornamos:
RETURN ;NAO
Observe que, nesse último caso, como nenhum dos flags estava setado, não há
nada para se fazer na subrotina de gravação da EEPROM.
Quando o flag “APAGAR_UNIDADE” está setado, o programa é desviado
para onde está a label “APAGA_UNIDADE_ANTERIOR”. Nesse trecho do programa, iremos apagar
a localidade onde anteriormente estava gravado o valor da unidade, escrevendo nela o valor 0 (zero).
Não precisamos apagar a localidade anterior da dezena, pois, ela não contêm o código de localização.
Primeiramente, copiamos o valor da variável LOCAL_DA_UNIDADE para o
EEADR para que este aponte para o endereço onde está gravada a unidade:
APAGA_UNIDADE_ANTERIOR
BANCO_1 ;SELECIONA BANCO 1 DE MEMÓRIA
MOVF LOCAL_DA_UNIDADE,W ;W = ENDEREÇO DA EEPROM ONDE ESTÁ GRAVADA A UNIDADE
MOVWF EEADR ;EEADR RECEBE ENDERECO
A seguir, zeramos o EEDATA:
CLRF EEDATA ;EEDATA = 0
Em seguida, setamos o bit WREN (bit 2 do EECON1) para habilitar a escrita
na EEPROM:
BSF EECON1,WREN ;HABILITA ESCRITA NA EEPROM
A seguir, zeramos o bit GIE (bit 7 do INTCON), para desabilitar as
interrupções:
BCF INTCON,GIE ;DESABILITA AS INTERRUPÇÕES
Após isso, testamos esse bit para confirmarmos que ele foi zerado:
BTFSC INTCON,GIE ;BIT GIE ESTÁ ZERADO?

126
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Se ele não houver sido zerado, executamos novamente a instrução BCF


INTCON,GIE, para zerá-lo:
GOTO $-2 ;DESVIA O PROGRAMA PARA 2 LINHAS ACIMA
A instrução GOTO $-2, desvia o programa para 2 linhas acima, onde está a
instrução BCF INTCON,GIE. O número após o cifrão indica a ordem da linha para a qual o
programa irá desviar. Se fosse, por exemplo, $+3, ele seria desviado para a 3ª linha abaixo. Esse
número deve estar no formato hexadecimal. Por exemplo, se escrevermos GOTO $+16, o programa
será desviado para a 22ª linha abaixo, pois, 16 no sistema hexadecimal é igual a 22 no sistema
decimal. Se quiséssemos que o programa fosse desviado para a 16ª linha abaixo, teríamos que
escrever GOTO $+10, pois, 10, no sistema hexadecimal equivale ao número 16 no sistema decimal.
Confirmado que o bit GIE foi zerado, escrevemos o número 55 e depois o
número AA hexadecimais no EECON2:
MOVLW 0x55 ;W = NUMERO 55 HEXADECIMAL
MOVWF EECON2 ;EECON2 = 55 HEXADECIMAL
MOVLW 0xAA ;W = NUMERO AA HEXADECIMAL
MOVWF EECON2 ;EECON = AA HEXADECIMAL

Em seguida, setamos o bit “WR” (bit 1 do EECON1) para iniciar a gravação:


BSF EECON1,WR ;INICIA A GRAVAÇÃO
A seguir, setamos o bit “GIE” para habilitar as interrupções:
BSF INTCON,GIE ;HABILITA INTERRUPÇÕES
Após isso, zeramos o flag “APAGAR_UNIDADE”:
BCF APAGAR_UNIDADE ;APAGA O FLAG

Por fim, setamos o flag “ESP_FIM_GRV_EEPROM”, selecionamos o banco


0 e retornamos:
BSF ESP_FIM_GRV_EEPROM ;SETA O FLAG PARA ESPERAR PELO FIM DA GRAVAÇÃO
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA
Na próxima vez que o programa entrar na subrotina de gravação da EEPROM,
ele encontrará o flag “ESP_FIM_GRV_EEPROM” setado e será desviado para onde está a label
TESTA_FIM_DA_GRAVACAO.
Nesse trecho do programa, iremos testar, durante cerca de 10 milissegundos,
se o bit “WR” foi zerado. Esse bit é zerado pelo microcontrolador quando termina o processo de
gravação da EEPROM, que dura cerca de 4 milissegundos.
Para a contagem do tempo de 10 milissegundos, iremos usar as variáveis
ESC_EEPROM_A e ESC_EEPROM_B. A variável ESC_EEPROM_A será decrementada todas as
vezes que o bit “WR” for testado e não estiver zerado. Quando essa variável chegar a 0, ela será
reiniciada e a variável ESC_EEPROM_B será decrementada. Quando essa última chegar a 0, terá
passado cerca de 10 milissegundos.

127
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Se passar 10 milissegundos sem que o bit “WR” tenha sido zerado, ou seja,
sem que a gravação tenha sido concluída, iremos setar os flags APAGAR_UNIDADE,
GRAVAR_UNIDADE e GRAVAR_DEZENA para reiniciar todo o processo de gravação e
apagaremos os flags “ESP_FIM_GRV_EEPROM” e “CONF_GRV_EEPROM” (este último estará
setado se a demora ocorreu na gravação da dezena).
Raramente isso irá ocorrer, mas temos de contar com essa situação.
Quando o bit “WR” for zerado, apagaremos o flag
“ESP_FIM_GRV_EEPROM”. Em seguida, iremos zerar o bit “WREN” para desabilitar a gravação
da EEPROM. A seguir, reiniciaremos as variáveis de contagem do tempo e retornaremos:
TESTA_FIM_DA_GRAVACAO
BANCO_1 ;SELECIONA O BANCO 1 DE MEMÓRIA
BTFSC EECON1,WR ;A GRAVAÇÃO TERMINOU?
GOTO DEC_CONT_TEMPO_FIM_ESCRITA ;NAO, DESVIA
BCF ESP_FIM_GRV_EEPROM ;SIM, APAGA FLAG
BCF EECON1,WREN ;DESABILITA ESCRITA NA EEPROM
MOVLW INI_ESC_EEPROM_A ;W = INI_ESC_EEPROM_A
MOVWF ESC_EEPROM_A ;REINICIA ESC_EEPROM_A
MOVLW INI_ESC_EEPROM_B ;W = INI_ESC_EEPROM_B
MOVWF ESC_EEPROM_B ;REINICIA ESC_EEPROM_B
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA
DEC_CONT_TEMPO_FIM_ESCRITA
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
DECFSZ ESC_EEPROM_A,F ;DECREMENTA ESC_EEPROM_A. ESC_EEPROM_A = 0?
RETURN ;NAO, RETORNA
MOVLW INI_ESC_EEPROM_A ;SIM, W = INI_ESC_EEPROM_A
MOVWF ESC_EEPROM_A ;REINICIA ESC_EEPROM_A
DECFSZ ESC_EEPROM_B,F ;DECREMENTA ESC_EEPROM_B.ESC_EEPROM_B = 0?
RETURN ;NAO, RETORNA
MOVLW INI_ESC_EEPROM_B ;SIM, W = INI_ESC_EEPROM_B
MOVWF ESC_EEPROM_B ;REINICIA ESC_EEPROM_B
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
BCF ESP_FIM_GRV_EEPROM ;APAGA FLAG DE ESPERA PELO FIM DA GRAVACAO
BCF CONF_GRV_EEPROM ;APAGA O FLAG PARA CONFERIR A GRAVAÇÃO DA EEPROM
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA

Repare que antes de retornar, selecionamos o banco 0, já que havíamos


selecionado o banco 1 anteriormente. Isso porque, quando o programa retornar, ele irá executar a
subrotina do botão 1, onde é testado um bit do PORTA, que se encontra no banco 0. É claro que
poderíamos ter selecionado o banco 0 no começo subrotina do botão.
Concluída a gravação da EEPROM que apagou a localidade anterior da
unidade, na próxima vez que o programa entrar na rotina de gravação da EEPROM, ele irá encontrar
setado o flag GRAVAR_UNIDADE e será desviado para onde está a label GRAVA_A_UNIDADE.
Nesse trecho do programa, iremos gravar o valor da unidade em uma nova
localidade da memória EEPROM, juntamente com o código para que ela seja encontrada na
inicialização do programa.
Nesse ponto do programa, O EEADR está com o valor do endereço onde
estava gravada anteriormente a unidade, então, devemos incrementar o EEADR duas vezes para que
ele aponte para o endereço da nova localidade onde iremos gravar a unidade:

128
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

GRAVA_A_UNIDADE
BANCO_1 ;SELECIONA O BANCO 1 DE MEMÓRIA
INCF EEADR,F ;INCREMENTA EEADR
INCF EEADR,F ;INCREMENTA EEADR
O registrador EEADR é de 8 bits, o que significa que seu valor pode variar de
0 a 255, porém, no PIC16F628A, existem somente 128 localidades de memória EEPROM. Por isso, o
valor do EEADR deverá variar entre 0 e 127. Por esse motivo, depois de incrementar o EEADR,
testamos se o seu valor é igual a 128. Se for, zeramos o EEADR:
MOVLW .128 ;W = 128
XORWF EEADR,W ;W = W XOR EEADR
BTFSS STATUS,Z ;EEADR = 128?
GOTO CONT_GRV_UNIDADE ;NAO, DESVIA
CLRF EEADR ;SIM, EEADR = 0

A seguir, copiamos o valor da unidade para o EEDATA:


CONT_GRV_UNIDADE
MOVF UNIDADE,W ;W = UNIDADE
MOVWF EEDATA ;EEDATA = UNIDADE
Em seguida, copiamos o número binário 01100000 para o W e, depois,
efetuamos a operação lógica OR, entre o W e o EEDATA, através da instrução IORWF, salvando o
resultado no EEDATA:
MOVLW B'01100000' ;W = 01100000
IORWF EEDATA,F ;EEDATA = EEDATA OR 01100000
A operação lógica OR é efetuada bit a bit entre o W, cujo valor é igual ao
número binário 01100000, e o EEDATA. Quando pelo menos um dos bits for igual a 1, o bit do
resultado será igual a 1. Se os dois bits forem iguais a 0, o bit do resultado será igual a 0.
Nos bits 0 a 4 do EEDATA, está o valor da unidade, que copiamos antes.
Como os bits 0 a 4 do número binário 01100000 são iguais a 0, o valor dos bits 0 a 4 do EEDATA
serão mantidos, pois, se o bit do EEDATA é igual a 0, o bit do resultado será igual a 0 e se o bit do
EEDATA for igual a 1, o do resultado será igual a 1.
Como os bits 5 a 8 do EEDATA são iguais a 0, pois o valor da unidade varia
de 0 a 9 (veja a tabela 1), os bits 5 a 8 do número binário 01100000 serão mantidos.
Dessa forma, após a execução da instrução IORWF, nos bits 0 a 4 do
EEDATA estará o valor da unidade e nos bits 5 a 8 estará o código que identifica a localidade da
memória EEPROM onde estará gravada a unidade.
A seguir, escrevemos a sequência de instruções para a gravação da EEPROM:
BSF EECON1,WREN ;HABILITA ESCRITA NA EEPROM
BCF INTCON,GIE ;DESABILITA AS INTERRUPÇÕES
BTFSC INTCON,GIE ;BIT GIE ESTÁ ZERADO?
GOTO $-2 ;DESVIA O PROGRAMA PARA 2 LINHAS ACIMA
MOVLW 0x55 ;W = NUMERO 55 HEXADECIMAL
MOVWF EECON2 ;EECON2 = 55 HEXADECIMAL
MOVLW 0xAA ;W = NUMERO AA HEXADECIMAL
MOVWF EECON2 ;EECON2 = AA HEXADECIMAL
BSF EECON1,WR ;INICIA A GRAVAÇÃO
BSF INTCON,GIE ;HABILITA INTERRUPÇÕES
A seguir, zeramos o flag “GRAVAR_UNIDADE”, setamos o flag
“ESP_FIM_GRV_EEPROM” e retornamos:

129
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

BCF GRAVAR_UNIDADE ;APAGA O FLAG


BSF ESP_FIM_GRV_EEPROM ;SETA O FLAG PARA ESPERAR PELO FIM DA GRAVAÇÃO
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
RETURN ;RETORNA
Na próxima vez que o programa entrar na subrotina de gravação da EEPROM,
ele irá encontrar o flag “ESP_FIM_GRV_EEPROM” setado e então será desviado para onde está a
label “TESTA_FIM_DA_GRAVACAO”.
Depois que o flag “ESP_FIM_GRV_EEPROM” houver sido apagado, na
próxima vez que o programa entrar na subrotina da gravação da EEPROM, ele irá encontrar o flag
GRAVAR_DEZENA setado e será desviado para onde está a label “GRAVA_A_DEZENA”.
Nesse trecho do programa, iremos efetuar a gravação do valor da dezena na
localidade de memória EEPROM que vem em seguida àquela onde gravamos a unidade.
Portanto, a primeira instrução que escrevemos é a que incrementa o EEADR:

GRAVA_A_DEZENA
BANCO_1 ;SELECIONA O BANCO 1 DE MEMÓRIA
INCF EEADR,F ;INCREMENTA EEADR
A seguir, copiamos o valor da dezena apara o registrador EEDATA:
MOVF DEZENA,W ;W = DEZENA
MOVWF EEDATA ;EEDATA = DEZENA
Em seguida, escrevemos as instruções que realizam a gravação na EEPROM:
BSF EECON1,WREN ;HABILITA ESCRITA NA EEPROM
BCF INTCON,GIE ;DESABILITA AS INTERRUPÇÕES
BTFSC INTCON,GIE ;BIT GIE ESTÁ ZERADO?
GOTO $-2 ;DESVIA O PROGRAMA PARA 2 LINHAS ACIMA
MOVLW 0x55 ;W = NUMERO 55 HEXADECIMAL
MOVWF EECON2 ;EECON2 = 55 HEXADECIMAL
MOVLW 0xAA ;W = NUMERO AA HEXADECIMAL
MOVWF EECON2 ;EECON2 = AA HEXADECIMAL
BSF EECON1,WR ;INICIA A GRAVAÇÃO
BSF INTCON,GIE ;HABILITA INTERRUPÇÕES

Após, zeramos o flag “GRAVAR_DEZENA” e setamos os flags


“ESP_FIM_GRV_EEPROM” e “CONF_GRV_EEPROM”, retornando em seguida:
BCF GRAVAR_DEZENA ;APAGA O FLAG
BSF ESP_FIM_GRV_EEPROM ;SETA O FLAG PARA ESPERAR PELO FIM DA GRAVAÇÃO
BSF CONF_GRV_EEPROM ;SETA O FLAG PARA CONFERIR A GRAVAÇÃO DA EEPROM
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
RETURN ;RETORNA
Na próxima vez que o programa entrar na subrotina de gravação da EEPROM,
ele irá encontrar o flag “ESP_FIM_GRV_EEPROM” setado e então será desviado para onde está a
label “TESTA_FIM_DA_GRAVACAO”.
Depois que o flag “ESP_FIM_GRV_EEPROM” houver sido apagado, na
próxima vez que o programa entrar na subrotina da gravação da EEPROM, ele irá encontrar o flag
“CONF_GRV_EEPROM” setado e então será desviado para onde está a label
“CONFERE_DEZENA”.

130
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Neste trecho do programa, vamos conferir se o valor da dezena gravado na


EEPROM está correto.
Setamos o bit “RD” e após 1 ciclo de instrução teremos, no EEDATA, o valor
da localidade onde foi gravada a dezena.
CONFERE_DEZENA
BANCO_1 ;SELECIONA O BANCO 1 DE MEMÓRIA
BSF EECON1,RD ;INICIA LEITURA

A seguir, copiamos o valor da dezena para o W:


MOVF DEZENA,W ;W = DEZENA
Em seguida efetuamos a operação lógica XOR entre o W e o registrador
EEDATA, para verificarmos se são iguais:
XORWF EEDATA,W ;W = W XOR EEDATA
Testamos o bit Z do registrador STATUS, o qual estará setado se eles forem
iguais:
BTFSC STATUS,Z ;VALOR DA DEZENA FOI GRAVADO CORRETAMENTE?
Estando setado, desviamos o programa para onde está a label
“CONFERE_APAGAMENTO”:
GOTO CONFERE_APAGAMENTO ;SIM
Porém, se o bit “Z” estiver zerado, significa que o valor gravado não está
igual ao da dezena, portanto apagamos o flag “CONF_GRV_EEPROM, setamos os flags
APAGAR_UNIDADE, GRAVAR_UNIDADE e GRAVAR_DEZENA para que o processo de
gravação seja repetido”.
BCF CONF_GRV_EEPROM ;APAGA FLAG
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA

Em “CONFERE_APAGAMENTO”, iremos conferir se a localidade onde


estava gravada anteriormente a unidade foi de fato apagado.
Como, neste ponto, o EEADR está apontando para o endereço atual a dezena,
pois acabamos de conferi-la, precisamos decrementá-lo três vezes para que ele aponte para a
localidade onde anteriormente estava gravada a unidade, conforme pode ser comprovado no esquema
abaixo:
Unidade Anterior Dezena Anterior Unidade Atual Dezena Atual

Como o EEADR é um registrador de 8 bits, cujo valor pode variar entre 0 e 255 e,
supondo que seu valor seja 1 (unidade atualmente na localidade 00 e dezena na 01), após decrementá-lo três vezes,
ele irá exibir o valor 254 (1 – 1 = 0; 0 – 1 = 255; 255 – 1 = 254), mas, como só existem 128 localidades de memória
EEPROM no PIC16F628A, o valor do EEADR deve variar entre 0 a 127. Qualquer valor além disso é inválido, pois,
corresponde a uma localidade que não existe na EEPROM. Por esse motivo, após decrementarmos três vezes o
EEADR, iremos testar seu valor. Se ele for igual a 254, iremos fazê-lo igual a 126, pois é esse valor que ele deve
assumir após ser decrementado três vezes partindo do valor 1 (1 – 1 = 0; 0 – 1 = 127; 127 – 1 = 126).

131
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Pode ser que você esteja se perguntando o que ocorreria se o valor do EEADR fosse 0 ou
2.
Isso não é possível, pois, neste ponto do programa, o EEADR aponta para o endereço
onde a dezena está gravada atualmente. A dezena estará sempre gravada numa localidade ímpar (01, 03, 05, etc.),
pois, ela é gravada pela primeira vez na localidade 01, durante a gravação do programa no microcontrolador, por
meio da diretiva “DE”. A partir daí, ela sempre será gravada duas localidades após a anterior, na subrotina de
gravação da EEPROM.
Por esse motivo, após decrementarmos o EEADR, testamos se o seu valor é igual a 254 e,
se for, o fazemos igual a 126.

CONFERE_APAGAMENTO
DECF EEADR,F ;DECREMENTA EEADR
DECF EEADR,F ;DECREMENTA EEADR
DECF EEADR,F ;DECREMENTA EEADR
MOVLW .254 ;W = 254
XORWF EEADR,W ;W = W XOR EEADR
BTFSS STATUS,Z ;EEADR = 254?
GOTO CONT_CONFERENCIA ;NAO, DESVIA
MOVLW .126 ;SIM, W = 126
MOVWF EEADR ;EEADR = 126

A seguir, setamos o bit “RD” e no próximo ciclo de instrução teremos o valor


dessa localidade no EEDATA, o qual copiamos para o W:

CONT_CONFERENCIA
BSF EECON1,RD ;INICIA LEITURA
MOVF EEDATA,W ;W = VALOR DA LOCALIDADE DA EEPROM

Como já comentamos, o bit Z do registrador STATUS serve para indicar


quando o resultado da última instrução executada pelo microcontrolador resultou em 0, inclusive a
instrução MOVF.
Não são todas as instruções que afetam o bit Z. No datasheet do
microcontrolador, há uma descrição de todas as instruções e quais bits são afetados por cada uma.
Como esperamos que o valor dessa localidade seja igual a 0, já podemos
testar o bit Z do registrador STATUS, pois, se o valor copiado do EEDATA para o W for igual a 0,
aquele bit estará setado. Se ele estiver setado, significa que o valor da localidade é igual a 0 e,
portanto, desviamos o programa para onde está a label “CONFERE_UNIDADE”. Se o bit “Z” estiver
zerado, setamos os flags para que o processo de gravação seja repetido:
BTFSC STATUS,Z ;LOCALIDADE ANTERIOR DA UNIDADE FOI APAGADO CORRETAMENTE?
GOTO CONFERE_UNIDADE ;SIM
BCF CONF_GRV_EEPROM ;NAO, APAGA FLAG
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA
Em “CONFERE_UNIDADE”, iremos conferir se o valor da unidade foi
gravado corretamente na EEPROM. Primeiramente, incrementamos duas vezes o registrador EEADR
para que ele aponte para o endereço onde está atualmente gravada a unidade, testando se o seu valor é
igual a 128, como já fizemos quando gravamos a unidade:

132
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

CONFERE_UNIDADE
INCF EEADR,F ;INCREMENTA EEADR
INCF EEADR,F ;INCREMENTA EEADR
MOVLW .128 ;W = 128
XORWF EEADR,W ;W = W XOR EEADR
BTFSS STATUS,Z ;EEADR = 128?
GOTO CONT_CONF_UNIDADE ;NAO, DESVIA
CLRF EEADR
Em seguida, procedemos da mesma forma que foi feito na conferência do
valor da dezena, com a diferença de que após copiarmos a unidade para o W, inserimos o código nos
bits 5 a 8 do W, com a instrução IORWF, antes de comparar com o registrador EEDATA.
Se a gravação estiver correta, o programa é desviado para onde está a label
GRAVACAO_CORRETA, onde apagamos o flag “CONF_GRV_EEPROM”, copiamos o valor do
EEADR para a variável LOCAL_DA_UNIDADE, atualizando essa variável e retornamos,
finalizando o processo de gravação.
Mas, se o valor da unidade não houver sido gravado corretamente, apagamos
o flag “CONF_GRV_EEPROM”, setamos os flags APAGAR_UNIDADE, GRAVAR_UNIDADE e
GRAVAR_DEZENA para que o processo de gravação seja repetido:
CONT_CONF_UNIDADE
BSF EECON1,RD ;INICIA LEITURA
MOVF UNIDADE,W ;W = UNIDADE
IORLW B'01100000' ;W = W OR B'01100000'
XORWF EEDATA,W ;W = W XOR EEDATA
BTFSC STATUS,Z ;VALOR DA UNIDADE FOI GRAVADO CORRETAMENTE?
GOTO GRAVACAO_CORRETA ;SIM
BCF CONF_GRV_EEPROM ;NAO, APAGA FLAG
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA
GRAVACAO_CORRETA
BCF CONF_GRV_EEPROM ;APAGA FLAG
MOVF EEADR,W ;W = EEADR
MOVWF LOCAL_DA_UNIDADE ;LOCAL_DA_UNIDADE = EEADR
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA

Com isso, terminamos de escrever o programa que ficou assim:

;***********************************************************************************************
; PROGRAMA: CONTADOR CRESCENTE E DECRESCENTE
; VERSÃO 1.1
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /
;***********************************************************************************************
#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A

;***********************************************************************************************

133
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF &
_LVP_OFF & _CP_OFF & DATA_CP_OFF

;**********************************************************************************************
; INICIALIZAÇÃO DA EEPROM
ORG 2100 ;APONTA PARA O ENDEREÇO 00H DA EEPROM
DE B'01100000', .0 ;ESCREVE O NÚMERO BINÁRIO 01100000 NO ENDEREÇO 00H DA EEPROM
;E O NÚMERO DECIMAL 0 NA LOCALIDADE 01h

;**********************************************************************************************
; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA

;***********************************************************************************************
; VARIÁVEIS

CBLOCK 0X70 ;ENDERECO PRIMEIRA VARIÁVEL

W_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR W


STATUS_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR STATUS
FLAGS ;REGISTRADOR DE FLAGS
DB1_BTA ;PRIMEIRO REGISTRADOR DO DE-BOUNCING DO BOTÃO 1
DB1_BTB ;SEGUNDO REGISTRADOR DO DE-BOUNCING DO BOTÃO 1
DB2_BTA ;PRIMEIRO REGISTRADOR DO DE-BOUNCING DO BOTÃO 2
DB2_BTB ;SEGUNDO REGISTRADOR DO DE-BOUNCING DO BOTÃO 2

UNIDADE ;UNIDADE DO NUMERO EXIBIDO NO DISPLAY


DEZENA ;DEZENA DO NUMERO EXIBIDO NO DISPLAY
ESC_EEPROM_A ;CONTA O TEMPO DE ESPERA DO FIM DA ESCRITA NA EEPROM
ESC_EEPROM_B ;CONTA O TEMPO DE ESPERA DO FIM DA ESCRITA NA EEPROM
LOCAL_DA_UNIDADE ;CONTEM O ENDEREÇO DA EEPROM ONDE ESTÁ SALVO O VALOR DA UNIDADE

ENDC ;FIM DO BLOCO DE MEMORIA

;***********************************************************************************************
; CONSTANTES

INI_DB1_BTA EQU .255 ;VALOR QUE DB1_BTA INICIA


INI_DB1_BTB EQU .8 ;VALOR QUE DB1_BTB INICIA
INI_DB2_BTA EQU .255 ;VALOR QUE DB2_BTA INICIA
INI_DB2_BTB EQU .8 ;VALOR QUE DB2_BTB INICIA
INI_ESC_EEPROM_A EQU .255 ;VALOR QUE ESC_EEPROM_A INICIA
INI_ESC_EEPROM_B EQU .2 ;VALOR QUE ESC_EEPROM_B INICIA
;***********************************************************************************************
; SAÍDAS

#DEFINE DIGITO_1 PORTA,2 ;SE = 1, DIGITO 1 DO DISPLAY ATIVADO


#DEFINE DIGITO_2 PORTA,3 ;SE = 1, DIGITO 2 DO DISPLAY ATIVADO

;***********************************************************************************************

134
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

; ENTRADAS

#DEFINE BOTAO_1 PORTA,0 ;BOTAO 1 LIGADO EM RA0


#DEFINE BOTAO_2 PORTA,1 ;BOTAO 2 LIGADO EM RA1

;***********************************************************************************************
; FLAGS

#DEFINE SOLTAR_BOTAO_1 FLAGS,0 ;SE = 1, AGUARDA SOLTAR O BOTÃO 1


#DEFINE SOLTAR_BOTAO_2 FLAGS,1 ;SE = 1, AGUARDA SOLTAR O BOTÃO 2
#DEFINE APAGAR_UNIDADE FLAGS,2 ;SE = 1, APAGAR A LOCALIDADE ANTERIOR DA UNIDADE

#DEFINE GRAVAR_UNIDADE FLAGS,3 ;SE = 1, GRAVAR UNIDADE NA EEPROM


#DEFINE GRAVAR_DEZENA FLAGS,4 ;SE = 1, GRAVAR DEZENA NA EEPROM
#DEFINE ESP_FIM_GRV_EEPROM FLAGS,5 ;SE = 1, ESPERA O FIM DA GRAVAÇÃO NA EEPROM
#DEFINE CONF_GRV_EEPROM FLAGS,6 ;SE = 1, CONFERE A GRAVAÇÃO NA EEPROM
;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO

GOTO INICIO ;DESVIA PARA INICIO

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

MOVWF W_TEMP ;SALVA W


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;CHAVEIA PARA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS
BTFSS INTCON,T0IF ;TMR0 ESTOUROU?
GOTO SAI_INT ;NAO, DESVIA
BCF INTCON,T0IF ;SIM, APAGA FLAG
BTFSS DIGITO_1 ;DIGITO 1 ESTA ATIVADO?
GOTO DESATIVA_DIGITO_2 ;NAO, DESVIA
BCF DIGITO_1 ;SIM, DESATIVA DIGITO 1
CLRF PORTB ;TODOS OS BITS DO PORT B = 0
BSF DIGITO_2 ;ATIVA DIGITO 2
GOTO COPIAR_UNIDADE ;DESVIA

DESATIVA_DIGITO_2
BCF DIGITO_2 ;DESATIVA DIGITO 2
CLRF PORTB ;TODOS OS BITS DO PORT B = 0
BSF DIGITO_1 ;ATIVA DIGITO 1
MOVF DEZENA,W ;W = DEZENA
CALL CONVERTE_BINARIO_7_SEGMENTOS ;CHAMA SUBROTINA
MOVWF PORTB ;PORTB RECEBE O VALOR CONVERTIDO
GOTO SAI_INT ;DESVIA

COPIAR_UNIDADE
MOVF UNIDADE,W ;W = UNIDADE
CALL CONVERTE_BINARIO_7_SEGMENTOS ;CHAMA SUBROTINA
MOVWF PORTB ;PORTB RECEBE O VALOR CONVERTIDO
GOTO SAI_INT ;DESVIA

135
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

CONVERTE_BINARIO_7_SEGMENTOS
ADDWF PCL,F ;PCL = PCL + W
RETLW B'00111111' ;W = 0
RETLW B'00000110' ;W = 1
RETLW B'01011011' ;W = 2
RETLW B'01001111' ;W = 3
RETLW B'01100110' ;W = 4
RETLW B'01101101' ;W = 5
RETLW B'01111101' ;W = 6
RETLW B'00000111' ;W = 7
RETLW B'01111111' ;W = 8
RETLW B'01101111' ;W = 9

SAI_INT
SWAPF STATUS_TEMP,W ;SWAP EM STATUS_TEMP
MOVWF STATUS ;RECUPERA STATUS
SWAPF W_TEMP,F ;SWAP EM W_TEMP
SWAPF W_TEMP,W ;RECUPERA W
RETFIE ;RETORNA DA INTERRUPÇÃO

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO
INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11010011' ;W = B'11010011'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO, PRESCALER 1:16
MOVLW B'11110011' ;W = B'11110011'
MOVWF TRISA ;CONFIGURA RA2 E RA3 COMO SAÍDAS E DEMAIS COMO ENTRADAS
MOVLW B'10000000'
MOVWF TRISB ;CONFIGURA O PINO RB7 COMO ENTRADA E DEMAIS COMO SAIDAS
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O
MOVLW B'11100000'
MOVWF INTCON ;HABILITA INTERRUPÇÃO DO TIMER 0

;***********************************************************************************************
; INICIALIZACAO DAS VARIAVEIS

CLRF FLAGS ;INICIALIZA FLAGS


MOVLW INI_DB1_BTA ;W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
MOVLW INI_DB1_BTB ;W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
MOVLW INI_DB2_BTA ;W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
MOVLW INI_DB2_BTB ;W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
MOVLW INI_ESC_EEPROM_A ;W = INI_ESC_EEPROM_A
MOVWF ESC_EEPROM_A ;INICIALIZA ESC_EEPROM_A
MOVLW INI_ESC_EEPROM_B ;W = INI_ESC_EEPROM_B
MOVWF ESC_EEPROM_B ;INICIALIZA ESC_EEPROM_B
BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA
MOVLW .254 ;W = 254
MOVWF EEADR ;INICIALIZA EEADR
INICIALIZA_UNIDADE_E_DEZENA
INCF EEADR,F ;INCREMENTA EEADR

136
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

INCF EEADR,F ;INCREMENTA EEADR


BSF EECON1,RD ;INICIA LEITURA
MOVF EEDATA,W ;W = VALOR DA LOCALIDADE DA EEPROM
ANDLW B'11110000' ;ZERA BITS 0 A 4 E MANTEM BITS 5 A 8
XORLW B'01100000' ;W = W XOR 01100000
BTFSS STATUS,Z ;W = 01100000?
GOTO INICIALIZA_UNIDADE_E_DEZENA ;NAO
MOVF EEADR,W ;SIM, W = EEADR
MOVWF LOCAL_DA_UNIDADE ;INICIALIZA A VARIÁVEL LOCAL_DA_UNIDADE
MOVF EEDATA,W ;SIM, W = VALOR DA LOCALIDADE DA EEPROM
ANDLW B'00001111' ;W = W AND B'00001111'
MOVWF UNIDADE ;INICIALIZA UNIDADE
INCF EEADR,F ;INCREMENTA EEADR
BSF EECON1,RD ;INICIA LEITURA
MOVF EEDATA,W ;W = VALOR DA LOCALIDADE DA EEPROM
MOVWF DEZENA ;INICIALIZA DEZENA
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA

;***********************************************************************************************
PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


CALL TRATA_BOTAO_1 ;CHAMA SUBROTINA
CALL TRATA_BOTAO_2 ;CHAMA SUBROTINA
CALL GRAVA_EEPROM ;CHAMA SUBROTINA
GOTO PRINCIPAL ;DESVIA

;***********************************************************************************************
TRATA_BOTAO_1

BTFSS SOLTAR_BOTAO_1 ;AGUARDA SOLTAR O BOTAO 1?


GOTO TESTA_BOTAO_1 ;NAO, DESVIA
BTFSS BOTAO_1 ;SIM, O BOTÃO 1 ESTÁ SOLTO?
RETURN ;NAO, RETORNA
BCF SOLTAR_BOTAO_1 ;SIM, APAGA FLAG

TESTA_BOTAO_1
BTFSC BOTAO_1 ;O BOTÃO 1 ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB_1 ;NÃO, DESVIA
DECFSZ DB1_BTA,F ;SIM, DECREMENTA DB1_BTA. DB1_BTA = 0?
RETURN ;NAO,RETORNA
MOVLW INI_DB1_BTA ;SIM, W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
DECFSZ DB1_BTB,F ;DECREMENTA DB1_BTB. DB1_BTB = 0?
RETURN ;NAO, RETORNA
MOVLW INI_DB1_BTB ;SIM, W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
BSF SOLTAR_BOTAO_1 ;SETA FLAG PARA AGUARDAR SOLTAR O BOTÃO 1
INCF UNIDADE,F ;INCREMENTA UNIDADE
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
MOVLW .10 ;W = 10
XORWF UNIDADE,W ;W = W XOR UNIDADE
BTFSS STATUS,Z ;UNIDADE = 10?
RETURN ;NAO, RETORNA
CLRF UNIDADE ;SIM, UNIDADE = 0

137
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

INCF DEZENA,F ;INCREMENTA DEZENA


MOVLW .10 ;W = 10
XORWF DEZENA,W ;W = W XOR DEZENA
BTFSS STATUS,Z ;DEZENA = 10?
RETURN ;NAO, RETORNA
CLRF DEZENA ;SIM, DEZENA = 0
RETURN ;RETORNA

REINC_CONT_DEB_1
MOVLW INI_DB1_BTA ;W = INI_DB1_BTA
MOVWF DB1_BTA ;INICIALIZA DB1_BTA
MOVLW INI_DB1_BTB ;W = INI_DB1_BTB
MOVWF DB1_BTB ;INICIALIZA DB1_BTB
RETURN ;RETORNA

;********************************************************************************************

TRATA_BOTAO_2

BTFSS SOLTAR_BOTAO_2 ;AGUARDA SOLTAR O BOTAO 2?


GOTO TESTA_BOTAO_2 ;NAO, DESVIA
BTFSS BOTAO_2 ;SIM, O BOTÃO 2 ESTÁ SOLTO?
RETURN ;NAO, RETORNA
BCF SOLTAR_BOTAO_2 ;SIM, APAGA FLAG

TESTA_BOTAO_2
BTFSC BOTAO_2 ;O BOTÃO 2 ESTÁ PRESSIONADO?
GOTO REINC_CONT_DEB_2 ;NÃO, DESVIA
DECFSZ DB2_BTA,F ;SIM, DECREMENTA DB2_BTA. DB2_BTA = 0?
RETURN ;NAO,RETORNA
MOVLW INI_DB2_BTA ;SIM, W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
DECFSZ DB2_BTB,F ;DECREMENTA DB2_BTB. DB2_BTB = 0?
RETURN ;NAO, RETORNA
MOVLW INI_DB2_BTB ;SIM, W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
BSF SOLTAR_BOTAO_2 ;SETA FLAG PARA AGUARDAR SOLTAR O BOTÃO 2
DECF UNIDADE,F ;DECREMENTA UNIDADE
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
MOVLW .255 ;W = 255
XORWF UNIDADE,W ;W = W XOR UNIDADE
BTFSS STATUS,Z ;UNIDADE = 255?
RETURN ;NAO, RETORNA
MOVLW .9 ;SIM, W = 9
MOVWF UNIDADE ;UNIDADE = 9
DECF DEZENA,F ;DECREMENTA DEZENA
MOVLW .255 ;W = 255
XORWF DEZENA,W ;W = W XOR DEZENA
BTFSS STATUS,Z ;DEZENA = 255?
RETURN ;NAO, RETORNA
MOVLW .9 ;SIM, W = 9
MOVWF DEZENA ;DEZENA = 9
RETURN ;RETORNA

138
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

EINC_CONT_DEB_2
MOVLW INI_DB2_BTA ;W = INI_DB2_BTA
MOVWF DB2_BTA ;INICIALIZA DB2_BTA
MOVLW INI_DB2_BTB ;W = INI_DB2_BTB
MOVWF DB2_BTB ;INICIALIZA DB2_BTB
RETURN ;RETORNA

;**********************************************************************************************
GRAVA_EEPROM

BTFSC ESP_FIM_GRV_EEPROM ;AGUARDA O FIM DA GRAVACAO NA EEPROM?


GOTO TESTA_FIM_DA_GRAVACAO ;SIM
BTFSC CONF_GRV_EEPROM ;NAO, CONFERIR A GRAVAÇÃO?
GOTO CONFERE_DEZENA ;SIM
BTFSC APAGAR_UNIDADE ;NAO, APAGAR A LOCALIDADE ANTERIOR DA UNIDADE?
GOTO APAGA_UNIDADE_ANTERIOR ;SIM
BTFSC GRAVAR_UNIDADE ;NAO, GRAVAR UNIDADE?
GOTO GRAVA_A_UNIDADE ;SIM
BTFSC GRAVAR_DEZENA ;NAO, GRAVAR DEZENA?
GOTO GRAVA_A_DEZENA ;SIM
RETURN ;NAO

APAGA_UNIDADE_ANTERIOR
BANCO_1 ;SELECIONA BANCO 1 DE MEMÓRIA
MOVF LOCAL_DA_UNIDADE,W ;W = ENDEREÇO DA EEPROM ONDE ESTÁ GRAVADA A UNIDADE
MOVWF EEADR ;EEADR RECEBE ENDERECO
CLRF EEDATA ;EEDATA = 0
BSF EECON1,WREN ;HABILITA ESCRITA NA EEPROM
BCF INTCON,GIE ;DESABILITA AS INTERRUPÇÕES
BTFSC INTCON,GIE ;BIT GIE ESTÁ ZERADO?
GOTO $-2 ;DESVIA O PROGRAMA PARA 2 LINHAS ACIMA
MOVLW 0x55 ;W = NUMERO 55 HEXADECIMAL
MOVWF EECON2 ;EECON2 = 55 HEXADECIMAL
MOVLW 0xAA ;W = NUMERO AA HEXADECIMAL
MOVWF EECON2 ;EECON = AA HEXADECIMAL
BSF EECON1,WR ;INICIA A GRAVAÇÃO
BSF INTCON,GIE ;HABILITA INTERRUPÇÕES
BCF APAGAR_UNIDADE ;APAGA O FLAG
BSF ESP_FIM_GRV_EEPROM ;SETA O FLAG PARA ESPERAR PELO FIM DA GRAVAÇÃO
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA

GRAVA_A_UNIDADE
BANCO_1 ;SELECIONA O BANCO 1 DE MEMÓRIA
INCF EEADR,F ;INCREMENTA EEADR
INCF EEADR,F ;INCREMENTA EEADR
MOVLW .128 ;W = 128
XORWF EEADR,W ;W = W XOR EEADR
BTFSS STATUS,Z ;EEADR = 128?
GOTO CONT_GRV_UNIDADE ;NAO, DESVIA
CLRF EEADR ;SIM, EEADR = 0
CONT_GRV_UNIDADE
MOVF UNIDADE,W ;W = UNIDADE
MOVWF EEDATA ;EEDATA = UNIDADE
MOVLW B'01100000' ;W = 01100000
IORWF EEDATA,F ;EEDATA = EEDATA OR 01100000
BSF EECON1,WREN ;HABILITA ESCRITA NA EEPROM
BCF INTCON,GIE ;DESABILITA AS INTERRUPÇÕES

139
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

BTFSC INTCON,GIE ;BIT GIE ESTÁ ZERADO?


GOTO $-2 ;DESVIA O PROGRAMA PARA 2 LINHAS ACIMA
MOVLW 0x55 ;W = NUMERO 55 HEXADECIMAL
MOVWF EECON2 ;EECON2 = 55 HEXADECIMAL
MOVLW 0xAA ;W = NUMERO AA HEXADECIMAL
MOVWF EECON2 ;EECON2 = AA HEXADECIMAL
BSF EECON1,WR ;INICIA A GRAVAÇÃO
BSF INTCON,GIE ;HABILITA INTERRUPÇÕES
BCF GRAVAR_UNIDADE ;APAGA O FLAG
BSF ESP_FIM_GRV_EEPROM ;SETA O FLAG PARA ESPERAR PELO FIM DA GRAVAÇÃO
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
RETURN ;RETORNA

GRAVA_A_DEZENA
BANCO_1 ;SELECIONA O BANCO 1 DE MEMÓRIA
INCF EEADR,F ;INCREMENTA EEADR
MOVF DEZENA,W ;W = DEZENA
MOVWF EEDATA ;EEDATA = DEZENA
BSF EECON1,WREN ;HABILITA ESCRITA NA EEPROM
BCF INTCON,GIE ;DESABILITA AS INTERRUPÇÕES
BTFSC INTCON,GIE ;BIT GIE ESTÁ ZERADO?
GOTO $-2 ;DESVIA O PROGRAMA PARA 2 LINHAS ACIMA
MOVLW 0x55 ;W = NUMERO 55 HEXADECIMAL
MOVWF EECON2 ;EECON2 = 55 HEXADECIMAL
MOVLW 0xAA ;W = NUMERO AA HEXADECIMAL
MOVWF EECON2 ;EECON2 = AA HEXADECIMAL
BSF EECON1,WR ;INICIA A GRAVAÇÃO
BSF INTCON,GIE ;HABILITA INTERRUPÇÕES
BCF GRAVAR_DEZENA ;APAGA O FLAG
BSF ESP_FIM_GRV_EEPROM ;SETA O FLAG PARA ESPERAR PELO FIM DA GRAVAÇÃO
BSF CONF_GRV_EEPROM ;SETA O FLAG PARA CONFERIR A GRAVAÇÃO DA EEPROM
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
RETURN ;RETORNA

TESTA_FIM_DA_GRAVACAO
BANCO_1 ;SELECIONA O BANCO 1 DE MEMÓRIA
BTFSC EECON1,WR ;A GRAVAÇÃO TERMINOU?
GOTO DEC_CONT_TEMPO_FIM_ESCRITA ;NAO, DESVIA
BCF ESP_FIM_GRV_EEPROM ;SIM, APAGA FLAG
BCF EECON1,WREN ;DESABILITA ESCRITA NA EEPROM
MOVLW INI_ESC_EEPROM_A ;W = INI_ESC_EEPROM_A
MOVWF ESC_EEPROM_A ;REINICIA ESC_EEPROM_A
MOVLW INI_ESC_EEPROM_B ;W = INI_ESC_EEPROM_B
MOVWF ESC_EEPROM_B ;REINICIA ESC_EEPROM_B
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA
DEC_CONT_TEMPO_FIM_ESCRITA
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
DECFSZ ESC_EEPROM_A,F ;DECREMENTA ESC_EEPROM_A. ESC_EEPROM_A = 0?
RETURN ;NAO, RETORNA
MOVLW INI_ESC_EEPROM_A ;SIM, W = INI_ESC_EEPROM_A
MOVWF ESC_EEPROM_A ;REINICIA ESC_EEPROM_A
DECFSZ ESC_EEPROM_B,F ;DECREMENTA ESC_EEPROM_B.ESC_EEPROM_B = 0?
RETURN ;NAO, RETORNA
MOVLW INI_ESC_EEPROM_B ;SIM, W = INI_ESC_EEPROM_B
MOVWF ESC_EEPROM_B ;REINICIA ESC_EEPROM_B
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE

140
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA


BCF ESP_FIM_GRV_EEPROM ;APAGA FLAG DE ESPERA PELO FIM DA GRAVACAO
BCF CONF_GRV_EEPROM ;APAGA O FLAG PARA CONFERIR A GRAVAÇÃO DA EEPROM
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA

CONFERE_DEZENA
BANCO_1 ;SELECIONA O BANCO 1 DE MEMÓRIA
BSF EECON1,RD ;INICIA LEITURA
MOVF DEZENA,W ;W = DEZENA
XORWF EEDATA,W ;W = W XOR EEDATA
BTFSC STATUS,Z ;VALOR DA DEZENA FOI GRAVADO CORRETAMENTE?
GOTO CONFERE_APAGAMENTO ;SIM
BCF CONF_GRV_EEPROM ;NAO, APAGA FLAG
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA

CONFERE_APAGAMENTO
DECF EEADR,F ;DECREMENTA EEADR
DECF EEADR,F ;DECREMENTA EEADR
DECF EEADR,F ;DECREMENTA EEADR
MOVLW .254 ;W = 254
XORWF EEADR,W ;W = W XOR EEADR
BTFSS STATUS,Z ;EEADR = 254?
GOTO CONT_CONFERENCIA ;NAO, DESVIA
MOVLW .126 ;SIM, W = 126
MOVWF EEADR ;EEADR = 126
CONT_CONFERENCIA
BSF EECON1,RD ;INICIA LEITURA
MOVF EEDATA,W ;W = VALOR DA LOCALIDADE DA EEPROM
BTFSC STATUS,Z ;LOCALIDADE ANTERIOR DA UNIDADE FOI APAGADO CORRETAMENTE?
GOTO CONFERE_UNIDADE ;SIM
BCF CONF_GRV_EEPROM ;NAO, APAGA FLAG
BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE
BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA

CONFERE_UNIDADE
INCF EEADR,F ;INCREMENTA EEADR
INCF EEADR,F ;INCREMENTA EEADR
MOVLW .128 ;W = 128
XORWF EEADR,W ;W = W XOR EEADR
BTFSS STATUS,Z ;EEADR = 128?
GOTO CONT_CONF_UNIDADE ;NAO, DESVIA
CLRF EEADR ;SIM, EEADR = 0
CONT_CONF_UNIDADE
BSF EECON1,RD ;INICIA LEITURA
MOVF UNIDADE,W ;W = UNIDADE
IORLW B'01100000' ;W = W OR B'01100000'
XORWF EEDATA,W ;W = W XOR EEDATA
BTFSC STATUS,Z ;VALOR DA UNIDADE FOI GRAVADO CORRETAMENTE?
GOTO GRAVACAO_CORRETA ;SIM
BCF CONF_GRV_EEPROM ;NAO, APAGA FLAG

141
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

BSF APAGAR_UNIDADE ;SETA O FLAG PARA APAGAR LOCALIDADE ANTERIOR DA UNIDADE


BSF GRAVAR_UNIDADE ;SETA O FLAG PARA GRAVAÇÃO DA UNIDADE
BSF GRAVAR_DEZENA ;SETA O FLAG PARA GRAVAÇÃO DA DEZENA
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA
GRAVACAO_CORRETA
BCF CONF_GRV_EEPROM ;APAGA FLAG
MOVF EEADR,W ;W = EEADR
MOVWF LOCAL_DA_UNIDADE ;LOCAL_DA_UNIDADE = EEADR
BANCO_0 ;SELECIONA O BANCO 0 DE MEMÓRIA
RETURN ;RETORNA
;*************************************************************************************************

END ;FIM DO PROGRAMA

Vamos simular a execução do programa.


No MPLAB, no menu “Project”, clique em “Open” e abra o projeto de nome
“Contador”. No menu “Project”, clique em “Remove File From Project” e remova o arquivo
“Contador.asm”. No menu “Project”, clique em “Add Files to Project” e inclua o arquivo
“ContadorII.asm”. Atenção: A opção é “Add Files to Project” e não “Add New File to Project”.
No menu “Project”, clique em “Build All”.
Na janela “Output”, verifique se a mensagem “BUILD SUCCEEDED” foi
exibida. Caso contrário, confira se o programa está exatamente igual.
No menu “View”, clique em “EEPROM”. Repare que a localidade 00 está
com o valor 60 hexadecimal, que corresponde ao número binário 01100000, enquanto a localidade 01
está com o número 00. O MPLAB escreveu esses valores ao executar a diretiva “DE” constante no
programa:

142
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Figura 1

No “Watch”, no botão “ADD SFR”, inclua os registradores EECON1,


EEADR, EEDATA e WREG.
Na barra de ferramentas do simulador, clique no botão “Reset”. Em seguida,
vá clicando no botão “Step Into”.
Quando chegar em “Inicialização das Variáveis”, clique nos botões do
“Stimulus” que fixam o RA0 e o RA1 em nível alto (“Set High”) para simular que os botões 1 e 2
estão soltos.
Durante a inicialização das variáveis, acompanhe, no “Watch”, os valores dos
registradores EECON1, EEADR, EEDATA e W, conforme as instruções vão sendo executadas.
Vamos medir o tempo de espera pela conclusão da gravação da EEPROM, que
queremos que seja de cerca de 10 milissegundos.
Esse é o tempo que o programa irá esperar para que o bit “WR” volte a 0,
depois que a gravação é iniciada.
Para podermos medir esse tempo, vamos fazer uma alteração no programa:
Em “TESTA_FIM_DA_GRAVACAO”, onde consta: BTFSC EECON1,WR
mude para: BTFSS EECON1,WR
Dessa forma, poderemos medir o tempo, pois esse bit está com o valor 0 e não
mudará.
Após essa alteração, monte o projeto novamente, clicando em “Build All”, no
menu “Project”.
Clique no botão “Reset” do simulador e depois vá clicando no botão “Step
Into” até chegar à inicialização das variáveis.

143
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

No “Stimulus”, clique nos botões que fixam o RA0 e o RA1 em nível alto
para simular que os botões estão soltos.
Continue clicando em “Step Into” até chegar à subrotina de gravação da
EEPROM.
Em “TESTA_FIM_DA_GRAVACAO”, insira um “breakpoint” na linha que
contém a instrução BCF ESP_FIM_GRV_EEPROM, dando um duplo clique nessa linha.
Quando o programa for executar essa instrução, o tempo de espera terá
terminado.
Precisamos setar manualmente o flag “ESP_FIM_GRV_EEPROM” para que
o programa entre em “TESTA_FIM_DA_GRAVACAO”.
Para fazer isso, no “Watch”, dê um duplo clique no campo “Binary” da linha
do registrador FLAGS, e altere o bit 5 para 1:

Figura 2

No “Stopwatch”, clique em “Zero”.


A seguir, clique no botão “Run” da barra de ferramentas do simulador. O
programa irá parar no “breakpoint”.
Confira no “Stopwatch” que demorou cerca de 22 milissegundos para que a
variável “ESC_EEPROM_B” chegasse a 0:

144
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Figura 3

Vamos diminuir esse tempo, alterando o valor de inicialização dessa variável


para 1 na seção “Constantes”, em “INI_ESC_EEPROM_B”.
Após essa alteração, monte o projeto novamente, clicando em “Build All”, no
menu “Project”.
Clique no botão “Reset” do simulador e depois vá clicando no botão “Step
Into” até chegar à inicialização das variáveis.
No “Stimulus”, clique nos botões que fixam o RA0 e o RA1 em nível alto
para simular que os botões estão soltos.
Continue clicando em “Step Into” até chegar à subrotina de gravação da
EEPROM.
No “Watch”, sete novamente o flag “ESP_FIM_GRV_EEPROM”, dando um
duplo clique no campo “Binary” da linha do registrador FLAGS, e alterando o bit 5 para 1.
No “Stopwatch”, clique em “Zero”.
A seguir, clique no botão “Run” da barra de ferramentas do simulador. O
programa irá parar novamente no “breakpoint”.
Confira, no “Stopwatch” que o tempo diminuiu para cerca de 11
milissegundos:

145
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Figura 4

Podemos deixar este tempo pois, está bem próximo dos 10 milissegundos que
pretendíamos.
Vamos desfazer a alteração feita em “TESTA_FIM_DA_GRAVACAO”,
voltando a instrução para: BTFSC EECON1,WR.
Após isso, monte o projeto novamente, clicando em “Build All”, no menu
“Project”.
Clique no botão “Reset” do simulador e depois vá clicando no botão “Step
Into” até chegar à inicialização das variáveis.
No “Stimulus”, clique nos botões que fixam o RA0 e o RA1 em nível alto
para simular que os botões estão soltos.
No menu “Debugger”, clique em “Breakpoints”. Na janela que se abre, clique
em “Remove All”.
Posicione as janelas para que possa clicar nos botões do “Stimulus” enquanto
observa os valores dos registradores no “Watch”.
Clique no botão “Run” do simulador.
No “Stimulus”, clique no botão que simula que o botão 1 foi pressionado e
solto (RA0 “Pulse Low”).
No “Watch” repare que o valor da unidade foi incrementado.
Clique no botão “Halt” do simulador.
No menu “Window”, selecione a janela “EEPROM”. Se ela não estiver na
lista, clique em “EEPROM” no menu “View” para exibi-la.

146
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Repare que a localidade 02 está com o valor 61 hexadecimal. Esse valor


corresponde em binário a 01100001, que contém o valor da unidade (1) nos bits 0 a 4 e o código nos
bits 5 a 8. A próxima localidade está com o valor 0 que corresponde ao valor da dezena:

Figura 5

Clique no botão “Run” do simulador e em seguida incremente novamente o


contador, clicando no botão do “Stimulus”.
Clique no botão “Halt” do simulador.
Na janela “EEPROM”, repare que a localidade 04 está com o valor 62
hexadecimal, que corresponde em binário a 01100010, que contém o valor da unidade (2) nos bits 0 a
4 e o código nos bits 5 a 8. A próxima localidade está com o valor 0 da dezena. Repare, também que a
localidade 02 foi zerada. Lembre-se que a localidade anterior da dezena não é apagado, pois não é
necessário:

147
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II

Figura 6

Continue incrementando ou decrementando o contador e repare que a cada


vez que o valor da unidade é alterado, os valores da unidade e da dezena são salvos em duas novas
localidades da memória EEPROM, conforme planejado.
Grave o programa no microcontrolador e teste na protoboard.
Repare que quando ligamos o circuito, ele exibe o valor com que estava antes
de ser desligado.
Aqui termina esta parte do tutorial. Na próxima, utilizaremos o PIC16F628A
para gerar um sinal PWM para controlar o brilho de um LED utilizando dois botões. Um dos botões
fará o brilho aumentar até o máximo enquanto o outro o fará diminuir até o LED apagar.
Até lá!

148
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Parte 7
Dimmer para LED
Nesta parte do tutorial, vamos montar o circuito da figura abaixo para
controlar o brilho de um LED usando dois botões: um para aumentá-lo e outro para diminui-lo.

Figura 1

Para isso, vamos utilizar o modo PWM do PIC16F628A. Esse recurso do


microcontrolador produz um sinal PWM no pino RB3/CCP1 (pino 9). A forma de onda de um sinal
PWM pode ser vista na figura abaixo:

Figura 2

149
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Chamaremos o tempo durante o qual a tensão é máxima de semiciclo ativo.


Suponha que apliquemos esse sinal a um LED de forma que, durante o
semiciclo ativo, ele esteja aceso e, no restante do tempo, apagado. Assim, esse LED ficará piscando
na frequência do sinal. A partir de uma determinada frequência, teremos a impressão de que ele está
permanentemente aceso, devido ao fenômeno da persistência da visão. Se reduzirmos o tempo de
duração do semiciclo ativo, mantendo o do ciclo, conforme figura abaixo, teremos a impressão de que
o brilho do LED diminuiu. Isso ocorre porque apesar de, durante o semiciclo ativo, o LED acender
com a mesma intensidade, ele fica aceso por um tempo menor.

Figura 3

Por outro lado, se aumentarmos o tempo de duração do semiciclo ativo,


mantendo o do ciclo, conforme figura abaixo, teremos a impressão de que o brilho do LED aumentou.

Figura 4

Como você pode observar, o tempo de duração do ciclo do sinal não muda,
portanto a frequência do sinal PWM, que é igual ao inverso do valor do tempo de duração do ciclo,
não muda. O que irá mudar é o tempo de duração do semiciclo ativo, isto é, o tempo em que o sinal
ficará em nível alto.

150
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Para que não se perceba que o LED está piscando, temos de escolher uma
frequência de, pelo menos, 75 Hz para o sinal PWM.
Vamos ver como o PIC16F628A produz o sinal PWM.
Na parte 2 deste tutorial, falamos sobre o Timer 0. O PIC16F628A possui
outros dois circuitos semelhantes: o Timer 1 e o Timer 2.
A principal diferença do Timer 1 em relação ao Timer 0 é que ele é composto
por dois registradores de 8 bits enquanto que o Timer 0 possui apenas um. Os registradores do Timer
1 são o TMR1L e o TMR1H.
Quando o Timer 1 é incrementado, esse incremento ocorre no TMR1L que
pode ir até o valor 255. No próximo incremento, ele volta para o valor 0 e o TMR1H é incrementado.
Assim, o Timer 1 pode ser incrementado até o valor 65535, o que o torna ideal para contagem de
tempos mais longos.
O Timer 1 pode ser incrementado tanto a partir do ciclo de instrução como a
partir de um sinal externo aplicado no pino 13 (RB7/T1OSI/PGD). Um cristal pode ser ligado entre
esse pino e o pino 12 (RB6/T1OSO/T1CKI/PGC), para fazer funcionar um oscilador interno
otimizado para a frequência de 32.768 KHz, ideal para relógios.
O Timer 1 possui um prescaler que pode ser configurado para 1:1, 1:2, 1:4 ou
1:8.
O Timer 2, assim como o Timer 0, é constituído de apenas um registrador de 8
bits, o TMR2 e, portanto, seu valor pode ser incrementado até 255, porém, somente é possível
incrementá-lo a partir do ciclo de instrução, não sendo possível incrementá-lo a partir de um sinal
externo.
O Timer 2 possui algumas características diferentes dos demais:
Existe um registrador chamado PR2 cujo valor define até onde o registrador
TMR2 poderá ser incrementado. Por exemplo, se o valor do PR2 for igual a 255, o TMR2 poderá ser
incrementado até esse valor e, no próximo incremento ele irá estourar, ou seja, seu valor voltará para
0. Já, se o valor do PR2 for, por exemplo, 100, o TMR2 poderá ser incrementado até esse valor
estourando no próximo incremento. Podemos escrever qualquer valor entre 0 e 255 no PR2.
O seu Prescaler pode assumir apenas os seguintes valores: 1:1, 1:4 ou 1:16, ou
seja, ele pode incrementado a cada ciclo de instrução ou a cada quatro ciclos ou a cada dezesseis
ciclos.
O Timer 2 possui um Postscaler, que pode ser configurado de 1:1 até 1:16. O
Postscaler define o número de vezes que o TMR2 deverá estourar para gerar uma interrupção. Por
exemplo, se ele estiver configurado para 1:1, a cada vez que o TMR2 estourar, o flag TMR2IF será
setado, gerando uma interrupção (desde que ela esteja habilitada). Já, se o Postscaler estiver
configurado, por exemplo, para 1:5, uma interrupção será gerada a cada 5 estouros do TMR2.
As configurações do Prescaler e do Postscaler são feitas no registrador
T2CON, onde também há um bit para ligar e desligar o Timer 2.
É no módulo CCP do PIC16F628A que é gerado o sinal PWM. A
configuração do módulo CCP é feita no registrador CCP1CON. Devemos setar os bits 3 e 2 desse
registrador para habilitar o modo PWM.

151
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

O ciclo do sinal PWM reinicia toda vez que o TMR2 estoura. A fórmula para
o cálculo do tempo de duração do ciclo do sinal PWM é esta:
Ciclo = (PR2 + 1) x ciclo de instrução x prescaler do TMR2

É fácil entender essa fórmula, considerando que:


 O ciclo termina (e um novo inicia) quando o TMR2 estoura.
 O TMR2 estoura no próximo incremento após seu valor se igualar ao PR2
(PR2 +1)
 O TMR2 é incrementado pelo ciclo de instrução.
 O prescaler define de quantos em quantos ciclos de instrução o TMR2 será
incrementado.
No nosso caso, queremos um sinal PWM com uma frequência de 75 Hz, o
que equivale a um ciclo de cerca de 13 milissegundos. Como estamos usando o oscilador interno do
PIC, que é de 4 MHz, o ciclo de instrução tem a duração de 1 microssegundo, pois o ciclo de instru-
ção é igual a quatro vezes o valor do ciclo do oscilador (1/4.000.000 x 4).
Vamos aplicar a fórmula com o valor do prescaler em 1:1:

Ciclo = (PR2 + 1) x ciclo de instrução x prescaler do TMR2


0,013 = (PR2 + 1) x 0,000001 x 1
0,013 = (PR2 + 1) x 0,000001
PR2 + 1 = 0,013/0,000001
PR2 + 1 = 13.000
PR2 = 13.000 – 1
PR2 = 12.999

Mas, o valor máximo para o PR2 é de 255. Vamos ver qual seria o valor do
PR2 para um prescaler de 1:4:

Ciclo = (PR2 + 1) x ciclo de instrução x prescaler do TMR2


0,013 = (PR2 + 1) x 0,000001 x 4
0,013 = (PR2 + 1) x 0,000004
PR2 + 1 = 0,013/0,000004
PR2 + 1 = 3.250
PR2 = 3.250 – 1
PR2 = 3.249

Novamente obtivemos um valor muito alto para o PR2. Vamos, então, aumen-
tar o valor do prescaler para 1:16:

Ciclo = (PR2 + 1) x ciclo de instrução x prescaler do TMR2


0,013 = (PR2 + 1) x 0,000001 x 16
0,013 = (PR2 + 1) x 0,000016
PR2 + 1 = 0,013/0,000016
PR2 + 1 = 812,5
PR2 = 812,5 – 1
PR2 = 811,5

152
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

O valor a ser escrito no PR2 continua acima de 255. Isto mostra que não é
possível obter um sinal PWM de 75 Hz se a frequência de oscilação do microcontrolador é de 4 MHz.
Teríamos de diminuir a frequência do oscilador se quiséssemos um sinal PWM de 75 Hz. Poderíamos
fazer isso alterando a frequência do oscilador interno para 48 KHz ou então usando um cristal externo.
Escrevendo no PR2 o seu valor máximo (255) e configurando o prescaler
também para seu valor máximo (16), obtemos a máxima duração do ciclo do sinal PWM para uma
frequência de oscilação de 4 MHz:

Ciclo = (PR2 + 1) x ciclo de instrução x prescaler do TMR2


Ciclo = (255 + 1) x 0,000001 x 16
Ciclo = 256 x 0,000001 x 16
Ciclo = 0,004096 segundo

Esse tempo de duração do ciclo corresponde à frequência de 244 Hz


(1/0,0049096). No nosso circuito, precisamos que o sinal PWM tenha uma frequência maior do que
75 Hz para evitar que se perceba que o LED está piscando. Sendo assim, podemos usar a frequência
de 244 Hz, até mesmo porque o LED pode operar em frequências bem maiores que esta.
Vimos como definir a duração do ciclo do sinal PWM. Vamos ver agora como
se define o tempo de duração do semiciclo ativo. O semiciclo ativo começa junto com o ciclo e ter-
mina quando o sinal vai para nível baixo.
O sinal PWM do PIC16F628A pode ter uma resolução de até 10 bits, depen-
dendo da frequência do próprio sinal e da frequência de oscilação. Uma resolução de 10 bits significa
que podemos definir 1024 (210) tempos de duração diferentes para o semiciclo ativo.
No nosso caso, o tempo de duração do ciclo é de 4,096 milissegundos. Divi-
dindo este valor por 1024, concluímos que podemos variar o tempo de duração do semiciclo ativo em
passos de 0,004 milissegundo desde o mínimo de 0,004 milissegundo até o máximo de 4,092 milisse-
gundos.
Para definirmos o tempo de duração do semiciclo ativo, escrevemos um valor
entre 0 e 1024 na combinação de 10 bits formada pelo registrador CCPR1L e os bits 5 e 4 do registra-
dor CCP1CON. Nessa combinação, o registrador CCPR1L representa os 8 bits mais significativos:

Bit 9 Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
CCP1CON CCP1CON
CCPR1L Bit 5 Bit 4

Tabela 1

Se escrevermos o valor 0, o sinal ficará o tempo todo em nível baixo. Se es-


crevermos o valor 1024, ele ficará o tempo todo em nível alto. Nesses casos, não teremos um sinal
PWM.
Se escrevermos o valor 1, o semiciclo terá a duração mínima (0,004 milisse-
gundo) e, se escrevermos o valor 1023, a máxima (4,092 milissegundos).
Vamos supor que queiramos que o tempo de duração do semiciclo ativo seja
de 10% do tempo de duração do ciclo. Nesse caso, temos de escrever o valor 102,4 (10% de 1024)
nos 10 bits da referida combinação.
Como o número a ser escrito tem de ser inteiro, arredondamos para 102, que,
em binário, é igual a 1100110. Portanto:

153
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

0 0 0 1 1 0 0 1 1 0
Bit 9 Bit 8 Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
CCP1CON CCP1CON
CCPR1L Bit 5 Bit 4

Tabela 2

Ou seja, temos de escrever o número binário 00011001 no registrador


CCPR1L, setar o bit 5 e zerar o bit 4 do registrador CCP1CON.
Uma outra combinação de 10 bits formada pelo registrador TMR2 e dois bits
do oscilador interno do microcontrolador é incrementada a cada ciclo do oscilador. Quando as duas
combinações se igualam o semiciclo ativo termina, isto é, o sinal PWM vai para nível baixo.
Se você preferir, pode usar a seguinte fórmula para o cálculo do tempo de du-
ração do semiciclo ativo:

Semiciclo ativo = (CCPR1L:CCP1CON<5:4>) x ciclo de oscilação x prescaler do TMR2

Nessa fórmula, (CCPR1L:CCP1CON<5:4>) é o valor de 0 a 1024 a ser es-


crito na combinação de 10 bits formada pelo registrador CCPR1L e os bits 5 e 4 do CCP1CON; ciclo
de oscilação é o tempo de duração de um ciclo do oscilador do microcontrolador, que, no nosso caso
é igual a 0,00000025 segundo (1/4000000).
Vamos aplicar a fórmula para o exemplo dos 10% que demos acima, onde a
duração do semiciclo ativo é de 10% do ciclo, ou seja, 0,0004096 segundo:

Semiciclo ativo = (CCPR1L:CCP1CON<5:4>) x ciclo de oscilação x prescaler do TMR2


0,0004096 = (CCPR1L:CCP1CON<5:4>) x 0,00000025 x 16
0,0004096 = (CCPR1L:CCP1CON<5:4>) x 0,000004
(CCPR1L:CCP1CON<5:4>) = 0,0004096 / 0,000004
(CCPR1L:CCP1CON<5:4>) = 102,4

Que resulta no valor 102,4 que havíamos encontrado quando dividimos o nú-
mero 1024 por 10.
A figura abaixo mostra os eventos que provocam o reinício do ciclo e o fim
do semiciclo ativo do sinal PWM.

154
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Figura 5

No nosso circuito, quando o botão 1 for pressionado, o brilho do LED irá au-
mentar até o máximo e, quando o botão 2 for pressionado, o brilho irá diminuir até o LED apagar.
Com o LED apagado, o botão 1 deverá ficar pressionado por cerca de 5 se-
gundos para que o brilho atinja o máximo. Esse também será o tempo que o botão 2 deverá ser man-
tido pressionado para que o LED apague, partindo do brilho máximo.
Como vimos, é possível definir 1024 valores diferentes para o tempo de dura-
ção do semiciclo ativo do sinal PWM, escrevendo na combinação de 10 bits, um valor entre 0 e 1024.
Não precisamos de uma resolução tão alta. Se modificarmos apenas o valor do
registrador CCPR1L, já teremos 256 valores diferentes, o que é mais do que suficiente para uma vari-
ação suave no brilho. Dividindo 5 segundos por 256, obtemos 0,01953125 segundo, ou seja, aproxi-
madamente 19 milissegundos.
Iremos configurar o Timer 0 para que ele provoque uma interrupção a cada
cerca de 19 milissegundos. Na rotina da interrupção, iremos verificar qual botão está pressionado. Se
for o botão 1, iremos incrementar o registrador CCPR1L. Se for o botão 2 decrementaremos o
CCPR1L. Se nenhum botão estiver pressionado, manteremos o valor do CCPR1L.
Para configurarmos o Timer 0 a fim de que ele provoque uma interrupção a
cada 19 milissegundos, primeiramente dividimos 19 milissegundos pelo tempo de duração de um
ciclo de instrução, que é de 1 microssegundo: 0,019 / 0,000001 = 19.000.
Isto significa que 19 milissegundos equivalem a 19.000 ciclos de instrução, ou
seja, o registrador TMR0 deverá estourar a cada 19.000 ciclos de instrução. Como o registrador
TMR0 estoura a cada 256 incrementos, se fizéssemos o valor do prescaler igual a 1:1, onde, o TMR0
é incrementado a cada ciclo de instrução, ele iria estoura a cada 256 ciclos de instrução apenas. Então,
precisamos aumentar o valor do prescaler para que ele estoure a cada 19.000 ciclos de instrução.

155
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Dividindo 19.000 por 256, encontramos o valor do prescaler: 19.000 / 256 =


74. Mas o prescaler pode ser definido para os seguintes valores: 1:1, 1:2, 1:4, 1:8, 1:16, 1:32, 1:64,
1:128 e 1:256, ou seja, não há como definir ele para 1:74. Nesse caso, vamos adotar o valor maior
mais próximo, que é 1:128.
Se o valor do prescaler é de 1:128, o TMR0 será incrementado a cada 128 ci-
clos de instrução, estourando a cada: 256 x 128 = 32.768 ciclos de instrução, porém queremos que ele
estoure a cada 19.000 ciclos de instrução.
Para resolver isso, a cada vez que o TMR0 estourar (seu valor passar de 255
para 0, gerando uma interrupção), iremos escrever nele um determinado valor. Para encontrar esse
valor, inicialmente dividimos 19.000 por 128, que resulta em 148,4375, que arredondamos para 148.
A seguir, subtraímos esse número de 256, o que resulta em 108 (256 – 148 = 108). Esse é o valor que
iremos escrever no TMR0 toda vez que ele estourar.
Se o TMR0 começa a ser incrementado com o valor 108, ele irá estourar a ca-
da 148 incrementos. Como o valor do prescaler é de 128, ele irá estourar a cada 128 x 148 = 18.944
ciclos de instrução. Esse valor é o mais próximo que conseguimos de 19.000.
Vamos ao programa!
No MPLAB, abra o arquivo “Pisca LED III.asm” da parte 3 deste tutorial e
salve-o com o nome “Dimmer para LED.asm”.
A primeira alteração é na seção “VARIÁVEIS”. Exclua essa seção. Isso mes-
mo, não precisaremos de nenhuma variável neste programa!
Exclua também a seção “CONSTANTES”.
Renomeie a seção “SAÍDA” para “ENTRADAS” e defina a label “BOTAO_1”
para o pino RA0 e a label “BOTAO_2” para o RA1:

;***********************************************************************************************
; ENTRADAS

#DEFINE BOTAO_1 PORTA,0 ;BOTAO 1 LIGADO EM RA0


#DEFINE BOTAO_2 PORTA,1 ;BOTAO 2 LIGADO EM RA1

;***********************************************************************************************

Na rotina de interrupção, a primeira instrução será BTFSS INTCON,T0IF


portanto, exclua as anteriores a ela. Essa instrução testa se a interrupção foi a do Timer 0. Caso não
tenha sido, a instrução da próxima linha será executada, onde saímos da interrupção. Se a interrupção
foi do Timer 0, apagamos o flag na linha seguinte:

;************************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

BTFSS INTCON,T0IF ;TMR0 ESTOUROU?


RETFIE ;NAO, SAI DA INTERRUPÇÃO
BCF INTCON,T0IF ;SIM, APAGA O FLAG

A seguir, escrevemos o número 108 no registrador TMR0:

MOVLW .108 ;W = 108


MOVWF TMR0 ;REINICIA TMR0

156
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Agora, testamos se o botão 1 está pressionado. Se estiver, desviamos o pro-


grama para onde esta a label “INCREMENTA_CCPR1L”:

BTFSS BOTAO_1 ;BOTAO 1 ESTÁ PRESSIONADO?


GOTO INCREMENTA_CCPR1L ;SIM

Caso contrário, testamos se o botão 2 está pressionado. Se estiver, desviamos


o programa para onde está a label “DECREMENTA_CCPR1L”:

BTFSS BOTAO_2 ;NAO, BOTAO 2 ESTÁ PRESSIONADO?


GOTO DECREMENTA_CCPR1L ;SIM

Se o botão 2 não estiver pressionado, saímos da interrupção:

RETFIE ;NAO, SAI DA INTERRUPÇÃO

Em “INCREMENTA_CCPR1L”, primeiramente testamos se o valor do


CCPR1L é igual a 255, pois, se for, iremos sair da interrupção sem incrementá-lo, caso contrário seu
valor passaria para 0, apagando o LED e queremos que quando o brilho do LED chegar ao máximo,
fique no máximo:

INCREMENTA_CCPR1L
MOVLW .255 ;W = 255
XORWF CCPR1L,W ;W = W XOR CCPRR1L
BTFSC STATUS,Z ;CCPR1L = 255?
RETFIE ;SIM, SAI DA INTERRUPÇÃO

Se o valor do CCPR1L não for igual a 255, incrementamo-lo e, em seguida,


saímos da interrupção:

INCF CCPR1L,F ;NAO, INCREMENTA CCPR1L


RETFIE ;SAI DA INTERRUPÇÃO

Em “DECREMENTA_CCPR1L”, primeiramente testamos se o valor do


CCPR1L é igual a 0, pois, se for, iremos sair da interrupção sem decrementá-lo, caso contrário seu
valor passaria para 255, acendendo o LED no máximo brilho e queremos que quando o LED apagar,
fique apagado:

DECREMENTA_CCPR1L
MOVLW .0 ;W = 0
XORWF CCPR1L,W ;W = W XOR CCPRR1L
BTFSC STATUS,Z ;CCPR1L = 0?
RETFIE ;SIM, SAI DA INTERRUPÇÃO
DECF CCPR1L,F ;NAO, DECREMENTA CCPR1L
RETFIE ;SAI DA INTERRUPÇÃO

Com isso concluímos a rotina de interrupção.


Na seção “CONFIGURACAO DOS REGISTRADORES DE USO
ESPECÍFICO”, vamos configurar o registrador OPTION_REG, para que o Timer 0 seja
incrementado pelo ciclo de instrução (bit 5 = 0), com prescaler de 1:128 (bits 3:0 = 0110):

157
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11010110' ;W = B'11010110'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO COM PRESCALER DE 1:128

Vamos também configurar todos os pinos do PORTA como entradas:


MOVLW B'11111111' ;W = B'11111111'
MOVWF TRISA ;CONFIGURA PORTA COMO ENTRADA

Quanto ao PORTB, vamos configurar o pino RB3/CCP1 como saída, pois é a


saída do sinal PWM, e os demais como entradas:
MOVLW B'11110111' ;W = B'11110111'
MOVWF TRISB ;CONFIGURA BR3/CCP1 COMO SAÍDA E DEMAIS COMO ENTRADA

A seguir, escrevemos o número 255 no registrador PR2:

MOVLW .255 ;W = 255


MOVWF PR2 ;PR2 = 255

Passamos agora para os registradores do banco 0, onde mantemos as


configurações do CMCON para que os pinos RA0 e RA1 possam ser usados como pinos de entrada, e
do INTCON para que a interrupção do Timer 0 esteja habilitada:
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O
MOVLW B'11100000'
MOVWF INTCON ;HABILITA INTERRUPÇÃO DO TIMER 0

A seguir, configuramos o registrador T2CON para ativar o Timer 2 (bit2 = 1),


com prescaler de 1:16 (bit 1 = 1):
MOVLW B'00000110'
MOVWF T2CON ;ATIVA O TIMER 2, COM PRESCALER DE 1:16

Em seguida zeramos o CCPR1L para que quando o circuito for ligado o LED
esteja apagado:
CLRF CCPR1L ;ZERA CCPR1L

Por fim, configuramos o registrador CCP1CON para habilitar o modo PWM,


setando os bits 3 e 2 desse registrador:

158
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

MOVLW B'00001100' ;W = B'00001100'


MOVWF CCP1CON ;HABILITA MODO PWM DO MODULO CCP

Com isso, concluímos a configuração dos registradores de uso específico.


Em seguida, exclua a seção “INICIALIZAÇÃO DAS VARIÁVEIS”.
A seguir vem a rotina principal. Vamos chamar esta parte do programa de
“ROTINA PARA AGUARDAR A INTERRUPÇÃO”, afinal, não tem sentido chamá-la de principal,
já que a rotina mais importante deste programa é a de interrupção.
Aqui o programa ficará executando a instrução CLRWDT até que ocorra a
interrupção:

;**********************************************************************************************

;ROTINA PARA AGUARDAR A INTERRUPÇÃO

CLRWDT ;LIMPA O WDT


GOTO $-1 ;RETORNA UMA LINHA

;**********************************************************************************************

O programa está pronto, tendo ficado assm:

;***********************************************************************************************
; PROGRAMA: DIMMER PARA LED
; VERSÃO 1.0
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /
;***********************************************************************************************

#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A

;***********************************************************************************************

; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF &
_LVP_OFF & _CP_OFF & DATA_CP_OFF

;**********************************************************************************************

; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA

;***********************************************************************************************
; ENTRADAS

#DEFINE BOTAO_1 PORTA,0 ;BOTAO 1 LIGADO EM RA0


#DEFINE BOTAO_2 PORTA,1 ;BOTAO 2 LIGADO EM RA1

;***********************************************************************************************

159
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO


GOTO INICIO ;DESVIA PARA INICIO

;***********************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

BTFSS INTCON,T0IF ;TMR0 ESTOUROU?


RETFIE ;NAO, SAI DA INTERRUPÇÃO
BCF INTCON,T0IF ;SIM, APAGA O FLAG
MOVLW .108 ;W = 108
MOVWF TMR0 ;REINICIA TMR0
BTFSS BOTAO_1 ;BOTAO 1 ESTÁ PRESSIONADO?
GOTO INCREMENTA_CCPR1L ;SIM
BTFSS BOTAO_2 ;NAO, BOTAO 2 ESTÁ PRESSIONADO?
GOTO DECREMENTA_CCPR1L ;SIM
RETFIE ;NAO, SAI DA INTERRUPÇÃO

INCREMENTA_CCPR1L
MOVLW .255 ;W = 255
XORWF CCPR1L,W ;W = W XOR CCPRR1L
BTFSC STATUS,Z ;CCPR1L = 255?
RETFIE ;SIM, SAI DA INTERRUPÇÃO
INCF CCPR1L,F ;NAO, INCREMENTA CCPR1L
RETFIE ;SAI DA INTERRUPÇÃO

DECREMENTA_CCPR1L
MOVLW .0 ;W = 0
XORWF CCPR1L,W ;W = W XOR CCPRR1L
BTFSC STATUS,Z ;CCPR1L = 0?
RETFIE ;SIM, SAI DA INTERRUPÇÃO
DECF CCPR1L,F ;NAO, DECREMENTA CCPR1L
RETFIE ;SAI DA INTERRUPÇÃO

;************************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'11010110' ;W = B'11010110'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO COM PRESCALER DE 1:128
MOVLW B'11111111' ;W = B'11111111'
MOVWF TRISA ;CONFIGURA PORTA COMO ENTRADA
MOVLW B'11110111' ;W = B'11110111'
MOVWF TRISB ;CONFIGURA RB3/CCP1 COMO SAÍDA E DEMAIS COMO ENTRADA
MOVLW .255 ;W = 255
MOVWF PR2 ;PR2 = 255
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O
MOVLW B'11100000'
MOVWF INTCON ;HABILITA INTERRUPÇÃO DO TIMER 0
MOVLW B'00000110'

160
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

MOVWF T2CON ;ATIVA O TIMER 2, COM PRESCALER DE 1:16


CLRF CCPR1L ;ZERA CCPR1L
MOVLW B'00001100' ;W = B'00001100'
MOVWF CCP1CON ;HABILITA MODO PWM DO MODULO CCP

;**********************************************************************************************
; ROTINA PARA AGUARDAR A INTERRUPÇÃO

CLRWDT ;LIMPA O WDT


GOTO $-1 ;RETORNA UMA LINHA

;**********************************************************************************************

END ;FIM DO PROGRAMA

Vamos simular a execução do programa.


No MPLAB, no menu “Project”, clique em “Project Wizard...”.
Na janela “Welcome”, clique em “Avançar”.
Na janela “Step One”, selecione o PIC16F628A e clique em “Avançar”.
Na janela “Step Two”, clique em “Avançar”.
Na janela “Step Three”, clique em “Browse”. Na janela que se abre, em
“Nome do arquivo”, escreva: “Dimmer para LED” e clique em salvar e, na janela “Step Three”,
clique em “Avançar”.
Na janela “Step Four”, selecione o arquivo Dimmer para LED.asm, clique em
“Add” e depois clique em “Avançar”.
Na janela “Summary”, clique em “Concluir”.
No menu “Project”, clique em “Build All”.
Na janela que se abre, clique em “Absolute”.
Verifique se a mensagem “BUILD SUCCEEDED” foi exibida na janela
“Output”. Se não, confira o programa.
No menu “Debugger”, clique em “Select Tool” e depois em “MPLAB SIM”.
No menu “Debugger”, clique em “Settings”. Na aba “Osc/Trace”, digite 4 no
campo “Processor Frequency” e selecione MHz em “Unit”. Na aba “Animation/Real Time Update”,
selecione “Enable Real Time watch updates” e mova a barra toda para a esquerda (Fastest). Clique
em “OK”.
No menu “File”, clique em “Open”. Selecione e abra o arquivo “Dimmer para
LED.asm”.
No menu “View”, clique em “Watch”. Adicione o PORTB e o CCPR1L,
selecionando-os na lista ao lado do botão “Add SFR” e depois clicando nesse botão.
No menu “Debugger”, clique em “Stimulus” e depois em “New Workbook”.
Na coluna “Pin/SFR, clique no campo em branco da primeira linha e selecione
o pino RA0. Na mesma linha, no campo em branco da coluna “Action”, selecione “Set High”. Repita
a operação na segunda linha, mas, selecionando “Set Low”.
Na terceira linha, selecione o pino RA1 e “Set High” e na quarta linha “Set
Low” para o mesmo pino, conforme figura 6 na próxima página.
Clique em “Save”. Escreva o nome Dimmer para LED e clique em “Salvar”.

161
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Figura 6

No menu “Window”, selecione a janela do programa (Dimmer para LED.asm),


na lista da parte de baixo do menu, para visualizá-la.
Na barra de ferramentas do simulador, clique no botão “Reset”.
Vá clicando no botão “Step Into” até chegar na instrução CLRWDT.
Selecione a janela do “Stimulus” no menu “Window”. Na coluna “Fire”,
clique nos botões da primeira e terceira linhas para levar os pinos RA0 e RA1 para nível alto,
simulando que os botões estão soltos.
Vamos medir o tempo entre uma interrupção e outra.
Volte para a janela do programa.
Insira um “breakpoint” na primeira instrução da rotina de interrupção, dando
um duplo clique na sua linha.
Clique no botão “Run” do simulador. O programa irá parar no “breakpoint”.
No menu “Debugger”, clique em “StopWatch”. Clique em “Zero”.
Clique novamente no botão “Run”. Quando o programa parar no “breakpoint”
novamente, volte para a janela do “Stopwatch” e repare que o tempo entre uma interrupção e outra é
de cerca de 19 milissegundos, conforme queríamos:

162
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Figura 7

Agora vamos ver se o CCPR1L está sendo incrementado e decrementado


pelos botões.
Remova o “breakpoint”, dando outro duplo clique na linha da primeira
instrução da interrupção.
Posicione as janelas de forma a poder visualizar a janela “Watch” enquanto
clica nos botões do “Stimulus”:

163
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Figura 8

Clique no botão “Run” do simulador.


No “Stimulus”, clique no botão da coluna “Fire” que leva o pino RA0 para
nível baixo (Set Low) para simular que o botão 1 foi pressionado.
Observe que o CCPR1L foi incrementado até o valor 255.
Agora, clique no botão que leva o pino RA0 para o nível alto, para simular
que o botão 1 foi solto.
Em seguida, clique no botão que leva o pino RA1 para nível 0, simulando que
o botão 2 foi pressionado.
Observe que o CCPR1L foi decrementado até o valor 0 e lá ficou.
Volte o pino RA1 para nível alto, clicando no botão da terceira linha do
Stimulus.

Vamos medir o tempo que o botão 1 deve ficar pressionado para o CCPR1L ir
de 0 até 255.
Clique no botão “Halt” do simulador.
Insira um “breakpoint” na linha que contem a instrução RETFIE, que é
executada quando o valor do CCPR1L for igual a 255:

164
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Figura 9

Na janela do “Stopwatch”, clique em “Zero”.


Clique no botão que leva o pino RA0 para nível baixo simulando que o botão
1 foi pressionado.
Clique no botão “Run” do simulador.
Quando o programa parar no “breakpoint”, veja, no “Stopwatch” que
transcorreu 4,84 segundos, conforme havíamos projetado:

165
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED

Figura 10

Remova o “breakpoint”.
Clique no botão “Run” do simulador.
No “Stimulus”, volte o pino RA0 para nível alto.
Repare que, quando o valor do CCPR1L é igual a 255, o pino RB3 fica fixo
em nível alto. Por outro lado, quando o valor do CCPR1L é igual a 0, ele fica fixo em nível baixo.
Com o valor do CCPR1L num ponto intermediário, o estado do RB3 varia
entre 0 e 1, indicando que temos o sinal PWM nesse pino.
Grave o programa no microcontrolador, monte o circuito na protoboard e
comprove o seu funcionamento. É muito interessante.
Se usarmos um relé de estado sólido, ligado no pino RB3 podemos controlar
equipamentos de maior potência, como por exemplo, lâmpadas incandescentes, aquecedores, motores
elétricos, etc.
Aqui termina esta parte do tutorial. Espero que tenha sido útil para você. Na
próxima parte iremos desenvolver um frequencímetro. Até lá!

166
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Parte 8
Frequencímetro

Nesta parte do tutorial, iremos montar um frequencímetro capaz de medir


frequências entre 1 Hz e 9.999 Hz, com resolução de 1 Hz, exibindo o valor da frequência num
display de LED de quatro dígitos.
Trata-se de uma montagem didática, cujo objetivo é conhecer o modo Capture
do módulo CCP do PIC16F628A, bem como, saber como realizar operações de subtração com
registradores.
Como fonte do sinal, cuja frequência será medida, iremos utilizar um circuito
composto por um C.I. LM555N.
O esquema completo pode ser visto na figura abaixo.

Figura 1

POT1 é um potenciômetro linear de 100 k, onde podemos variar a frequência


do sinal. Trocando o capacitor C2, mudamos a escala da frequência. Valores obtidos nos testes:

10 µF – 1 Hz a 40 Hz
1 µF – 16 Hz a 460 Hz
100 nF – 150 Hz a 4.100 Hz
10 nF – 1.400 Hz a 9.999 Hz

167
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Utilizaremos o modo “Capture” do módulo CCP do PIC16F628A para medir


a frequência do sinal. Esse modo funciona da seguinte forma:
Quando ocorre uma mudança de nível no pino RB3/CCP1 (que deve estar
configurado como entrada) o valor do registrador TMR1H é copiado para o registrador CCPR1H e o
valor do registrador TMR1L é copiado para o registrador CCPR1L. TMR1H e TMR1L são os
registradores do Timer 1. Vale relembrar o funcionamento do Timer 1:
O Timer 1 é um circuito do microcontrolador que incrementa o par de
registradores TMR1L e TMR1H a partir do ciclo de instrução ou a partir de um sinal externo aplicado
no pino 13 (RB7/T1OSI/PGD). Um cristal pode ser ligado entre esse pino e o pino 12
(RB6/T1OSO/T1CKI/PGC), para fazer funcionar um oscilador interno otimizado para a frequência
de 32.768 KHz, ideal para relógios.
Quando o Timer 1 é incrementado, esse incremento ocorre no TMR1L, cujo
valor pode chegar até 255. No próximo incremento ele estoura, isto é, volta a 0, e o TMR1H é
incrementado. O TMR1H, cujo valor também pode chegar a 255, irá estourar no incremento de n.º
65.536 (256 x 256). Assim, esse par de registradores pode ser incrementado até o valor decimal de
65.535.
O Timer 1 possui um prescaler que pode ser configurado para 1:1, 1:2, 1:4 ou
1:8.
Para usar o modo “Capture” do módulo CCP, o Timer 1 deve estar
configurado para ser incrementado ou a partir do ciclo de instrução ou a partir de um sinal externo
sincronizado com o oscilador principal do microcontrolador. Esse sinal externo não é o sinal cuja
frequência será medida, mas sim, um sinal de frequência conhecida. No nosso frequencímetro, iremos
configurar o Timer 1 para que ele seja incrementado pelo ciclo de instrução, cujo período é de 1
microssegundo, já que a frequência de oscilação é de 4 MHz.
O Timer 1 é incrementado continuamente. Quando o módulo CCP detecta
uma mudança de nível no pino CCP1, ele realiza a cópia dos valores dos registradores.
O módulo CCP pode ser configurado para que a cópia ocorra todas as vezes
que houver mudança de nível no pino CCP1, sendo que podemos escolher se a mudança a ser
detectada será de nível baixo para alto ou de alto para baixo. Também podemos configurá-lo para que
essa cópia ocorra a cada 4 ou ainda a cada 16 mudanças, mas, somente de nível baixo para alto. No
nosso projeto, iremos configurá-lo para que efetue a cópia a cada mudança de nível baixo para alto.
Conhecendo-se o intervalo de tempo entre cada incremento do Timer 1 e o
número de incrementos que ocorreram entre dois processos de cópia, pode-se calcular o tempo entre
as duas cópias, o que equivale ao período do sinal, e, a partir daí, calcula-se a sua frequência.
Para calcular o número de incrementos, subtrai-se os valores que o Timer 1
apresentava nas duas cópias.
Para calcular o período do sinal, multiplicamos o número de incrementos
pelo intervalo de tempo entre cada incremento, que é de 0,000001 segundo (ciclo de instrução do
oscilador interno de 4 MHz). O resultado é o período do sinal, em segundos.
Para encontrarmos a frequência, calculamos o seu inverso:

1
Frequência do sinal =
número de incrementos x 0,000001

168
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Simplificando essa fórmula obtemos:

1 1 1
Frequência do sinal = x
número de incrementos 0,000001

Frequência do sinal = 1 x 1.000.000


número de incrementos

1.000.000
Frequência do sinal =
número de incrementos

Portanto, para obtermos a frequência do sinal, em Hz, devemos dividir o


número 1.000.000 (decimal) pelo número de incrementos do Timer 1 ocorridos entre as duas cópias
dos registradores.
Queremos que o nosso frequencímetro tenha uma resolução de 1 Hz, isto é,
que ele seja capaz de distinguir duas frequências cuja diferença seja de apenas 1 Hz.
O período de uma frequência de 1 Hz é de 1 segundo e, portanto, o Timer 1
será incrementado 1.000.000 de vezes entre duas cópias consecutivas dos registradores. Para uma
frequência de 2 Hz, cujo período é de 500 milissegundos, o Timer 1 será incrementado 500.000 vezes.
Assim, no começo da escala, não haverá problemas para distinguir frequências separadas por 1 Hz,
pois, a diferença entre o número de incrementos é grande.
À medida que a frequência do sinal aumenta, a diferença entre o número de
incrementos para duas frequências separadas por 1 Hz diminui. Por exemplo, o período de uma
frequência de 998 Hz é de 1,002 milissegundo, portanto o Timer 1 será incrementado 1.002 vezes
durante esse período. O período de uma frequência de 999 Hz é de 1,001 milissegundo e, assim, o
Timer 1 será incrementado 1.001 vezes durante esse período. Até aqui ainda é possível diferenciar
frequências de 1 Hz de diferença.
A partir daí o número de incrementos do Timer 1 passa a ser o mesmo para
duas frequências próximas. Por exemplo, para uma frequência de 1.195 Hz, cujo período é de 836,8
microssegundos, o número de incrementos do Timer 1 é de 836 e para uma frequência de 1.196 Hz,
cujo período é de 836,1 microssegundos, o número de incrementos também é de 836. Portanto, o
frequencímetro não conseguirá distinguir essas frequências.
Uma forma de resolver esse problema é efetuar o cálculo da frequência depois
de um determinado número de ciclos do sinal, pois o Timer 1 terá sido incrementado mais vezes,
voltando a apresentar diferença entre a quantidade de incrementos para duas frequências próximas.
No fim da escala, onde a situação é pior, a frequência de 9.998 Hz tem um
período de 100,02 microssegundos, enquanto a frequência de 9.999 Hz tem um período de 100,01
microssegundos.

169
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

A diferença entre os períodos das duas é de 0,01 microssegundo. Isto significa


que, após 100 ciclos do sinal, teremos uma diferença de 1 incremento do Timer 1 para essas duas
frequências. Veja:
Durante 100 ciclos do sinal de frequência de 9.998 Hz, o Timer 1 será
incrementado 10.002 vezes, enquanto que durante 100 ciclos do sinal de frequência de 9.999 Hz ele
terá sido incrementado 10.001 vezes.
Mas, não podemos esperar 100 ciclos para frequências baixas, pois, demorará
muito tempo para o frequencímetro medi-las. Para uma frequência de 1 Hz, por exemplo, 100 ciclos
correspondem a 100 segundos!
Vimos que até 1.000 Hz, o número de incrementos para duas frequências
separadas de 1 Hz ainda era diferente. Então, iremos aguardar 100 ciclos somente para frequências
maiores que 1.000 Hz.
O número de incrementos do Timer 1 durante 1 ciclo do sinal de uma
frequência de 1.000 Hz é igual a 1.000. Portanto, no nosso programa, quando o número de
incrementos do Timer 1 entre a 1ª e a 2ª cópia dos valores dos registradores for maior do que 1.000,
indicando que a frequência é menor do que 1.000 Hz, vamos efetuar o cálculo imediatamente, caso
contrário, vamos aguardar 100 ciclos do sinal e efetuar o cálculo com base no número de incrementos
ocorridos entre a 1ª e a 101ª cópias.
Nesse último caso, a fórmula para calcular a frequência muda para:

100.000.000
Frequência do sinal =
número de incrementos

Pois, o número de incrementos é referente a 100 ciclos do sinal.


No nosso programa teremos três rotinas de interrupção: a do Timer 0, a do
Timer 1 e a do módulo CCP.
Na rotina de interrupção do Timer 0, iremos controlar os dígitos do display.
Na do Timer 1, iremos contar o número de estouros desse timer que ocorreram entre as cópias dos
registradores, necessário para o cálculo do número de incrementos. Na interrupção do módulo CCP,
iremos calcular o número de incrementos.
A frequência do sinal será calculada na rotina principal do programa.
No MPLAB, abra o arquivo Contador.asm da parte 5 deste tutorial e salve-o
com o nome Frequencimetro.asm.
Na seção VARIAVEIS, exclua DB1_BTA, DB1_BTB, DB2_BTA e
DB2_BTB e crie as seguintes variáveis: CENTENA, MILHAR, REG1A, REG2A, REG1B, REG2B,
REG3B, REG1C, REG2C, REG3C, DISP_DELAY, CAPTURA, MILHAO_1, MILHAO_2,
MILHAO_3, MILHAO_4, UNI_TEMP, DEZ_TEMP, CEN_TEMP, MIL_TEMP:

;***********************************************************************************************
; VARIÁVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

W_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR W


STATUS_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR STATUS
FLAGS ;REGISTRADOR DE FLAGS
UNIDADE ;UNIDADE DA FREQUÊNCIA

170
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

DEZENA ;DEZENA DA FREQUÊNCIA


CENTENA ;CENTENA DA FREQUÊNCIA
MILHAR ;MILHAR DA FREQUÊNCIA
REG1A ;REGISTRADOR MENOS SIGNIFICATIVO DA 1ª CAPTURA
REG2A ;REGISTRADOR MAIS SIGNIFICATIVO DA 1ª CAPTURA
REG1B ;REGISTRADOR MENOS SIGNIFICATIVO DA 2ª CAPTURA
REG2B ;REGISTRADOR MAIS SIGNIFICATIVO DA 2ª CAPTURA
REG3B ;REGISTRADOR QUE CONTA OS ESTOUROS DO TIMER 1
REG1C ;REGISTRADOR MENOS SIGNIFICATIVO DO Nº DE INCREMENTOS
REG2C ;REGISTRADOR INTERMEDIÁRIO DO Nº DE INCREMENTOS
REG3C ;REGISTRADOR MAIS SIGNIFICATIVO DO Nº DE INCREMENTOS
DISP_DELAY ;CONTA 250 MS ENTRE EXIBIÇÔES DA FREQUÊNCIA
CAPTURA ;CONTA AS CAPTURAS DO MÓDULO CCP
MILHAO_1 ;PRIMEIRO REGISTRADOR DO NÚMERO 1.000.000
MILHAO_2 ;SEGUNDO REGISTRADOR DO NÚMERO 1.000.000
MILHAO_3 ;TERCEIRO REGISTRADOR DO NÚMERO 1.000.000
MILHAO_4 ;QUARTO REGISTRADOR DO NÚMERO 100.000.000
UNI_TEMP ;REGISTRADOR TEMPORÁRIO DA UNIDADE
DEZ_TEMP ;REGISTRADOR TEMPORÁRIO DA DEZENA
CEN_TEMP ;REGISTRADOR TEMPORÁRIO DA CENTENA
MIL_TEMP ;REGISTRADOR TEMPORÁRIO DO MLHAR

ENDC ;FIM DO BLOCO DE MEMORIA

;***********************************************************************************************

Exclua a seção CONSTANTES.


Na seção SAIDAS, defina a label DIG_UNI para o RA0, que será o dígito da
unidade, DIG_DEZ para o RA1, que será o dígito da dezena, DIG_CEN para o RA2, que será o
dígito da centena e DIG_MIL para o RA3, que será o dígito do milhar, SEG_A para o RB0, SEG_B
para o RB1, SEG_C para o RB2, SEG_D para o RB4, SEG_E para o RB5, SEG_F para o RB6 e
SEG_G para o RB7 (teremos de controlar individualmente os segmentos do display, já que o pino
RB3 é a entrada do sinal:

;***********************************************************************************************
; SAÍDAS

#DEFINE DIG_UNI PORTA,0 ;SE = 1, DIGITO DA UNIDADE ATIVADO


#DEFINE DIG_DEZ PORTA,1 ;SE = 1, DIGITO DA DEZENA ATIVADO
#DEFINE DIG_CEN PORTA,2 ;SE = 1, DIGITO DA CENTENA ATIVADO
#DEFINE DIG_MIL PORTA,3 ;SE = 1, DIGITO DO MILHAR ATIVADO
#DEFINE SEG_A PORTB,0 ;SEGMENTO A DO DISPLAY
#DEFINE SEG_B PORTB,1 ;SEGMENTO B DO DISPLAY
#DEFINE SEG_C PORTB,2 ;SEGMENTO C DO DISPLAY
#DEFINE SEG_D PORTB,4 ;SEGMENTO D DO DISPLAY
#DEFINE SEG_E PORTB,5 ;SEGMENTO E DO DISPLAY
#DEFINE SEG_F PORTB,6 ;SEGMENTO F DO DISPLAY
#DEFINE SEG_G PORTB,7 ;SEGMENTO G DO DISPLAY

;***********************************************************************************************

Exclua a seção ENTRADAS.


Na seção FLAGS, defina CALCULAR_FREQ para o bit 0, CEM_MILHOES
para o bit 1 e ATUALIZA_DISP para o bit 2:

171
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

;***********************************************************************************************
; FLAGS

#DEFINE CALCULAR_FREQ FLAGS,0 ;SE = 1 A FREQUENCIA PODE SER CALCULADA


#DEFINE CEM_MILHOES FLAGS,1 ;SE = 100.000.000/N.º DE INCREMENTOS
#DEFINE ATUALIZA_DISP FLAGS,2 ;SE = 1, ATUALIZAR VALOR DO DISPLAY

;***********************************************************************************************

Na rotina de interrupção, primeiramente temos de testar qual das três


interrupções ocorreu.
Para verificar se a interrupção foi a do Timer 0, devemos testar o bit T0IF do
registrador INTCON. Para a do Timer 1, o bit TMR1IF do registrador PIR1. Para a do módulo CCP, o
bit CCP1IF também do registrador PIR1. Portanto, a rotina de interrupção começa assim:

;*************************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

MOVWF W_TEMP ;SALVA W


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;CHAVEIA PARA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS
BTFSC INTCON,T0IF ;INTERRUPÇÃO DO TIMER 0?
GOTO INT_TIMER_0 ;SIM
BTFSC PIR1,TMR1IF ;NÃO, INTERRUPÇÃO DO TIMER 1?
GOTO INT_TIMER_1 ;SIM
BTFSC PIR1,CCP1IF ;NÃO, INTERRUPÇÃO DO MÓDULO CCP?
GOTO INT_CCP ;SIM
GOTO SAI_INT ;NÃO, SAI DA INTERRUPÇÃO

O prescaler do Timer 0 será configurado para 8:1. Assim, ele irá gerar uma
interrupção a cada 2,048 milissegundos (0,000001 x 256 x 8). No intervalo entre cada interrupção do
Timer 0, apenas um dos dígitos estará ativo. A cada interrupção, desativaremos o dígito que estiver
ativo e ativaremos o próximo. Com isso, cada dígito ficará ativado por 2,048 milissegundos e
desativado por 6,144 milissegundos, num período total de 8,192 milissegundos, o que corresponde a
uma frequência de 122 Hz.
Na rotina de interrupção do Timer 0, primeiramente apagamos o flag TMR0IF:

INT_TIMER_0

BCF INTCON,T0IF ;APAGA FLAG

O frequencímetro estará constantemente medindo a frequência do sinal. Como,


a frequência do sinal poderá variar e, para que não tenhamos o efeito desagradável da rápida e
repetida alteração do valor mostrado no display, vamos atualizá-lo a cada cerca de 0,5 segundo.
Para isso, a cada interrupção do Timer 0, decrementamos a variável
DISP_DELAY. Quando o seu valor chegar a 0, setamos o flag ATUALIZA_DISP:

172
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

DECFSZ DISP_DELAY,F ;DECREMENTA DISP_DELAY. DISP_DELAY = 0?


GOTO TESTA_DIG_ATIVO ;NÃO
BSF ATUALIZA_DISP ;SIM, SETA FLAG

A seguir, testamos qual dígito está ativado:


TESTA_DIG_ATIVO
BTFSC DIG_MIL ;DÍGITO DO MILHAR ESTÁ ATIVADO?
GOTO CONT_DIG_CEN ;SIM
BTFSC DIG_CEN ;NÃO, DÍGITO DA CENTENA ESTÁ ATIVADO?
GOTO CONT_DIG_DEZ ;SIM
BTFSC DIG_DEZ ;NÃO, DÍGITO DA DEZENA ESTÁ ATIVADO?
GOTO CONT_DIG_UNI ;SIM
GOTO CONT_DIG_MIL ;NAO

Se for o dígito do milhar, desativamo-lo, apagamos os segmentos, ativamos o


dígito da centena, copiamos o valor da centena para o W e desviamos o programa para a subrotina de
conversão de binário para 7 segmentos:
CONT_DIG_CEN
BCF DIG_MIL ;DESATIVA DÍGITO DO MILHAR
BCF SEG_A ;APAGA SEGMENTO
BCF SEG_B ;APAGA SEGMENTO
BCF SEG_C ;APAGA SEGMENTO
BCF SEG_D ;APAGA SEGMENTO
BCF SEG_E ;APAGA SEGMENTO
BCF SEG_F ;APAGA SEGMENTO
BCF SEG_G ;APAGA SEGMENTO
BSF DIG_CEN ;ATIVA DÍGITO DA CENTENA
MOVF CENTENA,W ;COPIA CENTENA PARA O W
GOTO CONV_BIN_7_SEG ;CHAMA SUBROTINA

Repare que estamos controlando cada segmento do display individualmente,


diferentemente do que fizemos nos programas das parte 5 e 6 deste tutorial, pois, o PORTB não é
exclusivo dos displays (RB3 é a entrada do sinal).
Procedimento semelhante é feito para os outros dígitos:

CONT_DIG_DEZ
BCF DIG_CEN ;DESATIVA DÍGITO DA CENTENA
BCF SEG_A ;APAGA SEGMENTO
BCF SEG_B ;APAGA SEGMENTO
BCF SEG_C ;APAGA SEGMENTO
BCF SEG_D ;APAGA SEGMENTO
BCF SEG_E ;APAGA SEGMENTO
BCF SEG_F ;APAGA SEGMENTO
BCF SEG_G ;APAGA SEGMENTO
BSF DIG_DEZ ;ATIVA DÍGITO DA DEZENA
MOVF DEZENA,W ;COPIA DEZENA PARA O W
GOTO CONV_BIN_7_SEG ;CHAMA SUBROTINA

CONT_DIG_UNI
BCF DIG_DEZ ;DESATIVA DÍGITO DA DEZENA
BCF SEG_A ;APAGA SEGMENTO
BCF SEG_B ;APAGA SEGMENTO
BCF SEG_C ;APAGA SEGMENTO
BCF SEG_D ;APAGA SEGMENTO

173
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

BCF SEG_E ;APAGA SEGMENTO


BCF SEG_F ;APAGA SEGMENTO
BCF SEG_G ;APAGA SEGMENTO
BSF DIG_UNI ;ATIVA DÍGITO DA UNIDADE
MOVF UNIDADE,W ;COPIA UNIDADE PARA O W
GOTO CONV_BIN_7_SEG ;CHAMA SUBROTINA
CONT_DIG_MIL
BCF DIG_UNI ;DESATIVA DÍGITO DA UNIDADE
BCF SEG_A ;APAGA SEGMENTO
BCF SEG_B ;APAGA SEGMENTO
BCF SEG_C ;APAGA SEGMENTO
BCF SEG_D ;APAGA SEGMENTO
BCF SEG_E ;APAGA SEGMENTO
BCF SEG_F ;APAGA SEGMENTO
BCF SEG_G ;APAGA SEGMENTO
BSF DIG_MIL ;ATIVA DÍGITO DA MILHAR
MOVF MILHAR,W ;COPIA MILHAR PARA O W
GOTO CONV_BIN_7_SEG ;CHAMA SUBROTINA

Na subrotina de conversão de binário para 7 segmentos, primeiramente


somamos ao registrador PCL o valor que está no registrador W. Em seguida, o programa irá pular um
número de linhas igual ao valor do W, caindo numa instrução GOTO que o direcionará para uma
sequência de instruções que ativam os segmentos que formam o número a ser mostrado, saindo da
interrupção ao final:

CONV_BIN_7_SEG
ADDWF PCL,F ;PCL = PCL + W
GOTO NUM_ZERO ;W = 0
GOTO NUM_UM ;W = 1
GOTO NUM_DOIS ;W = 2
GOTO NUM_TRES ;W = 3
GOTO NUM_QUATRO ;W = 4
GOTO NUM_CINCO ;W = 5
GOTO NUM_SEIS ;W = 6
GOTO NUM_SETE ;W = 7
GOTO NUM_OITO ;W = 8
GOTO NUM_NOVE ;W = 9
NUM_ZERO
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_E ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
GOTO SAI_INT
NUM_UM
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
GOTO SAI_INT
NUM_DOIS
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_E ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT

174
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

NUM_TRES
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT
NUM_QUATRO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT
NUM_CINCO
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT
NUM_SEIS
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_E ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT
NUM_SETE
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
GOTO SAI_INT
NUM_OITO
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_E ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT
NUM_NOVE
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT

Assim, concluímos a rotina de interrupção do Timer 0.


O cálculo da frequência do sinal será feito a partir do número de incrementos
que ocorreram no Timer 1 entre as duas capturas do módulo CCP (entre a 1ª e a 2ª quando o número
de incrementos é maior do que 1000, caso contrário, entre a 1ª e a 101ª ).
Para calcularmos o número de incrementos, iremos subtrair os valores que o
Timer 1 apresentava nas duas capturas.

175
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Quando falamos em valor do Timer 1 estamos nos referindo ao valor formado


pelos registradores TMR1H e TMR1L. Então, esses dois registradores de 8 bits equivalem a um de 16
bits:

TMR1H TMR1L Timer 1


+ =
11111111 11111111 1111111111111111

Para obtermos o valor decimal do Timer 1 a partir dos valores decimais dos
registradores, multiplicamos o valor do TMR1H por 256 e somamos ao valor do TMR1L.
Por exemplo, se o valor decimal do TMR1H é 35 e o valor decimal do
TMR1L é 23, o valor decimal do Timer 1 é igual a 35 x 256 + 23 = 8983.
Para calcularmos os valores decimais dos registradores a partir do valor
decimal do Timer 1, dividimos o valor decimal do Timer 1 por 256. A parte inteira da divisão é o
valor do TMR1H. A seguir, multiplicamos a parte decimal por 256 e obtemos o valor do TMR1L.
Por exemplo, se o valor decimal do Timer 1 é de 8983, dividindo esse valor
por 256, obtemos 35,08984375. Portanto, TMR1H = 35. Multiplicando 0,08984375 por 256, obtemos
23, que é o valor do TMR1L.
No nosso projeto, o Timer 1 será incrementado continuamente. Quando
ocorrer uma captura, ele estará com um valor qualquer entre 0 e 65.535.
Vamos supor que na primeira captura, o valor do Timer 1 seja igual a 34.328
(TMR1H = 134 e TMR1L = 24) e que na segunda captura, o valor do Timer 1 seja igual a 43.436
(TMR1H = 169 e TMR1L = 172).
Se calcularmos a diferença entre esses dois valores, obtemos 9.108. Então, o
Timer 1 teria sido incrementado esse número de vezes entre as duas capturas. Mas, também pode ser
que ele tenha sido incrementado 74.644 vezes. Isso porque ele pode ter estourado entre as duas
capturas. Partindo do valor 34.328, com 31.208 incrementos ele irá estourar (65.536 – 34.328).
Somando 31.208 com 43.436, obtemos que ele foi incrementado 74.644 vezes.
Para sabermos se o Timer 1 estourou entre as capturas, usaremos a variável
REG3B, que iremos incrementar a cada vez que o Timer 1 estourar. Essa variável será como se fosse
o terceiro registrador do Timer1.
Assim, não haverá dúvida quanto ao número de incrementos que ocorreram
no Timer 1 entre as duas capturas, pois, poderemos contar até 16.777.215 incrementos, sendo que o
número máximo de incrementos será de 1.000.000 quando a frequência do sinal for de 1 Hz.
Para obtermos o valor decimal do Timer 1 a partir do valor decimal desses
três registradores, multiplicamos o valor do REG3B por 65.536, o valor do TMR1H por 256 e
somamos os resultados ao valor do TMR1L.
No exemplo anterior, onde na 2ª captura, TMR1H = 169 e TMR1L = 172, se o
REG3 for igual a 1, teremos o seguinte valor para o Timer 1: 1 x 65536 + 169 x 256 + 172 = 108.972.
Subtraindo o valor do Timer 1 na 1ª captura, que era igual a 34.328, obtemos 74.644.
A rotina de interrupção do Timer 1 consiste em apagar o flag TMR1IF e
incrementar a variável REG3B:
INT_TIMER_1

BCF PIR1,TMR1IF ;APAGA FLAG


INCF REG3B ;INCREMENTA REG3B
GOTO SAI_INT ;SAI DA INTERRUPÇÃO

176
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Na rotina de interrupção do módulo CCP iremos os calcular o número de


incrementos que ocorreram no Timer 1 durante o período do sinal.
O processo de cálculo do número de incrementos consistirá nas seguintes
etapas:
a) Na 1ª captura do módulo CCP, copiamos o valor do registrador CCPR1L
para a variável REG1A e o valor do registrador CCPR1H para a variável REG2A. Lembre-se que na
captura, o módulo CCP copia o valor do registrador TMR1H para o registrador CCPR1H e o valor do
registrador TMR1L para o registrador CCPR1L. Portanto, teremos o valor do Timer 1 na 1ª captura
salvo em REG2A e REG1A.
b) Na 2ª captura, copiamos o valor do registrador CCPR1L para a variável
REG1B e o do registrador CCPR1H para a variável REG2B.
Nesse momento, temos em REG2A e REG1A, o valor do Timer 1 no
momento da 1ª captura e em REG3B, REG2B e REG1B, o valor do Timer 1 no momento da 2ª
captura.
Não precisamos de uma variável REG3A, porque a variável REG3B serve
apenas para registrarmos os estouros que ocorreram no Timer 1 entre as duas capturas.
c) Efetuamos o cálculo da diferença entre os valores do Timer 1 na 1ª e na 2ª
captura para encontrar o número de incrementos.
d) Se o número de incrementos for maior do que 1.000, podemos calcular a
frequência, caso contrário, aguardamos a 101ª captura, quando, então, copiamos o valor do
registrador CCPR1L para a variável REG1B e o do registrador CCPR1H para a variável REG2B e
efetuamos o cálculo do número de incrementos e depois poderemos calcular a frequência.
Para indicar que a frequência pode ser calculada, iremos setar o flag
CALCULAR_FREQ.
Começamos por apagar o flag CCP1IF:

INT_CCP
CLRF PIR1,CCP1IF ;APAGA FLAG

Em seguida, precisamos saber se a interrupção é referente à 1ª captura.


Incrementamos a variável CAPTURA e testamos se o seu valor é igual a 1. Se for, tratar-se-á da 1ª
captura:

INCF CAPTURA,F ;INCREMENTA CAPTURA


MOVLW .1 ;W = 1
XORWF CAPTURA,W ;W = CAPTURA XOR 1
BTFSC STATUS,Z ;CAPTURA = 1?
GOTO PRIMEIRA_CAPTURA ;SIM

Na subrotina PRIMEIRA_CAPTURA, copiamos o valor do registrador


CCPR1L para o REG1A e o do CCPR1H para o REG2A, zeramos o REG3B e saímos da interrupção:

PRIMEIRA_CAPTURA
MOVF CCPR1L,W ;W = CCPR1L
MOVWF REG1A ;REG1A = CCPR1L
MOVF CCPR1H,W ;W = CCPR1H
MOVWF REG2A ;REG2A = CCPR1H
CLRF REG3B ;REG3B = 0
GOTO SAI_INT ;SAI DA INTERRUPÇÃO

177
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Na próxima interrupção do módulo CCP, a variável CAPTURA será


incrementada novamente e seu valor será igual a 2, o que significará que é a 2ª captura, então
desviamos para a subrotina CALC_INC:

MOVLW .2 ;NÃO, W = 2
XORWF CAPTURA,W ;W = CAPTURA XOR 2
BTFSC STATUS,Z ;CAPTURA = 2?
GOTO CALC_INC ;SIM

Na subrotina CALC_INC, copiamos o valor do registrador CCPR1L para a


variável REG1B e o do CCPR1H para a variável REG2B:

CALC_INC
MOVF CCPR1L,W ;W = CCPR1L
MOVWF REG1B ;REG1B = CCPR1L
MOVF CCPR1H,W ;W = CCPR1H
MOVWF REG2B ;REG2B = CCPR1H

Nesse momento, nos registradores REG2A e REG1A, temos o valor do Timer


1 na 1ª captura e nos registradores REG3B, REG2B e REG1B, o seu valor na 2ª captura e devemos
calcular a diferença entre esses valores.
Portanto, temos de efetuar uma operação de subtração entre um número
formado por 3 registradores de 8 bits e outro formado por 2 registradores de 8 bits:

REG3B REG2B REG1B -


REG2A REG1A
=

Os passos para efetuar essa operação são semelhantes aos realizados numa
operação de subtração no sistema decimal.
Por exemplo, para subtrair o número 32 do número 164, primeiramente
subtraímos os algarismos menos significativos, ou seja, as unidades:

1 6 4 -
0 3 2
2 =

A seguir, subtraímos os próximos algarismos em ordem de significância, as


dezenas:
1 6 4 -
0 3 2
3 2 =

Por fim, subtraímos as centenas:

1 6 4 -
0 3 2
1 3 2 =

178
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Com registradores, fazemos a mesma coisa; subtraímos os registradores


menos significativos (REG1B – REG1A), depois os próximos (REG2B – REG2A) e assim por diante.
Mas, a coisa complica um pouco quando o algarismo a ser subtraído é menor,
como, por exemplo, na operação 192 – 65:

1 9 2 -
0 6 5
=

Nesse caso, para subtrair a unidade, temos de decrementar a dezena e somar


10 à unidade:

1 9 2 -
1 8 12
0 6 5
7 =

Depois, subtraímos a dezena:

1 9 2 -
1 8 12
0 6 5
2 7 =

E, finalmente a centena:

1 9 2 -
1 8 12
0 6 5
1 2 7 =

Se, ao precisar decrementar a dezena, o seu valor for igual a 0, teremos de


decrementar a centena, somar 10 à dezena, decrementá-la, e somar 10 à unidade, como por exemplo,
na operação 102 – 34:

1 0 2 -
0 3 4
=

179
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

1 0 2 -
0 10 2
0 3 4
=

1 0 2 -
0 10 2
0 9 12
0 3 4
8 =

1 0 2 -
0 10 2
0 9 12
0 3 4
6 8 =

1 0 2 -
0 10 2
0 9 12
0 3 4
0 6 8 =

Ao subtrair registradores, para sabermos se o número subtraído era maior,


verificamos se o resultado da subtração foi negativo. Para sabermos se o registrador que foi
decrementado era igual a 0, testamos se o seu valor se tornou igual a 255.
O fluxograma a seguir mostra a sequência de operações que faremos.

180
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

REG1B -
REG1A

RESULTADO RESULTADO
POSITIVO NEGATIVO

REG2B - DECREMENTA
REG2A REG2B

RESULTADO RESULTADO
REG2B ≠ 255 REG2B = 255
POSITIVO NEGATIVO

OPERAÇÃO DECREMENTA REG2B - DECREMENTA


CONCLUÍDA REG3B REG2A REG3B

OPERAÇÃO RESULTADO RESULTADO REG2B -


CONCLUÍDA POSITIVO NEGATIVO REG2A

OPERAÇÃO DECREMENTA RESULTADO RESULTADO


CONCLUÍDA REG3B POSITIVO NEGATIVO

OPERAÇÃO OPERAÇÃO DECREMENTA


CONCLUÍDA CONCLUÍDA REG3B

OPERAÇÃO
CONCLUÍDA

Figura 2

181
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Começamos subtraindo os registradores menos significativos, ou seja,


fazemos REG1B – REG1A:
MOVF REG1A,W ;W = REG1A
SUBWF REG1B,F ;REG1B = REG1B - REG1A

A instrução SUBWF REG1B,F subtrai o valor de W, que é igual a REG1A,


do valor de REG1B e salva o resultado no próprio REG1B.
O resultado poderá ser positivo, se REG1B > REG1A ou negativo, se REG1B
< REG1A.
Para sabermos se o resultado foi positivo ou negativo, testamos o bit C do
registrador STATUS, que será igual a 0 se o resultado for negativo.
Se o resultado for positivo, desviamos para a subrotina SEG_SUB, onde
iremos fazer REG2B – REG2A. Se o resultado for negativo, decrementamos o REG2B e, em seguida,
testamos se o seu valor é igual a 255:

BTFSC STATUS,C ;RESULTADO NEGATIVO?


GOTO SEG_SUB ;NÃO
DECF REG2B,F ;SIM, DECREMENTA REG2B
MOVLW .255 ;W = 255
XORWF REG2B,W ;W = REG2B XOR 255
BTFSS STATUS,Z ;REG2B = 255?

Se o valor de REG2B for diferente de 255, desviamos para a subrotina


SEG_SUB, caso contrário, decrementamos REG3B e, a seguir, entramos na subrotina SEG_SUB,
onde fazemos REG2B – REG2A e testamos se o resultado é negativo:

GOTO SEG_SUB ;NÃO


DECF REG3B,F ;SIM, DECREMENTA REG3B
SEG_SUB
MOVF REG2A,W ;W = REG2A
SUBWF REG2B,F ;REG2B = REG2B - REG2A
BTFSC STATUS,C ;RESULTADO NEGATIVO?

Se o resultado for positivo, o processo de subtração terminou, caso contrário,


decrementamos o registrador REG3B e estará concluída a subtração:

BTFSC STATUS,C ;RESULTADO NEGATIVO?


GOTO TST_NR_INC ;NÃO, OPERAÇÃO CONCLUÍDA
DECF REG3B,F ;SIM, DECREMENTA REG3B

Nesse momento, os registradores REG3B, REG2B e REG1B contém o valor


do resultado da subtração, ou seja, o número de incrementos que o Timer 1 sofreu entre a 1ª e a 2ª
capturas. Agora temos de verificar se este valor é maior do que 1.000.
Quando o número de incrementos é igual a 1.000, REG2B = 3 e REG1B =
232 (3 x 256 + 232 = 1000).
Portanto, se o valor de REG2B for maior do que 3 o número de incrementos
será maior do que 1.000.
Se RG2B for igual a 3, REG1B terá de ser maior do que 232 para que o
número de incrementos seja maior do que 1.000.

182
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Primeiramente, verificamos se REG2B é maior do que 3, copiando seu valor


para W e executando a instrução SUBLW .3 Essa instrução efetua a operação 3 – W e salva o
resultado no W.:

TST_NR_INC
MOVF REG2B,W ;W = REG2B
SUBLW .3 ;W = 3 – REG2B

A seguir, verificamos qual foi o resultado. Se REG2B for igual ou menor do


que 3, o resultado será positivo. Nesse caso, desviamos para a subrotina TST_MENOR_3:
BTFSC STATUS,C ;REG2B MAIOR DO QUE 3?
GOTO TST_MENOR_3 ;NAO

Se REG2B for maior do que 3, o resultado será negativo, indicando que o


número de incrementos é maior do que 1.000, o que quer dizer que a frequência pode ser calculada.
Para indicar isso, setamos o flag CALCULAR_FREQ. Primeiramente, zeramos a variável
CAPTURA, para que a próxima seja novamente a 1ª.:
CLRF CAPTURA ;SIM, CAPTURA = 0

O cálculo da frequência, que é feito na rotina principal, demora um certo


tempo. Por isso, a seguir, testamos se o flag CALCULAR_FREQ está setado, pois, se estiver, o
cálculo iniciado com os valores da captura anterior ainda não foi concluído. Nesse caso, saímos da
interrupção, ou seja, os valores dessa captura serão desprezados:

BTFSC CALCULAR_FREQ ;FREQUÊNCIA SENDO CALCULADA?


GOTO SAI_INT ;SIM, SAI DA INTERRUPÇÃO

Se o flag não estiver setado, desviamos para a subrotina COPIA_NR_INC :


GOTO COPIA_NR_INC ;NAO, DESVIA

Nessa subrotina, copiamos os valores das variáveis REG1B, REG2B e


REG3B para as variáveis REG1C, REG2C e REG3C, setamos o flag CALCULAR_FREQ e saímos
da interrupção:
COPIA_NR_INC
MOVF REG1B,W ;W = REG1B
MOVWF REG1C ;REG1C = REG1B
MOVF REG2B,W ;W = REG2B
MOVWF REG2C ;REG2C = REG2B
MOVF REG3B,W ;W = REG3B
MOVWF REG3C ;REG3C = REG3B
BSF CALCULAR_FREQ ;SETA FLAG
GOTO SAI_INT ;SAI DA INTERRUPÇÃO

Na subrotina TST_MENOR_3, testamos se REG2B é menor do que 3,


efetuando a operação 2 – REG2B. Se ele for menor do que 3, o resultado será positivo. Nesse caso, o
número de incrementos é menor do que 1.000, e, então, iremos aguardar a 101ª captura e, por isso,
saímos da interrupção:

183
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

TST_MENOR_3
MOVF REG2B,W ;W = REG2B
SUBLW .2 ;W = 2 – REG2B
BTFSC STATUS,C ;REG2B MENOR DO QUE 3?
GOTO SAI_INT ;SIM

Se o resultado da operação anterior foi negativo, significa que REG2B é igual


3. Nesse caso, testamos o valor do REG1B para ver se é maior do que 232:

MOVF REG1B,W ;NÃO, W = REG1B


SUBLW .232 ;W = 232 – REG1B
BTFSC STATUS,C ;REG1B MAIOR DO QUE 232?

Caso REG1B seja igual ou menor do que 232, o resultado será positivo, o que
indica que o número de incrementos é menor do que 1.000 e, por isso, saímos da interrupção:

GOTO SAI_INT ;NAO

Se REG1B for maior do que 232, o resultado será negativo, o que significa
que o número de incrementos é maior do que 1.000, então, zeramos a variável CAPTURA, e testamos
se o flag CALCULAR_FREQ está setado:

CLRF CAPTURA ;SIM, CAPTURA = 0


BTFSC CALCULAR_FREQ ;FREQUÊNCIA SENDO CALCULADA?

Se estiver, saímos da interrupção, caso contrário, desviamos para a subrotina


COPIA_NR_INC:
GOTO SAI_INT ;SIM, SAI DA INTERRUPÇÃO
GOTO COPIA_NR_INC ;NAO, DESVIA

Quando o número de incrementos entre a 1ª e a 2ª captura é menor do que


1.000, temos de aguardar a 101ª captura. Por isso, havíamos saído da interrupção. Na próxima vez
que ocorrer uma interrupção do módulo CCP, após passar pelos testes com os valores 1 e 2, testamos
se o seu valor é igual a 101. Se não for, saímos da interrupção. Quando o valor da variável
CAPTURA for igual a 101, setamos o flag CEM_MILHOES e desviamos para a subrotina
CALC_INC:
MOVLW .101 ;NÃO, W = 101
XORWF CAPTURA,W ;W = CAPTURA XOR 101
BTFSS STATUS,Z ;CAPTURA = 101?
GOTO SAI_INT ;NAO, SAI DA INTERRUPÇÃO
BSF CEM_MILHOES ;SIM, SETA FLAG
GOTO CALC_INC ;DESVIA

Com isso, concluímos a rotina de interrupção do módulo CCP.

184
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Na subrotina SAI_INT, recuperamos os registradores W e STATUS:

SAI_INT
SWAPF STATUS_TEMP,W ;SWAP EM STATUS_TEMP
MOVWF STATUS ;RECUPERA STATUS
SWAPF W_TEMP,F ;SWAP EM W_TEMP
SWAPF W_TEMP,W ;RECUPERA W
RETFIE ;RETORNA DA INTERRUPÇÃO

Vamos agora para a rotina principal do programa, onde iremos efetuar o


cálculo da frequência.
Nos registradores REG3C, REG2C e REG1C, temos o número de
incrementos. Para calcular a frequência do sinal, devemos dividir 1.000.000 (decimal) ou
100.000.000 (decimal) por esse número, conforme ele seja maior ou menor do que 1.000.
No PIC16F628A, não existe uma instrução que efetue a divisão, porém, uma
divisão nada mais é que uma sequência de subtrações. Por exemplo, o resultado da divisão de 12 por
3 é igual a 4. Nessa operação, 12 é o dividendo, 3 o divisor e 4 o quociente.
Repare que 12 – 3 = 9; 9 – 3 = 6; 6 – 3 = 3; 3 – 3 = 0
Ou seja, partindo do número 12, foi possível subtrair 4 vezes o número 3.
Para efetuar uma divisão, contamos quantas vezes é possível subtrair o divisor
do dividendo. Após cada subtração, incrementamos uma variável. Quando não for mais possível
subtrair, a divisão terminou e teremos, na variável, o valor do resultado da divisão, ou seja, o
quociente.
No nosso programa, o resultado da divisão é o valor da frequência. Esse valor
deverá constar nas variáveis MILHAR, CENTENA, DEZENA e UNIDADE, para ser exibido no
display. Como o processo de divisão irá demorar certo tempo e para não atrapalhar a exibição dos
números no display, vamos utilizar as variáveis intermediárias MIL_TEMP, CEN_TEMP,
DEZ_TEMP e UNI_TEMP, que iremos incrementar a cada subtração.
A cada subtração iremos incrementar a variável UNI_TEMP, que quando
atingir o valor 10, será zerada e incrementaremos a variável DEZ_TEMP e assim por diante. Quando
a divisão terminar, copiaremos os valores para as variáveis MILHAR, CENTENA, DEZENA e
UNIDADE.
De quantas variáveis vamos precisar para formar o número 100.000.000?
Com 1 variável podemos ter 256 valores de 0 a 255. Com 2 variáveis
podemos ter 65.536 valores (256 x 256) de 0 até 65.535. Com 3 variáveis podemos ter 16.777.216
valores (256 x 256 x 256) de 0 até 16.777.215. Com 4 variáveis podemos ter 4.294.967.296 valores
(256 x 256 x 256 x 256) de 0 até 4.294.967.295.
Então, fica claro que precisamos de 4 variáveis para formar o número
100.000.000.
Essas variáveis serão: MILHAO_1, MILHAO_2 E MILHAO_3 e
MILHAO_4, sendo essa última a mais significativa.
Para escrevermos nessas variáveis o número 100.000.000, fazemos
MILHAO_4 = 5, MILHAO_3 = 245, MILHAO_2 = 255 e MILHAO_1 = 0.
O processo para chegarmos a esses valores é o seguinte:
Cada unidade da variável MILHAO_1 vale 1 no valor formado pelas quatro.
Cada unidade de MILHAO_2 vale 256 (1 x 256). Cada unidade de MILHAO_3 vale 65.536. Cada
unidade de MILHAO_4 vale 16.777.216.

185
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Começamos dividindo 100.000.000 por 16.777.216, obtendo


5,9604644775390625, cuja parte inteira (5) é o valor da variável mais significativa (MILHAO_4).
Em seguida, multiplicamos a parte decimal (0,9604644775390625) por 16.777.216, obtendo
16.113.920. A seguir, dividimos esse valor por 65.536, obtendo 245,87890625, cuja parte inteira (245)
é o valor de MILHAO_3. Em seguida multiplicamos a parte decimal (0,87890625) por 65.536,
obtendo 57.600. Depois, dividimos esse valor por 256, obtendo 225, que é o valor de MILHAO_2. Se
houvesse uma parte decimal, multiplicá-la-íamos por 256 para obtermos o valor de MILHAO_1.
O processo inverso, para achar o valor formado pelas quatro variáveis é mais
fácil: multiplicamos o valor de MILHAO_4_e por 16.777.216, multiplicamos o valor de MILHAO_3
por 65.536, multiplicamos o valor de MILHAO_2 por 256 e somamos os resultados ao valor de
MILHAO_1 (5 x 16.777.216 + 245 x 65.536 + 225 x 256 + 0 = 100.000.000).
O processo para escrevermos o número 1.000.000 é o mesmo, onde
encontraremos os seguintes valores: MILHAO_4 = 0, MILHAO_3 = 15, MILHAO_2 = 66 e
MILHAO_1 = 64.
Primeiramente, limpamos o WDT com a instrução CLRWDT. A Seguir,
testamos o flag CALCULAR_FREQ para verificar se devemos iniciar o cálculo. Se ele estiver zerado,
voltamos para o início da rotina PRINCIPAL, ficando em loop:
;***********************************************************************************************
PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


BTFSS CALCULAR_FREQ ;CALCULAR A FREQUÊNCIA?
GOTO PRINCIPAL ;NÃO

Se ele estiver setado, testamos o flag CEM_MILHOES, para verificar se


iremos dividir 1.000.000 ou 100.000.000 pelo número de incrementos, e, conforme o caso, iniciamos
os registradores com os valores devidos. No caso de 100.000.000, apagamos o flag CEM_MILHOES:

BTFSC CEM_MILHOES ;SIM, DIVIDIR 1.000.000 OU 100.000.000?


GOTO DIV_CEM_MILHOES ;100.000.000
MOVLW .64 ;1.000.000. W = 64
MOVWF MILHAO_1 ;MILHAO_1 = 64
MOVLW .66 ;W = 66
MOVWF MILHAO_2 ;MILHAO_2 = 66
MOVLW .15 ;W = 15
MOVWF MILHAO_3 ;MILHAO_3 = 15
CLRF MILHAO_4 ;MILHAO_4 = 0
GOTO INICIA_DIVISAO
DIV_CEM_MILHOES
BCF CEM_MILHOES ;APAGA FLAG
CLRF MILHAO_1 ;MILHAO_1 = 0
MOVLW .225 ;W = 225
MOVWF MILHAO_2 ;MILHAO_2 = 225
MOVLW .245 ;W = 245
MOVWF MILHAO_3 ;MILHAO_3 = 245
MOVLW .5 ;W = 5
MOVWF MILHAO_4 ;MILHAO_3 = 4

Vamos começar a divisão, lembrando que o número de incrementos é o valor


formado pelos registradores REG3C, REG2C e REG1C.
Primeiramente zeramos as variáveis temporárias:

186
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

INICIA_DIVISAO
CLRF UNI_TEMP ;UNI_TEMP = 0
CLRF DEZ_TEMP ;DEZ_TEMP = 0
CLRF CEN_TEMP ;CEN_TEMP = 0
CLRF MIL_TEMP ;MIL_TEMP = 0

A seguir, escrevemos a label CICLO_DE_SUBTRACAO.


No começo dessa subrotina será executada a instrução CLRWDT, pois, o
processo de cálculo da frequência demorará mais do que o tempo que o WDT leva para estourar.
CICLO_DE_SUBTRACAO
CLRWDT ;LIMPA O WDT

Em seguida, subtraímos os registradores menos significativos, ou seja,


fazemos MILHAO_1 – REG1C:

MOVF REG1C,W ;W = REG1C


SUBWF MILHAO_1,F ;MILHAO_1 = MILHAO_1 - REG1C

A seguir verificamos qual foi o resultado. Se foi positivo, desviamos o


programa para onde está a label SEG_DIV_1, onde iremos subtrair os próximos registradores, ou seja,
MILHAO_2 – REG2C. Se o resultado foi negativo, decrementamos MILHAO_2, e, a seguir,
testamos se o seu valor é igual a 255:

BTFSC STATUS,C ;RESULTADO NEGATIVO?


GOTO SEG_DIV_1 ;NÃO
DECF MILHAO_2,F ;SIM, DECREMENTA MILHAO_2
MOVLW .255 ;W = 255
XORWF MILHAO_2,W ;W = MILHAO_2 XOR 255
BTFSS STATUS,Z ;MILHAO_2 = 255?

Se o valor de MILHAO_2 for diferente de 255, desviamos para SEG_DIV_1,


caso contrário, decrementamos MILHAO_3 e verificamos se o seu valor é igual a 255:

GOTO SEG_DIV_1 ;NAO


DECF MILHAO_3,F ;SIM, DECREMENTA MILHAO_3
MOVLW .255 ;W = 255
XORWF MILHAO_3,W ;W = MILHAO_3 XOR 255
BTFSS STATUS,Z ;MILHAO_3 = 255?

Se o valor de MILHAO_3 for diferente de 255, desviamos para SEG_DIV_1,


caso contrário, decrementamos MILHAO_4 e verificamos se o seu valor é igual a 255:

GOTO SEG_DIV_1 ;NAO


DECF MILHAO_4,F ;SIM, DECREMENTA MILHAO_4
MOVLW .255 ;W = 255
XORWF MILHAO_4,W ;W = MILHAO_4 XOR 255
BTFSC STATUS,Z ;MILHAO_4 = 255?

Se o valor de MILHAO_4 for igual a 255, a divisão terminou. Caso contrário,


entramos na subrotina SEG_DIV_1, onde fazemos MILHAO_2 – REG2C:

187
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

GOTO DIVISAO_TERMINOU ;SIM


SEG_DIV_1
MOVF REG2C,W ;W = REG2C
SUBWF MILHAO_2,F ;MILHAO_2 = MILHAO_2 - REG2C

A seguir, verificamos qual foi o resultado. Se foi positivo, desviamos para


SEG_DIV_2, onde iremos subtrair os próximos registradores, ou seja, faremos MILHAO_3 –
REG3C. Se o resultado foi negativo, decrementamos MILHAO_3 e, em seguida, verificamos se o seu
valor é igual a 255:

BTFSC STATUS,C ;RESULTADO NEGATIVO?


GOTO SEG_DIV_2 ;NÃO
DECF MILHAO_3,F ;SIM, DECREMENTA MILHAO_3
MOVLW .255 ;W = 255
XORWF MILHAO_3,W ;W = MILHAO_3 XOR 255
BTFSS STATUS,Z ;MILHAO_3 = 255?

Se o valor de MILHAO_3 for diferente de 255, desviamos para SEG_DIV_2,


caso contrário, decrementamos MILHAO_4 e verificamos se o seu valor é igual a 255:
GOTO SEG_DIV_2 ;NÃO
DECF MILHAO_4,F ;SIM, DECREMENTA MILHAO_4
MOVLW .255 ;W = 255
XORWF MILHAO_4,W ;W = MILHAO_4 XOR 255
BTFSC STATUS,Z ;MILHAO_4 = 255?

Se o valor de MILHAO_4 for igual a 255, a divisão terminou. Caso contrário,


entramos na subrotina SEG_DIV_2, onde fazemos MILHAO_3 – REG3C:
GOTO DIVISAO_TERMINOU ;SIM
SEG_DIV_2
MOVF REG3C,W ;NAO, W = REG3C
SUBWF MILHAO_3,F ;MILHAO_3 = MILHAO_3 - REG3C

A seguir, verificamos se o resultado foi negativo. Se foi positivo, a subtração


foi possível e, então, desviamos para INC_UNI_TEMP, onde iremos incrementar a variável
UNI_TEMP. Se o resultado foi negativo, decrementamos MILHAO_4 e verificamos se o seu valor é
igual a 255:

BTFSC STATUS,C ;RESULTADO NEGATIVO?


GOTO INC_UNI_TEMP ;NAO
DECF MILHAO_4,F ;SIM, DECREMENTA MILHAO_4
MOVLW .255 ;W = 255
XORWF MILHAO_4,W ;W = MILHAO_4 XOR 255
BTFSC STATUS,Z ;MILHAO_4 = 255?

Se o valor de MILHAO_4 for igual a 255, a divisão terminou, caso contrário, foi
possível efetuar a divisão e então entramos na subrotina INC_UNI_TEMP:

GOTO DIVISAO_TERMINOU ;SIM


INC_UNI_TEMP

188
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Nesta subrotina, incrementamos UNI_TEMP e verificamos se o seu valor é igual


a 10. Se for diferente de 10, desviamos para CICLO_DE_SUBTRACAO para efetuarmos outra
subtração:
INCF UNI_TEMP,F ;INCREMENTA UNI_TEMP
MOVLW .10 ;W = 10
XORWF UNI_TEMP,W ;W = UNI_TEMP XOR 10
BTFSS STATUS,Z ;UNI_TEMP = 10?
GOTO CICLO_DE_SUBTRACAO ;NÃO

Se o valor de UNI_TEMP for igual a 10, zeramos essa variável e


incrementamos a variável DEZ_TEMP. Após incrementá-la, testamos se o seu valor é igual a 10. Se
não for, desviamos para CICLO_DE_SUBTRACAO. Se for, zeramo-la e assim por diante até a
variável MIL_TEMP:

CLRF UNI_TEMP ;SIM, UNI_TEMP = 0


INCF DEZ_TEMP,F ;INCREMENTA DEZ_TEMP
MOVLW .10 ;W = 10
XORWF DEZ_TEMP,W ;W = DEZ_TEMP XOR 10
BTFSS STATUS,Z ;DEZ_TEMP = 10?
GOTO CICLO_DE_SUBTRACAO ;NÃO
CLRF DEZ_TEMP ;SIM, DEZ_TEMP = 0
INCF CEN_TEMP,F ;INCREMENTA CEN_TEMP
MOVLW .10 ;W = 10
XORWF CEN_TEMP,W ;W = CEN_TEMP XOR 10
BTFSS STATUS,Z ;CEN_TEMP = 10?
GOTO CICLO_DE_SUBTRACAO ;NÃO
CLRF CEN_TEMP ;SIM, CEN_TEMP = 0
INCF MIL_TEMP,F ;INCREMENTA MIL_TEMP

Depois de incrementar a variável MIL_TEMP, testamos se o seu valor é igual


a 10. Se o valor de MIL_TEMP for diferente de 10, desviamos para CICLO_DE_SUBTRACAO. Se
for igual a 10, zeramo-la, e, em seguida, desviamos para DIVISAO_TERMINOU:

MOVLW .10 ;W = 10
XORWF MIL_TEMP,W ;W = MIL_TEMP XOR 10
BTFSS STATUS,Z ;MIL_TEMP = 10?
GOTO CICLO_DE_SUBTRACAO ;NÃO
CLRF MIL_TEMP ;SIM, MIL_TEMP = 0
GOTO DIVISAO_TERMINOU

Na subrotina DIVISAO_TERMINOU, primeiramente zeramos o flag


CALCULAR_FREQ:
DIVISAO_TERMINOU
BCF CALCULAR_FREQ ;APAGA FLAG

A seguir, testamos o flag ATUALIZA_DISP. Se esse flag estiver apagado,


significa que não é hora de atualizar o display e, portanto, voltamos para o início da rotina
PRINCIPAL:

189
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

BTFSS ATUALIZA_DISP ;ATUALIZAR O DISPLAY?


GOTO PRINCIPAL ;NÃO

Se ele estiver setado, zeramo-lo, copiamos os valores das variáveis


intermediárias e voltamos para o início da rotina PRINCIPAL:
BCF ATUALIZA_DISP ;SIM, APAGA FLAG
MOVF UNI_TEMP,W ;W = UNI_TEMP
MOVWF UNIDADE ;UNIDADE = UNI_TEMP
MOVF DEZ_TEMP,W ;W = DEZ_TEMP
MOVWF DEZENA ;DEZENA = DEZ_TEMP
MOVF CEN_TEMP,W ;W = CEN_TEMP
MOVWF CENTENA ;CENTENA = CEN_TEMP
MOVF MIL_TEMP,W ;W = MIL_TEMP
MOVWF MILHAR ;MILHAR = MIL_TEMP
GOTO PRINCIPAL

Por fim, encerramos o programa:

;*************************************************************************************************
END ;FIM DO PROGRAMA

Na seção “CONFIGURAÇÃO DOS REGISTRADORES DE USO


ESPECÍFICO”, vamos configurar o registrador OPTION_REG para que o prescaler do Timer 0 seja
de 8:1:

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'10000010' ;W = B'10000010'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO, PRESCALER 1:8

Configuramos o registrador TRISA para que os pinos RA0, RA1, RA2 e RA3
sejam saídas e os demais entradas e o TRISB para que o pino RB3 seja entrada e os demais saídas:

MOVLW B'11110000' ;W = B'11110000'


MOVWF TRISA ;CONFIGURA RA0, RA1, RA2 E RA3 COMO SAÍDAS E DEMAIS COMO ENTRADAS
MOVLW B'00001000'
MOVWF TRISB ;CONFIGURA O PINO RB3 COMO ENTRADA E DEMAIS COMO SAIDAS

Configuramos o registrador PIE1 para habilitamos as interrupções do Timer 1


e do módulo CCP:

MOVLW B'00000101'
MOVWF PIE1 ;HABILITA AS INTERRUPÇÕES DO TIMER 1 E DO MÓDULO CCP

190
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Em seguida, liberamos os pinos RA0 a RA3 do módulo comparador,


configurando o registrador CMCON e habilitamos a interrupção do Timer 0, no registrador INTCON:

BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA


MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O
MOVLW B'11100000'
MOVWF INTCON ;HABILITA INTERRUPÇÃO DO TIMER 0

A seguir, ligamos o Timer 1 com prescaler de 1:1 configurando o registrador


T1CON:

MOVLW B'00000001'
MOVWF T1CON ;HABILITA TIMER 1 COM PRESCALER DE 1:1

Finalmente, configuramos o registrador CCP1CON para habilitar o modo


“capture” do módulo CCP para que efetue uma captura a cada mudança de nível baixo para alto no
pino RB3:

MOVLW B'00000101'
MOVWF CCP1CON ;HABILITA MODO CAPTURE A CADA MUDANÇA DE BAIXO PARA ALTO

Essas configurações constam no datasheet do PIC16F628A, nas seções que


tratam do Timer 0, Módulo Comparador, PORTA, PORTB, Timer 1 e Módulo CCP.
Na seção “INICIALIZAÇÃO DA VARIÁVEIS”, inicializamos as variáveis
FLAGS, UNIDADE, DEZENA, CENTENA e MILHAR, REG3B com o valor 0.

;***********************************************************************************************
; INICIALIZACAO DAS VARIAVEIS

CLRF FLAGS ;INICIALIZA FLAGS


CLRF UNIDADE ;INICIALIZA UNIDADE
CLRF DEZENA ;INICIALIZA DEZENA
CLRF CENTENA ;INICIALIZA CENTENA
CLRF MILHAR ;INICIALIZA MILHAR
CLRF REG3B ;INICIALIZA REG3B
CLRF CAPTURA ;INICIALIZA CAPTURA

Com isso, o programa está concluído, tendo ficado assim:

;***********************************************************************************************
; PROGRAMA: FREQUENCIMETRO
; VERSÃO 1.0
; DESENVOLVIDO POR: MULDER_FOX
; DATA DE CONCLUSÃO: / /
;***********************************************************************************************

#INCLUDE <P16F628A.INC> ;ARQUIVO PADRAO MICROCHIP PARA O PIC16F628A

;***********************************************************************************************
; BITS DE CONFIGURAÇÃO

__CONFIG _INTOSC_OSC_NOCLKOUT & _WDT_ON & _PWRTE_ON & _MCLRE_OFF & _BOREN_OFF &
_LVP_OFF & _CP_OFF & DATA_CP_OFF

191
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

;**********************************************************************************************
; PAGINACAO DE MEMORIA

#DEFINE BANCO_0 BCF STATUS,RP0 ;SETA BANCO 0 DE MEMORIA


#DEFINE BANCO_1 BSF STATUS,RP0 ;SETA BANCO 1 DE MEMORIA

;***********************************************************************************************
; VARIÁVEIS

CBLOCK 0X20 ;ENDERECO INICIAL DA MEMORIA DO USUARIO

W_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR W


STATUS_TEMP ;USADA PARA SALVAR O CONTEUDO DO REGISTRADOR STATUS
FLAGS ;REGISTRADOR DE FLAGS
UNIDADE ;UNIDADE DA FREQUÊNCIA
DEZENA ;DEZENA DA FREQUÊNCIA
CENTENA ;CENTENA DA FREQUÊNCIA
MILHAR ;MILHAR DA FREQUÊNCIA
REG1A ;REGISTRADOR MENOS SIGNIFICATIVO DA 1ª CAPTURA
REG2A ;REGISTRADOR MAIS SIGNIFICATIVO DA 1ª CAPTURA
REG1B ;REGISTRADOR MENOS SIGNIFICATIVO DA 2ª CAPTURA
REG2B ;REGISTRADOR MAIS SIGNIFICATIVO DA 2ª CAPTURA
REG3B ;REGISTRADOR QUE CONTA OS ESTOUROS DO TIMER 1
REG1C ;REGISTRADOR MENOS SIGNIFICATIVO DO Nº DE INCREMENTOS
REG2C ;REGISTRADOR INTERMEDIÁRIO DO Nº DE INCREMENTOS
REG3C ;REGISTRADOR MAIS SIGNIFICATIVO DO Nº DE INCREMENTOS
DISP_DELAY ;CONTA 250 MS ENTRE EXIBIÇÔES DA FREQUÊNCIA
CAPTURA ;CONTA AS CAPTURAS DO MÓDULO CCP
MILHAO_1 ;PRIMEIRO REGISTRADOR DO NÚMERO 1.000.000
MILHAO_2 ;SEGUNDO REGISTRADOR DO NÚMERO 1.000.000
MILHAO_3 ;TERCEIRO REGISTRADOR DO NÚMERO 1.000.000
MILHAO_4 ;QUARTO REGISTRADOR DO NÚMERO 100.000.000
UNI_TEMP ;REGISTRADOR TEMPORÁRIO DA UNIDADE
DEZ_TEMP ;REGISTRADOR TEMPORÁRIO DA DEZENA
CEN_TEMP ;REGISTRADOR TEMPORÁRIO DA CENTENA
MIL_TEMP ;REGISTRADOR TEMPORÁRIO DO MLHAR

ENDC ;FIM DO BLOCO DE MEMORIA

;***********************************************************************************************
; SAÍDAS

#DEFINE DIG_UNI PORTA,0 ;SE = 1, DIGITO DA UNIDADE ATIVADO


#DEFINE DIG_DEZ PORTA,1 ;SE = 1, DIGITO DA DEZENA ATIVADO
#DEFINE DIG_CEN PORTA,2 ;SE = 1, DIGITO DA CENTENA ATIVADO
#DEFINE DIG_MIL PORTA,3 ;SE = 1, DIGITO DO MILHAR ATIVADO
#DEFINE SEG_A PORTB,0 ;SEGMENTO A DO DISPLAY
#DEFINE SEG_B PORTB,1 ;SEGMENTO B DO DISPLAY
#DEFINE SEG_C PORTB,2 ;SEGMENTO C DO DISPLAY
#DEFINE SEG_D PORTB,4 ;SEGMENTO D DO DISPLAY
#DEFINE SEG_E PORTB,5 ;SEGMENTO E DO DISPLAY
#DEFINE SEG_F PORTB,6 ;SEGMENTO F DO DISPLAY
#DEFINE SEG_G PORTB,7 ;SEGMENTO G DO DISPLAY

192
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

;***********************************************************************************************
; FLAGS

#DEFINE CALCULAR_FREQ FLAGS,0 ;SE = 1 A FREQUENCIA PODE SER CALCULADA


#DEFINE CEM_MILHOES FLAGS,1 ;SE = 100.000.000/N.º DE INCREMENTOS
#DEFINE ATUALIZA_DISP FLAGS,2 ;SE = 1, ATUALIZAR VALOR DO DISPLAY

;***********************************************************************************************
; VETOR DE RESET

ORG 0X00 ;ENDERECO INICIAL DE PROCESSAMENTO


GOTO INICIO ;DESVIA PARA INICIO

;*************************************************************************************************
; ROTINA DE INTERRUPÇÃO

ORG 0X04 ;VETOR DAS INTERRUPÇÕES

MOVWF W_TEMP ;SALVA W


SWAPF STATUS,W ;W = SWAP EM STATUS
BANCO_0 ;CHAVEIA PARA BANCO 0 DE MEMORIA
MOVWF STATUS_TEMP ;SALVA STATUS
BTFSC INTCON,T0IF ;INTERRUPÇÃO DO TIMER 0?
GOTO INT_TIMER_0 ;SIM
BTFSC PIR1,TMR1IF ;NÃO, INTERRUPÇÃO DO TIMER 1?
GOTO INT_TIMER_1 ;SIM
BTFSC PIR1,CCP1IF ;NÃO, INTERRUPÇÃO DO MÓDULO CCP?
GOTO INT_CCP ;SIM
GOTO SAI_INT

INT_TIMER_0
BCF INTCON,T0IF ;APAGA FLAG
DECFSZ DISP_DELAY,F ;DECREMENTA DISP_DELAY. DISP_DELAY = 0?
GOTO TESTA_DIG_ATIVO ;NÃO
BSF ATUALIZA_DISP ;SIM, SETA FLAG
TESTA_DIG_ATIVO
BTFSC DIG_MIL ;DÍGITO DO MILHAR ESTÁ ATIVADO?
GOTO CONT_DIG_CEN ;SIM
BTFSC DIG_CEN ;NÃO, DÍGITO DA CENTENA ESTÁ ATIVADO?
GOTO CONT_DIG_DEZ ;SIM
BTFSC DIG_DEZ ;NÃO, DÍGITO DA DEZENA ESTÁ ATIVADO?
GOTO CONT_DIG_UNI ;SIM
GOTO CONT_DIG_MIL ;NAO

CONT_DIG_CEN
BCF DIG_MIL ;DESATIVA DÍGITO DO MILHAR
BCF SEG_A ;APAGA SEGMENTO
BCF SEG_B ;APAGA SEGMENTO
BCF SEG_C ;APAGA SEGMENTO
BCF SEG_D ;APAGA SEGMENTO
BCF SEG_E ;APAGA SEGMENTO
BCF SEG_F ;APAGA SEGMENTO
BCF SEG_G ;APAGA SEGMENTO
BSF DIG_CEN ;ATIVA DÍGITO DA CENTENA
MOVF CENTENA,W ;COPIA CENTENA PARA O W
GOTO CONV_BIN_7_SEG ;CHAMA SUBROTINA

193
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

CONT_DIG_DEZ
BCF DIG_CEN ;DESATIVA DÍGITO DA CENTENA
BCF SEG_A ;APAGA SEGMENTO
BCF SEG_B ;APAGA SEGMENTO
BCF SEG_C ;APAGA SEGMENTO
BCF SEG_D ;APAGA SEGMENTO
BCF SEG_E ;APAGA SEGMENTO
BCF SEG_F ;APAGA SEGMENTO
BCF SEG_G ;APAGA SEGMENTO
BSF DIG_DEZ ;ATIVA DÍGITO DA DEZENA
MOVF DEZENA,W ;COPIA DEZENA PARA O W
GOTO CONV_BIN_7_SEG ;CHAMA SUBROTINA
CONT_DIG_UNI
BCF DIG_DEZ ;DESATIVA DÍGITO DA DEZENA
BCF SEG_A ;APAGA SEGMENTO
BCF SEG_B ;APAGA SEGMENTO
BCF SEG_C ;APAGA SEGMENTO
BCF SEG_D ;APAGA SEGMENTO
BCF SEG_E ;APAGA SEGMENTO
BCF SEG_F ;APAGA SEGMENTO
BCF SEG_G ;APAGA SEGMENTO
BSF DIG_UNI ;ATIVA DÍGITO DA UNIDADE
MOVF UNIDADE,W ;COPIA UNIDADE PARA O W
GOTO CONV_BIN_7_SEG ;CHAMA SUBROTINA
CONT_DIG_MIL
BCF DIG_UNI ;DESATIVA DÍGITO DA UNIDADE
BCF SEG_A ;APAGA SEGMENTO
BCF SEG_B ;APAGA SEGMENTO
BCF SEG_C ;APAGA SEGMENTO
BCF SEG_D ;APAGA SEGMENTO
BCF SEG_E ;APAGA SEGMENTO
BCF SEG_F ;APAGA SEGMENTO
BCF SEG_G ;APAGA SEGMENTO
BSF DIG_MIL ;ATIVA DÍGITO DA MILHAR
MOVF MILHAR,W ;COPIA MILHAR PARA O W
GOTO CONV_BIN_7_SEG ;CHAMA SUBROTINA

CONV_BIN_7_SEG
ADDWF PCL,F ;PCL = PCL + W
GOTO NUM_ZERO ;W = 0
GOTO NUM_UM ;W = 1
GOTO NUM_DOIS ;W = 2
GOTO NUM_TRES ;W = 3
GOTO NUM_QUATRO ;W = 4
GOTO NUM_CINCO ;W = 5
GOTO NUM_SEIS ;W = 6
GOTO NUM_SETE ;W = 7
GOTO NUM_OITO ;W = 8
GOTO NUM_NOVE ;W = 9

NUM_ZERO
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_E ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
GOTO SAI_INT

194
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

NUM_UM
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
GOTO SAI_INT

NUM_DOIS
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_E ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT

NUM_TRES
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT

NUM_QUATRO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT

NUM_CINCO
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT

NUM_SEIS
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_E ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT

NUM_SETE
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
GOTO SAI_INT

NUM_OITO
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_E ;ACENDE SEGMENTO

195
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

BSF SEG_F ;ACENDE SEGMENTO


BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT

NUM_NOVE
BSF SEG_A ;ACENDE SEGMENTO
BSF SEG_B ;ACENDE SEGMENTO
BSF SEG_C ;ACENDE SEGMENTO
BSF SEG_D ;ACENDE SEGMENTO
BSF SEG_F ;ACENDE SEGMENTO
BSF SEG_G ;ACENDE SEGMENTO
GOTO SAI_INT

INT_TIMER_1

BCF PIR1,TMR1IF ;APAGA FLAG


INCF REG3B ;INCREMENTA REG3B
GOTO SAI_INT ;SAI DA INTERRUPÇÃO

INT_CCP

CLRF PIR1,CCP1IF ;APAGA FLAG


INCF CAPTURA,F ;INCREMENTA CAPTURA
MOVLW .1 ;W = 1
XORWF CAPTURA,W ;W = CAPTURA XOR 1
BTFSC STATUS,Z ;CAPTURA = 1?
GOTO PRIMEIRA_CAPTURA ;SIM
MOVLW .2 ;NÃO, W = 2
XORWF CAPTURA,W ;W = CAPTURA XOR 2
BTFSC STATUS,Z ;CAPTURA = 2?
GOTO CALC_INC ;SIM
MOVLW .101 ;NÃO, W = 101
XORWF CAPTURA,W ;W = CAPTURA XOR 101
BTFSS STATUS,Z ;CAPTURA = 101?
GOTO SAI_INT ;NAO, SAI DA INTERRUPÇÃO
BSF CEM_MILHOES ;SIM, SETA FLAG
GOTO CALC_INC

PRIMEIRA_CAPTURA
MOVF CCPR1L,W ;W = CCPR1L
MOVWF REG1A ;REG1A = CCPR1L
MOVF CCPR1H,W ;W = CCPR1H
MOVWF REG2A ;REG2A = CCPR1H
CLRF REG3B ;REG3B = 0
GOTO SAI_INT ;SAI DA INTERRUPÇÃO

CALC_INC
MOVF CCPR1L,W ;W = CCPR1L
MOVWF REG1B ;REG1B = CCPR1L
MOVF CCPR1H,W ;W = CCPR1H
MOVWF REG2B ;REG2B = CCPR1H
MOVF REG1A,W ;W = REG1A
SUBWF REG1B,F ;REG1B = REG1B - REG1A
BTFSC STATUS,C ;RESULTADO NEGATIVO?
GOTO SEG_SUB ;NÃO
DECF REG2B,F ;SIM, DECREMENTA REG2B
MOVLW .255 ;W = 255
XORWF REG2B,W ;W = REG2B XOR 255

196
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

BTFSS STATUS,Z ;REG2B = 255?


GOTO SEG_SUB ;NÃO
DECF REG3B,F ;SIM, DECREMENTA REG3B
SEG_SUB
MOVF REG2A,W ;W = REG2A
SUBWF REG2B,F ;REG2B = REG2B - REG2A
BTFSC STATUS,C ;RESULTADO NEGATIVO?
GOTO TST_NR_INC ;NÃO
DECF REG3B,F ;SIM, DECREMENTA REG3B

TST_NR_INC
MOVF REG2B,W ;W = REG2B
SUBLW .3 ;W = 3 – REG2B
BTFSC STATUS,C ;REG2B MAIOR DO QUE 3?
GOTO TST_MENOR_3 ;NAO
CLRF CAPTURA ;SIM, CAPTURA = 0
BTFSC CALCULAR_FREQ ;FREQUÊNCIA SENDO CALCULADA?
GOTO SAI_INT ;SIM, SAI DA INTERRUPÇÃO
GOTO COPIA_NR_INC ;NAO, DESVIA
TST_MENOR_3
MOVF REG2B,W ;W = REG2B
SUBLW .2 ;W = 2 – REG2B
BTFSC STATUS,C ;REG2B MENOR DO QUE 3?
GOTO SAI_INT ;SIM, SAI DA INTERRUPÇÃO
MOVF REG1B,W ;NÃO, W = REG1B
SUBLW .232 ;W = 232 – REG1B
BTFSC STATUS,C ;REG1B MAIOR DO QUE 232?
GOTO SAI_INT ;NÃO, SAI DA INTERRUPÇÃO
CLRF CAPTURA ;SIM, CAPTURA = 0
BTFSC CALCULAR_FREQ ;FREQUÊNCIA SENDO CALCULADA?
GOTO SAI_INT ;SIM, SAI DA INTERRUPÇÃO
GOTO COPIA_NR_INC ;NAO, DESVIA

SAI_INT
SWAPF STATUS_TEMP,W ;SWAP EM STATUS_TEMP
MOVWF STATUS ;RECUPERA STATUS
SWAPF W_TEMP,F ;SWAP EM W_TEMP
SWAPF W_TEMP,W ;RECUPERA W
RETFIE ;RETORNA DA INTERRUPÇÃO

;***********************************************************************************************
; CONFIGURACAO DOS REGISTRADORES DE USO ESPECÍFICO

INICIO

BANCO_1 ;SELECIONA BANCO 1 DE MEMORIA


MOVLW B'10000010' ;W = B'10000010'
MOVWF OPTION_REG ;TIMER 0 INCREMENTADO PELO CICLO DE INSTRUCAO, PRESCALER 1:8
MOVLW B'11110000' ;W = B'11110000'
MOVWF TRISA ;CONFIGURA RA0, RA1, RA2 E RA3 COMO SAÍDAS E DEMAIS COMO ENTRADAS
MOVLW B'00001000'
MOVWF TRISB ;CONFIGURA O PINO RB3 COMO ENTRADA E DEMAIS COMO SAIDAS
MOVLW B'00000101'
MOVWF PIE1 ;HABILITA AS INTERRUPÇÕES DO TIMER 1 E DO MÓDULO CCP
BANCO_0 ;SELECIONA BANCO 0 DE MEMORIA
MOVLW B'00000111'
MOVWF CMCON ;CONFIGURA RA3, RA2, RA1 E RA0 COMO I/O
MOVLW B'11100000'

197
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

MOVWF INTCON ;HABILITA INTERRUPÇÃO DO TIMER 0


MOVLW B'00000001'
MOVWF T1CON ;HABILITA TIMER 1 COM PRESCALER DE 1:1
MOVLW B'00000101'
MOVWF CCP1CON ;HABILITA MODO CAPTURE A CADA MUDANÇA DE BAIXO PARA ALTO

;***********************************************************************************************
; INICIALIZACAO DAS VARIAVEIS

CLRF FLAGS ;INICIALIZA FLAGS


CLRF UNIDADE ;INICIALIZA UNIDADE
CLRF DEZENA ;INICIALIZA DEZENA
CLRF CENTENA ;INICIALIZA CENTENA
CLRF MILHAR ;INICIALIZA MILHAR
CLRF REG3B ;INICIALIZA REG3B
CLRF CAPTURA ;INICIALIZA CAPTURA

;***********************************************************************************************
PRINCIPAL ;ROTINA PRINCIPAL DO PROGRAMA

CLRWDT ;LIMPA O WDT


BTFSS CALCULAR_FREQ ;CALCULAR A FREQUÊNCIA?
GOTO PRINCIPAL ;NÃO
BTFSC CEM_MILHOES ;SIM, DIVIDIR 1.000.000 OU 100.000.000?
GOTO DIV_CEM_MILHOES ;100.000.000
MOVLW .64 ;1.000.000. W = 64
MOVWF MILHAO_1 ;MILHAO_1 = 64
MOVLW .66 ;W = 66
MOVWF MILHAO_2 ;MILHAO_2 = 66
MOVLW .15 ;W = 15
MOVWF MILHAO_3 ;MILHAO_3 = 15
CLRF MILHAO_4 ;MILHAO_4 = 0
GOTO INICIA_DIVISAO
DIV_CEM_MILHOES
BCF CEM_MILHOES ;APAGA FLAG
CLRF MILHAO_1 ;MILHAO_1 = 0
MOVLW .225 ;W = 225
MOVWF MILHAO_2 ;MILHAO_2 = 225
MOVLW .245 ;W = 245
MOVWF MILHAO_3 ;MILHAO_3 = 245
MOVLW .5 ;W = 5
MOVWF MILHAO_4 ;MILHAO_3 = 4

INICIA_DIVISAO
CLRF UNI_TEMP ;UNI_TEMP = 0
CLRF DEZ_TEMP ;DEZ_TEMP = 0
CLRF CEN_TEMP ;CEN_TEMP = 0
CLRF MIL_TEMP ;MIL_TEMP = 0

CICLO_DE_SUBTRACAO
CLRWDT ;LIMPA O WDT
MOVF REG1C,W ;W = REG1C
SUBWF MILHAO_1,F ;MILHAO_1 = MILHAO_1 - REG1C
BTFSC STATUS,C ;RESULTADO NEGATIVO?
GOTO SEG_DIV_1 ;NÃO
DECF MILHAO_2,F ;SIM, DECREMENTA MILHAO_2
MOVLW .255 ;W = 255
XORWF MILHAO_2,W ;W = MILHAO_2 XOR 255

198
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

BTFSS STATUS,Z ;MILHAO_2 = 255?


GOTO SEG_DIV_1 ;NAO
DECF MILHAO_3,F ;SIM, DECREMENTA MILHAO_3
MOVLW .255 ;W = 255
XORWF MILHAO_3,W ;W = MILHAO_3 XOR 255
BTFSS STATUS,Z ;MILHAO_3 = 255?
GOTO SEG_DIV_1 ;NAO
DECF MILHAO_4,F ;SIM, DECREMENTA MILHAO_4
MOVLW .255 ;W = 255
XORWF MILHAO_4,W ;W = MILHAO_4 XOR 255
BTFSC STATUS,Z ;MILHAO_4 = 255?
GOTO DIVISAO_TERMINOU ;SIM
SEG_DIV_1
MOVF REG2C,W ;W = REG2C
SUBWF MILHAO_2,F ;MILHAO_2 = MILHAO_2 - REG2C
BTFSC STATUS,C ;RESULTADO NEGATIVO?
GOTO SEG_DIV_2 ;NÃO
DECF MILHAO_3,F ;SIM, DECREMENTA MILHAO_3
MOVLW .255 ;W = 255
XORWF MILHAO_3,W ;W = MILHAO_3 XOR 255
BTFSS STATUS,Z ;MILHAO_3 = 255?
GOTO SEG_DIV_2 ;NÃO
DECF MILHAO_4,F ;SIM, DECREMENTA MILHAO_4
MOVLW .255 ;W = 255
XORWF MILHAO_4,W ;W = MILHAO_4 XOR 255
BTFSC STATUS,Z ;MILHAO_4 = 255?
GOTO DIVISAO_TERMINOU ;SIM
SEG_DIV_2
MOVF REG3C,W ;NAO, W = REG3C
SUBWF MILHAO_3,F ;MILHAO_3 = MILHAO_3 - REG3C
BTFSC STATUS,C ;RESULTADO NEGATIVO?
GOTO INC_UNI_TEMP ;NAO
DECF MILHAO_4,F ;SIM, DECREMENTA MILHAO_4
MOVLW .255 ;W = 255
XORWF MILHAO_4,W ;W = MILHAO_4 XOR 255
BTFSC STATUS,Z ;MILHAO_4 = 255?
GOTO DIVISAO_TERMINOU ;SIM
INC_UNI_TEMP
INCF UNI_TEMP,F ;INCREMENTA UNI_TEMP
MOVLW .10 ;W = 10
XORWF UNI_TEMP,W ;W = UNI_TEMP XOR 10
BTFSS STATUS,Z ;UNI_TEMP = 10?
GOTO CICLO_DE_SUBTRACAO ;NÃO
CLRF UNI_TEMP ;SIM, UNI_TEMP = 0
INCF DEZ_TEMP,F ;INCREMENTA DEZ_TEMP
MOVLW .10 ;W = 10
XORWF DEZ_TEMP,W ;W = DEZ_TEMP XOR 10
BTFSS STATUS,Z ;DEZ_TEMP = 10?
GOTO CICLO_DE_SUBTRACAO ;NÃO
CLRF DEZ_TEMP ;SIM, DEZ_TEMP = 0
INCF CEN_TEMP,F ;INCREMENTA CEN_TEMP
MOVLW .10 ;W = 10
XORWF CEN_TEMP,W ;W = CEN_TEMP XOR 10
BTFSS STATUS,Z ;CEN_TEMP = 10?
GOTO CICLO_DE_SUBTRACAO ;NÃO
CLRF CEN_TEMP ;SIM, CEN_TEMP = 0
INCF MIL_TEMP,F ;INCREMENTA MIL_TEMP
MOVLW .10 ;W = 10

199
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

XORWF MIL_TEMP,W ;W = MIL_TEMP XOR 10


BTFSS STATUS,Z ;MIL_TEMP = 10?
GOTO CICLO_DE_SUBTRACAO ;NÃO
CLRF MIL_TEMP ;SIM, MIL_TEMP = 0
GOTO DIVISAO_TERMINOU

DIVISAO_TERMINOU
BCF CALCULAR_FREQ ;APAGA FLAG
BTFSS ATUALIZA_DISP ;ATUALIZAR O DISPLAY?
GOTO PRINCIPAL ;NÃO
BCF ATUALIZA_DISP ;SIM, APAGA FLAG
MOVF UNI_TEMP,W ;W = UNI_TEMP
MOVWF UNIDADE ;UNIDADE = UNI_TEMP
MOVF DEZ_TEMP,W ;W = DEZ_TEMP
MOVWF DEZENA ;DEZENA = DEZ_TEMP
MOVF CEN_TEMP,W ;W = CEN_TEMP
MOVWF CENTENA ;CENTENA = CEN_TEMP
MOVF MIL_TEMP,W ;W = MIL_TEMP
MOVWF MILHAR ;MILHAR = MIL_TEMP
GOTO PRINCIPAL

;*************************************************************************************************
END ;FIM DO PROGRAMA

Vamos simular a execução do programa.


Crie o projeto com o “Project Wizard” e monte-o.
O procedimento para a criação e montagem de um projeto foi descrito em
detalhes nas outras partes deste tutorial.
No menu “View”, clique em “Watch” e adicione os seguintes registradores de
uso específico (SFR): PORTA, PORTB, TMR0, TMR1, CCPR1L e CCPR1H e as seguintes variáveis
(SYMBOL): CAPTURA, FLAGS, REG1A, REG2A, REG1B, REG2B, REG3B, MILHAO_1,
MILHAO_2, MILHAO_3, MILHAO_4, UNI_TEMP, DEZ_TEMP, CEN_TEMP, MIL_TEMP,
UNIDADE, DEZENA, CENTENA, MILHAR.
No menu “Debugger” em “Select Tool”, selecione o “MPLAB SIM”.
No menu “Debugger” em “Settings”, na aba “Osc/Trace”, em “Processor
Frequency” digite 4 e selecione “MHz”. Na aba “Animation/Real Time Updates”, selecione “Enable
real time watch updates” e ajuste para “Fastest”.
Clique no botão “Reset” da barra de ferramentas do simulador.
Primeiramente vamos acompanhar a execução do programa até ele entrar na
rotina principal. Para isso, vá clicando no botão “Step Into”.
Quando o programa estiver no loop da rotina principal, vamos conferir a
rotina de interrupção. Adicione um breakpoint na primeira linha da rotina de interrupção.
Clique no botão “Run”.
Quando o programa parar no breakpoint, vá clicando no botão “Step Into”.
Repare que se trata da interrupção do Timer 0 e que a subrotina executada é a de controle do dígito do
milhar.
Depois que o programa sair da interrupção, clique novamente no botão “Run”
e observe que agora a subrotina que será executada é a de controle do dígito da centena.
Depois que o programa sair da interrupção, abra a janela do “Stop Watch” no
menu “Debugger”.
Clique novamente no botão “Run” e observe que o tempo entre as
interrupções do Timer 0 é de 2 milisegundos, como planejado.

200
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Desta vez a subrotina executada é a de controle do dígito da dezena, a


próxima a da unidade e depois novamente a do milhar.
Estando a rotina de interrupção do Timer 0 aparentemente funcionando bem,
vamos simular a aplicação de um sinal no pino RB3.
No menu”Debugger”, em “Stimulus”, clique em “New Workbook”.
Na aba “Clock Stimulus”, na coluna “Label”, dê um nome qualquer ao sinal,
por exemplo, “Teste”. Na coluna “Pin”, selecione o RB3. Na coluna “Initial”, selecione “Low”, Nas
colunas “Low Cyc”, “High Cyc” escreva 833. Configure a coluna “Begin”, para “At Start” e a coluna
“End” para “Never. Clique em “Save” e dê um nome qualquer, por exemplo “Frequencímetro”. Em
seguida, clique em “Apply”:

Figura 3

Com essa configuração, o pino RB3 ficará alternando de nível a cada 833
ciclos de instrução, ou seja, a cada 833 microssegundos, o que corresponde a um sinal de frequência
de 600 Hz. Ou seja, estamos simulando que um sinal de 600 Hz está presente no pino RB3.
Remova o breakpoint do início da rotina de interrupção e insira um na
primeira instrução da rotina de interrupção do módulo CCP (BCF PIR1,CCP1IF).
Clique no botão “Run”.
Quando a simulação parar no breakpoint, vá clicando no botão “Step Into”.
Repare que é executada a subrotina da primeira captura.
Clique no botão “Run” novamente.
Quando a simulação parar no breakpoint, repare que, por ser a 2ª captura, é
executada a subrotina “CALC_INC”. Depois, em TST_NR_INC, é verificado que o número de
incrementos é maior do que 1.000, e, então, o flag para calcular a frequência é setado.

201
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Ao retornar para a rotina PRINCIPAL, como o flag CALCULAR_FREQ está


setado, o cálculo da frequência começa.
Insira um breakpoint na linha da primeira instrução da subrotina
DIVISAO_TERMINOU.
Clique no botão “Run”.
Quando a simulação parar no breakpoint, confira, no “Watch”, que
CEN_TEMP = 6, DEZ_TEMP = 0 e UNI_TEMP = 0, confirmando que a frequência foi calculada
corretamente:

Figura 4

Para a frequência de 600 Hz, o cálculo foi feito com base no número de
incrementos entre a 1ª e a 2ª capturas.
Vamos testar com um sinal de 3.012 Hz, cujo período é de 332
microssegundos. Sendo maior do que 1.000 Hz, o cálculo será feito com base no número de
incrementos entre a 1ª e a 101ª capturas.
No “Stimulus”, na aba “Clock Stimulus”, mude o valor das colunas “Low
Cyc” e “High Cyc” para 166, valor, em microssegundos, de meio ciclo da frequência de 3.012 Hz e
clique em “Save” e em “Apply”.
Clique no botão “Reset” da barra de ferramentas do simulador.
Insira um breakpoint na linha da primeira istrução da rotina de interrupção do
módulo CCP.
Clique no botão “Run”.

202
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Quando a simulação parar no breakpoint, vá clicando em “Step Into” e repare


que é executada a subrotina da 1ª captura.
Clique novamente em “Run”.
Repare que, por ser a 2ª captura é executada a subrotina CALC_INC. A
seguir, na subrotina TST_NR_INC, é verificado que o número de incrementos é menor do que 1.000.
A partir de agora o programa irá esperar até que o valor da variável
CAPTURA seja igual a 101.
Para agilizar, remova o breakpoint do começo da rotina de interrupção do
módulo CCP e insira um na primeira linha da subrotina CALC_INC.
Clique no botão “Run”.
Quando a simulação parar no breakpoint, já será a 101ª captura.
Vá clicando em “Step Into”. Quando a simulação voltar para a rotina
principal, o cálculo da frequência irá começar. Repare que desta vez, será usado o número
100.000.000.
Verifique se o breakpoint da primeira linha da subrotina
DIVISAO_TERMINOU ainda está lá. Se não, insira-o.
Clique no botão “Run”.
Quando a simulação parar no breakpoint, confira, no “Watch”, que o valor da
frequência é de 3012 Hz, conforme esperado:

Figura 5

203
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 8 – Frequencímetro

Se quiser, faça testes com outros valores de frequência, lembrando de clicar


no botão “Reset” do simulador depois de trocar o valor no “Clock Stimulus”.
Agora é só gravar o programa no microcontrolador e testar numa protoboard.
A figura abaixo mostra o circuito indicando uma frequência de 136 Hz,
enquanto o multímetro indica 138 Hz:

Figura 6

Aqui termina a parte 8 do tutorial. Espero que ela tenha sido útil para você.
Na 9ª parte, vamos usar o PIC16F628A para controlar um LED RGB, onde
iremos gerar 3 sinais PWM por software. Até lá.

204

Você também pode gostar