Upload
william-entler
View
152
Download
12
Embed Size (px)
Citation preview
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
1
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
2
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
3
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
Indica o início
Indica uma subrotina
Inicialização das
variáveis
Passou 0,5
segundo? não
sim
Acende LED LED está aceso? Apaga LED
sim não
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
4
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
5
;***********************************************************************************************
; 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
;**********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
6
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.
DATA_CP é um recurso para proteger a memória de dados contra cópia.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
7
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
8
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
variáveis. Por esse motivo, minha forma preferida para defini-las é com o uso da diretiva CBLOCK:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
9
;**********************************************************************************************
; 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 ;***********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
10
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
11
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 ;***********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
12
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).
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
13
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' MOVWF CMCON
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
14
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.
Vamos lá!
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
15
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á-la 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 isto, na próxima linha escrevemos: GOTO PRINCIPAL.
Isto 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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
16
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
17
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?
Faremos assim:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
18
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 ;**********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
19
; 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
;***********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
20
;**********************************************************************************************
; 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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
21
Figura 5
Na janela seguinte (“Step One”), selecione o PIC16F628A e clique em
“Avançar”:
Figura 6
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
22
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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
23
Figura 9
Em seguida clique em “Avançar”
Figura 10
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
24
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
25
No menu “Project”, clique em “Build All”. Na janela que se abre, clique em
“Absolute”:
Figura 13
A seguir, expanda a janela chamada “Output”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
26
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”.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
27
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
28
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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
29
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
30
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
31
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
32
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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
33
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”):
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
34
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
35
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
36
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
37
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).
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
38
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
39
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
40
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”
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
41
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”.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
42
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 1 – Pisca LED
43
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II
44
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II
45
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II
46
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II
47
;***********************************************************************************************
; 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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II
48
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II
49
;***********************************************************************************************
; 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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II
50
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II
51
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 2 – Pisca LED II
52
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
53
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
54
;***********************************************************************************************
; 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
;**********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
55
; 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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
56
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
57
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
58
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
59
;***********************************************************************************************
; 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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
60
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
61
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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
62
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 3 – Pisca LED III
63
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
64
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
65
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
66
;********************************************************************************************
; 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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
67
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
68
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
69
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
70
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
71
#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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
72
;***********************************************************************************************
; 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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
73
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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
74
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
75
Figura 2
Na caixa de seleção do campo “Action” ao lado, selecione “Set High”:
Figura 3
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
76
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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
77
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” .
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
78
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
79
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
80
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
81
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 4 – Pisca LED IV
82
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á.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
83
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
84
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
85
Figura 2
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
86
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
87
;***********************************************************************************************
; 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
;***********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
88
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”.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
89
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 0110111 1
Tabela 2
As figuras a seguir ilustram os valores da tabela anterior.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
90
01001111
00000110 00111111
01011011
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
91
00000111
01101101 01100110
01111101
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
92
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
01101111 01111111
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
93
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
94
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).
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
95
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
96
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
97
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 ;***********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
98
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?
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
99
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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
100
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
101
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
102
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?
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
103
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
104
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
105
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
106
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
107
;********************************************************************************************
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”.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
108
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
109
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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
110
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
111
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
112
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
113
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
114
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 5 – Contador Crescente e Decrescente
115
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
116
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
117
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”.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
118
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
119
#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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
120
;**********************************************************************************************
; 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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
121
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
122
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
;***********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
123
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
124
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 ;**********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
125
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
126
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?
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
127
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
128
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
129
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
130
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”.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
131
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).
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
132
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
133
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
;***********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
134
; 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
;***********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
135
; 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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
136
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
137
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
138
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
139
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
140
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
141
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
142
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
143
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
144
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
145
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
146
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
147
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 6 – Contador II
148
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á!
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
149
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
150
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
151
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
152
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
153
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
CCPR1L CCP1CON
Bit 5
CCP1CON
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
154
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
CCPR1L CCP1CON
Bit 5
CCP1CON
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
155
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.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
156
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
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
157
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):
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
158
;***********************************************************************************************
; 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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
159
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
;***********************************************************************************************
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
160
;***********************************************************************************************
; 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'
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
161
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”.
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
162
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
163
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”:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
164
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
165
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:
Tutorial de Programação Assembly para Microcontroladores PIC - Parte 7 – Dimmer para LED
166
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á!