Você está na página 1de 111

Linguagem C para

Microcontroladores PIC

Laurence Crestani Tasca


lctasca@bol.com.br

Caxias do Sul, 2010

Captulo 1 Introduo a Programao


Introduo
Digamos que voc deseja comprar um computador pessoal e vai at uma
loja e solicita ao vendedor os modelos disponveis. Este vai ate o estoque e lhe
apresenta dois modelos disponveis com as seguintes caractersticas bsicas:

Velocidade do Processador (1500 MHz ou 3 GHz)


Tamanho da Memria RAM (1 GB ou 2 GB)
Tamanho do Disco Rgido (160 GB ou 250 GB)

Baseado nessas caractersticas, no preo e na sua necessidade voc


optar por uma das opes. Dessas trs caractersticas o processador sem
dvida o mais importante, a unidade central responsvel pelas operaes
matemticas e o que determinar com maior significncia o bom rendimento da
mquina. A memria fornece ao processador os dados e os programas que devem
ser processados e o disco rgido alimenta a memria com os dados e os
programas no volteis guardados em seu interior. O diagrama abaixo ilustra os
trs principais componentes de um computador. Estes dispositivos esto
separados e distribudos dentro do gabinete:

Organizao Bsica de um Computador

2/111

Computador X Microcontrolador PIC


Fazendo uma analogia de um microcontrolador com um computador
podemos dizer, num primeiro momento, que ele possui os mesmos dispositivos
principais que um computador com a diferena de que todos estes dispositivos
esto integrados numa mesma pastilha (chip). Um esboo da organizao bsica
de um microcontrolador se encontra abaixo:

Organizao bsica de um Microcontrolador


Como podemos verificar ambas as arquiteturas tem processador, ambas
tem memria RAM, no entanto, os microcontroladores possuem uma memria
especial para programas. No caso dos microcontroladores PIC esta memria do
tipo Flash, uma memria no voltil muito usada em vrios dispositivos atualmente
(pen-drives, celulares etc).
Na maioria dos casos a memria de um computador que armazena os
dados e programas a mesma, sendo acessada por um nico barramento. Isto
caracteriza uma arquitetura do tipo Von Neumann. J no caso dos
microcontroladores PIC existe uma memria para dados e uma para programas
utilizando dois barramentos, caracterizando uma arquitetura do tipo Harvard. Os
barramentos separados podem ser visualizados nas figuras que mostram a
arquitetura bsica de um computador e de um microcontrolador.
Alm dos dispositivos principais os microcontroladores possuem outros
perifricos em seu interior como: portos de entrada/sada, portas seriais, timers
conversores analgico digitais etc. Cada um destes perifricos ser visto nos
captulos posteriores. O PIC que trataremos nessa apostila ser o 16F877A cujas
principais caractersticas so listadas abaixo:

8K words de ROM
358 bytes de RAM
256 bytes de E2PROM
3 Timers
1 Porta Serial Sncrona/Assncrona
8 Entradas Analgicas
2 Comparadores/ Sadas PWM
3/111

Famlias PIC
Na linha de produtos da Microchip encontramos diversas famlias de
microcontroladores de 8,16 e 32 bits, sendo as principais listadas abaixo:

PIC10

Famlia de microcontroladores de 6/8 pinos, mais indicado


em aplicaes de espao fsico limitado e programas de
pouca complexidade. Os encapsulamentos so todos
SMD (Surface Mounted Components Dispositivos de
Montagem Superficial) o que dificulta a utilizao por
Hobistas.

PIC12

Primeira famlia a apresentar microcontroladores de 8


pinos, a vantagem sobre a famlia 10F o fato de a famlia
PIC12 possuir encapsulamentos PTH (Pin Thought Hole)
indicado para aplicaes pequenas que requerem um
pequeno nmero de pinos e alguma inteligncia
programvel em comparao a integrados dedicados como
o 555, por exemplo.

PIC16

J foi a famlia com o melhor custo benefcio, mas hoje


est obsoleta e muitos itens no so mais fabricados.
Possui microcontroladores de 14 a 64 pinos. Mais indicado
em aplicaes de mdio porte, onde os requisitos de
programa no sejam to grandes. Para projetos novos
recomendado o uso de famlias mais modernas.

PIC18

Indicada para aplicao de mdio e grande porte.


Possui encapsulamentos de 18 at mais de 100 pinos,
instrues avanadas e so mais indicadas para a
programao em C, pois alguns modelos possuem
otimizao para a linguagem. Possuem os mais
avanados perifricos como USB e Ethernet.

4/111

PIC24/dsPIC

A famlia de microcontroladores de 16 bits da Microchip.


Os microcontroladores dsPIC so muito semelhantes aos
da famlia PIC18 com a diferena de contarem com
operaes que facilitam a implementao de filtros
digitais (verso digital microcontrolada de um filtro: passa
baixo, passa alto etc). J os microcontroladores PIC24
so a evoluo da famlia PIC18 com arquitetura 16 bits
reformulada.

PIC32

Lanados mais recentemente, a famlia PIC32 foi uma


grata surpresa para os usurios de microcontroladores
PIC, que agora tambm contam com a arquitetura de 32
bits.

Linguagens de Programao
Assembly
Uma linguagem de montagem ou assembly uma
notao legvel por humanos para o cdigo de mquina que
uma arquitetura especfica usa. A linguagem de mquina, que
um mero padro de bits, torna-se legvel pela substituio dos
valores em bruto por smbolos chamados mnemnicos.
Por exemplo, enquanto um computador sabe o que a
instruo-mquina IA-21 (10110000 01100001) faz, para os
programadores mais fcil recordar a representao
equivalente em intruses mnemnicas MOV AL, 61h. Tal
instruo ordena que o valor hexadecimal 61 (97, em decimal)
seja movido para um registrador chamado 'AL'.
As vantagens da programao de microcontroladores em assembly so a
velocidade e eficincia do cdigo gerado. Como desvantagens temos a difcil
manuteno devido a complexidade inerente a linguagem, alm da pouca
portabilidade devido ao grande comprometimento com um hardware especifico.

5/111

Linguagem C
C

uma
linguagem
de
programao estruturada e padronizada
criada na dcada de 1970 por Dennis
Ritchie e Ken Thompson para ser usada
no sistema operacional UNIX. Desde
ento se espalhou por muitos outros
sistemas operativos, e tornou-se uma
das linguagens de programao mais
usadas.
C tem como ponto-forte a sua
eficincia e a linguagem de
programao de preferncia para o
desenvolvimento
de
software
de
sistemas operacionais, apesar de tambm ser usada para desenvolver aplicaes.
Outra caracterstica importante de C sua proximidade a linguagem de mquina,
que permite que um projetista seja capaz de fazer algumas previses de como o
software ir se comportar ao executar.
A utilizao de C para a programao de microcontroladores PIC parece
uma escolha natural e realmente . Atualmente, a maioria dos microcontroladores
disponvel no mercado conta com compiladores de linguagem C para o
desenvolvimento de software. O uso de C permite a construo de programas e
aplicaes muito mais complexas do que seria vivel utilizando apenas assembly.
Lgica Booleana
Muitas operaes que o microcontrolador executa so lgicas. Para que
possamos entender e fazer uso dessas operaes, precisamos saber os princpios
de lgica booleana. O termo booleana vem do nome George Boole, matemtico
ingls, que foi o primeiro a defini-las como parte de um sistema de lgica em
meados do sculo XIX.
Lgica E
Suponhamos que voc seja uma pessoa que trabalha durante toda a
semana e no sbado e que portanto, seu nico dia de folga seja o domingo.
Suponhamos tambm que voc, como muitas outras pessoas, gosta de ir a praia
no domingo. Porm, para isso acontecer preciso haver sol correto? Sendo
assim, a situao descrita a seguinte:
Hoje Domingo?
no
no
sim
sim

Tem Sol?
no
sim
no
sim

Vamos a praia?
no
no
no
sim
6/111

Essa situao equivale a uma lgica E da proposio Hoje Domingo? e


Tem Sol? tendo como resultado a proposio Vamos a praia?. Podemos definir
ento a tabela verdade para a lgica E:
A
F
F
V
V

B
R
F
F
V
F
F
F
V
V
R=AeB

Lgica OU
Imaginemos um sistema de alarme de uma sala que monitore duas portas
atravs de dois sensores S1 e S2:

Para o alarme ser acionado basta que qualquer um dos sensores seja
acionado, isso se descreve na situao abaixo:
Acionou S1?
no
no
sim
sim

Acionou S2?
no
sim
no
sim

Aciona Alarme?
no
sim
sim
sim

Essa aplicao descreve uma lgica OU dos dois sensores do alarme a


tabela verdade da lgica OU pode ser encontrada abaixo:
A
B
R
F
F
F
F
V
V
V
F
V
V
V
V
R = A ou B

7/111

Lgica NO
No exemplo da lgica E usamos a expresso Hoje Domingo para indicar
o dia de folga da pessoa. Mas se quisssemos expressar de uma forma que
sempre fossemos obter a resposta se hoje o dia de folga da pessoa, poderamos
usar a expresso: No Trabalho Hoje?. Percebe-se que tratasse do contrario da
expresso Trabalho Hoje, portanto a tabela verdade da Lgica NO :
A
R
F
V
V
F
R = NO A
Lgica OU EXCLUSIVO
Imaginemos que ocorrera uma festa muito badalada na
cidade em que dois sujeitos foram convidados Joo e Jos.
Ambos so timas pessoas e ajudam a animar a festa. Se
nenhum dos dois comparecer, a festa ser muito chata, sem
animao. Porm se os dois se encontrarem brigaram a noite
inteira e estragam a festa dos outros. A situao acima pode
ser representada na tabela abaixo:
Joo foi festa?
no
no
sim
sim

Jos foi festa?


no
sim
no
sim

A festa boa?
no
sim
sim
no

O fato de a festa ser boa depender de apenas um dos dois (Joo ou Jos)
estar presente, o que caracteriza uma lgica OU EXCLUSIVO. O nome dado a
lgica em ingls XOR (Exclusive Or). A tabela verdade da lgica encontra-se
abaixo:
A
B
R
F
F
F
F
V
V
V
F
V
V
V
F
R = A xor B
Variveis
Todos os programas criados para o microcontrolador manipulam com
dados, mais comumente nos referimos aos dados como variveis. Variveis so
8/111

posies da memria que so utilizados para gravar resultados de clculos, contar


valores etc. As variveis podem ser classificadas nos seguintes tipos:

Lgicas
Numricas
o Inteiras
o Reais
Caractere
o Simples
o Texto

Lgicas
As variveis lgicas so usadas para armazenar valores lgicos, verdadeiro
ou falso, ocupam no mnimo 1 bit de memria (depende do compilador) e podem
armazenar o resultado de uma equao booleana por exemplo.
Numricas Inteiras
As variveis inteiras podem guardar valores inteiros. Podem ter
representaes com ou sem sinal. As variveis inteiras em geral podem ocupar 1,
2, 4 ou 8 bytes da memria do microcontrolador (dependendo tambm do
compilador).
Numricas Reais
Variveis Reais so utilizadas para armazenar nmeros reais (com vrgula)
sendo por definio com sinal. A representao utilizando ponto flutuante
geralmente ocupa 4 bytes.
Caractere Simples
Contm o valor de um caractere especfico. Ocupam 1 byte na memria, se
esse for representando como caractere da tabela ASCII (acrnimo para American
Standard Code for Information Interchange, que em portugus significa "Cdigo
Padro Americano para o Intercmbio de Informao") uma codificao de
caracteres de 7 bits baseada no alfabeto ingls. Surgiu posteriormente uma
verso com 8 bits do ASCII que previa o suporte para os caracteres especiais. O
ASCII se tornou o mais padro dos sistemas de codificao de caracteres e seu
uso quase unanimidade nos compiladores.
Caractere Texto
So usados para representar palavras ou textos, nada mais que uma
cadeia de caracteres simples. Utilizam uma poro contgua da memria

9/111

reservada para esse fim. Essa cadeia ocupa tantos bytes quantos for o tamanho
do texto em uma determinada codificao (geralmente ASCII).
Bases Numricas
A seguir descrevemos as trs bases que os programas de microcontrolador
usam: binria, decimal e hexadecimal.
Base Decimal
A base decimal, e a base que utilizamos no dia-a-dia tm base 10 pois
contm 10 algarismos (0 a 9). No so necessrias mais explicaes sobre essa
base, j que efetuamos todos os clculos que conhecemos nessa base.
Base Binria
A base binria utilizada quando queremos representar um nmero bit a
bit. Numa analogia, uma varivel de um byte (8 bits) representada em base
binria, se assemelha a oito lmpadas de diferentes potncias dispostas da
seguinte forma:

Com estas lmpadas podemos fazer uma iluminao de qualquer potencia


entre 0 e 255w. Na figura abaixo temos a representao para uma potncia 56w:

Na representao acima, cada lmpada faz o papel de um bit, e cada bit


tem um peso na representao do nmero. Logo o valor 56 decimal seria
representado como 0B00111000 em binrio.
A converso de um nmero em decimal para base binria e vista no
procedimento abaixo. Efetuamos sucessivas divises por 2 no nmero que
desejamos converter at que o ltimo quociente seja 0 ou 1. Feito isso, o sentido
10/111

da leitura do nmero convertido formado pelo ltimo quociente e os restos das


divises anteriores conforme o sentido da seta azul:

Na converso de um nmero de base binria para decimal nos utilizamos


do fato de que cada dgito binrio tem um peso no nmero proporcional a sua
posio, logo a transformao de 0B00111000 em decimal exemplificada
abaixo:
0 x 27 + 0 x 26 + 1 x 25 + 1 x 24 + 1 x 23 + 0 x 22 + 0 x 21 + 0 x 20
0 x 128 + 0 x 64 + 1 x 32 + 1 x 16 + 1 x 8 + 0 x 4 + 0 x 2 + 0 x 1
32 + 16 + 8 = 56
Base Hexadecimal
A base hexadecimal, tambm muito popular em programao de sistemas
microcontrolados. Possui base 16, logo contm 16 dgitos cujos valores possveis
so 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E e F. A base hexadecimal veio para
simplificar a representao de um byte que pode ser escrito com apenas dois
dgitos hexadecimais (diferentemente da base binria que necessita de 3 dgitos
para representar nmero acima de 99). Vale salientar que para cada byte so
usados dois dgitos, logo para uma varivel de um byte usam-se dois dgitos, para
uma de dois bytes usam-se quatro dgitos e assim por diante. Alguns exemplos de
representao de nmeros em base hexadecimal encontram-se abaixo:
Decimal
33
9830
1235
468146

Hexadecimal
0x21
0x2666
0x04D3
0x000724B2

A converso decimal-hexadecimal e feita por um processo semelhante a


base binria, com a diferena de a diviso ser pela base (16). A converso do
nmero 433 em hexadecimal dada abaixo, e o sentido de leitura indicado pela
seta vermelha:
11/111

importante observar que no caso do resto ser maior que 9 o dgito deve
ser convertido para hexadecimal. No caso do resto 11 acima, o seu
correspondente hexadecimal B. Para a converso de 0x01B1 hexadecimal para
decimal, a lgica semelhante a converso de binrio para decimal:
0 x 163 + 1 x 162 + B x 161 + 1 x 160
0 x 163 + 1 x 162 + 11 x 161 + 1 x 160
0 x 4096 + 1 x 256 + 11 x 16 + 1 x 1
256 + 176 + 1 = 433
Fluxogramas
Embora atualmente existam outras formas de representao de ciclo de
execuo, os fluxogramas so muito utilizados. Representam o ciclo de instrues
e tomada de deciso que devem ser tomados na soluo de um problema.
Constituem-se basicamente de diversos smbolos para cada operao, sendo os
mais comuns apresentados abaixo:

Terminal: indica o incio ou o fim de um programa.

Conector: serve para a unio dos fluxos de informaes.


Entrada: indica a entrada de informaes passadas pelo
usurio, seja por teclado, mouse, teclas ou qualquer
dispositivo de entrada.
Processamento: utilizado para fazer clculos e atribuio de
valores.
Sada: indica a sada de informaes processadas para o
usurio por qualquer dispositivo de sada como monitor,
impressora, display etc.

12/111

Condio: indica a diviso do fluxo por uma determinada


condio.

Seta de fluxo de informaes: indica o caminho que o


processamento pode tomar.

13/111

Etapas de um Projeto com Programao


Um projeto que envolva programao de microcontroladores geralmente
envolve um conjunto de cinco fases principais citadas a seguir:
1) Definio do Problema
Nessa fase o problema e apresentado ao programador, ora por um cliente
ora por uma necessidade do prprio programador. Nessa fase definido o que se
deseja fazer.
2) Definio da soluo
Tendo o problema definido podemos passar a fase de pensar numa soluo
que resolva o problema proposto. Nessa fase definido o como.
3) Fluxograma
Nessa fase definiremos o fluxograma da soluo proposta. Veremos
fluxogramas mais adiante, vale salientar que dependendo do tamanho da
aplicao, nem sempre vivel fazermos um fluxograma.
4) Codificao
Aqui transformaremos o fluxograma proposto na linguagem de
programao usada. No caso de nosso curso a linguagem C, mas poderia haver
outras formas de fazermos o programa do PIC, como Assembly, Basic, Pascal etc.
5) Depurao
Por mais experiente que seja um programador, nenhum programa est
imune a erros. Nessa fase o programa e testado e os eventuais erros so
corrigidos.

14/111

Aplicao Exemplo
Para exemplificar o ciclo de produo de um programa, vamos nos utilizar
de um exemplo de aplicao e mostrar cada etapa de sua implementao.
1) Definio do Problema
Um cliente lhe procura com um de seus produtos, solicitando que voc
desenvolva uma mquina que automatize o processo de fabricao. O produto
uma chapa de metal com um furo centralizado sem muita preciso:

2) Definio da Soluo
Aps o estudo para soluo voc chega ao esboo abaixo:

A chapa a ser furada ser colocada na esteira A. Ser aguardado o


pressionamento da tecla incio. A lmpada L ser ligada para demonstrar o
funcionamento da mquina. A esteira A ser acionada at que ambos os sensores
S1 e S2 detectem a presena da pea. A furadeira F ser avanada e recuada para
efetuar o furo na pea. A esteira A ser avanada novamente at que ambos os
sensores S1 e S2 no detectem mais a presena da pea na esteira B. Finalmente
a lmpada L ser desligada para informar o final da operao.

15/111

3) Fluxograma
Depois de definida a soluo convm fazermos o fluxograma que a expressa:

4) Codificao
5) Depurao
Tanto a codificao quanto a depurao dessa aplicao sero feitos nos
captulos posteriores quando tivermos os pr-requisitos necessrios.
16/111

Captulo 2 PIC 16F877A


Introduo
Nesse captulo estudaremos as bases numricas necessrias para
trabalharmos com microcontroladores assim como uma viso geral sobre a
arquitetura do PIC16F877A e seus principais perifricos.
Arquitetura PIC16F877A
Anteriormente foi descrita a arquitetura bsica dos microcontroladores PIC:
CPU, FLASH (memria de programas) e RAM (memria de dados). Agora
teremos uma viso mais detalhada dos componentes que compem o
microcontrolador. No diagrama logo abaixo encontramos os principais
componentes da arquitetura do PIC16F877A.

17/111

A FLASH onde fica armazenado o programa representada pela poro


em azul. A RAM onde so alocadas as variveis representada em vermelho. Em
roxo temos a decodificao de instrues e gerao de tempos do processador.
Em verde, o sistema responsvel pelo controle de reset do processador. E
finalmente, os perifricos internos do microcontrolador representados em amarelo.
Vale salientar que todos os perifricos internos do PIC16F877A operam com uma
freqncia equivalente a da freqncia real do processador, por exemplo, se a
fonte de clock do processador for um cristal de 20 MHz os perifricos funcionaro
a 5 MHz.
Circuito Mnimo
A fim de iniciarmos um projeto, precisamos saber o circuito mnimo que o
microcontrolador PIC16F877A requer para funcionar. O microcontroladores da
famlia PIC requerem um circuito mnimo composto basicamente de um cristal,
capacitores de filtro e alguns resistores dependendo da aplicao.

O principal componente do circuito o cristal, os PICS podem funcionar


com outros circuitos osciladores, mas o mais eficiente e preciso so os cristais. Os
cristais geram a base de tempo para o funcionamento do microcontrolador so
valores recomendados de cristal: 4, 8, 16 ou 20 MHz, pois esses valores facilitam
18/111

o clculo de funes de tempo dos perifricos que no caso dos microcontroladores


PIC rodam a da freqncia principal. Capacitores de no mnimo 27pf devem ser
colocados em paralelo com o cristal, pois esses so imprescindveis para a
oscilao do mesmo.
O pino MCLR (pino 1) quando conectado a zero (0V) coloca o
microcontrolador em reset. Logo, o resistor de 10k necessrio, para injetar 5V no
pino MCLR colocando o microcontrolador em modo normal de operao.
E finalmente, um capacitor de 100nF colocado prximo aos pinos de
alimentao garante um fornecimento constante de energia ao microcontrolador
no caso de surto de corrente por outra parte do circuito. Este capacitor atua como
uma pequena caixa dgua para o circuito, e sua utilizao muito importante.
Registradores PIC16F877A
No s de variveis formada a memria RAM do PIC. Alguns endereos
so reservados a configurao e interao com os perifricos do microcontrolador.
Logo abaixo temos o mapa de memria do PIC16F877A onde se pode notar que
alguns endereos so reservados. Apenas a rea de memria delimitada pelas
pores azuis est disponvel para o usurio.

19/111

Podemos observar tambm que a memria paginada em 4 partes,


embora isso s preocupe os programadores de assembly j que o acesso bem
como a alocao de memria feita automaticamente pelo compilador C ficando
transparente ao programador.
Portos
O meio de interao do microcontrolador com o mundo exterior se d muito
comumente atravs dos portos. O PIC16F877A possui cinco portos nomeados de
A a E sendo que todos possuem 8 pinos com exceo do A que possui 6 e do E
que possui apenas 3.
Cada porto possui registradores associados que controlam diversas
funes para cada pino alm da usual entrada ou sada. Um registrador que todos
os portos possuem o TRISx, onde x equivale a letra que descreve o nome do
porto. Esse registrador determina o sentido de cada pino do porto. Ao escrever 1,
configuramos o pino como entrada. Ao escrever 0, configuramos o pino como
sada.
Exemplificando: suponhamos que queiramos ligar trs botes e trs leds no
PORTA. Os leds nos pinos RA0, RA1 e RA2. E os botes nos pinos RA3, RA4,
RA5. Nesse caso o valor que deveria ser carregado no registrador TRISA seria
0B00111000 binrio, 56 decimal ou 0x38 hexadecimal. O circuito da situao
descrita seria o mostrado abaixo:

20/111

Timers
O PIC16F877A contm em sua arquitetura trs timers nomeados TMR0,
TMR1 e TMR2. Sendo o TMR0 e TMR2 de 1 byte e o TMR1 de 2 bytes. Os timers
so geralmente utilizados quando se deseja fazer uma medio precisa de tempo,
mas tambm podem ser configurados como contadores ou servir de base para
outro perifrico como o mdulo PWM (Pulse Width Modulation - Modulao por
largura de pulso).
Para entender o funcionamento do timer, imaginemos que o este um
tanque que se pode encher de gua, sendo assim podemos considerar que as
capacidades desses tanques sejam 255 litros para o TMR0 e TMR2 e 65535 litros
para o TMR1. Imagine agora que o processador joga 1 litro de gua a cada
quatro ciclos de clock (200ns se o microcontrolador estiver rodando a 20 MHz) ou
seja, incrementa o valor do timer em um.

Sabendo disso, temos uma referncia de tempo precisa, e podemos utilizar


essa frao de tempo pequena para contar tempos maiores. Se necessrio for,
existe a possibilidade de aumentar o tempo entre incremento dos timers. Isto
feito com a utilizao de prescalers que dividem o clock que entra no timer (ao
invs dos quatro ciclos de clock usuais), aumentando o perodo para incremento
do timer. Seguindo a analogia seria como se o processador jogasse um balde de
gua a cada 2, 4, 8 etc; perodos de tempo, permitindo assim temporizaes
maiores.
Para exemplificar a utilizao de Timers, tomemos o TMR1. Este possui um
valor representado por dois registradores TMR1H e TMR1L logo pode conter um
valor entre 0 e 65535. Este possui um prescaler configurvel em 1:1, 1:2, 1:4 e 1:8
o que para um clock de 20Mhz e prescaler 1:8 configura uma temporizao
mxima de aproximadamente:
65535

1
20000000

(4 x 8)

65535 x 1,6us

104ms

21/111

Porta Serial
Hoje em dia existe uma grande tendncia para que a comunicao entre
componentes eletrnicos seja serial. Isso denotado pela grande quantidade de
componentes que possuem interfaces seriais. Embora possa apresentar taxas de
transferncia inferiores a comunicao paralela, o menor uso de pinos e
conseqentemente uma economia de trilhas na placa do circuito impresso acaba
favorecendo o uso da comunicao serial.
Existem dois tipos de comunicao serial, a assncrona na qual no existe
um pino para sincronismo do tempo de cada bit numa transmisso; portanto,
fixo. E a sncrona que possui um pino que se alterna para indicar a transferncia
de um bit.
Destes dois tipos de serial a mais comumente utilizado a assncrona, esta
composta basicamente de dois pinos: TX (transmissor) e RX (receptor). Sendo
que o transmissor de um dispositivo ligado no receptor do outro e vice-versa. O
PIC16F877A possui uma porta serial universal (USART) que pode ser configurada
como assncrona ou sncrona. A porta serial se encontra nos pinos RC6 (TX) e
RC7 (RX) pinos 25 e 26 respectivamente do encapsulamento DIP.
Na serial assncrona cada byte transmitido bit a bit em uma freqncia
conhecida por ambos os dispositivos esta freqncia chamada de baudrate da
comunicao sendo que o inverso do valor do baudrate (perodo) representa o
tempo de um bit.
Como ambos os lados conhecem o baudrate da comunicao a
amostragem feita na metade desse meio perodo. A grosso modo, o transmissor
pega o byte a ser transmitido e amostra seus 8 bits no pino TX (quando
estudarmos a serial a fundo veremos que existem outros bits que so enviados
juntamente). O pino RX do receptor percebe cada um destes 8 bits e forma
novamente o byte do outro lado. Um diagrama bsico de uma comunicao
assncrona feita entre dois dispositivos visto abaixo. Nesta o Dispositivo A est
enviando um byte com valor 187 ou 0B10111011 em binrio para o Dispositivo B e
o perodo de um bit (P) representado pela seta vermelha:

22/111

Entradas Analgicas (A/D)


Um dispositivo muito comumente encontrado em microcontroladores o
conversor Analgico Digital (A/D). Este serve para capturamos uma tenso e
obtermos uma representao digital dela dentro do microcontrolador. Basicamente
a amostragem e feita comparando-se a tenso encontrada no pino da entrada
analgica com uma tenso de referncia VREF.
O PIC16F877A possui oito entradas analgicas (5 no PORTA e 3 no
PORTE). Embora possua mais de uma entrada a leitura de qualquer um dos oito
canais analgicos feita por dois registradores ADRESH e ADRESL que
representam respectivamente a parte alta e baixa da tenso lida. Os conversores
A/D dos microcontroladores PIC tm resoluo de 10bits. Ou seja, o valor lido da
entrada analgica ter um valor entre 0 e 1023. Suponhamos que tenhamos o
circuito abaixo no qual a entrada analogia AN0 (pino 2) foi ligada a um
potencimetro que coloca tenses entre 0 e 5v no pino:

Considerando que nesse caso o conversor A/D tem como referncia a


alimentao do microcontrolador (5V). Quando for colocado uma tenso em AN0
este valor ser comparado com a referencia (5V) e resultado da comparao
23/111

colocado nos registradores da leitura analgica. No caso da tenso em AN0 ser


2,5v o conversor estar na metade da escala, ou seja, conter um valor prximo
de 511~512.
Supondo que em determinado momento foi feita uma leitura do canal
analgico AN0 que possui referencia em 5V e que apontou o valor 300. Para
calcularmos o valor em tenso que esse nmero representa, precisamos calcular
a resoluo mnima do A/D que nada mais que o valor que uma unidade do
conversor representa.
Resoluo mnima =

VREF
=
1024

5V
1024

4,88 x 10-3V

= 4,88mV

No caso exemplificado o valor 4,88 mV por unidade. Desta forma o valor


lido 300, resultaria numa tenso de (300 x 4,88mV) = 1,46v. Analogamente
podemos fazer isso com qualquer valor lido e obter um valor proporcional a tenso
presente no pino da entrada analgica.
Interrupes
Em linhas gerais, uma interrupo um sinal de um dispositivo que
tipicamente resulta em uma troca de contexto, isso , o processador pra de fazer
o que est fazendo para atender o dispositivo que pediu a interrupo.
Interrupes foram concebidas para evitar o desperdcio de tempo
computacional em laos de software esperando eventos que sero disparados por
dispositivos. Ao invs de ficar parado esperando o evento acontecer, os
processadores tornaram-se capazes de realizar outras tarefas enquanto um ou
mais eventos esto pendentes. A interrupo avisa ao processador quando o
evento ocorreu, permitindo dessa forma uma acomodao eficiente para
dispositivos mais lentos.
Um diagrama do que ocorre em uma chamada de interrupo visto
abaixo. O ciclo principal do programa, que cclico e representado pela seta
vermelha, interrompido pela interrupo e o processamento desviado para o
tratamento da mesma. Aps o tratamento da interrupo o ciclo normal e
retomado ao ponto onde estava antes da chamada de interrupo, conforme
representado pela seta azul.

24/111

Interrupes do PIC
Os microcontroladores PIC apresentam diversas interrupes, no caso do
16F877A so 15 fontes diferentes. As mais comumente utilizadas so
exemplificadas abaixo.
Interrupo Externa
O pino RB0 (pino 33 no CI encapsulamento DIP) dotado de um circuito
interno capaz de detectar e gerar uma interrupo na subida ou descida do pino.
Isso configurvel atravs de programa e dentre outras coisas poderia ser usado
para efetuar contagem de pulsos de um sensor de velocidade. Bastando que para
isso uma varivel seja incrementada a cada chamada da funo de tratamento da
interrupo externa.

Interrupo de Timers
O PIC16F877A contm trs timers: TMR0, TMR1 e TMR2 cada qual
podendo gerar uma interrupo. O Timer nada mais que um registrador que
incrementado em com um perodo igual da freqncia do microcontrolador.
Conforme j foi mencionado, um timer se assemelha muito a uma pessoa
enchendo um taque com um balde com um perodo entre cada arremesso
equivalente ao do timer.

Considerando que o timer possui um valor e esse valor comparvel com a


quantidade de gua no tanque, assim como uma capacidade, a interrupo do
25/111

timer ocorrer quando o tanque transbordar, ou seja, um estouro de representao


na varivel do timer de 255 para 0 se o timer for de 8bits ou de 65535 para 0 se o
timer for 16 bits.
Exemplificando: para a interrupo do TMR1 ocorrer num determinado
tempo, precisamos encher o tanque com certa quantidade de gua. Esse valor
calculado pela seguinte equao:
TMR1 = 65536 -

(TEMPO x FREQUENCIA CLOCK PRINCIPAL)


4 x PRESCALER

Na equao o nmero 65536 representa o valor mximo que o TMR1 pode


conter acrescido de um. Isto , o nmero de baldes necessrio para que a partir
de um tanque vazio ocorra seu transbordamento. Este nmero e decrementado da
quantidade de baldes necessrios para contar o tempo desejado obtendo o valor
que o tanque precisa conter inicialmente. Assim se quisssemos que a
interrupo acontecesse-se a cada 10ms num processador com clock 20 MHz e
prescaler configurado para 1:8 teramos:
(10x10-3 x 20000000)
4x8
-3
TMR1 = 65536 - 10x10 x 625000
TMR1 = 65536 - 6250
TMR1 = 59286 = 0xE796 (hexadecimal)
TMR1 = 65536

Portanto bastaria carregarmos o TMR1 com o valor 0xE796. Feito isso a


interrupo do TMR1 ocorrer 10ms depois. Quando a interrupo ocorrer e o
tratamento da interrupo for chamado, devemos re-programar o TMR1 com o
mesmo valor (0xE796) afim de a prxima interrupo ocorrer dali 10ms e assim
por diante.
Interrupo Serial
A porta serial dos microcontroladores PIC pode gerar interrupo na
recepo e transmisso de um byte, tornando mais fcil a tarefa do programador
que no sabe, por exemplo, quando o byte vai ser enviado pelo outro dispositivo
ou deseja que sejam executadas outras tarefas enquanto o byte transmitido.

26/111

Captulo 3 Primeiros Passos


Seqncia de Criao de um Programa C para PIC
Para a criao de um programa C para microcontroladores PIC, uma ordem
precisa ser seguida. Esta ordem representada na figura abaixo:

Primeiramente o programa criado no compilador C (em nosso caso o


compilador CCS). No compilador efetuada a compilao, que nada mais do
converter a linguagem C contida no cdigo fonte em um arquivo de transporte (no
caso do compilador CCS o padro hex da Intel utilizado). Finalizada a
compilao, dependendo do gravador de PIC que o usurio dispe existe um
programa apropriado que importar o arquivo hex e efetuar a gravao no
microcontrolador.
No caso dos gravadores comercias da Microchip como o ICD2 e o PicStart,
por exemplo, deve-se utilizar o MPLAB. O MPLAB uma ferramenta de
desenvolvimento completa fornecida gratuitamente pela Microchip. Contm alm
do gerenciamento de gravadores, um mdulo de codificao em Assembly.
Quando associado ao desenvolvimento em linguagem C o MPLAB e mais
comumente utilizado para efetuar gravao e ou depurao de programas.
No caso da Cuscopic que contm um gravador paralelo embutido na placa
principal, o programa utilizado pode ser escolhido dentre os diversos existentes
que gerenciam um gravador de PIC paralelo. O mais comumente utilizado, o
EpicWin, da mesma forma que no MPLAB importa e grava os arquivos hex.
Para fins didticos o gravador paralelo suficiente alm de apresentar
baixo custo. Porm, para aplicaes profissionais se recomenda o uso de
gravadores Microchip pela sua maior confiabilidade e uma compatibilidade maior
com os novos microcontroladores lanados pela Microchip. Alm do mais, como
os gravadores paralelos so mais utilizados por hobbystas, o suporte a gravadores
paralelos bem menor que o oferecido pela Microchip com o MPLAB.

27/111

Primeiro Programa C
A seguir fornecido um programa exemplo, de uma aplicao muito
simples que pisca com um perodo de 500ms os 8 leds ligados ao PORTD do
PIC16F877A da placa base da Cuscopic:

28/111

Antes de comentarmos o significado de cada linha, convm fazer meno a


alguns aspectos do texto que inserido no compilador. Verificamos que no cdigo
existem pores que esto na cor cinza. Estas pores so chamadas
comentrios. Existem duas formas de fazer comentrios em linguagem C. A
primeira forma o comentrio de mltiplas linhas que se inicia com /* e termina
com */ e a segunda o comentrio de linha simples indicado por // que faz o
compilador considerar o restante da linha como comentrio.
evidente que os comentrios no so interpretados pelo compilador como
cdigo, e sua incluso tem sua importncia para fins de documentao do
programa.

Na primeira linha do programa exemplo temos a diretiva #include. Essa diz


ao compilador que o arquivo com o nome especificado entre <> deve ser
acrescido ao cdigo. No caso da primeira linha inclumos o arquivo 16F877A.h que
indica ao compilador qual modelo de microcontrolador PIC o programa se refere.
Logo aps temos a linha que inclui o arquivo que define os registradores de
uso especial do PIC16F877A. O arquivo 16F877A-sfrs includo para podermos
ter acesso diretamente aos registradores do microcontrolador, coisa que
normalmente no feita com o uso de funes de alto nvel embutidas no
compilador da CCS. Essas funes em alguns momentos facilitam e aceleram o
desenvolvimento, porm costumam no funcionar em todos os microcontroladores
e gerar cdigos demasiadamente ineficientes. Por este motivo, criar nossas
prprias funes acessando diretamente os registradores do PIC uma escolha
acertada. O arquivo tambm contm constantes de manipulao de bit que sero
utilizadas posteriormente. bom salientar que esse arquivo deve ser criado (no
fornecido pelo compilador) com base no mapa de memria (encontrado no
datasheet) do microcontrolador que se deseja trabalhar.
Na terceira linha temos a diretiva #fuses. Est define as configuraes
gerais do microcontrolador tais como: tipo do cristal, utilizao do watchdog,
proteo de cdigo contra leitura etc. Estas configuraes so carregadas pelo
programa que efetuar a gravao do chip. A listagem dos fuses vlidos para o
microcontrolador PIC16F877A pode ser encontrada no menu do compilador:
view>valid fuses.
29/111

Em seguida encontramos a diretiva #use delay(clock=20000000) que indica


ao compilador qual a freqncia que o microcontrolador trabalha. O uso dessa
rotina necessrio para podermos fazer laos de temporizao em software com
tempos precisos.
A seguir temos a funo main() que a funo principal do programa. Main
em ingls significa principal e essa a primeira funo chamada pelo
processador. Todas as instrues que o programa executar estaro dentre as
chaves que delimitam a funo main().
A primeira instruo da funo main() uma atribuio (=) no registrador
TRISD. Conforme j foi dito os registradores TRISx configuram o sentido do porto
no caso em questo o porto D. No caso, ao carregarmos TRISD com o valor
0B00000000 estamos configurando todo o PORTD como sada.
A segunda instruo da funo main() a inicializao do valor do PORTD
que ao receber o valor 0B00000000 coloca todos os pinos no valor lgico zero, ou
seja, desligado-os. imprescindvel essa instruo esteja no inicio do programa
para garantir que o porto possua um valor conhecido, uma vez que o valor dos
registradores eu controlam os portos quando o microcontrolador ligado
desconhecido.
Na seqncia temos a instruo while(true). A instruo while utilizada
para laos de repetio enquanto a condio descrita entre parentes for
verdadeira, nesse caso a condio explicitamente verdadeira atravs do valor
true que uma constante que para o compilador tem valor um (1) (em c qualquer
valor diferente de zero considerado verdadeiro). Isso faz com que todas as
instrues contidas entre as chaves delimitadoras da instruo while sejam
executadas infinitamente, ou seja, durante todo o perodo em que o
microcontrolador permanecer ligado. Resumidamente, este o lao principal do
programa. Por enquanto tudo que precisamos saber sobre a funo while, mais
detalhes sobre sua utilizao sero vistos posteriormente.
Delimitadas pelas chaves da instruo while(tur) temos quatro instrues: a
primeira faz uma atribuio do valor 0xFF no PORTD colocado todos as sadas
em nvel lgico um. Posteriormente temos a instruo delay_ms(500) que gera um
uma interrupo do processamento por 500ms (lao de software). Em seguida
novamente uma atribuio ao PORTD agora como valor 0, que coloca todas as
sadas em nvel lgico 0 e finalmente mais uma chamada a funo delay_ms(500)
que gera mais um atraso de 500ms.
conveniente observar que utilizamos as trs bases possveis nesse
pequeno programa. Base binria na atribuio do registrador TRISD, base
hexadecimal na primeira atribuio ao registrador PORTD e base decimal na
segunda atribuio ao PORTD. Evidentemente que isso foi feito para ilustrao e
que cabe ao programador decidir qual base ser usada em cada trecho do
programa para representar os valores desejados.

30/111

Captulo 4 Fundamentos da Linguagem C


A partir de agora comearemos a estudar os fundamentos da linguagem C,
vale lembrar que os fundamentos da linguagem C do compilador PCWH da CCS
so os mesmos que a da linguagem C ANSI (padronizada) o que torna o
aprendizado muito simples para quem j conhece a linguagem C.
Tipos de variveis
O compilador PIC-C da CCS possui 5 principais tipos de variveis int1, int8,
int16, int32 e float. A tabela abaixo apresenta as propriedades de cada uma
Tipo

Descrio

Tamanho

int1

Varivel boleana

1 bit

int8

Inteira

1 byte

int16

Inteira

2 bytes

int32

Inteira

4 bytes

float

Ponto Flutuante

4 bytes

Intervalo
0
ou
1
0 a 255
ou
-128 a 127
0 a 65535
ou
-32768 a 32767
0 a 4294967295
ou
-2147483648 a 2147483647
-1.7E38 a 1.7E38
(preciso de 6 dgitos)

Repare que ao definirmos os intervalos colocamos dois, um sem e outro


com sinal (onde permitindo o uso de nmeros negativos). Evidentemente que os
nmeros so sempre positivos na memria mas para que esses sejam visto com
sinal o compilador faz uso de uma codificao. A codificao de sinal mais
comumente utilizada a de complemento de dois que em linhas gerais pode ser
descrita como dividir o intervalo de representao em dois sendo um com e outro
sem sinal (por este motivo nmeros com sinais tem a metade do intervalo em cada
extremo).
Alm dessas temos outros tipos, mas que refletem variveis de mesmo
tamanho das j citadas, e podem ser utilizadas opcionalmente.
Tipo
short
char

Equivalncia
int1
Int8

Utilizao
Outra nomenclatura para int1.
Varivel de 1 byte que representa um caractere
e no uma varivel inteira. Usada somente para
melhor adequao.
31/111

long

int16

Variveis long geralmente expressam o dobro da


varivel padro do processador. O dobro de 8
bits no caso do pic.

Declarao de Variveis
Para serem usadas as variveis precisam ser declaradas, as declaraes
podem ser feitas da seguinte forma:
tipo nome-da-variavel;
ou:
tipo nome-da-variavel1, nome-da-variavel2, ...;
Onde tipo o tipo da varivel e os nomes-da-variavelx so os nomes das
variveis separados por vrgulas. Opcionalmente na declarao da varivel ela
pode ser inicializada com um valor, caso contrrio o valor inicial da varivel
desconhecido:
tipo nome-da-variavel = valor-inicial;
ou:
tipo nome-da-variavel1 = v-inicial1, nome-da-variavel2 = v-inicial2, ...;
Por padro as variveis so sem sinal com exceo do tipo float que por
definio tem sinal. No caso de desejarmos utilizar variveis com sinal a sua
declarao deve ser precedida com o prefixo signed:
signed tipo nome-da-variavel;
ou:
signed tipo nome-da-variavel1, nome-da-variavel2, ...;
Existem casos que se deseja declarar uma varivel que representa um
conjunto com um tamanho determinado como um texto. Isso comumente
chamado de array ou vetor. Para isso devemos determinar o seu tamanho entre
colchetes; para que dessa forma o compilador aloque no uma mas n posies de
memria com o tipo especificado.
tipo nome-da-variavel [tamanho-do-array] = {valor-inicial1,valor-inicial2,...};

32/111

Nomenclatura de variveis
E recomendado na nomenclatura de variveis o uso de prefixos que
especificam o seu tipo. Isso auxilia o programador a identificar, em qualquer parte
do programa, o tipo da varivel sem ter de retornar e olhar a sua declarao. Os
prefixos para as principais variveis so exemplificados abaixo:
Tipo
int1
int8
int16
int32
signed int1
signed int8
signed int16
signed int32
float
char

Prefixo
i1_
i8_
i16_
i32_
si1_
si8_
si16_
si32_
f
c

Exemplo de declarao
int1 i1_Variavel;
int8 i8_Variavel;
int16 i16_Variavel;
int32 i32Variavel;
signed int1 i1_Variavel;
signed int8 i8_Variavel;
signed int16 i16_Variavel;
signed int32 i32Variavel;
float fVariavel;
char cVariavel;

Isso tambm ajuda o programador a evitar atribuio de variveis fora de


seus limites como o valor 300 a uma varivel de um byte cujo limite 255. Embora
o uso de prefixos no impea que isso ocorra, ajudar o programador reparar no
tipo da varivel e no se esquecer desse detalhe.
Constantes
Existem diversos tipos de constantes que podem ser representadas: inteira,
ponto flutuante, caractere e cadeia de caracteres.

Constante Numrica
10
0B10010111
0xAB
27.35

Significado
Constante Inteira em base Decimal
Constante Inteira em base Binria
Constante Inteira em base Hexadecimal
Constante Ponto Flutuante

Constantes de caractere so representadas entre apstrofos () e


equivalente ao nmero que a representa. No caso do compilador CCS, utilizada
a tabela ASCII para representao de caracteres:
Constante Caractere
A
Z
=

Valor ASCII
65
90
61

33/111

Constantes de caracteres especiais so representadas procedidas pela


barra invertida (\), seguido de um determinado caractere.
Constante Caractere
\n
\r
\t
\\
\
\
\0

Caractere Representado
Caractere de mudana de Linha (LF)
Caractere de retorno do Carro (CR)
Caractere de Tabulao (TAB)
Caractere de barra Invertida
Caractere Apstrofo
Caractere Aspas
Caractere Nulo

O caractere nulo e colocado ao final de uma cadeia de caracteres para


representar o seu final. Cadeias de caracteres so usadas somente em variveis
que foram declaradas como array.
Cadeia
abcde

Significado
Cadeia que armazena a, b, c, d, e e \0.
Cadeia vazia que armazena apenas \0

Tamanho
6 bytes
1 byte

Declarao de Constantes
Alm das constantes que j esto definidas na linguagem C. possvel
declarar outras constantes como o nmero pi, nmero de Euler etc. Para isso
usamos a diretiva #define conforme exemplificado abaixo:
#define nome-da-constante valor-da-constante
conveniente observar que o nome da constante no pode ser o de uma
palavra reservada da linguagem C (como a palavra main, por exemplo) e que
exista pelo menos um espao entre a palavra e o nome da constante e dessa
ultima antes do valor da constante. Outro ponto importante e que semelhante as
variveis aconselhvel que as constantes tenham como prefixo a letra
k(minscula) seguida pelo nome da constante escrito todo em letras maisculas.
Isso se justifica para que o programador possa identificar que o que est sendo
referenciado uma constante em qualquer ponto do programa.
#define kEXEMPLO_DE_CONSTANTE_COM_VALOR_DEZ 10
Operadores
Operadores Aritmticos
Os operadores aritmticos so os seguintes: + (adio), - (subtrao), *
(multiplicao) e / (diviso). No caso de diviso entre nmeros inteiros, o resultado
34/111

truncado (somente a parte inteira o resultado). O operador % fornece o resto


da diviso e s funciona como operador entre tipos inteiros:
Expresso
22 / 3
22 % 3
14 / 4
14 % 4
21 / 7
21 % 7

Tem o valor
7
1
3
2
3
0

Operador de atribuio
O operador de atribuio (=) copia o valor do lado direito para a varivel, ou
endereo, do lado esquerdo. Alm disso, o operador = tambm retorna este valor
como resultado, isso significa que, se fizermos x = y = 0, ser processada
inicialmente a operao y = 0, atribuindo o valor 0 varivel y. Esta expresso
fornece o resultado 0, que atribudo varivel x. No final do processamento,
ambas as variveis tero o valor 0.
Expresso
i=3
i=3+4
i=k=4

Operao
Coloca o valor 3 em i
O valor 7 colocado em i
O valor 4 colocado em k; o valor da
atribuio (4) ento colocado em i

Em uma operao de atribuio de variveis de tipos diferentes,


recomendado o uso de casting explcito. Isto , dizer ao compilador que uma
varivel deve ser convertida para o tipo da varivel que a receber antes da
atribuio ser realizada. Exemplos de casting podem ser vistos na tabela abaixo.
Expresso

Explicao
A varivel de dois bytes i16_Soma recebe o
valor da varivel de um byte i8_Termo aps
i16_Soma = (int16) i8_Termo
esta ser convertida para uma varivel de
dois bytes.
A varivel de dois bytes i16_Var recebe a
varivel de quatro bytes i32_Resultado aps
i16_Var = (int16) i32_Resultado
esta ser convertida para uma varivel de
dois bytes.
Atribuies entre variveis de tipos diferentes pode resultar em erro de
estouro de limite, ou perda de informao. Por exemplo: uma varivel de quatro
bytes com o valor 70000(0x00011170) atribuda a uma varivel de dois bytes.
Neste caso apenas os valor dos dois bytes menos significativos sero repassados
e a varivel de dois bytes receber o valor 4464(0x1170).
35/111

Operadores relacionais
Os operadores relacionais em C so: == (igual a), != (diferente de), > (maior
que), < (menor que), >= (maior ou igual a) e <= (menor ou igual a). O resultado de
dois valores conectados por um operador relacional ser zero (0) para falso ou um
(1) para verdadeiro.
Expresso
5<3
3<5
5 == 5
3 == 5
5 <= 5

Resultado
0
1
1
0
1

Evidentemente que no lugar de constantes poderamos comparar variveis


e constantes ou variveis com variveis, como var1 >= 3 ou var1 < var2 mas no
faria sentido usarmos operadores relacionais para comparar constantes uma vez
que seu resultado seria sempre o mesmo.
Operadores lgicos
Os dois operadores lgicos binrios so && (e) e || (ou). O resultado de
suas operaes tambm ser 0 (falso) ou 1 (verdadeiro).
Expresso
5 || 3
5 || 0
5 && 3
5 && 0
(i > 5) && (i <= 7)

Resultado
1
1
1
0
1 se i for maior que 5 e menor ou igual a 7.
0, qualquer outro caso

O operador negao (!) inverte o sentido do valor que o segue. Qualquer


valor no zero ser convertido para 0 e um valor 0 ser convertido para 1.
Expresso
!5
!0
!(i > 5)

Resultado
0
1
1 se i no for maior que 5. 0, se i for maior que 5.

Operadores bit a bit


Os operadores bit a bit operam apenas com nmeros inteiros. Os
operadores so: & (e, bit a bit), | (ou, bit a bit), ^ (ou exclusivo, bit a bit), ~
(negao, bit a bit), << (deslocamento esquerda) e >> (deslocamento direita).
36/111

So utilizados normalmente para ligar ou desligar bits, testar bits especficos em


variveis inteiras etc.
Expresso
1|2
0xFF & 0x0F
0x35 | 0xCC
0x0F << 2
0x1C >> 1
~0xA5

Valor
3
0x0F
0xFF
0x3C
0x0E
0x5A

Expresso binria
00000001 | 00000010
11111111 & 00001111
00110101 | 11001100
00001111 << 2
00011100 >> 1
~10100101

Valor binrio
00000011
00001111
11111111
00111100
00001110
01011010

Atribuies reduzidas
A linguagem C oferece muitos operadores de atribuio que so reduo de
outros operadores. Eles tomam a forma de op=, onde op pode ser +, -, *, /, %, <<,
>>, &, ^, |. A expresso f op= g anloga a f = f op g. Atribuies reduzidas so
somente um atalho para que o programador tenha o cdigo mais sucinto
(menor):
Expresso
a += 2
i <<= 1
s /= (7 + 2)

Equivale a
a=a+2
i = i << 1
s = s / (7 + 2)

Operadores pr e ps-fixados
Os operadores pr e ps-fixados incrementam (++) ou decrementam (--)
uma varivel. Uma operao prefixada realizada antes que o valor da varivel
seja utilizado. Uma operao ps-fixada efetuada aps a utilizao da varivel.
Por exemplo, para uma varivel i inteira com valor 5:
Expresso
5 + (i++)
5 + (i--)
5 + (++i)
5 + (--i)

Valor de i utilizado
na avaliao
5
5
6
4

Valor da expresso

Valor final de i

10
10
11
9

6
4
6
4

Precedncia de operadores
Os operadores tm uma ordem de precedncia. Isto , sem parnteses,
certas operaes so efetuadas antes de outras com menor precedncia. Para
operadores com precedncia igual, a associatividade da esquerda para direita.
Para se ter certeza da interpretao, as expresses que se deseja interpretar
primeiro devem ser agrupadas entre parnteses. Caso no haja parnteses a
ordem de execuo das operaes bsicas :
37/111

1)
2)
3)
4)

*
/
+
-

38/111

Captulo 5 Estruturas Condicionais


Manipulao de Bits
Em diversos momentos ao utilizarmos a linguagem C para desenvolver
programas para microcontroladores, necessitamos efetuar manipulao de bits em
variveis e ou registradores. Anteriormente, quando fizemos a apresentao do
primeiro programa em C, efetuamos a incluso de um arquivo que nos permite o
acesso direto aos registradores do PIC, o arquivo 16F877A-sfrs. Ao observarmos
a parte final desse arquivo, que consta na figura abaixo, verificamos que existem
as constantes de manipulao de bits.

Utilizando-se dessas constantes podemos fazer manipulaes em alguns


bits de uma varivel ou registrador sem alterar os demais. Um exemplo de
operao de manipulao de bits e utilizar estas constantes com os registradores
PORTx para efetuar mudanas de estado dos pinos do microcontrolador.

39/111

Os bits dos registradores PORTx refletem o valor atribudo nos pinos de seu
porto quando estes esto configurados como sada. Quando configurado como
entrada, a leitura do registrador reflete o valor lgico de cada pino do porto.
Operao
PORTx = 0xFF
PORTx = 0x00
i8_ PORTx = PORTx

Resultado
Coloca todos os pinos do PORTx em nvel lgico 1
Coloca todos os pinos do PORTx em nvel lgico 0
L o valor do PORTx e o coloca na varivel i8_ PORTx

Manipulao de Bits de um Porto


Basicamente podemos fazer trs operaes com um pino configurado como
sada: setar (colocar um ou mais pinos em nvel lgico 1), resetar (colocar um
ou mais pinos em nvel lgico 0) e togglar (inverter um ou mais pinos, se em
nvel lgico 1 vai para nvel lgico 0 e vice versa). As expresses aqui usadas
derivam dos verbos em ingls: set, reset e toggle.
Setar um pino
PORTx

Operao

Operao em Binrio

Resultado

0B11001100

PORTx |= set_bit0

PORTx = 0B11001100 | 0B00000001

0B11001101

Pega o valor do PORTx (0B11001100) efetua lgica OU com a constante


set_bit0 (0B00000001) e atribui o resultado ao PORTx. Setando o bit 0.
Resetar um pino
PORTx

Operao

Operao em Binrio

Resultado

0B00110111

PORTx &= clear_bit4

PORTx = 0B00110111 & 0B11101111

0B00100111

Pega o valor do PORTx (0B00110111) efetua lgica E com a constante


clear_bit4 (0B11101111) e atribui o resultado ao PORTx. Resetando o bit 4.
Togglar um pino
PORTx

Operao

Operao em Binrio

Resultado

0B11110110

PORTx ^= set_bit7

PORTx = 0B11110110 ^ 0B10000000

0B01110110

Pega o valor do PORTx (0B11110110) efetua lgica XOR com a constante


set_bit7 (0B10000000) e atribui o resultado ao PORTx. Toglando o bit 7. Convm
salientar que se a operao de toglar fosse realizada novamente, sobre o
resultado obtido, o valor do porto voltaria a ser 0B11110110.

40/111

Teste de bits de um Porto


Alm de manipulao de bits, as constantes podem ser usadas para testar
o estado de um ou mais bits de um registrador. Sendo que nesse caso, ser
formado uma expresso que retornar falso (0) ou verdadeiro (1).
Valor do PORTx

Expresso

Resultado

0B10010000

((PORTx & set_bit0) == 0)

0B10010000

((PORTx & set_bit0) != 0)

0B10010000

((PORTx & set_bit7) == 0)

0B10010000

((PORTx & set_bit7) != 0)

Descrio
Testa se o bit 0
do PORTx est
em nvel lgico 0.
Resultou em 1
(verdadeiro)
Testa se o bit 0
do PORTx est
em nvel lgico 1
Resultou em 0
(falso)
Testa se o bit 7
do PORTx est
em nvel lgico 0.
Resultou em 0
(falso)
Testa se o bit 7
do PORTx est
em nvel lgico 1
Resultou em 1
(verdadeiro)

Como podemos observar a expresso que efetua um teste de bit e formada


de duas partes. Primeiramente o valor contido no porto computado com lgica E
com uma constante set_bitx, que isola o bit a ser testado. O resultado da operao
anterior comparado para ver se igual a zero, caso estejamos testando se o bit
est em nvel lgico 0. Ou comparado se diferente de zero, caso estivermos
testando se o bit est em nvel lgico 1. As expresses de teste de bits so
utilizadas quando se deseja efetuar uma ao mediante a alterao de um pino do
porto ou esperar que determinada condio acontea para que o processamento
continue.

41/111

Captulo 6 Teste Condicional


Teste condicional if() {} else {}
O teste condicional if() (se em portugus) avalia a expresso entre
parnteses e executa o comando ou serie de comandos subseqentes delimitados
por chaves caso a expresso em questo for verdadeira. A clusula else (seno
em portugus) pode ser usada para determinar os comandos que sero
executados caso a expresso seja do teste condicional seja falsa.
// Funo principal do programa
void main()
{
// Configura o sentido do PORTC
// Todos os pinos so sada
TRISC = 0B00000000;
// Inicializa o valor das sadas do PORTC
// Todos os pinos desligados
PORTC = 0B00000000;
// Configura o sentido do PORTD
// Todos os pinos so entrada
TRISD = 0B11111111;
// Lao principal
while(TRUE)
{
// Se o valor do PORTD for igual a 4
if(PORTD == 4)
{
// Liga o PORTC
PORTC = 0xFF;
}
// Seno
else
{
// Desliga o PORTC
PORTC = 0x00;
}
}
}
// Funo principal do programa
void main()
{
// Configura o sentido do PORTC
// Todos os pinos so sada
TRISC = 0B00000000;
// Inicializa o valor das sadas do PORTC
// Todos os pinos desligados
PORTC = 0B00000000;

42/111

// Configura o sentido do PORTD


// Todos os pinos so entrada
TRISD = 0B11111111;
// Lao principal
while(TRUE)
{
// Se o valor do PORTD for maior que 4 e menor que 63
if((PORTD > 4) && (PORTD < 63))
{
// Liga o PORTC
PORTC = 0xFF;
}
// Seno
else
{
// Desliga o PORTC
PORTC = 0x00;
}
}
}

Teste condicional aplicado


O teste condicional pode ser aplicado juntamente com uma expresso de
teste de bits sobre um porto para que conforme o estado de um pino seja tomada
uma ao:
// Funo principal do programa
void main()
{
// Configura o sentido do PORTC
// Todos os pinos so sada
TRISC = 0B00000000;
// Inicializa o valor das sadas do PORTC
// Todos os pinos desligados
PORTC = 0B00000000;
// Configura o sentido do PORTD
// S-S-S-S-S-S-E-E
TRISD = 0B00000011;
// Inicializa o valor das sadas do PORTD
// Todos os pinos desligados
PORTD = 0B00000000;
// Lao de repetio
while(TRUE)
{
// Se pressionou o Boto RD0
if((PORTD & set_bit0) != 0)
{

43/111

// Escreve o valor 12 no PORTC


PORTC = 12;
}
// Seno se pressionou o Boto RD1
else if((PORTD & set_bit1) != 0)
{
// Escreve o valor 101 no PORTC
PORTC = 101;
}
// Seno
else
{
// Escreve o valor 0 (desliga) o PORTC
PORTC = 0;
}
}
}

Escolha condicional Switch { case _: break; default: break;}


Em determinados casos quando queremos fazer uma escolha condicional
em uma varivel inteira, podemos utilizar o comando switch (escolha em
portugus). O comando switch verifica o valor de um argumento que deve ser um
inteiro ou registrador e compara seu valor com os rtulos dos casos. Cada caso
deve ser rotulado por uma constante inteira e deve ser terminada por dois pontos
(:) e no por ponto-e-vrgula. Pode haver uma ou mais instrues seguindo cada
case, e estas no necessariamente precisam estar entre chaves.
// Funo principal do programa
void main()
{
// Configura o sentido do PORTC
// Todos os pinos so sada
TRISC = 0B00000000;
// Inicializa o valor das sadas do PORTC
// Todos os pinos desligados
PORTC = 0B00000000;
// Configura o sentido do PORTD
// Todos os pinos so entrada
TRISD = 0B11111111;
// Lao principal
while(TRUE)
{
// Dependendo do valor do PORTD
switch(PORTD)
{
// Caso no PORTD tiver a senha 1
case 3:
// Liga o PORTC por 1 segundo
PORTC = 0XFF;
delay_ms(1000);

44/111

break;
// Caso no PORTD tiver a senha 2
case 59:
// Liga o PORTC por 2 segundos
PORTC = 0XFF;
delay_ms(2000);
break;
// Caso no PORTD tiver a senha 3
case 129:
// Liga o PORTC por 3 segundos
PORTC = 0XFF;
delay_ms(3000);
break;
// Caso padro
default:
// Desliga o PORTC
PORTC = 0;
break;
}
}
}

O corpo de um switch deve estar entre as chaves que o delimitam. Se um


caso for igual ao valor do argumento, a execuo comea nele. Se nenhum caso
for satisfeito, o controle do programa sai do bloco switch, a menos que exista um
caso default. Se existir, a execuo comea nele. Os rtulos dos casos devem ser
todos diferentes.
O comando break causa uma sada imediata do programa. Se no houver
um break seguindo as instrues do caso, o programa segue executando todas as
instrues dos casos abaixo, at encontrar um break ou o fim do corpo do switch.

45/111

Captulo 7 Laos de Repetio


Lao de Repetio while() {} e do {} while
O lao de repetio while() {} executa o que estiver delimitado pelas chaves
enquanto a expresso pressente entre os parnteses for verdadeira da mesma
forma funciona o lao do {} while(). A diferena entre esses dois laos que
enquanto o lao while() {} executa o teste da expresso e somente se o resultado
for verdadeiro executa o que est entre as chaves o lao do {} while() executa o
que est entre as chaves a primeira vez sem efetuar o teste e s continua a
executar o que est entre as chaves se o resultado da expresso for verdadeiro.
// Funo principal do programa
void main()
{
// Configura o sentido do PORTC
// Todos os pinos so sada
TRISC = 0B00000000;
// Inicializa o valor das sadas do PORTC
// Todos os pinos desligados
PORTC = 0B00000000;
// Configura o sentido do PORTD
// S-S-S-S-S-S-E-E
TRISD = 0B00000011;
// Inicializa o valor das sadas do PORTD
// Todos os pinos desligados
PORTD = 0B00000000;
// Lao principal
while(TRUE)
{
// Espera pressionar RDO ou RD1
// Enquanto
// o bit0 do PORTD estiver desligado
// e
// o bit1 do PORTD estiver desligado
// {} = nada
while(((PORTD & set_bit0) == 0) && ((PORTD & set_bit1) == 0))
{}
// Se pressionar RD0
if((PORTD & set_bit0) != 0)
{
// Valor inicial do contador recebe 1
i8_ContInicial = 1;
}
// Seno se pressionar RD1
if((PORTD & set_bit1) != 0)

46/111

{
// Valor inicial do contador recebe 4
i8_ContInicial = 4;
}
// Espera soltar RDO e RD1
// Faa
// {} = nada
// Enquanto
// o bit0 do PORTD estiver ligado
// ou
// o bit1 do PORTD estiver ligado
do
{}
while(((PORTD & set_bit0) != 0) || ((PORTD & set_bit1) != 0));
// Contador recebe o valor inicial
i8_Cont = i8_ContInicial;
// Lao while {}
while(i8_Cont <= 3)
{
// Pisca RC0
PORTC |= SET_BIT0;
delay_ms(500);
PORTC &= CLEAR_BIT0;
delay_ms(500);
// Incrementa contador
i8_Cont++;
}
// Contador recebe o valor inicial
i8_Cont = i8_ContInicial;
// Lao do {} while
do
{
// Pisca RC0
PORTC |= SET_BIT1;
delay_ms(500);
PORTC &= CLEAR_BIT1;
delay_ms(500);
// Incrementa contador
i8_Cont++;
}
while(i8_Cont <= 3);
}
}

47/111

Lao de Repetio for( ; ; ) {}


O lao de repetio for composto de basicamente trs partes: uma
inicializao, um teste e uma interao. Essas trs partes so delimitadas pelo
ponto e vrgula e nenhuma delas obrigatria (somente os ponto e virgula). Os
laos for geralmente so utilizados quando se deseja executar uma lista de
instruo por um nmero determinado de vezes como pode se verificar no
exemplo abaixo onde a varivel i8_LacoFor inicializada com zero, comparada se
menor ou igual a cinco (se sim continua a execuo) e incrementada de um em
um a cada passagem do lao:
// Funo principal do programa
void main()
{
// Configura o sentido do PORTC
// Todos os pinos so sada
TRISC = 0B00000000;
// Inicializa o valor das sadas do PORTC
// Todos os pinos desligados
PORTC = 0B00000000;
// Configura o sentido do PORTD
// Todos os pinos so entrada
TRISD = 0B11111111;
// Lao de repetio
while(true)
{
// Para i8_LacoFor de 0 at 5 de 1 em 1
for(i8_LacoFor = 0; i8_LacoFor <= 5; i8_LacoFor++)
{
// Joga o valor de i8_LacoFor no PORTC
PORTC = i8_LacoFor;
// Delay
delay_ms(500);
// Se pressionou o Boto RD7
if((PORTD & set_bit7) != 0)
{
// Pra o lao
break;
}
}
}
}

48/111

Captulo 8 Procedimentos e Funes


Procedimentos e Funes
A partir que vamos evoluindo nossos conhecimentos na linguagem C
verificamos que existem tarefas que podem ser agrupadas e serem executadas
diversas vezes. A isso na linguagem C chamamos de procedimentos e funes. A
nica e pequena diferena entre procedimento e funo reside no fato de que as
funes retornam uma varivel. Tanto procedimentos como funes podem ter ou
no passagem de variveis para a sua execuo essas variveis chamamos de
parmetros.
Ao trabalharmos com procedimentos/funes surge o conceito de
visibilidade de variveis, pois dentro dos procedimentos/funes podem ser
declaradas variveis, e essas variveis so classificadas como locais e existem
somente no escopo de execuo da funo, isto , aps o termino da execuo do
procedimento/funo a varivel se torna inacessvel (ela destruda). Se
necessitarmos de que uma varivel tenha visibilidade em todo programa devemos
declar-la fora do limite de qualquer procedimento/funo (inclusive da funo
principal). Essa varivel ento denominada varivel global.
Outro conceito que devemos ter em mente ao trabalhar com
procedimentos/funes so as diretivas #inline e #separate. Essas diretivas
quando colocadas acima da declarao de um procedimento/funo indicam que a
funo ser copiada para a posio de chamada ou o programa executa um salto
para a sua execuo, respectivamente. Sendo assim os procedimentos/funes
#inline ocupam mais memria de programa mas tem execuo mais rpida e as
que so escritas com #separate ocupam mais memria mas tm um pequeno
atraso de processamento para a sua chamada (devido a troca de contexto e
carregamento de possveis parmetros). Tendo em mente isso, o programador
deve ento decidir qual dos dois tipos utilizar sabendo que o tipo padro (caso
nada seja definido explicitamente) o tipo #separate.
/*
Programa que demonstra a utilizao do
estrutura condicional e funes
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
// HS
// NOWDT
// PUT

-> Cristal > 4mhz


-> No utilizar o Watchdog
-> Power Up Time

49/111

//
//
//
//

PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->

Protege o cdigo contra leitura


Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Constantes ===========================================================
#define kSENHA1 3
// Senha 1 (0B00000011)
#define kSENHA2 59 // Senha 2 (0B00111011)
#define kSENHA3 129 // Senha 3 (0B10000001)
// Prottipos de funes ================================================
#separate
// Piscar o PORTC 3 vezes
void pisca_portc(void);
// ======================================================================
#separate
// Liga PORTC por um tempo definido em segundos
void liga_portc(int8 i8_Tempo);
// ======================================================================
#separate
// Retorna se as teclas esto liberadas
int1 teclas_liberadas(void);
// ======================================================================
#separate
// Retorna se senha passada correta
int1 testa_senha(int8 i8_Senha);
// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido do PORTC
// Todos os pinos so sada
TRISC = 0B00000000;
// Inicializa o valor das sadas do PORTC
// Todos os pinos desligados
PORTC = 0B00000000;
// Configura o sentido do PORTD
// Todos os pinos so entrada
TRISD = 0B11111111;
// Pisca o PORTC no incio do programa
pisca_portc();

50/111

// Lao principal
while(TRUE)
{
// Se a senha que estiver no PORTD estiver correta
if(testa_senha(PORTD))
{
// Dependendo do valor do PORTD
switch(PORTD)
{
// Caso no PORTD tiver a senha 1
case kSENHA1:
// Liga o PORTC por 1 segundo
liga_portc(1);
break;
// Caso no PORTD tiver a senha 2
case kSENHA2:
// Liga o PORTC por 2 segundos
liga_portc(2);
break;
// Caso no PORTD tiver a senha 3
case kSENHA3:
// Liga o PORTC por 3 segundos
liga_portc(3);
break;
// Caso padro
default:
// Desliga o PORTC
PORTC = 0;
break;
}
// Espera liberar as teclas
do {} while(!teclas_liberadas());
}
}
}
// ======================================================================
#separate
// Piscar o PORTC 3 vezes
void pisca_portc(void)
{
// Usada no lao for que faz o PORTC piscar
int8 i8_i;

51/111

// Para i8_i de 0 at 2 (3 vezes)


for(i8_i=0; i8_i<3; i8_i++)
{
// Pisca o PORTC
PORTC = 0xFF;
delay_ms(100);
PORTC = 0;
delay_ms(100);
}
}
// ======================================================================
#separate
// Liga PORTC por um tempo definido em segundos
void liga_portc(int8 i8_Tempo)
{
// Liga o PORTC pelo tempo definido
PORTC = 0xFF;
for(; i8_Tempo>0; i8_Tempo--)
delay_ms(1000);
PORTC = 0;
}
// ======================================================================
#separate
// Retorna se as teclas esto liberadas
int1 teclas_liberadas(void)
{
// Se nenhuma tecla est pressionada
if(PORTD == 0)
{
// Retorna que sim (true), as teclas esto liberadas
return(true);
}
// Seno
else
{
// Retorna que no/falso (false), as teclas no esto liberadas
return(false);
}
}
// ======================================================================
#separate
// Retorna se senha passada correta
int1 testa_senha(int8 i8_Senha)
{
// Se a senha passada for a senha 1 ou a senha 2 ou a senha3
if((PORTD == kSENHA1) || (PORTD == kSENHA2) || (PORTD == kSENHA3))
{
// Retorna que sim (true), a senha correta
return(true);
}

52/111

// Seno
else
{
// Retorna que no/falso (false), as senha passada no correta
return(false);
}
}
// ======================================================================

No exemplo acima cada um dos tipos possveis de procedimentos e


funes, repare que quando no h parmetros e definido isso explicitamente
colocando a palavra reservada void que em ingls significa vazio:
* Exemplo de procedimento sem parmetros: void pisca_portc(void);
Exemplo de procedimento com parmetros: void liga_portc(int8 i8_Tempo);
Exemplo de funo sem parmetros: int1 teclas_liberadas(void);
Exemplo de funo com parmetros: int1 testa_senha(int8 i8_Senha);
Podemos observar tambm que nesse exemplo existem antes do programa
principal uma sesso que define como os procedimentos/funes sero. A isso
chamamos de prottipos e permitem que as funes tenham visibilidade entre si
(consigam chamar umas as outras. Os prottipos dizem ao compilador que existir
um procedimento/funo com determinadas caractersticas e esse pode estar
efetivamente implementado mais abaixo no programa.

53/111

Captulo 9 Funo Printf


Funo Printf
/*
Exemplo da utilizao da funo printf
com o display LCD da IHM da CuscoPic
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
#define kTEMPO_ENTRE 500 // Tempo em ms entre cada demonstrao de valor
// Variveis globais ====================================================
// Int1 =================================================================
int1 i1_i = false; // Usado para a utilizao de printf com uma int1
// Int8 =================================================================
int8 i8_i = 0; // Usado para a utilizao de printf com uma int8
// Int16 ================================================================
int16 i16_i = 0; // Usado para a utilizao de printf com uma int16
// Int32 ================================================================
int32 i32_i = 0; // Usado para a utilizao de printf com uma int32
// Float ================================================================
float f_i = 0; // Usado para a utilizao de printf com uma float
// ======================================================================

54/111

// Funo principal do programa


void main()
{
// Configura o sentido dos portos
TRISB = 0B00000000;
TRISC = 0B00000000;
TRISD = 0B11111111;
// Zera
PORTB =
PORTC =
PORTD =

os portos
0B00000000;
0B00000000;
0B00000000;

// Inicializa o display
inic_display();
// Lao de repetio
while(true)
{
// Reflete no PORTC o que est lendo do PORTD
PORTC = PORTD;
// Conforme o que for pressionado no PORTD
switch(PORTD)
{
// Se for DB0
case 0B00000001:
// Sinaliza o que esta mostrando
end_display(1,1);
mostra("1 BYTE:
");
// Para i8_i de 0 a 254 de 1 em 1
for(i8_i=0; i8_i<0xFF; i8_i++)
{
// Mostra a varivel por um tempo
end_display(2,1);
printf(mostra,"%u %3u %03u %c %X ",
i8_i,i8_i,i8_i,i8_i,i8_i);
delay_ms(kTEMPO_ENTRE);
}
break;
// Se for DB1
case 0B00000010:
// Sinaliza o que esta mostrando
end_display(1,1);
mostra("1 BYTE SINAL:
");
// Para i8_i de 0 a 254 de 1 em 1
for(i8_i=0; i8_i<0xFF; i8_i++)
{
// Mostra a varivel por um tempo
end_display(2,1);
printf(mostra,"%d %03d %c %X
",

55/111

i8_i,i8_i,i8_i,i8_i,i8_i);
delay_ms(kTEMPO_ENTRE);
}
break;
// Se for DB2
case 0B00000100:
// Sinaliza o que esta mostrando
end_display(1,1);
mostra("2 BYTES:
");
// Para i16_i de 0 a 60000 de 1000 em 1000
for(i16_i=0; i16_i<=60000; i16_i+=1000)
{
// Mostra a varivel por um tempo
end_display(2,1);
printf(mostra,"%lu %05lu %lX
",i16_i,i16_i,i16_i);
delay_ms(kTEMPO_ENTRE);
}
break;
// Se for DB3
case 0B00001000:
// Sinaliza o que esta mostrando
end_display(1,1);
mostra("2 BYTES SINAL: ");
// Para i16_i de 0 a 60000 de 1000 em 1000
for(i16_i=0; i16_i<=60000; i16_i+=1000)
{
// Mostra a varivel por um tempo
end_display(2,1);
printf(mostra,"%ld %lX
",i16_i,i16_i);
delay_ms(kTEMPO_ENTRE);
}
break;
// Se for DB4
case 0B00010000:
// Sinaliza o que esta mostrando
end_display(1,1);
mostra("4 BYTES:
");
// Para i32_i de 0 a 1000000000 de 10000000 em 10000000
for(i32_i=0; i32_i<=1000000000; i32_i+=10000000)
{
// Mostra a varivel por um tempo
end_display(2,1);
printf(mostra,"%lu
",i32_i);
delay_ms(kTEMPO_ENTRE);
}

56/111

break;
// Se for DB5
case 0B00100000:
// Sinaliza o que esta mostrando
end_display(1,1);
mostra("4 BYTES SINAL: ");
// Para i32_i de 0 a 1000000000 de 10000000 em 10000000
for(i32_i=0; i32_i<=1000000000; i32_i+=10000000)
{
// Mostra a varivel por um tempo
end_display(2,1);
printf(mostra,"%ld
",i32_i);
delay_ms(kTEMPO_ENTRE);
}
break;
// Se for DB6
case 0B01000000:
// Sinaliza o que esta mostrando
end_display(1,1);
mostra("4 BYTES FLOAT: ");
// Para f_i de 0 a 20 de 0.01 em 0.01
for(f_i=0; f_i<20; f_i+=0.01)
{
// Mostra a varivel por um tempo
end_display(2,1);
printf(mostra,"%f %01.3f
",f_i,f_i);
delay_ms(kTEMPO_ENTRE);
}
break;
// Se for DB7
case 0B10000000:
// Sinaliza o que esta mostrando
end_display(1,1);
mostra("1 BIT:
");
// Varivel recebe falso
i1_i = false;
// Mostra a varivel por 10x o tempo normal
end_display(2,1);
printf(mostra,"%d %u
",i1_i,i1_i);
delay_ms(10*kTEMPO_ENTRE);
// Varivel recebe verdadeiro
i1_i = true;

57/111

// Mostra a varivel por 10x o tempo normal


end_display(2,1);
printf(mostra,"%d %u
",i1_i,i1_i);
delay_ms(10*kTEMPO_ENTRE);
break;
// Qualquer outro valor
default:
// Sinaliza para escolher uma opo
end_display(1,1);
mostra(" ESCOLHA OPCAO ");
end_display(2,1);
mostra("
DE PRINTF
");
break;
}
}
}
// ======================================================================

58/111

Captulo 10 Timers
Timer 0 como contador
/*
Programa demonstrando TMR0 operando como contador
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
// Variveis globais ====================================================
// Int1 =================================================================
// Int8 =================================================================
int8 i8_Var = 0; // Varivel de estouros do TMR0
// Int16 ================================================================
// Int32 ================================================================
// Float ================================================================
// Prottipos de funes ================================================
// Prottipos de interrupes ===========================================
#int_timer0
// Interrupo de overflow do TMR0
void t0_int(void);

59/111

// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido dos portos
TRISA = 0B00010000;
TRISB = 0B00000000;
TRISC = 0B00000000;
TRISD = 0B00000000;
// Inicializa os portos
PORTA = 0B00000000;
PORTB = 0B00000000;
PORTC = 0B00000000;
PORTD = 0B00000000;
// Incremento TMR pelo RA4, na subida, prescaler para TMR0,
// prescaler 1:2 (Datasheet pgina 25)
// Bit 07 - RBPU: PORTB Pull-up Enable bit
// Bit 06 - INTEDG: Interrupt Edge Select bit
// Bit 05 - T0CS: TMR0 Clock Source Select bit
// Bit 04 - T0SE: TMR0 Source Edge Select bit
// Bit 03 - PSA: Prescaler Assignment bit
// Bit 02 - \
// Bit 01 - | PS2:PS0: Prescaler Rate Select bits
// Bit 00 - /
OPTION_REG = 0B00100000;
// Coloca o valor inicial no TMR0 (Datasheet pgina 55)
TMR0 = 0xF6;
// Habilita interrupes, interrupes de perifricos
// e TMR0 (Datasheet pgina 26)
// Bit 07 - GIE: Global Interrupt Enable bit
// Bit 06 - PEIE: Peripheral Interrupt Enable bit
// Bit 05 - TMR0IE: TMR0 Overflow Interrupt Enable bit
// Bit 04 - INTE: RB0/INT External Interrupt Enable bit
// Bit 03 - RBIE: RB Port Change Interrupt Enable bit
// Bit 02 - TMR0IF: TMR0 Overflow Interrupt Flag bit
// Bit 01 - INTF: RB0/INT External Interrupt Flag bit
// Bit 00 - RBIF: RB Port Change Interrupt Flag bit
INTCON = 0B11100000;
// Inicializa o display
inic_display();
// Lao de repetio
while(true)
{
// Mostra o PORTA, varivel, TMR0 e INTCON
end_display(1,1);
printf(mostra,"PORTA:%X VAR:%X ",PORTA,i8_Var);
end_display(2,1);
printf(mostra,"TMR0:%X INTC:%X ",TMR0,INTCON);

60/111

// Pisca o RC0
PORTC ^= set_bit0;
// Intervalo de 0,5s
delay_ms(500);
}
}
// ======================================================================
#int_timer0
// Interrupo de overflow do TMR0
void t0_int(void)
{
// Incrementa o valor da varivel
i8_Var++;
}
// ======================================================================

Timer 1 como Timer


// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
// Variveis globais ====================================================
// Int1 =================================================================
// Int8 =================================================================
int8 i8_Cont50ms = 0; // Usado para contar tempos de 50ms
int8 i8_Cont100ms = 0; // Usado para contar tempos de 100ms
int8 i8_Cont500ms = 0; // Usado para contar tempos de 500ms
int8 i8_Cont1s
= 0; // Usado para contar tempos de 1s
int8 i8_Cont5s
= 0; // Usado para contar tempos de 5s

61/111

// Int16 ================================================================
// Int32 ================================================================
// Float ================================================================
// Prottipos de funes ================================================
// Prottipos de interrupes ===========================================
#int_timer1
// Interrupo de overflow do TMR1
void t1_int(void);
// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido dos portos
TRISA = 0B00000000;
TRISB = 0B00000000;
TRISC = 0B00000000;
TRISD = 0B00000000;
// Inicializa os portos
PORTA = 0B00000000;
PORTB = 0B00000000;
PORTC = 0B00000000;
PORTD = 0B00000000;
// Prescaler do TMR1 8, clock FOSC/4,
// liga o timer (Datasheet pgina 59)
// Bit 07 - Unimplemented: Read as 0
// Bit 06 - Unimplemented: Read as 0
// Bit 05 - \ T1CKPS1:T1CKPS0: Timer1 In. Clock Prescaler Select bits
// Bit 04 - /
// Bit 03 - T1OSCEN: Timer1 Oscillator Enable Control bit
// Bit 02 - T1SYNC: Timer1 Ext. Clock In. Synchronization Cont. bit
// Bit 01 - TMR1CS: Timer1 Clock Source Select bit
// Bit 00 - TMR1ON: Timer1 On bit
T1CON = 0B00110001;
// Clculo e carregamento do tempo para interrupo do TMR1
// TMR1 = 65536 - ((TEMPOINT1 * FOSC ) / (4 * PRESCALER))
// TMR1 = 65536 - ((10e-3 * 20000000 ) / (4 * 8))
// TMR1 = 65536 - 6250
// TMR1 = 59286
// TMR1 = 0xE796
TMR1H = 0xE7;
TMR1L = 0x96;
//
//
//
//

Habilita interrupes, interrupes de perifricos


(Datasheet pgina 26)
Bit 07 - GIE: Global Interrupt Enable bit
Bit 06 - PEIE: Peripheral Interrupt Enable bit

62/111

// Bit
// Bit
// Bit
// Bit
// Bit
// Bit
INTCON

05 - TMR0IE: TMR0 Overflow Interrupt Enable bit


04 - INTE: RB0/INT External Interrupt Enable bit
03 - RBIE: RB Port Change Interrupt Enable bit
02 - TMR0IF: TMR0 Overflow Interrupt Flag bit
01 - INTF: RB0/INT External Interrupt Flag bit
00 - RBIF: RB Port Change Interrupt Flag bit
= 0B11000000;

// Habilita interrupo pelo TMR1 (Datasheet pgina 27)


// Bit 07 - PSPIE: Parallel Slave Port Read/Write Int. Enable bit(1)
// Bit 06 - ADIE: A/D Converter Interrupt Enable bit
// Bit 05 - RCIE: USART Receive Interrupt Enable bit
// Bit 04 - TXIE: USART Transmit Interrupt Enable bit
// Bit 03 - SSPIE: Synchronous Serial Port Interrupt Enable bit
// Bit 02 - CCP1IE: CCP1 Interrupt Enable bit
// Bit 01 - TMR2IE: TMR2 to PR2 Match Interrupt Enable bit
// Bit 00 - TMR1IE: TMR1 Overflow Interrupt Enable bit
PIE1 = 0B00000001;
// Inicializa o display
inic_display();
// Lao de repetio
while(true)
{
// Mostra uma animao
end_display(1,1);
mostra("TIMER 1 OPERANDO");
end_display(2,1);
mostra("
");
// Tempo de 1s entre
delay_ms(1000);
// Mostra uma animao
end_display(1,1);
mostra("
");
end_display(2,1);
mostra("TIMER 1 OPERANDO");
// Tempo de 1s entre
delay_ms(1000);
}
}
// ======================================================================
#int_timer1
// Interrupo de overflow do TMR1
void t1_int(void)
{
// Reprograma o TMR1
TMR1H = 0xE7;
TMR1L = 0x96;
// Pisca RC0 (10ms)
PORTC ^= set_bit0;

63/111

// Incrementa contador para 50ms


i8_Cont50ms++;
// Se passou 50ms
if(i8_Cont50ms >= 5)
{
// Zera o contador de 50ms
i8_Cont50ms = 0;
// Pisca o RC1 (50ms)
PORTC ^= set_bit1;
}
// Incrementa contador para 100ms
i8_Cont100ms++;
// Se passou 100ms
if(i8_Cont100ms >= 10)
{
// Zera o contador de 100ms
i8_Cont100ms = 0;
// Pisca o RC2 (100ms)
PORTC ^= set_bit2;
// Incrementa contador para 500ms
i8_Cont500ms++;
// Se passou 500ms
if(i8_Cont500ms >= 5)
{
// Zera o contador de 500ms
i8_Cont500ms = 0;
// Pisca o RC3 (500ms)
PORTC ^= set_bit3;
}
// Incrementa contador para 1s
i8_Cont1s++;
// Se passou
if(i8_Cont1s
{
// Zera o
i8_Cont1s

1s
>= 10)
contador de 1s
= 0;

// Pisca o RC4 (1s)


PORTC ^= set_bit4;
// Incrementa contador para 5s
i8_Cont5s++;
// Se passou 5s
if(i8_Cont5s >= 5)
{

64/111

// Zera o contador de 5s
i8_Cont5s = 0;
// Pisca o RC5 (5s)
PORTC ^= set_bit5;
}
}
}
}
// ======================================================================

65/111

Captulo 11 PWM (Pulse Width Modulation)


Configurao do PWM
/*
Programa que demonstra o uso do PWM
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
// Variveis globais ====================================================
// Int1 =================================================================
// Int8 =================================================================
// Int16 ================================================================
int16 i16_PWM = 0; // Usada para variar o PWM
// Int32 ================================================================
// Float ================================================================
// Prottipos de funes ================================================
#separate
// Carrega os 10 bits do PWM
void carrega_pwm(int16 i16_PWM);
// Prottipos de interrupes ===========================================

66/111

// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido dos portos
TRISA = 0B00000000;
TRISB = 0B00000000;
TRISC = 0B00000000;
TRISD = 0B11111111;
TRISE = 0B00000000;
// Inicializa os portos
PORTA = 0B00000000;
PORTB = 0B00000000;
PORTC = 0B00000000;
PORTD = 0B00000000;
PORTE = 0B00000000;
// Liga o TMR2, Prescaler 1:1 (Datasheet pgina 63)
// Bit 07 - Unimplemented: Read as 0
// Bit 06 - \
// Bit 05 - \ TOUTPS3:TOUTPS0: Timer2 Output Postscale Select bits
// Bit 04 - /
// Bit 03 - /
// Bit 02 - TMR2ON: Timer2 On bit
// Bit 01 - \ T2CKPS1:T2CKPS0: Timer2 Clock Prescale Select bits
// Bit 00 - /
T2CON = 0B00000100;
// Habilita o mdulo pwm (Datasheet pgina 66)
// Bit 07 - Unimplemented: Read as 0
// Bit 06 - Unimplemented: Read as 0
// Bit 05 - \ CCPxX:CCPxY: PWM Least Significant bits
// Bit 04 - /
// Bit 03 - \
// Bit 02 - \ CCPxM3:CCPxM0: CCPx Mode Select bits
// Bit 01 - /
// Bit 00 - /
CCP1CON = 0B00001111;
// Configura a freqncia para 19,53kHz
// PR2 = (1 / (Freqncia PWM * TOSC * Presaler TMR2 * 4)) - 1
// PR2 = (1 / (19530 * (1/2000000) * 1 * 4)) - 1
// PR2 = (1 / (19530 * 0,00000005 * 1 * 4)) - 1
// PR2 = (1 / 0,003906) - 1
// PR2 = 256,01 - 1
// PR2 = 256 - 1
PR2 = 255;
// Carrega o valor zero no PWM
carrega_pwm(0);
// Inicializa o display
inic_display();

67/111

// Mostra o que o programa se prope


end_display(1,1);
mostra("
TESTE PWM
");
delay_ms(1000);
// Lao de repetio
while(true)
{
// Para i8_PWM de 0 at 1023 de 1 em 1
for(i16_PWM=0; i16_PWM<=1023; i16_PWM++)
{
// Carrega o valor atual no PWM
carrega_pwm(i16_PWM);
// Mostra o valor do PWM
end_display(1,1);
printf(mostra,"PWM:%lu
",i16_PWM);
end_display(2,1);
printf(mostra,"PR1L:%X P1CON:%X",CCPR1L,CCP1CON);
// Tempo entre
delay_ms(100);
// Se pressionar algo do PORTD espera soltar
do {} while(PORTD != 0);
}
// Tempo aps completar 100%
delay_ms(3000);
}
}
// ======================================================================
#separate
// Carrega os 10 bits do PWM
void carrega_pwm(int16 i16_PWM)
{
// Utilizado como rascunho nessa funo
int8 i8_RascPWM;
// Pega os 2 bits menos significativos e
// os coloca no Bit 5 e 4 de i8_RascPWM
i8_RascPWM = (int8)(i16_PWM << 4);
i8_RascPWM &= 0B00110000;
// Limpa os bits 5 e 4 de CCP1CON
CCP1CON &= 0B11001111;
// Coloca os bits de i8_RascPWM em CCP1CON
CCP1CON |= i8_RascPWM;
// Coloca os 8 bits mais significativos em CCPR1L
CCPR1L = (int8)(i16_PWM >> 2);
}
// ======================================================================

68/111

Captulo 12 Conversor Analgico Digital


Utilizando o Conversor Analgico Digital
/*
Programa que demonstra o uso do
conversor A/D (Analgico/Digital)
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
// Variveis globais ====================================================
// Int1 =================================================================
// Int8 =================================================================
// Int16 ================================================================
int16 i16_ValorAD = 0; // Contm o valor da leitura analgica
// Int32 ================================================================
// Float ================================================================
// Prottipos de funes ================================================
#separate
// Seleciona o canal de leitura da analgica
void seleciona_canal_analogica(int8 i8_Canal);
// ======================================================================

69/111

#separate
// Efetua a leitura do canal analgica passado
int16 le_canal_analogica(int8 i8_Canal);
// Prottipos de interrupes ===========================================
// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido dos portos
TRISA = 0B00000001;
TRISB = 0B00000000;
TRISC = 0B00000000;
TRISD = 0B00000000;
TRISE = 0B00000000;
// Zera
PORTA =
PORTB =
PORTC =
PORTD =
PORTE =

os portos
0B00000000;
0B00000000;
0B00000000;
0B00000000;
0B00000000;

// Inicializa o display
inic_display();
// Clock analgica FOSC/64, liga o A/D (Datasheet pgina 129)
// Bit 07 - \ ADCS1:ADCS0: A/D Conversion Clock Select bits
// Bit 06 - /
// Bit 05 - \
// Bit 04 - | CHS2:CHS0: Analog Channel Select bits
// Bit 03 - /
// Bit 02 - GO/DONE: A/D Conversion Status bit
// Bit 01 - Unimplemented: Read as 0
// Bit 00 - ADON: A/D On bit
ADCON0 = 0B10000001;
// Justificado a direita, FOSC/64,
// apenas AN0 analgica (Datasheet pgina 130)
// Bit 07 - ADFM: A/D Result Format Select bit
// Bit 06 - ADCS2: A/D Conversion Clock Select bit
// Bit 05 - Unimplemented: Read as 0
// Bit 04 - Unimplemented: Read as 0
// Bit 03 - Unimplemented: Read as 0
// Bit 02 - \
// Bit 01 - | PCFG3:PCFG0: A/D Port Configuration Control bits
// Bit 00 - /
ADCON1 = 0B11001110;
// Lao de repetio
while(true)
{
// L o canal 0
i16_ValorAD = le_canal_analogica(0);

70/111

// Mostra o valor da leitura


end_display(1,1);
printf(mostra,"A/D[0]:%4lu

",i16_ValorAD);

// Mostra uma barra percentual proporcional ao valor do A/D


mostra_barra_percentual((int32)i16_ValorAD,1023,false);
}
}
// ======================================================================
#separate
// Seleciona o canal de leitura da analgica
void seleciona_canal_analogica(int8 i8_Canal)
{
// Se o canal for invlido canal o 7
if(i8_Canal > 7) i8_Canal = 7;
// Desloca 3 bits para a esquerda
i8_Canal <<= 3;
// Limpa os bits de seleo de canal do ADCON0
ADCON0 &= 0B11000111;
// Seleciona o canal analgica
ADCON0 |= i8_Canal;
// Delay para a seleo do canal
delay_us(50);
}
// ======================================================================
#separate
// Efetua a leitura do canal analgica passado
int16 le_canal_analogica(int8 i8_Canal)
{
// Contm o valor da entrada analgica
int16 i16_Analogica = 0;
// Primeiro seleciona o canal
seleciona_canal_analogica(i8_Canal);
// Inicia a converso analgica
ADCON0 |= set_bit2;
// Espera a converso acabar
do {} while((ADCON0 & set_bit2) != 0);
// Pega a parte alta do resultado da converso
i16_Analogica = (int16)ADRESH;
i16_Analogica <<= 8;
// Acrescenta a parte baixa
i16_Analogica += (int16)ADRESL;

71/111

// Retorna o valor da analgica


return(i16_Analogica);
}
// ======================================================================

72/111

Captulo 13 E2PROM
Utilizando E2PROM - Electrically-Erasable Programmable Read-Only Memory
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
#define kENDERECO_E2PROM_VARIAVEL 0x05 // End. da E2prom para a varivel
// Variveis globais ====================================================
// Int1 =================================================================
// Int8 =================================================================
int8 i8_Variavel = 0; // Varivel que ser guardada na eeprom
// Int16 ================================================================
// Int32 ================================================================
// Float ================================================================
// Prottipos de funes ================================================
#separate
// Grava um byte na E2prom no endereo passado
void grava_int8_e2prom(int8 i8_Endereco, int8 i8_Dado);
// ======================================================================
#separate
// L um byte na E2prom no endereo passado
int8 le_int8_e2prom(int8 i8_Endereco);

73/111

// Prottipos de interrupes ===========================================


// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido dos portos
TRISA = 0B00000000;
TRISB = 0B00000000;
TRISC = 0B00000000;
TRISD = 0B00000011;
TRISE = 0B00000000;
// Zera
PORTA =
PORTB =
PORTC =
PORTD =
PORTE =

os portos
0B00000000;
0B00000000;
0B00000000;
0B00000000;
0B00000000;

// Inicializa o display
inic_display();
// L a varivel da eeprom
i8_Variavel = le_int8_e2prom(kENDERECO_E2PROM_VARIAVEL);
// Lao de repetio
while(true)
{
// Se pressionar RD1 incrementa varivel a
if((PORTD & set_bit1) != 0) i8_Variavel++;
// Se pressionar RD0 decrementa varivel a
if((PORTD & set_bit0) != 0) i8_Variavel--;
// Mostra as variveis no display
end_display(1,1);
printf(mostra,"VARIAVEL:%u
",i8_ i8_Variavel);
// Guarda o byte na E2prom
grava_int8_e2prom(kENDERECO_E2PROM_VARIAVEL, i8_Variavel);
// Enquanto alguma tecla estiver pressionada
do {} while(PORTD != 0);
// Enquanto no pressionar nenhuma tecla
do {} while(PORTD == 0);
}
}
// ======================================================================
#separate
void grava_int8_e2prom(int8 i8_Endereco, int8 i8_Dado)
{

74/111

// Guarda o valor de INTCON


int8 i8_INTCON;
// Define
// Bit 07
// Bit 06
// Bit 05
// Bit 04
// Bit 03
// Bit 02
// Bit 01
// Bit 00
EECON1 &=

que a escrita ser realizada na E2prom (Datasheet pgina 36)


- EEPGD: Program/Data EEPROM Select bit
- Unimplemented: Read as 0
- Unimplemented: Read as 0
- Unimplemented: Read as 0
- WRERR: EEPROM Error Flag bit
- WREN: EEPROM Write Enable bit
- WR: Write Control bit
- RD: Read Control bit
clear_bit7;

// Coloca o endereo de escrita (Datasheet pgina 35)


EEADR = i8_Endereco;
// Coloca o valor a ser escrito na E2prom (Datasheet pgina 35)
EEDATA = i8_Dado;
// Habilita a escrita na E2prom (Datasheet pgina 36)
EECON1 |= set_bit2;
// Guarda o valor de INTCON
i8_INTCON = INTCON;
// Desabilita interrupes
INTCON &= clear_bit7;
// Libera a escrita do dado para a E2prom
EECON2 = 0x55;
EECON2 = 0xAA;

(Datasheet pgina 37)

// Inicia a escrita da E2prom (Datasheet pgina 36)


EECON1 |= set_bit1;
// Espera completar a escrita
do {} while((EECON1 & set_bit1) != 0);
// Restaura o valor de INTCON
INTCON = i8_INTCON;
// Inibe a escrita na E2prom (Datasheet pgina 36)
EECON1 &= clear_bit2;
}
// ======================================================================
#separate
int8 le_int8_e2prom(int8 i8_Endereco)
{
// Define que a leitura ser realizada na E2prom
EECON1 &= clear_bit7;
// Coloca o endereo de leitura (Datasheet pgina 35)
EEADR = i8_Endereco;

75/111

// Inicia a leitura da E2prom (Datasheet pgina 36)


EECON1 |= set_bit0;
// Espera completar a leitura
do {} while((EECON1 & set_bit0) != 0);
// Retorna o valor lido da E2prom
return(EEDATA);
}
// ======================================================================

76/111

Captulo 14 Porta Serial


Recepo e Transmisso de dados pela USART
/*
Programa que demonstra a recepo e
transmisso de um byte pela serial.
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
// Variveis globais ====================================================
// Int1 =================================================================
// Int8 =================================================================
int8 i8_Byte = 0; // Usado para receber e enviar um byte pela 232
// Int16 ================================================================
// Int32 ================================================================
// Float ================================================================
// Prottipos de funes ================================================
#separate
// Envia um byte pela porta serial
void envia_232(int8 i8_DadoParaEnviar);
// Prottipos de interrupes ===========================================

77/111

// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido dos portos
TRISA = 0B00000000;
TRISB = 0B00000000;
TRISC = 0B10000000;
TRISD = 0B00000000;
TRISE = 0B00000000;
// Zera
PORTA =
PORTB =
PORTC =
PORTD =
PORTE =

os portos
0B00000000;
0B00000000;
0B00000000;
0B00000000;
0B00000000;

// Habilita transmisso, 8 bits, assncrona e


// hi-speed (Datasheet pgina 113)
// Bit 07 - CSRC: Clock Source Select bit
// Bit 06 - TX9: 9-bit Transmit Enable bit
// Bit 05 - TXEN: Transmit Enable bit
// Bit 04 - SYNC: USART Mode Select bit
// Bit 03 - Unimplemented: Read as 0
// Bit 02 - BRGH: High Baud Rate Select bit
// Bit 01 - TRMT: Transmit Shift Register Status bit
// Bit 00 - TX9D: 9th bit of Transmit Data, can be Parity bit
TXSTA = 0B00100100;
// Habilita a recepo, 8 bits (Datasheet pgina 114)
// Bit 07 - SPEN: Serial Port Enable bit
// Bit 06 - RX9: 9-bit Receive Enable bit
// Bit 05 - SREN: Single Receive Enable bit
// Bit 04 - CREN: Continuous Receive Enable bit
// Bit 03 - ADDEN: Address Detect Enable bit
// Bit 02 - FERR: Framing Error bit
// Bit 01 - OERR: Overrun Error bit
// Bit 00 - RX9D: 9th bit of Received Data
RCSTA = 0B10010000;
// Configura o Baud rate para 9600bps e hi-speed
// SPBRG = (FOSC / (16 * BaudRate)) - 1
// SPBRG = (20000000 / (16 * 9600)) - 1
// SPBRG = (20000000 / (153600)) - 1
// SPBRG = 130 - 1
SPBRG = 129;
// Inicializa o display
inic_display();
// Mostra o que esta fazendo
end_display(1,1);
mostra("RECEBENDO...
");

78/111

// Lao de repetio
while(true)
{
// Espera receber um byte
do {} while((PIR1 & set_bit5) == 0);
// Limpa flag de recepo
PIR1 &= clear_bit5;
// Recebe o byte
i8_Byte = RCREG;
// Notifica o que recebeu
end_display(2,1);
printf(mostra,"D:%03u H:%X C:%c

",i8_Byte,i8_Byte,i8_Byte);

// Devolve o byte que recebeu


envia_232(i8_Byte);
}
}
// ======================================================================
#separate
// Envia um byte pela porta serial
void envia_232(int8 i8_DadoParaEnviar)
{
// Aguarda TX buffer esvaziar
while((TXSTA & set_bit1 ) == 0);
// Coloca o byte no registrador de envio da USART
TXREG = i8_DadoParaEnviar;
}
// ======================================================================

Recepo utilizando interrupo e Transmisso de dados pela USART


/*
Programa que demonstra a
recepo e transmisso de um byte pela serial.
Utilizando-se da interrupo de recepo serial.
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
// HS
// NOWDT

-> Cristal > 4mhz


-> No utilizar o Watchdog

79/111

//
//
//
//
//

PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->

Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
#define kFOSC
20000000 // Freqncia do cristal 20Mhz
#define kPRESCALER_TMR1 8
// Prescaler do TMR1
#define kTEMPO_INT_TMR1 100e-3
// Tempo de interrupo do TMR1
// Variveis globais ====================================================
// Int1 =================================================================
int1 i1_Passou500ms = false; // Determina que passou 500ms
// Int8 =================================================================
int8 i8_TMR1H
= 0; // Mais significativa do TMR1
int8 i8_TMR1L
= 0; // Menos significativa do TMR1
int8 i8_Cont500ms
= 0; // Usado para contar 500ms
int8 i8_Laco
= 0; // Usado em laos
int8 i8_ContReticencias = 0; // Usado para animar as reticncias
int8 i8_Byte
= 0; // Usado para receber/enviar pela serial
// Int16 ================================================================
int16 i16_TMR1 = 0; // Usado para calculo do valor do TMR1
// Int32 ================================================================
// Float ================================================================
// Prottipos de funes ================================================
// Prottipos de interrupes ===========================================
#int_rda
// Tratamento da interrupo por recepo de dado da USART
void rx_int(void);
// ======================================================================
#int_timer1
// Interrupo de overflow do TMR1
void t1_int(void);
// ======================================================================
// Funo principal do programa
void main()
{

80/111

// Configura o sentido dos portos


TRISA = 0B00000000;
TRISB = 0B00000000;
TRISC = 0B10000000;
TRISD = 0B00000000;
TRISE = 0B00000000;
// Zera
PORTA =
PORTB =
PORTC =
PORTD =
PORTE =

os portos
0B00000000;
0B00000000;
0B00000000;
0B00000000;
0B00000000;

// Habilita transmisso, 8 bits, assncrona e


// hi-speed (Datasheet pgina 113)
// Bit 07 - CSRC: Clock Source Select bit
// Bit 06 - TX9: 9-bit Transmit Enable bit
// Bit 05 - TXEN: Transmit Enable bit
// Bit 04 - SYNC: USART Mode Select bit
// Bit 03 - Unimplemented: Read as 0
// Bit 02 - BRGH: High Baud Rate Select bit
// Bit 01 - TRMT: Transmit Shift Register Status bit
// Bit 00 - TX9D: 9th bit of Transmit Data, can be Parity bit
TXSTA = 0B00100100;
// Habilita a recepo, 8 bits (Datasheet pgina 114)
// Bit 07 - SPEN: Serial Port Enable bit
// Bit 06 - RX9: 9-bit Receive Enable bit
// Bit 05 - SREN: Single Receive Enable bit
// Bit 04 - CREN: Continuous Receive Enable bit
// Bit 03 - ADDEN: Address Detect Enable bit
// Bit 02 - FERR: Framing Error bit
// Bit 01 - OERR: Overrun Error bit
// Bit 00 - RX9D: 9th bit of Received Data
RCSTA = 0B10010000;
// Configura o Baud rate para 9600bps e hi-speed
// SPBRG = (FOSC / (16 * BaudRate)) - 1
// SPBRG = (20000000 / (16 * 9600)) - 1
// SPBRG = (20000000 / (153600)) - 1
// SPBRG = 130 - 1
SPBRG = 129;
// Prescaler do TMR1 8, clock (FOSC/4),
// liga o timer (Datasheet pgina 59)
// Bit 07 - Unimplemented: Read as 0
// Bit 06 - Unimplemented: Read as 0
// Bit 05 - \ T1CKPS1:T1CKPS0: Timer1 In. Clock Prescaler Select bits
// Bit 04 - /
// Bit 03 - T1OSCEN: Timer1 Oscillator Enable Control bit
// Bit 02 - T1SYNC: Timer1 Ext. Clock In. Synchronization Control bit
// Bit 01 - TMR1CS: Timer1 Clock Source Select bit
// Bit 00 - TMR1ON: Timer1 On bit
T1CON = 0B00110001;

81/111

// Clculo do Tempo de Interrupo do TMR1:


i16_TMR1 = (65536-((kTEMPO_INT_TMR1*kFOSC)/(4*kPRESCALER_TMR1)));
// Guarda os valores de carga do TMR1
i8_TMR1H = (int8)(i16_TMR1 >> 8);
i8_TMR1L = (int8)i16_TMR1;
// Carrega o TMR1
TMR1H = i8_TMR1H;
TMR1L = i8_TMR1L;
// Habilita interrupes, interrupes de perifricos
// (Datasheet pgina 26)
// Bit 07 - GIE: Global Interrupt Enable bit
// Bit 06 - PEIE: Peripheral Interrupt Enable bit
// Bit 05 - TMR0IE: TMR0 Overflow Interrupt Enable bit
// Bit 04 - INTE: RB0/INT External Interrupt Enable bit
// Bit 03 - RBIE: RB Port Change Interrupt Enable bit
// Bit 02 - TMR0IF: TMR0 Overflow Interrupt Flag bit
// Bit 01 - INTF: RB0/INT External Interrupt Flag bit
// Bit 00 - RBIF: RB Port Change Interrupt Flag bit
INTCON = 0B11000000;
// Habilita Interrupo pela recepo da USART e
// TMR1 (Datasheet pgina 27)
// Bit 07 - PSPIE: Parallel Slave Port Read/Write Int.Enable bit(1)
// Bit 06 - ADIE: A/D Converter Interrupt Enable bit
// Bit 05 - RCIE: USART Receive Interrupt Enable bit
// Bit 04 - TXIE: USART Transmit Interrupt Enable bit
// Bit 03 - SSPIE: Synchronous Serial Port Interrupt Enable bit
// Bit 02 - CCP1IE: CCP1 Interrupt Enable bit
// Bit 01 - TMR2IE: TMR2 to PR2 Match Interrupt Enable bit
// Bit 00 - TMR1IE: TMR1 Overflow Interrupt Enable bit
PIE1 = 0B00100001;
// Inicializa o display
inic_display();
// Mostra o que esta fazendo
end_display(1,1);
mostra("RECEBENDO
");
// Lao de repetio
while(true)
{
// Se passou 500ms
if(i1_Passou500ms)
{
// Os eventos que ocorrem a cada 500ms foram tratados
i1_Passou500ms = false;
// Incrementa o nmero de . da reticencia
i8_ContReticencias++;
// Avalia o limite de .
if(i8_ContReticencias > 3) i8_ContReticencias = 0;

82/111

// Enderea para a posio das reticncias


end_display(1,10);
// Conforme o nmero de . da reticncia que tem de mostrar
for(i8_Laco=0; i8_Laco<i8_ContReticencias; i8_Laco++)
{
// Mostra um .
mostra(".");
}
// Apaga os . da reticncia que no formam escritos
for(i8_Laco=i8_ContReticencias; i8_Laco<3; i8_Laco++)
{
// Apaga o restante da mensagem
mostra(" ");
}
}
// Se ocoreu erro de recepo (OVERRUN)
if((RCSTA & set_bit1) != 0)
{
// Mostra o RCSTA
end_display(2,1);
printf(mostra,"ERRO!!! RCSTA=%x",RCSTA);
// Beepa sinalizando
PORTE |= set_bit2;
delay_ms(1000);
PORTE &= clear_bit2;
// Reinicia a recepo serial
RCSTA &= clear_bit4;
RCSTA |= set_bit4;
}
// Notifica o que recebeu
end_display(2,1);
printf(mostra,"D:%03u H:%X C:%c

",i8_Byte,i8_Byte,i8_Byte);

}
}
// ======================================================================
#int_rda
// Tratamento da interrupo por recepo de dado da USART
void rx_int(void)
{
// Recebe o byte
i8_Byte = RCREG;
delay_ms(1000);
}
// ======================================================================
#int_timer1
// Interrupo de overflow do TMR1
void t1_int(void)

83/111

{
// Reprograma o timer
TMR1H = i8_TMR1H;
TMR1L = i8_TMR1L;
// Incrementa o contador de 500ms
i8_Cont500ms++;
// Se passou 500ms
if(i8_Cont500ms >= 5)
{
// Reinicia o contador
i8_Cont500ms = 0;
// Determina que passou 500ms
i1_Passou500ms = true;
}
}
// ======================================================================

84/111

Captulo 15 Watchdog
Utilizando o Watchdog
/*
Programa demonstrando o Watchdog operando
resetando caso algo trave o microcontrolador
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,WDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
WDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


Utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
// Variveis globais ====================================================
// Int1 =================================================================
// Int8 =================================================================
int8 i8_i = 0; // Usada em laos for
// Int16 ================================================================
// Int32 ================================================================
// Float ================================================================
// Prottipos de funes ================================================
// Prottipos de interrupes ===========================================
// ======================================================================

85/111

// Funo principal do programa


void main()
{
// Configura o sentido dos portos
TRISC = 0B00000000;
TRISD = 0B00000001;
// Zera os portos
PORTC = 0B00000000;
PORTD = 0B00000000;
// Prescaler para WDT, prescaler 1:256 (Datasheet pgina 25)
// Bit 07 - RBPU: PORTB Pull-up Enable bit
// Bit 06 - INTEDG: Interrupt Edge Select bit
// Bit 05 - T0CS: TMR0 Clock Source Select bit
// Bit 04 - T0SE: TMR0 Source Edge Select bit
// Bit 03 - PSA: Prescaler Assignment bit
// Bit 02 - \
// Bit 01 - | PS2:PS0: Prescaler Rate Select bits
// Bit 00 - /
OPTION_REG = 0B00001111;
// Pisca o RC0 5 vezes
for(i8_i=0; i8_i<10; i8_i++)
{
PORTC ^= set_bit0;
delay_ms(100);
}
// Lao de repetio
while(true)
{
// Se pressionar RD0 "trava o programa"
do {} while((PORTD & set_bit0) != 0);
// Pisca o RC1
PORTC ^= set_bit1;
delay_ms(100);
#asm
// Reinicia o contador do watchdog
CLRWDT
#endasm
}
}
// ======================================================================

86/111

Utilizando o Watchdog com Sleep


/*
Programa demonstrando o Watchdog operando
acordando o microcontrolador do sleep
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,WDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
WDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


Utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
// Variveis globais ====================================================
// Int1 =================================================================
// Int8 =================================================================
// Int16 ================================================================
// Int32 ================================================================
// Float ================================================================
// Prottipos de funes ================================================
// Prottipos de interrupes ===========================================
// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido dos portos
TRISC = 0B00000000;

87/111

// Zera os portos
PORTC = 0B00000000;
// Prescaler para WDT, prescaler 1:256
// Bit 07 - RBPU: PORTB Pull-up Enable bit
// Bit 06 - INTEDG: Interrupt Edge Select bit
// Bit 05 - T0CS: TMR0 Clock Source Select bit
// Bit 04 - T0SE: TMR0 Source Edge Select bit
// Bit 03 - PSA: Prescaler Assignment bit
// Bit 02 - \
// Bit 01 - | PS2:PS0: Prescaler Rate Select bits
// Bit 00 - /
OPTION_REG = 0B00001111;
// Lao de repetio
while(true)
{
#asm
// Coloca o microcontrolador para dormir
SLEEP
#endasm
// Pisca todo o PORTC
PORTC = ~PORTC;
}
}
// ======================================================================

88/111

Captulo 16 Mquina de Estados


Exibio de Mensagens com Mquina de Estados
/*
Programa de mensagens utilizando mquina de estados
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Incluso da biblioteca do Teclado
#include<TecladoCusco.c>
// Constantes ===========================================================
#define kFOSC
20000000 // Freqncia do cristal 20Mhz
#define kPRESCALER_TMR1 8
// Prescaler do TMR1
#define kTEMPO_INT_TMR1 10e-3
// Tempo de interrupo do TMR1
// Variveis globais ====================================================
// Int1 =================================================================
int1 i1_Passou10ms
= false; // Notifica passagem de 10ms
int1 i1_Passou1Segundo
= false; // Notifica passagem de um segundo
int1 i1_SinalizandoEvento = false; // Indica se sinalizando um evento
// Int8 =================================================================
int8 i8_TMR1H
= 0; // Mais significativa do TMR1
int8 i8_TMR1L
= 0; // Menos significativa do TMR1
int8 i8_Estado
= 0; // Estado da mquina de estados
int8 i8_Cont1Segundo
= 0; // Conta perodos de um segundo
int8 i8_TimeOutConfirmaCancela = 0; // Timeout da funo confirma/cancela

89/111

// Int16 ================================================================
int16 i16_TMR1
= 0; // Usado para calculo do valor do TMR1
int16 i16_ContSinalizaEvento = 0; // Conta tempo de sinalizao de evento
// Int32 ================================================================
// Float ================================================================
// Prottipos de funes ================================================
#separate
// Programa o tempo de sinalizao do evento em ms
void sinaliza_evento(int16 i16_Tempo);
// ======================================================================
#separate
// Retorna o estado atual ou o de cancelamento e confirmao se
// pressionar as teclas retorna tambm aps o timeout programado
// o estado de timeout
int8 confirma_cancela(int8 i8_EstadoCancela, int8 i8_EstadoAtual,
int8 i8_EstadoConfirma, int8 i8_EstadoTimeout);
// Prottipos de interrupes ===========================================
#int_timer1
// Interrupo de overflow do TMR1
void t1_int(void);
// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido dos portos
TRISA = 0B00000000;
TRISB = 0B00000000;
TRISC = 0B00000000;
TRISD = 0B00000000;
TRISE = 0B00000000;
// Zera
PORTA =
PORTB =
PORTC =
PORTD =
PORTE =
//
//
//
//
//
//
//
//

os portos
0B00000000;
0B00000000;
0B00000000;
0B00000000;
0B00000000;

Prescaler do TMR1 8, clock (FOSC/4), liga o timer


Bit 07 - Unimplemented: Read as 0
Bit 06 - Unimplemented: Read as 0
Bit 05 - \ T1CKPS1:T1CKPS0: Timer1 In. Clock Prescaler Select bits
Bit 04 - /
Bit 03 - T1OSCEN: Timer1 Oscillator Enable Control bit
Bit 02 - T1SYNC: Timer1 Ext. Clock In. Synchronization Control bit
Bit 01 - TMR1CS: Timer1 Clock Source Select bit

90/111

// Bit 00 - TMR1ON: Timer1 On bit


T1CON = 0B00110001;
// Clculo do Tempo de Interrupo do TMR1:
i16_TMR1 = (65536-((kTEMPO_INT_TMR1*kFOSC)/(4*kPRESCALER_TMR1)));
// Guarda os valores de carga do TMR1
i8_TMR1H = (int8)(i16_TMR1 >> 8);
i8_TMR1L = (int8)i16_TMR1;
// Carrega o TMR1
TMR1H = i8_TMR1H;
TMR1L = i8_TMR1L;
// Habilita interrupes, interrupes de perifricos
// Bit 07 - GIE: Global Interrupt Enable bit
// Bit 06 - PEIE: Peripheral Interrupt Enable bit
// Bit 05 - TMR0IE: TMR0 Overflow Interrupt Enable bit
// Bit 04 - INTE: RB0/INT External Interrupt Enable bit
// Bit 03 - RBIE: RB Port Change Interrupt Enable bit
// Bit 02 - TMR0IF: TMR0 Overflow Interrupt Flag bit
// Bit 01 - INTF: RB0/INT External Interrupt Flag bit
// Bit 00 - RBIF: RB Port Change Interrupt Flag bit
INTCON = 0B11000000;
// Habilita interrupo pelo TMR1
// Bit 07 - PSPIE: Parallel Slave Port Read/Write Int. Enable bit(1)
// Bit 06 - ADIE: A/D Converter Interrupt Enable bit
// Bit 05 - RCIE: USART Receive Interrupt Enable bit
// Bit 04 - TXIE: USART Transmit Interrupt Enable bit
// Bit 03 - SSPIE: Synchronous Serial Port Interrupt Enable bit
// Bit 02 - CCP1IE: CCP1 Interrupt Enable bit
// Bit 01 - TMR2IE: TMR2 to PR2 Match Interrupt Enable bit
// Bit 00 - TMR1IE: TMR1 Overflow Interrupt Enable bit
PIE1 = 0B00000001;
// Inicializa o display
inic_display();
// Lao de repetio
while(true)
{
// Se passou 10ms
if(i1_Passou10ms)
{
// Determina que j tratou os eventos de 10ms
i1_Passou10ms = false;
// Se a temporizao no acabou
if(i16_ContSinalizaEvento > 0)
{
// Decrementa temporizador
i16_ContSinalizaEvento--;
}
// Se acabou
else

91/111

{
// Determina que no esta mais sinalizando
i1_SinalizandoEvento = false;
}
}
// Se passou um segundo
if(i1_Passou1Segundo)
{
// Determina que j tratou os eventos de 1 segundo
i1_Passou1Segundo = false;
// Decrementa para timeout confirma/cancela
if(i8_TimeOutConfirmaCancela > 0) i8_TimeOutConfirmaCancela--;
// Togla RC0
PORTC ^= set_bit0;
}
// Se no estiver sinalizando um evento
if(!i1_SinalizandoEvento)
{
// Conforme o estado da mquina de estado
switch(i8_Estado)
{
// Mensagem 1
case 0:
// Mostra a mensagem
end_display(1,1);
mostra(" ESTE PROGRAMA ");
end_display(2,1);
mostra(" VEM DEMONSTRAR ");
// Sinaliza por um tempo
sinaliza_evento(2000);
// Passa para o prximo estado
i8_Estado = 10;
break;
// Mensagem 2
case 10:
// Mostra a mensagem
end_display(1,1);
mostra("A UTILIZACAO DE ");
end_display(2,1);
mostra(" MAQ. DE ESTADOS");
// Sinaliza por um tempo
sinaliza_evento(3000);
// Passa para o prximo estado
i8_Estado = 20;

92/111

break;
// Mensagem 3
case 20:
// Mostra a mensagem
end_display(1,1);
mostra(" ASSOCIADA A ");
end_display(2,1);
mostra("TEMPORIZACAO DE");
// Sinaliza por um tempo
sinaliza_evento(2000);
// Passa para o prximo estado
i8_Estado = 30;
break;
// Mensagem 4
case 30:
// Mostra a mensagem
end_display(1,1);
mostra(" EVENTOS SEM ");
end_display(2,1);
mostra(" USO DE DELAYS! ");
// Sinaliza por um tempo
sinaliza_evento(4000);
// Passa para o prximo estado
i8_Estado = 40;
break;
// Prepara a deciso de confirma/cancela
case 40:
// Notifica a escolha
end_display(1,1);
mostra("DESEJA VER AS ");
end_display(2,1);
mostra("MSGS NOVAMENTE?");
// Programa o timeout de confirma/cancela
i8_TimeOutConfirmaCancela = 15;
// Passa para o prximo estado
i8_Estado = 50;
break;
// Escolhe se deseja ver novamente a mensagem
case 50:

93/111

// Decide se confirma ou cancela


i8_Estado = confirma_cancela(60, 50, 70, 70);
break;
// Decidiu no mostrar a mensagem
case 60:
// Mostra a mensagem
end_display(1,1);
mostra("QUE INCOVENIENTE");
end_display(2,1);
mostra(" POR QUE NAO? ");
// Sinaliza por um tempo
sinaliza_evento(3000);
// Reinicia a mquina
i8_Estado = 0;
break;
// Decidiu mostrar a mensagem novamente
case 70:
// Reinicia a mquina
i8_Estado = 0;
break;
// Estado invlido
default:
// Reinicia a mquina
i8_Estado = 0;
break;
}
}
}
}
// ======================================================================
#separate
// Programa o tempo de sinalizao do evento em ms
void sinaliza_evento(int16 i16_Tempo)
{
// Calcula o nmero de interrupes para o tempo desejado
i16_ContSinalizaEvento = (i16_Tempo / (1000 * kTEMPO_INT_TMR1));
// Determina que est sinalizando evento
i1_SinalizandoEvento = true;
}
// ======================================================================

94/111

#separate
// Retorna o estado atual ou o de cancelamento e confirmao se
// pressionar as teclas retorna tambm aps o timeout programado
// o estado de timeout
int8 confirma_cancela(int8 i8_EstadoCancela, int8 i8_EstadoAtual,
int8 i8_EstadoConfirma, int8 i8_EstadoTimeout)
{
// Se pressiona alguma tecla
if(i1_TeclaNova)
{
// Determina que tratou a tecla apertada
i1_TeclaNova = false;
// Conforme o valor de teclas
switch(i16_Teclas)
{
// Se for confirma
case kCONFIRMA:
// Retorna o estado de confirmao
return(i8_EstadoConfirma);
break;
// Se for cancela
case kCANCELA:
// Retorna o estado de cancelamento
return(i8_EstadoCancela);
break;
}
}
// Se ocorreu timeout
if(i8_TimeOutConfirmaCancela == 0)
{
// retorna o estado de timeout
return(i8_EstadoTimeout);
}
// Se chegou aqui retorna o estado atual
return(i8_EstadoAtual);
}
// ======================================================================
#int_timer1
// Interrupo de overflow do TMR1
void t1_int(void)
{
// Reprograma o timer
TMR1H = i8_TMR1H;
TMR1L = i8_TMR1L;
// Efetua a leitura do teclado
ler_teclas();

95/111

// Notifica que passou 10ms


i1_Passou10ms = true;
// Incrementa contador de segundo
i8_Cont1Segundo++;
// Se passou um segundo
if(i8_Cont1Segundo >= 100)
{
// Reinicia contador
i8_Cont1Segundo = 0;
// Notifica que passou um segundo
i1_Passou1Segundo = true;
}
}
// ======================================================================

Minuteira com Mquina de Estados


/*
Programa de uma minuteira
utilizando mquina de estados e defines
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
#define kFOSC
20000000 // Freqncia do cristal 20Mhz
#define kPRESCALER_TMR1
8
// Prescaler do TMR1
#define kTEMPO_INT_TMR1
10e-3
// Tempo de interrupo do TMR1
#define kTEMPO_TEMPORIZACAO 5
// Temporizao em segundos

96/111

// Estados da mquina de estado =========================================


#define kESPERA_PRESSIONAR_TECLA 0 // Espera pressionar a tecla
#define kLIGA_RELE
1 // Liga o Rel
#define kESPERA_SOLTAR_TECLA
2 // Espera soltar a tecla
#define kPROGRAMA_TEMPO
3 // Programa temporizao
#define kESPERA_TEMPO_ACABAR
4 // Verifica se o tempo passou
#define kDESLIGA_RELE
5 // Desliga Rel
// Variveis globais ====================================================
// Int1 =================================================================
int1 i1_Passou100ms
= false; // Notifica passagem de 100ms
int1 i1_Passou1Segundo = false; // Notifica passagem de um segundo
// Int8 =================================================================
int8 i8_TMR1H
= 0; // Mais significativa do TMR1
int8 i8_TMR1L
= 0; // Menos significativa do TMR1
int8 i8_Estado
= 0; // Estado da mquina de estados
int8 i8_Cont100ms
= 0; // Conta perodos de 100ms
int8 i8_Cont1Segundo = 0; // Conta perodos de um segundo
int8 i8_TempMinuteira = 0; // Temporizador da minuteira
int8 i8_IndDeslPORTC = 0; // Indice do deslocamento do portc
// Int16 ================================================================
int16 i16_TMR1 = 0; // Usado para calculo do valor do TMR1
// Int32 ================================================================
// Float ================================================================
// Array de int8 ========================================================
// Usado para fazer um deslocamento de leds no PORTC
const int8 i8_DeslocaPORTC[3] = {0B01001001,0B10010010,0B00100100};
// Prottipos de funes ================================================
// Prottipos de interrupes ===========================================
#int_timer1
// Interrupo de overflow do TMR1
void t1_int(void);
// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido dos portos
TRISA = 0B00000000;
TRISB = 0B00000000;
TRISC = 0B00000000;
TRISD = 0B00000001;
TRISE = 0B00000000;
// Zera os portos
PORTA = 0B00000000;

97/111

PORTB
PORTC
PORTD
PORTE

=
=
=
=

0B00000000;
0B00000000;
0B00000000;
0B00000000;

// Prescaler do TMR1 8, clock (FOSC/4), liga o timer


// Bit 07 - Unimplemented: Read as 0
// Bit 06 - Unimplemented: Read as 0
// Bit 05 - \ T1CKPS1:T1CKPS0: Timer1 Input Clock Prescale Select bits
// Bit 04 - /
// Bit 03 - T1OSCEN: Timer1 Oscillator Enable Control bit
// Bit 02 - T1SYNC: Timer1 Ext. Clock In. Synchronization Control bit
// Bit 01 - TMR1CS: Timer1 Clock Source Select bit
// Bit 00 - TMR1ON: Timer1 On bit
T1CON = 0B00110001;
// Clculo do Tempo de Interrupo do TMR1:
i16_TMR1 = (65536-((kTEMPO_INT_TMR1*kFOSC)/(4*kPRESCALER_TMR1)));
// Guarda os valores de carga do TMR1
i8_TMR1H = (int8)(i16_TMR1 >> 8);
i8_TMR1L = (int8)i16_TMR1;
// Carrega o TMR1
TMR1H = i8_TMR1H;
TMR1L = i8_TMR1L;
// Habilita interrupes, interrupes de perifricos
// Bit 07 - GIE: Global Interrupt Enable bit
// Bit 06 - PEIE: Peripheral Interrupt Enable bit
// Bit 05 - TMR0IE: TMR0 Overflow Interrupt Enable bit
// Bit 04 - INTE: RB0/INT External Interrupt Enable bit
// Bit 03 - RBIE: RB Port Change Interrupt Enable bit
// Bit 02 - TMR0IF: TMR0 Overflow Interrupt Flag bit
// Bit 01 - INTF: RB0/INT External Interrupt Flag bit
// Bit 00 - RBIF: RB Port Change Interrupt Flag bit
INTCON = 0B11000000;
// Habilita interrupo pelo TMR1
// Bit 07 - PSPIE: Parallel Slave Port Read/Write Int. Enable bit(1)
// Bit 06 - ADIE: A/D Converter Interrupt Enable bit
// Bit 05 - RCIE: USART Receive Interrupt Enable bit
// Bit 04 - TXIE: USART Transmit Interrupt Enable bit
// Bit 03 - SSPIE: Synchronous Serial Port Interrupt Enable bit
// Bit 02 - CCP1IE: CCP1 Interrupt Enable bit
// Bit 01 - TMR2IE: TMR2 to PR2 Match Interrupt Enable bit
// Bit 00 - TMR1IE: TMR1 Overflow Interrupt Enable bit
PIE1 = 0B00000001;
// Inicializa o display
inic_display();
// Lao de repetio
while(true)
{
// Mostra o estado atual da mquina
end_display(1,1);

98/111

printf(mostra,"ESTADO:%u

",i8_Estado);

// Se passou 100ms
if(i1_Passou100ms)
{
// Determina que j tratou os eventos de 100ms
i1_Passou100ms = false;
// Coloca o deslocamento atual do PORTC
PORTC = i8_DeslocaPORTC[i8_IndDeslPORTC];
// Incrementa e avalia o limite do ndice
i8_IndDeslPORTC++;
if(i8_IndDeslPORTC >= 3) i8_IndDeslPORTC = 0;
}
// Se passou um segundo
if(i1_Passou1Segundo)
{
// Determina que j tratou os eventos de 1 segundo
i1_Passou1Segundo = false;
// Se estiver temporizando a minuteira
if(i8_TempMinuteira > 0)
{
// Decrementa um segundo
i8_TempMinuteira--;
}
}
// Conforme o estado
switch(i8_Estado)
{
// Espera pressionar a tecla
case kESPERA_PRESSIONAR_TECLA:
// Se pressionou a tecla
if((PORTD & set_bit0) != 0)
{
// Passa para o prximo estado
i8_Estado = kLIGA_RELE;
}
break;
// Liga o Rel
case kLIGA_RELE:
// Liga Rel
PORTE |= set_bit0;
// Passa para o prximo estado
i8_Estado = kESPERA_SOLTAR_TECLA;
break;

99/111

// Espera soltar a tecla


case kESPERA_SOLTAR_TECLA:
// Se soltou a tecla
if((PORTD & set_bit0) == 0)
{
// Passa para o prximo estado
i8_Estado = kPROGRAMA_TEMPO;
}
break;
// Programa temporizao
case kPROGRAMA_TEMPO:
// Programa o tempo da minuteira (5 segundos)
i8_TempMinuteira = kTEMPO_TEMPORIZACAO;
// Passa para o prximo estado
i8_Estado = kESPERA_TEMPO_ACABAR;
break;
// Verifica se o tempo passou ou pressionou a tecla novamente
case kESPERA_TEMPO_ACABAR:
// Se pressionou a tecla
if((PORTD & set_bit0) != 0)
{
// Passa novamente ao estado aps pressionar a tecla
i8_Estado = kLIGA_RELE;
}
// Seno se passou o tempo da minuteira
else if(i8_TempMinuteira == 0)
{
// Passa para o prximo estado
i8_Estado = kDESLIGA_RELE;
}
break;
// Desliga Rel
case kDESLIGA_RELE:
// Desliga o Rel
PORTE &= clear_bit0;
// Reinicia mquina de estados
i8_Estado = kESPERA_PRESSIONAR_TECLA;
break;
// Estado invlido
default:
// Reinicia mquina de estados
i8_Estado = kESPERA_PRESSIONAR_TECLA;

100/111

break;
}
}
}
//
=======================================================================
#int_timer1
// Interrupo de overflow do TMR1
void t1_int(void)
{
// Reprograma o timer
TMR1H = i8_TMR1H;
TMR1L = i8_TMR1L;
// Incrementa contador de 100ms
i8_Cont100ms++;
// Se passou 100ms
if(i8_Cont100ms >= 10)
{
// Reinicia contador
i8_Cont100ms = 0;
// Notifica que passou 100ms
i1_Passou100ms = true;
// Incrementa contador de segundo
i8_Cont1Segundo++;
// Se passou um segundo
if(i8_Cont1Segundo >= 10)
{
// Reinicia contador
i8_Cont1Segundo = 0;
// Notifica que passou um segundo
i1_Passou1Segundo = true;
}
}
}
// ======================================================================

101/111

Captulo 17 Ponteiros
Exemplo de ponteiros
/*
Programa que demonstra o uso de ponteiros
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
// Variveis globais ====================================================
// Int1 =================================================================
// Int8 =================================================================
// Int16 ================================================================
// Int32 ================================================================
int32 i32_Variavel = 0x44332211; // Varivel exemplo para ponteiros
#locate i32_Variavel=0x47 // Coloca a varivel em um endereo especfico
// Float ================================================================
// Prottipos de funes ================================================
// Prottipos de interrupes ===========================================
// ======================================================================

102/111

// Funo principal do programa


void main()
{
// Configura o sentido dos portos
TRISB = 0B00000000;
TRISC = 0B00000000;
TRISD = 0B11111111;
// Zera
PORTB =
PORTC =
PORTD =

os portos
0B00000000;
0B00000000;
0B00000000;

// Inicializa o display
inic_display();
// Lao de repetio
while(true)
{
// Mostra variveis no display
end_display(1,1);
printf(mostra,"VAR: %lX
",i32_Variavel);
end_display(2,1);
mostra("
");
// Enquanto no pressionar nenhuma tecla
do {} while(PORTD == 0);
// Conforme o valor do PORTD
switch(PORTD)
{
// RD0
case 0B00000001:
// Mostra o endereo da varivel
end_display(1,1);
mostra("ENDERECO DA VAR:");
end_display(2,1);
printf(mostra,"%X
",&i32_Variavel);
break;
// RD1
case 0B00000010:
// Modifica o primeiro byte
*(&i32_Variavel) = 0;
// Notifica o que foi feito
end_display(1,1);
mostra("ZEROU O PRIMEIRO");
end_display(2,1);
mostra("BYTE DA VARIAVEL");
break;

103/111

// RD2
case 0B00000100:
// Modifica o terceiro byte
*(&i32_Variavel + 2) = 0;
// Notifica o que foi feito
end_display(1,1);
mostra("ZEROU O TERCEIRO");
end_display(2,1);
mostra("BYTE DA VARIAVEL");
break;
// RD3
case 0B00001000:
// Mostra o segundo byte da varivel
end_display(1,1);
mostra("SEGUNDO BYTE:
");
end_display(2,1);
printf(mostra,"%X
",*(&i32_Variavel+1));
break;
// RD4
case 0B00010000:
// Mostra o quarto byte da varivel
end_display(1,1);
mostra("QUARTO BYTE:
");
end_display(2,1);
printf(mostra,"%X
",*(&i32_Variavel+3));
break;
}
// Enquanto alguma tecla estiver pressionada
do {} while(PORTD != 0);
}
}
// ======================================================================

104/111

Captulo 18 Structs
Exemplo de Structs
/*
Programa que demonstra a utilizao de structs
*/
// Biblioteca CCS do PIC16F877A
#include<16f877A.h>
// Definio dos Registradores do PIC16F877A
#include<16F877A-sfrs.h>
// Definies de configuraes do PIC
#fuses HS,NOWDT,PUT,PROTECT,NOCPD,NOBROWNOUT,NOLVP
//
//
//
//
//
//
//

HS
NOWDT
PUT
PROTECT
NOCPD
NOBROWNOUT
NOLVP

->
->
->
->
->
->
->

Cristal > 4mhz


No utilizar o Watchdog
Power Up Time
Protege o cdigo contra leitura
Memria EEPROM desprotegida contra leitura
Sem reset por BROWNOUT
Sem programao em baixa tenso

// Define que o clock do PIC 20 Mhz


#use delay(clock=20000000)
// Incluso da biblioteca de LCD
#include<LcdCusco.c>
// Constantes ===========================================================
#define kFOSC
20000000 // Frequencia do cristal 20Mhz
#define kPRESCALER_TMR1 8
// Prescaler do TMR1
#define kTEMPO_INT_TMR1 10e-3
// Tempo de interrupo do TMR1
// Definio de structs =================================================
// Data
typedef struct
{
int8 i8_Dia; // Dia
int8 i8_Mes; // Ms
int16 i16_Ano; // Ano
} data;
// Hora
typedef
{
int8
int8
int8
} hora;

struct
i8_Hora; // Hora
i8_Minuto; // Minuto
i8_Segundo; // Dados da mensagem cam

105/111

// Horrio = (Data + Hora)


typedef struct
{
data sData; // Data
hora sHora; // Hora
} horario;
// Variveis globais ====================================================
// Int1 =================================================================
int1 i1_Pisca500ms = false; // Alterna-se com um perodo de 500ms
int1 i1_Passou1s = false; // Indica que passou 1 segundo
// Int8 =================================================================
int8 i8_TMR1H = 0; // Mais significativa do TMR1
int8 i8_TMR1L = 0; // Menos significativa do TMR1
int8 i8_Contador500ms = 0; // Contador de 500ms
int8 i8_Contador1s = 0; // Contador de 1 segundo
// Int16 ================================================================
int16 i16_TMR1 = 0; // Usado para calculo do valor do TMR1
// Int32 ================================================================
// Structs ==============================================================
horario sHorario; // Horrio atual
// Prottipos de funes ================================================
#separate
// Mostra o horrio no display
void mostra_horario(horario & sHorario);
// ======================================================================
#separate
// Lgica do relgio (deve ser chamada a cada 1 segundo)
void relogio(horario & sHorario);
// Prottipos de interrupes ===========================================
#int_timer1
// Interrupo de overflow do TMR1
void t1_int(void);
// ======================================================================
// Funo principal do programa
void main()
{
// Configura o sentido dos portos
TRISB = 0B00000000;
TRISC = 0B00000000;
TRISD = 0B11111111;
// Zera os portos
PORTB = 0B00000000;

106/111

PORTC = 0B00000000;
PORTD = 0B00000000;
// Prescaler do TMR1 8, clock (FOSC/4), liga o timer
// Bit 07 - Unimplemented: Read as 0
// Bit 06 - Unimplemented: Read as 0
// Bit 05 - \ T1CKPS1:T1CKPS0: Timer1 Input Clock Prescale Select bits
// Bit 04 - /
// Bit 03 - T1OSCEN: Timer1 Oscillator Enable Control bit
// Bit 02 - T1SYNC: Timer1 Ext. Clock In. Synchronization Control bit
// Bit 01 - TMR1CS: Timer1 Clock Source Select bit
// Bit 00 - TMR1ON: Timer1 On bit
T1CON = 0B00110001;
// Clculo do Tempo de Interrupo do TMR1:
i16_TMR1 = (65536-((kTEMPO_INT_TMR1*kFOSC)/(4*kPRESCALER_TMR1)));
// Guarda os valores de carga do TMR1
i8_TMR1H = (int8)(i16_TMR1 >> 8);
i8_TMR1L = (int8)i16_TMR1;
// Carrega o TMR1
TMR1H = i8_TMR1H;
TMR1L = i8_TMR1L;
// Habilita interrupes, interrupes de perifricos
// Bit 07 - GIE: Global Interrupt Enable bit
// Bit 06 - PEIE: Peripheral Interrupt Enable bit
// Bit 05 - TMR0IE: TMR0 Overflow Interrupt Enable bit
// Bit 04 - INTE: RB0/INT External Interrupt Enable bit
// Bit 03 - RBIE: RB Port Change Interrupt Enable bit
// Bit 02 - TMR0IF: TMR0 Overflow Interrupt Flag bit
// Bit 01 - INTF: RB0/INT External Interrupt Flag bit
// Bit 00 - RBIF: RB Port Change Interrupt Flag bit
INTCON = 0B11000000;
// Habilita interrupo pelo TMR1
// Bit 07 - PSPIE: Parallel Slave Port Read/Write Int. Enable bit(1)
// Bit 06 - ADIE: A/D Converter Interrupt Enable bit
// Bit 05 - RCIE: USART Receive Interrupt Enable bit
// Bit 04 - TXIE: USART Transmit Interrupt Enable bit
// Bit 03 - SSPIE: Synchronous Serial Port Interrupt Enable bit
// Bit 02 - CCP1IE: CCP1 Interrupt Enable bit
// Bit 01 - TMR2IE: TMR2 to PR2 Match Interrupt Enable bit
// Bit 00 - TMR1IE: TMR1 Overflow Interrupt Enable bit
PIE1 = 0B00000001;
// "Acerta" o relgio
sHorario.sData.i8_Dia = 29;
sHorario.sData.i8_Mes = 8;
sHorario.sData.i16_Ano = 2009;
sHorario.sHora.i8_Hora = 8;
sHorario.sHora.i8_Minuto = 40;
sHorario.sHora.i8_Segundo = 10;
// Inicializa o display
inic_display();

107/111

// Lao principal
while(true)
{
// Se passou 1 segundo
if(i1_Passou1s)
{
// Tratou eventos de 1 segundo
i1_Passou1s = false;
// Chama a lgica do relgio
relogio(sHorario);
}
// Mostra o horario
mostra_horario(sHorario);
}
}
// ======================================================================
#separate
// Mostra o horrio no display
void mostra_horario(horario &sHorario)
{
// Dia, ms e ano
int8 i8_Dia,i8_Mes;
int16 i16_Ano;
// Pega dia, ms e ano da data
i8_Dia = sHorario.sData.i8_Dia;
i8_Mes = sHorario.sData.i8_Mes;
i16_Ano = sHorario.sData.i16_Ano;
// Mostra a data no display
end_display(1,1);
printf(mostra,"
%02u/%02u/%04lu

",i8_Dia,i8_Mes,i16_Ano);

// Se for para mostrar os :


if(i1_Pisca500ms)
{
// Mostra a hora com os :
end_display(2,1);
printf(mostra,"
%02u:%02u:%02u
",
sHorario.sHora.i8_Hora,
sHorario.sHora.i8_Minuto,
sHorario.sHora.i8_Segundo);
}
// Seno
else
{
// Mostra a hora sem os :
end_display(2,1);
printf(mostra,"
%02u %02u %02u
",
sHorario.sHora.i8_Hora,
sHorario.sHora.i8_Minuto,
sHorario.sHora.i8_Segundo);

108/111

}
}
// ======================================================================
#separate
// Lgica do relgio (deve ser chamada a cada 1 segundo)
void relogio(horario &sHorario)
{
// Usada para calcular o limite do dia
int8 i8_LimiteDia;
// Incrementa o segundo
sHorario.sHora.i8_Segundo++;
// Avalia o limite do segundo
if(sHorario.sHora.i8_Segundo >= 60)
{
// Reinicia a varivel do segundo
sHorario.sHora.i8_Segundo = 0;
// Incrementa a varivel de minuto
sHorario.sHora.i8_Minuto++;
// Avalia o limite de minuto
if(sHorario.sHora.i8_Minuto >= 60)
{
// Reinicia a varivel de minuto
sHorario.sHora.i8_Minuto = 0;
// Incrementa a varivel de hora
sHorario.sHora.i8_Hora++;
// Avalia o limite de hora
if(sHorario.sHora.i8_Hora >= 24)
{
// Reinicia a varivel de hora
sHorario.sHora.i8_Hora = 0;
// Incrementa a varivel de dia
sHorario.sData.i8_Dia++;
// Se o ms for fevereiro
if(sHorario.sData.i8_Mes == 2)
{
// Se o ano for bissexto
if((sHorario.sData.i16_Ano % 4) == 0)
{
// Limite do dia 29
i8_LimiteDia = 29;
}
// Seno
else
{
// Limite do dia 28
i8_LimiteDia = 28;
}

109/111

}
// Seno se o ms for de 30 dias
else if((sHorario.sData.i8_Mes ==
(sHorario.sData.i8_Mes ==
(sHorario.sData.i8_Mes ==
(sHorario.sData.i8_Mes ==
{
// Limite do dia 30
i8_LimiteDia = 30;
}
// Seno o ms de 31 dias
else
{
// Limite do dia 31
i8_LimiteDia = 31;
}

4) ||
6) ||
9) ||
11))

// Avalia o limite de dia


if(sHorario.sData.i8_Dia > i8_LimiteDia)
{
// Reinicia a varivel de dia
sHorario.sData.i8_Dia = 1;
// Incrementa a varivel de ms
sHorario.sData.i8_Mes++;
// Avalia o limite de ms
if(sHorario.sData.i8_Mes > 12)
{
// Reinicia a varivel de ms
sHorario.sData.i8_Mes = 1;
// Incrementa a varivel de ano
sHorario.sData.i16_Ano++;
}
}
}
}
}
}
// ======================================================================
#int_timer1
// Interrupo de overflow do TMR1
void t1_int(void)
{
// Reprograma o timer
TMR1H = i8_TMR1H;
TMR1L = i8_TMR1L;
// Incrementa contador de 500ms
i8_Contador500ms++;
// Se passou 500ms
if(i8_Contador500ms >= 50)

110/111

{
// Reinicia contador
i8_Contador500ms = 0;
// Pisca 500ms
i1_Pisca500ms = ~i1_Pisca500ms;
// Incrementa contador de 1s
i8_Contador1s++;
// Se passou 1s
if(i8_Contador1s >= 2)
{
// Reinicia contador
i8_Contador1s = 0;
// Indica que passou 1 segundo
i1_Passou1s = true;
}
}
}
// ======================================================================

111/111

Você também pode gostar