Você está na página 1de 261

Histórico e utilização dos microcontroladores

1.0 A evolução da Tecnologia


1.1 Os primeiros computadores
1.2 Evolução dos sistemas eletrônicos
1.3 Onde é possível encontrar microcontroladores

Introdução aos sistemas microcontrolados e


microprocessados
1.0 Microprocessadores e Microcontroladores:
1.1 Microprocessador:
1.2 Microcontrolador:
1.3 Funcionamento básico dos sistemas microcontrolados:
1.4 O que é necessário para trabalharmos com microcontroladores
1.5 O Mercado de Microcontroladores

A evolução da tecnologia:

Desde os primórdios da sociedade, os homens buscavam maneiras de facilitar e simplificar atividades


complicadas e repetitivas; com o conhecimento matemático adquirido com o passar dos anos, e seu grande
uso nas atividades comerciais um dos maiores problemas encontrados passou a ser a realização de cálculos
mais complexos, o que levou a criação de dispositivos mecânicos para auxílio nesta tarefa, os quais são
chamados genericamente de ábaco.

Pode não parecer, mas com a criação destes


dispositivos tão simples, nascia um conceito de máquina
que muitos anos e séculos depois daria origem a sistemas
muito complexos como os microprocessadores e
microcontroladores.

figura 1.0 - Ábaco


Apartir de então essas ferramentas foram sendo aperfeiçoadas continuamente durante séculos, originando
diversos dispositivos e ferramentas que auxiliavam em tarefas complexas, principalmente nos cáculos
matemáticos.

figura 1.1 – Calculadora de Pascal


Figura 1.2 – Maquina Diferencial de Babbage

Os primeiros computadores:
Com domínio da manipulação da
tensão e da corrente elétrica, os dipositivos de
cálculo anteriormente mecânicos passaram a ser
elétricos, o que facilitava o projeto, a construção e
reduzia o tamanho dos sistemas, mesmo assim
os primeiros computadores eram gigantescos e
ocupavam prédios inteiros devido aos
componentes utilizados na época (vávulas). Um
bom exemplo era o ENIAC utilizado para
realização de cálculos balísticos na década de 40.

figura 2.0 – ENIAC


Observando todos esses equipamentos, embora eles sejam muito diferentes é possível notar um ponto
em comum, todos eles foram projetados com uma finalidade específica e única, caso se quisesse que eles
realizassem outra tarefa, todo o projeto teria que ser refeito. Essa era uma característica muito ruim, pois
tornava extremamente difícil qualquer alteração ou atualização que fosse necessária.
É neste contexto que as idéias e máquinas do Sr.
Herman Hollerith, um dos precursores da IBM, começam a ter
espaço e se tornam uma solução para o problema apresentado,
pois ele havia desenvolvido, já no final do século XIX, um sistema
de cartões e fitas perfuradas capazes de informar á maquinas
específicas, o que elas deveriam fazer, nasciam os conceitos de
programa e de memória, tornando o sistema muito mais versátil,
uma vez que era posível com uma mesma máquina a realização
de diversas tarefas e cálculos diferentes, unicamente com a
mudaça dos cartões ou da fita.

figura 2.1 – Fita perfurada

Evolução dos sistemas eletrônicos:

Conforme as tecnologias para produção de componentes eletrônicos avançavam, os computadores


eletrônicos tornavam-se cada vez mais compactos e poderosos, já na década de 70 esse avanço chegou a
tal ponto que foi possível, a partir da idéia do o Dr. Marcian Hoff, um dos primeiros funcionários da Intel, a
criação do primeiro dispositivo eletrônico capaz de ser programado para a realização de diversas tarefas e
controles, nascia ali o primeiro microprocessador, componente que revolucionaria a história da eletrônica
devido sua versatilidade, rapidez e precisão, podendo controlar qualquer tipo de sistema ou processo.
Os microcontroladores que são o foco de nosso estudo são frutos de toda essa evolução e hoje
com o barateamento dessa tecnologia e as facilidades de projeto e utilização, eles são empregados em
praticamente todas as áreas com as mais diferentes aplicações e funcionalidades.
Onde é possível encontrar microcontroladores?

· Telefonia
· Atomóveis
· Aviação
· Diversão
· Eletrodomésticos
· Calculadoras
· Máquinas
· Informática
· Sistemas bancários
· Segurança
· Celulares
· Sist. Hospitalares
etc..

Microprocessadores e Microcontroladores:

Vimos até agora que microprocessadores e microcontroladores são o fruto de muitos anos de
avanço técnico, são utilizados para controlar diversos tipos de sistemas e máquinas, e que a grande
vantagem de seu uso é a possibilidade de alteração e atualização rápida e simples, bastando para isso a
criação de um novo programa.
Mas quais as diferenças entre microprocessadores e microcontroladores, em que casos eles são
utilizados?
Embora muitos confundam as duas nomenclaturas e as utilizem até como sinônimos, eles são
dispositivos com conceitos e funções distintas, pelos seguintes aspéctos:
Microprocessador:

Dispositivo eletrônico destinado a controle de processamento de sistemas grandes e complexos,


ele tem como função processar os dados vindos de dispositivos externos (memórias, sistema de I/O, etc) e
apartir deles executar tarefas pré-programadas.
Dentro do encapsulamento do microprocessador existe apenas uma estrutura a CPU, a qual é
composta basicamente pela ULA, pela UC e alguns registradores.

CPU: Unidade Central de Processamento


ULA: Unidade Lógica e Aritmética (responsável pelas operações matemáticas e lógicas)
UC: Unidade de controle (responsável pelo controle de fluxo dos dados, pelo gerenciamento e
execução dos comandos e atividades)

Microcontrolador:

Os microcontroladores por sua vez têm como alvo o controle autônomo de sistemas, dessa forma
geramlente procura-se que eles sejam baratos e pequenos, para atingir esse objetivo, é inserido no mesmo
encapsulamento, além da CPU, diversos Periféricos Internos, isso reduz muito o espaço físico na PCI (placa
de circuito impresso), barateando o projeto e tornando atualizações e modificações um processo muito mais
simples e rápido; hoje dependendo da aplicação é possível se escolher o microcontrolador mais adequado
observando a lista de periféricos internos que ele possui.
Periféricos Internos: São sub-circuitos presentes na mesma pastilha de silício da CPU, hoje podemos
encontrar incorporado aos microcontroladores uma grande lista de periféricos internos:

Funcionamento básico dos sistemas microcontrolados:

Os microcontroladores e microprocessadores embora sejam considerados por muitos como


componentes “inteligentes”, não possuem intelecto algum, por si só não são capazes de executar nenhum
tipo de tarefa, ficando a cargo do programador informar a esses dispositivos tudo que eles precisam saber
para realizarem o trabalho, assim se o programador informou algum dos procedimentos de forma errada o
microcontrolador executará a tarefa erroneamente.
Por outro lado eles são componentes extremamente rápidos e precisos, então quando ordenarmos
que um microcontrolador faça algo, poderemos ter certeza que ele executará exatamente como pedimos e
num intevalo de tempo curtíssimo.
Outro apecto importantíssimo e que deve ficar bem claro de agora em diante é que os
microcontroladores são máquinas “seqüênciais”, ou seja executam as funções uma a uma, de forma seguida
e contínua, assim a metodologia para trabalhar e programar microcontroladores deve ser a seguinte:

1º Estudar muito bem a tarefa a ser realizada pelo microcontrolador


2º Dividir a tarefa em partes ou atividades
3º Ordenar corretamente cada uma das atividades
4º Agora sim, podemos informar ao microcontrolador o que fazer

Vamos enteder melhor com um exemplo simples:

Vamos pensar em uma tarefa bem simples e apresentar cada um dos passos que devem ser
executadas para a realização da mesma.

Tarefa: Fazer um copo de suco de laranja

Materiais necessários:

três laranjas
faca
espremedor
coador
copo
colher
açúcar

Sequência de procedimentos:

1º Pegar todos os materiais necessários


2º Utilizar a faca e cortar as três laranjas
3º Utilizar o espredor para retirar o suco das laranjas
4º Utilizar o coador para coar o suco
5º Colocar o suco no copo
5º Acrescentar açúcar a gosto e misturar com a colher
6º Acrescentar gelo a gosto

Com esse simples exemplo foi possível verificar que mesmo as atividades mais simples passam por
várias etapas até sua conclusão, e quando estivermos criando nossos programas para os microcontroladores
deveremos pensar em todas as etapas da tarefa. Um bom modo para essa análise é a criação de um
fluxograma, mas isso discutiremos um pouco mais para frente.

Porém existe um detalhe importantíssimo que não levamos em consideração até agora: que idioma
ou linguagem usaremos para transmitir as informações da tarefa ao microcontrolador?

Será o português? Inglês? Japonês? Javanês? Esperanto?

Na verdade cada família de microcontroladores possui um idioma próprio chamado genericamente


de linguagem Assembly, esta é criada pelo fabricante e conversa diretamente com a máquina.
Mas existe um outro detalhe técnico, a linguagem Assembly na verdade é apenas uma
representação gráfica, constituída por pequenas palavras chamadas MNEMÔNICOS, que são como
“apelidos” para os códigos binários ou hexadecimais, os quais são a única informação que os
microcontroladores ou qualquer dispositivo eletrônico realmente entende, aí é que entra um novo
personagem o COMPILADOR.

Linguagem Assembly: Conjunto de códigos (mnemônicos) que representam o conjunto de instruções


interpretadas por um microcontrolador

MNEMÔNICOS: Pequenas palavras, geralmente baseadas no idioma inglês, que representam cada uma da
instruções ou códigos da linguagem Assembly
Compilador: É um software que pode ser considerado como um tradutor, ele pega os mnemônicos da
linguagem e os transforma nos respectivos códigos de máquina (hexadecimal ou binário)
Após a compilação de nosso programa, o arquivo gerado (binário ou hexadecimal) é gravado dentro do
microcontrolador, e sendo assim o mesmo já pode começar a executar os comandos do programa passoa a
passo.
Curiosidade:

Onde o microcontrolador armazena os passos da tarefa a serem executados?


Os microcontroladores possuem uma memória que chamamos de “memória de programa”, ela é do tipo não
volátil, ou seja, não perde as informações quando a desenergizamos, assim o microcontrolador estará,
sempre que for ligado, apto a executar a tarefa desejada!!!

Assembly e Assembler são as mesmas coisas?


Não, Assembly é a designação da linguagem, já Assembler (montador em inglês) é um sinônimo de
compilador, portanto não confunda mais!!!

O que é necessário para trabalharmos com microcontroladores?

Para trabalhar com microcontroladores são necessários as seguintes ferramentas:

Editor e compilador (IDE): Software onde escreveremos o programa e irá traduzir os comandos da
linguagem (assembly, C, BASIC, PAscal, etc) para o código de máquinas (hexadecimal ou binário)
Software para simulação e teste da lógica do programa
Hardware de gravação do microcontrolador ou Kit de desenvolvimento

O Mercado de Microcontroladores

Existe uma grande disponibilidade de fabricantes de microcontroladores, onde cada um desenvolve seus
componentes com determinadas características. Entretando, caso venhamos compará-los, descobriremos
que sua estrutura básica interna de funcionamento são semelhantes. Quando projetamos determinado
circuito com microcontrolador, devemos selecionar o modelo de microcontrolador mais adequado, levando em
consideração, as facilidades de compra, obtenção continua deste chip e multiplicidade de fornecedores para
projetos industriais.

Os principais fabricantes de microcontroladores disponíveis no mercado são descritos a seguir:

Atmel: - Família AVR, 8051


Microchip - Família PIC
Freescale (Motorola) - Família HS908
NXP (Phlips) - Família ARM (LPC)
Texas Instruments - Família MSP, TMS e C2000
Zilog - Família F8
National - Família COP
Cypress - Família PSOC
Intel - Família 8051, 8052, 8096
Analog Device - Família ADuc

... etc.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral
desse material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Introdução a programação
1.0 Um pouco de História
1.1 Escrevendo programas em C para Microcontroladores
1.2 O papel do Compilador
1.3 Etapas de desenvolvimento e gravação de um programa em C no microcontrolador PIC

Introdução a programação
1. 0 Kit PICgenios I
1.1 Controle de display LCD
1.2 Varredura de displays de 7 segmentos
1.3 Varredura de teclado matricial
1.4 Acionamento de leds
1.5 canal de comunicação serial RS232 e RS485
1.6 Conversores A/D
1.7 Canal PWM

Um pouco de História

A liguagem C foi criada por Dennis Ritchie e Ken Thompson no Laboratório Bell em 1972. C é uma linguagem profissional e é
aceita e utilizada na criação de sistemas operacionais, tais como Unix, Windows e Linux.
A linguagem de programação C tornou-se rapidamente uma das mais importantes e populares, principalmente por ser muito
poderosa, portátil e flexível. Essa flexibilidade tem seus incovenientes de permitir que se criem programas desorganizados e de
difícil compreensão. É uma linguagem de programação genérica desenvolvida para ser tão eficiente e rápido quanto o assembly
e tão estruturada e lógica quanto as linguagens de alto nível (PASCAL, JAVA, etc).
C foi desenhada para que usuários possa planejar programas mais estruturados e modulares. O resultado é um programa mais
legível e documentado. Os programas em C tendem a ser bastante compactos e de execução rápida.
A linguagem C habitualmente pe denominada general purpose, pois sua aplicação não se destina a uma área específica,
diferente de outras linguagem como COBOL, PASCAL, FORTRAN, entre outras, e esta é uma das grandes vantagens, pois
adapta-se ao desenvolvimento de qualquer projeto. A linguagem C é utilizada para construção de compiladores de outras
linguagens.
Vamos descrever algums pontos importantes com relação as características do C:

Rapidez - possui performance semelhante as obtidas pelo assembly, pois permite acesso a memória e manipulações de bits,
utilizando linguagem de alto nível.
Simples - possui sintaxe simples, e números de comandos reduzidos, facilitando o aprendizado da linguagem.
Portável - a linguagem C é padronizada (ANSI) no qual define padrões para os compiladores. Podemos transportar nossos
programas para outras plataformas e compila-los sem alteração no código.
Popular - internacionalmente conhecida e amplamente utilizada por programadores.
Modular - permite a separação do código em módulos distintos e independentes, podendo estes serem utilizados em varíos
programas.
Alto Nível - o C é considerado linguagem de alto nível, quando comparada com a linguage Assembly.
Bibliotecas Poderosas - possui diversas bibliotecas e permite a criação de novas pelo programador.
Evolução - o C deu origem a novas linguagens de programação, como por exemplo o C++.

Escrevendo programas em C para Microcontroladores

Devido a sua qualidade, portabilidade, eficiência e controle , o C, podemos dizer, é a linguagem mais utilizada por
programadores de microcontroladores. Atualmente, a maioria dos microcontroladores existentes no mercado contam com
compiladores de linguagem C para o desenvolvimento de programas.
Quanto estamos tratando de programas para microcontroladores, devemos tomar certos cuidados com relação a escolha da
linguagem de programação e do compilador a ser utilizada , pois a capacidade de memória de armazenamento do programa é
extremamente reduzida, comparando com PC.

Programa escrito em linguagem C


Sabemos que hoje temos computadores portáteis com capacidades de centenas de gigabytes de memória, nesses aspectos o
"tamanho" do código não é tão importante para o programador. Agora, quando estamos falando de microcontroladores devemos
tomar certas preocausões, pois microcontroladores como: PIC12C508 e PIC16C54 possuem apenas 512byte de memória de
programa e 25 byte de RAM, fato que exige do programador otimização do código e eficiência na elaboração lógico do
programa.

O papel do Compilador
A única maneira de se comunicar com o microcontrolador é através da linguagem de máquina, ou melhor dizendo, através de
códigos de máquinas. Por tanto os programas em C devem necessariamente serem interpretados e compilados a fim de termos
como resultado os comandos de máquinas a serem gravados na memória de programa do microcontrolador.
Existem no mercado diversos compiladores de programas em C para microcontroladores PIC, tais como: HI-TECH PICC, C18,
C30, CCS, SDCC, WIZ-C, mikroC, CC5, PICmicro C, entre outros.
Em nosso curso iremos utilizar a IDE MikroC desenvolvido pela empresa Mikroelektronika (www.mikroe.com), no qual permite
editar, compilar e simular programas em C para microcontroladores PIC da familia 12, 16 e 18.

IDE mikroC - Editor, compilador, simulador e debugador para PIC em linguagem C

Iremos utilizar o mikroc devido a sua eficiência e flexibilidade. Além disso, este compilador possui uma extensa biblioteca de
funções prontas para controle de diversas periféricos conectados ao nosso microcontrolador PIC.
Aos escrevermos e compilarmos um programa em nosso compilador, caso o programa não tenha erros de sintaxe, ou algum
outro erro cometido pelo programador, teremos como resultado a criação do arquivo de máquina hexadecimal (extensão .hex).
Este arquivo .hex é conhecido como código de máquina, e será este o arquivo a ser gravado na memória do microcontrolador.

Etapas de desenvolvimento e gravação de um programa em C no microcontrolador PIC

Descreveremos abaixo as etapas passo a passo para editar, compilar, simular e gravar um programa no microcontrolador PIC.

1º Fase: A elaboração do Fluxograma.


Estude as tarefas e funções que o microcontrolador PIC deverá executar. Para melhor entendimento das funções lógicas a ser
executadas faça um fluxograma.

2º Fase: Edição e compilação do programa em C


A partir das informações e funções elaborada no fluxograma, escreva seu programa em linguagem C na IDE mikroC, compile e
simule seu programa (estudaremos detalhadamente cada função e ferramenta do mikroC mais adiante).
3º Fase: O programa compilado
Após a compilação do seu programa em C, o compilador criará o arquivo de máquina .hex
(nomedoprograma.hex). Este é o arquivo que deverá ser gravado no microcontrolador PIC.

4º Fase: Gravação do programa no PIC


Gravar o arquivo compilado .hex no microcontrolador através de um gravador de microcontroladores PIC,
ou através de um Kit de desenvolvimento.

Estudaremos com mais detalhes nas unidades seguintes do nosso curso os processos de edição,
compilação e gravação de programas.

Durante nosso curso estudaremos basicamente 4 assuntos:


O microcontrolador PIC - PIC18F452 / 4520
A IDE mikroC - editor, compilador e simulador para PIC 12, 16 e 18 em linguagem C.
Estudo da Linguagem C para programação de microcontroladores PIC
Projetos e aplicações práticas com PIC18F

Descreveremos agora algumas dos exemplos de programas e aplicações práticas que serão estudados em nosso curso:

Kit PICgenios PIC18F - Módulo Profissional :

Através deste Kit podemos desenvolver os mais variados tipos de programas, tais como:

Controle de displays LCD alfanumérico 16X2 (16 colunas por 2 linhas ) no modo 4 e 8 bits. Os displays LCD são
utilizados nas grandes maioria dos projetos eletrônicos hoje em dia.
4 displays de 7 segmentos acionados por varredura.
Matriz de teclado com 12 teclas.
7 teclas de acesso direto ao pino, sendo que 3 teclas dessas simulam a interrupções externa INT0, INT1 e INT2 do PIC.
16 leds para controle lógico visual.
2 relés NA/NF para acionamento de cargas externas de 10A / 220V.
RTC - relógio de tempo real com bateria. Através desse relógio o programador poderá programar temporizadores,
acionamentos programados, calendarios, entre outras aplicações.
Canal Serial RS232: canal para comunicação serial com PC ou outras máquinas
Canal Serial RS232 Emulada: o programador pode emular uma serial via software e então trabalhar com duas seriais
RS232 no seu projeto.
Canal USB 2.0 para implementações em projetos que necessitem comunicação USB (necessário usar PIC18F4550)
Canal PS/2: permite ligar ao microcontrolador teclado de PC ou mouse para otimizar o projeto eletrônico.
Aquecedor via PWM. O aluno poderá controlar o canal PWM do PIC simulando na placa.
Sensor de temperatura LM35: o aluno poderá realizar ensaios práticos com este sensor de temperatura.
Acionamento de Buzzer. Em alguns projetos é indispensável a utilização de um alarme sonoro.
Acesso a todas as portas de expansão do microcontrolador PIC, que nos permite ligar outros dispositivos externos ao Kit.

Ventoinha acionada por PWM. É importante pois o aluno poderá controlar por PWM a velocidade de giro da ventoinha.
Contadores de Pulsos. Através desse circuito poderemos saber a velocidade de giro da ventoinha. (contador RPM).
Memória serial E2PROM via I2C 24C04. Este tipo de memória são muito utilizada em diversos equipamento e máquinas.
2 trimpots para simulação e programação do canal A/D do PIC (Analógico 1 , e Analógico 2).
Canal de comunicação RS485: Protocolo muito utilizado em redes industriais e em PLC's.
Chave Load/Run para gravação ISP (gravação no próprio circuito).
Microcontrolador PIC18F452 DIP com 32Kbyte de Flash;
Canal de gravação ICSP: Conector para modo debugger e ICD2.
Regulador de tensão.
Chave Dip seletora de funções.
Suporte para displays LCD Gráficos 128 x 64 controlador KS107/108.
Suporte para displays LCD Gráficos 128 x 64 controlador T6963C.
Suporta os PIC18F4xxxx, PIC18F2XX0 e os PIC16F873/876 de 28 pinos.

Sei que você deve estar pensando, nossa quanto dispositivo conectado ao microcontrolador. É verdade, mas esses
microcontroladores podem realizar muitas outras funções... em outras palavras, esse PIC é 10!

Durante nosso treinamento iremos realizar diversas experiências práticas utilizando os recursos da linguagem C junto com
as funções da IDE mikroC.

Vamos detalhar um pouco cada experiência e conhecer um pouco mais sobre o que vamos estudar daqui por diante.

Aulas de laboratório e experiências práticas

Veremos abaixo algumas das aplicações práticas que iremos realizar durante nosso treinamento:

Controle de display LCD

Iremos no decorer das unidades programar o PIC para controlar e escrever mensagens publicitária nos display LCD 16X2
alfanumérico:

Os displays LCD são amplamente utilizados em diversos equipamentos e aparelhos. No decorrer dos estudos iremos explorar
as funções do mikroC em linguagem C para controle de displays LCD. Estudaremos passo a passo como escrever mensagens
de textos nos modos 4 e 8 bits.

Aplicações com displays LCD:

Equipamentos domésticos
Equipamentos industriais Equipamentos de
Informática CLPs e controladores Equipamentos portáteis
industriais

Varredura de displays de 7 segmentos

Os diplays de 7 segmentos são largamente utilizados em equipamentos como: balança, painéis de máquinas industriais,
equipamentos médicos, eletrodomésticos entre outros. Podemos controlar os displays de 7 segmentos através de conversores
BCD, como por exemplo o 74HC247 (decodificador BCD) ou desenvolver códigos BCD pelo microcontrolar. Em nosso caso, os
displays estão sendo acionados por varredura.
Para acionar os displays de 7 segmentos, iremos utilizar o sistema de varredura, que permite através de um barramento de
dados de 8 bits e mais 4 pinos de acionamento, "escrever" o valor correspondente ao dado que deve ser mostrado no visor.

Aplicações reais com displays de 7 segmentos:

Panéis de equipamentos industriais


Balanças

Varredura de Teclado matriciais

O sistema de varredura de teclado matricial permite que o microcontrolador leia muitas teclas ligada ao seus pinos. O teclado
matricial é muito utilizado para economizar pinos físicos do microcontrolador. Equipamentos de diversos tipos usam o teclado
matricial para inserir dados ao microcontrolador.
Aplicações reais com teclados matriciais:

Equipamentos Eletrodomésticos Aparelhos de som


CLPs

Acionamento de Leds

Os leds são utilizados praticamente em quase todas as aplicações eletrônicas. Através dos leds podemos visualizar o status de
uma máquina, "desenhar" mensagens de textos, iluminar objetos, criar animações visuais, entre outras aplicações.
Iremos estudar os recursos de programação em C para controle das portas de saída disponíveis no microcontroladores PIC
utilizando os leds como barramento de dados visual.

Aplicações reais com Leds

Os leds são utilizados em diversos equipamento no mercado para as mais variadas aplicações. Muitas das aplicações é o
microcontrolador responsável pelo controle desses leds.

Letreiros Controle de Iluminação de painéis


letreiros com leds

Canal de comunicação serial RS232 e RS485

É de grande importância que os equipamentos eletrônicos tenham um canal de comunicação com o meio externo. Através de
um canal de comunicação é possível monitorar o estado do equipamento assim como enviar e receber informações em tempo
real. Iremos aprender a utilizar o canal de comunicação serial do microcontrolador PIC realizando comunicações de envio e
recepção de dados com o PC.
Aplicações reais com Comunicação Serial RS232 e RS485

Supervisórios Industriais
Comunicação com computadores PC
Comunicação com outras máquinas

Conversor Analógico digital (A/D)

A aplicação básica do microcontrolador PIC trabalhando com o conversor A/D abaixo é simples, mas possui um grande
conteúdo educativo para nós neste momento. No exemplo abaixo utilizamos dois simples trimpots para variarmos o valor de
tensão no pino A/D do PIC. Este exemplo na verdade representa inumeras aplicações práticas de equipamentos do mercado,
tais como: aparelhos de medição, leitores de sensores de temperatura, atuadores, entre outros. Criaremos programas para
controle e leitores de tensão nas unidades seguintes.

Veremos também como ler e interpretar valores analógicos vindo de sensores de temperatua (LM35) utilizando os recurso da
linguagem C.
Aplicações reais com os conversores A/D do PIC

Sensores de proximidade Sondas e termopares


equipamentos de medição

Controle PWM de dispositivos

Iremos simular programas de controle de largura de pulsos. Através do canal PWM disponível no PIC,
podemos controlar diversos equipamentos, tais como: inversores de frequência, estabilizadores, fonte
chaveada, controle de velocidade de motores DC, entre outras.
Nem nossos laboratório, iremos controlar a velocidade de giro de uma ventoinha e controlar o
aquecimento de uma caraga resistiva via PWM.

Aplicações reais com os conversores A/D do PIC


inversores de frequência
Fontes chaveadas Drive de Motores

Informações importantes

Outros exemplos de programas também serão tratados e comentados em nosso curso, mas ficarão como
sendo material complementar ao aluno.
Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem
prévio aviso. Maiores informações: suporte@microgenios.com.br
As características gerais da família PIC
1.0 A família PIC
1.1 Desempenho da família PIC
1.2 Tipo de memórias de programa

A família PIC

Os microcontroladores PIC são fabricados pela empresa Microchip. Existem


basicamente três familias de PICs diferenciadas pelo tamanho da palavra de
memória de programa: 12, 14 e 16 bits. Todos estes dispositivos possuem
internamente um barramento de 8 bits. Atualmente a Microchip lançou
recentemente uma nova família de microcontroladores chamada DsPIC que
possui barramento interno de 16 bits e 24 bits, contrário da tradicional família
de 8 bits.

O PIC possui uma arquitetura interna do tipo Havard. A diferença entre essa
arquitetura e as tradicionais, do tipo Von-Neumann, é que ela possui um
barramento para o programa e outro para os dados, diferente da arquitetura
tradicional em que um barramento é tanto de dados como de endereço.

O aumento no tamanho da palavra de programa possibilita um aumento no


número de instruções: os PICs de 12 bits (12C508, 12C509, 12CE518, 16C54,
16C55) possuem apenas 33 instruções, os de 14 bits (12C671, 12C672,
12CE673, 12C674, 14000, 16C55x) possuem 35 instruções e os de 16 bits
(17C4x, 17C75X, 17C76X, 18C2XX, 18C4XX) possuem 77 instruções.

Os PICs foram otimizados para trabalharem com execução de pequeno


conjunto de instruções a grandes velocidades de processamento. Podemos
trabalhar com alguns microcontroladores PIC a 10MIPS ( Milhões de
Instruções por segundo) com um pic da série 16 ou 18 trabalhando com
cristal de 40Mhz.

Desempenho da família PIC


Capacidade de pepilene (enquanto executa uma instrução, o
processador busca a próxima instrução na memória, de forma a acelerar
a execução do programa)
Execução de uma instrução por ciclo de máquina, com exceção das
instruções de desvios que consomem dois ciclos de máquinas para
serem executadas.

Um ciclo de máquina no PIC equivale a 4 ciclos de clock, ou seja, o sinal


de clock é dividido por 4 antes de executar a instrução (falaremos mais
sobre esse tópico adiante).

Cada Instrução ocupa uma posição de memória de programa (FLASH).

Tempo de execução das instruções fixa, com exceção das instruções de


desvios que consomem dois ciclos de máquina.

Outra característica importante da arquitetura PIC reside na semelhança e


compatibilidade entre os diversos microcontroladores membros de sua família.
Isto facilita grandemente a migração de microcontrolador para outro,
bastando mudar, em alguns casos, apenas alguns comandos no programa,
pois partes dos registradores internos não se diferem muito entre si.

Tipos de Memória de Programa


Os PICs da série 12, 14 e 16 armazenam o programa em sua memória
interna. Membros da família 17 e 18 podem funcionar com memória de
programa externas.

Tipos de memórias de programas disponíveis nos PICs:

ROM: Memória do tipo não volátil gravadas na fábrica pelo processo


conhecido como máscara. Os chips com esse tipo de memória normalmente
possuem custos menores mas somente são viáveis na fabricação de grandes
quantidades.

OTP: Memória fabricadas do tipo PROM. Saem da fábrica "virgens" e


permitem uma única gravação. São inviáveis nas fases de implantação e
desenvolvimento de equipamentos. Esses chips são identificados pelo sufixo
"C".

EEPROM: Podemos encontrar chips com memória do tipo EEPROM.


Normalmente são mais caros que os dispositivos ROM e OTP e podem ser
identificados através do seu sufixo "JW" para os dispositivos com
encapsulamento DIP, ou "CL" para os dispositivos com encapsulamento do
tipo PLCC.

FLASH: Os microcontroladores PIC que utilizam este tipo de memória são


indicados para etapas de desenvolvimento e testes até mesmo para
implantações finais. Permitem no máximo 1000 ciclos de
gravações/apagamento, possem um custo relativamente mediano com relação
aos outros chips.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse
material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Conhecendo o Hardware do PIC18F452
1.0 O PIC18F452
1.1 Tipos de Capsulamentos
1.2 Estrutura Interna do PIC18F452
1.3 Descrição das funções dos pinos do PIC18F452

Memória do PIC18F452
1.0 Tipos de Memórias
1.1 Memória de Programa
1.2 Memória de dados - EEPROM
1.3 Memória de dados - RAM
Osciladores
1.0 O clock
1.1 Osciladores: Cristal e Ressonador
1.2 Cristal de Oscilação RC
1.3 Modo HSPLL
1.4 Ciclos de Máquina
1.5 Reset
Bits configurations
1.0 Os bits de configuração
1.1 FOSC (frequêncy Oscilator)
1.2 OSCSEN (SYSTEM CLOCK SWITCH BIT)
1.3 PWRTEN (SYSTEM CLOCK SWITCH BIT)
1.4 BROWN-Out
1.5 BOREN
1.7 WDTEN - Watchdog Timer Enable (cão de guarda)
1.8 Watchdog Postscale Select Bit
1.9 CCP2MX
2.0 BKBUG (debug)
2.1 LVP (Low Voltagem Programming)
2.2 STVREN (Stack Full/Underflow Reset Enable Bit)

O PIC18F452
O PIC18F452 é um microcontrolador que possui memória do tipo FLASH, que nos representa uma grande facilidade em
desenvolvimentos de projetos e protótipos pois não requer apagá-lo através de luz-ultravioleta como as versões antigas que utilizavam
EEPROM.

O PIC18F452 é um microcontrolador com tecnologia CMOS (baixíssimo consumo) fabricado pela empresa Microchip Technology.

Veja em seguida as principais características do PIC18F452

· microcontrolador de 40 pinos;
· memória de programa FLASH de 32K ( 16384 bytes words)
· memória de dados RAM de 1536 bytes;
· memória EEPROM de 256 byte;
· Processamento de até 10MIPS (milhões de instruções por sergundo)
· quatro temporizadores internos (um de 8 bits e 3 de 16 bits) (TIMER0, TIMER1, TIMER2, TIMER3)
· 2 canais capture/compare/PWM - módulo CCP
· Master synchronous Serial Port (MSSP) module.
· Unhaced Usart
· 8 canais A/D de 10 bits;
· Detector de baixa voltagem programável
· Permite até 100 000 ciclos de escrita e leitura na memória de programa Flash
· permite 1 000 000 ciclos de leitura e escrita na E2PROM
· Retenção de dados na Flash de 40 anos
· Watchdog timer com oscilador próprio e programável
· três pinos de interrrupção externa. INT0, INT1, INT2

Tipos de encapsulamentos:
As primeiras versões do PIC eram baseadas em encapsulamentos do tipo DIP40, hoje os dipositivos de 40 pinos ainda são muito comuns, porém de acordo com
a aplicação e os periféricos internos presentes no Chip eles podem ser encontrados em diversos encapsulamentos como:
DIP → Dual In-line Pin
PLCC → Leadless Chip Carrier.
TQFP
Nosso estudo será baseado em microcontroladores com o encapsulamento DIP (Dual In-line Pin), devido a facilidade de utilização e disponibilidade no mercado,
porém não há grandes dificuldades para se migrar para outros encapsulamentos, basta analisar um pouco o datasheet do microcontrolador.

Estrutura interna do PIC18F452


Descrição das funções dos pinos do PIC18F452

O PIC18F452 possui no total de 33 pinos de I/O divididos entre as PORTA, PORTB, PORTC, PORTD e PORTE.

PORTA: encontramos 7 pinos físicos intitulados de RA0 a RA6 que podem ser utilizados como I/O de uso geral ou como conversor analógico/ digital A/D, (com
excessão de RA4), além de possuir também a função de detecção de baixa tensão (LVD), referência analógica do A/D e contador externo.

PORTB: encontramos 8 pinos intitulado de RB0 a RB7 configuráveis como I/O de uso geral. Nesse PORT podemos trabalhar com três interrupções externas,
módulo CCP e pinos de gravação e debugger.

PORTC: encontramos 8 pinos intitulados de RC0 a RC7 configuráveis como I/O de uso geral, saída do oscilador do timer, módulo CCP, Clock e data para os
modos SPI, I2C e UART.

PORTD: encontramos 8 pinos intitulado de RC0 a RC7 que pode ser configurado como I/O de uso geral ou ser configurado como PSP para ter saída TTL (por
exemplo: interfaciamento com microprocessadores ).

PORTE: podemos utilizá-lo como PORT de I/O de uso geral ou utilizar os pinos de WR e CS para acesso ao modo paralelo Slave Port (acesso a memória
externa por exemplo).

Abaixo segue com detalhes a função de cada pino do microcontrolador PIC18F452.

Pino
Função Tipo Descrição
(DIP)

1 /MCLR/VPP entrada - entrada Entrada do RESET externo. Pino de habilitação de alta voltagem ICSP
13 OSC1 /CLK1 entrada entrada do cristal oscilador / entrada do clock externo

14 OSC2 / CLK1/ RA6 saída - saída - I/O saída do cristal oscilador / entrada do clock exteno / entrada e saída de uso geral
2 RA0 / AN0 I/O - entrada entrada e saída de uso geral / entrada do conversor AD0
3 RA1 / AN1 I/O - entrada entrada e saída de uso geral / entrada do conversor AD1
entrada e saída de uso geral/ entrada do conversor AD2 / entrada de referência baixa
4 RA2 / AN2 /Vref- I/O - entrada - entrada
do A/D
entrada e saída de uso geral / entrada do conversor AD3 / entrada de referência Alta
5 RA3 / AN3 / Vref+ I/O - entrada - entrada
do A/D
6 RA4 / T0CKI I/O - entrada - entrada entrada e saída de uso geral / entrada de clock timer0
I/O - entrada - entrada - entrada e saída de uso geral / entrada do conversor AD4 / entrada de seleção SPI,
7 RA5/AN4/SS/LVDIN
entrada detector de baixa voltagem.
33 RB0 / INT0 I/O - entrada entrada e saída de uso geral / interrupção externa 0
34 RB1 / INT1 I/O - entrada entrada e saída de uso geral / interrupção externa 1
35 RB2 / INT2 I/O - entrada entrada e saída de uso geral / interrupção externa 2
36 RB3 / CCP2** I/O - I/O entrada e saída de uso geral / módulo CCP2 (multiplexado com RB3 )
37 RB4 I/O entrada e saída de uso geral / pino de interrupção por mudança de nível
entrada e saída de uso geral (interrupção por mudança de estado) / pino de habilitação
38 RB5/PGM I/O - I/O
ICSP baixa voltagem
entrada e saída de uso geral (interrupção por mudança de estado) / pino ICSP In-
39 RB6 / PGC I/O - I/O
Circuit Debugger
entrada e saída de uso geral (interrupção por mudança de estado) / pino ICSP In-
40 RB7 / PGD I/O - I/O
Circuit Debugger
15 RC0/T10S0/T1CKI I/O - saída - entrada entrada e saída de uso geral / saída do 2º oscilador /Contador externo Timer 1
RC1 / T10SI / entrada e saída de uso geral / entrada 2º oscilador / módulo CCP2 (multiplexado com
16 I/O - entrada -saída
CCP2** RB3 )
17 RC2/CCP1 I/O - I/O entrada e saída de uso geral / módulo CCP1
entrada e saída de uso geral / entrada e saída do clock serial para o modo SPI /
18 RC3/SCK/SCL I/O - I/O - I/O
entrada e saída do clock serial para o modo I2C
23 RC4/SDI/SDA I/O - entrada - I/O entrada e saída de uso geral / entrada de dados SPI / entrada e saída de dados I2C
24 RC5/SD0 I/O - saída entrada e saída de uso geral / saída de dados SPI
entrada e saída de uso geral / canal de transmissão UART / clock de sincronismo
25 RC6/TX/CK I/O - saída - I/O
UART
26 RC7/RX/DT I/O - entrada - I/O entrada e saída de uso geral / canal de recepção UART / clock de sincronismo UART
19 RD0/PSP0 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
20 RD1/PSP1 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
21 RD2/PSP2 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
22 RD3/PSP3 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
27 RD4/PSP4 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
28 RD5/PSP5 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
29 RD6/PSP6 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
30 RD7/PSP7 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
entrada e saída de uso geral / controle de leitura do port paralelo / entrada analógica
8 RE0/RD/AN5 I/O
AD5
entrada e saída de uso geral / controle de escrita do port paralelo / entrada analógica
9 RE1/WR/AN6 I/O
AD6
entrada e saída de uso geral / controle de seleção do port paralelo / entrada analógica
10 RE2/CS/AN7 I/O
AD7
12 ,
GND alimentação negativo
31
11, 32 VCC alimentação positivo

Tipos de Memórias
No PIC18F452 encontramos três tipos de memórias:

Memória de Programa - FLASH


Memória de dados - RAM
Memória de dados EEPROM

Como nós sabemos, memória de programa (ROM, EPROM, FLASH) é do tipo não volátil, ou seja, podemos desenergizar nosso sistema microcontrolado e ele
não perderá o programa gravado.

Nosso microcontrolador estudado possui internamente 32Kbyte de memória de programa e possui um barramento de programa de 16 bits. Além disso, existem 21
bits para endereçamento da memória de programa, o que permite que até 2 Mbyte sejam endereçados pelo microcontrolador da familia PIC18F.

Memória de Programa
A memória de programa utilizada em nosso microcontrolador é do tipo FLASH (sufixo "F") que permite ser gravada/apagada no minimo 1000 vezes. Este tipo de
memória utilizada é ideal para ser utilizada em desenvolvimento de projetos e até mesmo em produtos finais.
Memória de Programa FLASH do PIC18F442

Podemos perceber que a memória de programa do PIC18F452 inicia-se no endereço 0000h e atinge o valor máximo de 7FFFH, ou seja , 32767K byte
(32Kbyte).

Nota:

Não podemos confundir de forma alguma memória de programa e memória de dados. Sabemos que memória de programa é
onde nosso programa estará gravado, enquanto memória de dados é onde os valores de nossas variáveis serão salvas
temporariamente.

O endereço inicial 0000h é chamado de vetor de reset. A instrução de programa que estiver nesse endereço de memória será a primeira a ser
executada pelo processador do PIC. Sempre que energizarmos ou resetarmos o microcontrolador, o contador de programa PC apontará sempre para este
primeiro endereço da memória.

Em seguida temos os endereços 0008h e 0018h. Estes endereços são chamados de vetores de interrupção. (veremos mais adiante em nosso curso
sobre esses vetores de interrupção).

Em seguida temos os endereços 0008h e 0018h. Estes endereços são chamados de vetores de interrupção. (veremos mais adiante em nosso curso sobre esses
vetores de interrupção).

Vamos exemplificar um processo de gravação na memória de programa do PIC.

Acompanhe o programa exemplo de programa abaixo:

/Este programa tem por função alterar o estado dos 8 pinos do PORTB do PIC em intervalos de 1 segundo.

void main() {
TRISB = 0; // configura o portB como saída
PORTB = 0x00; // coloca zero em todos os pinos do portB
do {
PORTB = 0xFF; // coloca um em todos os pinos do portB
Delay_ms(1000); // delay de 1 segundo (1000 ms)
PORTB = 0x00; // Coloca zero em todos os pinos do portB
Delay_ms(1000); // delay de 1 segundo
} while(1);

Ao compilarmos o programa acima com o mikroC teremos como resultado o seguinte código de máquina (descrito somente algumas linhas do código gerado;
A partir da imagem acima, podemos verificar os valores que serão gravados na memória de programa após gravação do programa exemplo acima. Esses valores
são úteis para o programador experiente, pois permite visualizar e acompanhar a execução por completo da compilação dos arquivos.

Nota: Os códigos em assembly visualizado na figura acima foi gerado pelo compilador mikroC após a compilação de um
programa escrito em C.

Memória de dados - EEPROM


Como estudamos anteriormente, sabemos que as memórias do tipo EEPROM são muito parecidos com a memória RAM, sua diferença que as distingue é devido
ao fato da memória EEPROM não "perder" as informações salvas quando a desenergizarmos. Podemos salvar nesta memória dados não voláteis durante o
decorrer da execução de nosso programa.

O PIC18F452 possui internamente 256 byte de memória de dados EEPROM.

O endereço inicial da memória de dados EEPROM é 0 e seu último endereço é FFh.


Estudaremos através de comandos como escrever e ler dados nessa memória de programa nas unidades seguintes.

Memória de dados - RAM


Chegamos a um assunto de grande importância no estudo referente ao nosso microcontrolador PIC, neste tópico apresentaremos toda a estrutura de memória de
dados RAM. Aprenderemos a trabalhar com ela e também onde estão localizados os “botões” que ligam/desligam, configuram e controlam os periféricos internos
do PIC, ou seja, os SFR (Registradores de Funções Especiais).

A memória de dados RAM é dividida em dois grupos: os chamados GPR (Registradores de Propósito Geral) e os SFR ( Registradores de Funções Especiais).

Os GFR tem a função de armazenar dados de uso geral que utilizamos e criamos durante a execução do nosso programa. Podemos guardar nesse região da
memória RAM dados voláteis de variáveis, tabelas, constantes, entre outras.

Os SFR's é a parte principal do microcontrolador, é nesta área da memória RAM que estão armazenados todo o setup de funcionamento do PIC. Apartir desses
registradores podemos configurar o modo de trabalho dos timers/counters, USART, conversores analógicos digitais, etc.
O PIC18F452 em estudo possui internamente 1536 bytes de memória de dados RAM.

O banco de memória RAM da família 18F é dividido em 16 bancos que contém 256 bytes de memória. Os 6 primeiros bancos são a região dos GPR's, e o último
banco são dos SFR's. Acompanhe a figura abaixo:

Vamos examinar o mapa de registradores dos SFR's. (endereço F80h à FFFh)

Os SFR´s são registradores de funções especiais responsáveis pela configuração dos modos de trabalhos do PIC. Através da
configuração desses registradores especiais, podemos também monitorar o status do microcontrolador em determinados momentos e
situações. Estudaremos a função desses registradores quando estivermos trabalhando com projetos, pois dessa maneira ficará mais
claro seu funcionamento e aplicações.

O Clock
Todo microcontrolador requer um circuito de oscilação pois é quem dá o sinal de "partida" e "sincronismo" para qualquer atividade interna da chip. A freqüência de
operação de oscilação é um dos agentes que determinam a velocidade de execução das instruções do microcontrolador.

O PIC18F452 pode funcionar com diversas fontes de osciladores:

1. LP low-power cristal
2. XT cristal ou ressonador
3. HS High-Speed cristal (cristal de alta velocidade) ou ressonador
4. HS + PLL High-Speed cristal ou ressonador com PLL habilitada
5. RC Resistor / Capacitor externo
6. RCIO Resistor / Capacitor externo com pino de I/O
7. EC Clock externo
8. ECIO Clock exteno com pino de I/O

Osciladores: Cristal e Ressonador


Para utilizarmos cristal ou ressonadores em nosso chip, devemos conectá-lo da seguinte maneira:

Figura 01 - clock gerado a partir de um cristal de quartzo

Nota: Quando projetamos um dispositivo, a regra é colocar o oscilador tão perto quanto possível do microcontrolador, de modo a evitar
qualquer interferência nas linhas que ligam o oscilador ao microcontrolador.

Note que existe dois capacitores ligados em paralelo com o cristal. O valor desses capacitores variam de acordo com a frêquencia do
cristal utilizado. Abaixo segue uma tabela apresentando os valores dos capacitores:

Valores tipicos para o


Freqüência
Tipo de oscilador capacitor
do cristal
C1 C2
LP (cristal de baixa 32kHz 33pF 33pF
freqüência) 200KHz 15pF 15pF
200KHz 22-68pF 22-68pF
XT (cristal) 1.0MHz 15pF 15pF
4.0MHz 15pF 15pF
4.0MHz 15pF 15pF
HS (cristal de alta 8.0 MHz 15-33pF 15-33pF
freqüência) 20.0MHz 15-33pF 15-33pF
25MHz 15-33pF 15-33pF

Ressonador cerâmico
cristal de quartzo

Os ressonadores cerâmicos são comercializados em dois tipos: com dois terminais e com três terminais. Os mais utilizados são os ressonadores com três
terminais pois não precisam de capacitores externos. O ressonador com dois terminais obedecem a ligação da figura 01, enquanto no de três o pino central deve
ser aterrado. segue abaixo o esquema de ligação do ressonador com três terminais:

Os ressonadores cerâmicos é uma segunda opção. Não é tão barato quanto um RC mas é bem mais estável e preciso.

Nota: Antes de gravarmos um programa no microcontrolador PIC devemos "queimar" os fusivel de configuração informando qual o tipo
de oscilador estamos utilizando em nosso hardware.
Nos projetos do nosso curso iremos utilizar cristal de quartzo de 4MHz pois garantimos maior precisão nas rotinas de tempo nas execuções das instruções.

Circuito de Oscilação RC
Este é o tipo de oscilador mais simples que existe e também o mais barato, mas, por outro lado, é o menos preciso, variando muito a tensão de trabalho,
temperatura e tolerâncias. O circuito RC deve ser ligado conforme a figura abaixo:

Modo HSPLL
HSPLL é na verdade um modo em que podemos multiplicar o valor da freqüência de entrada do cristal oscilador por 4. É ideal para ambientes em que o
dispositivo não pode gerar EMI (interferência Eletromagnética). Este modo deve ser habilitado nos "fusíveis de configurações" no momento em que formos gravar
o chip.

Por exemplo:

Caso venhamos conectar ao PIC um cristal de 10 MHz entre os pinos OSC1 e OSC2 e ativarmos o modo HSPLL no momento em que gravarmos o chip, o PIC
passa a operar a 40 MHz, executando as instruções internas em 10MHz (10 MIPS - milhões de instruções por segundo), pois a frequência real de operação do
seu microcontrolador é: Fosc / 4.

Para representar os bits de configuração de nosso microcontrolador, iremos utilizar um software gratuito de gravação de microcontroladores PIC disponível na
internet, chamado WinPIC800. (www.winpic800.com).

Nota: No programa Winpic800 podemos definir o tipo de oscilador utilizado no PIC através do seguinte setup:

Os bits de configuração (fusíveis)


Os microcontroladores PIC possui internamente regiões de memórias não voláteis que são resposáveis por determinar o modo de
trabalho do chip. São chamados de bits de configuração ou fusíveis. É de grande importância que venhamos entender o
funcionamento desses bits, caso contrário teremos sérios problemas no funcionamento do nosso projeto.

Os bits de configuração do PIC estão armazenados em uma área reservada do PIC.

Mapa dos bits de configuração do PIC18F452

Internamente no microcontrolador, encontramos 8 registradores de 16bits, divididos em duas partes: Byte alto e Byte Baixo, e são
responsáveis pela configuração geral do PIC. Esta região de memória não é acessivel durante a execução do programa, mas somente
podemos configura-lá no momento da gravação do chip.
Apresentamos a seguir uma tabela com a descrição de todos os bits de configuração do PIC18F452:

RC, EC, LP, XT, HS, HS-PLL, RC-


Oscilador
SOC2 com RA6, EC-OSC2 com RA6
oscilador switch enable habilitado ou desabilitado
power up timer habilitado ou desabilitado
brown out detect habilitado ou desabilitado
brown out voltage 2.5V, 2.7V, 4.5V ou 4.7V

watchdog timer habilitado ou desabilitado


1:1 , 1:2, 1:4, 1:8, 1:16, 1:32, 1:64, ou
watchdog postscaler
1:128
ccp2mx pino RC1 ou RB3
stack overflow enable habilitado ou desabilitado
low voltage program habilitado ou desabilitado
code protect 0x0200 a 0x1FFF habilitado ou desabilitado
code protect 0X2000 a 0X3FFF habilitado ou desabilitado
code protect 0X4000 a 0X5FFF habilitado ou desabilitado
code protect 0X6000 a 0X7FFF habilitado ou desabilitado
data ee read protect habilitado ou desabilitado
code protect boot habilitado ou desabilitado
Table write protect 0x2000 a 0x1FFF habilitado ou desabilitado
Table write protect 0x2000 a 0x3FFF habilitado ou desabilitado
Table write protect 0x4000 a 0x5FFF habilitado ou desabilitado
Table write protect 0x6000 a 0x7FFF habilitado ou desabilitado
data ee write protect habilitado ou desabilitado
table write protect boot habilitado ou desabilitado
config write protect habilitado ou desabilitado
Table read protect 0x0200 a 0x1FFF habilitado ou desabilitado
Table read protect 0x2000 a 0x3FFF habilitado ou desabilitado
Table read protect 0x4000 a 0x5FFF habilitado ou desabilitado
Table read protect 0x6000 a 0x7FFF habilitado ou desabilitado
Table read protect boot habilitado ou desabilitado

No WinPIC800, programa de gravação de microcontroladores PIC utilizado no kit PICGenios da Microgenios, podemos visualizar e
configurar os bits de configuração antes de gravarmos o programa no PIC.

Podemos configurar os bits de configuração de duas maneiras:

Através do mikroC, no momento ou no decorrer da elaboração do projeto

Através do programa de gravação do PIC (WinPIC800, IC-Prog, etc)

A melhor maneira de configurar os bits de configuração é no momento de criação do programa no compilador, pois uma vez
configurado esses bits, não precisaremos reconfigura-los a cada compilação de nos programa.

Configurando os bits de configuração do PIC

FOSC (frequêncy Oscilator)

Através dessa opção podemos configurar o tipo de clock com o qual o microcontrolador irá operar. As fontes de clock que podem ser
selecionadas são:

Oscilador externo RC, com RA6 configurado como pino de saida de clock;

Oscilador HS com PLL ativo - multiplica o valor da frequência de oscilação por 4;

Oscilador do tipo EC, utilizando o pino RA6 como saida de clock (Fosc/4);

Oscilador EC Utilizando o pino RA6 como I/O

Oscilador HS, XT, LP

OSCSEN (SYSTEM CLOCK SWITCH BIT)

Através desse bit de configuração, podemos selecionar o modo de acionamento do oscilador do PIC. Caso venhamos a selecionar este
bit, a frequência de oscilação passa a ser gerada internamente pelo TIMER1, caso contrário, o oscilador selecionado é FOSC.

Obs: Caso desejemos utilizar o oscilador interno do PIC, devemos selecionar este bit de configuração e configurar o TIMER1 para
trabalhar como oscilador para o clock (basta habilitar o T1OSCSEN do TIMER1), caso não venhamos a programar o TIMER1
corretamente, automaticamente o bit de configuração do OSCSEN será desabilitado.

PWRTEN (SYSTEM CLOCK SWITCH BIT)

A través desse bit, podemos configurar o modo POWER para o acionamento do PIC, quando este bit estiver acionado o modo POWER-UP TIMER estará
habilitado. O temporizadore de power-up pode seu utilizado para fazer com que o chip permaneça em reset por aproximadamente 72ms após o chip ter sido
ligado. E deve ser utilizado para garantir a estabilização da fonte de energia no projeto.
BROWN-OUT voltage

O detector de brown-out é utilizado para provocar o reset da CPU no caso de a tensão de alimentação cair abaixo de um determinado
limite. Uma vez que a tensão retorne ao seu valor nominal, o circuito de reset é liberado e o programa é reiniciado.
No PIC18F452 podemos selecionar 4 tipos de tensões para o BROWN-OUT: 2.0V, 2.7V, 4.2V ou 4.5V.
BROWN-OUT detect

Podemos habilitar ou desabilitar o detector de brown-out, basta selecionar o bit de configuração caso desejemos habilitar o brown-out.
WDTEN - Watchdog Timer Enable (cão de guarda)

O watchdog timer ou cão de guarda é um sistema interno ao microcontrolador que evita que o programa pare em um determinado
ponto. Imagine que você desenvolveu uma aplicação e que por um erro de software em certo momento o seu programa pára (entra em
loop infinito). Caso o watchdog dog não seja resetado em tempos em tempos ele irá "estourar", ou seja, chegará ao final da sua
contagem fazendo com que o microcontrolador resete e reinicie todo o processo.
Para habilitar o Watchdog marque o bit WDTEN. (basta deixar em ON).
Watchdog Postscale Select Bit

O tempo minimo para estouro do watchdog é de 18 ms, porém podemos estender esse tempo através do postscale. Com essa função
podemos multiplicar o tempo mínimo de 18 ms com os seguintes multiplicadores do prescaler: 1:1; 1:2, 1:4, 1:8, 1:16, 1:32, 1:64, 1:128.
CCP2 MX

Podemos através desse bit definir se queremos multiplexar o módulo CCP2 com o pino RC1 (CCP2MX = 0) com o pino RB3 (CCP2MX
= 1)
BKBUG (debug)

Através desse bit , podemos habilitar o modo DEBUGGER no PIC. Nesse modo é possivel emular um programa no próprio projeto
eletrônico. Emular consiste no processo de junto com o computador, testar passo a passo o funcionamento do programa que está
rodando no microcontrolador. Se esta opção estiver ativa, os pinos de gravação RB6 e RB7 deixam de funcionar como I/O's; caso
contrário, o funcionamento desses pinos fica normal. Faz necessários a utilização de equipamentos de debugação fornecidos pela
Microchip, ou outras empresas para realizar esta operação.
LVP (Low Voltagem Programming)

Quando o PIC é gravado, é acionado uma tensão de 12Vcc no pino MCLR. Se a opção LVP estiver ativa, para gravar o PIC basta ter
nivel lógico 1 no pino RA5. Esta opção é ideal para aplicações em campo onde é dificil encontrar tensões de 12V disponível em
determinados equipamentos.

STVREN (Stack Overflow Reset Enable Bit)

O PIC18F452 possui internamente 31 endereços de pilha. Quando habilitamos esse bit STVREN, toda vez que ultrapassarmos o limete
da pilha (ocorrer overflow) o microcontrolador será resetado.

Área de Proteções contra leitura e escrita

Code Protect 0x0200 a 0x1FFF

Caso este bit esteja habilitado, protege contra leitura a memória de programa de 0x0200 a 0x1FFF.

Code Protect 0x2000 a 0x3FFF

Caso este bit esteja habilitado, protege contra leitura a memória de programa de 0x2000 a 0x3FFF.

Code Protect 0x4000 a 0x5FFF

Caso este bit esteja habilitado, protege contra leitura a memória de programa de 0x4000 a 0x5FFF.

Code Protect 0x6000 a 0x7FFF

Caso este bit esteja habilitado, protege contra leitura a memória de programa de 0x6000 a 0x7FFF.

Data EE Read Protect

Caso este bit esteja habilitado, protege contra leitura os 256 bytes da memória EEPROM (memória não volátil).

Code Protect Boot

Caso este bit esteja habilitado, protege contra leitura a região de memória Boot (área da memória de programa) 0x0000 até 0x0FF0.

Table Write Protect 0x0200 a 0x1FFF

Caso este bit esteja habilitado, protege a memória de programa contra escrtita por tabela no endereço especificado.

Table Write Protect 0x2000 a 0x3FFF

Caso este bit esteja habilitado, protege a memória de programa contra escrtita por tabela no endereço especificado.

Table Write Protect 0x4000 a 0x5FFF

Caso este bit esteja habilitado, protege a memória de programa contra escrtita por tabela no endereço especificado.

Table Write Protect 0x6000 a 0x7FFF

Caso este bit esteja habilitado, protege a memória de programa contra escrtita por tabela no endereço especificado.

Data Write Protect

Caso este bit esteja habilitado, protege contra escrita os 256 bytes da memória EEPROM (memória não volátil).

Table Write Protect Boot

Caso este bit esteja habilitado, protege contra escrtita por tabela a área de boot.
Config Write Protect

Caso este bit esteja habilitado, protege contra escrita a área de configuração do microcontrolador.

Table Read Protect 0x0200 a 0x1FFF

Caso este bit esteja habilitado, protege a memória de programa contra leitura por tabela no endereço especificado.

Table Read Protect 0x2000 a 0x3FFF

Caso este bit esteja habilitado, protege a memória de programa contra leitura por tabela no endereço especificado.

Table Read Protect 0x4000 a 0x5FFF

Caso este bit esteja habilitado, protege a memória de programa contra leitura por tabela no endereço especificado.

Table Read Protect 0x6000 a 0x7FFF

Caso este bit esteja habilitado, protege a memória de programa contra leitura por tabela no endereço especificado.

Ciclos de máquina
A contagem de tempo não é medida diretamente pela freqüência de oscilação do cristal e sim através do que chamamos de CICLO DE MÁQUINA.

Internamente no microcontroladores PIC a freqüência do cristal é dividida por 4, o que nos resulta que a freqüência real de trabalho é:

freqüência de trabalho real = freqüência oscilador / 4

Concluimos então que: a cada 1 cliclo de máquina corresponde a 4 pulsos do oscilador.

Para exemplificar vamos supor que temos conectado ao microcontrolador um cristal de quartzo de 8MHz. Qual é a freqüência real de execução das instruções do nosso
microcontrolador?

É muito simples, acompanhe:

Sabemos que a freqüência do cristal utilizado é de 4 MHz, e que cada instrução leva exatamente 1 ciclo de máquina para ser executada; Basta dividir o valor do
cristal por 4 para sabermos o valor real de trabalho do PIC.

freqüência de trabalho real = 8MHz / 4 => 2MHz

Concluimos então que nosso microcontrolador PIC com cristal de 8MHz esta trabalhando efetivamente a 2MHz, ou seja , cada instrução de programa leva 0,5 us
para ser executada.

Nota: Temos que lembrar que nosso microcontrolador em estudo pode operar com cristal oscilador de até 40MHz, disso nos resulta em 10MHz a
freqüência máxima de trabalho permitida por esse chip.

Freqüência de trabalho real = 40MHz / 4 => 10MHz

Reset
Sempre que ligamos ou energizamos nosso circuito microcontrolado é interessante que o PIC seja resetado, para isso é necessária a inserção de um circuito
eletrônico bem simples, mas capaz de realizar esse procedimento que costumamos chamar de POWER ON RESET.

Além disso é interessante que exista um botão para que o usuário possa reiniciar o sistema sempre que for necessário (reset manual), a seguir indicamos um
exemplo de circuito para o reset.

Os PICs possuem diversos tipos de reset:

POR - Power On Reset - é o reset que ocorre quando o chip é ligado.


Reset do MCLR durante a operação normal
Reset do MCLR durante o modo SLEEP
Reset do watchdog durante operação normal
Reset do detector de Brown-out (BOR)
Instrução de Reset
Reset por estouro de Pilha - Stack Overflow

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
As Portas de I/O's
1.0 Introdução as Portas de I/O
1.1 PORTA
1.2 PORTB
1.3 PORTC
1.4 PORTD
1.5 PORTE

Introdução as portas de I/O


Os microcontroladores PIC possuem pinos físicos destinados à comunicação de dados com os circuitos
externos. Através desses pinos podemos enviar níveis lógicos (0 ou 1) para, por exemplo, acender ou
apagar um LED, acionar displays LCD, ler botões e sensores, etc.

O microcontrolador PIC18F452 da Microchip possuem 34 pinos de escrita e leitura (I/O), os quais


estão divididos em quatro portas chamadas: PORTA, PORTB, PORTC, PORTD e PORTE.

figura 01

Os PORTB, PORTC e PORTD possuem 8 pinos de I/O cada, enquanto o PORTE possue 3 pinos de I/O e
PORTA 6 pinos de I/O.

Cada porta de I/O possui dois registradores que controlam suas funções: um registrador PORT e um
registrador TRIS.

Os registradores PORT (PORTA, PORTB, PORTC, PORTD, PORTE) que são posições na RAM que
contêm os dados das portas paralelas (I/O's) do microcontrolador. Qualquer escrita realizada em um
desses registros automaticamente altera todo o conteúdo presente na saída do chip.

O registrador TRIS é utilizado para configurar cada pino da respectiva porta como entrada ou saída.
Assim, cada bit do registrador corresponde a um pino da porta. Se o bit estiver em 1 (um), o pino
correspondente esta configurado como entrada de dados, e se estiver em 0 (zero) configuramos este
pino como saída de dados.

As portas de comunicação paralela do PIC são utilizadas para efetuar diversas tarefas:

PORTA
A primeira porta a ser estudada é a PORTA. Além das funções de entrada e saída de dados encontramos
também diversas outras funções multiplexadas aos pinos da PORTA:

Vamos conhecer as funções de cada pino da PORTA:

PINO FUNÇÃO TIPO DESCRIÇÃO


2 RA0 / AN0 I/O - entrada Pino de entrada e saída de uso geral / entrada do conversor AD0
3 RA1 / AN1 I/O - entrada Pino de entrada e saída de uso geral / entrada do conversor AD1
I/O - entrada - Pino de entrada e saída de uso geral/ entrada do conversor AD2 / entrada
4 RA2 / AN2 /Vref-
entrada analógica de referência baixa
I/O - entrada - Pino de entrada e saída de uso geral / entrada do conversor AD3 / entrada
5 RA3 / AN3 / Vref+
entrada analógica de referência alta
I/O - entrada -
6 RA4 / T0CKI Pino de entrada e saída de uso geral / entrada de contador T0 do timer0
entrada
I/O - entrada - Pino de entrada e saída de uso geral / entrada do conversor AD4 / entrada de
7 RA5/AN4/SS/LVDIN
entrada -entrada seleção SPI, detector de baixa voltagem.
saída do cristal oscilador / entrada do clock exteno / entrada e saída de uso
14 OSC2 / CLK1/ RA6 saída - saída - I/O
geral

Registrador PORTA

Figura 02

O resistrador PORTA esta localizado na memória RAM do PIC e possui o endereço (0XF80). Este registrador
irá acessar os pinos da PORTA da seguinte maneira:

BIT 0 do registrador PORTA -> atua sobre o bit RA0


BIT 1 do registrador PORTA -> atua sobre o bit RA1
BIT 2 do registrador PORTA -> atua sobre o bit RA2
BIT 3 do registrador PORTA -> atua sobre o bit RA3
BIT 4 do registrador PORTA -> atua sobre o bit RA4
BIT 5 do registrador PORTA -> atua sobre o bit RA5
BIT 6 do registrador PORTA -> atua sobre o bit RA6

Toda a porta de I/O possui internamente um latch de saída para cada pino. Dessa forma, uma escrita no
registrador PORTA irá na realidade escrever em cada latch do PORTA.

Para ler um dado na PORTA, basta ler o conteúdo do registrador PORTA.

Nota: O registrador PORTA é o "espelho" dos pinos da PORTA do PIC, para lermos ou
escrevermos nesse registrador estamos na verdade atuando sobre os pinos do port
respectivamente. Não podemos esquecer que o registrador TRISA também afeta a
leitura e escrita no port.

Além de operar como I/O de uso geral, o PORTA, pode ser configurado como entrada analógica. Para
isso, faz necessário que o programador venha a programar os pinos do PORTA como entrada
analógica, através do registrador ADCON1.

Nota: após o reset do seu microcontrolador, os pinos do PORTA e PORTE vem configurados como A/D
(entrada analógica), caso queiramos acionar por exemplo um relê com um pino deste PORT,
precisamos configurar adequamente este pino como I/O através do registrador ADCON1.
Conhecendo o registrador ADCON1:

X pode ser 0 ou 1

Figura 03

O registrador ADCON1 é responsável por determinar quais os pinos do PORTA e PORTE (com excessão
de RA4) serão entrada analógica ou digital. Repare que na tabela (figura 03) para configurarmos todos os
pinos do PORTA como entrada digital, ou seja, como I/O de uso geral, basta carregar o valor ADCON1 =
0B00000111; ou ADCON1 = 0B00000110;

Exemplo de programa:

void main() {
adcon1 = 0b00000111; // configura pinos do PORTA e PORTE como I/O digital
trisa.f0 = 0; //configura pino RA0 como saida
porta.F0 = 1; // envia nivél lógico 1 no pino RA0
}

Podemos definir determinadas tensões externas de referência para nosso conversor analógico / digital,
através dos pinos RA2 (AN2) e RA3 (AN3). Podemos também programar nossa referência de tensão do
conversor A/D como sendo VSS e VDD, ou seja, a própria tensão de alimentação do chip. Neste caso, os
pinos RA2 e RA3 poderão ser utilizados como entrada analógica, ou como I/O de uso geral.

Registrador TRISA

O registrador TRISA é utilizado para o controle da direção de atuação de cada pino de I/O do PORTA.
Através desse registradores definimos e configuramos o modo de trabalho do pino do
microcontrolador como entrada ou saída de dados.

Este registrador define os pinos do PORTA da seguinte maneira:

BIT 0 do registrador TRISA -> atua sobre o bit RA0


BIT 1 do registrador TRISA -> atua sobre o bit RA1
BIT 2 do registrador TRISA -> atua sobre o bit RA2
BIT 3 do registrador TRISA -> atua sobre o bit RA3
BIT 4 do registrador TRISA -> atua sobre o bit RA4
BIT 5 do registrador TRISA -> atua sobre o bit RA5
BIT 6 do registrador TRISA -> atua sobre o bit RA6

Se o bit do registrador TRISA estiver em nível lógico 1, o pino correspondente a este bit do PORTA estará
configurado como entrada.

Se o bit do registrador TRISA estiver em nível lógico 0, o pino correspondente a este bit do PORTA estará
configurado como saída.
void main() {
trisa = 0b00000000; //configura todos os pinos do PORTA como saida
trisb = 0b00001111; //configura somente os pinos de RB7 a RB4 do PORTB como saida
trisa.f0 = 0; //configura pino RA0 como saida
trise.f2 = 1; //configura pino RE2 como entrada de dados
trisc.f1 = 1; //configura pino RC1 como entrada de dados
}

Aprendendo como configurar os pinos do PIC18F452:

Exemplo:

Com base no circuito eletrônico abaixo, vamos configurar os registradores de direção de pinos:

void main() {
adcon1 = 0b00000110; //configura pinos do PORTA e PORTE como I/O digital
trisa.f0 = 0; //configura RA0 do PORTA como saida
trisa.f2 = 0; //configura RA2 do PORTA como saida
trisa.f4 = 0; //configura RA4 do PORTA como saida
porta = 0b00010101; //ascende somente todos os leds.

trisb.f0 = 1; //configura pino RB0 como entrada de dados


trisb.f3 = 1; //configura pino RB3 como entrada de dados
trisb.f7 = 1; //configura pino RB7 como entrada de dados
}

No circuito acima, os pinos RA0, RA2, RA4 dO PORTA são responsáveis pelo acionamento dos leds.
Devemos configurar o PORTA como saída, pois nosso objetivo é "escrever" no port, ou seja, enviar "0"
ou "1" lógico nos pinos do PORTA correspondente.

Os pinos RB0, RB3 e RB7 do PORTB tem a função de "ler" os estados das teclas, quando as teclas
estão soltas temos nível lógico 1 nos pinos, quando a tecla é pressionada, o estado do pino é alterado
para nível 0. Devemos configurar o PORTB como entrada, pois iremos "ler" o estados dos pinos, ou
seja, o estados das teclas.

Seguindo o mesmo raciocínio lógico, vamos programar o PIC abaixo de forma que todos os leds pisquem a
cada segundo.
void main(){
trisd = 0; //configura todos os pinos do PORTC como saida
portd = 0; //inicia programa com portd em zero
while(1){ //laço de repetição
portd = 0; //apaga todos os leds
delay_ms(1000); //delay de 1 segundo
portd = 0b11111111; //envia nível lógico 1 em todos os pinos do PORTD
delay_ms(1000); //delay de 1 segundo
}
}

Exercício:

Com base na tabela (figura 03), programa os pinos RA0, RA1 e RA3 como entrada analógica, as demais
entradas analógicas deverão ser programadas como I/O digital.

void main(){
adcon1 = 0b00000100; //configura pinos RA0, RA1, RA3 do PORTA entrada analógica
}

Leitura do LATCH de um pino do PIC

Para ler o LATCH de saída do microcontrolador ao invés do pino utilizamos o registrador LATA,
referente ao PORTA, ou LATB, referente ao PORTB. O latch de saída encontra-se antes do buffer de
saída do pino, e por esse motivo podemos ler seu estado.

PORTB
O PORTB, tal qual o PORTA, implementa diversas outras funções multiplexadas aos seus pinos, acompanhe:

PINO FUNÇÃO TIPO DESCRIÇÃO


33 RB0 / INT0 I/O - entrada Pino de entrada e saída de uso geral / interrupção externa 0
34 RB1 / INT1 I/O - entrada Pino de entrada e saída de uso geral / interrupção externa 1
35 RB2 / INT2 I/O - entrada Pino de entrada e saída de uso geral / interrupção externa 2
36 RB3 / CCP2 I/O - I/O Pino de entrada e saída de uso geral / módulo CCP
37 RB4 I/O Pino de entrada e saída de uso geral / pino de interrupção por mudança de nível
Pino de entrada e saída de uso geral (interrupção por mudança de estado) / pino
38 RB5/PGM I/O - I/O
de habilitação ICSP baixa voltagem
Pino de entrada e saída de uso geral (interrupção por mudança de estado) / pino
39 RB6 / PGC I/O - I/O
ICSP In-Circuit Debugger
Pino de entrada e saída de uso geral (interrupção por mudança de estado) / pino
40 RB7 / PGD I/O - I/O
ICSP In-Circuit Debugger
Assim como no PORTA, o PORTB possui dois registradores que definem o modo de trabalho dos seus pinos de
I/O: são eles: PORTB e TRISB.
Tanto o registrador PORTB quanto o registrador TRISB estão localizados na memória RAM do PIC e pertencem
aos SFR'S (Registradores de Funções Especiais).
O registrador TRISB tem as mesmas funções que o registrador TRISA estudado anteriormente, que é
programar os pinos do PIC para serem entradas ou saídas.
O registrador PORTB funciona da mesma maneira descrita no PORTA.

PORTC
O PORTC, tal qual o PORTA, implementa diversas outras funções multiplexadas aos seus pinos, acompanhe:

PINO FUNÇÃO TIPO DESCRIÇÃO


I/O - saída - Pino de entrada e saída de uso geral / saída do oscilador timer1 / entrada
15 RC0/T10S0/T1CKI
entrada oscilador timer1 / timer3
RC1 / T10S1 / I/O - entrada - Pino de entrada e saída de uso geral / entrada do oscilador timer1 /
16
CCP2 saída módulo CCP2
17 RC2/CCP1 I/O - I/O Pino de entrada e saída de uso geral / módulo CCP2
Pino de entrada e saída de uso geral / entrada e saída do clock serial para
18 RC3/SCK/SCL I/O - I/O - I/O
o modo SPI / entrada e saída do clock serial para o modo I2C
I/O - entrada - Pino de entrada e saída de uso geral / entrada de dados SPI / entrada e
23 RC4/SDI/SDA
I/O saída de dados I2C
24 RC5/SD0 I/O - saída Pino de entrada e saída de uso geral / saída de dados SPI
I/O - saída - Pino de entrada e saída de uso geral / canal de transmissão UART / clock
25 RC6/TX/CK
I/O de sincronismo UART
I/O - entrada - Pino de entrada e saída de uso geral / canal de recepção UART / clock de
26 RC7/RX/DT
I/O sincronismo UART
Assim como no PORTA, o PORTC possui dois registradores que definem o modo de trabalho dos seus pinos de
I/O: são eles: PORTC e TRISC.
Tanto o registrador PORTC quanto o registrador TRISC estão localizados na memória RAM do PIC e pertencem
aos SFR'S (Registradores de Funções Especiais).
O registrador TRISC tem as mesmas funções que o registrador TRISA estudado anteriormente, que é
programar os pinos do PIC para serem entradas ou saídas.
O registrador PORTC funciona da mesma maneira descrita no PORTA.
As funções especiais de cada pino serão vistas quando estudarmos suas aplicações reais através dos projetos
que vamos desenvolver durante nosso curso.

PORTD
O PORTD, tal qual o PORTA, PORTB e PORTC implementa diversas outras funções multiplexadas aos seus
pinos, acompanhe:

PINO FUNÇÃO TIPO DESCRIÇÃO


19 RD0/PSP0 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
20 RD1/PSP1 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
21 RD2/PSP2 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
22 RD3/PSP3 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
27 RD4/PSP4 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
28 RD5/PSP5 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
29 RD6/PSP6 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela
30 RD7/PSP7 I/O - I/O entrada e saída de uso geral / porta de comunicação paralela

Assim como nas outras portas, o PORTD possui dois registradores que definem o modo de
trabalho dos seus pinos de I/O: são eles: PORTD e TRISD.
Tanto o registrador PORTD quanto o registrador TRISD estão localizados na memória RAM
do PIC e pertencem aos SFR'S (Registradores de Funções Especiais).
O registrador TRISD tem a função de programar os pinos do PIC para serem entradas ou
saídas.
O registrador PORTD é o "espelho" dos pinos do PORTD do PIC, para lermos ou
escrevermos nesse registrador estamos na verdade atuando sobre os pinos do port
respectivamente. Não podemos esquecer que o registrador TRISD também afeta a leitura
e escrita no port.
As funções especiais de cada pino serão vistas quando estudarmos suas aplicações reais
através dos projetos que vamos desenvolver durante nosso curso.

PORTE
O PORTE, tal qual as outras portas, implementa diversas outras funções multiplexadas aos seus pinos,
acompanhe:

PINO FUNÇÃO TIPO DESCRIÇÃO


Pino de entrada e saída de uso geral / controle de leitura do port paralelo / entrada
8 RE0/RD/AN5 I/O
analógica AD5
Pino de entrada e saída de uso geral / controle de escrita do port paralelo / entrada
9 RE1/WR/AN6 I/O
analógica AD6
Pino de entrada e saída de uso geral / controle de seleção do port paralelo / entrada
10 RE2/CS/AN7 I/O
analógica AD7

Assim como nas outras portas, o PORTE possui dois registradores que definem o modo de trabalho
dos seus pinos de I/O: são eles: PORTE e TRISE.
Tanto o registrador PORTE quanto o registrador TRISE estão localizados na memória RAM do PIC e
pertencem aos SFR'S (Registradores de Funções Especiais).
O registrador TRISE tem a função de programar os pinos do PIC para serem entradas ou saídas.
O registrador PORTE é o "espelho" dos pinos do PORTE do PIC, para lermos ou escrevermos nesse
registrador estamos na verdade atuando sobre os pinos do port respectivamente. Não podemos
esquecer que o registrador TRISE também afeta a leitura e escrita no port.
As funções especiais de cada pino serão vistas quando estudarmos suas aplicações reais através dos
projetos que vamos desenvolver durante nosso curso.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso.
Maiores informações: suporte@microgenios.com.br
Exercícios de Passagem
Manipulando as Portas de I/O

1.0 Exercício 1
1.1 Exercício 2
1.2 Exercício 3
1.3 Exercício 4

Manipulando as Portas de I/O


Conforme estudamos nesta unidade, aprendemos que para acessarmos lermos ou escrevermos um determinado dado nas
portas de entrada e saida -I/O do PIC, devemos definir o sentido de leitura ou escrita do Pino através do registrador TRIS. O
registrador PORT é o espelho do que acontece nos pinos fisicos do PIC; ao alterarmos o estado de um pino automaticamente
alteramos o estado do bit correspondente no registrador PORT.

EXERCÍCIO 1:

Conectado ao PORTB temos 8 leds ligados conforme o esquema abaixo:

Figura 01

Nossa missão neste exercício é programar o PORTB para ascender todo os leds.

Resposta:

Para ascender os leds, devemos programar o PORTB como saida, pois nosso intuito é "colocar" nivel lógico 1 (um) nos pinos
RB0 a RB7 para acendermos os leds. Logo precisaremos programar os registradores TRIS e PORT.

TRISB = 0b00000000; //configura o PORTB como saída


PORTB = 0b11111111; //envia para todos os pinos do PORTB o valor 1 , acendendo todos os leds;

OBS: todas as vezes que tivermos que ler ou escrever um determinado dado nos pinos do PIC, devemos definir sua "direção"
através do registrador TRIS.
Quando o registrador TRIS for igual a 0 (zero) estaremos programando o PORT como saída.
Quando o registraor TRIS for igual a 1 (um), estaremos programando o PORT como entrada.

EXERCÍCIO 2:

Aproveitando o mesmo circuito eletrônico do exercício anterior (figura 01), programe o PORTB somente para acender
os leds alternadamente.

Resposta:

TRISB = 0b10101010; //configura os pinos RB0, RB2, RB4, RB6 do PORTB como saída. Neste exemplo acionamos
somente os pinos pares do PORTB como saída
PORTB = 0b10101010; //envia nível lógico 1 nos pinos do PORTB cujos estão acionados em 1

EXERCÍCIO 3:

O exercício seguinte é parecido com os anteriores, sua particularidade é que os leds estão agora conectados na PORTA do
PIC. Como sabemos a PORTA possui diversas funções, entre elas a de conversão analógico / digital (A/D). A PORTA e o
PORTE após reset (por default) vem configurado todos seus pinos como A/D (com excessão do pino RA4, que não é entrada
A/D). Para acionarmos nossos leds, conforme nosso exemplo seguinte, devemos programar além do TRIS (registrador de
definição de sentido do PORT) e do PORT ( registrador "espelho" dos pinos do PIC) o registrador: ADCON1.

O registrador ADCON1 é um registrador de funções especiais SFR's cujo endereço na memória RAM é 0xFC1h. Este
registrador possui diversas funções especiais que serão estudadas mais adiante em nosso curso.

Caso queremos utilizar todos os pinos da PORTA como I/O de uso geral, devemos programar o registrador ADCON1 com o
valor 6 (seis).

Pergunta:

Conforme circuito abaixo, programe a PORTA do PIC como saída para que acenda todos os leds;

Figura 02

Resposta:

ADCON1 = 6; //configura todos os pinos da PORTA como I/O de uso geral


TRISA = 0b00000000; //configura o PORTA como saída.
PORTA = 0b00011111; //Acende os leds conectados em RA0 a RA5

obs: a emissão do registrador ADCON1 nos programas, acarreta o não acionamento adequado do PORTA e
consequentemente provoca o mal funcionamento do programa.
O pino RA4 do PIC é dreno aberto, isto implica que devemos conectar resitor de pull-up quando formos trabalhar este pino
como I/O de uso geral.
O PORTE também possui a função de conversores analógicos / digitais (A/D) também. Devemos configurar o ADCON1 para
trabalharmos o PORTE como I/O de uso geral.

EXERCÍCIO 04:
Neste próximo exercício resolvido desejamos acender os leds conectado a PORTA e ler as teclas conectadas no PORTD. Programe somente os
registradores resposáveis para execução dessa tarefa:

Circuito eletrônico:
Figura 03

Resposta:

Tirando como referência os exercícios anteriores, podemos acionar os leds e ler os estados das teclas configurando os
seguintes registradores;

TRISB = 0b00000000; //configura o PORTB como saída


TRISD = 0b00100101; //configura os pinos RD0, RD2, RD5 do PORTD como entrada
PORTB = 0b11111111; //envia para o PORTB o valor 1 em todos os pinos, acendendo todos os leds;

Dica: para lermos os estados das teclas conectado ao PORTD, conforme figura 03, podemos utilizar o comando de atribuição
de igualdade:

A = PORTD;

Sendo:

A -> variável do tipo byte de uso geral.

Através dessa instrução a variável A passa a ter no momento da execução do comando o estado do PORTD. Agora basta
analizar o byte lido e tomar as decisões necessárias no programa;

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
O Compilador mikroC
Manipulando as Portas de I/O

1.0 Introdução
1.1 Conhecendo o mikroC
1.2 Criação de um projeto no mikroC
1.3 Conhecendo o ambiente mikroC
1.4 Code Editor (editor de código)
1.5 Code Assistant (assistente de código)
1.6 Parameter Assistant (assistente de parâmetro)
1.7 Auto Correct (correção automática)
1.8 Comment / Uncomment (Comentar / não comentar)
1.9 Goto Line (vá para linha)
2.0 Messages Window (janela de mensagens)
2.1 Procedures List (Lista de procedimentos)
2.2 Project Setup
2.3 Ferramentas Integradas

Introdução
O mikroC é um compilador desenvolvido e comercializado pela empresa MikroElektronika (www.mikroe.com). Ele se consiste um sistema
integrado de desenvolvimento (IDE) para os sistema operacional Windows e suporta toda a linha de microcontroladores PIC (séries PIC12,
PIC14, PIC16, PIC18) e existe outras versões para dsPIC (dsPIC30, dsPIC33 e PIC24). Os PICs da série 14 e 17, além dos microcontroladores
da UBICOM/SCENIX (SX), não são suportados por esta versão do compilador.

Integrado ao compilador mikroC, temos diversas ferramentas que nos auxiliam no desenvolvimento de aplicações, tais como: emuladores de
Terminal Serial, ferramentas para LCD gráficos e displays de 7 segmentos, EEPROM, etc. Temos também a opção de utilizar o debugador de
código na própria IDE.

Vejamos agora as principais caracteristicas do compilador em estudo:

Grande facilidade na elaboração de programas


Grande eficiência no código gerado;
Grande diversidade de funções e bibliotecas para controle dos mais variados tipos de dispositivos, fato que o torna um dos mais práticos
compiladores C para microcontroladores PIC nos dias atuais.
Grande portabilidade de código de entre os diversos microcontroladores PIC, ou seja, é muito fácil adaptar um programa escrito em C para
outro microcontroladore de outra série PIC.
O mikroC possui seu próprio debugger que é comercializado pelo fabricante. O mikroC não é compatível com o MPLAB da Microchip.
Diverge em alguns pontos com relação ao padrão ANSI-C.

O mikroC não é um software gratuito, e é comercializado pelo seu fabricante por poucos dólares. A versão demo que iremos utilizar no
desenvolvimento do nosso curso possui limite de geração de código de 2 kbyte de programa. Ultrapassando esse limite, é necessário adquirir a
versão completa (full).

O mikroC possui diversas bibliotecas de funções prontas para o tratamento dos mais variados dispositivos. Acompanhe:

Bibliotecas de Funções do mikroC

ADC Library
CAN Library
CANSPI Library
Compact Flash Library
EEPROM Library
Ethernet Library
SPI Ethernet Library
Flash Memory Library
Graphic LCD Library
T6963C Graphic LCD Library
I²C Library
Keypad Library
LCD Library
LCD Custom Library
LCD8 Library
Manchester Code Library
Multi Media Card Library
OneWire Library
PS/2 Library
PWM Library
RS-485 Library
Software I²C Library
Software SPI Library
Software UART Library
Sound Library
SPI Library
USART Library
USB HID Library
Util Library
SPI Graphic LCD Library
Port Expander Library
SPI LCD Library
SPI LCD8 Library
SPI T6963C Graphic LCD Library

Bibliotecas ANSI C Standard

ANSI C Ctype Library


ANSI C Math Library
ANSI C Stdlib Library
ANSI C String Library

Outras bibliotecas

Conversions Library
Trigonometry Library
sprint Library
Setjmp Library
Time Library

Conhecendo o mikroC
Vamos conhecer nosso compilador mikroC

Após a instalação do compilador, é visualizado um ícone na área de trabalho denominado mikroC. Clique duas vezes nesse ícone que o
programa começa a ser carregado e apresenta a seguinte tela:

Figura 01 - O compilador mikroC

A primeira vez que instalamos o MikroC em nosso computador, sua interface é apresentado com background de cor preta. Podemos alterar esta
configuração acessando o menu Tools > options > Colors > Scheme.

Nosso compilador trabalha com a concepção de criação de projeto. Necessáriamente devemos criar um projeto para cada aplicação que venhamos a desenvolver.

A concepção de criação de projeto no mikroC é importante para que possamos incluir vários arquivos-fontes e configurações ao nosso projeto.

Criação de um projeto no mikroC


Para criarmos uma aplicações no mikroC é muito fácil, acompanhe o modelo passo a passo:

Abra o mikroC e vá ao menu Project > New Project. A tela seguinte aparece:
Figura 02 - criação de um projeto no mikroC

>> Em Project Name digite o nome do seu projeto, em Project Path.


Todos os arquivos de saida (list, hex, ASM)
terão o mesmo nome do campo Project Name.
Nota: Não crie projetos com nomes com acentuação, caracteres especiais, espaços entre caracteres, e nomes reservados pelo compilador, como
por exemplo: Button.

>> Em Devide Flag, ajuste os configurantions bits do microcontrolador,


que informam como o microcontrolador deve operar. Com ele é possível alterar o tipo de oscilador utilizado pelo projeto, assim como se vai usar
o watchdog ou acionar a proteção contra leitura do chip. Esses bits são conhecidos como "fusíveis".

>> O campo Description é opcional. Neste campo podemos descrever


de forma resumida o funcionamento de nosso projeto, assim como colocar versões ou informações adcionais que o programador ache necessário.

>> No campo Device podemos selecionar o modelo do microcontrolador PIC que utilizaremos em
nosso projeto.

>> No campo Clock especificamos o valor da frequencia de trabalho de nosso microcontrolador. No


caso de aplicações com o Kit PICgenios PIC18F, utilizaremos o valor de 8MHz (frequencia do cristal externo conectado ao chip).

O campo Device Flags é onde iremos configurar os bits de


configuração de nosso microcontrolador. Através desses bits de
configuração podemos determinar as configurações iniciais de
funcionamento de nosso microcontrolador, assim como proteger nosso
programa contra leituras, determinar os tipo de oscilador utilizado no
projeto, dentre outras. Podemos nomear e salvar as configurações dos
bits de configuração após termos configurados todos seus bits, pois
dessa forma não precisaremos reconfigurar esses bits a cada criação de
um novo projeto, basta apenas carregar as configurações já salvas
anteriormente.

Repare que cada modelo de microcontrolador possui um painel diferente


de bits de configuração. Estudaremos mais detalhado a função de cada
um dos fusíveis mais adiante em nosso curso;

Após ter configurado todos os parâmentros e campos, basta clicar no botão ok para salvar seu projeto. Pronto, nosso projeto está
pronto e podemos inserir os códigos do programa no editor.
Conhecendo o ambiente mikroC

Observe o ambiente mikroC na figura abaixo:

Figura 03

O compilador mikroC permite o desenvolvimento rápido de aplicações complexas graças aos recursos do editor avançado Code Editor. Além disso, é permitido utilizar as
bibliotecas incluídas na linguagem para aumentar a velocidade e a facilidade de desenvolvimento dos projetos como comunicação serial, displays, aquisição de dados,
memória, etc.
Após a compilação de nosso programa, o mikroC também gera arquivos LIST, código em assembly e arquivo .HEX. Integrado ao compilador, temos um debugador para
testes do programa no ambiente.
Code Editor (editor de código)

O editor de código do compilador mikroC oferece grandes auxílios no desenvolvimentos de projetos. Alguns aspectos desse editor são:

Sintaxe ajustável
Assistentes de parâmetros
Código modelo
Autocorreção para tipos comuns
Função para saltos de linhas.
Assistentes de códigos

Code Assistant (assistente de código)

O Assistente de código é uma excelente ferramenta disponível no mikroC, pois o programador não precisa editar todo o código e ainda por cima analisa o formato da
constução do comando;

Figura 04

Para acioná-lo, digite as primeiras letras e pressione CTRL + SPACE que aparecerá uma janela informando os comandos associados às letras digitadas.

Parameter Assistant (assistente de parâmetro)


O assistente de parâmetro é usado para auxiliar o programador a redigitar alguns parâmetros do programa;

Para acioná-lo, basta pressionar as teclas CTRL + SHIFT + SPACE.

Auto Correct (correção automática)

O compilador mikroC possui a opção de correção automática, corrigindo erros de sintaxe cometido pelo programador. Para corrigir os palavras é necessário que você
adicione as palavras corretas e erradas através do menu Tools > Preferences > Auto Correct. As palavras erradas serão substituídas pelas palavras corretas desde que estejam
registradas no menu > Auto Correct.

Comment / Uncomment (Comentar / não comentar)

Através dessa função você poderá facilmente adicionar linhas ou blocos de comentários no seu programa. Basta clicar com o mouse no ícone Comment / Uncomment do
Code Toolbar.

Goto Line (vá para linha)


Essa opção é aplicada para saltar de uma linha do editor de código para outra. Basta pressionar as teclas de atalho CTRL + G que aparecerá uma caixa onde deverá ser
inserida o número da linha que deseja saltar.

Figura 05

Messages Window (janela de mensagens)


Nos casos de erros encontrados durante a compilação, o compilador reporta o erro e não gera o arquivo .hex. Além disso, o Message Window informa o tamanho do código
de programa (ROM) gerado pelo sistema, assim como a memória de dados (RAM) usada pelo programa. Para visualizar a janela de mensagens, vá em View > Messages.

Figura 06

Procedures List (Lista de procedimentos)

Para visualizar a lista de todos os procedimentos utilizados pelo compilador, selecione View > Procedures List pelo menu ou prossione
CTRL + L no teclado.

Project Setup
Em Project Setup podemos alterar as configurações dos projetos criado no mikroC. Para acessá-lo pressione Project > Edit Project.

Ferramentas Integradas
O compilador MikroC possui diversas ferramentas que nos auxiliarão no desenvolvimento de nosso projeto. Dentre as ferramentas, encontramos
terminais seriais, terminais de comunicação USB, Ethernet, LCD alfanumérico e Gráfico, Bootloader, display de 7 segmentos, dentre outros.
Para ter acesso a esses painéis, acesse o menu Tools.
Figura 07
Vamos conhecer cadas uma das ferramentas do MikroC:

ASCII Chart standart


Através desse painel podemos visualizar os caracteres presente na tabela ASCII. Os caracteres ASCII são muito utilizados na grande maioria dos
programas que fazem comunicação serial e operações com displays LCD. Repare que ao passarmos o mouse sobre os caracteres, será nos
apresentado o valor do caracterer em ASCII, decimal, hexadecimal e binário.

Figura 08 - Tabela ASCII Chart

Export Code to HTML


Podemos exportar todo nosso programa desenvolvido para HTML (página simples de internet), dessa forma, podemos visualizar e
garantir a formatação e tabulação das fontes e linhas de nosso programa.

MikroBootLoader
Alguns modelos de microcontroladores PIC possui internamente uma região em sua memória de programa denominada Boot. Nesta
região podemos gravar um pequeno programa chamado BootLoader, no qual permite, via um software de PC, gravar o microcontrolador
via PC RS232 < > USART do PIC. A empresa Mikroelektronika disponibilizou o programa de Boot para diversos componentes da família
PIC16F e PIC18F, que podem ser encontrados na pasta:

c:\...\MikroeleKtronica\Mikro_c\Examples\EasyPic5\extra_examples\Bootloader
Figura 09 - MikroBootLoader

O sistema de Bootloader são altamente utilizados em equipamentos que permitem atualização de versões do programa do microcontrolador, tais
como CLP.
Nota: Existe vários programas de bootloader para microcontroladores PIC na internet. Digite bootloader PIC no google (www.google.com.br)
para conhecer um pouco melhor sobre o tema.

USART Terminal
O mikroC possui uma excelente ferramenta para recepção e envio de dados seriais via RS232, o Comunication Terminal (terminal de
comunicação). Para acessá-lo, clique no menu Tools > USART Terminal ou no ícone terminal que fica na Toolbar.

Figura 10 - Terminal Serial RS232

Para configurá-lo é muito fácil e simples; basta escolher a porta de comunicação serial através do Settings Com Port, definir a
velocidade de comunicação em Settings Baud, os bits de dados e a paridade. Após este procedimento, clique em Connect COM para que
a porta seja aberta. Todos os caracteres recebidos e enviados serão mostrados na caixa de texto maior da janela. Para enviar algum
dado, basta digitá-lo na caixa de texto ao lado do botão send e pressionar send.

Seven Segment Display Decoder (Decodificador de 7 segmentos)


O decodificador de 7 segmentos é uma ferramenta simples que permite descobrir os valores decimais ou hexadecimais correspondente
ao dígito do display desejado. Basta clicar com o mouse na imagem do display para visualizar os valores em cátodo e ânodo comum. Esta
ferramenta é muito importante quando acionamos displays de 7 segmentos por varredura.
Figura 11 - Decodificador 7 segmentos

Graphic LCD Bitmap Generator (gerador de imagem para LCD Gráfico)


Esta ferramenta do mikroC gera código hex. de uma imagem BMP para utilizar em display gráficos.

Figura 12 - Painel de display Gráfico

Sua utilização é muito simples, permite que utilizemos LCD's gráficos com controladores T6963C e com Drives KS0108. Basta clicar no
botão Load BMP Picture para carregar a imagem BMP.

LCD Custom Caracterer


Em muitas aplicações, precisamos criar novos caracteres e simbolos em nosso display LCD alfanúmerico (lcd 16x1, 16x2, 20x4, dentre outros).
Para que possamos "desenhar" estes novos caracteres, faz necessário acessarmos a área de memória editável do LCD: a CGRAM.
O compilador MikroC disponbiliza uma excelente ferramenta que irão acelerar e muito o desenvolvimento de nosso programa para esta
aplicação, pelo qual podemos ter acesso a ela via menu Tools > LCD custom character.
Figura 13 - Ferramenta de criação de um novo caracterer no LCD

Após termos desenhado nosso caracterer, podemos clicar no botão GENERATE, para que apareça uma tela com nosso código (subrotina) pronto
para inserirmos em nosso programa principal.

Outras Ferramentas:

Debugger
É possível debugar o programa compilado através do próprio mikroC. O uso do debugador é muito importante para certos aplicativos,
pois evita a necessidade de gravar um programa em uma placa de teste. Para ativar o Debugador, vá em Run > Start Debugger.

Figura 12 - Painel de debugação

Através do debugador você poderá testar o funcionamento lógico do seu programa, podendo visualizar todos os registradores, variáveis, funções
entre outras.

Janelas de estatísticas

Após a compilação do programa, é possivel ter acesso à janela de estatísticas que nos informa o tamanho do código gerado, tamanho das
rotinas, posições ocupadas pelas variáveis, memória de programa e dados, etc. Para visualizá-la, clique em View > View Statistics.
Note que a janela de estatísticas é composta por seis abas. A primeira informa o tamanho de RAM e ROM usadas pelo programa.

Informa os endereços de início e término de cada


Informa o tamanho das rotinas utilizadas no programa
rotina do programa

Informa com maiores detalhes os endereços, tamanho Informa todos os endereços das variáveis na RAM, os
das rotinas compiladas do projeto. SFR's e os GPRS.

Informa nosso programa C convertido em Assembly pelo compilador.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Criação de um projeto
Criação de um projeto no mikroC passo a passo
Vamos aprender passo a passo como criar um projeto no mikroC:

1º. O primeiro passo é abrir o mikroC;

2º. Acesse o menu Project > New Project...


3º. No menu New Project, coloque o nome do seu projeto, a pasta onde serão salvos os
arquivos-fontes do programa, escolha o microcontrolador que deseja programar, o valor da
freqüência do cristal, e configure os fusíveis do PIC selecionado.

Neste exemplo, colocamos no nome de meu_programa em nosso projeto, os


arquivos -fontes do projeto serão salvos em C:\...\Meu_programa\ , o
microcontrolador que iremos utilizar no projeto é o PIC18F452, a freqüência do
cristal utilizado é de 8 MHZ, e os fusíveis configurados são:

Oscilador -> HS (definimos o tipo de oscilador utilizado no projeto)


Watchdog -> OFF (Watchdog timer desligado)
LVP -> OFF (Low Voltagem Programming - desligado)
Brown Out -> OFF (Brown-out desligado)

4º. Clique no botão ok do painel New Project para salvar as configurações do projeto.

A tela seguinte irá aparecer, edite seu programa no Code Editor (painel de edição).

Para exemplificar copie e cole o programa abaixo no painel de edição do mikroC.

// o programa abaixo tem por função escrever no PORTD do PIC o


valor FF
void main() {

TRISD = 0x00;
PORTD= 0xff;
while (1);
}

clique aqui e baixe o programa:

5º. Salve seu programa através do ícone salvar na barra de ferramentas do mikroC e pressione o
ícone Build Project ou pressione as teclas de atalho CTRL + F9 para compilar seu
programa;

6º. Seu programa será compilado e seu resultado poderá ser visualizado através da Janela de
mensagens:

Pronto, seu projeto foi criado e compilado com sucesso; O compilador mikroC gerou na
compilação o arquivo .HEX, é este arquivo que deverá ser gravado no microcontrolador PIC.
Arquivo meu_programa.hex gerado pelo mikroC na compilação do nosso projeto

:1000000004EF00F0FFFFFFFF956AFF0E836EFFD73E
:10001000FFD7FFFFFFFFFFFFFFFFFFFFFFFFFFFF18
:020000040030CA
:0E000000FFFAFFFEFFFFFBFFFFFFFFFFFFFF0A
:00000001FF

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse
material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
A linguagem C
1.0 Introdução a Linguagem C
1.1 Formas e representação numéricas e de caracteres
1.2 mikroC: Case Insensitive
1.3 Manipulação de registradores no mikroC

Introdução a Linguagem C
Até este pondo do curso, estudamos vários tópicos fundamentais para o desenvolvimento do nosso curso, conhecemos a história dos microcontroladores,
estudamos os recursos e estruturas de hardware do PIC18F452 e aprendermos a compilar e editar um programa no compilador mikroC.

Chegou o momento de aprendermos a programar em linguagem C os microcontroladores PIC. Antes de estudarmos os conceitos da linguagem, devemos saber
que a linguagem C trata-se de uma linguagem complexa e de difícil assimilação para programadores inexperientes. Procuraremos abordar o conteúdo e
estrutura da linguagem baseando-se em exemplos de programas, pois dessa forma, ficará muito mais claro para o aluno entender a linguagem.

Acompanhe o exemplo abaixo:

//Primeiro Programa

/* Programa Pisca -Pisca (1 segundo)


Este programa tem por objetivo ascender e apagar um led conectado no pino RB0 do PIC em intervalos de 1 segundo aproximadamente;
Curso Online: Microcontroladores PIC - Programação em C
*/

void main()
{
trisb = 0; //define portb como saída
portb = 0; //coloca nível lógico 0 em todos os pinos do portb

while(1)
{
portb.f0 = 1; //seta pino RB0
Delay_ms(1000); //aguarda 1000 ms (milisegundos)
portb.f0 = 0; //resseta pino RB0
Delay_ms(1000); // aguarda 1 segundo
}
}

Vejamos o significado de cada linha de programa:

A primeira linha de programa:

//Primeiro Programa

É chamada de comentário. Os comentários são importantes para documentar o programa. Podemos adicionar comentários em nosso programa de duas
maneiras:

Através de comentários de linhas simples: quando queremos comentar apenas uma linha de programa, iniciamos os comentários com os caracaters " // " (igual a
linha do programa anterior). Esses tipo de comentário não faz parte da padronização ANSI original, mas atualmente é encontrado em vários compiladores.

Através de comentários de múltiplas linhas: podemos comentar linhas ou blocos de código de programa utilizando a seqüência de caracteres "/* " para iniciar o
comentário e a seqüência "*/" para terminar o comentário.

/* Programa Pisca -Pisca


Este programa tem por objetivo ascender e apagar um led conectado no pino RB0 do PIC em intervalos de 1 segundo aproximadamente;
Curso Online: Microcontroladores PIC - Programação em C
*/

Na próxima linha temos:

void main()

A declaração main()especifica o nome da função. A função main(), na linguagem C, é sempre a primeira a ser executada e deve ser a única no
programa. O programa termina quando for encerrada a execução da função main().

Uma função, em C, nada mais é do que um conjunto de instruções que pode ser executada a partir de qualquer ponto do programa. Utilizamos o sinal
de abertura de chave "{" para iniciar uma função e o sinal de fechamento de chave "}" para finalizar a função. Todas as instruções devem estar dentro
das chaves que iniciam e terminam a função e são executadas na ordem em que as escrevemos

No caso deste programa exemplo, ela não recebe nenhum parâmetro e também não retorna parâmetro nenhum. Isto fica explícito através da palavra-
chave void escrita na frente do programa.
As funções e as suas características serão apresentadas em detalhes mais adiante em nosso curso;

Na próxima linha de programa encontramos:

trisb = 0; //define portb como saída


portb = 0; //coloca nível lógico 0 em todos os pinos do portb

Como estudamos nas unidades anteriores, o comando trisb define o sentido de acionamento do PORTB do PIC. Neste exemplo, trisb = 0, logo o PORTB
foi programado como saída.
O comando portb = 0 coloca nivél lógico 0 em RB0 a RB7. As instruções C são sempre encerradas por um ponto-e-vírgula ( ; ). O ponto-e-vírgula é
parte da instrução e não um simples separador e devemos ao final de cada instrução colocar o acréscimo de um ponto-vírgula “;”.

Na próxima linha temos:

while(1)
Este é um comando de controle utilizado na repetição de um determinado bloco de instrução. O bloco de instrução será executado repetidamente
enquanto o condição for verdadeira, ou seja, for diferente de zero. No nosso exemplo, o valor 1 utilizado no comando while garante que a condição seja
sempre verdadeira. Estudaremos mais sobre o comando while mais adiante no curso;

Os comandos a seguir pertencem ao bloco da instrução while;


{
portb.f0 = 1; //seta pino RB0
Delay_ms(1000); //aguarda 1000 ms (milisegundos)
portb.f0 = 0; //resseta pino RB0
Delay_ms(1000); // aguarda 1 segundo
}

Como não temos nenhum comando que interrompa o laço while, os blocos de comandos apresentados serão executados indefinitamente até que o
processador seja desligado ou reiniciado.

A operação portb.f0 = 1 faz com que o pino RB0 do PORTB seja colocado em nivel lógico 1; Para se referir a um pino das portas do PIC, PORTA,
PORTB, PORTC, PORTD, PORTE, devemos apresentar o nome do porta, mais o ponto ".", mais a inicial "f" e o número do pino correspondente.

Exemplo:

portb.f0 = 0; // estamos nos referindo ao pino RB0 do portb


portd.f5 = 1; // estamos nos referindo ao pino RD5 do portd
porte.f1 = 1; // estamos nos referindo ao pino RE1 do porte

A linha de programa seguinte, Delay_ms(1000), é uma função interna do compilador mikroC utilizada para gerar atrasos em escala de milissegundos. No
nosso exemplo, o comando irá gerar atraso de 1000 milessegundos, ou seja, 1 segundo.

A operação portb.f0 = 0 faz com que o pino RB0 do PORTB seja colocado em nível lógico 0;

Desta forma, ao programarmos o PIC com o programa exemplo, o pino RB0 ficará mudando de estado lógico 1 e 0 a cada 1 segundo,

Coloque em prática este pequeno projeto. Copie e compile este programa exemplo no compilador mikroC, isso lhe ajudará nos seus estudos.

Vamos estudar mais um programa:

Acomanhe:

//Segundo Programa

/* Programa Pisca -Pisca (100 milisegundos)


Este programa tem por objetivo ascender e apagar um led conectado no pino RB0 do PIC em intervalos de 1 segundo aproximadamente;
Curso Online: Microcontroladores PIC - Programação em C
*/

void main()
{
trisb = 0; //define portb como saida
portb = 0; //coloca nível lógico 0 em todos os pinos do portb

trisd = 0; //define portb como saida


portd = 0; //coloca nível lógico 0 em todos os pinos do portb

while(1)
{
portb = 255; //seta todos os pinos do portb
portd = 255; //seta todos os pinos do portd
Delay_ms(100); //aguarda 1000 ms (milisegundos)
portb = 0; //resseta todos os pinos do portb
portd = 0; //resseta todos os pinos do portd
Delay_ms(100); //aguarda 1000 ms (milisegundos)
}
}

O programa acima tem por objetivo piscar infinitamente o portb e portd do PIC em intervalos de 100 milissegundos. Suas caracteristicas são parecidas com a
do programa anterior, sua única diferença esta no tratamento das portas;

portb = 255; //seta todos os pinos do portb


portd = 255; //seta todos os pinos do portd

Formas e representação numéricas e de caracteres


No compilador mikroC podemos manipular dados do tipo: decimal, binário, hexadecimais e octal.

Podemos representar um valor numérico de diversas formas. Para exemplificar, vamos supor o que desejamos carregar o valor 187 no registrador PORTB no
PIC utilizando o mikroC, acompanhe:

Representação decimal:

Para representarmos um número em decimal basta colocar seu valor sem nenhuma abreviatura, conforme o linha abaixo:

portb = 187; //representação decimal

ATENÇÃO: NUNCA DEVEMOS REPRESENTAR UM NÚMERO DECIMAL INICIANDO COM 0 (ZERO), POIS O MIKROC INTERPRETARÁ O NÚMERO COMO OCTAL.

EX:
portb = 25 (25 é representado em decimal)
é diferente de
portb = 025 (025 é representado em octal)

Representação Hexadecimal:

Para representar um número em hexadecimal devemos colocar o prefixo 0x (ou 0X) antes do número hexadecimal. (0 a F)

portb = 0xBB; //representação hexadecimal do numero 187 decimal


ou
portb = 0Xbb; //representação hexadecimal do numero 187 decimal

Representação binária:

Para representarmos um número em binário devemos colocar o prefixo 0b (ou 0B) antes do número em binário.

portb = 0b10111011; //representação binária do número 187 decimal


ou
portb = 0B10111011; //representação binária do número 187 decimal

A representação binária de um número ocorre da seguinte forma:


0 B 1 0 1 1 1 0 1 1
- - RB7 RB6 RB5 RB4 RB3 RB2 RB1 RB0
- - MSB LSB

Observe que na representação binária de um número, o bit mais significativo fica ao lado do prefixo 0b. Não confunda de forma alguma a construção e as
representações de um número binário, caso contrário seu programa não funcionará corretamente.

Representação octal:

O sistema octal não é um sistema muito difundido nos dias atuais. Apesar do compilador mikroC suportar esse tipo de representação numérica, somente
devemos utilizá-la quando for realmente necessário.

Para representar um número octal é necessário colocar o prefixo 0 (zero) antes do valor numérico octal.

portd = 0273; //representação octal do número 187 em decimal

Atenção: Não confundir números decimais com octais. Números Octais são precedidos com 0, ex: 0140 é um número octal. Números decimais não é

tem prefixo: ex: 140 é um número decimal

Exercícios 01:

Gostaríamos de enviar aos pinos do PORTB os seguintes estados;

RB0 = 0
RB1 = 1
BR2 = 1
RB3 = 0
RB4 = 0
RB5 = 1
RB6 = 0
RB7 = 0

Qual o valor numérico que devemos colocar na instrução abaixo para representar os estados dos pinos do PORTB. Obedeça as representações numéricas
solicitadas:

Representação decimal:
portb = X; ?

Representação binária:
portb = X; ?

Representação hexadecimal:
portb = X; ?

Representação octal:
portb = X; ?

obs: A respostas desse exercício estão disponível no final dessa matéria!

mikroC : Case Insensitive

Diferente do pradrão ANSI C, No mikroC podemos utilizar caracteres maiúsculos e minúsculos no programa (case insensitive). Acompanhe:

contador, Contador e COnTadOR


são interpretados como sendo a mesma palavra. (esse recurso é uma particularidade do mikroC e não pertence ao padrão ANSI C Standart).

Manipulação de registradores bit no mikroC


Podemos manipular os bits de registradores do PIC de diversas formas no compilador mikroC

Vejamos:

Exemplo de Manipulação de bits de registradores:

Podemos manupular os bits dos registradores do PIC da seguinte maneira:

PORTA.F5 = 0; // -->> faz referência ao pino RA5 do PIC.


PORTD.F7 = 1; //-->> faz referência ao pino RD7 do PIC.
TRISB.RB0 = 1; // -->> faz referencia ao bit 0 do registrador TRISB
T0CON.PSA = 0; //-->> faz referência ao bit 3

Ou acessá-los dessa maneira:


obs: não são todos os registradores que permitem acesso direto dos seus bits:

RB0 = 0; --->> faz referência ao pino 0 do PORTB


RA2 = 1; -->> faz referência ao pino 0 do PORTA

Respostas dos exercícios 01:

Representação decimal:
portb = 38;

Representação binária:
portb = 0b00100110;

Representação hexadecimal:
portb = 0x26;

Representação octal:
portb = 046;

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Estrutura da Linguagem C
1.0 Estrutura de um programa em C
1.1 Protótipos de Funções

Estrutura de um programa em C
Todo programa escrito em C consiste em uma ou mais funções, tendo como poarticularidade
deste fato a possibilidade de construir programas modulares e estruturados. O programa
principal escrito em C é uma função. O C é uma linguagem estremamente estruturada e exige
do programador domínio adequado de tal conceito. Veja a seguir, o menor programa possível
de ser escrito em C:

main( )

em que:

main() - é a primeira e principal função a ser executada

{ - inicia ou abre o corpo da função

} - finaliza ou fecha o corpo da função

A função main() é a principal instrução a ser considerada em um programa escrito na linguagem C e deve estar
presente em algum lugar do programa, pois é ela que marca o ponto de inicialização do processo de execução do
programa.
A seguir veremos um modelo de escrita de um programa em C, com todas as definições e comentários.
Acompanhe:

[ blocos de comentários de grande importância na documentação do programa]

[<definições de pre-processamento - cabeçalhos>]


[<declaração das variáveis globais>]

[<tipo>] nome_da_funcao([<parâmetros>])
[<declaração de parâmentros>]

/*
Este trecho é reservado para o corpo da função_nome, com a declaração de suas variáveis locais, seus comandos
e funções de trabalho. Esta função pode ser chamada de sub-rotina do programa.
*/

[return ou return() ou return(valor)]


}

void main([<parâmetros>])
{

/*
Este trecho é reservado para o corpo da função, com a declaração de suas variáveis locais, seus comandos e
funções de trabalho. Aqui estão os primeiros comandos que serão executados no programa.
*/

Toda a informação situada entre colchetes "[" e "]" indica informações que podem ou não estar presentes em um
programa.

Vamos comentar cada bloco de programa:

O primeiro bloco: Os comentários gerais

[ blocos de comentários de grande importância na documentação do programa]

O programador poderá inserir ou não o comentários em seu programa. Por convenção, é importante colocarmos
sempre os cabeçalhos nos programas, pois é a apresentação do programa.

O segundo bloco: O cabeçalho

[<definições de pre-processamento - cabeçalhos>]


[<declaração das variáveis globais>]

Neste trecho do programas podemos declarar todo o cabeçalho do nosso programa assim como definir variáveis
globais que poderão ser utilizadas em todas as funções do nosso programa.

Exemplo de cabeçalho:

#include "minhas_funcoes.h" // incluimos em nosso programa as bibliotecas de funções que estão no arquivo
minhas_funcoes.h.
int a ; //definimos a variável a como inteiro e como sendo global
int b; //definimos a variável b como inteiro e como sendo global

O terceiro bloco: As funções de subrotinas

[<tipo>] nome_da_funcao([<parâmetros>])
[<declaração de parâmentros>]

/*
Este trecho é reservado para o corpo da nome_da_funcao, com a declaração de suas variáveis locais, seus
comandos e funções de trabalho. Esta função pode ser chamada de sub-rotina do programa.
*/

[return ou return() ou return(valor)]

}
Este último bloco trata-se de uma função que carrega o nome nome_da_funcao. Seu tipo pode ou não ser
explicitado. Caso não seja, qualquer função será do tipo int por default.
Esta função não faz parte da função main() e deverá ser chamada em algum momento pelas função main() ou por
outra função para seja executados seus comandos. Podemos no momento da chamada da função enviar
parâmetros (estudaremos mais sobre as funções a seguir).
No final da função, encontramos o comando return, cujo objetivo é retornar a função que a chamou um valor de
retorno da função.
Não é obrigatório o uso do comando return, caso não precisemos retornar nenhum valor na função.

Exemplo de função de subrotina:

void funcao(void) {
portb = ~portb;
return;
}

O quarto bloco: A função main()

void main([<parâmetros>])
{

/*
Este trecho é reservado para o corpo da função, com a declaração de suas variáveis locais, seus comandos e
funções de trabalho. Aqui estão os primeiros comandos que serão executados no programa.
*/

A função main() será a primeira a ser executada pelo processador. Junto a função main(), temos o modificador de
tipo void que tem a função de declarar que a função main() não deve retornar nenhum valor.
A manipulação de programas em linguagem C para microcontroladores é diferente de trabalharmos com
programas para PC's, pois nos computadores PC's existem o sistema operacional que receberá o retorno da função
main(). Nos microcontroladores, como não possuimos um sistema operacional rodando em paralelo com o
programa, não podemos retornar nenhum valor na função main(). Para este caso, devemos sempre iniciar a
função main() com o modificador de tipo nulo void.

Exemplo de função main():

void main() {
PORTB = 0;
TRISB = 0;
PORTB = ~PORTB;
Delay_ms(1000);
}

INFORMAÇÕES IMPORTANTISSIMAS:
AS FUNÇÕES DE SUBROTINAS DEVEM SER COLOCADAS ANTES DA FUNÇÃO MAIN(), CASO
CONTRÁRIO OCORRERÁ ERRO DE COMPILAÇÃO DO PROGRAMA. ISSO OCORRE PORQUE não
podemos usar uma função sem declará-la previamente. PARA COLOCARMOS FUNÇÕES
ABAIXO DA FUNÇÃO MAIN() FAZ NECESSÁRIO UTILIZAR OS PROTÓTIPOS DE FUNÇÕES, CUJO
OBJETIVO É MOSTRAR AO COMPILADOR O TIPO, NOME E PARÂMETROS DA FUNÇÃO DAS
SUBROTINAS.
Exercício 01:

Vamos análisar um exemplo de programa em linguagem C disponível no próprio compilador mikroC como
exemplo. Em princípio, não quero que você entenda o funcionamento do programa, mas quero que você
identifique os principais blocos de funções desse programa. Vejamos:

/*
* Project name:
PWM_Test_01 (PWM library Demonstration)
* Copyright:
(c) MikroElektronika, 2005.
* Test configuration:
MCU: P18F8520
Dev.Board: BIGPIC3
Oscillator: HS, 10.0 MHz
Ext. Modules: -
SW: mikroC v5.00
* NOTES:
None.
*/
unsigned short j, oj;

void InitMain() {

PORTB = 0;
TRISB = 0;

ADCON1 |= 0x0F;
PORTA = 255;
TRISA = 255;

PORTC = 0x00;
TRISC = 0;
Pwm_Init(5000);
}//~

void main() {
initMain();

j = 127;
oj = 0;
Pwm_Start();

while (1) {
if (Button(&PORTA, 0,1,1))
j++ ;
if (Button(&PORTA, 1,1,1))
j-- ;

if (oj != j) {
Pwm_Change_Duty(j);
oj = j;
PORTB = oj;
}
Delay_ms(200);
}
}//~!
Resposta:

Primeiro bloco: Os comentários

/*
* Project name:
PWM_Test_01 (PWM library Demonstration)
* Copyright:
(c) MikroElektronika, 2005.
* Test configuration:
MCU: P18F8520
Dev.Board: BIGPIC3
Oscillator: HS, 10.0 MHz
Ext. Modules: -
SW: mikroC v5.00
* NOTES:
None.
*/

Este bloco do programa são comentários e são resposável pela parte "documental do programa". No momento da
compilação, esses textos serão descartados pelo compilador.

Segundo Bloco: O cabeçalho

unsigned short j, oj;

Neste trecho do programas é definido o cabeçalho do programa, e nele foi declarado as variável globais do
programa.

Terceiro Bloco: as subrotinas

void InitMain() {

PORTB = 0;
TRISB = 0;

ADCON1 |= 0x0F;
PORTA = 255;
TRISA = 255;

PORTC = 0x00;
TRISC = 0;
Pwm_Init(5000);
}//~

Esta função é uma subrotina do programa, e foi chamada pelo programador de InitMain();

Quarto Bloco: as função main()


void main() {
initMain();

j = 127;
oj = 0;
Pwm_Start();

while (1) {
if (Button(&PORTA, 0,1,1))
j++ ;
if (Button(&PORTA, 1,1,1))
j-- ;

if (oj != j) {
Pwm_Change_Duty(j);
oj = j;
PORTB = oj;
}
Delay_ms(200);
}
}//~!

Apesar do função main() ser a última na ordem de escrita do programa, ela será a primeira função a ser executada
pelo processador.

Dicas:

Porque usar funções de subrotinas ?

Para permitir o reaproveitamento de código já construído(por você ou por outros


programadores);
Para evitar que um trecho de código que seja repetido várias vezes dentro de um mesmo
programa;
Para permitir a alteração de um trecho de código de uma forma mais rápida. Com o uso
de uma função é preciso alterar apenas dentro da função que se deseja;
Para que os blocos do programa não fiquem grandes demais e, por conseqüência, mais
difíceis de entender;
Para facilitar a leitura do programa-fonte de uma forma mais fácil;
Para separar o programa em partes(blocos) que possam ser logicamente compreendidos
de forma isolada.

voltar para o topo da página

Protótipos de Funções
Não podemos usar uma função sem declará-la previamente. Trata-se duma instrução
geralmente colocada no início do programa ou do arquivo, obrigatoriamente antecedendo
a definição e a chamada da função. O protótipo informa ao compilador o tipo que a função
retorna, o nome da função, bem como os parâmetros que ela recebe. Eis um exemplo:
void minha_rotina (); //protótipo de função, esta linha de programa deve ser colocada no
topo do programa , junto com as declarações;
//....

void main()
{
// aqui esta meu programa principal
}

//***************** função de subrotina ********************


void minha_rotina()
{
//aqui esta os comandos da sua subrotina
}

Confome podemos ver no programa acima, a função de subrotina minha_rotina() ficou em


depois da função main(), nesse caso, necessariamente, devemos colocar o protótipo de função
(linha de programa com o cabeçalho da função de subrotina) no topo do programa, caso
contrário teremos erro de compilação pois o não podemos utilizar nenhuma função antes de
declará-la. No caso de colocarmos as funções de subrotinas antes da função main() no
programa, não precisamos declarar os protótipos de funções no programa.

voltar para o topo da página

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse
material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Variáveis e Tipos de Dados
1.0 Os indetificadores
1.1 Tipos de dados
1.1 Modificadores de Tipo
1.1 Declaração de variáveis
1.1 Variáveis globais:
1.1 Variáveis locais:

Os indetificadores
A linguagem C define identificadores como sendo nomes usados para se fazer referência a
entidades do programa (variáveis, funções, rótulos, etc.) definidas pelo programador. Em
C, um identificador é composto de um ou mais caracteres, sendo que, para identificadores
internos, os 31 primeiros são significativos. O primeiro caractere deve ser uma letra ou
um sublinha (_) e os caracteres subseqüentes devem ser letras, números ou sublinhas.
Eis aqui alguns exemplos de identificadores corretos e incorretos:

Corretos Incorretos descrição


Um identificador deve sempre começar com uma
cont 1cont
letra do alfabeto, nunca por um número.
Um identificador não pode conter
valor23 alô
acentos (´, ^, ~, )
Um identificador não pode conter
totalGeral valor-total
simbolos (-)

Isto quer dizer que se duas variáveis têm em comum os 31 primeiros caracteres e diferem
apenas a partir do trigésimo segundo, o compilador C não será capaz de distingui-Ias. Por
exemplo, esses dois identificadores são iguais:

isto_e_um_exemplo_de_um_nome_longo
isto_e_um_exemplo_de_um_nome_longo_tambem

Tipos de dados

Existem cinco tipos primitivos de dados em C suportados no mikroC: caractere, inteiro,


ponto flutuante (float e double) e sem valor. As palavras reservadas usadas para declarar
variáveis desse tipo são char, int, float/double e void, respectivamente. A tabela abaixo
representa o valor e a escala de cada tipo de dado em C.

TAMANHO
TIPO INTERVALO
EM BITS
char 8 0 a 255
int 8 0 a 255
3.4E-38 a
float 32
3.4E+38
nenhum
void 0
valor

O tipo char é utilizado para representação de caracteres ASCII de 8 bits. Cada variável do
tipo char pode representar um caractere ASCII. O tipo int é utilizado para representar
números inteiros de 8 bits (0 a 255). Estes tipos de dados são amplamente usados em
programação C.
O tipo char e int representam números inteiros e não podem ser utilizados para
representar números frácionários. Para isso, deve ser utilizado o tipo float, também
chamado de ponto flutuante.
O tipo float deve ser evitado ao máximo e restrito apenas às operações que
realmente necessitarem de um tipo de dados como este.

Modificadores de Tipo
Podemos utilizar comandos especiais do C para obter outros tipos de dados. Esses
comandos especiais são chamados de modificadores de tipo e são os seguintes:

signed, unsigned, short, e long.

O modificador de tipo signed pode ser utilizado para modificar um tipo base de dados
para que ele possa representar um número positivo ou negativo;
O modificador short é utilizado para definir uma variável com tamanho menor que o tipo
modificado, ou seja, uma versão reduzida do tipo especificado.
O modificador long é utilizado para ampliar a magnitude de representação do tipo
especificado.
TIPO TAMANHO RANGER

(unsigned )
8 -bit 0 à 255
char
signed char 8 -bit -128 à +127
(signed)
8 - bit -128 à +127
short (int)
unsigned
8 - bit 0 à 255
short (int)
(signed) int 16 - bit -32768 à 32767
unsigned
16 - bit 0 à 65535
int
(signed) -2147483648 à
32 - bit
long int 2147483647
unsigned
32 - bit 0 à 4294967295
long int
+/-
1.17549435082E-
float 32 - bit
38 à +/-
6.80564774407E38
+/-
1.17549435082E-
double 32 - bit
38 à +/-
6.80564774407E38
+/-
1.17549435082E-
long double 32 - bit
38 à +/-
6.80564774407E38
void nulo sem valor
Obs: tipos de float, double e long double são considerados do mesmo tipo no
mikroC.
Declaração de variáveis

Definir uma variável é criá-la na memória (alocá-la), dar a ela um nome e especificar o
tipo de dado que nela vai armazenar.

Variável, do ponto de vista da área de programação, é uma região de memória


previamente identicada que tem por finalidade armazenar os dados ou informações de um
programa por um determinado espaço de tempo. Uma variável llimita-se a armazenar
apenas um valor por vez.

Sintaxe para criação de uma variável em C;

<tipo> nome_da_variavel;

O tipo deve ser um tipo de dado válido em C tais como: char, int, float, com ou sem seus
modificadores, unsigned, signed, short e long. E nome_da_variavel é o nome da
variável adotada pelo programador.

Em C, podemos declarar várias variáveis do mesmo tipo em um única linha de programa,


bastando apenas separá-las por vírgulas, acompanhe:

int soma ;
unsigned char i,j,k ;
float salário;
unsigned int idade;
short int y;
long caminho, estado;
unsigned valor;

Outro aspecto importante da declaração das variáveis é o local onde são declaradas.

Basicamente, uma variável pode ser declarada em dois pontos distintos do programa:

Variáveis globais:
No corpo principal do programa: variáveis declaradas no corpo principal do programa são
variáveis globais que podem se acessadas por qualquer função do programa. Essas variáveis
estão fora de qualquer função do programa, inclusive a função main().
Exemplo:

int contador; //define a variável global "contador" como inteiro


char a; //define a variável global "a" como char

void main() //função principal do programa


{

contador = contador + 10

Repare que as variáveis "contador" e "a" forma definidas fora de qualquer função do programa,
essas variáveis serão tratadas como variáveis globais e seus dados poderão ser manipulados
por qualquer função do programa;

Vamos apresentar mais um exemplo de programa utilizando as variáveis globais:

int contador; //define a variável global "contador" como


inteiro
char a; //define a variável global "a" como char

void subrotina () //função de sub-rotina qualquer do programa


{
contador = contador - 20;
a = 100;
}

void main() //função principal do programa


{
contador = contador + 10;
a = 55;
}

Repare no programa exemplo acima que as variáveis "contador" e "a" estão sendo manipuladas
no corpo da função main() e na função subrotina(). Este tipo de manipulação de variáveis
somente é possível se declararmos como sendo do tipo GLOBAIS, ou seja, necessáriamente
devemos defini-las no corpo do programa (fora de qualquer função do programa, inslusive a
função main()).

Variáveis locais:

Ao contrário das variáveis globais, uma variável local somente existe dentro da função em que
foi declarada. Isto significa que uma variável local somente existe enquanto a função esta
sendo executada. No momento que ocorre o retorno da função, as variáveis locais são
descartadas;

Acompanhe o exemplo abaixo:

void main() //função principal do programa


{
int contador; //define a variável local "contador" como int
contador = contador + 10
}

Repare que a variável "contador" foi declarada (criada) dentro da função main(), as variável
locais somente terá validade dentro da função que a declarou, neste caso, "contador" somente
poderá ser manipulada no programa dentro da função main().
Acompanhe mais um exemplo de variável local:

void subrotina () //função de sub-rotia qualquer do programa


{
int tempo;
tempo = tempo * 2;
}

void main() //função principal do programa


{
int tempo;
tempo = tempo / 2;
}

Repare no programa exemplo acima que temos duas funções, a função main() e a função
subrotina(). Dentro de cada uma dessas funções foram criada uma variável chamada "tempo"
do tipo int, essas variáveis são locais e somente tem validade dentro da função que a declarou,
ou seja, a variável "tempo" da função subrotina() NÃO tem nenhuma ligação com a variável
"tempo" da função main(), pois são variáveis locais;

Se por ventura cometermos o seguinte erro abaixo, acompanhe:

void subrotina () //função de sub-rotia qualquer do programa


{
tempo = tempo * 2;
}

void main() //função principal do programa


{
int tempo;
}

Repare que a variável "tempo" foi declarada unicamente dentro da função main() e que seus
dados estão sendo manipuladas por outra função chamada subrotina(). Ao compilarmos este
programa, certamente o compilador apresentará um erro de compilação nos informando que a
variável "tempo" não foi definida. Esse é um erro comum que muitos programadores
inexperientes cometem na linguagem C, pois esquecem que uma variável local somente tem
validade dentro da função que a declarou.

Podemos declarar variáveis como parâmetros formais de uma função, que são também tratadas
como variáveis locais. Iremos estudar esse tipo de variável quando estivermos falando de
funções em nosso curso;

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse
material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Exercícios Resolvidos
Programando os bits de configuração do PIC

1.0 Exercício 1
1.1 Exercício 2
1.2 Exercício 3

Estudando a estrutura de um programa em C

1.3 Exercício 4
1.4 Exercício 5
1.5 Exercício 6
1.6 Exercício 7

Programando os bits de configuração do PIC


EXERCÍCIO 1:
Pergunta:

Analise a figura abaixo e descreva como foi configurado os bits configurations (fusiveis) do PIC.
Comente somente os bits que estão ativos no painel de controle:

Resposta:

Devemos programar corretamente os bits de configurações do PIC, caso contrário nosso


programa não funcionará corretamente ou não funcionará definitivamente. Podemos
configurar esses bits de configuração no momento ou durante a criação do projeto no
compilador mikroC. O gravador de PIC WinPic800 permite que alteramos os bits de
configuração antes de gravarmos nosso programa no microcontrolador PIC.
Podemos ver que:

O bit HS esta selecionado no nosso painel (figura acima), isso comprova que
nosso microcontrolador PIC será acionado por oscilador exteno do tipo cristal ou
ressonador (pois bit OSCSEN não esta habilitado).

O bit BOREN esta acionado e programado para provocar reset no PIC quando a
tensão cair abaixo de 2.0V (verifique o valor da tensão selecionado).

O watchdog esta ativado (WDTEN ativo) com prescaler de 1:128, o que nos
resulta tempo de 2304us para ocorrer o reset.

Os restantes dos bits de configuração estão desativados.

EXERCÍCIO 2:

Pergunta:

Faça uma análise apurada dos estados dos bits de configuração do PIC descrevendo
as funções de cada bit que esteja ativado:

Resposta:

O microcontrolador selecionado no menu é o PIC18F442 (no winpic, o sistema


de gravação é o mesmo para o PIC18F442 e para o PIC18F4439);
O oscilado utilizado é o cristal ou ressonador. O bit HS w/PLL ON ativa o modo PLL
do PIC, fazendo com que o valor do tempo de oscilação seja multiplicado por 4.
O bit de configuração PWRTEN esta habilitado, fazendo com que o PIC ative o
temporizador de power-up, responsável em fazer com que o chip permaneça em reset por
aproximadamente 72ms após o chip ter sido ligado.
O bit de configuração do BROWN-OUT esta ativo e programado para ser acionado para tensões
abaixo de 4,5V, ou seja, quando a tensão de alimentação do PIC ser inferior a 4.5V o BROWN-
OUT é acionado e mantem o PIC resetado.
Watchdog timer esta ativado graças ao bit WDTEN, e ajustado com prescaler de 1:8.
O bit CCP2MX esta ativado, fazendo com que o modo CCP2 do PIC seja acionado através do
pino RB3.
O bit do modo LVP esta ativado. Este bit afeta o modo de gravação de baixa tensão do PIC.
STVREN esta ativado, isto signigica que nosso PIC esta programado para resetar no caso da pilha
"estourar", ou seja, ultrapassar sua capacidade máxima de 31 endereços.
O modo de proteção contra leitura CPB, protege a região de boot (memória de programa ) do PIC;
O bit de configuração WRTD aciona o modo de proteção contra escrita na EEPROM do PIC.
Os restantes dos bits de configuração estão desativados.

Estudando a estruturas de programas em C

EXERCÍCIO 3:

Pergunta:

Separe e comente cada bloco do programa exemplo abaixo identificando as 4


principais divisões: Comentários, cabeçalho, subrotinas e programa principal:

Obs: o exemplo de programa que apresentamos vem na instalação do mikroC. O


objetivo desse exercício é fazer com que você se acostume a estudar através dos mais
variádos códigos nas mais variádas linguas, pois nessa aréa de microcontroladores as
informações são restritas e precisamos nos acostumar com os exemplos que
encontramos, muita das vezes na internet.

/*
* Project name:
Seg2Cif_Static (Usage of 7Seg displays)
* Copyright:
(c) Mikroelektronika, 2005.
* Description:
This code demonstrates how to display a static number on two 7-
segment
displays (common cathode), in multiplex mode. Both displays are
connected
to PORTB (RB0..RB7, segment A to RB0, segment B to RB1, etc), with
refresh
via pins of porta (RA0,RA1).
This code also demonstrates how to use interrupts.
* Test configuration:
MCU: PIC16F877A
Dev.Board: EasyPIC3
Oscillator: HS, 08.0000 MHz
Ext. Modules: -
SW: mikroC v6.0
* Notes:
None.
*/

unsigned short v, por1, por2;

void interrupt() {
if (v==0) {
PORTB = por2; // send mask for digit 3 to portb
PORTA = 1; // turn on 1st 7 seg., turn off 2nd
v = 1;
} else {
PORTB = por1; // send mask for digit 8 to portb
PORTA = 2; // turn on 2nd 7 seg., turn off 1st
v = 0;
}
TMR0 = 0; // clear TMRO
INTCON = 0x20; // clear TMR0IF and set TMR0IE
}//~

void main() {

trisC = 0;
OPTION_REG = 0x80; // pull up resistors
PORTA = 0; // clear porta (make sure both displays are
off)
TRISA = 0; // designate porta pins as output
PORTB = 0; // clear portb (make sure LEDs are off)
TRISB = 0; // designate portb pins as input
TMR0 = 0; // clear TMRO
por1 = 0x7F; // mask for 8 (7 seg. display)
por2 = 0x4F; // mask for 3 (7 seg display)
portC = 2;
INTCON = 0xA0; // enable T0IE
// wait for interrupt
}//~!

Resposta:

Acompanhe as divisões dos blocos passo a passo e vejam como um programa em C é


estruturado:

Primeiro bloco: Os comentários

/*
* Project name:
Seg2Cif_Static (Usage of 7Seg displays)
* Copyright:
(c) Mikroelektronika, 2005.
* Description:
This code demonstrates how to display a static number on two 7-
segment
displays (common cathode), in multiplex mode. Both displays are
connected
to PORTB (RB0..RB7, segment A to RB0, segment B to RB1, etc), with
refresh
via pins of porta (RA0,RA1).
This code also demonstrates how to use interrupts.
* Test configuration:
MCU: PIC16F877A
Dev.Board: EasyPIC3
Oscillator: HS, 08.0000 MHz
Ext. Modules: -
SW: mikroC v6.0
* Notes:
None.
*/

Este bloco não é obrigatório, mas sempre é importante colocarmos em nosso


programa pois trata-se da apresentação do funcionamento e dados do projeto:

Segundo Bloco: O cabeçalho

unsigned short v, por1, por2;

No cabeçalho apresentamos as variáveis globais do programa, assim como as chamadas de inclusão de


bibliotecas defunções, definições do compilador e protótipos de funções.

Terceiro Bloco: As funções de Sub-rotinas

void interrupt() {
if (v==0) {
PORTB = por2; // send mask for digit 3 to portb
PORTA = 1; // turn on 1st 7 seg., turn off 2nd
v = 1;
} else {
PORTB = por1; // send mask for digit 8 to portb
PORTA = 2; // turn on 2nd 7 seg., turn off 1st
v = 0;
}
TMR0 = 0; // clear TMRO
INTCON = 0x20; // clear TMR0IF and set TMR0IE
}//~

Esta é a única função de sub-rotina do nosso programa exemplo e é chamada de interrupt(), A função
interrupt() é do tipo void, que representa que esta função não possui retorno.

Quarto Bloco: Função Principal main().

void main() {

trisC = 0;
OPTION_REG = 0x80; // pull up resistors
PORTA = 0; // clear porta (make sure both displays are
off)
TRISA = 0; // designate porta pins as output
PORTB = 0; // clear portb (make sure LEDs are off)
TRISB = 0; // designate portb pins as input
TMR0 = 0; // clear TMRO
por1 = 0x7F; // mask for 8 (7 seg. display)
por2 = 0x4F; // mask for 3 (7 seg display)
portC = 2;
INTCON = 0xA0; // enable T0IE
// wait for interrupt
}//~!

Esta é a função main() do programa , esta é a primeira função que será executada pelo
processador do PIC.

EXERCÍCIO 4:

Pergunta:

Suponhamos que temos precisamos criar uma variável no programa que assumirá valores de 0 a 100
durante toda a execução do programa, utilizaremos o compilador mikroC. Qual o tipo de variável
que devemos declarar em nosso programa para armazenar o valor apresentado?
Obs: Procure definir o tipo de variável mais adequado pois sabemos que se ttratando de
microcontroladores nossos recursos de memória são limitados

Resposta:

Sabemos que no mikroC basicamente temos os tipos de dados: char, int, float, double e void.
No mikroC os tipos float e double são interpretados como sendo do mesmo tipo, ou seja, ambos tem os
mesmos tamanhos.

O tipo de dados que se enquadra para o ranger de 0 a 100 são: unsigned char, short int, unsigned int e
long int.
Mas conforme o anunciado precisamos criar uma variável mais adequada, pois assim otimizaremos o
consumo de memória de dados em nosso microcontrolador;
O tipo que mais adequado é unsigned char. (0 a 255) ou o unsigned short int (0 a 255).

EXERCÍCIO 5:

Pergunta:

Qual é a diferença entre um variável unsigned e signed?

Resposta:

As variáveis signed podem representar números com sinais e possuem magnitude de


representação inferior às unsigned que não podem representar valores com sinais.

EXERCÍCIO 6:

Pergunta:

No programa seguinte, descubra quais são as variáveis local e quais são as variáveis
globais: Defina a diferença entre variáveis local e global:

//programa exemplo

char contador;
int limite;

void funcao(void)
{
int a;
limite = a + contador;
portb = limite;

void funcao2(void)
{
signed char b;
portd = b;
}

void main()
{
int c;

trisb = 0;
portb = 0;

trisd = 0;
portd = 0;

funcao() ;
funcao2();

Resposta:

Variável Local:

int a;
signed char b;
int c;

Variável Global:

char contador;
int limite;

EXERCÍCIO 7:

Pergunta:

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material
sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Os Operadores
1.0 Os operadores Aritméticos:
1.1 Adição e Subtração
1.2 Multiplicação e Divisão
1.3 O operador %
1.4 O operador de Incremento ++ e decremento --:
1.5 Operadores Relacionais;
1.6 Operadores lógicos ou Booleanos
1.7 Os operadores Bit a Bit
1.8 O Operador AND (E)
1.9 O Operador OR (|)
2.0 O Operador XOR (^)
2.1 O Operador NOT (~)
2.2 O Operador de deslocamento << >>

Tipos de Operadores
A linguagem C faz uso de diversos operadores. Podemos classifica-los em algumas categoria
principais: artméticos, relacionais, lógicos, lógicos bit a bit , outros.

Os operadores Aritméticos:

Os operadores aritméticos são utilizados para efetuar determinadas operações matemáticas


entre dados e são classificados em duas categorias, sendo binário ou unário. Os operadores
binários atuam em operações de exponenciação, divisão, adição e subtração. Os unários atua
na inversão de valores, atribuindo o sinal de positivo ou negativo.

operador ação
+ adição
subtração ou menos
-
unário
* Multiplicação
/ Divisão
Resto da divisão
%
inteira
++ incremento
-- Decremento

Adição e Subtração

Os operadores de adição e subtração são muito simples de serem utilizados, acompanhe:

c = a + b;
d = d + b;
c = c - a;
a = d - a + c;
Multiplicação e Divisão

Os operadores de multiplicação e de divisão, em C, são de fácil assimilação;

c = a * b;
d = d * d;
c = c / a;
a = (d * a) / c;

O operador %

O operador % é utilizado para retornar o resto de uma operação de divisão inteira. Vejamos
um exemplo:

7 / 3 = 2, em uma divisão inteira, sendo o resto igual a 1.

Assim, o valor de 7 / 3 = 2 e o valor de 7 % 3 = 1.

O operador de Incremento ++ e decremento --:

O operador de incremento e decrevemento são utilizados para somar 1 ou subtrair 1 de uma


variável. Exemplo:

C = C + 1;

No exemplo acima a variável C foi incrementada em 1 unidade. Podemos ter o mesmo


resultado da operação acima através do operador de incremento ++ que termos o mesmo
resultado;

C++; (equivale a c = c + 1;)

c ++; //incrementa em 1 unidade o valor da variável C


d--; //incrementa em 1 unidade o valor da variável D

Acompanhe o exemplo abaixo:

variavel _a = variavel_b ++;

Observe que o valor da variável variavel _b é atribuido a variável variavel _a, e


logo após isso, o valor de variavel _b é incrementado em 1 unidade:

Isso também é válido para o comando de decremento --.

variavel _a = variavel_b --;


Observe que o valor da variável variavel _b é atribuido a variável variavel _a, e
logo após isso, o valor de variavel _b é decrementado em 1 unidade:

Devemos tomar alguns cuidados em C com relação aos operadores de incremento e


decremento: vejam;

variavel _a = variavel_b ++;

é diferente de

variavel _a = ++ variavel_b;

Note que no primeira linha de comando, variavel_b é incrementada em 1 depois que


atribuiu seu valor a variável variavel _a. Na segunda linha, o valor da variavel_b é
incrementada em 1 antes de atribuir seu valo a variável variavel _a.

Vejamos um exemplo:

int a, b, c;
a = 0;
b = a ++;
c = ++ a;

Neste caso, após a execução dos três comandos, o valor de "a" será igual a 2, o valor da
variável "b" será igual a 0 e o valor da variável "c" será igual a 2.

Operadores Relacionais;

Os operadores relacionais servem para comparar expressões. São muito utilizado para
comparar condições de controle do fluxo de execução de programas.

Operação
operador
realizada
> maior que
>= maior ou igual a
< menor que
<= menor ou igual a
== igual a
!= diferente de

Estes operadores serão muito utilizado para construir expressões condicionais, como veremos
mais adiante em nosso curso.

Operadores lógicos ou Booleanos


Os operadores lógicos ou booleanos são de grande importância para construções de testes
condicionais.

Operação
operador
realizada
&& AND (E)
|| OR (OU)
! NOT (NÃO)

Com esses operadores podemos relacionar diversas condições diferentes em um mesmo teste
lógico.

if (c = 10 && b = 5) c = 5; // condição verdadeira de teste: se a variável "c" for igual a 10 e a


variável b = 5 então "c" assumirá o valor 5.

if (c>0 || a==0) b = a; // condição verdadeira de teste: se a variável "c" for maior que 0 ou a for
igual a 0 então "b" é igual a variável "a".

if (!a) b = c; // condição verdadeira de teste: se a variável "a" for igual a 0, a variável "b"
assumirá o valor da variável "c". Note que estamos utilizando o operador de negação "!" NOT,
por esse motivo a variável "a" assumirá o valor verdadeiro, já que possui valor 0.

Os operadores Bit a Bit

Os operadores Bit a Bit são utilizados para realizar operações lógicas entre elementos ou
variáveis.

Operação
operador
realizada
& AND (E)
| OR (OU)
XOR (OU
^
exclusiva)
NOT
~ (complemento de
um)
deslocamento à
>>
direita
deslocamento à
<<
esquerda

O Operador AND (E)

O operador lógico AND realiza operação separadamente para cada bit dos operandos.
Utilizamos muito este o operando AND como "máscara" de um byte, para habilitar ou
desabilitar somente os bits que desejamos. Veja um exemplo:

int a, b;
a = 125;
b = 28;
a = a & b;

A operação AND ocorrerá da seguinte maneira:

a = 100 ----> 01100100


AND (&)
b = 28 ----> 00011100
___________________

Resultado= 00000100 //ou 4 decimal

O Operador OR (|)

O operador OR é muito similar ao operador AND, sua operação também é realizada para cada
bit do operando. Exemplo:

int a, b;
a = 125;
b = 28;
a = a | b;

A operação OR ocorrerá da seguinte maneira:

a = 100 ----> 01100100


OR (|)
b = 28 ----> 00011100
___________________

Resultado= 01111100 //ou 124 decimal

O Operador XOR (^)

O operador XOR são muito utilizado em funções de comparação de valores, pois em uma
operação lógica, o resultado somente será verdadeiro (nível lógico 1) se um e somente um
deles for verdadeiro ( nível 1). Exemplo:

int a, b;
a = 125;
b = 28;
a = a ^ b;

A operação OR ocorrerá da seguinte maneira:

a = 100 ----> 01100100


XOR (^)
b = 28 ----> 00011100
___________________

Resultado= 01111000 //ou 120 decimal

O Operador NOT (~)

O operador NOT inverte o estado de cada bit do operando em uma operação. Exemplo:

int a, b, c;
a = 1;
b = 1;
b = ~a
c = ~b

A operação OR ocorrerá da seguinte maneira:

a = 0B00000001; NOT de "a" ----> 0b11111110;

b = 0b00000001; NOT de "b" ----> 0b11111110;

O Operador de deslocamento << >>

O operador >> desloca para a direita os bits de uma variável um determinado número de vezes.
exemplo:

int a, b, c;
a = 10;
b = 10;
b = a >> 1;
c = b << 5;

No exemplo acima, os valores dos bits da variável "a" foram deslocados 2 vezes para a direita,
enquanto os bits da variável b foram deslocados 5 vezes para a esquerda.

Teremos como resultado:

variável a; // ----> 00001010 - valor 10 em binário


>>
// ----> 00000101 - valor 5 em binário

Observe que o valor após deslocamento passa a ser agora 5 em binário.

A descrição do operador (>>), deslocamento à direita, é análoga ao operador deslocamento à


esquerda (>>), com ressalva de que os bits serão deslocados à esquerda.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse
material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Estruturas de controles e laços de repetição
1.0 O comando IF (SE)
1.1 O comando Switch

Estruturas de Controle
As estruturas de controle são usadas nos programas em lugares em que necessitamos
que a máquina faça repetidas operações ou necessite de tomadas de decisão.

O comando IF (SE)

O IF (SE, em português) é uma estrutura de tomada de decisão baseada no resultado lógico de um teste. Sua
forma geral é:

if (condição) comando;

ou

if (condição) { blocos de comandos};

Em C, qualquer número diferente de zero é tido como verdadeiro. O que significa que uma condição em C só é
falsa, quando os resultados dos operadores que aplicados for zero (0). Qualquer outro valor, mesmo negativo, é
considerado como verdadeiro ou não zero.

No comando If, caso a condição seja verdadeira (diferente de zero), o comando, ou blocos de comandos serão
executados, caso contrário, quando a condição for falsa (0 zero) o comando será ignorado.

Exemplo:

void main() {
char a = 10;
char b = 0 ;
if (a) b--;
}

A variável "a" foi atribuida o valor 10, que é diferente de zero (0). Consequentemente, a o comando if será
executado e a condição de decremento da variável b será executado; ( if (a) b--; ).

também é possível executar outro comando ou bloco de comandos no caso da condição ser avaliada como falsa,
para isso utilizamos os recursos do comando if - else.

Acompanhe seu formato geral:

if (expressão)
comando 1
else
comando 2

Exemplo:

if (x > 5)
z = 10;
else
z = 20;

A linha de comando if (x > 5) tem por função verificar se acondição x>5 é verdadeira, caso seja verdadeira, a
linha de programa z = 10 será executada. Caso a condição do comando IF seja falsa, ou seja, se "x" for menor que
5, então a linha de comando z = 20 será executada.

Todos os operadores do C podem ser incluidos no teste de uma condição, expressões válidas da linguagem C
podem ser avaliadas para a geração de um resultado booleano na estrutura if. As expressões seguintes são válidas
e seus resultados dependerão do valor de suas variáveis. Exemplo:

if (a > b) b = a; // se a expressão a > b for verdadeira, a variável "b" assume o valor da "a"
if (b < a) b = 10; // se a expressão a < b for verdadeira, a variável "b" assume o valor 10
if (a != b) b = 55; // se a expressão a !=b (diferente) for verdadeira, a variável "b" assume o valor 55
if ( (a + b) > (b + d) ) b++; // se a expressão (a + b) > (b + d) for verdadeira, a variável "b" será incrementada em
uma unidade.

Observações importantes:

O operador de atribuição de igualdade em C (=) é diferente do operador relacional de igualdade (==). Para testar a
condição de uma variável qualquer, utilizamos o operador relacional de igualdade (==). Acompanhe:

if (a == b) c = 10;

é diferente de

if (a = b) c = 10;

A condição (a == b) faz uma comparação entre as duas variáveis, caso a condição seja verdadeira, a variável "C"
assumirá o valor 10;

O comando If seguinte, possui uma expressão de atribuição de igualdade (a = b). O compilador ao compilar este
comando, irá primeiramente atribuir a variável "a" o valor contido em "b", e depois disso, verificará se acondição
não é zero, caso seja verdadeiro o resultado (diferente de zero), a variável "c" assumirá o valor 10;

No comando IF para adicionarmos blocos de programas, faz necessário o uso dos simbolos abre- chaves ({)
efecha-chaves (}).

Exemplo:

if (portb == portc)
{ //blocos de comandos
a++;
portb.f0 = 1;
d = c + e;
}

A estrutura if, else, if

Podemos criar arranjos (nesting) de comandos através da estrutura if, else, if. Os arranjos são criados bastando
colocar estruturas if aninhadas com outras estruturas if.
Seu formato geral é:

if (condição1) declaração 1; else


if (condição2) declaração 2; else
if (condição3) declaração 3; else
if (condição4) declaração 4;

Exemplo:

void main() {

int contador = 10;


int sinal = 5;

if (contador > sinal) sinal++; else


if (contador < sinal) sinal--; else
if (contador == sinal) sinal = 0;
}

Podemos também executar blocos de comandos utilizando os recursos das chaves ( { e } ).

Exemplo:

void main() {

int contador = 10;


int sinal = 5;

if (contador > sinal)


{ //utilização de blocos de comando na estrutura if-else-if
sinal++;
contador = 20;
}
else
if (contador < sinal)

{
sinal--;
contador = 15;
}

else
if (contador == sinal) sinal = 0;
}

A estrutura if é uma das mais utilizadas para tomada de decisões lógicas. Iremos utilizá-la em vários programas
que desenvolveremos durante nosso curso.

O comando Switch

O comando switch é uma forma mais clara e elegante de tomar decisão dentro de um programa em C. Diferente
do comando if, a estrutura switch não aceita expressão para a tomada de decisão, mas só aceita constante.

switch (variável)
{
case constante1:
declaração1A;
declaração1B;
declaração1N;
break;

case constante2:
declaração2A;
declaração2B;
declaração2N;
break;

default;
declaração_default;
}

O valor da variável no comando switch é comparada contra as constantes especificadas pela cláusula case. Caso a
variável e a constante possuam valores iguais, os comandos seguinte a cláusula case serão executados. Caso não
tenha nennhuma constante com o mesmo valor da variável, então os comandos especificados pela cláusula default
serão executados.

Acomanhe o exemplo:

void main() {

int contador = 10;


int sinal = 5;

switch(contador)
{
case 2: sinal++;
break;

case 1: sinal = 2;
break;

case 10: contador--;


break;

default: sinal = 0;
}
}

No exemplo de acima, a variável contador será comparada às constantes 2, 1 e 10. Como a variável contador
possui o valor 10, consequentemente o comando que será executado no exemplo acima é case 10:
contador--; (decrementa a variável contador).
A cláusula Break possui a função de encerrar uma sequencia de comandos de
uma cláusula case.
A cláusula default é o último comando switch.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse
material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Estruturas de Repetição
1.0 O comando For
1.1 O comando While
1.2 O comando do - while
1.3 O comando break e continue

Introdução
Os laços de repetição servem para repetir uma ou mais vezes determinada instrução ou blocos de instruções.
Existem basicamente três tipos de estruturas de repetição na linguagem C:

for
While
Do - While

A estrutura for basicamente é utilizada para laços finitos de contagem, normalemtne utilizando uma variável de
controle da contagem.
A estrutura while basicamente é utilizado para repetição de um determinado conjunto de instrução enquanto uma
condição for verdadeira.
O comando do - while é similar à estrutura while, diferenciando apenas o momento que é analisada a condição.

Vamos conhecer sobre cada estrutura de repetição, acompanhe:

O comando For

O laço for é utilizado quando necessitamos de um ciclo de repetições controlado, pois em sua
declaração podemos inicializar e incrementar ou decrementar a variável de controle. Antes de
serem executados os comandos do laço For-next, primeiramente é avaliada a condição do teste.
Caso seja verdadeira, são executados os comandos do laço. A cada ciclo que é executado o
faço for, a variável de controle será incrementada ou decrementada no valor programado no
incremento.

Veja a estrutura do comando For :

For (inicialização ; condição ; incremento ) comando;

ou

For (inicialização ; condição ; incremento )


{
...
comandoA1
comandoA2
...
}

em que:
inicialização: expressão válida utilizada normalemtne para inicialização da variável de controle
do laço for.
condição: condição para que decide pela continuidade ou não do laço de repetição, enquanto
esta condição foi verdadeira , o laço for permanecerá em execução.
incremento: valor incrementado em a cada repetição do laço for.

Exemplo:

int contador;
for (contador = 0 ; contador = 10 ; contador ++ ) portb = contador;

Na estrutura anterior, a variável contador inicialmente é carregada com o valor 0 (zero), e os


comandos do laço for são executados. Após a execução de todos os comandos presentes no
laço for, a variável contador é incrementada no passo do valor do incremento, que no nosso
exemplo é 1 unidade, e novamente os comandos do laço for são executados. A estrutura de
repetição for termina quando a variável contador assume o valor 10.

Exemplo:

int v, d;
for (v = 0 ; v = d ; v++)
{
Comando A
Comando B
}

Este exemplo funciona da seguinte maneira:

1) A variável V recebe o valor 0.


2) O comando A e o comando B são executados.
3) A variável V tem seu valor incrementado em 1 unidade.
4) Após esta incrementação, o valor de V é comparado com o valor D, e a seqüência retorna
ao item 2, para nova repetição.
5) Caso contrário (V ultrapassou D), o laço de repetição for é finalizado.

O grande benifício do laço for é sua flexibilidade, pois aceita qualquer expressão válida em C, mesmo que essas
expressões não tenham relacionamento com o laço de repetição diretamente.
Um exemplo simples de aplicação do laço for é a criação de pequenos tempos de atrasos (delays).

int atraso;
for (atraso = 0 ; atraso < 1000 ; atraso++);

O laço for acima faz com que o processamento fique incrementando constantemente a variável atraso em 1 até
que esta variável seja igual ao valor 1000;

Observe o fragmento de programa seguinte válido dentro do loop for:

for (n = 0 ; (n < 100) && portb.f0 ; n++)

No exemplo acima a variável n será incrementada 100 vezes, desde que o pino RB0 permaneça em estado lógico
alto. Se o pino RB0 em algum momento do laço cair para nivel lógico baixo, o loop será imediatamente
encerrado.
Programa Exemplo:

Aproveitando que estamos estudando as estruturas do laço de repetição for, vamos elaborar um program que pisca
um led conectado ao pino RD0 do PIC utilizando os recursos do laço for.

Acompanhe o esquema abaixo:

O programa seguinte vai contar até 100.000. Para acomodar um número dessa grandeza poderiamos ter utilizado
uma variável long, mas optamos pela variável int para que você entenda a construção de dois laços for
encadeados. Para contar até 100,000 é necessário termos uma variável que conte até 100 e outra até 1000 (100 *
1000 = 100.000).

//**********************************************************
// programa de perda de tempo utilizando o laço de repetição for.
//**********************************************************

void atraso(void) // rotina de perda de tempo


{
int contador1, contador2;
for (contador1 = 0 ; contador1 < 100 ; contador1++) //laço de repetição for
for (contador2 = 0; contador2 < 1000; contador2++) ;
}

void main() {

trisd = 0;
while(1) {
atraso(); //chama rotina de perda de tempo
portd = ~portd; // inverte os estados do portd do PIC
}
}

O comando While

Muitos dos programadores iniciantes não sabem do que iremos comentar agora.

Os microcontroladores no geral não possuem sistemas operacionais, os programas que


desenvolvemos para os microcontroladores PIC devem rodar eternamente, ou possuirem
mecanismos que paralise a operação da CPU em alguns momentos.
Isso é necessário, pois, por não possuir sistem operacional, o programa do PIC não tem para
onde sair, devendo ficar em operação, de preferência eterna. Esses é um dos motivos da função
principal main() dos programa para microcontroladores deverem ser do tipo void (valor nulo),
pois não tem quem receba os valores de retorno da função principal main().

O comando while é o comando ideal criar loops infinitos no nosso programa.

A estrutura while verIfica inicialmente se a condição de teste é verdadeira. Caso verdadeiro,


todas os comandos dentro do laço while serão executados. Ao retornar para o início do laço, é
verificado novamente se a condição de teste é verdadeira, se for verdadeira, executa novamente
os blocos de comandos; se for falsa, o laço while é finalizado.

A companhe o exemplo abaixo:

void main() {

while (1) // condição a ser testada. Neste exemplo a condição sempre será verdadeira (1);
{
declaração 1;
declaração 2;
declaração n;
}
}

Veja outro exemplo de programa:

void main() {

int a = 25;

while (a = 5)
{
a++;
portb = a;
}
}

Repare que no exemplo acima o valor a condição do laço while é falsa, neste cado os comandos do laço não serão
executados no programa até que a condição seja verdadeira, ou seja, até que a = 5.
O comando do - while
O comando do -while forma uma estrutura de repetição diferente dos comandos while e for estudado
anteriormente. Sua diferença fundamental com relação as outras tradicionais laços de loop while e for esta no fato
da avaliação da condição de teste ser no final do laço de repetição, contrário dos outros laços que estudamos, que
testam as condições no início de cada ciclo.

formato geral:

do comando while (condição);

ou

do
{
comando 1;
comando 2;
} while (condição de teste);

Na estrutura do-while a condição de teste é efetuada somente na parte final do loop. Isso faz
com que as instruções contidas no interior do laço do - while sejam executadas ao menos uma
vez. Caso a condição teste seja atendida, o laço de repetição é finalizado, caso contrário o
bloco que está na estrutura seria novamente executado até que a condição teste seja atendida.

Exemplo:

void main() {
int a = 0; //declara a variável a como inteiro com o valor 0

do
{
a++; //incrementa a variável a em uma unidade
portd = ~portd; //inverte os estados do portd
}
while(a > 100); // enquanto a < 100 permanece no loop do-while;
}

O comando break e continue

Break
O comando break, na linguagem C, é utilizado para interromper (quebrar) qualquer laço de
repetição instantaneamente. No momento da execução do loop, ao encontrar o comando break,
o laço de repetição é finalizado.

Exemplo:

void main() {
int a = 0;
do
{
a++; //incrementa a variável A em uma unidade
break; //interrompe a execução do laço de repetição do-while
portd = ~portd; //inverte os estados dos portd
}
while(a < 100); //enquanto a variável a for menor que 100 a rotina do-while será executada.
}

O comando break é valido para os laços do-while, while, for e case.

Continue
O comando continue reinicia novamente o teste da condição do laço de repetição. Toda a vez
que no laço for encontrado o comando continue, os comandos seguintes não serão executados
no laço.

Exemplo:

void main() {
int a = 0;

do
{
a++; //incrementa a variável A em uma unidade
continue; //iretorna para o inicio do laço de repetição
portd = ~portd; //inverte os estados dos portd
}
while(a < 100); //enquanto a variável a for menor que 100 a rotina do-while será executada.
}

No exemplo de programa acima, os comandos abaixo de continue não serão executados, pois,
toda a vez que for executado o comando continue, automaticamente o programa será
redirecionado para o cabeçalho do laço de repetição independente de qualquer estado de
variáveis.
O comando continue é valido para os laços do-while, while, for e case.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse
material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Manipulação de Teclados no mikroC

Introdução

É um dispositivo indispensável na maioria dos projetos, pois permite adicionar dados ao Mc. Acompanhe o
esquema de um teclado:

O resistor utilizado chama-se pull-up, pois garante o nível lógico 1 na entrada do Mc quando a chave estiver
aberta. Quando pressionamos a tecla, alteramos o nível lógico do pino do Mc, ou seja, se por exemplo: o pino
RB0, RB4 e RB7 estiver em nível lógico 1, ao pressionarmos a tecla S1 o pino vai para nível 0. ( apesar do portb
do PIC possuir resistores de pull-up interno convencionamos a utilização também de resistores externos)

Quando tivermos no projeto teclas conectadas ao Mc, devemos tomar cuidado com o efeito mecânico Debouncing
ao pressionarmos essas teclas.

Debouncing: o efeito mecânico de rebatimento que a tecla sofre logo após ser pressionada é chamado "boucing",
o qual provoca a leitura falsa da tecla, isto é, o rebatimento faz com que a tecla seja lida mais de uma vez. A rotina
de debouncing nada mais é do que um pequeno intervalo de tempo entre o momento em que a tecla é solta e a
nova leitura do teclado. Esse intervalo permite que o fenômeno do rebatimento termine naturalmente, evitando
assim a leitura falsa dela. Tempo aproximado de 25 ms.

No mikroC podemos manipular teclados através de uma simples função intitulada button. Acompanhe:

A Função Button
A função button tem como sintaxe:

Button(&port, pin, time, estado_de_ativação);

onde:

&port = port onde esta ligado a tecla, pode ser PORTA, PORTB, PORTC, PORTD
ou PORTE.
pin = é o pino onde esta ligado a tecla no PIC, pode ser de 0 a 7.
timer = tempo de debounce em escala de milisegundos.
estado_de_ativação = determina se a tecla vai ser ativada em nível 0 ou em
1.
Exemplo:

button (&portb, 1, 10, 0)

Apartir da configuração da função button acima, descobrimos que a tecla que será lida está conectada ao portb,
pino RB0, delay de debounce de 10 milisegundos e é acionada em nível lógico 0 (zero).

O retorno da função button é: valor 0 (tecla não pressionada) ou 1 (tecla pressionada).


A função button automaticamente configura os respectivo port como entrada.

Vejamos um exemplo de programa utilizando a função button:

Primeiro Programa:

Conectado ao PIC temos uma tecla ( push- button) e um led. Repare que o led é ligado com nivel lógico 1.

figura 01

Desejamos que toda vez que pressionarmos e soltermos a tecla S1, o led 1 deverá alterar seu estado lógico, ou
seja, se o led estiver acesso, ao pressionar e soltar a tecla S1, o led será apagado, e ao pressionar a tecla
novamente, o led será acesso. Devemos programar delay para correção de debounce de 20 milisegundos.

Para realizar essa tarefa, iremos fazer um programa bem simples utilizando a função button, acomanhe:

void main()
{
trisd.f0 = 0; //configura pino RD0 como saida (leds)
trisb.f0 = 1; //configura pino RB0 (tecla) como entrada

do {
if (Button(&PORTB, 0, 20, 0)) //se a tecla S1 estiver pressionada o comando if será
executado
{
delay_ms(200); //delay de 200 milisegundos
portd.f0 = ~pord.f0; //inverte o estado do led
}
} while(1);
}

No programa acima configuramos o pino RD0 como saída, pois tráta-se de um led.

trisd.f0 = 0; //configura portd como saida (leds)

Criamos um laço de repetição através do comando do-while, cujo objetivo é fazer com que o microcontrolador
sempre esteja executando os comandos do laço.

A função button foi programada da seguinte forma:

Acompanhe o trecho de programa seguinte:

if (Button(&PORTB, 0, 20, 0)) //se a tecla S1 estiver pressionada o comando if será


executado
{
delay_ms(200); //delay de 200 milisegundos
portd.f0 = ~pord.f0; //inverte o estado do led
}

Caso a tecla S1 seja pressionada, ao executar a função button, teremos como retorno valor 1 (verdadeiro), e caso
não venhamos a pressionar a tecla, a função button nos retornará o valor 0. Apartir disso concluímos que a
condição IF somente será verdadeira caso o retorno da função button for verdadeiro, que neste caso, ocorrerá
quando a tecla for pressionada.

O programa apresentado acima possui um problema, pois caso deixamos a tecla pressionada, a condição IF sempre
será verdadeira e então seus comandos serão executados, fazendo com que o led acenda e apague enquanto a tecla
estiver pressionada.

Nosso segundo Programa :

Para corrigir o problema do programa anterior, vamos elaborar um novo programa que acenda e apague o led 1 da
figura 01 para cada toque que dermos no teclado, ou seja, temos que pressior e soltar a tecla para executar a
inversão do estado do led, se deixarmos a tecla pressionada, o estado lógico do led somente mudará um vez. O
tempo de debouncing programado deverá ser de 20 milisegundos.

Programa:

/*
Programa de leitura de tecla.
Curso: Microcontroladores PIC programação em C
Este programa tem por objetivo ler o estado de uma tecla para ligar/desligar um led.
*/
#define led1 portd.f0 //a diretiva define atribui ao pino RD0 o nome led1.

int estado_antigo; //define uma variável de controle, cujo objetivo é armazenar um


estado.

void main() {
trisd.f0 = 0; //configura o pino RD0 como saida, pois iremos ligar/desligar o
led.
trisb.f0 = 1; //configura pino RB0 (tecla) como entrada

do {
if (Button(&PORTB, 0, 20, 0)) estado_antigo = 1; //se a tecla fo pressionada, a
variável estado_antigo = 1.
if (estado_antigo == 1 && Button(&PORTB, 0, 20, 1)) { //verifica se a tecla foi solta
led1 = ~led1; //inverte o estado do led 1.
estado_antigo = 0; //inverte o valor da variável estado_antigo para o próximo
acionamento.
}
} while(1);

Estudo detalhado do programa:

Primeira parte: Comentário


Nesta primeira parte do programa, acrescentamos comentário de apresentação do programa.

/*
Programa de leitura de tecla.
Curso: Microcontroladores PIC programação em C
Este programa tem por objetivo ler o estado de uma tecla para ligar/desligar um led.
*/

Segunda parte: a diretiva define

Até este ponto do curso não estudamos a utilização da diretiva define. Esta diretiva tem por função atribuir a uma
constante ou registrador um novo nome. Veja o comando:

#define led1 portd.f0 //a diretiva define atribui ao pino RD0 o nome led1.

A diretiva #define atribui o nome led1 ao pino RD0 do PIC, partir deste ponto do programa, toda vez que
utilizarmos o nome led1, o compilador sabe que se refere ao pino portd.f0 (RD0), e substituirá na compilação este
novo nome pelo endereço fisico de memória do pino RB0.

Exemplo:

#define display1 portd.f0 //a diretiva define atribui ao pino RD0 o nome led1.
#define led2 portd.f1 //a diretiva define atribui ao pino RD0 o nome led1.

void main() {
trisd = 0; //configura o pino RD0 como saida, pois iremos ligar/desligar o led.
display1 = 0;
led2 = 1;
}

Terceira parte: definição da variável

Definimos uma variável do tipo int chamada estado_antigo. Esta variável tem por função fazer com que o led seja
ligado/desligado somente com um unico toque na tecla, evitando que ao deixarmos a tecla pressionada os
comandos if sejam sempre executados.

int estado_antigo; //define uma variável de controle, cujo objetivo é armazenar um


estado.

quarta parte: a função principal


A função principal main() é a primeira a ser executada no programa em C. Repare que a função main()
acompanha um modificador de tipo void que tem como objetivo informar ao compilador que esta função é nula e
não possui retorno de dados, pois, não tem pra quem retornar os dados pois tratá-se da função principal.

void main() {

Quinta parte: programando as portas como entrada ou saida

Como já estudamos anteriormente, o registrador TRIS do PIC, tem por função informar a
direção (leitura ou escrita) dos pinos da porta. Neste programa temos uma tecla conectado
ao pino RB0, e para lermos essa tecla é necessário configurar este pino como entrada. A
função button se encarrega de configurar esses registradores de sentido para nós.

Conectado ao pino RD0, temos um led. Para acionar este led, faz necessário enviar nível
lógico 1 a este pino, então devemos configurar este pino como sáida.

trisd.f0 = 0; //configura o pino RD0 como saida, pois iremos ligar/desligar o


led.

Sexta parte: Rotina de loop

Lembre-se que devemos sempre manter o microcontrolador executando alguma rotina, mesmo que essa rotina
seja um comando sem saida (while(1);). Devemos criar um programa que "amarre" o microcontrolador a sempre
executar suas funções. No nosso programa exemplo utilizamos os recursos do laço de repetição do - while.

do {
if (Button(&PORTB, 0, 20, 0)) estado_antigo = 1; //se a tecla fo pressionada, a
variável estado_antigo = 1.
if (estado_antigo && Button(&PORTB, 0, 20, 1)) { //verifica se a tecla foi solta
led1 = ~led1; //inverte o estado do led 1.
estado_antigo = 0; //inverte o valor da variável estado_antigo para o próximo
acionamento.
}
} while(1);

Repare que a condição de teste do laço de repetição do-while será sempre verdadeira (while(1)).

Sétima parte: Rotina de tomada de decisão e função button

Os comandos seguintes tem por objetivo ler o estado da tecla S1, conectada ao pino RB0 do PIC, e ligar ou apagar
o led 1 conectado ao pino RD0.
Para lermos a tecla, utilizamos a função do mikroC: Button, em conjunto com o comando de tomada de decisão
IF.

Vejamos:

if (Button(&PORTB, 0, 20, 0)) estado_antigo = 1; //se a tecla fo pressionada, a variável


estado_antigo = 1.

Sabemos que o comando if somente será executado se a condição de teste for verdadeira, neste caso, a variável
estado_antigo somente será executada se a função button for verdadeira, para isso é necessário que a tecla seja
pressionada.

Outro comando:

if (estado_antigo == 1 && Button(&PORTB, 0, 20, 1)) { //verifica se a tecla foi solta

Nesta linha de programa efetuamos uma operação AND (E) entre a variável estado_antigo e o retorno da função
Button, cujo objetivo é verificar se a tecla foi solta ou não. Repare que nesta segunda função Button o estado
lógico de acionamento da tecla passa a ser 1 (Button(&PORTB, 0, 20, 1)), contrário da função Button da linha
anterior, essa alteração indica que somente teremos a condição de retorno verdadeira (1), caso a tecla não esteja
pressionada.
Os comandos seguintes somente serão executados se a condição if for verdadeira, e para isso faz necessário que a
variável estado_antigo seja igual a 1 e que o retorno da função Button seja verdadeira, ou seja, a tecla tem que
estar solta.

oitava parte: Inversão do estado do led e resset de variável de estado

led1 = ~led1; //inverte o estado do led 1.


estado_antigo = 0; //inverte o valor da variável estado_antigo para o próximo
acionamento.

Leituras de 4 teclas:

Nosso próximo passo é elaborar um programa para lermos 4 teclas conectado ao PIC, conforme figura abaixo:

Figura 03

Perceba no esquema eletrônico acima que nossos teclados não possuem mais resistores de pull-up externo
(compare com o circuito eletrônicos da figura 01), neste caso devemos acionar os resistores de pull-up interno
disponivel no PORTB do PIC através do bit RBPU do registrador INTCON2.
No PORTB do PIC18f442, por default, o circuito de pull-up interno vem desativado (após reset). Esses resistores
de pull-up são automaticamente ativados quando programamos o PORTB como saida (trisb = 0), para outros
casos, devemos nós mesmos ativá-los.

O registrador bit RBPU é ativado com nível lógico 0 (zero), e destivado com nível lógico 1 (um).

Nosso programa:

/*
Programa de leitura de tecla.
Curso: Microcontroladores PIC programação em C
Este programa tem por objetivo ler o estado de 4 tecla para ligar/desligar um led.
*/

#define led1 portd.f0 //a diretiva define atribui ao pino RD0 o nome led1.

int estado_antigo; //define uma variável de controle, cujo objetivo é armazenar o


estado anterior da tecla.

void main() {
trisd = 0; //configura o pino RD0 como saida, pois iremos ligar/desligar o led.
portd = 0;
intcon2.rbpu = 0; //habilita os resistores internos de pull-up do PORTB do PIC
trisb.f0 = 1; //configura pino RB0 (tecla) como entrada
trisb.f1 = 1; //configura pino RB1 (tecla) como entrada
trisb.f2 = 1; //configura pino RB2 (tecla) como entrada
trisb.f3 = 1; //configura pino RB3 (tecla) como entrada

do {
if (Button(&PORTB, 0, 20, 0)) //lê tecla 1
portd = 0b00000001;
else if (Button(&PORTB, 1, 20, 0)) //lê tecla 2
portd = 0b00000010;
else if (Button(&PORTB, 2, 20, 0)) //lê tecla 3
portd =0b00000011;
else if (Button(&PORTB, 3, 20, 0)) //lê tecla 4
portd =0b00000100;
Delay_ms(200); //delay de 200 milisegundos

} while(1);
}

Video do funcionamento do programa acima no KIT PICGenios .

The Camtasia Studio video content presented


here requires JavaScript to be enabled and the
latest version of the Macromedia Flash Player.
If you are you using a browser with JavaScript
disabled please enable it now. Otherwise,
please update your version of the free Flash
Player by downloading here.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio
aviso. Maiores informações: suporte@microgenios.com.br
Exercícios Resolvidos
1.0 Exercício 1
1.1 Exercício 2
1.2 Exercício 3

1.3 Exercício 4
1.4 Exercício 5
1.5 Exercício 6
1.6 Exercício 7
1.6 Exercício 8

Introdução
Apartir de agora iremos colcolar em prática tudo que estudamos nesta unidade do curso através de exercícios;

Exercício 1:
Qual é a diferença entre o operador = e o operador ==?

Resposta:

O operador = é de atribuição, enquanto o operador == é de igualdade.

Exercício 2:

Qual o resultado da variável "a" após a seguinte expressão ?

a = 25 & 12;

Resposta:

25 = 000011001
&
12 = 000001100
________________
00001000

A=8

Exercício 3:
Comparando os laços de repetição, do-while e while, descreva suas principais diferenças com relação a avaliação da
condição de teste:

Resposta:

O laço do-while avalia a condição de teste somente no final, após ter executado todos os comandos do laço. Como garantia
temos que o laço será executado ao menos uma vez no programa.
O laço while analisa a avaliação de teste no início do programa, caso a condição seja verdadeira, os comandos dos seu laço
serão executados, caso falso, o laço de repetição while é finalizado.

Exercício 4:
O programa seguinte tem como objetivo piscar um led conectado ao pino RB5 do PIC em intervalos de 1 segundo. Ao
compilar esse programa, o programador percebeu que o programa não funcionou. Analise o programa abaixo e aponte o erro
cometido pelo programador inesperiênte: Refaça o programa com as correções necessárias para seu perfeito funcionamento.

void main()
{
trisb = 0;
portb.f5 = 1;
Delay_ms(1000); // 1 second delay
portb.f5 = 0;
Delay_ms(1000); // 1 second delay
}

Resposta:

O erro cometido no programa acima, se deve ao fato de não existir uma rotina de loop que faça com que o processamento do programa volte a se
repetir.
Para corrigir este erro, colocamos o comando de repetição while em nosso programa com a condição verdadeira.

void main()
{
trisb = 0;
while(1){ // condição sempre verdadeira

portb.f5 = 1;
Delay_ms(1000); // 1 second delay
portb.f5 = 0;
Delay_ms(1000); // 1 second delay
}
}

Exercício 5:
Analise

Qual dos dois comandos If executará a instrução portb = 0? Explique o porquê?

x = 0;
if (x = 1) portb = 0;

ou

x = 1;
if (x ==0) portb = 0;

Resposta:

Somente o primeiro comando IF (if (x = 1) portb = 0; ) é verdadeira e executará a instrução portb = 0;


A condição de teste (x = 1) esta atribuindo a variável "x" o valor 1, e por causa disso a condição if passa a ser verdadeira.
O segundo if, (if (x == 1) portb = 0; ) a condição de de igualdade é falsa, pois x não é igual a 1, por esse motivo a instrução if não será executada.

Exercício 6:

Faça um programa que monitore o acionamento de uma tecla conectado ao port RB1 do PIC, conforme esquema abaixo. A
cada acionamento da tecla todos os leds do portd deverão alterar seu estado anterior (ligado/desligado). Para realizar este
programa , utilize os recursos do comando if para detectar o acionamento das teclas. Não iremos considerer neste programa o
efeito debouncing.
Programa:

/*
Programa de leitura de tecla.
Curso: Microcontroladores PIC programação em C
Este programa tem por objetivo ler o estado de uma tecla para ligar/desligar todos os leds do portd
*/

#define tecla1 portb.f0 // atribui ao pino RB0 o nome tecla1

void main() {

trisd = 0;
portd = 0;
intcon2.rbpu = 0;
//habilita os resistores internos de pull-up do PORTB do PIC

while(1){
if (tecla1 == 0){ //se tecla1 for pressionada então....
portd = ~portd; //inverte os estados dos leds do portd
Delay_ms(200); //delay de 200 milisegundos
}
}
}

Visualização do programa na prática:


The Camtasia Studio video content presented
here requires JavaScript to be enabled and the
latest version of the Macromedia Flash Player.
If you are you using a browser with JavaScript
disabled please enable it now. Otherwise,
please update your version of the free Flash
Player by downloading here.

Repare no video que ao pressionarmos a tecla1, os leds são acionados e desacionados diversas vezes, isso representa que
enquanto a tecla estiver pressionada (nivel 0) a condição if será verdadeira e seus comandos serão executados.

Para resolver esse problema, podemos utilizar diversos artificios em C, mas no nosso estudo vamos utilizar a função button
do mikroC.

Exercício 7:

Faça o mesmo exercício anterior só que agora utilizando a função button do mikroC para leituras da tecla. Utilize delay de
25ms para correção do debouncing.

Programa:

/*
Programa de leitura de tecla.
Curso: Microcontroladores PIC programação em C
Este programa tem por objetivo ler o estado de uma tecla para ligar/desligar todos os leds do portd
*/

#define tecla1 portb.f0 // atribui ao pino RB0 o nome tecla1

void main() {
trisd = 0;
portd = 0;
intcon2.rbpu = 0;
//habilita os resistores internos de pull-up do PORTB do PIC
while(1){
if (button(&portb, 0, 25, 0)){ //se tecla1 for pressionada a condição será verdadeira.
portd = ~portd; //inverte os estados dos leds do portd
Delay_ms(200); //delay de 200 milisegundos
}
}
}

Observe que a função button no momento que é executada, configura o port como entrada (trisb = 255) . Por esse motivo,
não é obrigatório configurarmo o registrador TRISB.F0.

Exercício 8:

Faça um programa utilizando os recursos da função button para ler 3 teclas conectada aos pinos RB0, RB1, RA5. Cada tecla
deve ser programada para funcionarem somente com toque, ou seja, é necessário pressionar e soltar a tecla para cada
acionamento.

A tecla RB0 tem a função de ascender somente os leds pares.


A tecla RB1 tem a função de ascender somente os leds impares.
a tecla RA5 tem a função de Ascender todos os leds.

Inicialmente os todos os leds deverão estar desligados. Tempo de debouncing: 25ms.


hardware:

Programa:

/*
Programa de leitura de tecla.
Curso: Microcontroladores PIC programação em C
Este programa tem por objetivo ler o estado de uma tecla para ligar/desligar um led.
*/

#define tecla1 portb.f0 //atribui ao pino RB0 o nome tecla1


#define tecla2 portb.f1 //atribui ao pino RB0 o nome tecla1
#define tecla3 porta.f5 //atribui ao pino RB0 o nome tecla1

void main() { //função principal do programa

int estado_tecla1 = 0; //definição de variáveis sinalizadores de estado para as teclas


int estado_tecla2 = 0;
int estado_tecla3 = 0;

adcon1 = 6; //configura todos os pinos A/D como I/O de uso geral


trisa = 0;
portd = 0;
intcon2.rbpu = 0; //habilita os resistores internos de pull-up do PORTB do PIC

while(1){

if (button(&portb, 0, 25, 0)) estado_tecla1 = 1;


if (button(&portb, 1, 25, 0)) estado_tecla2 = 1;
if (button(&porta, 5, 25, 0)) estado_tecla3 = 1;

if (estado_tecla1 && button(&portb, 0, 25, 1) ){


portd = 0b10101010;
Delay_ms(200);
estado_tecla1 = 0;
}

if (estado_tecla2 && button(&portb, 1, 25, 1) ){


portd = 0b01010101;
Delay_ms(200);
estado_tecla2 = 0;
}
if (estado_tecla3 && button(&porta, 5, 25, 1) ){
portd = 0b11111111;
Delay_ms(200);
estado_tecla3 = 0;
}
}
}

The Camtasia Studio video content presented


here requires JavaScript to be enabled and the
latest version of the Macromedia Flash Player.
If you are you using a browser with JavaScript
disabled please enable it now. Otherwise,
please update your version of the free Flash
Player by downloading here.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores
informações: suporte@microgenios.com.br
Os Displays LCD Alfanuméricos
1.0 Introdução
1.1 Hardware e Pinagem
1.2 Endereços de cada caractere no LCD 16x2
1.3 LCD Modo 8 Bits
1.4 LCD Modo 4 bits - Somente um port de controle/dados
1.5 LCD Modo 4 bits - Controlado por dois ports do PIC

Introdução
Agora que já conhecemos os recursos fundamentais da linguagem C, estamos prontos para criar nossos programas para controle de equipamentos. Estudaremos as
principais funções e ferramentas para manipular displays LCD. Para implementar nossos projetos é interessante a utilização de um display, capaz de tornar mais
simples a visualização de informações e a interação com nosso equipamento, escolhemos para tratar nesta obra de um display de utilização muito comum em
sistemas microcontrolados, suas características são as seguintes:

Tipo: Display LCD


Exibição: Caracteres Alfanuméricos
Quantidade de informações: 16 x 2 (dezesseis caracters em 2 linhas)
Controlador: padrão HD44780 Hitchi

Um display LCD desse tipo, possui embutido em seu circuito um outro microcontrolador, e para podermos utilizá-lo temos que entender um pouco de seus
comandos e funções. Dessa forma, da mesma maneira com que trabalhamos com o PIC, para utilizar um display LCD temos que seguir uma sequência de
procedimentos.

Hardware e Pinagem
Existem modelos diferentes, com localização diferente da pinagem, porém a numeração e função dos pinos geralmente é a mesma, nos kits didáticos e
profissionais Microgenios é utilizado o LCD apresentado em seguida:

Descrição das Pinagens:

Pino Função Descrição


1 alimentação Terra ou GND
2 Alimentação VCC ou +5V
Tensão para ajuste de contraste (ver
3 V0
figura1)
4 RS Seleção: 1 - dado, 0 - instrução
5 R/W seleção 1 - Leitura, 0 - Escrita
6 E chip Select 1 ou (1 - 0) Habilita, 0 - desabilita
7 B0 LSB
8 B1
9 B2
10 B3
11 B4 barramento de dados
12 B5
12 B6
14 B7 MSB
A (display c/
15 Anodo p/ LED backlight
back)
K (display c/
16 Catodo p/ LED backlight
back)
Além da pinagem é importante entendermos uma outra característica de hardware desse dispositivo, que são suas áreas de
memória; ele possui três áreas de memória com funções bem diferentes:

CGROM: Memória não volátil onde estão gravados os códigos para escrita dos caracteres, isso é muito útil, pois apenas
enviamos o código ASCII do caracter e esse já é escrito no display.

DDRAM: É uma área de memória volátil, onde escrevemos o que queremos que apareça no display, cada endereço dessa
memória equivale a um endereço de caracter dos display, podemos fazer a seguinte analogia para facilitar, cada caracter do
display é como uma “janela” que exibe o conteúdo que escrevemos na DDRAM.

CGRAM: É uma pequena área de memória onde podemos “desenhar” caracteres diferentes (apenas 8 caracteres). Ela é muito
utilizada quando precisamos criar caracteres que não são comuns a todas as línguas como por exemplo o caracter “ç” do
português.

Endereço de cada caracter no display 16 x 2

Abaixo está o endereço em hexadecimal de cada caracter no display LCD 16 x 2, assim fica mais fácil a movimentação do cursor
e das letras a serem escritas:

Lista de códigos dos Caracteres

A seguir o código que devemos enviar para o LCD a fim de obtermos um determinado caracter:
Endereços e Instruções

Agora conhecemos a pinagem desse LCD, vamos conhecer seu modo de funcionamento e saber um pouco mais das instruções e
dados que precisaremos utilizar.
É interessante atentarmos para a seguinte característica, a via de dados e instruções é a mesma, dessa forma a sinalização, que
indica se o Byte que chega no display é dado ou instrução é proveninte do sinal encontrado no pino 4 (RS):

RS = 0 -> Instrução
RS = 1 -> Dado

Outra característica importante é que precisamos pulsar (de 1 para 0) o sinal Enable (pino 6) para que a informação que chega
aos terminais do LCD seja interpretada pelo controlador do display.

Em nosso estudo manteremos o pino 5 (R/W) em zero, pois apenas escreveremos no LCD. Podemos ligar este pino do LCD no
PIC também.

Para utilizarmos esse novo dispositivo em nossos projetos, devemos seguir alguns passos:

1º Inicializar o LCD: Primeiramente criar uma rotina em nosso programa, que envie para o display uma sequência de informações
que determinará o modo com o qual trabalharemos, com por exemplo:

- Definição da matriz de pontos (nosso caso 5 x 7)


- Definição do cursor
- Definição da direção de deslocamento (mensagem ou cursor)
- Limpeza do display.

O mikroC automaticamente carrega as rotinas de inicilização do LCD na compilação de um programa. O programador não precisa se
preocupar em inicilizar o LCD desde que o hardware do dispositivo esteja configurado no programa corretamente no modo 4 ou 8 bits;
(veremos com detalhes mais adiante).

Tabelas de Instruções

Na seqüência disponibilizamos as tabelas de instruções para esse tipo display, como nosso objetivo não é o estudo do
display propriamente dito, aconselhamos aos interessados nesse dispositivo que procurem bibliografias dedicadas a eles
para um maior detalhamento das funções.

Tabela com o conjunto completo de instruções:


Tabela com as instruções mais comuns:

Resumo com as instruções mais úteis:


Endereços de cada caractere no LCD 16x2

Abaixo está o endereço em hexadecimal de cada caracter no display LCD 16 x 2, assim fica mais fácil a movimentação do cursor
e das letras a serem escritas:(ENDEREÇOS EM HEXADECIMAL)

80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F
C0 C1 C2 C3 C4 C5 C6 C7 C8 C9CACB CC CD CE CF

Inicialização do LCD
Toda vez que alimentarmos o LCD, devemos executar um procedimento de inicialização, o qual consiste em enviar comandos que
configuram o modo de operação para execução de um dado programa: fixação das condições de utilização, fixação do modo de controle
ativo/inativo do display e limpeza da tela.
No mikroC não precisamos nos preocupar em programar todos os comandos de inicialização do LCD, pois, através de simples funções,
o compilador se encarrega de programá-lo para nós. Por esse motivo devemos informar ao sistema o “tipo” de LCD que estamos
utilizando por meio de linhas de comando. A seguir, vamos conhecer e estudar as instruções de controle de LCD usadas no mikroC.

Definindo o modo de operação do LCD.

Os LCD's podem ser programados para operarem das seguintes formas:

modo 8 bits
modo 4 bits

No mikroC é possível trabalhar com os LCD's nos dois modos.

no modo 8 bits.
no modo 4 bits - somente um port de controle/dados
no modo 4 bits - com dois ports para controle e dados do LCD

LCD Modo 8 Bits

No modo 8 bits, todos os pinos do barramento de dados do LCD são utilizados, conforme figura abaixo:

Repare que todos os 8 pinos do barramento de dados do LCD (D0 a D7) estão ligados no microcontrolador PIC, neste modo, estamos
enviando as instruções e comandos para o LCD em 8 bits.

O compilador mikroC possui diversas bibliotecas de funções prontas para trabalharmos com displays LCD. Para exemplificar, vamos
apresentar um programa em C que utiliza os recursos das funções de controle de LCD do compilador mikroC, acompanhe:

/**************************************************************************
Centro de tecnologia Microgenios
Programa exemplo: LCD_modo_8bits.c
Este programa tem por objetivo escrever a mensagem "Microgenios" na primeira
linha do LCD e na segunda "Kit PICGENIOS".
**************************************************************************
*/

void main()
{
trisd = 0; //configura todos os pinos do portd como saída
trise = 0; //configura todos os pinos do porte como saida
ADCON1 = 0x06; //configura todos os pinos de A/D como I/O de uso geral

lcd8_config(&porte, &portd,2,1,0,7,6,5,4,3,2,1,0); //configura e inicializa o LCD


lcd8_cmd(lcd_clear); //apaga LCD
lcd8_cmd(lcd_cursor_off); // desliga cursor
lcd8_out(1,1,"Microgenios"); // escreve mensagem na primeira linha do LCD
lcd8_out(2,1,"KIT PICGENIOS"); // escreve mensagem na segunda linha do LCD
delay_ms(10); //delay de 10 ms

while(1); //loop infinito

Descrição detalhada do programa:


O programa acima tem a função de escrever no LCD 16X2 duas mensagens "Microgenios" na primeira linha do LCD, e "KIT
PICGENIOS" na segunda linha do LCD.

A função main(), já conhecemos.

Os comando abaixo definem os sentidos das portas de i/o do PIC.

trisd = 0; //configura todos os pinos do portd como saída


trise = 0; //configura todos os pinos do porte como saida
ADCON1 = 0x06; //configura todos os pinos de A/D como I/O de uso geral

Os comandos seguintes controlam o display LCD no modo 8 bits. Vamos estudar cada um dos comandos:

O comando lcd8_config ( ):

O comando lcd8_config determina quais são os pinos do barramento de dados e de controle do LCD que estão ligados ao PIC, assim
como também inicializa o LCD.

Sintaxe:

lcd8_config(&port_controle, &port_dados, RS, EN, RW, D7, D6, D5, D4, D3, D2, D1, D0 );

Onde:

port_controle e port_dados pode ser: PORTA, PORTB, PORTC, PORTD ou PORTE.


RS : pino do port_controle que determina o bit de instruções ou dado do LCD.
EN : pino do port_controle que determina o bit de "enable" do LCD.
RW : pino do port_controle que determina o bit de leitura /escrita do LCD. (nolmalmente coloca-se este pino do LCD no GND).
D7 à D0: Pinos do port_dados
Vejamos nosso exemplo de programa:

lcd8_config(&porte, &portd,2,1,0,7,6,5,4,3,2,1,0); //configura e inicializa o LCD

A função lcd8_config acima, determina os PORTs e os pinos que participam do controle do LCD:

Port que controla o LCD:

PORTE

Funções dos pinos de controle do port de controle:

RS: 1 //pino RE2


EN: 1 //pino RE1
RW: 0 //pino RE0

Port de dados do LCD;

PORTD

Funções dos pinos do port de dados no LCD;

D7: 7 //pino RD7 do PORTD


D6: 6 //pino RD6 do PORTD
D5: 5 //pino RD5 do PORTD
D4: 4 //pino RD4 do PORTD
D3: 3 //pino RD3 do PORTD
D2: 2 //pino RD2 do PORTD
D1: 1 //pino RD1 do PORTD
D0: 0 //pino RD0 do PORTD

Configuramos então nosso lcd conforme figura abaixo:


Apartir desta primeira linha de programa, concluimos que nosso LCD esta ligado da seguinte maneira:

Repare que o pino R/W do LCD foi aterrado. No comando Lcd8_config informamos que o pino RW era RE0 do PIC.

O comando lcd8_cmd(comandos_LCD):

No mikroC podemos configurar o LCD de diversas formas diferentes. Através da função lcd8_cmd(), podemos determinar diversas
"tarefas" que o LCD deverá executar. Acompanhe:

Sintaxe:

Lcd8_cmd(comandos_LCD);

onde:

Comando_LCD pode ser:

Comandos Descrição
LCD_FIRST_ROW Move cursor para primeira linha do LCD
LCD_SECOND_ROW Move cursor para segunda linha do LCD
LCD_THIRD_ROW Move cursor para a terceira linha do LCD
LCD_FOURTH_ROW Move cursor para a quarta linha do LCD
LCD_CLEAR Apaga display
LCD_RETURN_HOME Retorna cursor para a primeira coluna da primeira linha do LCD
LCD_CURSOR_OFF Desliga cursor
LCD_UNDERLINE_ON Salta cursor para a linha inferior
LCD_BLINK_CURSOR_ON Ativa o modo piscante do cursor
LCD_MOVE_CURSOR_LEFT Move cursor para a esquerda sem movimentar os textos
LCD_MOVE_CURSOR_RIGHT Move cursor para a direita sem movimentar os textos
LCD_TURN_ON Liga cursor
LCD_TURN_OFF Apaga todo o visor do LCD, sem perder os dados no visor
LCD_SHIFT_LEFT Movimenta textos do LCD para a esquerda
LCD_SHIFT_RIGHT Movimenta textos do LCD para a direita
No exemplo utilizamos o seguinte comando para apagar o display LCD:

lcd8_cmd(lcd_clear); //apaga LCD

Este e os demais comandos que estudaremos somente podem ser utilizados após a execução do comando lcd8_config().

Utilizamos o comando para desligar o cursor do LCD

lcd8_cmd(lcd_cursor_off); // desliga cursor

E agora escrevemos a mensagem "Microgenios" na coordenada informada pela função lcd8_out no LCD.

lcd8_out(1,1,"Microgenios"); // escreve mensagem na primeira linha do LCD

Os números 1, 1 da função acima, informa que a mensagem "Microgenios" será escrita na primeira linha da primeira coluna do LCD. o
Primeiro numero informa a linha do LCD e o segundo número informa a coluna do LCD.

Os displays LCD são constituidos de linhas e colunas conforme podemos ver na figura abaixo:

O LCD apresentado possui 4 linhas por 20 colunas.

Podemos escrever um dado em qualquer posição do LCD. basta informar a linha e a coluna para inicio do texto.

No caso de desejarmos escrever uma mensagem "ligado" na quarta linha, décima coluna do LCD 20 x 4, basta escrever o seguinte
comando:

lcd8_out(4,10,"ligado"); // escreve mensagem na quarta linha, décima coluna do lcd 20 x 4

Vamos escrever agora outro programa que escreve uma mensagem no display lcd 16 x 4:

/**************************************************************************
Centro de tecnologia Microgenios
Programa exemplo: LCD_modo_8bits_01.c
Este programa tem por objetivo escrever a mensagem "Maquina: ligado" na primeira
linha do LCD e na segunda linha "Prog: ligado".
**************************************************************************
*/

char *texto = "ligado"

void main()
{
trisd = 0; //configura todos os pinos do portd como saída
trise = 0; //configura todos os pinos do porte como saida
ADCON1 = 0x06; //configura todos os pinos de A/D como I/O de uso geral

lcd8_config(&porte, &portd,2,1,0,7,6,5,4,3,2,1,0); //configura e inicializa o LCD


lcd8_cmd(lcd_clear); //apaga LCD
lcd8_cmd(lcd_cursor_off); // desliga cursor
lcd8_out(1,1,"Maquina: "); // escreve mensagem na primeira linha do LCD
lcd8_out_cp(texto); // escreve mensagem na segunda linha do LCD
lcd8_out(2,1,"Prog: "); // escreve mensagem na segunda linha do LCD
lcd8_out_cp(texto); // escreve mensagem na segunda linha do LCD
delay_ms(10); //delay de 10 ms

while(1); //loop infinito

Neste exemplo repare que utilizamos a mesma configuração de hardware do LCD anterior. Repare que agora temos um comando novo
em nosso programa, o comando lcd8_out_cp, cuja função é escrever no lcd uma string ou variável na corrente posição do cursor.

Repare que o comando lcd8_out(1,1,"Maquina: "); deixa o cursor posicionada na sétima coluna da primeira linha do lcd. O
próximo comando que vem em seguida no programa é lcd8_out_cp(texto), que escreve na posição do cursor do lcd o valor da
variável "texto", que neste caso é a string "ligado".

Teremos como resultado:

Ma qu i n a : L i g a d o
P r og : L i g a do

LCD Modo 4 bits - Somente um port de controle/dados


No modo de 4 bits, devemos ligar apenas o nibble mais significativo (DB4 a DB7) do barramento de dados do LCD ao
microcontrolador, conforme o esquema elétrico seguinte:
Para exemplificar este modo 4 bits iremos apresentar um programa:

/**************************************************************************
Centro de tecnologia Microgenios
Programa exemplo: LCD_modo_4bits_00.c
Este programa tem por objetivo escrever a mensagem "teste LCD" na primeira
linha do LCD. Programa no modo 4 bits
**************************************************************************
*/

void main() {
TRISD = 0; // programa portD como saida
Lcd_Config(&PORTD,1,3,2,7,6,5,4); //configura e inicializa LCD
Lcd_Cmd(Lcd_CLEAR); // apaga display
Lcd_Cmd(Lcd_CURSOR_OFF); // desliga cursor
Lcd_Out(1, 1, "teste LCD"); // escreve texto "teste LCD" na primeira linha, primeira coluna do LCD

while(1);
}

Descrição detalhada do programa:

O programa acima tem a função de escrever na primeira linha do display lcd o texto "teste LCD". Repare na imagem acima que
utilizamos somente um port do PIC para controle e envio de dados para o LCD.

Os comandos seguintes controlam o display LCD no modo 4 bits. Vamos estudar cada um dos comandos:

O comando lcd_config ( ):

O comando lcd_config( ) determina quais são os pinos do barramento de dados e de controle do LCD que estão ligados ao PIC, assim
como também inicializa o LCD.

Sintaxe:

lcd_config(&port_controle/dados, RS, EN, RW, D7, D6, D5, D4 );

Onde:

port_controle e port_dados pode ser: PORTA, PORTB, PORTC, PORTD ou PORTE.


RS : pino do port_controle que determina o bit de instruções ou dado do LCD.
EN : pino do port_controle que determina o bit de "enable" do LCD.
RW : pino do port_controle que determina o bit de leitura /escrita do LCD. (nolmalmente coloca-se este pino do LCD no GND).
D7 à D4: pinos do port_dados

Vejamos nosso exemplo de programa:

Lcd_Config(&PORTD,1,3,2,7,6,5,4); //configura e inicializa LCD

A função lcd_config acima, determina o PORT e os pinos que participam do controle do LCD:

Port que controla o LCD:

PORTD

Funções dos pinos de controle do port de controle:

RS: 1 //pino RD2


EN: 3 //pino RD3
RW: 2 //qualquer valor de outro pino, pois o RW do LCD esta aterrado

Port de dados do LCD;

PORTD

Funções dos pinos do port de dados no LCD;

D7: 7 //pino RD7 do PORTD


D6: 6 //pino RD6 do PORTD
D5: 5 //pino RD5 do PORTD
D4: 4 //pino RD4 do PORTD

PODEMOS ATERRAR O PINO R/W DO LCD, POIS AS FUNÇÕES DE CONTROLE DE LCD DO MIKROC NÃO UTILIZA ESTE PINO
SIMPLESMENTE DEIXAM ESTE PINO EM NIVEL LÓGICO 0. CASO VENHAMOS A ATERRAR O PINO RW, ECONOMIZAREMOS 1 PINO DE
I/O DO PIC.

Os demais comandos do programa estudamos anteriormente, em caso de dúvida leia novamente o conteúdo do tópico lcd modo 8 bits.

LCD Modo 4 bits - Controlado por dois ports do PIC

Este é mais um dos modos 4 bits de trabalharmos com LCD. Neste novo modo podemos ter um port de controle e outro port do PIC para
dados. Veja o esquema abaixo:

Repare que para controlar o display LCD utilizamos dois ports do PIC, o PORTA para controle, e o PORTD como dados.

Vamos estudar um programa para entendermos os comandos de controle:

/**************************************************************************
Centro de tecnologia Microgenios
Programa exemplo: LCD_modo_4bits_01.c
Este programa tem por objetivo escrever a mensagem "Programa LCD" na primeira
linha do LCD. Programa no modo 4 bits
**************************************************************************
*/

void main() {

ADCON1 = 6; // programa pinos de A/D como I/O de uso geral


TRISA = 0; // programa portA como saida
TRISD = 0; // programa portD como saida
Lcd_custom_Config(&PORTD,0,2,4,5,&PORTA,1,2,3); //configura e inicializa LCD no modo 4 bits
Lcd_Cmd(Lcd_CLEAR); // apaga display
Lcd_Cmd(Lcd_CURSOR_OFF); // desliga cursor
Lcd_Out(1, 4, "Programa LCD"); // escreve texto "Programa LCD" na primeira linha, quarta coluna do LCD

while(1);
}

Descrição detalhada do programa:


O programa acima tem a função de escrever na primeira linha, quarta coluna do display lcd o texto "Programa LCD". Repare no
esquema eletrônico acima que temos dois ports do PIC ligado ao LCD.

Os comandos seguintes controlam o display LCD no modo 4 bits. Vamos estudar cada um dos comandos:

O comando lcd_custom_config ( ):

O comando lcd_custom_config( ) determina quais são os pinos do barramento de dados e de controle do LCD que estão ligados ao PIC,
assim como também inicializa o LCD.

Sintaxe:

lcd_config(&port_dados, D7, D6, D5, D4, &port_controle, RS, RW, EN );

Onde:

port_controle e port_dados pode ser: PORTA, PORTB, PORTC, PORTD ou PORTE.


RS : pino do port_controle que determina o bit de instruções ou dado do LCD.
EN : pino do port_controle que determina o bit de "enable" do LCD.
RW : pino do port_controle que determina o bit de leitura /escrita do LCD. (nolmalmente coloca-se este pino do LCD no GND).
D7 à D4: pinos do port_dados

Vejamos nosso exemplo de programa:

Lcd_custom_Config(&PORTD,0,2,4,5,&PORTA,1,2,3); //configura e inicializa LCD no modo 4 bits

A função lcd_custom_Config acima, determina os PORTs e os pinos que participam do controle e envio de dados para o LCD:

Port que controla o LCD:

PORTA

Funções dos pinos de controle do port de controle:

RS: 1 //pino RA1


EN: 3 //pino RA3
RW: 2 //qualquer valor de outro pino, pois o RW do LCD esta aterrado, neste caso conectados a
RA2

Port de dados do LCD;

PORTD

Funções dos pinos do port de dados no LCD;

D7: 0 //pino RD0 do PORTD


D6: 2 //pino RD2 do PORTD
D5: 4 //pino RD4 do PORTD
D4: 5 //pino RD5 do PORTD

Aprendendo Programando:

Apartir de agora, iremos desenvolver alguns exemplos de programas utilizando display LCD alfanuméricos.

1º Programa:
Vamos elaborar um programa utilizando os recursos das funções de display LCD do MikroC para que funcione da seguinte maneira:

Nosso hardware esta operando o LCD no formato 8 bits


Inicialmente será escrito uma string na primeira linha do display LCD e outra na segunda linha.
As mensagem deverão se deslocar 16 casas para a direita e posteriormente deverá retornar ao seu local de origem:
Cada deslocamente levará aproximadamente 200 milisegundos.

Microcontrolador: PIC18F4x2
Cristal: 8MHz

Acompanhe o harware:
figura 01 - Esquema elétrico

/*********************** Animação de Display LCD ************************


Centro de Tecnologia Microgenios
Programa: Animação LCD
Compilador: MikroC
Cristal: 8Mhz
Microcontrolador: PIC18F452
Objetivo: Deslocar as mensagem dos display LCD para a direita e para a esquerda
utilizando laço For.
*************************************************************************/

void main() {
unsigned char inc = 0; //declara variavel chamada "inc" como char

trisd = 0; //configura todos os pinos do portd como saída


trise = 0; //configura todos os pinos do porte como saida
ADCON1 = 0x06; //configura todos os pinos de A/D como I/O de uso geral

lcd8_config(&porte, &portd,2,1,4,7,6,5,4,3,2,1,0); //
lcd8_cmd(lcd_clear); //apaga display LCD
lcd8_cmd(lcd_cursor_off); //desliga cursor
lcd8_out(1,1,"Microgenios"); //escreve no display LCD
lcd8_out(2,1,"KIT PICGENIOS"); //escreve no display LCD

//*********** desloca mensagens do lcd para a direita *********************


for (inc = 0 ; inc < 16 ; inc++){
lcd8_cmd(LCD_SHIFT_RIGHT); //desloca mensagem para a direita
Delay_ms(200); //aguarda 200 milisegundos
}

//*********** desloca mensagens do lcd para a esquerda *********************


for (inc = 0 ; inc < 16 ; inc++){
lcd8_cmd(LCD_SHIFT_left); //desloca mensagem para a esquerda
Delay_ms(200); //aguarda 200 milisegundos
}

while(1); //loop infinito

}
Obs: repare neste programa que o pino R/W do display LCD foi aterrado, e na função lcd8_config foi declarado como sendo pino
RE4. O pino R/W normalmente não é utilizado nas maioria das aplicações, neste caso, como a função lcd8_config necessita de
algum parâmetro, informamos um valor qualquer, neste caso foi um pino inexistente no chip. (lembre-se que todos os ports do PIC são
composto por 8 bits, neste caso a posição de memória RE4 existe, porém não tem aplicação para este chip).

Regras do C: Se na declaração de uma função declararmos "n" parametros, necessáriamente deveremos enviar todos os "n"
parâmetros na chamada desta função.

2º Programa:
Nosso segundo programa, tem como objetivo escrever no display LCD o valor numérico 100 armazenado em uma variável do tipo char,
chamada "contador". Utilizaremos o mesmo esquema eletrônico apresentado do exemplo 01.

Acompanhe:
/*********************** Animação de Display LCD ************************
Centro de Tecnologia Microgenios
Programa: Animação LCD
Compilador: MikroC
Cristal: 8Mhz
Microcontrolador: PIC18F452
Objetivo: enviar para o display LCD o valor da variável contador
*************************************************************************/

void main() {
unsigned char contador = 100; //declara variável char contador e atribui o valor 100
unsigned char texto[10]; //cria vetor (array) do tipo char com 11 elementos (os indice começam do 0)

trisd = 0; //configura todos os pinos do portd como saída


trise = 0; //configura todos os pinos do porte como saida
ADCON1 = 0x06; //configura todos os pinos de A/D como I/O de uso geral

lcd8_config(&porte, &portd,2,1,4,7,6,5,4,3,2,1,0); //
lcd8_cmd(lcd_clear); //apaga display LCD
lcd8_cmd(lcd_cursor_off); //desliga cursor

bytetostr(contador, texto); //converte o valor numérico de contador e atribui ao vetor Texto


lcd8_out(1,1,texto); //escreve no display LCD o valor do vetor texto

while(1); //loop infinito

}
O programa deste exercício possui muitas informações importantes, dentre elas, a utilização de Arrays, ou como neste caso é chamado,
vetor.
Para que possamos enviar o valor numérico presente em uma variável qualquer, a principio precisamos converter cada um de seus
algorismo em caracterer, pois a função de display LCD do MikroC somente opera com caracteres e string.

Um erro comum (acredito que você já tentou), é utilizar as funções dessa forma:

lcd8_out(1,1,contador); //escreve no display LCD o valor da variável contador

Se você pensou que iria aparecer no display LCD o valor da variável contador no display LCD você se enganou.

Vamos analizar com mais calma:

Seu display LCD somente pode operar com caracterer, ou seja, char. Lembre-se que o barramento do LCD é de 8 bits, no qual permite o
envio de valores de 0 a 255 no máximo.
Cada caracterer no padrão ASCII é do comprimento de um char, neste caso você já começa a pensar...

"Tenho que enviar um caracter por vez no display LCD".

É exatamente isso, temos que enviar caracter por caracter, caso nossa intensão seja escrever uma mensagem no visor do LCD.

Nosso compilador possui algumas bibliotecas que irão nos ajudar e muito na elaboração do nosso programa. Utilizamos uma função
chamada bytetostr no programa, que tem por objetivo converter o valor numérico de uma variável para String e armazenar em uma
Tabela (vetor).
" Professor, estou com uma dúvida !! . Afinal qual é a diferença entre um caractere e uma string???

Na linguagem C diferenciamos um caractere de uma string através das aspas simples ' e aspas duplas ". Exemplo:

'A' ---> caractere A padrão ASCII


"A" ---> string A

mas qual é a diferença?

Caractere:
todo caractere ASCII possui um valor numérica (consulte o menu Tools > ASC Chart do MikroC)

String:
Não exite uma representação numérica para uma string, pois está é composta por caractere + 1 caractere de terminação, chamado "Null", cujo valor é Zero.
então teremos:

"A" ---> é igual a: --- > 'A' (caractere) + "\0" (caractere de terminação de string null)

Pense e responda:
Qual é o número que representa seu nome?

resp:
Não existe um número que represente seu nome, e isso acontece em qualquer string.

Veja como ficará o resultado no modo de debugação no MikroC:


Figura 02 - Debugação do programa anterior

Perceba o preenchimento do vetor Texto após a execução do comando: bytetostr(contador, texto);

3º Programa:

Este Terceiro programa tem por objetivo mostrar utilizar os recursos da memória CGRAM do display LCD. Neste exemplo, aparecerá
um pequeno "cavalinho" andando no display LCD alfanumérico.
Teste este programa no seu kit PICgenios e veja o resultado:

/*********************** Animação de Display LCD ************************


Centro de Tecnologia Microgenios
Programa: Animação LCD
Compilador: MikroC
Cristal: 8Mhz
Microcontrolador: PIC18F452
Objetivo: enviar para o display LCD o valor da variável contador
http://br.youtube.com/watch?v=NS6-T3bGNd4
*************************************************************************/

//1º caracterer************
const char character1[] = { 32 , 32 , 32 , 32 , 3 , 7 , 14 , 14};
const char character2[] = { 32 , 32 , 32 , 32 , 15 , 31 , 31 , 31};
const char character3[] = { 32 , 32 , 32 , 32 , 3 , 31 , 31 , 31};
const char character4[] = { 32 , 1 , 31 , 31 , 31 , 27 , 19 , 16};
const char character5[] = { 12 , 24 , 16 , 1 , 1 , 1 , 32 , 32};
const char character6[] = { 31 , 31 , 30 , 23 , 32 , 32 , 16 , 32};
const char character7[] = { 31 , 31 , 1 , 1 , 1 , 1 , 32 , 32};
const char character8[] = { 28 , 28 , 4 , 4 , 8 , 32 , 32 , 32};

const char character1a[] = { 32 , 32 , 32 , 32 , 32 , 7 , 15 , 30};


const char character2a[] = { 32 , 32 , 32 , 32 , 14 , 31 , 31 , 31};
const char character3a[] = { 32 , 32 , 32 , 32 , 7 , 31 , 31 , 31};
const char character4a[] = { 32 , 2 , 31 , 31 , 31 , 27 , 19 , 16};
const char character5a[] = { 16 , 32 , 32 , 1 , 1 , 1 , 32 , 32};
const char character6a[] = { 31 , 31 , 27 , 18 , 1 , 1 , 17 , 32};
const char character7a[] = { 31 , 31 , 1 , 1 , 1 , 1 , 32 , 32};
const char character8a[] = { 24 , 28 , 4 , 8 , 16 , 32 , 16 , 32};
const char character1b[] = { 32 , 32 , 32 , 7 , 15 , 14 , 28 , 24};
const char character2b[] = { 32 , 32 , 32 , 15 , 31 , 31 , 31 , 3};
const char character3b[] = { 32 , 32 , 32 , 3 , 31 , 31 , 31 , 31};
const char character4b[] = { 1 , 31 , 31 , 31 , 27 , 27 , 16 , 16};
const char character5b[] = { 19 , 19 , 2 , 2 , 4 , 32 , 32 , 32};
const char character6b[] = { 31 , 7 , 14 , 6 , 1 , 32 , 32 , 32};
const char character7b[] = { 15 , 7 , 19 , 15 , 32 , 16 , 32 , 32};
const char character8b[] = { 16 , 24 , 12 , 4 , 2 , 1 , 32 , 32};

void escreve(char pos_row, char pos_char, char pos_row1, char pos_char1,char num) {
char i;

if (num == 1){
LCD8_Cmd(64);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character1[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character2[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character3[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character4[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character5[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character6[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character7[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character8[i]);
LCD8_Cmd(LCD_RETURN_HOME);
lcd8_chr(pos_row, pos_char, 0);
LCD8_Chr_cp(1);
LCD8_Chr_cp(2);
LCD8_Chr_cp(3);
lcd8_chr(pos_row1, pos_char1, 4);
LCD8_Chr_cp(5);
LCD8_Chr_cp(6);
LCD8_Chr_cp(7);
}

if (num == 2){
LCD8_Cmd(64);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character1a[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character2a[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character3a[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character4a[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character5a[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character6a[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character7a[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character8a[i]);
LCD8_Cmd(LCD_RETURN_HOME);
lcd8_chr(pos_row, pos_char, 0);
LCD8_Chr_cp(1);
LCD8_Chr_cp(2);
LCD8_Chr_cp(3);
lcd8_chr(pos_row1, pos_char1, 4);
LCD8_Chr_cp(5);
LCD8_Chr_cp(6);
LCD8_Chr_cp(7);
}

if (num == 3){
LCD8_Cmd(64);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character1b[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character2b[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character3b[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character4b[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character5b[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character6b[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character7b[i]);
for (i = 0; i<=7; i++) LCD8_Chr_Cp(character8b[i]);
LCD8_Cmd(LCD_RETURN_HOME);
lcd8_chr(pos_row, pos_char, 0);
LCD8_Chr_cp(1);
LCD8_Chr_cp(2);
LCD8_Chr_cp(3);
lcd8_chr(pos_row1, pos_char1, 4);
LCD8_Chr_cp(5);
LCD8_Chr_cp(6);
LCD8_Chr_cp(7);
}

void main() {
unsigned char texto[10];

trisd = 0; //configura todos os pinos do portd como saída


trise = 0; //configura todos os pinos do porte como saida
ADCON1 = 0x06; //configura todos os pinos de A/D como I/O de uso geral

lcd8_config(&porte, &portd,2,1,4,7,6,5,4,3,2,1,0); //
lcd8_cmd(lcd_clear); //apaga display LCD
lcd8_cmd(lcd_cursor_off); //desliga cursor

while(1){

escreve (1,1,2,1,3);
delay_ms(100);
escreve (1,1,2,1,2);
delay_ms(100);
escreve (1,1,2,1,1);
delay_ms(100);

}
}
Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores
informações: suporte@microgenios.com.br
Conversor A/D do PIC
1.0 Introdução

Introdução
O PIC18F452 possui internamente 8 canais de A/D com resolução de 10 bits cada. Comu m conversor A/D com
resolução de 10 bits e tensão de referência de 5V, podemos obter um valor de 4,8876.. mV, pois 5V/ (2E10 - 1) =
4,8876... mV.

Os conversores A/D dos PICs utilizam uma técnica de aproximação sucessivas, normalmente com resolução de 10 bits,
com clock selecionável pelo usuário e múltiplas entradas multiplexadas.

Canais AD multiplexados

Os conversores A/D do nosso microcontrolador estão ligados nos pinos RA0 (AN0), RA1 (AN1), RA2 (AN2), RA3
(AN3), RA5 (AN4), RE0 (AN5), RE1 (AN6), RE7 (AN7). Os registradores que participam da configuração do conversor
A/D são: ADCON0 e ADCON1.

Nosso A/D possui resolução de 10 bits (8 + 2 bits), onde os valores das conversões são armazenados nos registradores
ADRESH e ADRESL.

Registrador ADCON0:

vejamos o funcionamento de cada bit desse registrador:

ADCON0:
ADCS1 ADCS0 CHS2 CHS1 CHS0 GO/DONE ----- ADON

Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0

ADCS1 , ADCS2: Bits de seleção de velocidade de conversão A/D


tabela 1.0

CHS2, CHS0: Bit de seleção de canal A/D

GO/DONE: Bit de status da conversão A/D


0 - Conversão A/D não está sendo realizada
1 - Conversão A/D está sendo realizada

ADON: Liga ou desliga o A/D


0 - Conversor A/D desligado
1 - Conversor A/D ligado

Antes de configurarmos os registradores ADCS2, ADCS1 e ADCS0, devemos levar em consideração a


frequência do cristal que estamos utilizando, pois nosso conversor necessita de 1,6us para fazer a conversão.
A tabela seguinte apresenta os valores de configuração dos registradores ADCS2, ADCS1 e ADCS0 para as
faixas de frêquencia do cristal utilizado.

Registrador ADCON1:

vejamos o funcionamento de cada bit desse registrador:

ADCON1:
ADFM ADCS2 ------ ----- PCFG3 PCFG2 PCFG1 PCFG0

Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0

ADFM : Ajusta o formato do resultado da conversão A/D


0 - Justifica a direita
1 - Justifica a esquerda

ADCS2 : Bit de seleção da velocidade de conversão


*consulte a tabela 1.0

PCFG3, PCFG2, PCFG1, PCFG0: bits configuração do A/D


tabela 2.0

O bit de ADFM tem a função de organizar o resultado da conversão A/D, de forma que o os valores convertidos sejam
justificados a direita ou a esquerda nos registradores ADRESH e ADRESL. Caso venhamos configurar ADFM = 1,
organizamos o valor da conversão a direita, ou seja, os oitos bits menos significativo será armazendo em ADRESL, e os 2
bits mais significativo serão armazenados em ADRESH.

Caso ADFM = 0, justificaremos a esquerda os valores de conversão, desta forma os oitos bits mais significativos ficarão
em ADRESH e os 2 menos significativo ficará em ADRESL.

Através dos bits PCFG3, PCFG2, PCFG1, PCFG0 informamos quais os pinos que serão A/D ou I/O digital.

Exemplo (consulte tabela 2.0):

ADCON1 = 0 ; //programamos todos os pinos RA0, RA1 , RA2 , RA3, RA5, RE0, RE1, RE2 como A/D
ADCON1 = 6 ; //programamos todos os pinos RA0, RA1 , RA2 , RA3, RA5, RE0, RE1, RE2 como I/O de uso geral
ADCON1 = 14 ; //programamos os pinos RA1 , RA2 , RA3, RA5, RE0, RE1, RE2 como I/O de uso geral e somente RA0
como AD (referência VDD).
ADCON1 = 4 ; //programamos os pinos RA2 , RA5, RE0, RE1, RE2 como I/O de uso geral e RA0, RA1 e RA3 como
AD (referência VDD).

Nosso módulo possui um conversor com resolução máxima de 10 bits, ou seja, 1024 divisões (0 a 1023 divisões).
Podemos configurar e determinar tensões de referência externa para nosso conversor A/D através dos pinos AN3 e AN2,
ou selecionar a tensão interna do chip (vcc e gnd) como referência.

Fórmula de conversão analógico para digital:

Rad = ( Vin - Vref-) * 1023 / (Vref+ - Vref-)

onde:

Rad = Resultado da conversão

Vref+ e Vref- = valor de tensão de referência máxima e mínima

Vin = Tensão de entrada no pino A/D

obs: 1023 é o valor máximo de conversão do A/D, lembrando que o valor é de 0 a 1023.
Analize o esquema elétrônico seguinte:
figura 01

Analizando o esquema eletrônico apresentado, vamos encontrar o valor analógico convertido para digital:

Digamos que o valor da tensão Vin no momento de leitura do AN0 seja de 2V.

Nossa Fórmula:
Rad = ( Vin - Vref-) * 1023 / (Vref+ - Vref-)
logo:
Rad = (2 - 1) *1023 / (3 - 1)
Rad = 1 * 1023 / 2
Rad = 511

OBS: A TENSÃO DE VREF EXTERNA NUNCA PODERÁ ULTRAPASSAR A TENSÃO DE ALIMENTAÇÃO


DO MICROCONTROLADOR.

Trabalhando com o MikroC

No mikroC utilizamos os seguinte função para leitura do conversor A/D do PIC:

sintaxe:

Adc_Read(canal_ AD);

onde:

Canal_AD: canal do conversor AD do PIC que desejamos ler. O número do canal AD depende do tipo de PIC usado.

Exemplo:

int leitura_ad;
leitura_ad = Adc_Read(0); //le canal ad0 do PIC e salva valor na variável temp_res

A função adc_read salva o valor da conversão AD (canal 0 - pino RA0) na variável leitura_ad.

Lembre-se que o valor da resolução do AD do PIC18F452 é de 10 bits, e por esse motivo devemos utilizar uma variável
do tipo inteiro para armazenar o valor do A/D.

Estudo do registrador ADCON1:

O registrador ADCON1 é responsável por configurar o modo de trabalho dos pinos com A/D. Através dos bits PCFG3,
PCFG2, PCFG1, PCFG0 configuramos o pino como A/D ou como I/O de uso geral.

Exemplo de programa:

Acompanhe o exemplo simples onde utilizamos um potenciometro a entrada RA0 e um display LCD 16x2 para
visualizarmos o valor da conversão do AD.

Exemplo de programa:

/****************************************************************************
Centro de Tecnologia Microgenios
Programa: Diplay_7_seg_01
Placa: KIT PICGENIOS
Objetivo: este programa tem por função ler o canal AD0 e escrever no lcd
o valor de conversão
*******************************************************************************
*/

char texto[16];
int temp_res = 0;

void main() {
trisd = 0; //define portd como saida
trise = 0; //define porte como saida
trisa.f0 = 1; //define pinos RA0 como entrada
ADCON1 = 0B00001110; //configura pino RA0 como entrada analógica e os demais com I/O digital

Lcd8_Config(&PORTE,&PORTD,2,1,4,7,6,5,4,3,2,1,0); //inicializa lcd


Lcd8_Cmd(Lcd_Clear); //apaga lcd
Lcd8_Cmd(LCD_CURSOR_OFF); //desliga cursor do lcd
Lcd8_Out(1, 1, "Canal AN0: "); //escreve mansagem na linha 1, coluna 1 do lcd
Delay_ms (10); //delay 10 milisegundos

do
{
temp_res = Adc_Read(0); //le canal ad0 do PIC e salva valor na variável temp_res
Delay_10us; //delay de 10 microsegundos
wordToStr(temp_res, texto); //converte valor da conversão do ad0 para string
lcd8_out(1,11,texto); //escreve no lcd o valor da conversão do ad0
}while (1);

Detalhes do programa:

Primeiramente definimos as variáveis globais do programa

char texto[16];
int temp_res = 0;

Depois iniciamos a função main() e definimos o sentido dos ports que participam do controle do LCD: Repare que
programamos o registrador ADCON1 com o valor 2, pois o porte do PIC é utilizado como I/O para controlar o LCD.

trisd = 0; //define portd como saida


trise = 0; //define porte como saida
trisa.f0 = 1; //define pinos RA0 como entrada
ADCON1 = 0B00001110; //configura pino RA0 como entrada analógica e os demais com I/O digital

Os comandos seguintes são utilizados para controle e escrita do display LCD.

Lcd8_Config(&PORTE,&PORTD,2,1,4,7,6,5,4,3,2,1,0); //inicializa lcd


Lcd8_Cmd(Lcd_Clear); //apaga lcd
Lcd8_Cmd(LCD_CURSOR_OFF); //desliga cursor do lcd
Lcd8_Out(1, 1, "Canal AN0: "); //escreve mansagem na linha 1, coluna 1 do lcd
Delay_ms (10); //delay 10 milisegundos

O comando seguinte lê o canal AN0 do PIC e salva o valor na variável temp_res.

temp_res = Adc_Read(0); //le canal ad0 do PIC e salva valor na variável temp_res

A função abaixo tem pertence ao compilador mikroC e tem por objetivo converter o valor de uma variável numérica do
tipo word em string. Esta conversão é necessária pois iremos escrever o valor numérico da variável temp_res, resultado da
conversão AN0, e escrever no LCD. A variável que assimilará o valor convertido em string é chamada de "texto".

wordToStr(temp_res, texto); //converte valor da conversão do ad0 para string

Em seguida enviamos para o LCD o valor da variável texto.

lcd8_out(1,11,texto); //escreve no lcd o valor da conversão do ad0

Projeto: Leitura do sensor de Temperatura LM35


Para solidificarmos a teoria de leitura do A/D do PIC, vamos efetuar a leitura de sensor de temperatura LM35 conectado
ao canal AN1 e de um trimpot conectado ao canal AN0 do PIC. O valor da conversão deverá ser escrito no LCD:

Esquema elétrico:

Programa:

/****************************************************************************
Centro de Tecnologia Microgenios
Programa: Diplay_7_seg_01
Placa: KIT PICGENIOS
Objetivo: este programa tem por função ler o canal AD0 e AD1 e escrever no lcd
o valor de conversão
*******************************************************************************
*/

char texto[16];
int leitura_an0 = 0;
int leitura_an1 = 0;

void main() {
trise = 0; //define portb como saida
trisd = 0; //define portd como saida
ADCON1 = 0b00000100; //habilita canal A/D 0 e A/D1 do PIC
trisa = 0b00001111; //define pinos como entrada

Lcd_custom_Config(&PORTD,7,6,5,4,&PORTE,2,0,1); //inicializa lcd


Lcd8_Cmd(Lcd_Clear); //apaga lcd
Lcd8_Cmd(LCD_CURSOR_OFF); //desliga cursor do lcd
Lcd8_Out(1, 1, "Canal AN0: "); //escreve mansagem na linha 1, coluna 1 do lcd
delay_ms (10); //delay de 10ms
Lcd8_Out(2, 1, "Canal AN1: "); //escreve mensagem na linha 2, coluna 1 do lcd
delay_ms (10); //delay 10 milisegundos

do
{
leitura_an0 = Adc_Read(0); //le canal ad0 do PIC e salva valor na variável leitura_an0
wordToStr(leitura_an0, texto); //converte valor da conversão do ad0 para string
lcd8_out(1,11,texto); //escreve no lcd o valor da conversão do ad0

leitura_an1 = Adc_read(1); //lê canal ad1 do PIC e salva valor na variável leitura_an1
leitura_an1 = leitura_an1 / 2.048; //ajusta a escala de conversão do sensor de temperatura
wordToStr(leitura_an1, texto); //converte valor da conversão do ad1 para string
lcd8_out(2,11,texto); //escreve no lcd o valor da conversão do ad1

}while (1);
}

O o sensor de temperatura utilizado em nosso projeto é o LM35 fabricado pela empresa National Semicondutores. Este
circuito integrado é um sensor de temperatura linear cujo valor de saida de tensão é diretamente proporcional à
temperatura. Sua escala já é calibrada em graus celsius com saida de 10mV / ºC no ranger minimá de - 55 ºC a +150 ºC.

A resolução do A/D de nosso microcontrolador é de 10 bits e em nosso programa utilizamos a tensão de referencia interna
do A/D (5V).

Calculo para leitura correta do sensor de temperatura:

Resolução do PIC:

Tensão de referencia (resolução do AD - 1) = resolução

5V (1024 - 1) = 4,8876 mV

A resolução do A/D do PIC para tensão de referencia de 5V é de 4,8876mV

Resolução do sensor LM35:

10mV/ºC (dado fornecido pelo fabricante)

Logo teremos

10 / 4,8876 = 2,048

Em nosso programa arrendondamos o valor 2.046 para 2 para que nosso programa não ultrapasse o valor limite demo do
compilador (2Kbyte).

leitura_an1 = adc_read(1); //lê canal ad1 do PIC e salva valor na variável leitura_an1
leitura_an1 = leitura_an1 / 2.048; //ajusta a escala de leitura do sensor lm35.
Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso.
Maiores informações: suporte@microgenios.com.br
Canal PWM do PIC
Introdução

Os controles de potência, inversores de freqüência, conversores para servomotor, fonte chaveadas e muitos outros circuitos utilizam a tecnologia do
PWM (Pulse Width Modulation) ou Modulação de Largura de Pulso como base de seu funcionamento.

Fonte Chaveada ServoMotores

Inversores de Freqüência

PWM é a abreviação de Pulse Width Modulation ou Modulação de Largura de Pulso. Para que se entenda como funciona esta tecnologia no controle de
potência, partimos de um circuito imaginário formado por um interruptor de ação muito rápida e uma carga que deve ser controlada, conforme a
figura abaixo:

Quando o interruptor está aberto não há corrente na lâmpada e a potência aplicada é nula. No instante em que o interruptor é fechado, a carga
recebe a tensão total da fonte e a potência aplicada é máxima.
Como fazer para obter uma potência intermediária, digamos 50%, aplicada à carga? Uma idéia é fazermos com que a chave seja aberta e fechada
rapidamente de modo a ficar 50% do tempo aberta e 50% fechada. Isso significa que, em média, teremos metade do tempo com corrente e metade
do tempo sem corrente.

A potência média e, portanto, a própria tensão média aplicada à carga é neste caso 50% da tensão de entrada.

Veja que o interruptor fechado pode definir uma largura de pulso pelo tempo em que ele fica nesta condição, e um intervalo entre pulsos pelo tempo
em que ele fica aberto. Os dois tempos juntos definem o período e, portanto, uma freqüência de controle. A relação entre o tempo em que temos o
pulso e a duração de um ciclo completo de operação do interruptor nos define ainda o ciclo ativo.
Variando-se a largura do pulso e também o intervalo de modo a termos ciclos ativos diferentes, podemos controlar a potência média aplicada a uma
carga. Assim, quando a largura do pulso varia de zero até o máximo, a potência também varia na mesma proporção (duty cycle), conforme está
indicado na figura abaixo:

Este princípio é usado justamente no controle PWM: modulamos (variamos) a largura do pulso de modo a controlar o ciclo ativo do sinal aplicado a
uma carga e, com isso, a potência aplicada a ela.

Trabalhando com PWM no PIC

O microcontrolador PIC18F442 possui internamente 2 módulos CCP (capture/Compare/PWM Module), CCP1 e CCP2. Podemos manipular com grande
facilidade as instruções para geração de sinal PWM no mikroC. Acompanhe:

Os comandos para manipular o módulo CCP1 são os seguintes:

Pwm_Init
Pwm_Change_Duty
Pwm_Start
Pwm_Stop

Função de Inicialização da geração do sinal PWM:

No mikroC utilizamos a função Pwm_Init() para informar a freqüência do sinal PWM:

//Sintaxe

Pwm_Init(valor_da_freqüência)

onde:

valor_da freqüência: fator da freqüência em Hz do sinal PWM.

PS.: Consultar datasheet do PIC utilizado para saber a freqüência de oscilação.

Exemplo:

Pwm_Init(4000); // inicia pwm com freqüência de 4khz

Função de duty Cycle:

A través da função Pwm_Change_Duty() podemos controlar o duty cycle do sinal PWM. O valor do duty cycle varia de 0 a 255, onde "0" é igual a 0%, "127" igual a 50% e "255" igual a
100%.

Sintaxe:

Pwm_Change_Duty(valor_duty_cycle)

onde:

valor_duty_cycle: valor do tipo char (0 à 255) que determina a porcentagem do duty cycle PWM.

Exemplo:

Pwm_Change_Duty(192); // carrega duty cycle para 75%


Para outros valores de duty cycle calculamos através da formula: (Percentual * 255) /100

No caso de configurarmos o duty conforme a figura acima, devemos carregar o seguinte valor para:

Duty de 10%

Pwm_Change_Duty(25); // carrega valor 26 pois : (10% * 255) / 100 = 25,5 , como somente podemos colocar valores inteiros entre 0 a 255, o valor será arrendondado para 25

Duty de 50%

Pwm_Change_Duty(127); // carrega valor 127 pois : (50% * 255) / 100 = 127,50 , como somente podemos colocar valores inteiros entre 0 a 255, o valor será arrendondado para 127

Duty de 90%

Pwm_Change_Duty(229); // carrega valor 229 pois : (90% * 255) / 100 = 229,50 , como somente podemos colocar valores inteiros entre 0 a 255, o valor será arrendondado para 229

Função de Start e Stop:

Através da função Pwm_Start(), damos início a geração do sinal PWM no PIC, e através da função Pwm_Stop() finalizamos a geração do sinal Pwm.

Sintaxe:

Pwm_Start(); // inicial a geração do sinal PWM no módulo CCP1 do PIC


Pwm_Stop(); // interrompe a geração do sinal PWM no módulo CCP1 do PIC

Nem todos os PIC possuem um segundo módulo CCP2, para os PIC que possuem este segundo módulo ou mais módulos, basta acrescentar o número 1 ou 2 nas funções para informar o
módulo que está sendo programado:
Exemplo:

Pwm1_Init
Pwm1_Change_Duty
Pwm1_Start
Pwm1_Stop

As funções acima faz referência ao módulo CCP2 do PIC e possue configuração identica a função do módulo CCP1 estudado acima.

Exemplo de programa:

Para entendermos melhor o funcionamento da função Pwm do mikroC, vamos estudar um exemplo de programa simples, acomanhe:

Programa:

void main() {

Pwm_init(5000);
Pwm_Change_Duty(127);
Pwm_Start();

while(1);
}

No programa acima configuramos o módulo CCP1 do PIC para trabalhar como PWM com freqüência de 5Khz e duty cycle de 50%, conforme gráfico abaixo:
No qual:

Freqüência = 1 / período --> 5Khz


Duty = 50%

Segundo Programa

Vamos programar o módulo CCP2 do PIC18F442:

Programa:

void main() {

Pwm1_init(5000);
Pwm1_Change_Duty(127);
Pwm1_Start();

while(1);
}

repare que o programa acima tem a mesma função do programa anterior, a única diferença é que colocamos o número 1 nas funções para indicar que estamos trabalhando com o segundo
módulo CCP do PIC, ou seja, o módulo CCP2.

Controlando a velocidade de uma ventoinha:

Vamos elaborar um programa em C cuja finalidade é carregar na função Pwm_change_duty() o valor lido do conversor AN0 do PIC, ou seja, iremos ler constantemente o valor do AD AN0
do PIC, tratar o valor númerico da conversão para que fique na escala de 0 a 255, pois esta é a escala de 0% a 100% de duty do PWM. A saída PWM do PIC controlará a velocidade de
rotação de uma ventoinha.

Esquema:

Programa:
/****************************************************************************
Centro de Tecnologia Microgenios
Programa: Leitura_AD_e_PWM
Placa: KIT PICGENIOS
Objetivo: Controlar uma ventoinha via PWM através da entrada AD0.
*/

//**************** definição de variáveis ********************************

void main() {

int a = 0; //define variável

ADCON1 = 0b00001001; //reconfigura e define pinos de A/D


trisa = 255; //define os pinos como saida

Pwm_Init(5000); // Inicializa módulo PWM com 5Khz


Pwm_Start(); // Start PWM

do // rotina de loop
{
a = Adc_Read(0); //lê canal ad0 e salva em temp_res
a = (a * 0.24); //converte valor para o duty cycle
Pwm_Change_Duty(a); //envia o valor lido de "a" para o módulo CCP1 pwm
Delay_10us; //aguarda 10us
}
while (1);

Termos como resultado o valor a seguinte forma de onda:

O programa exemplo foi compilado no mikroC e simulado no programa de simulação PROTEUS (www.labcenter.co.uk).

Voltar para o topo da página

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Introdução ao estudo dos TIMERS
1.0 Os Timers/Counters
1.1 TIMER0
1.2 TIMER1
1.3 TIMER2
1.4 TIMER3

Os Timers/Counters
Os timers são ferramentas internas dos microcontroladores em geral que servem para contagem de tempo, eventos,
temporização entre outras funções.

O PIC18F452 possui internamente 4 TIMERS:

TIMER0

TIMER1

TIMER2

TIMER3

Vamos conhecer cada um desses temporizadores:

TIMER0

O TIMER0 é um temporizador/contador de 8 ou 16 bits que possuem duas funções básicas:


Contagem de eventos externos (quando a entrada de clock é feita por meio do pino RA4/T0CKI;
Temporização (contagem de tempo) quando os pulsos de contagem é proveniente do clock interno do PIC
(o clock do sistema dividido por 4).
O TIMER0 pode ser configurado para trabalhar com prescaler dedicado. O prescaler nada mais é do que um
contador/divisor programável que é utilizado para reduzir a freqüência do sinal de clock aplicado ao TIMER0 por um fator
conhecido.

O valor do prescaler pode ser configurado a partir dos bits T0PS2, T0PS1 e T0PS0 do registrador T0CON (TIMER0
CONTROL REGISTER);
O prescaler passa a ser uma importantíssima ferramentas do timer, pois através dele conseguimos gerar tempos
muito maiores.
Quando o TIMER0 é configurado para operar no modo de 8 bits, podemos efetuar contagens de 0 a 255 (limite
da capacidade para 8 bits). Quando a contagem chega até seu valor máximo de 255, o próximo pulso acarretaria o que
chamamos de "estouro de contagem", fazendo com que o valor de contagem de início novamente a partir do 0 (zero).
No caso anterior, caso tenhamos a interrupção do TIMER0 habilitada, no momento que ocorre o "estouro de
contagem", seria gerado um pedido de interrupção do TIMER0.
No modo 16 bits do TIMER0, seu funcionamento é igual ao modo de 8 bits, porém neste cado a faixa de
contagem é de 0 a 65535.
Os valores de iniciais de temporização/contagem devem ser carregados nos registradores especiais intitulado de
TMR0L (TIMER0 Module Low Byte Register) e TMR0H (TIMER0 Module Hight Byte Register);
Quando programamos os timers para atuarem como temporizadores, estamos considerando que os pulsos que
incrementam os registradores de contagem são proveniente do valor do oscilador / 4, ou seja, caso estivermos utilizando
um cristal de 4MHz, iremos incrementar em uma unidade os registradores de contagem a cada 1 us, pois 4Mhz/4 =
1MHz = 1us (microsegundos).

Dica:

Repare que a unidade resultante da divisão da Frequencia do oscilador / 4 (ciclo de máquina) esta em MHz ( unidade
de frequência), neste caso para sabermos o tempo (periodo), basta dividir 1 / seu valor.

Ex: 1 / (Fosc / 4 )

Para um cristal de 8 MHz teremos o seguinte tempo de ciclo de máquina:


1 / (8 / 4) = 0,5 us (microsegundos).

Os Registradores relacionados ao TIMER0 são:


Figura 01 - Registrador relacionados com o TIMER0

TMR0L é um registrador de contagem de 8 bits que possui a função de armazenar a parte baixa do valor de contagem
programada do TIMER0.

TMR0H é um registrador de contagem de 8 bits que possui a função de armazenar a parte alta do valor de contagem
programada do TIMER0.

Os registradores TMR0L e TMR0H juntos formam um único registrador de 16 bits, que nos permite uma contagem máxima de
0 a 65535.

INTCON é um registrador de 8 bits responsável pela configuração do modo de operação do TIMER0. Podemos definir através
desse registrador o valor do prescaler que desejamos acionar, o modo de operação de contagem de 8 ou 16 bits, seleção da fonte
de clock (interna ou externa) para o timer, seleção de disparo do timer através de borda de subida ou borda de descida do clock
externo no pino RA4/T0CK1.

Faça download do diagrama esquemático do TIMER0 disponível na informações complementar dessa unidade.

Registradores de configuração do TIMER0:


O registrador T0CON é responsável pela configuração pelo modo de operação do Timer0, é de vital importância conhecer todas
as funções de seus bits.

T0CON: (TIMER0 Counter Register)


TMR0ON T08BIT T0CS T0SE PSA T0PS2 T0PS1 T0PS0

Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0

TMR0ON: Bit de controle para ligar e desligar a contagem do TIMER0;


0 - TIMER para de contagem
1 - Habilita a contagem do TIMER0
T08BIT: Bit de seleção de funcionamento do TIMER0 em 8 ou 16 bits;
0 - TIMER0 configurado para funcionar como contador/temporizador em 16 bits;
1 - TIMER0 esta configurado para funcionar como contador /temporizador de 8 bits;
T0CS: Bit de seleção de fonte de clock para o TIMER0;
0 - A fonte de clock é proveniente do meio interno do chip
1 - A fonte de clock é proveniente do pino T0CKI (meio externo)
T0SE: Bit de seleção de borda (válido somente caso a fonte de clock seja pelo meio externo ao chip)
0 - Contagem do timer ocorre por borda de subida no Pino T0CKI
1 - O incremento do timer ocorre na borda de descida no pino T0CKI
/PSA: Bit de seleção da utilização do prescaler;
1 - TIMER0 não utiliza prescaler. A cada alteração do pulso de clock, corre incremento de uma unidade nos
registradores de contagem.
0 - TIMER0 utiliza prescaler.

T0PS2; T0PS1; T0PS0 : Bits de seleção de fonte de prescaler

T0PS2 T0PS1 T0PS0 Prescaler

1 1 1 1:256

1 1 0 1:128

1 0 1 1:64

1 0 0 1:32

0 1 1 1:16

0 1 0 1:8

0 0 1 1:4

0 0 0 1:2
Configuração do Prescaler:

Como sabemos, através do prescaler conseguimos tempos maiores com os timers, para entendermos melhor sua
utilização acompanhe o exemplo abaixo:
Digamos que o ciclo de máquina no PIC sejá de 1us e o TIMER0 esteja configurado no modo 8 bits (contagem de
0 a 255) e sem o presacaler. O TIMER0 ira "estourar" sua contagem em 256us.
Agora digamos que para o mesmo anunciado anterior, configuramos e acionamos o prescaler para 1:2. Nosso
intervalo de "estouro" do TIMER0 não mais será 256us mas sim 512us.
Se ao invés de prescaler de 1:2 utilizarmos prescaler 1:32, o tempo agora será de 256us x 32 = 8192us.

Como calcular o tempo de estouro do TIMER0?


Para calcular o tempo de "estouro" do TIMER com prescaler, utilizamos a seguinte fórmula:

Tempo de estouro: ciclo de máquina x prescaler x (modo 8 /16bits – valor de contagem).

Em que:

Ciclo de máquina: é o valor da relação entre: 1 / (Fosc / 4 ), onde Fosc é justamente a frequeência do cristal.
Obs: modo PLL desabilitado, iremos estudar esta função mais adiante.
Prescaler : é o fator de divisão programado. Podemos considerar como sendo um "redutor" de frequencia.
Modo 8/16bits: é o modo de contagem programado no TIMER0, para 8 bits o valor é 256, e para 16 bits, o valor será
de 65535.
Valor de contagem: é o valor de carregagem no registrador de contagem TMR0H eTMR0L.

Exemplo:

Precisamos ligar e desligar um relé a cada segundo ( um segundo ligado e um segundo desligado), estamos utilizamdo
um cristal de 8Mhz, utilizaremos para isso os recursos do Temporizador Timer0, acompanhe:

T0CON = 0B10000110; //configura timer modo 16 bits, com prescaler 1:128, fonte de clock interno
TMR0L = 0XF6; //carrega valores de contagem C2F6 equivale a
TMR0H = 0XC2; //carrega valores de contagem

Calculo do tempo de estouro do Timer0:

Tempo de estouro: 1 / (8 / 4) x 128 x (65536 – 49910 )


Tempo de estouro: 1.000.064 us (microsegundos) ou aproximadamente 1 segundo.

obs: o valor 49910 surgio da conversão do valor em hexadecimal F6C2 para decimal

O registrador INTCON

O registrador INTCON possui diversar funções, entre elas a de habilitar algumas interrupções do PIC (veremos mais
adiante no nosso curso) e de armazenar os bits de estatus de estouro do TIMER0:

INTCON (Interrupt Control)

GIE/GIEH PEIE/GIEL TMR0IE INT0IE RBIE TMR0IF INT0IF RBIF

Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0

Iremos estudar todos esses bits mais adiante em nosso curso, pois eles tratam das configurações das interrupções, e
neste momento não é importante conhecer alguns deles. Vamos apresentar somente os bits que são utilizados nos TIMERS.

INTCON. GIEH = bit de acionamento da "chave geral das interrupções" e habilitação das interrupções de alta prioridade
0 - liga a chave geral das interrupções
1 - desliga a chave geral das interrupções
INTCON. GIEL = bit de habilitação das interrupções de baixa prioridade
0 - TIMER0 não ocorreu estouro de contagem (overflow)
1 - TIMER0 ocorreu estouro de contagem (overflow). Este bit deve ser apagado por software.

INTCON.TMR0IE = bit que habilita interrupção do TIMER0 na ocorrência do estouro de contagem.


0 - Desabilita interrupção do TIMER0 por ocorrencia de estouro de contagem (overflow)
1 - Habilita interrupção do TIMER0 por ocorrência de estouro de contagem (overflow).
INTCON. TMR0IF = bit sinalizador de estouro do TIMER0
0 - TIMER0 não ocorreu estouro de contagem (overflow)
1 - TIMER0 ocorreu estouro de contagem (overflow). Este bit deve ser apagado por software.
Aprendendo a programar os TIMER0:

Conhecemos os registradores responsáveis pela configuração do TIMER0 do PIC, agora vamos configurá-los:
Vamos nos recordar da fórmula do TIMER0:

Tempo de estouro: ciclo de máquina x prescaler x (modo 8 /16bits – valor de contagem).

Exemplo 1 :

Se tivermos conectado ao nosso PIC um cristal de 8 MHz e o TIMER0 seja programado como temporizador, com prescaler de
1:4, modo 8 bits e contagem inicial em TMR0L = 0, teremos então:

Tempo de estouro: ciclo de máquina x prescaler x (modo 8 /16bits – valor de contagem).


Tempo de estouro: 0,5us * 4 *(255 - 0)
Tempo de estouro: 510us

Exemplo 2 :

Se tivermos conectado ao nosso PIC um cristal de 8 MHz e o TIMER0 seja programado como temporizador, com prescaler de
1:256, modo 16 bits e contagem inicial em TMR0L = 0 e TMR0H = 0, teremos então:

Tempo de estouro: ciclo de máquina x prescaler x (modo 8 /16bits – valor de contagem).


Tempo de estouro: 0,5us * 256 * (65536 - 0)
Tempo de estouro: 8.388.608us, ou seja, 8, 388 segundos

Obs: Repare que este é o tempo máximo de estouro do TIMER0

Vamos realizar mais um exemplo de programa:

vamos supor que desejamos que um led conectado ao pino RB0 do PIC pisque em intervalos de 1 segundo (ligado e
desligado) utilizando os recursos de temporização do TIMER0.
Cristal: 8Mhz
PIC18F452
TIMER0 / MODO 16BITS

Acompanhe o esquema eletrico abaixo:

Primeiramente devemos programar os TIMER0:


Para este exemplo, vamos programar o TIMER0 no modo 16 bits (contagem de 0 a 65535), prescaler de 1:16, clock
interno, cristal de 8Mhz e modo HSPLL desativado (multiplicador do oscilador), termos então:

tempo de estouro do TIMER0 = ciclo de máquina x prescaler x (modo 8/16 bits - valor de contagem do TIMER0)
logo:
1 000 0000 = 0,5 * 16 * (65536 - X)
x = 59464

Concluimos que o valor de carregagem em TMR0L e TMR0H deverá ser de 59464, ou seja, TMR0L = 0X48; , TMR0H =
0XE8; (valores 59464 convertido em hexadecimal é igual a E848)
T0CON = 0b10000011; //configura temporizador 16 bits, com prescaler ativado em 1:16, clock interno
TMR0L = 0x48; //carrega valores de contagem em TMR0L - registrador baixo de contagem
TMR0H = 0xE8; //carrega valores de contagem em TMR0H - registrador alto de contagem

O programa abaixo tem por função configurar o modo de trabalho do TIMER0. Repare que o registrador T0CON foi
configurado da seguinte forma:

TIMER0 ligado. (TMR0ON = 1)


Clock interno (T0CS = 0)
Modo de contagem de 16 bits (T08BIT = 0)
Prescaler ligado (PSA = 0)
Prescaler 1:16 (T0PS2 = 0, T0PS1 = 1, T0PS0 = 1)

Os registradores de contagem TMR0L e TMR0H serão inicializados com o valor 59464.

Vamos realizar mais um exemplo de programa:

O programa seguinte tem por objetivo alterar os estados dos leds conectados ao PORTB em intervalos de 1 segundo.
Hardware:
Cristal: 8 Mhz
Microcontrolador: PIC18f452
Temporizador: Timer0

/*********************************************************************
Centro de tecnologia Microgenios
Placa: Kit PICGenios
Programa: Pisca Pisca utilizando o TIMER0 do PIC
Objetivo: Este programa tem por objetivo piscar os leds do portb em
intervalo de 1 segundo (ligado e desligado) utilizando o TIMER0 do PIC.
Modo programado: 16 bits, prescaler 1:128
***********************************************************************/

void main() { //função principal do programa


T0CON = 0b10000110; //configura timer modo 16 bits, com prescaler
TMR0L = 0XF7; //carrega valores de contagem
TMR0H = 0XC2; //carrega valores de contagem
intcon.tmr0if = 0;
TRISB = 0; // PORT B configurado como saída
PORTB = 0xFF; // Initialize PORT B

do {
if (intcon.tmr0if == 1) { //se o flag de estouro do TIMER0 for igual a 1, então
PORTB = ~PORTB; // inverte o estado do portb
TMR0L = 0xF7;
TMR0H = 0XC2;
Iintcon.tmr0if = 0; // apaga flag de entouro do TIMER0
//para uma nova contagem
}
} while(1); // loop
}

Calculo do tempo de estouro do timer0

tempo de estouro do TIMER0 = ciclo de máquina x prescaler x (modo 8/16 bits - valor de contagem do TIMER0)
tempo de estouro = 0,5 * 128 * (65536 - 49911)
Tempo de estouro = 1.000,000 us ou 1 segundo

Estudo detalhado do programa:

void main() { //função principal do programa


T0CON = 0b10000011; //configura timer modo 16 bits, com prescaler 128
TMR0L = 0XF7; //carrega valores de contagem
TMR0H = 0xC2; //carrega valores de contagem
intcon.tmr0if = 0; // apaga flag de entouro do TIMER0

Os comandos seguinte configuram o portb como saída.

TRISB = 0; // PORT B é configurado como saida


PORTB = 0xFF; // Initialize PORT B

Repare que nosso TIMER0 já foi configurado e iniciou a contagem dos pulsos de ciclo de máquina. Agora precisamos
monitorar o bit de estouro do TIMER0, pois não sabemos a que momento o estouro vai ocorrer, o que sabemos é que vai levar 1
segundos para ocorrer o estouro, ou seja, o bit TMR0IF vai para 1. Para isso, utilizaremos o comando de tomada de decisão IF,
que terá a seguinte função:

Se o bit de estouro intcon.tmr0if for igual a 1, representa que o TIMER0 estourou, e os comandos do bloco IF são
executados. Toda a vez que o TIMER0 estura, ou seja, o timer chega até 1 segundos, é invertido o estado do portb e reiniciado a
contagem do TIMER0.

if (intcon.tmr0if == 1) { //se o flag de estouro do TIMER0 for igual a 1, então


PORTB = ~PORTB; // inverte o estado do portb
TMR0L = 0XF7; //carrega valores de contagem
TMR0H = 0xC2; //carrega valores de contagem
intcon.tmr0if = 0; // apaga flag de entouro do TIMER0
}

Modo Contador do TIMER0:

Vamos fazer mais um exemplo de programa utilizando o TIMER0 do PIC:

Neste novo exemplo de programa, vamos programar o TIMER0 do PIC para trabalhar como contador de pulsos
externos. Neste modo, os pulsos externos, são aplicados no pino RA0/T0CK1 do PIC. Devemos contar apenas 10 pulsos
externos, que neste caso, configuraremos o TIMER0 do PIC no modo 8 bits com prescaler 1:1, e os pulsos serão lidos por borda
de descida. Ao completar a contagem de 10 pulsos, o led conectado ao pino RB7 do PIC deverá acender para sinalizar o fim da
contagem.

Esquema elétrico:
Para realizar o seguinte desafio é necessário configurar os seguintes registradores do TIMER0:

INTCON.TMR0IF (Bit de estouro do TIMER0)


TMR0L (registrador de contagem do TIMER0)
T0CON (registrador de configuração do modo de operação do TIMER0)

Programa:

/*********************************************************************
Centro de tecnologia Microgenios
Placa: Kit PICGenios
Programa: Pisca Pisca utilizando o contador T0CK1 do PIC
Objetivo: Este programa tem por objetivo piscar os leds do portb a cada
10 pulsos gerados no pino contador RA4/T0CK1 do PIC.
***********************************************************************/

void main() { //função principal do programa


T0CON = 0b11100000; //configura timer modo 8 bits, com prescaler 1:1
TRISB.f7 = 0; // PORT B é configurado como saida
PORTB.f7 = 0; // Initializa PORT B
TMR0L = 246; //carrega valores de contagem de pulsos
//como somente vamos contar 10 pulsos, temos: (255 - 10 = 245)
intcon.tmr0if = 0;
do {
if (intcon.tmr0if == 1) { //se o flag de estou for igual a 1, então
PORTB.f7 = ~PORTB.f7; // inverte o estado do pino RB0
TMR0L = 246; //recarrega valor de contagem de contagem
intcon.tmr0if = 0; // Seta T0IE, apaga flag de entouro do TIMER0
//para uma nova contagem
}
} while(1); // loop
}

Estudo detalhado do programa:

Repare que o nos comandos abaixo que o registrador T0CON foi configurado como contador de pulsos externos, modo
de operação do TIMER0 de 8 bits, com prescaler 1:1.
O registrador de contagem TMR0L é o registrador onde será armazenado os valores de contagem dos pulsos externos,
e neste caso ele foi pré configurado com o valor 246, devido a necessidade de contarmos apenas 10 pulsos. Devemos lembrar
que os registradores de contagem do TIMER0 são incrementados em 1 unidade e contam para cima, neste caso, como
desejamos programar o TIMER0 para que a cada 10 pulsos no pino RA0/T0CK1 seja gerado um estouro, devemos carregar em
TMR0L o valor da diferença entre o valor máximo de contagem para o modo 8 bits, que é 256, pelo valor da quantidade de
pulsos a ser contado, que neste cado é 10. Então teremos:

256 - 10 = 246
O TIMER1:

Realizamos nas unidades passadas um estudo não muito aprofundado sobre as funções e características dos TIMERS
do PIC. Agora chegou o momento de estudarmos mais profundamente os recursos e a programação dos registradores e modos
de trabalho desses temporizadores e contadores.
O TIMER1 pode operar como temporizador ou como contador de 16 bits, suas características são muito parecida com a
do TIMER0.
Dizemos que o timer está operando como temporizador quando a referência do clock de incremento da contagem é
realizada pelo clock interno do PIC, e dizemos que o timer esta operando como contador quando a referência do clock de
incremento da contagem é proveniente de um clock externo ao PIC. (pulso aplicado no pino RB6/T1OSO/T1CK1).

Registradores de configuração do TIMER1:

Figra 01 - Registrador relacionados com o TIMER0

P1R1: é um registrador onde é armazenado os bits de status das interrupções e estouro dos timers.
P1E1: é um registrador de 8 bits onde é habilitado as interrupções do PIC.
TMR1L é um registrador de contagem de 8 bits que possui a função de armazenar a parte baixa do valor de contagem
programada do TIMER0.
TMR1H é um registrador de contagem de 8 bits que possui a função de armazenar a parte alta do valor de contagem
programada do TIMER0.
Os registradores TMROL e TMR0H juntos formam um único registrador de 16 bits, que nos permite uma contagem
máxima de 0 a 65535.
T1CON é um registrador de 8 bits responsável pela configuração do modo de operação do TIMER1. Podemos definir
através desse registrador o valor do prescaler que desejamos acionar, o modo de operação de contagem de 8 ou 16 bits, seleção
da fonte de clock (interna ou externa) para o timer, seleção de disparo do timer através de borda de subida ou borda de descida
do clock externo no pino RB6/T10S0/T1CK1.

T1CON: (TIMER1 CONTROL REGISTER):

RD16 --- T1CKPS1 T1CKPS0 T10SCEN T1SYNC TMR1CS TMR1ON

Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0

RD16: bit de leitura e escrita em 16bits


0 – leitura e escrita em 16 bits habilitada
1 – leitura e escrita em 16 bits desabilitada
T1CKPS1; T1CKPS0: Bits de seleção de fonte de prescaler

T1CKPS1 T0CKPS0 Prescaler

1 1 1:8

1 0 1:4

0 1 1:2

0 0 1:1

T1OSCEN: Habilitação do oscilador externo de baixa frequeência nos pinos T1OSO e T1OSI
0 - Oscilador desligado
1 - Oscilador Ligado
T1SYNC: Controle do sincronismo interno. Caso TMR1CS = 0, esse bit é descartado
0 - Sincronismo ligado
1 - Sincronismo desligado
TMR1CS: Bit de seleção de clock;
0 - A base de clock para o TIMER1 é interna (Fosc/4);
1 - Clock externo no pino RC0/T1CK1;

TMR1ON: Habilita TIMER1;


0 - TIMER1 desligado
1 - TIMER1 ligado

Obs: Bit6 não implementado, lido como "0".


Faça download do diagrama esquemático do TIMER1 disponível na informações complementar dessa unidade.

A figura 01 apresenta o diagrama de funcionamento do TIMER1 operando com o oscilador de baixa frequência.

Figura 01

Abaixo segue um exemplo de ligação ( Revista Elektor / Ano 4 Nº 43)

Exemplo de programa:

/*********************************************************************
Centro de tecnologia Microgenios
Placa: Kit PICGenios
Programa: Pisca Pisca utilizando o TIMER1 do PIC
Objetivo: Este programa tem por objetivo piscar os leds do portb em
intervalo de 0,5 segundo (ligado e desligado) utilizando o TIMER1 do PIC.
Modo programado: 8 bits, prescaler 1:16
***********************************************************************/
void main() //função principal do programa
{
TRISB = 0; //define portb como saida
T1CON = 0B10110001; //liga TIMER1, prescaler 1:8, modo 16bits.
PIR1.TMR1IF = 0; //apaga flag de estouro do TIMER1

TMR1L = 0; //carrega valor de contagem baixa do TIMER1


TMR1H = 0; //carrega valor de contagem alta do TIMER1
PORTB = 0x00; //reseta portb

do {
if (PIR1.TMR1IF == 1) //se flag estiver em 1, representa que houve o estouro do timer1
{
PORTB = ~PORTB; //inverte o valor do portb
TMR1L = 0; //recarrega valor de contagem baixa do timer1
TMR1H = 0; //recarrega valor de contagem alta do timer1
PIR1.TMR1IF = 0; //resseta flag de estouro do timer1 para uma nova contagem.
}
} while (1);
}

O TIMER2 :

O TIMER2 é um timer de 8 bits com recarga automática. Esse TIMER tem um registrador de configuração, um de
contagem e outro de comparação. Ele possui um registrador de contagem de 8 bits ( 0 a 255) chamado TMR2.
Diferentemente dos outros timers, o TIMER2 possui um prescale e um postscaler. Os registradores especiais responsável pela
configuração do TIMER2 são:

T2CON (TIMER2 CONTROL REGISTER): Configura o setup do TIMER2;


TMR2: Registrador de contagem do TIMER2 (8 bits);
PR2: Registrador de comparação do TIMER2

Dizemos que o TIMER2 é um timer com recarga automática pois quando o valor carregado em PR2 é igual ao de
contagem TMR2, o valor de TMR2 é zerado e inicia-se uma nova contagem, ou melhor dizendo, temporização.

Os registradores relacionados com o TIMER2 são:

Através do registrador T2CON, podemos configurar o modo de operação do TIMER2, tais como: valor do prescale e
postcale e ligar ou desligar o timer2.

Faça download do diagrama esquemático do TIMER3 disponível na informações complementar dessa unidade.

Registradores de configuração do TIMER2:

Registradores de configuração do TIMER2:

T2CON: (TIMER1 CONTROL REGISTER):

--- TOUTPS3 TOUTPS2 TOUTPS1 TOUTPS0 TMR2ON T2CKPS1 T2CKPS0

Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0

TOUTPS3 TOUTPS2 TOUTPS1 TOUTPS0: Bits de ajuste do postcale:

TOUTPS3 TOUTPS2 TOUTPS1 TOUTPS0 Postscale


0 0 0 0 1:1
0 0 0 1 1:2
0 0 1 0 1:3
0 0 1 1 1:4
0 1 0 0 1:5
0 1 0 1 1:6
0 1 1 0 1:7
0 1 1 1 1:8
1 0 0 0 1:9
1 0 0 1 1:10
1 0 1 0 1:11
1 0 1 1 1:12
1 1 0 0 1:13
1 1 0 1 1:14
1 1 1 0 1:15
1 1 1 1 1:16

O funcionamento do postcale é muito semelhante ao prescaler, sua diferença básica está na contagem. Em vez
de contar pulsos de ciclos de máquina, o postcale conta n comparações do TIMER2 com PR2. Após n comparações, o flag
de estouro do TIMER2 é sinalizado com nível lógico 1.

TMR2ON: Habilitação do TIMER2;

0 - TIMER2 desligado
1 - TIMER 2 ligado

T2CKPS1 T2CKPS0: Bits responsáveis pelo ajuste de prescaler:

T2CKPS1 T2CKPS0 Prescaler

0 0 1:1

0 1 1:4

1 x 1:16
X pode ser 1 ou 0

Para exemplificar, vamos estudar um programa que tem como função piscar um led conectado ao pino RB0 do PIC em
intervalos de 1 segundo (ligado e desligado). Neste exemplo iremos utilizar o TIMER2 com ajuste de prescale e postcale para
entendermos o funcionamento.

Esquema elétrico:
Programa:

T2CON = 0b01111111; //programa TIMER2 com prescaler 1:16, postcale 1:16


TMR2 = 0; //inicia contagem a partir do 0
PR2 = 0; //inicia contagem a partir do 0

Descrição detalhada do programa:

Para calcularmos o tempo de estouro do TIMER2 utilizamos as seguintes contas (observe que estamos utilizando um cristal
externo de 4MHz):

Tempo de estouro do TIMER2 = valor do prescale x valor do postcale x PR2 x ( 1 /(Fosc/4) )

que nos resulta:

Tempo de estouro do TIMER2 = 16 x 16 x 255 x 1us = 65280us

Exemplo de Programa:

/***************************************************************************
Microgenios soluções eletrônica Ltda
Programa: Pisca Pisca utilizando o TIMER2
Compilador: MikroC
Objetivo: Este programa tem por objetivo piscar o led conectado ao pino RB0 do PIC em intervalos de 1
segundo (ligado / desligado) APROXIMADAMENTE.
***************************************************************************/

unsigned short i; //define variável i

//************************* subrotina de inversão do led *******************


void inverte_led() { //subrotina de inversão do estado do led
PORTB.f0 = ~PORTB.f0;
}

//************************ função principal do programa ******************


void main() {
i = 0;
TRISB = 0;
PORTB = 0;
T2CON = 0b01111111; //programa TIMER2 com prescaler 1:16, postcale 1:16
PR2 = 200; //inicia contagem a partir do 0

while (1)
{
if (pir1.tmr2if == 1) //se o TIMER2 estourar os comandos serão executados
{
i++; //foi criado a variável i para que somente depois de
//15 estouros do TIMER2, a subrotina de inversão do
//estado do led seja chamada.
//a variável i é incrementada em um unidade a cada
//estouro do TIMER2

if (i > 15) //caso i seja maior que 15, então chama subrotina de
{
inverte_led(); //inversão dos leds
i = 0; //zera novamente i para uma nova contagem
}
PIR1.TMR2IF = 0; //apaga o bit de sinalização de estouro do TIMER2
//para inciar uma nova contagem
}
}
}

O TIMER3 :

O TIMER3 é um temporizador e contador de 16 bits. Possui internamente dois registradores de 8 bits, TMR3L e
TMR3H, que juntos formam um registrador de 16 bits (contagem de 0 a 65535).
Os registradores relacionados com o TIMER3 são:
Através do registrador T3CON, podemos configurar o modo de operação do TIMER3, tais como: valor do prescale ,
ligar ou desligar o TIMER3, seleção de clock interno ou externo (contador) e fonte do timer para o módulo CCP do PIC.

Faça download do diagrama esquemático do TIMER3 disponível na informações complementar dessa unidade.

Registradores de configuração do TIMER3:

O registrador de controle do TIMER3;

T3CON: (TIMER3 CONTROL REGISTER);

RD16 T3CCP2 T3CKPS1 T3CKPS0 T3CCP1 T3SYNC TMR3CS TMR3ON

Bit7 Bit6 Bit5 Bit4 Bit3 Bit2 Bit1 Bit0

RD16: bit de leitura e escrita em 16bits


0 – leitura e escrita em 16 bits habilitada
1 – leitura e escrita em 16 bits desabilitada

T3CCP2 T3CCP1: Habilita a fonte de timer para o módulo CCP;

T3CCP2 T3CCP1 Prescaler


TIMER1 fonte de timer
0 0
para os módulos CCP
TIMER1 fonte de timer
0 1
para o módulo CCP1
TIMER3 fonte de timer
1 0
para o módulo CCP2
TIMER3 fonte de timer
1 1
para os módulos CCP

T3CKPS1 T3CKPS0: bits de ajuste do prescaler do TIMER3;

T3CKPS1 T3CKPS0 Prescaler


0 0 1:1
0 1 1:2
1 0 1:4
1 1 1:8

/T3SYNC: Controle de sincronismo interno. Caso TMR3CS = 0, este bit é descartado;


obs: este bit somente é utilizado para trabalhar com outro cristal ligado ao T1OSO e T1OSI.

0 - Sincronismo ligado
1 - Sincronismo desligado

TMR3CS: Seleção de Clock

0 - Clock Interno ( Fosc/ 4 )


1 - clock externo no pino T1CK1

TMR3ON: Habilitação do TIMER3;

0 - TIMER3 desligado
1 - TIMER3 ligado

Para exemplificar a utilização do TIMER3, vamos programar seus registradores:

Primeiramente devemos programar os TIMER3:

Para este exemplo, vamos programar o TIMER3 no modo 16 bits (contagem de 0 a 65535), prescaler de 1:8, clock
interno, cristal de 4Mhz e modo HSPLL desativado (multiplicador do oscilador), termos então:

Tempo de estouro do TIMER0 = valor de contagem do TIMER0 x PRESCALER X tempo de ciclo de máquina

65535 x 8 x 1us = 524280 us, ou 0.524 segundos.

Logo o tempo de estouro do TIMER3 será a cada 0.524 segundos.

T3CON = 0b10110001; //programa TIMER3 com prescaler 1:8, modo 16 bits, clock interno
TMR3l = 0; //inicia contagem a partir do 0
TMR3H = 0; //inicia contagem no modo 16 bits

Descrição detalhada do programa :

O programa abaixo tem por função configurar o modo de trabalho do TIMER3. Repare que o registrador T3CON foi
configurado da seguinte forma:

TIMER3 ligado. (TMR3ON = 1)


Clock interno (TMR3CS = 0)
Modo temporizador em 16 bits (RD16 = 1)
Prescaler 1:8 (T3CKPS1 = 0, T3CKPS0 = 1)

Os registradores de contagem dos pulsos dos clocks internos do PIC são inicializados a partir do zero.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Programando o Timer0

1.0 Estudo dos Timers/Counters do PIC

O TIMER0:

Realizamos nas unidades passada um estudo não muito aprofundado sobre as funções e caracteristicas dos TIMERS do PIC. Agora chegou o
momento de estudarmos mais profundamente os recursos e a programação dos registradores e modos de trabalho desses temporizadores e
contadores.

O TIMER0 pode operar como temporizador ou como contador. Esse periférico do PIC pode operar em dois modos:

Modo 8 bits, com contagem máxima de 0 a 255


Modo 16 bits, com contagem máxima de 0 a 65535

Dizemos que o timer esta operando como temporizador quando a referencia do clock de incremento da contagem é realizada pelo clock interno do
PIC, e dizemos que o timer esta operando como contador quando a referencia do clock de incrremento da contagem é proveniente de um clock
externo ao PIC. (pulso aplicado no pino T0CK1

Os Registradores relacionados ao TIMER0 são:

Figra 01 - Registrador relacionados com o TIMER0

TMR0L é um registrador de contagem de 8 bits que possui a função de armazenar a parte baixa do valor de contagem programada do TIMER0.
TMR0H é um registrador de contagem de 8 bits que possui a função de armazenar a parte alta do valor de contagem programada do TIMER0.
Os registradores TMROL e TMR0H juntos formam um único registrador de 16 bits, que nos permite uma contagem máxima de 0 a 65535.

INTCON é um registrador de 8 bits responsável pela configuração do modo de operação do TIMER0. Podemos definir através desse registrador o
valor do prescaler que desejamos acionar, o modo de operação de contagem de 8 ou 16 bits, seleção da fonte de clock (interna ou externa) para o
timer, seleção de disparo do timer através de borda de subida ou borda de descida do clock externo no pino RA4/T0CK1.

ESTUDE O MATERIAL DA SEGUNDA UNIDADE DO CURSO PARA OBTER MAIORES INFORMAÇÕES SOBRE OS MODOS DE
TRABALHO DOS TIMERS.

A final como realmente funciona um TIMER?

Primeiramente vamos estudar o TIMER0. Analise o diagrama do Timer0 seguinte:


Repare que os esquemas parecem com uma instalação elétrica, no qual vai habilitando ou desabilitando módulos, de acordo com o chaveamanto
definido pelo operador.

Agora vamos elaborar um desafio:

Exemplo 01:

Vamos programar o TIMER0 para funcionar como temporizador programado para "estourar" sua contagem em 3 segundos. A cada estouro do
TIMER0 será alterado os estados dos leds ligado ao PORTD, e uma nova contagem de 3 segundo é reiniciada.

Descrição do nosso hardware:

Configurações:

Cristal: 8 Mhz
Microcontrolador: PIC18F452
Vamos programar nosso TIMER0 passo a passo: Acompanhe a numeração da figura seguinte:

Numeração Função
1 Cristal externo
Chave seletora de função: define se o timer0 irá operar como
2
temporizador ou contador
PSA : Chave seletora que habilita ou desabilita a utilização do prescaler
3
(podemos considerar prescaler como um "redutor" de tempo)
4 Escala de ajuste do prescaler
Chave seletora do modo de contagem do TIMER0: formato 8 bits ou
5
formato 16 bits
Registradores de contagem TMR0L e TMR0H (dois registradores de 8
6
bits, formando 16 bits)
Bit de sinalização de estouro do TIMER0 (muita atenção no uso desse
7
bit)
8 Chave liga desliga do Timer0

Vamos inciar nosso programa

void main(){
//************ configuração do Leds ************************
trisd = 0; //define portd como saida
portd = 0; //estado incial dos leds apagado

//*********** configuração do Timer0 ************************


//vamos seguir a numeração
//cristal de 8Mhz, ciclo de máquina: 8MHz / 4 = 2Mhz --> 1/2Mhz = 0,5us
t0con.t0cs = 0; //numero2: timer0 operando como temporizador
t0con.psa = 0; //numero3: prescaler ativado
t0con.t0ps2 = 1; //numero4: define prescaler 1:128
t0con.t0ps1 = 1; //numero4: define prescaler 1:128
t0con.t0ps0 = 0; //numero4: define prescaler 1:128
t0con.t08bit = 0; //numero5: define contagem no modo 16 bits

tmr0h = ?? ;
tmr0l = ??;

intcon.tmr0if = 0; //numero7: apaga flag de estouro do timer0, pois é fundamental para a sinalização do estouro
t0con.tmr0on = 1; //numero8: liga timer0

while(1){

if (intcon.tmr0if == 1){
portd = ~portd; //inverte o estado do portd
tmr0l = ??;
tmr0h = ?? ;
intcon.tmr0if = 0; //apaga flag de estouro do timer0 para uma nova contagem de tempo
}
}
}

Calculo do tempo de estouro do timer0:

Sabemos que a formula para o calculo do estouro do Timer0 é:

tempo de estouro = ciclo de máquina * prescaler * (modo 8/16 bits - valor de contagem inicial em TMR0L e TMR0H)
logo teremos: (lembre-se que nosso objetivo é gerar um pisca pisca com tempo de 3 segundos, para isso converteremos 3 segundos para escala de
microsegundo: 3000000)

3.000.000us = 0,5us * 128 (65536 - valor de contagem)

Logo teremos:

Valor de contagem = 18661

O valor 18661 é justamente o valor que devemos carregar em TMR0L e TMR0H. Para realizarmos com facilidade esta operação matemática, basta
simplesmente converter este valor decimal para hexadecima:

18661 (decimal) = 48E5 , teremos então:

TMR0L = 0XE5;
TMR0H = 0X48;

Com certeza você deverá estar pensando da onde surgio o valor 1:128 do prescaler no exercício?? Na formula de temporização do Timer0, é mais
fácil encontrarmos o valor de carregagem nos registradores de contagem TMR0H e TMR0L do que o valor do prescaler, pois lembre-se que temos
algumas poucas opções de valores de prescaler, que já são pré-definidas no hardware do PIC. No exercício adotamos um valor alto do prescaler,
pois nossa temporização de 3 segundos é considerado um tempo alto para quem trabalha em escalas de us segundos.

pronto, nosso programa final esta pronto, basta compilarmos e visualizarmos seu funcionamento no kit de desenvolvimento.

Programa Final:
void main(){
//************ configuração do Leds ************************
trisd = 0; //define portd como saida
portd = 0; //estado incial dos leds apagado

//*********** configuração do Timer0 ************************


//vamos seguir a numeração
//numero1: cristal de 8Mhz, ciclo de máquina: 8MHz / 4 = 2Mhz --> 1/2Mhz = 0,5us
t0con.t0cs = 0; //numero2: timer0 operando como temporizador
t0con.psa = 0; //numero3: prescaler ativado
t0con.t0ps2 = 1; //numero4: define prescaler 1:128
t0con.t0ps1 = 1; //numero4: define prescaler 1:128
t0con.t0ps0 = 0; //numero4: define prescaler 1:128
t0con.t08bit = 0; //numero5: define contagem no modo 16 bits

tmr0l = 0xe5; //carrega o valor baixo do numero 18661


tmr0h = 0x48 ; //carrega o valor alto do número 18661

intcon.tmr0if = 0; //numero7: apaga flag de estouro do timer0, pois é fundamental para a sinalização do estouro
t0con.tmr0on = 1; //numero8: liga timer0

while(1){

if (intcon.tmr0if == 1){ //monitorando estado do flag de estouro do timer0


portd = ~portd; //inverte o estado do portd
tmr0l = 0xe5; //carrega o valor baixo do numero 18661
tmr0h = 0x48 ; //carrega o valor alto do número 18661
intcon.tmr0if = 0; //apaga flag de estouro do timer0 para uma nova contagem de tempo
}
}
}
Dica: Refaça o programa acima criando um pisca pisca com temporização de 5 segundos (ligado e desligado).

Exemplo 02:

Vamos elaborar um programa de varredura de teclado utilizando os recursos de temporização do Timer0.

Neste novo exemplo, iremos realizar um programa que irá ler os estados de 2 teclas ligada ao PIC a cada 20ms. Enquando nosso timer0 permanece
incrementando sua contagem, ficaremos alternando o estado de um led para simbolizar o funcionamento do programa.

Nosso hardware:
Configurações:

Cristal: 8 Mhz
Microcontrolador: PIC18F452

Esboço do programa:

Calculo do tempo de estouro TIMER0:


tempo de estouro = ciclo de máquina * prescaler * (modo 8/16 bits - valor de contagem inicial em TMR0L e TMR0H)

Logo: (lembre-se que nosso tempo desejado agora é de 20ms, ou seja, 20 000us

20.000us = 0,5us * 16 * (65536 - valor de contagem)

portanto:

valor de contagem = 63036

O valor 63036 é justamente o valor que devemos carregar em TMR0L e TMR0H. Para realizarmos com facilidade esta operação matemática, basta
simplesmente converter este valor decimal para hexadecima:

63036 (decimal) = F63C , teremos então:

TMR0L = 0X3C;
TMR0H = 0XF6;

Programa:

//********************** rotina de varredura do teclado **************************************

void varredura_teclas(){

if (portb.f0 == 0 ){ //verifica se tecla rb0 foi pressionada


portb.f7 = 1;
}

if (portb.f1 == 0 ){ //verifica se tecla rb1 foi pressionada


portb.f7 = 0;
}

portb.f6 = ~portb.f6; //fica constantemente alterando o estado do led rb6


//esta alteração ocorre a cada 20ms

tmr0l = 0x3c; //carrega o valor baixo do numero 18661


tmr0h = 0xf6; //carrega o valor alto do número 18661
intcon.tmr0if = 0; //apaga flag de estouro do timer0 para uma nova contagem de tempo
}

//********************** programa principal*****************************************************

void main(){
//************ configuração do Leds ************************
trisb.f0 = 1; //define portb.rb0 como entrada
trisb.f1 = 1; //define portb.rb1 como entrada
trisb.f6 = 0; //define portb.rb6 como saída
trisb.f7 = 0; //define portb.rb7 como saída
portb.f6 = 0; //apaga led rb6
portb.f7 = 0; //apaga led rb7

//*********** configuração do Timer0 ************************


//vamos seguir a numeração
//numero1: cristal de 8Mhz, ciclo de máquina: 8MHz / 4 = 2Mhz --> 1/2Mhz = 0,5us
t0con.t0cs = 0; //numero2: timer0 operando como temporizador
t0con.psa = 0; //numero3: prescaler ativado
t0con.t0ps2 = 0; //numero4: define prescaler 1:16
t0con.t0ps1 = 1; //numero4: define prescaler 1:16
t0con.t0ps0 = 1; //numero4: define prescaler 1:16
t0con.t08bit = 0; //numero5: define contagem no modo 16 bits

tmr0l = 0x3c; //carrega o valor baixo do numero 63036


tmr0h = 0xf6 ; //carrega o valor alto do número 63036

intcon.tmr0if = 0; //numero7: apaga flag de estouro do timer0, pois é fundamental para a sinalização do estouro
t0con.tmr0on = 1; //numero8: liga timer0

while(1){

if (intcon.tmr0if == 1) varredura_teclas();

}
}

Obs: Podemos substituir o seguinte trecho do nosso programa por uma simples linha de código:

t0con.t0cs = 0; //numero2: timer0 operando como temporizador


t0con.psa = 0; //numero3: prescaler ativado
t0con.t0ps2 = 0; //numero4: define prescaler 1:16
t0con.t0ps1 = 1; //numero4: define prescaler 1:16
t0con.t0ps0 = 1; //numero4: define prescaler 1:16
t0con.t08bit = 0; //numero5: define contagem no modo 16 bits
e
t0con.tmr0on = 1; //numero8: liga timer0

por:

t0con = 0b10000011;

Desafio: Faça você mesmo um programa de varredura de teclado operando o timer0 com tempo de varredura de 80ms.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Exercícios resolvidos
1.0 Exercício 01
1.1 Exercício 02
1.2 Exercício 03

Os exercícios abaixo são baseados no esquema elétrico seguinte:

Exercício 01

Programando o TIMER0 do PIC:

Vamos gerar um pisca pisca com intervalo de 1 segundo ligado e 1 segundo desligado:

Nosso hardware:
Cristal: 8 Mhz
Microcontrolador: PIC18F452

portanto:

valor de contagem = 49911

O valor 49911 é justamente o valor que devemos carregar em TMR0L e TMR0H. Para realizarmos com facilidade esta
operação matemática, basta simplesmente converter este valor decimal para hexadecima:

49911 (decimal) = C2F7 , teremos então:

TMR0L = 0XF7;
TMR0H = 0XC2;

Programa:

/*********************************************************************
Centro de tecnologia Microgenios
Placa: Kit PICGenios
Programa: Pisca Pisca utilizando o TIMER0 do PIC
Objetivo: Este programa tem por objetivo piscar os leds do portb em
intervalo de 1 segundo (ligado e desligado) utilizando o TIMER0 do PIC.
Modo programado: 16 bits, prescaler 1:128
cristal 8mhz
kitpicgenios pic18f
***********************************************************************/

void main() { //função principal do programa


T0CON = 0b10000110; //configura timer modo 16 bits, com prescaler 1:128
TMR0L = 0xF7; //carrega valores de contagem
TMR0H = 0xC2; //carrega valores de contagem
TRISD = 0; // PORTD como saida
PORTD = 0xFF; // Inicializa PORTD com 255
intcon.tmr0if = 0; // apaga flag de estouro do TIMER0

do {
if (intcon.tmr0if == 1) { //se o flag de estouro do TIMER0 for igual a 1, então
PORTD = ~PORTD; // inverte o estado do portD
TMR0L = 0xF7; //parte baixa do valor de contagem
TMR0H = 0xC2; //parte balta do valor de contagem
intcon.tmr0if = 0; // apaga flag de entouro do TIMER0
//para uma nova contagem
}
} while(1); // loop
}
Nosso hardware:
Cristal: 8 Mhz
Microcontrolador: PIC18F452

/*********************************************************************
Centro de tecnologia Microgenios
Placa: Kit PICGenios
Programa: Pisca Pisca utilizando o TIMER0 do PIC
Objetivo: Este programa tem por objetivo piscar os leds do portb em
intervalo de 1 minuto (ligado e desligado) utilizando o TIMER0 do PIC.
Modo programado: 16 bits, prescaler 1:128
cristal: 8mhz
kit picgenios pic18f
***********************************************************************/

void main() { //função principal do programa


unsigned char a = 0; //define variável "a" como byte sem sinalização
T0CON = 0b10000110; //configura timer modo 16 bits, com prescaler 1:128
TMR0L = 0xF7; //carrega valores de contagem
TMR0H = 0xC2; //carrega valores de contagem
TRISB = 0; // PORTB is output
PORTB = 0xFF; // Initialize PORTB

do {
if (intcon.tmr0if == 1) { //se o flag de estouro do TIMER0 for igual a 1, então
a++; //incrementa uma unidade em "a"

if (a == 30){ //somente quando a variável "a" for igual a 30 o portb será invertido
PORTB = ~PORTB; // inverte o estado do portb
a = 0;
} //zera a para uma nova temporização

TMR0L = 0xF7; //carrega valores de contagem


TMR0H = 0xC2; //carrega valores de contagem
intcon.tmr0if = 0; //apaga flag de estouro timer0
}
} while(1); // loop
}

portanto:

valor de contagem = 49911

O valor 49911 é justamente o valor que devemos carregar em TMR0L e TMR0H. Para realizarmos com facilidade esta
operação matemática, basta simplesmente converter este valor decimal para hexadecima:

49911 (decimal) = C2F7 , teremos então:

TMR0L = 0XF7;
TMR0H = 0XC2;
Agora basta repetir o ciclo por 60 vezes para que altere os estados dos leds:
if (intcon.tmr0if == 1) { //se o flag de estouro do TIMER0 for igual a 1, então
a++; //incrementa uma unidade em "a"

if (a == 60){ //somente quando a variável "a" for igual a 30 o portb será invertido
PORTB = ~PORTB; // inverte o estado do portb
a = 0;
} //zera a para uma nova temporização

TMR0L = 0xF7; //carrega valores de contagem


TMR0H = 0xC2; //carrega valores de contagem
intcon.tmr0if = 0; //apaga flag de estouro timer0
}
repare que criamos e incrementamos uma variável 'a' a cada estouro do TIMER0. Quando esta variável assumir o valor 60
representa que nosso ciclo de estouro chegou ao final (1 segundo) e os leds são invertidos seus estados.
Nosso hardware:
Cristal: 8 Mhz
Microcontrolador: PIC18F452

/*********************************************************************
Centro de tecnologia Microgenios
Placa: Kit PICGenios
Programa: Pisca Pisca utilizando o TIMER0 do PIC
Objetivo: Este programa tem por objetivo piscar os leds do portb em
intervalo de 1 HORA (ligado e desligado) utilizando o TIMER0 do PIC.
Modo programado: 16 bits, prescaler 1:256

cristal: 8mhz
***********************************************************************/

void main() { //função principal do programa


int a = 0;
T0CON = 0b10000111; //configura timer modo 16 bits, com prescaler 1:256
TMR0L = 0xdc; //carrega valores de contagem
TMR0H = 0x0B; //carrega valores de contagem
TRISB = 0; // PORTB is output
PORTB = 0xFF; // Initialize PORTB

do {
if (intcon.tmr0if == 1) { //se o flag de estouro do TIMER0 for igual a 1, então
a++; //incrementa uma unidade em "a"
intcon.tmr0if = 0; //apaga flag de estouro do timer0 para uma nova contagem
TMR0L = 0xdc; //carrega valor de contagem do timer0
TMR0H = 0x0B; //carrega valor de contagem do timer0
if (a == 450){ //somente quando a variável "a" for igual a 450 o portb será invertido
PORTB = ~PORTB; // inverte o estado do portb
a = 0;
} //zera a para uma nova temporização
}
} while(1); // loop
}
Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso.
Maiores informações: suporte@microgenios.com.br
Exercícios resolvidos
1.0 Exercício 01
1.1 Exercício 02
1.2 Exercício 03
1.3 Exercício 04
1.4 Exercício 05
1.5 Exercício 06
1.6 Exercício 07

Ma q u i n a L i g a d a

Esquema elétrico:

/****************************************************************************
Centro de Tecnologia Microgenios
Programa: acionamento LCD modo 8 bits
*******************************************************************************
*/

void main() {
Lcd8_Config(&PORTC,&PORTB,2,1,0,7,6,5,4,3,2,1,0); //inicializa lcd modo 8 bits
Lcd8_Cmd(Lcd_Clear); //apaga lcd
Lcd8_Cmd(LCD_CURSOR_OFF); //desliga cursor do lcd
Lcd8_Out(1, 1, "Maquina Ligada"); //escreve mansagem na linha 1, coluna 1 do lcd
while (1);
}

Ma q u i n a
L i g a d a
/****************************************************************************
Centro de Tecnologia Microgenios
Programa: acionamento LCD modo 8 bits
*******************************************************************************
*/

void main() {
Lcd8_Config(&PORTC,&PORTB,2,1,0,7,6,5,4,3,2,1,0); //inicializa lcd modo 8 bits
Lcd8_Cmd(Lcd_Clear); //apaga lcd
Lcd8_Cmd(LCD_CURSOR_OFF); //desliga cursor do lcd
Lcd8_Out(1, 1, "Maquina"); //escreve mansagem na linha 1, coluna 1 do lcd
Lcd8_Out(2, 9, "Ligada"); //escreve mansagem na linha 9, coluna 2 do lcd
while (1);
}
P R OG R AMA C A O EM C
PARA P I C 1 8 F
/**************************************************************************
Centro de tecnologia Microgenios
Programa exemplo: LCD_modo_4bits
Perceba que neste modo 4 bits, somente 1 port do PIC controla todo o diaplay LCD
**************************************************************************
*/
void main() {

Lcd_Config(&PORTB,3,1,2,7,6,5,4); //configura e inicializa LCD no modo 4 bits


Lcd_Cmd(Lcd_CLEAR); // apaga display
Lcd_Cmd(Lcd_BLINK_CURSOR_ON); // torna cursor piscante
Lcd_Out(1, 1, "PROGRAMACAO EM C"); // escreve na primeira linha, primeira coluna do LCD
Lcd_Out(2, 1, "PARA PIC18F"); // escreve na primeira linha, segunda coluna do LCD

while(1);
}

lcd_config(&port_controle/dados, RS, EN, RW, D7, D6, D5, D4 );

Simulação no PROTEUS:
# # # LCD 1 6 X 2 # # #
A L F A N UME R I C O
/**************************************************************************
Centro de tecnologia Microgenios
Programa exemplo: LCD_modo_4bits
Perceba que neste outro modo 4 bits, 2 ports do PIC participam para controle do LCD
**************************************************************************
*/
void main() {

ADCON1 = 6; //define pinos do porta e porte como I/O


TRISA = 0; //programa porta como saida

Lcd_custom_Config(&PORTB,7,6,5,4,&PORTA,2,1,0); //configura e inicializa LCD no modo 4 bits


Lcd_Custom_Cmd(Lcd_CLEAR); // apaga display
Lcd_Custom_Cmd(Lcd_CURSOR_OFF); //desliga cursor do lcd
Lcd_custom_Out(1, 1, "### LCD 16X2 ###"); // escreve na primeira linha, primeira coluna do LCD
Lcd_Custom_Out(2, 1, "ALFANUMERICO"); // escreve na primeira linha, segunda coluna do LCD

while(1);
}

lcd_config(&port_dados, D7, D6, D5, D4, &port_controle, RS, RW, EN );

Simulação no PROTEUS:
Exercício 05:

Apartir de agora iremos trabalhar com display LCD e teclado juntos.

O programa seguinte tem por objetivo incrementar uma variável chamada contador enquanto a tecla RB0 (S1) estiver pressionada e decrementar esta mesma variável
quando a tecla RB1 (S2) estiver pressionada. O valor deverá ser monitorado através do display LCD 16x2, programado no modo 8 bits.

Dados do projeto:

LCD modo 8 bits


Microcontrolador PIC18F452
Kit PICgenios PIC18F - Módulo Profissional
Cristal: 8MHz

Esquema elétrico:

Programa:

/**************************************************************************
Centro de tecnologia Microgenios
Programa exemplo: LCD_modo_8bits_01.c
Este programa tem por objetivo escrever no LCD o valor de incremento e decremento da variável contador.
Cristal: 8Mhz
Kit PICgenios PIC18F
Compilador MikroC
**************************************************************************
*/

void main() { //programa principal

char contador = 0; //define variável contador do tipo char


char texto[10]; //define vetor texto

ADCON1 = 6; // programa pinos de A/D como I/O de uso geral


TRISE = 0; // programa porte como saida
TRISD = 0; // programa portD como saida
trisb.f0 = 1; //configura tecla RB0 como entrada
trisb.f1 = 1; //configura tecla RB1 como entrada

Lcd8_Config(&porte, &portd,2,1,4,7,6,5,4,3,2,1,0); //configura e inicializa LCD no modo 8 bits


Lcd8_Cmd(Lcd_CLEAR); // apaga display
Lcd8_Cmd(Lcd_CURSOR_OFF); // desliga cursor
Lcd8_Out(1, 1, "Exemplo"); // escreve texto "Programa LCD" na primeira linha, quarta coluna do LCD
delay_ms(1000);
Lcd_custom_Out(2, 1, "Painel LCD"); // escreve texto "Programa LCD" na segunda linha, primeira coluna do LCD

while(1){

if (portb.f0 == 0){ //se a tecla RB0 for pressionada ...


contador++; //incrementa contador
wordToStr(contador, texto); //converte valor numérico de contador para string e salva o resultado em texto
Delay_ms(200);
Lcd8_Out(2, 12,texto); // escreve variável texto na 2º linha, 12º coluna do LCD
}
else
if (portb.f1 == 0 ) { //senão, se a tecla RB1 for pressionada
contador--; //decrementa contador
wordToStr(contador, texto); //converte valor numérico de contador para string e salva o resultado em texto
Delay_ms(200);
Lcd8_Out(2, 12,texto); // escreve variável texto na 2º linha, 12º coluna do LCD
}

}while(1);
}

Podemos utilzar as seguintes funções de conversão numérica <-> string no MikroC:

ByteToStr
ShortToStr
WordToStr
IntToStr
LongToStr
FloatToStr

Substitua os códigos acima por uma das funções acima e veja o resultado.

Exercício 06:

Nosso próximo programa exemplo, tem por objetivo modificar o programa anterior (Exercício 05) de forma que o incremento do teclado
somente ocorra quando o operador pressionar e soltar a tecla.
Repare no programa anterior que enquanto estivermos pressionando a tecla, a variável contador será incrementada ou decrementada
(dependendo da tecla pressionada).

Dados do projeto:

LCD modo 4 bits


Microcontrolador PIC18F452
Kit PICgenios PIC18F - Módulo Profissional
Cristal: 8MHz

Esquema elétrico:
Programa:
/**************************************************************************
Centro de tecnologia Microgenios
Programa exemplo: LCD_modo_4bits_01.c
Este programa tem por objetivo escrever o valor da variável contador no LCD
Tecla RB0 incrementará 1 unidade em contador.
Tecla RB1 decrementará 1 unidade em contador.
Cursor desligado
***************************************************************************/

//******** cabeçalho do programa ************************************


char estado_antigo, estado_antigo1;
char contador = 0;
char texto[10];

// ********************* Programa principal *******************************


void main() {

ADCON1 = 6; //programa pinos de A/D como I/O de uso geral


TRISE = 0; //programa porte como saida
TRISD = 0; //programa portD como saida
trisb.f0 = 1; //define pinos RB0 como entrada (tecla 0)
trisb.f1 = 1; //define pinos RB1 como entrada (tecla 1)

Lcd_custom_Config(&PORTD,7,6,5,4,&PORTE,2,4,1); //configura e inicializa LCD no modo 4 bits


Lcd_custom_Cmd(Lcd_CLEAR); // apaga display
Lcd_custom_Cmd(Lcd_CURSOR_OFF); // desliga cursor
Lcd_custom_Out(1, 1, "Exemplo"); // escreve texto "Programa LCD" na 1º linha, 1ºcoluna do LCD
delay_ms(1000);
Lcd_custom_Out(2, 1, "Painel LCD"); // escreve texto "Programa LCD" na 2º linha, 1º coluna do LCD

while(1){
// *************** bloco de programa para incremento **********************************
if (button(&portb, 0, 30, 0)) estado_antigo1 =1; // se tecla RB0 for pressionada altera o valor da variável
if (button(&portb, 0, 30, 1)&& estado_antigo1){ //se tecla RB0 estiver solta e estado_antigo == 1 então...
contador++; //incrementa contador
shortToStr(contador, texto); //converte valor da variável contador para string e salva em Texto
Delay_ms(200);
Lcd_custom_Out(2, 12,texto); // escreve variável texto na 2º linha, 12º coluna do LCD
Lcd_Custom_Chr_CP('%'); //escreve na possição do cursor o simbolo de % Porcentagem
estado_antigo1 = 0; //zera variável estado_antigo1
}
else
// *************** bloco de programa para decremento **********************************
if (button(&portb, 1, 30, 0)) estado_antigo =1;
if (button(&portb, 1, 30, 1)&& estado_antigo){
contador--;
shortToStr(contador, texto); //converte valor da conversão do ad0 para string
Delay_ms(200);
Lcd_custom_Out(2, 12,texto); // escreve variável texto na 2º linha, 10º coluna do LCD
Lcd_Custom_Chr_CP('%');
estado_antigo = 0;
}

}
while(1);
}

Exercício 07:

Vamos programar agora nosso display LCD sem auxilio de nenhuma função pronta do MikroC. Em alguns momentos, iremos nos deparar
com situaçoes em que nosso display LCD não estão conectados diretamente aos pinos físicos do LCD, para esses casos, temos que nos
recorrer a programação um pouco mais trabalhosa. Acompanhe:

Dados do projeto:
LCD modo 8 bits
Microcontrolador PIC18F452
Kit PICgenios PIC18F - Módulo Profissional
Cristal: 8MHz

O programa tem por objetivo escrever duas simples mensagem no display LCD no modo 8 bits, conforme esquema do exercício 05.

Nosso Programa:

#define CD porte.re2 //Ligado ao pino CD do display (seleção comando/dado)


#define CS porte.re1 //Ligado ao pino CS do display (habilitação)

//Rotina para aguardar tempo proporcional ao valor passado como argumento


void delay (unsigned char tempo)
{
for(;tempo!=0;tempo--);
}

//Rotina para enviar byte ao display


//Argumentos: d-> Byte a ser enviado
// s-> em 0 indica comando, e em 1 indica dado
void send(unsigned char d, char s)
{
//Coloca o byte na P1
Portd = d;

//Configura o pino CD do display indicando se o byte a ser enviado


//será comando(0) ou dado(1)
if (s==0) CD=0;
else CD=1;

//Borda de descida em CS, para forçar leitura do byte


CS=1;
CS=0;

//Aguarda algum tempo, esperando o display porcessar o byte recebido


delay(20);

//Caso o byte enviado seja um comando de "Limpar Display" ou "Home de cursor"


//aguarda mais tempo. Estes comandos são mais demorados...
if((s==0)&&(d<5)) delay(100);
}

//Rotina de inicialização do Display


void initlcd()
{
//Configura display em modo 8bits, 2 linhas e fonte 5x7 dots
send(0x39,0);

//Configura auto-incremento da posição do cursor para a direita


send(0x06,0);

//Liga display, e inibe exibição do cursor


send(0x0c,0);

//Limpa conteúdo da memória do display


send(0x01,0);
}

//Rotina para escrita de uma string no display


void write(char *c)
{
//Enquanto o caractere apontado por c não for nulo (final da string),
//Percorre a string enviando caracteres para o display
for(;*c!=0;c++) send(*c,1);
}

//Programa principal
void main ()
{
trisd = 0; //define portd como saida
trise = 0; //define porte como saída
adcon1 = 6;//configura todos os pinos que são A/D como I/O digital
trisb.f0 = 1; //configura pino RB0 como entrada de dados
trisb.f1 = 1; //configura pino RB1 como entrada de dados
//inicializa display
initlcd();

//loop inifinito, o uC executará este trecho do código indefinidamente


while(1){

//Posiciona cursor na primeira posição, da primeira linha


send(0x80,0);
//Escreve string
write("Programa LCD");
//Posiciona cursor na primeira posição, da segunda linha
send(0xc0,0);
//Escreve string
write("Sem a funcao LCD ");

//Agurada algum tempo


delay(100);
}
}

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Estudo dos Timers/Counters do PIC - Parte 2

1.0 O TIMER1

O TIMER1:

Realizamos nas unidades passadas um estudo não muito aprofundado sobre as funções e características dos TIMERS do PIC. Agora
chegou o momento de estudarmos mais profundamente os recursos e a programação dos registradores e modos de trabalho desses
temporizadores e contadores.

Diferente do TIMER0, o TIMER1 somente pode operar no formato 16bits ( contagem de 0 a 65535) e possui menor ranger em seu
prescaler. podendo operar como temporizador ou como contador de 16 bits.

Não se esqueça: Dizemos que o timer está operando como temporizador quando a referência do clock de incremento da contagem é
realizada pelo clock interno do PIC, e dizemos que o timer esta operando como contador quando a referência do clock de incremento
da contagem é proveniente de um clock externo ao PIC. (pulso aplicado no pino RB6/T1OSO/T1CK1 no caso de operarmos com
timer1).

Os Registradores relacionados ao TIMER1 são:

Figra 01 - Registrador relacionados com o TIMER0

P1R1: é um registrador onde é armazenado os bits de status das interrupções e estouro dos timers.
P1E1: é um registrador de 8 bits onde é habilitado as interrupções do PIC.
TMR1L é um registrador de contagem de 8 bits que possui a função de armazenar a parte baixa do valor de contagem programada do
TIMER0.
TMR1H é um registrador de contagem de 8 bits que possui a função de armazenar a parte alta do valor de contagem programada do
TIMER0. Os registradores TMROL e TMR0H juntos formam um único registrador de 16 bits, que nos permite uma contagem máxima de
0 a 65535.
T1CON é um registrador de 8 bits responsável pela configuração do modo de operação do TIMER1. Podemos definir através desse
registrador o valor do prescaler que desejamos acionar, o modo de operação de contagem de 8 ou 16 bits, seleção da fonte de clock
(interna ou externa) para o timer, seleção de disparo do timer através de borda de subida ou borda de descida do clock externo no pino
RB6/T10S0/T1CK1.

O registrador T1CON (TIMER1 Control) é o principal registrador de configuração do TIMER1.


Antes de iniciarmos nossos exemplos de programas, vamos analizar a estrutura de hardware do TIMER1, acompanhe:
repare que o prescaler máximo do TIMER1 é 1:8, somente podemos operar no formato 16 bits, e existe a possibilidade de trabalhamos
com contagem de pulsos externos através od pino RC0.

Vamos esturar agora alguns exemplos de programas:

Programando o TIMER1 do PIC

Exemplo 01:

Para exemplificar a utilização do TIMER1 do PIC vamos analizar um simples exemplo de programa cujo objetivo é piscar 8 leds
conectados ao PORTD do PIC utilizando o TIMER1 como temporizador:

Hardware:
Descrição:

Microcontrolador: PIC18F452
Cristal: 8 MHz
Compilador: MikroC ver8.0

Programa:

/*********************************************************************
Centro de tecnologia Microgenios
Placa: Kit PICGenios
Programa: Pisca Pisca utilizando o TIMER1 do PIC
Objetivo: Este programa tem por objetivo piscar os leds do PORTD em
intervalo de 0,5 segundo (ligado e desligado) utilizando o TIMER1 do PIC.
Modo programado: 8 bits, prescaler 1:16
***********************************************************************/

void main() //função principal do programa


{
TRISD = 0; //define portD como saida
PORTD = 0x00; //reseta portD
T1CON = 0B10110001; //liga TIMER1, prescaler 1:8, modo 16bits, modo temporizador
PIR1.TMR1IF = 0; //apaga flag de estouro do TIMER1

TMR1L = 0; //carrega valor de contagem baixa do TIMER1


TMR1H = 0; //carrega valor de contagem alta do TIMER1

do {
if (PIR1.TMR1IF == 1) //se flag estiver em 1, representa que houve o estouro do timer1
{
PORTD = ~PORTD; //inverte o valor do portD
TMR1L = 0; //recarrega valor de contagem baixa do timer1
TMR1H = 0; //recarrega valor de contagem alta do timer1
PIR1.TMR1IF = 0; //resseta flag de estouro do timer1 para uma nova contagem.
}
} while (1);
}

O programa acima configura o TIMER1 para operar em 16 bits com prescaler 1:8, clock interno. A cada estouro do TIMER1, o estado dos
pinos do PORTD são invertido.

Análise detalhado do programa:

Sabendo que nosso microcontrolador esta trabalhando com um cristal externo de 8MHz, vamos calcular o tempo de estouro do TIMER1:

Configuração do TIMER1:

Modo de Operação: 16 bits


Fonte de oscilação: Ciclo interno.
Prescaler: 1:8
Tempo de estouro:

Cálculo: ciclo de máquina * Prescaler * (65536 - valor do registrador de contagem) x


65536 x 0,5 x 8 = 262,144 ms

logo o tempo de estouro do TIMER1 do nosso exemplo de programa é de aproximadamente 262 milisegundos, o que nos
representa um pisca pisca em intervalos de 262 milisegundos, ligado/desligado.

Exemplo 02:

Agora que ja estamos entendendo o funcionamento dos TIMERS/COUNTERS, vamos elaborar um outro programa um pouco mais
complexo, acompanhe:

Objetivo: Quando pressionarmos a tecla ligada em RB0 (conforme hardware seguinte), o led ligado em RD0 deverá piscar 5 vezes a cada
150ms. Ao pressionarmos a tecla ligada em RB1, o led ligado em RD1 deverá piscar 4 vezes em intervalos também de 150 ms (ligado e
desligado). Para determinar a temporização deveremos utilizar o TIMER1 como temporizador.
Obs: A tecla deverá ser pressionada e solta para que o evento ocorra, ou seja, o evento deverá realmente ocorrer na passagem de nível 1
para 0.

Hardware:

Descrição:

Microcontrolador: PIC18F452
Cristal: 8 MHz
Compilador: MikroC ver8.0

Programa:

/**************************************************************************
centro de tecnologia microgenios
Kit PICgenios PIC18F
MicroICD zif
Compilador MikroC
Cristal 8MHZ
**************************************************************************
*/

void main() {

char estatus = 0b00000011; //declara variavel estatus


char i; //declara variável i
intcon.rbpu = 0; // habilita resistores de pull up interno no PIC
trisb.rb0 = 1; //define pino RB0 como saida, pois trata-se de uma tecla
trisb.rb1 = 1; //define pino RB1 como saida, pois trata-se de uma tecla

trisd.rb0 = 0; //define portd como saida


portd.rb0 = 0; //reseta portd

while(1){

if (portb.f0 == 0 && estatus.f0 == 1 ){ //verifica se tecla RB0 foi pressionada

for (i=0;i<8; i++) { //inicia laço de repetição for


portd.rd0 = ~portd.rd0;
t1con = 0b10110001; //liga timer1, prescaler 1:8, modo 16bits, modo temporizador
pir1.tmr1if = 0; //apaga flag de estouro do timer1
tmr1l = 0X84; //carrega valor de contagem baixa do timer1
tmr1h = 0X6D; //carrega valor de contagem alta do timer1
while(pir1.tmr1if == 0);
pir1.tmr1if = 0;
}
estatus.f0 = 0; //zera o flag de estatus
}

if (portb.f0 ==1 && estatus.f0 == 0) estatus.f0 = 1;

//****************** outra tecla ****************************

if (portb.f1 == 0 && estatus.f1 == 1 ){

for (i=0;i<8; i++) {


portd.rd1 = ~portd.rd1;
t1con = 0b10110001; //liga timer1, prescaler 1:8, modo 16bits, modo temporizador
pir1.tmr1if = 0; //apaga flag de estouro do timer1
tmr1l = 0X84; //carrega valor de contagem baixa do timer1
tmr1h = 0X6D; //carrega valor de contagem alta do timer1
while(pir1.tmr1if == 0);
pir1.tmr1if = 0;
}
estatus.f1 = 0;
}

if (portb.f1 ==1 && estatus.f1 == 0) estatus.f1 = 1;

}
while(1);
}
Clique aqui para baixar o programa acima:

Configuração do TIMER1:

Modo de Operação: 16 bits


Fonte de oscilação: Ciclo interno.
Prescaler: 1:8
Tempo desejado: 150ms

Tempo de estouro:

Cálculo: ciclo de máquina * Prescaler * (65536 - valor dos registradores de contagem) x

150 000 = 0,5 * 8 * (65536 - valor dos registradores de contagem)


37500 = 65536 - valor dos registradores de contagem
valor dos registradores de contagem = 28036

logo:

devemos converter o valor 28036 para hexadecimal para simplificarmos nosso entendimento na locação do valor em TMR1L e TMR1H.

28036 (decimal) ----> = 6D84 (hexadecimal)

então:
TMR1L = 0X84;
TMR1H = 0X6D;

Estudo do programa acima:

1º Neste pequeno trecho do programa, configuramos os pinos das teclas como entrada e os pinos dos leds como saida e declaramos as
variáveis utilizadas em nosso programa:

*******************************************************************
void main() {
char estatus = 0b00000011; //declara variavel estatus
char i; //declara variável i

trisb.rb0 = 1; //define pino RB0 como saida, pois trata-se de uma tecla
trisb.rb1 = 1; //define pino RB1 como saida, pois trata-se de uma tecla

trisd.rb0 = 0; //define portd como saida


portd.rb0 = 0; //reseta portd
****************************************************************************

2º O segundo bloco de programa, tem por objetivo varrer a tecla RB0. Caso essa tecla seja pressionada, iniciaremos um bloco de repetição
for e programaremos o TIMER1 para "estourar" a cada 262 ms.

***********************************************************************************
if (portb.f0 == 0 && estatus.f0 == 1 ){ //verifica se tecla RB0 foi pressionada

for (i=0;i<8; i++) { //inicia laço de repetição for


portd.rd0 = ~portd.rd0;//inverte o estado do led RD0
t1con = 0b10110001; //liga timer1, prescaler 1:8, modo 16bits, modo temporizador
pir1.tmr1if = 0; //apaga flag de estouro do timer1
tmr1l = 0X84; //carrega valor de contagem baixa do timer1
tmr1h = 0X6D; //carrega valor de contagem alta do timer1
while(pir1.tmr1if == 0); //aguarda até que flag do TIMER1 seja sinalizado em 1
pir1.tmr1if = 0;
}
estatus.f0 = 0; //zera o flag de estatus
}

if (portb.f0 ==1 && estatus.f0 == 0) estatus.f0 = 1;


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

3º O bloco de program seguinte possui a mesma funcionalidade que o trecho de programa anterior, a única diferença esta no fato de
trabalharmos com a tecla RB1.

****************************************************************************
if (portb.f1 == 0 && estatus.f1 == 1 ){

for (i=0;i<8; i++) {


portd.rd1 = ~portd.rd1; //inverte o estado do led RD1
t1con = 0b10110001; //liga timer1, prescaler 1:8, modo 16bits, modo temporizador
pir1.tmr1if = 0; //apaga flag de estouro do timer1
tmr1l = 0X84; //carrega valor de contagem baixa do timer1
tmr1h = 0X6D; //carrega valor de contagem alta do timer1
while(pir1.tmr1if == 0);
pir1.tmr1if = 0;
}
estatus.f1 = 0;
}

if (portb.f1 ==1 && estatus.f1 == 0) estatus.f1 = 1;

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

Exemplo 03:

Vamos imaginar agora uma situação real. Imagine que trabalhamos em uma empresa de embalagens, e em um determinado dia, seu
superior lhe fornece a seguinte missão:

Automatizar uma pequena esteira de produção, de forma que cada vez que uma embalagem passa por um sensor conectado a esteira de
produção é somado 1 unidade. Veja a animação seguinte:

Quando 30 caixas passarem por um sensor de presença (sensor infravermelho), a esteira de produção deverá ser desligada e uma
mensagem no display LCD deverá aparecer informando o operador para separar o lote de caixas, em seguinda, basta pressionar uma tecla
para que o processo continua do ponto onde parou.
Para realizar este projeto, devemos utilizar o modo contador de pulsos externos do TIMER1.

Hardware:

Descrição:

Microcontrolador: PIC18F452
Cristal: 8 MHz
Compilador: MikroC ver8.0

Programa:

/**************************************************************************
centro de tecnologia microgenios
programa exemplo: lcd_modo_4bits_01.c
este programa tem por objetivo mostrar no display lcd
cursor desligado
***************************************************************************/
#define esteira portb.f0
#define tecla_restart portb.f1

char texto[6];

void main() {

adcon1 = 6; // programa pinos de a/d como i/o de uso geral


trise = 0; // programa porte como saida
trisd = 0; // programa portd como saida
trisb.f0 = 0;
trisb.f1 = 1;
lcd_custom_config(&portd,7,6,5,4,&porte,2,0,1); //configura e inicializa lcd no modo 4 bits
lcd_custom_cmd(lcd_clear); // apaga display
lcd_custom_cmd(lcd_cursor_off); // desliga cursor
lcd_custom_out(1, 1, "producao"); // escreve texto "programa lcd" na primeira linha, quarta coluna do lcd
lcd_custom_out(2, 1, "numeros ="); // escreve texto "programa lcd" na primeira linha, quarta coluna do lcd
t1con = 0b00000011; //liga timer1, prescaler 1:8, modo 8bits, contador externo
tmr1l = 0; //carrega valor de contagem baixa do timer1
while(1){

bytetostr(tmr1l, texto); //converte valor da conversão do ad0 para string


delay_ms(200);
lcd_custom_out(2, 10,texto); // escreve variável texto na 2º linha, 10º coluna do lcd
lcd_custom_out_cp("cx"); //escreve a string "cx" na corrente posição do cursor

if (tmr1l < 10) esteira = 1; //se o valor do registrador de contagem do timer1 < 10
else //liga a esteira, caso contrário desliga a esteira
esteira = 0;

if (tecla_restart == 0 ) tmr1l = 0; //tecla de reinício de contagem

}
}
Clique aqui para baixar o programa acima:

Teste o programa no seu kit PICgenios e veja o resultado!


voltar para o topo da página

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
As Interrupções do PIC18Fxx2

1.0 Introdução

O que é Interrupção?

As interrupções são um evento externo ou interno que obriga o Microcontrolador a suspender suas atividades temporariamente,
para atender a esse evento que a interrompeu. Em resumo é uma ocorrência que faz o microprocessador parar sua rotina e desviar
para outro ponto do software, em que se localiza o serviço de interrupção que foi gerado pela ocorrência.
Para ligarmos as interrupções no microcontrolador, é necessário selecionar as interrupções desejadas no registrados SFR's
chamado IE (Interrupt Enable), bastando colocar o valor 1 para habilitar e 0 para desabilitar as interrupções que se deseja acionar.
Quando uma interrupção é acionada, o programa realiza uma instrução idêntica a CALL (chamada de sub-rotina), onde ocorre um
salto incondicional até a rotina da interrupção chamada. Nesse ponto do programa serão executados os comandos da interrupção
referente. Após o término do programa de interrupção o Mc volta a executar o programa principal do ponto seguinte em que parou
antes da chamada da interrupção.

No PIC18F452 existem diversos tipos de interrupções:

Interrupção do TIMER0
Interrupção do TIMER1
Interrupção do TIMER2
Interrupção do TIMER3
Interrupção por mudança de estado (pinos RB4, RB5, RB6, RB7)
Interrupção externa INT0
Interrupção externa INT1
Interrupção externa INT2
Interrupção na conversão AD
Interrupção na recepção serial
Interrupção na transmissão serial
Interrupção do módulo CCP
Interrupção de escrita na EEPROM/FLASH
Interrupção por queda de tensão.

Introdução a interrupção do PIC18F


Diferente da família PIC16, essa nova família PIC18F implementa prioridades nas interrupções.
Na memória FLASH do PIC18F452 possui dois endereços de interrupção: um chamado de vetor de alta prioridade, cujo endereço é
0008h, e outro chamado de vetor de baixa prioridade, cujo endereço é 0018h.

As prioridades de interrupção funcionam da seguinte maneira: Caso venhamos programar interrupções de alta e baixa
prioridade em nosso programa, e por um determinado momento seja solicitado ao mesmo tempo as duas interrupções,
a interrupção de alta prioridade será atendida primeiro. Ao término da execução da rotina de interrupção de alta
prioridade, automaticamente a rotina de baixa prioridade começa o seu tratamento. Quando uma rotina de baixa
prioridade estiver sendo executada e ocorrer uma interrupção de alta prioridade, a interrupção de alta prioridade será
atendida priomeiro, e ao seu término, a interrupção de baixa prioridade continuará a sua execução do ponto seguinte
onde parou.

Prioridades de Interrupção
Vamos supor que temos duas interrupções no Mc sendo solicitadas simultaneamente. Qual interrupção o Mc solicitará
primeiro? É por este motivo que devemos definir as prioridades das interrupções quando formos solicitá-las. No PIC18F
existe dois níveis de interrupção pré-estabelecida, alta prioridade e baixa prioridade:

Exemplo:

Caso o programador habilite no programa as interrupções externas Int1 e Serial, eu pergunto a você, qual dessas duas
interrupções será atendida primeiro, caso venham a serem solicitadas ao mesmo tempo?

Nos SFR's do PIC existe um registrador chamado IP (Interrupt Priority), cuja função é definir as interrupções que
vão ser atendidas primeiro, bastando colocar o valor 1 na interrupção desejada para maior prioridade e 0 para a menor
prioridade. Para interrupção com o mesmo nível de prioridade, o programador deverá informar por código qual a
interrupção que deverá ser atendida primeiro.

O registrador IE
As interrupções do nosso microcontrolador é parecido com a instalação elétrica de sua residência, acompanhe:

figura 01

Repare que para ascender a lampada de sua residência, é necessário que o interruptor esteja acionado (IE) e a chave
geral esteja acionada (GIEH).
Nos microcontroladores a interrupção funciona da mesma forma:

Existe um bit de habilitação ou desabilitação da interrupção do periférico chamado IE (interrupt enable) e um outro bit
de habilitação geral das interrupções chamado GIEH (interrupção geral)

Exemplo: habilitação de interrupção do TIMER0


INTCON2.TMR0IE = 1; //habilita interrupção do TIMER0

O registrador GIEH
Como podemos visualizar na figura 01 (figura anterior), o bit GIEH é responsável por habilita e desabilitar todas as
interrupções do nosso microcontrolador.

Exemplo: Habilitação da interrupção do TIMER0


INTCON.GIEH = 1; // liga chave geral das interrupções
INTCON.GIEH = 0; // desliga todas as interrupções do PIC

O registrador GIEL
O bit GIEL é responsável em habilitar/desabilitar as interrupções de baixa prioridade do PIC.

Exemplo: Habilitação da interrupção de baixa prioridade do TIMER0


INTCON.GIEL = 1; // Habilitação da interrupção de baixa prioridade do TIMER0
INTCON.GIEL = 0; // desabilitação da interrupção de baixa prioridade do TIMER0

O registrador IP
O bit intitulado "IP" é responsável por definir a prioridade das interrupções de cadas periférico, quando colocamos nível
lógico 1, habilitamos alta prioridade, caso seja 0, definimos como baixa prioridade.

Exemplo: Prioridade de Interrupção do TIMER0:

INTCON.TMR0IP = 1; // define TIMER0 em alta prioridade de interrupção


INTCON.TMR0IP = 0; // define TIMER0 em baixa prioridade de interrupção

Vamos estudar a princípio as interrupções do TIMER0 e TIMER1:

O registrador IF
Todos os periféricos do nosso microcontrolador possuem um bit de sinalização de estado, neste caso denominado IF. A
interrupção do periférico somente ocorre quando este bit é setado durante a execução do programa.

Exemplo: Flag de interrupção do TIMER0

INTCON.TMR0IF = 0; //apaga flag de estouro do TIMER0


INTCON.TMR0IF = 1; //seta o flag de estouro do TIMER0

O registrador RCON.IPEN
O registrador RCON.IPEN é responsável por definir os níveis de prioridade de interrupção do PIC. Nosso microcontrolador
da família PIC18 possuem recursos adcionais em comparação com os PIC16, quando configuramos este registrador
RCON.IPEN = 1; habilitamos o modo de alta e baixa prioridade de interrupção do PIC, presente na família PIC18F, e quando
definimos RCON.IPEN = 0;, desabilitamos este modo e herdamos as características do PIC16F com somente um nível de
prioridade de interrupção.

Exemplo:

RCON.IPEN = 0; // habilitamos o modo de alta e baixa prioridade de interrupção do PIC, presenta na família PIC18F
RCON.IPEN = 1; // desabilitamos o modo de alta e baixa prioridade de interrupção do PIC (herança da família PIC16F)

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

Vamos agora entender um pouco melhor sobre as interrupções e suas prioridades:

Veja a figura seguinte:

REGISTRADORES DE CONFIGURAÇÃO GERAL DAS INTERRUPÇÕES

1º repare que a interrupção geral INTCON.GIEH esta habilitada, a interrupção de baixa prioridade também esta habilitada, e
foi definido os dois níveis de prioridades (alta e baixa) atravé do registrador RCON.IPEN.

Agora vamos habilitar a interrupção do TIMER0, para melhor entendimento:


Agora estamos programando a interrupção do TIMER0, perceba que apagamos o flag de estouro do TIMER0
(INTCON.TMR0IF), habilitamos a interrupção (INTCON.TMR0IE) e definimos como alta prioridade de interrupção
TIMER0 através do registrador INTCON2.TMR0IP.

OBS: o registrador INTCON.GIEH possui mais de uma função, além de ser o responsável por habilitar/desabilitar todas
as interrupções (chave geral), é também responsável por habilitar as interrupções de alta prioridade no PIC. Você
encontrará no manual ou em exemplos de programas nomeclaturas a este registrador desta forma: INTCON.GIE. Na
verdade tanto faz escrever INTCON.GIEH ou INTCON.GIE que o compilador entenderá como sendo o mesmo bit. Isso é
válido também para o registrador INTCON.GIEL, que pode ser encontrado como INTCON.PEIE.

Função de Interrupção no compilador MikroC

Nosso compilador possui duas funções reservadas para tratamento das rotinas de interrupção de alta prioridade e de baixa
prioridade, que são:

void interrupt(){ //função de tratamento de interrupção de alta prioridade

}
e
void interrupt_low(){ //função de tratamento de interrupção de baixa prioridade

Para entendermos melhor o funcionamento das interrupções, vamos analizar e estudar com detalhes a interrupção do
TIMER0, acompanhe:

Interrupção do TIMER0:

Acompanhe o programa abaixo:

No programa abaixo estamos utilizando os recursos da interrupção do TIMER0 para inverter o estado lógico do PORTB. Cada interrupção
do TIMER0 ocorrerá a 3 segundos. Nosso objetivo neste programa é inverter o estado dos leds conectados ao PORTB a cada interrupção
do TIMER0, ou seja, a cada 3 segundos (ligado - desligado). O TIMER0 foi configurado no modo 16 bits, com prescaler 1:128 e o valor
contagem inicial do registrador de contagem é de 18661 (analise o cálculo do tempo de estouro do TIMER0 seguinte). Esta configuração
nos garante um delay de 3 segundos para que o PORTB seja invertido.

Hardware:
Características:
Cristal: 8Mhz
Microcontrolador: PIC18F452

Cálculo do tempo de estouro e interrupção do TIMER0 do programa abaixo:

Tempo de estouro do TIMER0 = ciclo de máquina * prescaler * (Modo 16 bits - valor do registrador TMR0L e TMR0H)
3.000.000 us = 0,5 * 128 * (65536 - x ) logo
x = 18661

convertendo o valor 18661 para hexadecimal teremos: 0x48E5

agora basta alocar nos registradores de contagem do TIMER0:

O Programa:

/**************************************************************************************************
centro de tecnologia microgenios
programa: inversão lógica do portb
objetivo: este programa tem por objetivo alterar o estado lógico do portb do pic em intervalos de
1 segundos. utilizaremos o timer0 para geração do delay, no modo 16 bits, com prescaler 1:2 com
base no cristal de 8mhz.
**************************************************************************************************/

//********************** rotina de interrupção **********************************************************


void interrupt() { //vetor de interrupção de alta prioridade padrão do mikroc
//todas as interrupção de alta prioridade serão vetoradas aqui.
portb = ~portb; //inverte o estado do portb
tmr0l = 0xe5; //valor inicial de contagem
tmr0h = 0x48; //valor inicial de contagem
intcon.tmr0if = 0; //apaga flag de estouro do timer0 para nova contagem
}

//********************* programa principal *********************************************************


void main() {

t0con = 0b10000110; // modo 16 bits, com prescaler 1:128, timer0 ligado e como temporizador
tmr0l = 0xe5; //valor inicial de contagem
tmr0h = 0x48; //valor inicial de contagem
intcon.tmr0if = 0; //apaga flag de estouro do timer0
intcon.gieh = 1; //habilita chave geral das interrupções
intcon.tmr0ie = 1; //habilita interrupção do timer0
rcon.ipen = 1; //define caracteristicas pi18f (2 níveis de prioridade de interrupção)
intcon2.tmr0ip = 1; //define interrupção do timer0 como alta prioridade

trisb = 0; //configura portb como saida


portb = 0xff; //seta todos os pinos do portb

while(1); //laço infinito (permanece na mesma linha para sempre )


}

faça download do programa acima clicando aqui:

Leia os comentários do programa acima para entender as funções de cada comando.

Diferente dos outros programas estudados até aqui, o programa acima utiliza o recurso das interrupções do microcontrolador PIC
utilizando o TIMER0. Repare que no programa acima, não precisamos ficar monitorar o bit sinalizador de estouro do TIMER0, pois,
quando este bit for setado, ou seja, quando o TIMER0 estourar, automaticamente o programa em execução é interrompido e desviado para
a rotina de interrupção. Após o término do tratamento das rotinas de interrupção, o programa retorna para o ponto seguinte de onde parou
anteriormente.

Estudo dos registradores relacionados a interrupção do PIC.

Existem dez registradores que são usados para controlar as interrupções;

RCON
INTCON
INTCON2
INTCON3
PIR1, PIR2
PIE1 , PIE2
IPR1, IPR2

A função do registrador RCON na interrrupção é definir se será utilizado as prioridades altas e baixas das interrupções (0x08 e
0x18) ou no modo normal, compativel com a familia PIC16 em que somente há um vetor de interrupção.
O bit do registrador RCON responsável pela seleção de habilitação do modo de interrupção chama-se: IPEN.

Os registradores INTCON, INTCON2 e INTCON3 definem a habilitação das interrupções de baixa e alta prioridade, além de
definir se algumas interrupções externas serão utilizadas.

Os registradores P1R1 e P1R2 são responsáveis em armazenar os estados das interrupções. Quando uma interrupção foi
programada, é através dos bits desse registrador que poderemos monitorar o estado da interrupção (acionada ou não acionada).

Através dos registradores IPR1 e IPR2 definimos as prioridades altas e baixas das interrupções, caso esse sistema seja utilizado.

Resumidamente, cada interrupção é constituido de três bits que determinam a sua forma de funcionamento:

1 - bit para habilitar a interrupção desejada (interrupção do TIMER0, interrupção Int0, serial, etc), cuja indicação é chamada "IE",
exemplo: intcon.tmr0ie = 1;
1 - bit para monitor o estado da interrupção, também chamado de Flag, cuja indicação é chamada: "IF", exemplo: intcon.tmr0if =
0;
1 - bit de definição de prioridade de interrupção (alta ou baixa prioridade), cuja indicação é chamada: "IP", exemplo:
intcon2.tmr0ip = 1;

Bit (flag) sinalizadores de interrupção:

PIR1 (sinaliza Interrupção dos Periféricos)


bit Evento
PIR1.PSPIF (BIT < 7 bit de status da Interrupção da porta Parallel port
>)=1 Slave
PIR1.ADIF (BIT < 6
bit de status da Interrupção do conversor A/D
>)=1
PIR1.RCIF (BIT < 5
bit de status da Dado recebido pela USART
>)=1
PIR1.TXIF ( BIT < 4
bit de status da Dado transmitido pela USART
>)=1
PIR1.SSPIF (BIT < 3
bit de status da interrupção do módulo SSP
>)=1
PIR1.CCP1IF (BIT <
bit de status da Interrupção do módulo CCP
2>)=1
bit de status do estouro do TIMER2
PIR1.TMR2IF (BIT <
1>)=1
PIR1.TMR1IF (BIT<
bit de status do estouro do TIMER1
0>)=1

PIR2 (sinaliza Interrupção dos Periféricos)


bit Evento
PIR2.EEIF (BIT < 4 bit de status da Interrupção de escrita na
>) = 1 EEPROM/Flash
PIR2.BCLIF (BIT <
bit de status da Interrupção por colisão de dados
3 >) = 1
PIR2.LCDIF (BIT < bit de status da Interrupção do módulo de baixa
2 >) = 1 voltagem LDV
PIR2.TMR3IF (BIT<
bit de status da Interrupção de estouro do TIMER3
1 >) = 1
PIR2.CCP2IF (BIT <
bit de status da Interrupção do módulo CCP2
0 >) = 1
INTCON (Interrupt Control)

bit Evento
INTCON.GIE/GIEH
Habilitação geral das interrupções
(BIT <7>) = 1
INTCON.PEIE/GIEL
Habilitação Interrupção dos periféricos
(BIT<6>) = 1
INTCON.TMR0IE (BIT
Habilitação Interrupção doTIMER0
<5>) = 1
INTCON.INT0IE (BIT
Habilitação Interrupção INT0 externa
<4>) = 1
INTCON.RBIE
Habilitação Interrupção por mudança de estado
(BIT<3>) = 1
INTCON.TMR0IF
Flag de estouro do TIMER0
(BIT<2>) = 1
INTCON.INT0IF (BIT
Flag de interrupção externa INT0
<1>) = 1
INTCON.RBIF
Flag de interrupção por mudança de estado
(BIT<0>) = 1
No registrador INTCON, os registradores marcados em amarelo na tabela acima são bits de sinalização de interrupção.

INTCON2 (Interrupt Control 2)

bit Evento
INTCON2.RBPU (BIT <7>)
Habilita resistores de pull-up do portb
=0
INTCON2.RBPU (BIT <7>)
desabilita resistores de pull-up do portb
=1

INTCON2.INTEDG0 (BIT Configura interrupção externa INT0 por


<6>) = 1 borda de Subida
INTCON2.INTEDG0 (BIT Configura interrupção externa INT0 por
<6>) = 0 borda de descida

INTCON2.INTEDG1 (BIT Configura interrupção externa INT1 por


<5>) = 1 borda de Subida
INTCON2.INTEDG1 (BIT Configura interrupção externa INT1 por
<5>) = 0 borda de descida

INTCON2.INTEDG2 (BIT Configura interrupção externa INT2 por


<4>) = 1 borda de Subida
INTCON2.INTEDG2 (BIT Configura interrupção externa INT2 por
<4>) = 0 borda de descida

INTCON2.TMR0IP (BIT
Alta prioridade da Interrupção do TIMER0
<2>) = 1
INTCON2.TMR0IP
Baixa prioridade da Interrupção do TIMER0
(BIT<2>) = 0

INTCON2.RBIP (BIT <0>) = Alta prioridade da Interrupção por mudança


1 de estado
INTCON2.RBIP (BIT <0>) = Baixa prioridade da Interrupção por mudança
0 de estado

INTCON3 (Interrupt Control 3)

bit Evento
INTCON3.INT2IP (INTCON3 <7>) = 1 Alta prioridade na Interrupção externa INT2
INTCON3.INT2IP (INTCON3 <7>) = 0 Baixa prioridade na Interrupção externa INT2
INTCON3.INT1IP (INTCON3 <6>) = 1 Alta prioridade na Interrupção externa INT1
INTCON3.INT1IP (INTCON3 <6>) = 0 Baixa prioridade na Interrupção externa INT1
INTCON3.INT2IE (INTCON3 <4>) = 1 Habilita interrupção externa INT2
INTCON3.INT2IE (INTCON3 <4>) = 0 desabilita interrupção externa INT2
INTCON3.INT1IE (INTCON3 <3>) = 1 Habilita interrupção externa INT1
INTCON3.INT1IE (INTCON3 <3>) = 0 desabilita interrupção externa INT1
INTCON3.INT2IF (INTCON3 <1>) = 1 Bit de sinalização de Interrupção INT2
INTCON3.INT2IF (INTCON3 <1>) = 0 Bit de sinalização de Interrupção INT2
INTCON3.INT1IF (INTCON3 <0>) = 1 Bit de sinalização de Interrupção INT1
INTCON3.INT1IE (INTCON3 <0>) = 0 Bit de sinalização de Interrupção INT1

Bit (flag) Habilitadores de interrupção:

PIE1 (Habilitação das interrupção dos periféricos )


bit Evento
habilita Interrupção da porta Parallel port
PIE1.PSPIE (BIT < 7 > ) = 1
Slave
PIE1.ADIE (BIT < 6 > ) = 1 habilita Interrupção do conversor A/D
PIE1.RCIE (BIT < 5 > ) = 1 habilita Interrupção da recepção serial
PIE1.TXIE (BIT< 4 > ) = 1 habilita Interrupção da transmissão serial
PIE1.SSPIE (BIT < 3 > ) = 1 habilita interrupção do módulo SSP
PIE1.CCP1IE (BIT < 2 > ) =
habilita Interrupção do módulo CCP
1
PIE1.TMR2IE (BIT < 1 > ) =
habilita interrupção do TIMER2
1
PIE1.TMR1IE (BIT < 0 > ) =
habilita interrupção do TIMER1
1
PIE2 (Habilitação das Interrupção dos Periféricos)
bit Evento
Habilita Interrupção de escrita na
PIE2.EEIF (BIT < 4 >) = 1
EEPROM/Flash
PIE2.BCLIF (BIT < 3 >) = 1 Habilita Interrupção por colisão de dados
Habilita Interrupção do módulo de baixa
PIE2.LCDIF (BIT < 2 >) = 1
voltagem LDV
PIE2.TMR3IF (BIT < 1 >) =
Habilita Interrupção do TIMER3
1
PIE2.CCP2IF (BIT < 0 >) = 1 Habilita Interrupção do módulo CCP2

IPR1 (registrador de prioridade de interrupções)

bit Evento
IPR1.PSPIP (BIT < 7 >) = 1 Alta prioridade na interrupção paralela
IPR1.PSPIP (BIT < 7 >) = 0 Baixa prioridade na interrupção paralela
IPR1.ADIP (BIT < 6 >) = 1 Alta prioridade na interrupção conversão AD
Baixa prioridade na interrupção conversão
IPR1.ADIP (BIT < 6 >) = 0
AD
Alta prioridade na interrupção de recepção
IPR1.RCIP (BIT < 5 >) = 1
serial
Baixa prioridade na interrupção de recepção
IPR1.RCIP (BIT < 5 >) = 0
serial
Alta prioridade na interrupção de
IPR1.TXIP (BIT < 4 >) = 1
transmissão serial
Baixa prioridade na interrupção de
IPR1.TXIP (BIT < 4 >) = 0
transmissão serial
IPR1.SSPIP (BIT < 3 >) = 1 Alta prioridade na interrupção MSSP
IPR1.SSPIP (BIT < 3 >) = 0 Baixa prioridade na interrupção MSSP
Alta prioridade na interrupção do módulo
IPR1.CCPIP (BIT < 2 >) = 1
CCP
Baixa prioridade na interrupção do módulo
IPR1.CCPIP (BIT < 2 >) = 0
CCP
IPR1.TMR2IP (BIT < 1 >) =
Alta prioridade na interrupção do TIMER2
1
IPR1.TMR2IP (BIT < 1 >) =
Baixa prioridade na interrupção do TIMER2
0
IPR1.TMR1IP (BIT < 0 >) =
Alta prioridade na interrupção do TIMER1
1
IPR1.TMR1IP (BIT < 0 >) =
Baixa prioridade na interrupção do TIMER1
0

IPR2 (registrador de prioridade de interrupções 2)

bit Evento
IPR2.EEIP (BIT < 4 >) = 1 Alta prioridade na interrupção EEPROM
IPR2.EEIP (BIT < 4 >) = 0 Baixa prioridade na interrupção EPROM
Alta prioridade na interrupção por colisão no
IPR2.BCLIP (BIT < 3 >) = 1
barramento
Baixa prioridade na interrupção por colisão
IPR2.BCLIP (BIT < 3 >) = 0
no barramento
Alta prioridade na interrupção por queda de
IPR2.LVDIP (BIT < 2 >) = 1
tensão
Baixa prioridade na interrupção por queda de
IPR2.LVDIP (BIT < 2 >) = 0
tensão
IPR2.TMR3IP (BIT < 1 >) =
Alta prioridade na interrupção do TIMER3
1
IPR1.TMR3IP (BIT < 1 >) =
Baixa prioridade na interrupção do TIMER3
0
IPR1.CCP2IP (BIT < 0 >) = 1 Alta prioridade na interrupção CCP2IP
IPR1.CCP2IP (BIT < 0 >) = 0 Baixa prioridade na interrupção CCP2IP

No mikroC o tratamento das interrupções de mesma prioridades devem ser obedecidas e tratadas por ordem de tratamento através de laços
de tomada de decisão, como o exemplo seguinte:

void interrupt() {
if (intcon.tmr0if==1) { //testa se a interrupção chamada é do timer0
portb = ~portb; //inverte o estado do portb
tmr0 = 96;
intcon.tmr0f = 0;
}
else if (intcon.rbif==1) //caso contrário, verifica se a interrupção é por mudança de estado
portd = ~portd; //inverte o estado do portd
intcon.rbif = 0;
}
}

A interrupção do TIMER0 e do módulo RB (interrupção por mudança de estado) foram habilitadas com a mesma prioridade. No código,
a interrupção do TIMER0 possui maior prioridade na execução da interrupção que a interrupção por mudança de estado, pois o análise do
flag de sinalização do TIMER0 é realizado primeiro, e somente se, a condição for falsa, é que será testada a interrupção RB.
Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
As Interrupções do PIC18Fxx2

1.0 Interrupção TIMER1

Interrupção TIMER1:

Os registradores de interrupção do TIMER1 são:

T1CON : registrador de configuração do TIMER1


INTCON.TMR1IF: bit sinalizador de estouro do TIMER1
INTCON.TMR1IE: bit que habilita ou desabilita a interrupção do TIMER1
INTCON3.INT1IP: bit de seleção de prioridade de interrupção do TIMER1
TMR1L: registrador de contagem do byte LSB do TIMER1
TMR1H: registrador de contagem do byte MSB do TIMER1

***************** registradores de configuração geral das interrupções (válido para todas as interrupções do PIC)
*************************

INTCON.GIEH: habilita a chave geral das interrupções e habilita as interrupções de alta prioridade
INTCON.GIEL: habilita as interrupções de baixa prioridade
RCON.IPEN: herança da família PIC18F ou PIC16F (dois níveis de prioridade de interrupção ou somente um nível)

Para aprendermos a programar a interrupção do TIMER1, vamos desenvolver passo a passo um programa:

Nosso programa terá a função de ler o canal analógico AN0 (trimpot R2 conectado ao pino RA0) e mostrar seu valor no LCD, e a cada 2
segundos o estado do relé Q2 deverá ser alterado (ligado e desligado). Para realizarmos a temporização de 2 segundos utilizaremos o TIMER1
e sua interrupção. A cada interrupção será incrementada uma variável, e seu valor poderá ser visualizado no LCD.

Esquema elétrico:

Hardware:
Cristal: 8Mhz
Microcontrolador: PIC18F452

Cálculo da temporização do TIMER1:

Cálculo do tempo de estouro e interrupção do TIMER0 do programa abaixo:

Tempo de estouro do TIMER0 = ciclo de máquina * prescaler * (Modo 16 bits - valor do registrador TMR0L e TMR0H)
200.000 us = 0,5 * 8 * (65536 - x ) logo
x = 15536

convertendo o valor 15536 para hexadecimal teremos: 0x3CB0

agora basta alocar nos registradores de contagem do TIMER0:


Programando a interrupção do TIMER1:

Primeiro devemos configurar o modo de operação do TIMER1. Para exemplificarmos, vamos configurar o TIMER1 em 16 bits com prescaler
1:8, clock interno e timer1 ligado.

T1CON = 0b10110001;

Segundo: devemos carregar os registradores de contagem do TIMER1: (vamos supor que desejamos carregar o valor 0 nesses registradores)

TMR1L = 0xB0;
TMR1H = 0x3C;

terceiro: devemos apagar o bit sinalizador de estouro do TIMER1

PIR1.TMR1IF = 0; //bit sinalizador de estouro do TIMER1

Quarta: devemos habilitar a chave geral das interrupções

INTCON.GIEH = 1;

Quinto: devemos habilitar a interrupção do TIMER1:

PIE1.TMR1IE = 1; //bit que habilita ou desabilita a interrupção do TIMER1

Sexto: devemos definir a prioridade de interrupção

IPR1.TMR1IP = 1; //define TIMER1 como alta prioridade de interrupção

sétimo: configurar o registrador RCON.IPEN

RCON.IPEN = 1; //define dois níveis de prioridade de interrupção presente na família PIC18F

Pronto agora é só criarmos nosso programa:

/**************************************************************************
centro de tecnologia microgenios
kit picgenios pic18f

***************************************************************************/

char contador = 0;

void interrupt(){ //rotina de interrupção de alta prioridade


contador++; //incrementa variável contador
if (contador > 10){
portb.f0 = ~portb.f0;
contador = 0;
}
tmr1l = 0xb0; //carrega valor de contagem baixa do timer1
tmr1h = 0x3c; //carrega valor de contagem alta do timer1
pir1.tmr1if = 0; //apaga flag de interrupção timer1
}

void main() {

char texto[10];
int leitura_ad0 = 0;

adcon1 = 0b00001110; // programa pinos de ra0 como a/d e os demais como i/o de uso geral
trise = 0; // programa porte como saida
trisd = 0; // programa portd como saida
trisb.f0 = 0; //define pino como saida
trisa.f0 = 1; //pino como entrada (ad)

lcd_custom_config(&portd,7,6,5,4,&porte,2,4,1); //configura e inicializa lcd no modo 4 bits


lcd_custom_cmd(lcd_clear); // apaga display
lcd_custom_cmd(lcd_cursor_off); // desliga cursor
lcd_custom_out(1, 1, "inter. timer1"); // escreve texto "programa lcd" na primeira linha, quarta coluna do lcd
lcd_custom_out(2, 1, "numeros ="); // escreve texto "programa lcd" na primeira linha, quarta coluna do lcd

t1con = 0b10110001; //liga timer1, prescaler 1:8, modo 8bits, temporizador


tmr1l = 0xb0; //carrega valor de contagem baixa do timer1
tmr1h = 0x3c; //carrega valor de contagem alta do timer1

intcon.gieh = 1; //habilita chave geral das interrupções


rcon.ipen = 1; //define niveis de prioridade de interrupção - pic18f
pie1.tmr1ie = 1; //habilita interrupção do timer1
pir1.tmr1if = 0; //apaga flag de interrupção timer1
ipr1.tmr1ip = 1; //define como alta prioridade timer1

while(1){

bytetostr(contador, texto); //converte valor da variável contador no lcd


lcd_custom_out(2, 10,texto); // escreve variável texto na 2º linha, 10º coluna do lcd
lcd_custom_out_cp("qt"); //escreve a string "qt" na corrente posição do cursor

leitura_ad0 = adc_read(0);
wordtostr(leitura_ad0, texto); //converte valor da variável contador no lcd
lcd_custom_out(1, 10,texto); // escreve variável texto na 2º linha, 10º coluna do lcd
lcd_custom_chr_cp('%'); //escreve a string "qt" na corrente posição do cursor

}
}

clique aqui para baixar o programa acima:


Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Exercícios resolvidos

1.0 Introdução

1.0 Exercício 01
1.1 Exercício 02
1.2 Exercício 03

Introdução:

Um dos grandes recursos dos temporizadores do PIC é a capacidade de gerar contagens de tempos precisos. Conforme estudamos nesta unidade,
descobrimos que é possível gerar interrupções com nossos temporizadores, e, mais legal ainda é que, essas interruções não afetam diretamente o
desenvolvimento do programa principal, ou seja, as interrupções dos temporizadores somente ocorrerão quando o tempo programado "estourar", durante este
intervalo de tempo podemos desenvolver diversas outras atividades com nosso microcontrolador.

A utilização das interrupções dos temporizadores é muito importante, pois através deles podemos desenvolver diversas atividades em paralelo com a
programação dos temporizadores:
Acompanhe um rascunho de um programa:

Repare na imagem acima que o programa de tratamento das interrupções dos timers ocorrem em paralelo com nosso programa principal. A principio, este
fato não causou grandes impacto para você acredito. Analise agora a proxima imagem:

Repare na figura acima que nosso programa agora assumiu várias funções. A rotina de interrupção do TIMER0 tem por objetivo lêr as teclas de um teclado,
acionar/desacionar leds e desligar relés, enquanto a rotina de interrupção do TIMER1 tem por objetivo lêr o canal A/D, escrever um dado na memória e
comparar valores no programa. Todas essas rotinas são realizadas no momento em que cada timer gera sua interrupção, ou seja, após o "estouro" da
contagem. Repare que nosso programa principal esta livre para executar suas rotinas.

Esta técnica de programação é muito utilizada pelos programadores, pois dessa forma criamos blocos de programas trabalhando em paralelo com o
programa principal.

Bom, agora que já aprendemos a importancia das interrupções, vamos desenvolver alguns exercícios sobre os temporizadores:
Programando as interrupções do TIMER0:

Exercício 01:

Vamos elabora um programa exemplo cuja finalidade será acionar e desacionar um led em intervalos de 0.5 segundos. Para realizar esta tarefa, vamos
utilizar o TIMER0 do PIC:

Acomanhe o esquema elétrico abaixo:

Repare que estamos utilizando cristal de 8 MHZ

Programa:

/****************************************************************************
centro de tecnologia microgenios
programa: pisca_pisca por interrupção do timer0
placa: kit picgenios
cristal: 8 mhz
objetivo: piscar os leds do portd em intervalos de 1 segundo utilizando as
interrupções do timer0 do pic
****************************************************************************/

//*********** rotina de interrupção ******************************************


void interrupt() { //rotina de interrupção
portd = ~portd; //inverte o estado do portd
//recarrega tempo de contagem do timer0
tmr0l = 0xee; //parte menos significativa do resultdado da conta (65535 - 34285)
tmr0h = 0x85; //parte mais significativa do resultado da conta (65535 - 34285)
intcon.tmr0if = 0; //apaga flag de estouro do timer0 para uma nova contagem
}

//***********************programa principal ************************************

void main() { //rotina principal do programa


trisd = 0; //configura portd como saida
portd = 0; //apaga todos os leds

t0con = 0b10000101; //configura o timer0 no modo 16 bits com prescaler 1:64, timer como temporizador
tmr0l = 0xee; //parte menos significativa do resultdado da conta (65536 - 34285)
tmr0h = 0x85; //parte mais significativa do resultado da conta (65536 - 34285)

intcon.tmr0if = 0; //apaga flag de estouro do timer0


intcon.tmr0ie = 1; //habilita interrupção do timer0
intcon2.tmr0ip = 1; //define interrupção timer0 como alta prioridade
intcon.gieh = 1; //habilita a interrupção geral do timer0
rcon.ipen = 1; //habilita niveis de prioridade de interrupção

while(1); //fica executando eternamente esta linha de programa


}

/*calculo matemático:

1º : 65536 - 31250 = 34286 => 85ee hexadecimal


2º : fosc / 4 = ciclo de máquina => 8mhz/ 4 = 0,5us
3º : prescaler programado para 1:64

tempo de estouro do timer0 será dado pela seguinte formula:

tempo de estouro = (65536 - valor de carregagem nos timers) x prescaler x ciclo de máquina

logo: 31250 x 64 x 0.5 = 1000000 us ou 1 segundo.

*/

Clique aqui para fazer download do programa

Observação: Neste exemplo de programa estamos utilizando cristal de 8 MHz, diferente dos outros programas que desenvolvemos até este ponto do nosso
curso.

Calculo do cilclo de máquina:

Ciclo de máquina = Fosc / 4 logo -> 8 Mhz / 4 = 2Mhz portanto -> 0.5 us.

Exercício 02:

Aproveitando os recursos do programa de interrupção do TIMER0 acima, vamos elaborar outro programa cuja função será incrementar uma variável do tipo
inteiro (int) a cada estouro do TIMER0 e enviar para o display lcd seu valor. Repare que agora os leds encontra-se no portB do PIC.

Acomanhe o esquema elétrico:

Esquema elétrico

Programa:

/****************************************************************************
centro de tecnologia microgenios
programa: pisca_pisca por interrupção do timer0
placa: kit picgenios
cristal: 8 mhz
objetivo: piscar os leds do portd em intervalos de 1 segundo utilizando as
interrupções do timer0 do pic. cada interrupção incrementará em 1 unidade a
variável contador e enviará o valor para o display lcd.

****************************************************************************/
char contador = 0; //define variável contador como inteiro
char texto[10]; //cria matris com 10 elementos do tipo char

//*********** rotina de interrupção ******************************************


void interrupt() { //rotina de interrupção
portb = ~portb; //inverte o estado do portd
contador++; //incrementa em 1 unidade a variável contador
//recarrega tempo de contagem do timer0
tmr0l = 0xee; //parte menos significativa do resultdado da conta (65535 - 34285)
tmr0h = 0x85; //parte mais significativa do resultado da conta (65535 - 34285)
intcon.tmr0if = 0; //apaga flag de estouro do timer0 para uma nova contagem
}

//***********************programa principal ************************************

void main() { //rotina principal do programa

trisb = 0; //configura todos os pinos do portd como saída


trise = 0; //configura todos os pinos do porte como saida
adcon1 = 0x06; //configura todos os pinos de a/d como i/o de uso geral

lcd_custom_config(&portd,7,6,5,4,&porte,2,0,1); //configura e inicializa lcd no modo 4 bits


lcd_custom_cmd(lcd_clear); // apaga display
lcd_custom_cmd(lcd_cursor_off); // desliga cursor

trisb = 0; //configura portd como saida


portb = 0; //apaga todos os leds

t0con = 0b10000101; //configura o timer0 no modo 16 bits com prescaler 1:64


tmr0l = 0xee; //parte menos significativa do resultdado da conta (65535 - 34285)
tmr0h = 0x85; //parte mais significativa do resultado da conta (65535 - 34285)

intcon.f2 = 0; //apaga flag de estouro do timer0


intcon.f5 = 1; //habilita interrupção do timer0
intcon.f7 = 1; //habilita a interrupção geral do timer0
intcon.f6 = 1; //habilita a interrupção geral de periféricos
// teriamos o mesmo resultado -> intcon = 0b11010000;
do {
wordtostr(contador,texto); //converte valor da variável contador em string
lcd_custom_out(1, 1, texto); //escreve variável texto no lcd

} while(1); //fica executando eternamente esta linha de programa


}

/*calculo matemático:

1º : 65536 - 31250 = 34286 => 85ee hexadecimal


2º : fosc / 4 = ciclo de máquina => 8mhz/ 4 = 0,5us
3º : prescaler programado para 1:64

tempo de estouro do timer0 será dado pela seguinte formula:

tempo de estouro = (65536 - valor de carregagem nos timers) x prescaler x ciclo de máquina

logo: 31250 x 64 x 0.5 = 1000000 us ou 1 segundo.

*/

Clique aqui para fazer download do programa

Exercício 03:

Vamos programar agora as interrupções do TIMER0 e TIMER1. A rotina de interrupção do TIMER1 terá por objetivo inverter os estados dos leds conectado
ao portb em intervalos de 1 segundos e a rotina de interrupção do TIMER1 terá objetivo inverter os estados dos leds conectados ao portd do PIC em
intervalos de 50 milisegundos.

Acompanhe o esquema elétrico abaixo:

Programa:
/****************************************************************************
centro de tecnologia microgenios
programa: pisca_pisca por interrupção do timer0 e timer1
placa: kit picgenios
cristal: 8 mhz
objetivo: piscar os leds do portb em intervalos de 1 segundo utilizando as
interrupções do timer0 do pic. a interrupção do timer1 terá como função
inverter os estado do portd em intervalos de 50 ms. timer0 terá maior prioridade
do que timer1.

****************************************************************************/

//*********** rotina de interrupção de alta prioridade *************************


void interrupt() { //rotina de interrupção

if (intcon.tmr0if == 1){ // verifica se ocorreu estouro do timer0


tmr0l = 0xee; //parte menos significativa do resultdado da conta (65535 - 34285)
tmr0h = 0x85; //parte mais significativa do resultado da conta (65535 - 34285)
portb = ~portb; //inverte o estado do portb
intcon.tmr0if = 0; //apaga flag de estouro do timer0 para uma nova contagem
}
}

// ************ interrupção de baixa prioridade ********************************


void interrupt_low(){
if (pir1.tmr1if == 1){ // verifica se ocorreu estouro do timer1
tmr1l = 0xb0; //carrega valor de contagem no timer1
tmr1h = 0x3c; //carrega valor de contagem no timer1
portd = ~portd; //inverte estado do portd
pir1.tmr1if = 0; // resseta bit sinalizador de estouro do
}
}

//***********************programa principal ************************************

void main() { //rotina principal do programa

char contador = 0; //define variável contador como inteiro


char texto[10]; //cria matris com 10 elementos do tipo char
char estado_antigo =1; //define uma variável de controle, cujo objetivo é armazenar um estado.

adcon1 = 6;
trisa.ra5 = 1;

trisc.f0 = 0;
portc.f0 = 0;

trisb = 0; //configura portb como saida


portb = 0; //apaga todos os leds do portb

trisd = 0; //configura portd como saida


portd = 0; //apaga todos os leds do portd

//configura timer0
t0con = 0b10000101; //configura o timer0 no modo 16 bits com prescaler 1:64, temporizador
tmr0l = 0xee; //parte menos significativa do resultdado da conta (65536 - 34285)
tmr0h = 0x85; //parte mais significativa do resultado da conta (65536 - 34285)

// habilita interrupção do timer0


intcon.tmr0ie = 1; //habilita interrupção do timer0
intcon.tmr0ip = 1; //define timer0 como alta prioridade
intcon.tmr0if = 0; //apaga flag de interrupção do timer0

//configura timer1
t1con = 0b10010001; //configura timer1 como 16 bits, prescaler 1:2, timer1 ligado
tmr1l = 0xb0; //inicial contagem timer1
tmr1h = 0x3c; //carrega contagem timer1

//habilita interrupção do timer1


pir1.tmr1if = 0; //resseta bit sinalizador de estouro do timer1
pie1.tmr1ie = 1; //bit que habilita a interrupção do timer1
ipr1.tmr1ip = 0; //define interrupção do timer1 como baixa prioridade

//configura interrupção geral dos periféricos


intcon.gieh = 1; //liga chave geral das interrupções
intcon.giel = 1; //liga interrupção de baixa prioridade
rcon.ipen = 1; //define dois níveis de prioridade de interrução presente pic18f

trisc.f0 = 0; //configura pino rc0 do pic como saida


trisa.f5 = 1; //configura pino ra5 como saida
porta.f5 = 1; //aciona rele

while(1);

/*************** calculos de temporização*************************************

calculo da temporização do timer0:

1º : 65536 - 31250 = 34285 => 85ee hexadecimal (valor de contagem)


2º : fosc / 4 = ciclo de máquina => 8mhz/ 4 = 0,5us
3º : prescaler programado para 1:64

tempo de estouro do timer0 será dado pela seguinte formula:

tempo de estouro = (65536 - valor de carregagem nos timers) x prescaler x (ciclo de máquina
logo: 31250 x 64 x 0.5 = 1000000 us ou 1 segundo.

######################################################################

calculo da temporização do timer0:

1º : 65536 - 15536 = 50000 => 3cb0 hexadecimal (valor de contagem)


2º : ciclo de máquina = fosc / 4 portanto 8mhz / 4 = 0.5 us.
3º : prescaler programado para 1:2

tempo de estouro = (65536 - valor de carregagem nos timers) x prescaler x ciclo de máquina

logo: 50000 x 2 x 0.5 = 50.000 us

*******************************************************************************/

Clique aqui para fazer download do programa

No programa anterior acionamos a interrupção do TIMER0 e a interrupção do TIMER1. Repare que as rotinas de tratamento das interrupções são
responsáveis pela inversão do PORTB e PORTD. O acionamento das interrupções trabalham em paralelo com nosso programa principal, que neste caso, sua
única função era executar o laço de repetição do - while ().

Veja na figura abaixo a divisão básica do programa:

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Estudo dos Timers/Counters do PIC - TIMER2

1.0 O TIMER2

O TIMER2 :

O TIMER2 somente pode operar como TEMPORIZADOR (contagem baseada no ciclo de máquina). Diferente de outros temporizadores,
TIMER2 possui postcaler, que ao invés de contar pulsos do ciclo que máquina, conta n comparações do TMR2 com PR2. Após n comparações,
o flag de estouro do TIMER2 é sinalizado com nível lógico 1.

Veja em seguida o diagrama do TIMER2:

Formula de estouro do TIMER2:

Tempo de estouro: prescaler * postscale * ciclo de máquina * (PR2 +1).

Perceba que na fórmula de tempo de estouro do TIMER2 o valor de PR2+1 é utilizado porque a contagem sempre começa do 0 (zero).

Exemplo: Calcule o tempo de estouro do TIMER2 sabendo que o valor do prescaler configurado é de 1:16, postscale:1:10, cristal utilizado
8Mhz, e PR2 = 200.:

Tempo de estouro = 10 * 16 * 0,5 * (200 + 1)


tempo de estouro = 16.080 us

logo teremos:

T2CON = 0B01001111;
PR2 = 200

Exemplo de programa:

Descrição de Hardware:

Microcontrolador: PIC18F452
Cristal: 8MHz

Esquema eletrônico:
Objetivo: Faça um programa para controle de uma forno elétrico. A cada 10 segundos o relé deverá pemanecer ligado e durante 20 segundos
deverá permanecer desligado. O display LCD 16x2 deverá apresentar em seu visor o valor de 0 a 100% lido de um sensor de temperatura,
conectado a entrada analógica AN0.

Obs: para simular o sensor de temperatura estamos utilizando um trimpot de 10K, conforme esquema eletrônico apresentado.

Desafio: A temporização deverá ser feita através do TIMER2 utilizando interrupção.

Programa:

/******************************************************************************
Centro de Tecnologia Microgenios
Proprama: Interrupção TIMER2
Objetivo: ligar relé a cada 10 segundos e desliga-lo a cada 5 segundos
Ler o sensor de temperatura em AN0 e mostrar valor no LCD
*******************************************************************************/

#define rele portc.f0

unsigned int contador = 0; //variável contado do programa


unsigned char estado = 0;

//************************** rotina de interrupção ***********************************

void interrupt() { //vetor de interrupção


contador++; //incrementa variável contador
if (contador == 500 && estado == 0 )
{
rele = ~rele; //inverte o estado do (relé)
contador = 0;
estado = 1;
}

if (contador == 250 && estado == 1)

{
rele = ~rele; //inverte o estado do relé
estado = 0;
contador = 0;
}

PIR1.TMR2IF = 0; // resseta bit sinalizador de estouro do TIMER2 para uma nova interrupção
}

//********************** programa principal *******************************************

void main() {

char contador = 0;
char texto[10];
int leitura_ad0;

//*********************** configura LCD ***************************************************


ADCON1 = 0b00001110; // programa pinos RA0 como AD
TRISA.RA0 = 1; //pino RA0 como entrada
TRISE = 0; // programa porte como saida
TRISD = 0; // programa portD como saida
Lcd_custom_Config(&PORTD,7,6,5,4,&PORTE,2,4,1); //configura e inicializa LCD no modo 4 bits
Lcd_custom_Cmd(Lcd_CLEAR); // apaga display
Lcd_custom_Cmd(Lcd_CURSOR_OFF); // desliga cursor
Lcd_custom_Out(1, 1, "Forno Eletrico"); // escreve texto "Programa LCD" na primeira linha, quarta coluna do LCD
Lcd_custom_Out(2, 1, "Temp: "); // escreve texto "Programa LCD" na primeira linha, quarta coluna do LCD

//*********************** configura saida relé************************************************

trisc.f0 = 0; //configura pino RC0 como saida


portc.f0 = 0; //desliga rele

//*********************** configura timer2 *************************************************


T2CON = 0b01001111; //configura timer2 , prescaler 1:16, timer2 ligado, postcale 1:10
pr2 = 249;

//*********************** configura interrupção geral ***************************************


intcon.gieh = 1;
intcon.giel = 1;
rcon.ipen = 1;

//*********************** configura interrupção TIMER2 ***************************************


PIR1.TMR2IF = 0; //resseta bit sinalizador de estouro do TIMER2
PIE1.TMR2IE = 1; //bit que habilita ou desabilita a interrupção do TIMER2
Ipr1.TMR2IP = 1; //define interrupção timer2 como alta prioridade

while(1)
{
leitura_ad0 = adc_read(0) * (100.0 /1023.0); //le canal AN0 e salva em leitura_ad0
wordToStr(leitura_ad0, texto); //converte o valor lido no AN0 em string
Lcd_custom_Out(2,7,texto); //envia para o lcd o valor string da conversão ad0
delay_us(10); //delay de 10us
}

Calculo de estouro do TIMER2:

Tempo de estouro: prescaler * postscale * ciclo de máquina * (PR2 +1).

Tempo de estouro: 16 * 10 * 0,5 * (249 + 1)

Tempo de estouro: 20.000us

relé ligado: 20.000 * 500 = 10.000.000us ou 10 segundos

relé desligado: 20.000 * 250 = 5.000.000 us ou 5 segundos

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2008. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
As Interrupções do PIC18F452

1.0 Interrupção externas


1.1 Programando a Interrupção externa INT0:
1.2 Programando a Interrupção externa INT1:
1.3 Programando a Interrupção externa INT2:

As Interrupção externa

O microcontrolador PIC18F452 possui 3 pinos de interrupção externa, podendo ser acionadas por mudança de nível lógico 1 para 0
(borda de descida), ou de 0 para 1 (borda de subida).

As interrupção externas são independentes entre si, e estão multiplexadas com os pinos RB0 (INT0), RB1 (INT1) e RB2 (INT2). Para
que as interrupções externas funcionem, são necessário configurar e habilitar sua função no PIC. vamos estudar um pouco sobre cada
interrupção externa:

Programando a Interrupção externa INT0:


A interrupção interna INT0 do PIC18F452 está multiplexada com o pino RB0. Podemos programar a interrupção externa por borda de
descida ou por borda de subida:

Registradores de configuração da interrupção externa INT0:

INTCON.INT0IE : bit de habilitação da interrupção externa INT0


INTCON.INT0IF : bit de sinalização da interrupção externa INT0
INTCON2.INTEDG0 : bit de configuração do modo de disparo da interrupção externa INT0
1 = Aciona interrupção externa por borda de subida
0 = aciona interrupção externa por borda de descida

Obs: a interrupção externa INT0, por hardware, é de alta prioridade, não sendo possível defini-lá como baixa prioridade.

Diagrama de hardware:
Programa exemplo:

Para exemplificar o funcionamento da interrpção externa INT0, vamos criar um programa cuja função é acionar a interrupção externa
INT0 a cada pulso proveniente de um sensor inflavermelho. A interrupção será disparada por borda de descida. A cada disparo da
interrupção INT0, iremos incrementar uma unidade um uma variável chamada "contador" e enviar o valor dessa variável no portd.

Esquema elétrico:

Programa:

/******************************************************************************
Centro de Tecnologia Microgenios
Proprama: Interrupção TIMER1
Objetivo: incrementar uma unidade no portb a cada interrupção externa INT0
*******************************************************************************/

//************************** rotina de interrupção ***********************************

void interrupt(){ //vetor de interrupção

contador++; //inverte o estado do pino RD0


portb = contador;
intcon.int0if = 0; //apaga flag sinalizador de interrupção externa INT0
//temos que apagar esse flag para que uma nova interrupção ocorra
}

//********************** programa principal *******************************************

void main() { //função principal do programa

intcon2.rbpu = 0; //programa resistore de pull us interno do pic


//caso não venhamos a colocar resistores de pull up externos
//temos que habilitar esse bit. (lembre-se que é habilitado em 0)

trisb.f0 = 1; //configura pino RB0 como entrada


trisd = 0; //configura portd como saida
portd = 0; //resseta todos os pinos do portd

intcon = 0b11010001; //habilita interrupção geral


//habilita interrupção de periféricos
//habilita interrupção externa INT0
//resseta bit sinalizador da interrupção EXTERNA INT0
intcon2.intedg0 = 0; //aciona interrupção por borda de descida

Rcon.ipen = 1; //define como dois niveis de prioridade de interrupção presente nos PIC18F

while(1); //fica em loop infinito aguardando o acionamento da interrupção por mudança de estado.

Programando a interrupção externa INT1:

O funcionamento da interrupção externa INT1 é igual a da interrupção INT0, sua única diferença esta no fato dos registradores de
configuração serem outros:

Registradores de configuração da interrupção externa INT1:

INTCON3.INT1IE : bit de habilitação da interrupção externa INT1


INTCON3.INT1IF : bit de sinalização da interrupção externa INT1
INTCON2.INTEDG1 : bit de configuração do modo de disparo da interrupção externa INT1
1 = Aciona interrupção externa por borda de subida
0 = aciona interrupção externa por borda de descida
INTCON3.INT1IP : bit de habilitação de alta ou baixa prioridade de interrupção externa INT1

Diagrama de hardware:

Exemplo de programa:
/******************************************************************************
centro de tecnologia microgenios
proprama: interrupção timer1
objetivo: incrementar uma unidade no portd a cada interrupção externa int1
*******************************************************************************/

//************************** rotina de interrupção ***********************************


char contador = 0;

void interrupt()//vetor de interrupção


{
contador++;
portd = contador;
intcon3.int1if = 0; //apaga flag sinalizador de interrupção externa int1
//temos que apagar esse flag para que uma nova interrupção ocorra
}

//********************** programa principal *******************************************

void main() //função principal do programa


{

intcon2.rbpu = 0; //programa resistore de pull us interno do pic


//caso não venhamos a colocar resistores de pull up externos
//temos que habilitar esse bit. (lembre-se que é habilitado em 0)

//*********************** configura interrupção geral ***************************************


intcon.gieh = 1; //liga chave geral das interrupções
intcon.giel = 1; //liga interrupções de baixa prioridade
rcon.ipen = 1; //defini dois vetores de interrupção presente nos pic18f

//*********************** configura interrupção int1 ***************************************

intcon3.int1ie = 1; // bit de habilitação da interrupção externa int1


intcon3.int1if = 0; // bit de sinalização da interrupção externa int1
intcon2.intedg1 = 0;// bit de configuração do modo de disparo da interrupção externa int1
intcon3.int1ip = 1; //defini alta prioridade na interrupção externa int1

trisb.f0 = 1; //configura pino rb0 como entrada


trisd = 0; //configura portd como saida
portd = 0; //resseta todos os pinos do portd

while(1); //fica em loop infinito aguardando o acionamento da interrupção por mudança de estado.

Programando a interrupção externa INT2:

O funcionamento da interrupção externa INT2 é igual a da interrupção INT1 e INT0:

Registradores de configuração da interrupção externa INT2:

INTCON3.INT2IE : bit de habilitação da interrupção externa INT2


INTCON3.INT2IF : bit de sinalização da interrupção externa INT2
INTCON2.INTEDG2 : bit de configuração do modo de disparo da interrupção externa INT2
1 = Aciona interrupção externa por borda de subida
0 = aciona interrupção externa por borda de descida
INTCON3.INT2IP: bit de habilitação de alta ou baixa prioridade de interrupção externa INT2
Exemplo de programa:

/******************************************************************************
centro de tecnologia microgenios
proprama: interrupção timer1
objetivo: incrementar uma unidade no portd a cada interrupção externa int2
*******************************************************************************/

//************************** rotina de interrupção ***********************************


char contador = 0;

void interrupt()//vetor de interrupção


{
contador++;
portd = contador;
intcon3.int2if = 0; //apaga flag sinalizador de interrupção externa int2
//temos que apagar esse flag para que uma nova interrupção ocorra
}

//********************** programa principal *******************************************

void main() //função principal do programa


{

intcon2.rbpu = 0; //programa resistore de pull us interno do pic


//caso não venhamos a colocar resistores de pull up externos
//temos que habilitar esse bit. (lembre-se que é habilitado em 0)

//*********************** configura interrupção geral ***************************************


intcon.gieh = 1; //liga chave geral das interrupções
intcon.giel = 1; //liga interrupções de baixa prioridade
rcon.ipen = 1; //defini dois vetores de interrupção presente nos pic18f

//*********************** configura interrupção int1 ***************************************


intcon3.int2ie = 1;// bit de habilitação da interrupção externa int2
intcon3.int2if = 0; // bit de sinalização da interrupção externa int2
intcon2.intedg2 = 0;// bit de configuração do modo de disparo da interrupção externa int2
intcon3.int2ip = 1;// bit de habilitação de alta ou baixa prioridade de interrupção externa int2

trisb.f0 = 1; //configura pino rb0 como entrada


trisd = 0; //configura portd como saida
portd = 0; //resseta todos os pinos do portd

while(1); //fica em loop infinito aguardando o acionamento da interrupção por mudança de estado.

}
Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores
informações: suporte@microgenios.com.br
Técnicas avançadas da linguagem C
1.0 Protótipos de funções
1.1 Ponteiros

Introdução
Agora que já estudamos muitos tópicos de grande importância em nosso curso, vamos explorar mais dois itens da linguagem C, tais como:

Protótipos de funções

Ponteiros

Protótipos de funções:
Para melhor entendermos o conceito de protótipos de funções da linguagem C, vamos estudar um simples exemplo de programa em C,
acompanhe:

/******************************************************************************
Centro de Tecnologia Microgenios
Proprama exemplo
Objetivo: apresentar o conceito: protótipo de função
*******************************************************************************/

//************************** subrotina aciona led ******************************


void aciona_led(){
portb = 1;
}

//************************** subrotina delay ***********************************


void gera_delay() {
delay_ms(1000);
}

//********************** função main() *****************************************


void main() {
trisb = 0;
aciona_led();
gera_delay();
while(1);
}

Repare que a função principal do programa, ou seja, a função main() encontra-se no final do nosso programa enquanto as demais funções
encontra-se no início. Com certeza você estará se perguntando: porque o programa é construido dessa maneira? porque não colocamos a função
main() no início do programa e as demais subrotinas (funções) no final do programa?.

Em um programa em C, não devemos chamar uma função antes do compilador saber que esta função existe. Por esse motivo, no momento da
compilação, faz necessário que as funções (subrotinas) do programa sejam colocadas no início do programa, para que o compilador a
reconheça.

Vejamos um erro classico em C:

O programa abaixo é o mesmo comentado anteriormente, sua única diferença esta no fato de invertermos a posição da
função main() para o início do programa.

/******************************************************************************
Centro de Tecnologia Microgenios
Proprama exemplo
Objetivo: apresentar o conceito: protótipo de função
*******************************************************************************/
//********************** função main() *****************************************
void main() {
trisb = 0;
aciona_led();
gera_delay();
while(1);
}
//************************** subrotina aciona led ******************************
void aciona_led(){
portb = 1;
}

//************************** subrotina delay ***********************************


void gera_delay() {
delay_ms(1000);
}

Ao tentarmos compilar o programa acima no mikroC, será apresentado o seguinte erro de compilação:

Este erro se deve ao fato de do pré-compilador do mikroC, no momento da compilação, encontrar o comando de chamada de função
aciona_led();
e gera_delay(); antes de encontar a subrotina void aciona_led()e void gera_delay().

Para corrigirmos este problema, podemos utilizar os protótipos de funções no topo do nosso programa, cujo objetivo é informar ao pré-
compilador do mikroC quais são as funções de subrotinas do nosso programa, dessa maneira podemos colocar essas funções antes ou depois da
função main(), ou então espalhada em módulos ou outros arquivos linkados com nosso programa.

Para exemplificarmos os protótipos de funções, vamos corrigir nosso programa utilizando esse recurso:

/******************************************************************************
Centro de Tecnologia Microgenios
Proprama exemplo
Objetivo: apresentar o conceito: protótipo de função
*******************************************************************************/

//************************ protótipos de funções *******************************


void aciona_led(); //protótipo de função
void gera_delay(); //protótipo de função

//********************** função main() *****************************************


void main() {
trisb = 0;
aciona_led();
gera_delay();
while(1);
}
//************************** subrotina aciona led ******************************
void aciona_led(){
portb = 1;
}

//************************** subrotina delay ***********************************


void gera_delay() {
delay_ms(1000);
}

Repare que o protótipo de função se constitui na declaração da própria função que vamos utilizar no programa, sem o bloco de comandos que
esta função irão executar.

Os protótipos de funções são de grande utilidade, uma vez que eles ajudam, evitando cometermos erros na codificação dos programas,
escrevendo códigos com parâmetros inadequados para uma função.

Ponteiro
Os ponteiros são tipos de dados muito utilizado na linguagem C, podemos utilizar seus recursos em aplicações complexas e de alta flexibilidade.
Resumidamente podemos dizer que um ponteiro nada mais é do que uma variável utilizada para guardar o endereço de outra variável, dizemos
que um ponteiro é um apontador de outra variável.
Para definirmos uma variável do tipo ponteiro, utilizamos o simbolo ' * ' (asterisco). Acompanhe o exemplo abaixo:

int *teste
char *soma

O primeiro declara uma variável teste do tipo int como ponteiro, e o segundo declara uma variável soma do tipo char
como ponteiro.
Em C os ponteiros também tem tipos, pois ao declararmos como ponteiros, devemos informar seu tipo de variável que
irá apontar.
Repare que os dois ponteiros criados acima não foram inicializados, isso significa que eles podem estar apontando para
qualquer endereço da memória. Para não cometermos nenhum erro na utilização de ponteiros, sempre é recomendado no
momento da criação de um ponteiro inicializa-los.

A linguagem C nos oferece o apontador (&), que é responsável por indicar a posição específica de uma variável na
memória.

variavel_soma = &teste;

No comando acima, o endereço da memória da variável teste é armazendado na variável variável_soma.


Para exemplificar a utilização de ponteiro, acampanhe o programa abaixo:

int *endereco_soma;
int contador;
contador = 120;
endereco_soma = &contador;
*endereco_soma = 50;

Definimos uma variável ponteiro do tipo int chamada endereço_soma e uma variável do tipo int chamada contador. Na
linha de programa seguinte, é atribuido a variável contador o valor 120. Em seguida na variável ponteiro endereco_soma é
atribuido o ENDEREÇO de memória da variável contador. Dizemos agora que a a variável endereco_soma aponta para
a variável contador. Em seguida, atribui-se o valor 50 ao endereço apontado pela variável endereco_soma. Como
resultado, a variável contador passará a armazenar o valor 50.

Exemplos de aplicação com ponteiros:

Acompanhe o exemplo:

int soma = 100;


int *apontador;
apontador = &soma;

No código acima declaramos uma variável do tipo int chamada soma e a inicializamos com o valor 100; declaramos
também um ponteiro do tipo int chamado apontador. A expressão, &soma, atribui ao ponteiro o endereço da variável
soma. O ponteiro apontador, passa a apontar para a variável soma. Apartir de agora, podemos alterar o valor da variável
soma através do ponteiro apontador. Quando atribuimos ao ponteiro apontador o endereço da variável soma, significa
dizer que a variável apontador é a própria variável soma. Podemos então alterar o valor da variável soma através do
ponteiro apontador. Para isso, fariamos da seguinte maneira:

*apontador = 20;

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso.
Maiores informações: suporte@microgenios.com.br
Comunicação Serial
1.0 Introdução
1.1 Modos de Comunicaçâo
1.2 Canais de Comunicaçâo
1.3 Padrão RS 232
1.4 MAX 232

Introdução

Como sabemos, nosso microcontrolador possui internamente um canal USART. Através desse canal serial
podemos nos comunicar serialmente com diversos dispositivos e equipamentos, entre eles o PC.
O compilador mikroC possui internamente funções que nos permite manipular com grande facilidade o canal
serial do PIC, vamos conhecer um pouco sobre o canal serial do PIC e seus registradores de configuração e seus
modos de trabalho.

Devemos nos lembrar que estamos estudando em particular o microcontrolador PIC18F442, mas as funções do
compilador mikroC não se restringe somente a este tipo de microcontrolador, mas praticamente todos os PICs que
possuem internamente um ou mais canais USART.

O canal USART do PIC18F442 é um canal de recepção e transmissão de dados (RX e TX) que pode ser
configurado como full duplex (pode receber e enviar dados ao mesmo tempo) para comunicação com PC´s por
exemplo, também conhecido como modo assincrono, ou half duplex, que pode ser utilizado como canal de
comunicação de dados entre A/D ou D/A, seriais EEPROM, etc, chamado de modo sincrono.

Os pinos multiplexados com o canal USART são RC6 (TX/CK) e RC7 (RX/DT)
Exemplo de aplicação: Modo full duplex - assincrono:
Modos de Comunicaçâo

Há duas maneiras para comunicação serial : modo síncrono e modo assíncrono. Modo
Síncrono — é necessário sincronismo entre os dois sistemas em comunicação. O
sincronismo é gerado por um grupo de bits, chamado de bits de sincronismo. Os bits são
recebidos pelo receptor que ajusta seu clock interno para receber o conjunto de bits
referentes aos dados. É possível receber qualquer quantidade de dados, de acordo com o
previsto no projeto. Após receber o último bit de dado, o transmissor envia um conjunto
de bits denominado bits de parada, que ao ser detectado pelo receptor informam-no que
acabaram os bits de dados.

Observe a ilustração na figura seguinte.


Modo Assíncrono — no modo assíncrono não há necessidade de gerar o sincronismo. Cada
um dos caractere é transmitido e para cada um há bits de inicio de transmissão (start bit)
e bits de fim de transmissão (stop bit).

Observe na figura que o stop bit é reconhecido pela transição de 1 para 0. Neste instante
o clock interno do sistema inicia uma varredura da linha de tempos em tempos para
detectar o nível da mesma. Ao receber o sétimo bit o sistema fica esperando o stop bit,
que é a transição de 0 para 1, ou a permanência em nível 1, se já estava em 1. Neste
ponto o sistema entra em repouso aguardando um novo start bit. Os sinais de
temporização e controles utilizados são gerados pelo hardware, especialmente
desenvolvido para este tipo de comunicação. Neste caso, deve-se garantir que os
sistemas transmissor e receptor operem com a mesma taxa de comunicação.

Canais de Comunicação

Existem basicamente, três formas de interligação dos sistemas digitais, considerando


apenas os sinais de dados. Canal simplex — corresponde a um sistema digitai que só
transmite e outro que só recebe; a comunicação se efetiva em apenas um sentido. Alguns
exemplos típicos deste tipo de canal são impressoras e terminais de bancos de dados.

Canal half-duplex —ou semi-duplex, é o que possui elementos que tanto recebem como
transmitem os dados. As duas operações, recebimento e transmissão, não podem ser
feitas ao mesmo tempo. Temos então, uma comunicação nos dois sentidos mas não
simultâneos. O walkie-talkie é um exemplo deste tipo de comunicação.

Canal FuII-duplex — ou simplesmente duplex, consiste num modo pelo qual os


sistemas podem transmitir e receber dados simultaneamente. Como exemplo temos os
computadores e o microcontrolador.

Padrão RS 232

A comunicação RS-232 nasceu da necessidade de se criar um padrão de comunicação


serial, por meio da definição de níveis de tensão e de impedância para transmissão e
dados, para que equipamentos incompatíveis entre si pudessem ser interligados. A figura
seguir mostra os níveis de tensão padrão RS-232 que vai de –15V a +15V, divididos da
seguinte forma: A região entre -5 e +5 volts não é reconhecida pelo sistema e é
conhecida como região de transição.

MAX 232

Pode-se implementar no sistema a comunicação serial através do padrão RS-232, em vez


de usar os níveis TTL presentes nos pinos do chip. Para isso pode-se usar um chip
bastante difundido, o MAX 232. O MAX232, da MAXIM, é um circuito integrado que
possibilita a conversão entre os níveis de tensões TTL e RS232. Há diversos C.l. deste
fabricante com a mesma função como o MAX225, MAX237, e outros que possuem
recursos extras, como controle de habilitação de entradas e saídas, modo de baixo
consumo, etc. Mas, o MAX232 é bastante prático e funcional, além de ser um padrão
industrial entre os fabricantes. Sua configuração é de 16 pinos e sua tensão de
alimentação é de +5v. A sua pinagem pode ser vista na figura a seguir. E desejar mais
informações acesse o seguinte endereço da internet:
http://www.maximic.com/reliability/MAX232xxE.pdf.
O MAX232 possui ao todo 4 linhas de transmissão e recepção, ou seja, 2 canais de
comunicação serial, com um par TX/RX cada um (TTL e RS232). Ele opera com 5
capacitores externos de l.F e pode transmitir dados á uma velocidade de até 116 Kbits/s.
Veja, a seguir, o diagrama em bloco e a configuração dos capacitores: Basicamente este
circuito é composto de dobrador de tensão, inversor de tensão, drivers de RS232 e
receptores RS232. O dobrador de tensão utiliza o capacitor C1 para dobrar a tensão de
+5V para +10V em C3 (na saída V+) e o inversor utiliza o pino 02 para inverter de +10V
para -10v em 04 (na saída V-). Os drivers de RS232 fazem com que a saída varie de +8 a
-8V (com uma carga de 5k e Vcc= +5V), ficando perfeitamente dentro das faixas de
tensão RS232 para níveis altos e baixos.

Este integrado é muito útil em aplicações de comunicação serial utilizando circuitos


integrados UART (Universal Asynchronous Receiver/Transmitter ou Transmissor/ Receptor
Universal Assíncrono) em computadores PC. O MAX232 possui dois canais de
comunicação. um pode ser utilizado para a sinalização e o outro para os dados. Na
utilização deste integrado, é importante não confundir as entradas e saídas dos canais 1 e
2, nos lados TTL e RS232. Por exemplo, se for utilizado o par T1in e R1out (canal 1, pinos
11 e 12 respectivamente) no lado TIL, então será utilizado o par Tlout e Rim (canal 1,
pinos 14 e 13) no lado RS232; sendo um conectado ao TXdo circuito externo e Rlout
conectado ao RX do circuito externo. Observe na figura, a seguir, o esquema sugerido
para as conexões entre o circuito externo e o PC, utilizando o MAX232. Os conectores
utilizados são do tipo DB9, podendo ser modificados para DB25, de acordo com a
pinagem .
Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse
material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Comunicação Serial
1.0 Introdução

Introdução

Como sabemos, nosso microcontrolador possui internamente um canal USART. Através desse canal serial podemos nos comunicar serialmente com
diversos dispositivos e equipamentos, entre eles o PC.

Pinos RX e TX do microcontrolador PIC

Programando o canal USART do PIC no mikroC:

O compilador mikroC possui funções de tratamento de comunicação serial, que vem a facilitar e acelerar o tempo no desenvolvimento de programas.
Basicamente possuimos 4 funções no mikroC para manipulação da USART do PIC, que são:

Usart_Init ; configura e inicializa os registradores do canal USART do PIC, assim como define o valor de
BAUDRATE (velocidade da comunicação) de trabalho do PIC.
Usart_Data_Ready ; esta função é utilizado para sabermos se algum byte chegou no buffer serial do PIC. Utilizamos esta
função antes de utilizarmos a função Usart_Read().
Usart_Read ; função que lê o valor armazenado no buffer serial do PIC. Toda vez que um dado chegar serialmente
no PIC, este byte será armazenado em um BUFFER interno no PIC, através dessa função do mikroC podemos lêr este dado
recebido.
Usart_Write ; esta função envia pela serial o valor do tipo BYTE programado pelo programador. É utilizado para
enviar um determinado byte pelo canal serial do PIC durante a execução do programa.

Vamos estudar um exemplo de programa:

O programa seguinte tem como função enviar o caracterer " A " pelo canal serial do PIC com a taxa de velocidade de 9600 bps (bits por segundo).
Acompanhe o esquema elétrico abaixo:
Comunicação serial

Programa exemplo:

/*********************************************************************
Centro de tecnologia Microgenios
Placa: Kit PICGenios
Objetivo: Este programa tem por objetivo enviar pela serial rs232 do kit o
caractere "A".
Baudrate 9600, N, 8, 1
*/

void main() {
USART_init(9600); //inicializa serial.
USART_Write('A'); //envia o caractere "A" pela serial do kit
while(1);
}

O programa acima tem unicamente a função de enviar por uma única vez o caractere 'A' pelo canal serial RS232 com velocidade de 9600 bps.

Programa detalhado:

O comando USART_init(9600)tem por função inicializar e determinar a velocidade de comunicação do canal serial do PIC, neste caso configuramos
para operar em 9600 bps, mas poderiamos, sem problema algum, configurar outras taxas de transferências, tais como:

110, 220, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 38400, 56000, 57600, etc.

O comando USART_Write('A')tem a função de enviar pelo canal serial do PIC o caractere 'A'. Para utilizar esta função temos algumas restrições:

A função USART_Write() envia somente 1 byte pela serial quando ela é executada.

Exemplo:

USART_Write('A'); //envia o caractere 'A' é do tipo byte.


USART_Write(0xFF); //envia o valor 0xFF é do tipo byte.
USART_Write(256); //envia o valor 0xFF é do tipo byte.

Vejamos agora algumas RESTRIÇÕES NA UTILIZAÇÃO DA FUNÇÃO USART_WRITE():

USART_Write("MICROGENIOS") ; //A função de escrita na serial NÃO suporta envio de String dessa maneira.
USART_Write("A"); //Repare que o caracterer é precedido com aspas duplas, na linguagem C este
caratere é tratado como string. Neste caso, o caratere A é composto por 'A' + \0 por ser tratar de uma
string. Para enviar um caracterer necessário utilizarmos aspas simples: Ex: 'A'.
Para operar com a função USART_Write(), o modo mais "elegante", ou talvez, o modo "mais usual", é a utilização de ponteiros. Aprendemos nas lições
anteriores como manipular uma variável ponteiro.

Os ponteiros resumidamente podemos dizer que nada mais é do que uma variável utilizada para guardar o endereço de outra variável, dizemos que um
ponteiro é um apontador de outra variável.

Para exemplificar um programa de comunicação serial com ponteiros, vamos desenvolver um simples programa, que possui como objetivo, enviar a string
"microgenios" através da serial do PIC.

Programa:

/*********************************************************************
Centro de tecnologia Microgenios
Placa: Kit PICGenios
Baudrate 2400, N, 8, 1
**********************************************************************/

unsigned char *i = "microgenios"; //define variável como i ponteiro

void main() {

USART_init(2400); //inicializa serial com baudrate 2400 bps

do {
USART_Write(*i); //envia o caractere pela serial do kit
*i++; //incrementa contador e endereço apontado pelo ponteiro
delay_ms(100); //aguarda 100ms
} while (*i != '\0'); //verifica se ponteiro é igual a NULL

Analizando o programa acima, percebemos que foi definida uma variável ponteiro do tipo char chamada i. Quando definimos uma variável como ponteiro,
na verdade estamos apontando para um determinado endereço de memória do PIC. No comando unsigned char *i = "microgenios" definimos
a variável i ponteiro e do tipo char não sinalizada e a inicilizamos com a cadeia de caractere "microgenios". Para entendermos melhor como será armazenado
nossa cadeia de caracteres, analize a tabela abaixo:

endereço na memória 00h 01h 02h 03h 04h 05h 06h 07h 08h 09h 10h 11h 12h 13h 14h 15h 16h
dado na memória (caractere) m i c r o g e n i o s \0

Repare que possuimos na primeira linha da tabelas os endereços da memória onde nossa variável estará armazenada, e na segunda linha de programa, temos
os dados de nossa string armazenado seguenciamente na memória.
Quando declaramos um parâmetro entre aspas (ex: "microgenios"), o compilador sabe que essa forma é para declaração de strings e automaticamente inclui
em seu final o terminador ('\0' - NULL).

Podemos incrementar ou decrementar um ponteiro. Quando efetuamos este tipo de operação temos que ter em mente que o ponteiro vai apontar para o
proximo valor correspondente ao seu tipo de dado definido. Se, por exemplo, definimos uma variável ponteiro do tipo char, a cada incremento, estaremos
apontando para o próximo endereço de memória de um byte, e se incrementarmos uma variável ponteiro declarada do tipo long, fará com que o ponteiro
aponte para o próximo long, que é um tipo de dados de quatro bytes.

Com base no que vimos até agora, podemos conluir que a função USART_Write(*i) no programa exemplo, tem como objetivo enviar pelo canal serial
do PIC o dado apontado pelo apontador i. Repare que a cada envio de um caractere apontado pelo apontador i, este mesmo apontado é incrementado em 1
unidade, como o ponteiro i foi definido como byte, a cada incremento, i aponta para a próxima posição de memória que armazena um outro caractere.

Leitura do canal serial


Através da função Usart_Read (), podemos ler o byte recebido que permanece no buffer serial do microcontrolador. Mas antes de
utilizarmos a função Usart_Read (), devemos utilizar outra função, chamada Usart_Data_Ready(), para verificar se realmente
algum byte chegou pela serial do PIC.

Sempre antes de lermos o buffer serial do PIC através da função Usart_Read (), precisamos saber se existe algum byte lá, e para
isso, realizamos uma condição de teste com a função Usart_Data_Ready() no comando de tomada de decisão IF.

Acompanhe um exemplo de programa:

char rtx;
...
if (Usart_Data_Ready() == 1 ) { //verifica se existe algum byte para ler no buffer serial do PIC
rtx = Usart_Read(); //le o byte que chegou pela serial e a salva na variável rtx.
}

Repare no programa que utilizamos o comando IF para testarmos o retorno da função Usart_Data_Ready(). A função
Usart_Data_Ready() tem como retorno zero (0) quando o buffer serial do PIC encontra-se vazio e tem retorno igual a 1 (um)
quando temos no buffer do PIC algum byte armazenado.

Para exemplificarmos, vamos elaborar um programa de comunicação serial, cuja finalidade é ascender ou apagar um led
conectado em RD0 de acordo com o byte recebido pela serial do PIC. Quando o byte recebido for o caractere " a " o led deverá
ascender ou quando o byte for recebido for " b " o led deverá apagar. De início, vamos considerar o led no estado apagado.

Programa:

/************************************************************************
Centro de Tecnologia Microgenios
Programação C para microcontroladores PIC
Autor: Fernando Simplicio
Objetivo: este programa tem por objetivo ascender o led quando o pic recebir o
caractere ' a ' pela serial, e apagar este mesmo led quando receber o byte ' b '
pela serial.
Velocidade: 9600, 8, 1, N
KitPICgenios
Cristal: 4MHz
**************************************************************************/

void main() {
unsigned short int dt;

TRISD.F0 = 0; //define pino RD0 como saida


PORTD.F0 = 0; //apaga led

Usart_Init(9600); //baudrate programado para 9600 bps

do {
if (Usart_Data_Ready()) { //verifica se algum byte chegou no buffer serial
dt = Usart_Read(); //lê o byte do buffer e salva na variável dt
Usart_Write(dt); //envia pela serial o valor do byte recebido
//Também conhecido como eco, normalmente
//reenviamos o byte recebido como confirmação do
//recebimento para o transmissor
}

switch(dt) {
case 'a': //caso o byte recebido seja o caracterer ' a ', então..
PORTD.F0 = 1; //acende o led
break; //finaliza a condição switch
case 'b': //caso o byte recebido seja o caracterer ' b ', então..
PORTD.F0 = 0; //apaga o led
break; //finaliza a condição switch
}
} while(1);
}

Clique aqui para fazer download do programa acima:

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores
informações: suporte@microgenios.com.br
Comunicação Serial Entre PC e microcontrolador PIC

1.0 Introdução

Vamos colocar em prática nossos estudos. Iremos elaborar um programa

Comunicação Serial Entre PC e microcontrolador PIC

Vamos apresentar um exemplo de programa, em que utilizaremos os recursos dos comandos de comunicação seriais do mikroC, para fazer a comunicação entre
o PIC e um PC (padrão IBM). Utilizaremos o HiperTerminal do Windows para transmitir e receber dados seriais entre ambos.

Primeiros Passos - Instalação do HiperTerminal

Primeiramente devemos nos certificar se o HiperTerminal do Windows está instalado no computador PC (IBM). O HiperTerminal é fabricado pela empresa
Hilgraeve e pode ser encontrado no site do fabricante (http://www.hilgraeve.com).

Para habilitar o HiperTerminal no Windows (98SE e XP), entre na pasta "arquivos de sistemas>instalar/remover programas> componentes/instalação do
windows>comunicação", e com o mouse habilite o ícone chamado HiperTerminal.

Configuração do HiperTerminal

1) Abra o HiperTerminal .

2) Nomeie a nova conexão de ProgSerial (adotaremos este nome somente para facilitar a
compreensão do exercício).

3) Clique no botão OK.

Figura 17.1
4) Nesta tela informamos a porta de comunicação serial que vamos utilizar para transmitir e Figura 17.3
receber dados.

5) Clique no botão OK.


6) Em seguida configure o HiperTerminal da seguinte maneira:

baud rate = 2400 bps

Bits de parada = 8

Paridade = nenhum

Bits de parada = 1

Controle de Fluxo = nenhum

Clique no botão OK.

Figura 17.4
O HiperTerminal está pronto e configurado para receber e enviar dados pela serial do PC. Não se esqueça de que devemos programar o Figura
microcontrolador com a mesma taxa de comunicação programada no HiperTerminal para não ocorrerem erros de comunicação. 17.5

Segundo Passo - O Hardware de Comunicação Serial

Monte os circuitos conforme a figura 17.6 em um proto board, ou utilize o KITPICGENIOS (kit de desenvolvimento) comentado no início do nosso curso
para realização deste exercício.

Hardware
Figura 17.6

Montagem do Cabo Serial

Precisamos do cabo serial para ligar o microcontrolador PIC (circuito da figura) no PC. A porta serial do PC é a antiga porta de conexão do
mouse/COM1/COM2.

Esquema elétrico do cabo serial para ser ligado na porta serial COM1 do PC.

Figura 17.7

Esquema elétrico do cabo serial para ser ligado na porta COM2 do PC.

Figura 17.8

Após a montagem dos circuitos eletrônicos, montagem e conexão do cabo serial no microcontrolador e no PC através da porta serial, vamos partir para o
terceiro e último passo do exercício, o programa de comunicação serial.

Terceiro Passo - Comunicação Serial Entre PC e PIC


O programa seguinte tem a função de manter comunicação entre o microcontrolador PIC e PC.

NOTA Para fazer o exemplo de programa seguinte, certifique-se que o HiperTerminal do windows esteja aberto e configurado, que os cabos de
comunicação serial RS232 estejam montados corretamente e conectado ao hardware conforme figura 17.6.

Neste painel devemos configurar a porta de comunicação do PC (COM1, COM2 ...), determinar o baudrate programado, o stop bit, paridades, e clicar no botão
Connect para iniciar a recepção ou transmissão de dados pela serial:
Para exemplificar a utilização do USART Terminal do mikroC, vamos elaborar um programa cuja finalidade é transmitir para o compilador mikroC uma
seguencia de três textos infinitamente. A velocidade da comunicação deverá ser a 2400 bps.

Programa:

/*********************************************************************
Centro de tecnologia Microgenios
Objetivo: este programa tem por objetivo enviar pela serial do PIC três textos.
Placa: Kit PICGenios
Baudrate 2400, N, 8, 1
Cristal = 4MHz
**********************************************************************/

unsigned char *i = "microgenios"; //define variável como i ponteiro


unsigned char *j = "kitpicgenios"; //define variável como j ponteiro
unsigned char *h = "PIC18F452 MICROCHIP"; //define variável como h ponteiro

void texto_i(){
do {
usart_write(*i); //envia pela serial o caracterer apostado pelo ponteiro
*i++; //incrementa contador e endereço apontado pelo ponteiro
} while (*i != '\0'); //verifica se ponteiro é igual a NULL

void texto_j(){
do {
usart_write(*j); //envia pela serial o caracterer apostado pelo ponteiro
*j++; //incrementa contador e endereço apontado pelo ponteiro
} while (*j != '\0'); //verifica se ponteiro é igual a NULL

void texto_h(){
do {
usart_write(*h); //envia pela serial o caracterer apostado pelo ponteiro
*h++; //incrementa contador e endereço apontado pelo ponteiro
} while (*h != '\0'); //verifica se ponteiro é igual a NULL

void main() {

USART_init(2400); //inicializa serial com baudrate 2400 bps


texto_i(); //chama subrotina de escrita do texto 'microgenios"
usart_Write('\n'); //pula de linha (consulte tabela ASCII)
texto_j(); //chama subrotina de escrita do texto 'kitpicgenios"
usart_Write('\n'); //pula de linha (consulte tabela ASCII)
texto_h(); //chama subrotina de escrita do texto "PIC18F452 MICROCHIP"
usart_Write('\n'); //pula de linha (consulte tabela ASCII)

while(1);

Clique aqui para fazer download do programa acima:


Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Comunicação I2C
1.0 Introdução

I2C
Esse novo barramento foi batizado pela empresa Holandesa (Philips) como
Inter IC ou I2C e possibilita a utilização de grande quantidade de
componentes padronizados, os quais podem realizar diversas funções,
além de possibilitar a troca eficaz de informações entre eles. Esse novo
barramento obteve uma grande aceitação do mercado, tornando-se um
dos mais utilizados e isso foi determinate para a crianção da versão 2.0,
lançada em 1998.

O conceito do barramento I2C é facilitar a integração de circuitos de caráter final de


aplicação como por exemplo sensores e conversores , com um sistema de controle, de
modo que eles possam trabalhar com seus sinais de maneira direta.

O barramento I2C

Uma característica bastante interessante deste barramento é a possibilidade de utilizar,


em um mesmo sistema, componentes de tecnologias construtivas diferentes sem que haja
incompatibilidade e nem conflitos na comunicação.

No I2C a transmissão da informação entre os dispositivos é feita através de 2 fios (Serial


Data DAS e Serial Clock SCL).

Os dispositivos ligados em Inter IC possuem um endereço fixo (cada componente recebe


um endereço específico), e podemos configurá-los para receber ou transmitir dados;
dessa maneira eles podem ser classificados de várias formas, como: mestres (MASTER),
escravos (SLAVE), entre outras.

O barramento I2C é do tipo multimestre, isso significa que mais de um dispositivo de


controle pode ser conectado a ele. No entanto, durante uma comunicação, somente um
dos mestres pode estar ativo, ou ocorrerá uma colisão de dados no barramento.
Por exemplo: enquanto um microcontrolador envia sinais a um conversor, um outro
microcontrolador troca informações com uma memória usando o mesmo barramento.
Tudo isso é possível, graças a uma "arbitragem" que determina qual dos sinais tem
prioridade no envio dos dados.

As linhas SDA como SCL são bidirecionais e devem ser ligadas ao positivo da alimentação
através de uma fonte de corrente ou de um resistor pull-up, para garantir que ambas as
linhas permaneçam em nível alto, quando o barramento está livre.

Uma das vantagens do padrão I2C é que ele não fixa a velocidade de transmissão
(freqüência), pois ela será determinada pelo circuito MASTER (transmissão do SCL).

Condições de Start e Stop

A comunicação entre os dispositivos mestres e os escravos, conectados ao barramento I2C, é


iniciada pela condição de start (início) e finalizada pela condição de stop (fim). Acompanhe a
condição de start e stop na figura seguinte:

Dentro da operação I2C, temos os procedimentos start e stop.

Uma transição de nível alto para baixo na linha SDA, enquanto a linha SCL está no nível
alto, é o indicativo da situação de START. Já uma transição do nível baixo para o nível alto
da linha SDA enquanto a linha SCL se mantém no nível alto, define uma condição STOP.
Sempre o mestre é o resposável pela geração dessas condições. Após uma condição de
START o barramento é considerado ocupado, e apenas volta a ficar livre algum tempo
depois da condição de STOP.

Se forem geradas condições START seguidamente, o barramento vai se manter ocupado,


porém com circuitos apropriados pode-se implementar a função de detectar as condições
START e STOP nos dispositivos que devem ser conectados ao barramento.

Formato dos Dados e Reconhecimento

As informações colocadas na linha SDA devem ter comprimento de 1 byte. O número de


bytes que pode ser transferido em cada operação de transferência é limitado, além disso
os bytes são transferidos enviando primeiramente o bit mais significativo MSB.

Caso o dispositivo que está recebendo o sinal, por algum motivo, não puder trabalhar os
dados recebidos, ele pode alterar a linha SCL, colocando-a no nível baixo, e assim forçar o
mestre a entrar num estado de espera. Dessa forma, fomente quando a linha estiver
novamente livre a transferência dos dados pode continuar.
Após o escrita/leitura de um byte no barramento, o dispositivo receptor gera um bit de
reconhecimento (acknowledge). O sinal de reconhecimento permite a fluência da
transferência dos dados. Assim, por exemplo, após a condição de Start e o
endereçamento estarem concluídos, o estágio (escravo) selecionado deve fornecer o sinal
de reconhecimento (ACK).

O reconhecimento dos dados transferidos é obrigatório. O pulso gerado para esta


finalidade é visualizado na figura seguinte:

A linha SDA do receptor é colocada em nível baixo durante o pulso de clock de modo que
ele permaneça estável no nível baixo durante o período alto do pulso de clock.

Endereçamento-padrão

O formato básico de um comando I2C é constituído por 7 bits de endereço, utilizando para
especificar o dispositivo escravo a ser acessado, seguido por um bit indicador de
leitura/escrita.

Normalmente o endereço básico de um comando I2C é composto por duas partes: a


primeira, de 4 bits, especifica o tipo de dispositivo escravo a ser acessado. A segunda, de
3 bits, especifica um entre até oito dispositivos daquele tipo, o qual será acessado.

O bit R/W indica se a operação é de leitura (nível 1) ou de escrita (nível 0).

Formato da Transmissão

Em um dos formatos de transmissão, talvez o mais utilizado, o transmissor mestre envia os


sinais para um receptor escravo, conforme a figura apresentada a seguir:

1) A transmissão é inicializada pelo mestre através de um bit de start (S).

2) Em seguida é enviado o endereço físico do escravo ligado ao barramento I2C. O bit LSB
desse endereço (R/W) informa se queremos ler ou escrever na memória.

3) Após a recepção do endereço, o escravo envia um bit de reconhecimento (ACK) na linha


SDA, informando para o master que está pronto para receber dados.

4) Master envia os dados para o escravo. A confirmação do recebimento de cada byte é


fornecida pelo bit ACK enviado pelo escravo.

5) Ao término da comunicação, o master envia a condição de stop.


Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse
material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Comunicação I2C
1.0 memória 24Cxx
1.1 Pinagem:
1.2 Endereçamento
1.3 Gravação
1.4 Leitura
1.5 Programa de Gravação I2C.
1.6 Programa de Leitura I2C.

Memória 24Cxx

A série 24Cxx EEPROM de baixo custo, baixo consumo e tamanho reduzido (oito
pinos) é excelente para aplicação em desenvolvimentos compactos. Utiliza
tecnologia I2C (permite leitura/escrita com duas vias SDA e SCL).

A figura mostra a pinagem da memória 24CXX .

Tipos de memória serial EEPROM: 24C01, 24C02, 24C04, 24C08, 24C16, 24C32,
24C64, 24C128, 24C256, 24C512 fabricado por: ATMEL, STM, etc.

Modelo Pinagem Capacidade


24C01 8 pinos 1Kbits
24C02 8 pinos 2Kbits
24C04 8 pinos 4Kbits
24C08 8 pinos 8Kbits
24C16 8 pinos 16Kbits
24C64 8 pinos 64Kbits

Pinagem:

Endereçamento

Os pinos A0, A1 e A2 definem o endereço físico da 24CXX no barramento I2C, que permite ligar até oito
memórias dessas no barramento.
Para podermos ler ou escrever dados nessas memórias, é necessário sabermos seu endereço no
barramento I2C. O endereço é definido da seguinte maneira:
O endereço é formado por 1 byte subdividido em três partes:

1010 - esse endereço é fixo para esse tipo de memória serial.


A2, A1, A0 - endereço da memória no barramento programado no hardware.
R/W - bit que define se vamos ler ou escrever na 24CXX. Para ler colocamos o valor 1
em R/W. E para escrever colocamos o valor 0 em R/W.

Gravação

Diagrama de gravação de dados:

Para gravarmos dados (byte) na EEPROM 24CXX, temos de seguir estes processos:

1) Envio do bit de início da comunicação - START .


2) Envio do byte com o endereço físico da memória 24CXX no barramento I2C, sendo o bit
LSB desse byte (R/W) em nível 0 (escrita), pois vamos escrever na memória.
1) Envio do endereço na EEPROM em que o dado será gravado.
2) Envio dos dados (byte).
3) Envio do bit de finalização de comunicação - STOP.

No processo de escrita, os bits de reconhecimento de dados ACK são gerados pelo receptor (memória).

Leitura

Diagrama de leitura de dados.

1) Envio do bit de início da comunicação - START.


2) Envio do byte com o endereço físico da memória 24CXX no barramento I2C, sendo o bit
LSB desse byte (R/W) em nível 0 (escrita).
3) Envio do endereço na EEPROM em que será lido o dado.
4) Envio novamente do bit de início da comunicação - START.
5) Envio novamente do byte com o endereço físico da memória 24CXX no barramento I2C,
sendo o bit LSB desse byte (R/W) agora em nível 1 (leitura), pois vamos ler dados da memória.
6) Leitura dos dados na memória. A cada byte lido o mestre envia uma confirmação de
recebimento de dado ACK no barramento.
7) Ao término da leitura, o master informa que não quer mais ler dados através do bit
NOACK, e finaliza a leitura pelo bit stop.

No processo de leitura, os bits de reconhecimento de dados ACK são gerados pelo mestre (receptor).

Receita de Bolo
É muito fácil de gravar dados em um memória da série 24Cxx em programas feitos com auxilio do
mikroC. Acompanhe essa receita de bolo:Invertemos o diagrama de gravação de dados na qual mostra
todas as etapas para gravar a memória 24Cxx, e colocamos para cada estado do diagrama a linha de
programa correspondente em BASIC.

Programa de Gravação I2C.

Teremos então como exemplo de programa:

I2C_Init(100000); // frequencia de clock do barramento I2C (CONSULTAR DATASHEET


DO CHIP PARA SABER SUA FREQUENCIA)
//esta função só precisa ser inicializada somente 1 unica vez no programa

I2C_Start(); // start no barramento i2c


I2C_Wr(0xA0); // endereço da memória no hardware do kit (w = 0)
I2C_Wr(0x00); // endereço da eerprom onde será salvo o dado
I2C_Wr(variavel_A); // grava a variável A no endereço 00 da memória serial
I2C_Wr(variavel_B); // grava a variável B no endereço 01 da memória serial
I2C_Wr(variavel_C); // grava a variável C no endereço 02 da memória serial
I2C_Wr(variavel_D); // grava a variável D no endereço 03 da memória serial
I2C_Stop(); // condição de stop na comunicação i2c

Este diagrama de comunicação I2C é padrão e utilizados em muitos dispositivos I2C. Apesar de
estarmos se tratando do processo de gravação para a serie de memória 24Cxx, este bloco de comandos
são válidos para quase todos outros componentes I2C.Por exemplo: conversores A/D e D/A I2C, como o
PCF8591N; RTC - PCF8583N, DS1307, entre outros.
Programa de Leitura I2C.

O processo de leitura de já em um pouco mais trabalhoso e confuso para muitos programadores que
utilizam o assembly ou o C. Em BASIC o processo é muito fácil de ser programado, acomanhe:

Teremos então como exemplo de programa:

I2C_Start(); // start no barramento i2c


I2C_Wr(0xA0); // endereço da memória no hardware (w = 0)
I2C_Wr(0x00); // endereço da eeprom onde será lido o byte
I2C_Repeated_Start(); // re-start no barramento
I2C_Wr(0xA1); // endereço da memória no hardware do kit (w = 1)
variavel_A = I2C_Rd(ack); // salva o valor armazenado no endereço 00h da memória na
variável A
variavel_B = I2C_Rd(ack); // salva o valor armazenado no endereço 01h da memória na
variável B
variavel_C = I2C_Rd(ack); // salva o valor armazenado no endereço 02h da memória na
variável C
variavel_D = I2C_Rd(0); // salva o valor armazenado no endereço 03h da memória na
variável D: (NO acknowledge)
I2C_Stop(); // finaliza a comunicação i2c

Acompanhe passo a passo o diagrama de leitura e escrita de dispositivos


no barramento I2C. Para programar outros tipos de componentes, basta
verificar o diagrama de leitura e escrita no datasheet fornecido pelo
fabricante.
Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem
prévio aviso. Maiores informações: suporte@microgenios.com.br
Exercícios resolvidos
1.0 Exercício 01
1.1 Exercício 02

Introdução

Aprendemos nesta unidade um pouco sobre a comunicação serial RS232. Agora iremos colocar em prática o que estudamos
até agora através dos execícios resolvidos abaixo:

Exercício 01:
Vamos elaborar um simples programa, que cujo objetivo, seja ler o canal AD0 do PIC, converter esse valor em string e
transmiti-lo pelo canal serial do PIC a uma velocidade de 9600 bps.

Acompanhe o esquema elétrico abaixo;

Programa:

/*************************************************************************
Microgenios Soluções eletrônica Ltda
Prog: le_ad0_envia_serial
Autor: Fernando Simplicio
KitPIcgenios
cristal: 8MHz
**************************************************************************/

unsigned char texto[] = " "; //define matriz texto


int temp_res0 = 0;

//*********************** rotina de transmissão serial ********************


void TX_AD(){ //subrotina de envio de dado pela serial do PIC
char x = 0; //definine variável x do tipo char

Usart_Write(13); //retorna para o inicio da linha


Usart_Write('\n'); //pula de linha

do{

Usart_Write(texto[x]); //envia pela serial o caracterer apostado pelo ponteiro


x++; //incrementa variável
}
while (texto[x] != '\0'); //verifica se ponteiro é igual a NULL

}
//*****************************programa principal ******************************
void main(){

adcon1 = 2; //programa canal RA0 como AD0


Usart_Init(9600); //inicializa serial com 9600 bps

while(1){

temp_res0 = Adc_Read(0); //le canal ad0 do PIC e salva valor na variável temp_res0
intToStr(temp_res0, texto); //converte valor da conversão do ad0 para string
delay_ms(10);
TX_AD(); //chama função de envio do val

}
}

//************************ fim do programa *************************************

Clique e faça download do código fonte do programa acima:

Descrição do programa:

Não fique muito assustado caso não tenha compreendido o funcionamento de todos os comandos do programa anterior, veremos
agora algumas de suas particularidades:

Acompanhe a seguinte linha de programa:

unsigned char texto[] = " "; //define matriz texto

No comando acima defimos uma variável matriz do tipo char não sinalizada. Repare que não informamos no comando texto[] o
número de elementos da matriz, neste caso, por regra da linguagem C, somos obrigados a inicilizar esta matriz. No comando acima
termos então a seguinte construção:

texto[0] = " "


texto[1] = " "
texto[2] = " "
texto[3] = NULL <-- caractere de terminação de string (\0)

O comando a seguir configura os pinos do PIC como A/D e define a velocidade de baudrate na comunicação serial.

void main(){

adcon1 = 2; //programa canal RA0 como AD0


Usart_Init(9600); //inicializa serial com 9600 bps

o bloco seguinte tem por objetivo, ler o canal AD0 do PIC, converter o valor lido em string e chamar a subrotina de transmissão
desse valor pela serial do PIC.

while(1){

temp_res0 = Adc_Read(0); //le canal ad0 do PIC e salva valor na variável temp_res0
intToStr(temp_res0, texto); //converte valor da conversão do ad0 para string
delay_ms(10);
TX_AD(); //chama função de envio do val

agora só nos resta entender o funcionamento da subrotina de transmissão serial. A função de tratamento da transmissão e recepção
serial no mikroC somente permite que enviamos um bytes de dados por vez, isso implica que devemos tratar nossas variáveis ou
strings antes de utilizar estas funções.

Acompanhe o raciocínio:

Através dos comandos abaixo, o valor lido no AD0 é atribuido a variável temp_res0, e após isso, convertemos o valor dessa
variável em string e atribuimos esse novo valor na variável texto, através do comando: intToStr(temp_res0, texto).

Como sabemos, a variável texto é do tipo matriz, isso significa que essa variável aloca seus dados sequênciais na memória, byte a
byte.
Imaginemos agora, para entendermos melhor, que o valor lido no AD0 do PIC seja o valor 652, Teremos como resultado presente
na variável texto após a execução do comando intToStr(temp_res0, texto) o seguinte resultado:

texto[0] = "6"
texto[1] = "5"
texto[2] = "2"
texto[3] = "\0" <-- caractere de terminação de string NULL (\0)

A partir desse raciocício, criamos uma subrotina que tem por objetivo enviar do PIC cada caracterer presente na variável matriz
texto de forma a "reconstruir" novamente o valor lido no canal AD0.

void TX_AD(){ //subrotina de envio de dado pela serial do PIC


char x = 0; //definine variável x do tipo char

Usart_Write(13); //retorna para o inicio da linha


Usart_Write('\n'); //pula de linha

do{
Usart_Write(texto[x]); //envia pela serial o caracterer apostado pelo ponteiro
x++; //incrementa variável x, cujo obetivo é alterar o indice da matriz.
}
while (texto[x] != '\0'); //verifica se ponteiro é igual a NULL

Copie e compile o programa deste exercício no mikroC, grave no microcontrolador PIC e teste seu funcionamento utilizando o
hyperterminal do windows para visualizar os dados enviado através do canal serial do PIC.

Visualização do programa no hyperterminal do windows:

Exercício 02:
Para aprimorar ainda mais nossos conhecimentos, vamos implementar ao nosso programa anterior um display LCD 16X2 e mais um
trimpot ligado ao AD1, de forma que todos os caracteres envias pela serial do PIC, também seja mostrado no LCD.

Esquema elétrico:

Programa:
/*************************************************************************
Microgenios Soluções eletrônica Ltda
Prog: le_ad0_AD1_envia_serial
Autor: Fernando Simplicio
KitPIcgenios
cristal: 8MHz
**************************************************************************/

unsigned char texto[] = " ";


unsigned char texto1[] = " ";

int temp_res0 = 0;
int temp_res1 = 0;

//***************************transmite canal AD0 ************************


void TX_AD0(){
char x = 0;
Usart_Write(13);
Usart_Write('\n');

do{

Usart_Write(texto[x]); //envia pela serial o caracterer apostado pelo ponteiro


x++;
}
while (texto[x] != '\0'); //verifica se ponteiro é igual a NULL

//***************************transmite canal AD1 ************************


void TX_AD1(){
char x = 0;
Usart_Write(13);
Usart_Write('\n');

do{

Usart_Write(texto1[x]); //envia pela serial o caracterer apostado pelo ponteiro


x++;
}
while (texto1[x] != '\0'); //verifica se ponteiro é igual a NULL

//*************************** programa principal ***************************


void main()
{
ADCON1 = 6; // Configure analog inputs and Vref

Lcd_custom_Config(&PORTD,7,6,5,4,&PORTE,2,0,1); //configura e inicializa LCD no modo 4 bits


Lcd_custom_Cmd(Lcd_CLEAR); // apaga display
Lcd_custom_Cmd(Lcd_CURSOR_OFF); // desliga cursor
Lcd_Custom_Out(1,1, "Canal AD0 = "); //escreve mansagem na linha 1, coluna 1 do lcd
Lcd_Custom_Out(2,1, "Canal AD1 = "); //escreve mensagem na linha 2, coluna 1 do lcd

Usart_Init(9600);
adcon1 = 2;

do{

temp_res0 = Adc_Read(0); //le canal ad0 do PIC e salva valor na variável temp_res0
temp_res1 = Adc_Read(1); //lê canal ad1 do PIC e salva valor na variável temp_res1

intToStr(temp_res0, texto); //converte valor da conversão do ad0 para string


Lcd_Custom_Out(1,11,texto); //escreve no lcd o valor da conversão do ad0
delay_ms(10);
TX_AD0();

intToStr(temp_res1, texto1); //converte valor da conversão do ad1 para string


Lcd_Custom_Out(2,11,texto1); //escreve no lcd o valor da conversão do ad1
delay_ms(10);
TX_AD1();
}

while(1);
}

Clique e faça download do código fonte do programa acima:


Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores
informações: suporte@microgenios.com.br
RTC - Estudo do relógio DS1307
1.0 RTC - Relógio de Tempo Real
1.1 DS1307
1.2 Pinagens do DS1307:
1.3 Escrevendo no relógio DS1307
1.4 Lendo o relógio DS1307

RTC - Relógio de Tempo Real


Bom, já conhecemos até este ponto do nosso curso as particularidades do protocolo de comunicação I2C. Estudaremos agora sobre um
dispositivo muito utilizado em diversos equipamentos industriaisl, o Relógio de tempo real, também conhecido como RTC.

Existe uma série muito grande de chips RTC no mercado, em nosso curso utilizaremos o DS1307 fabricado pela DALLAS semicondutores.

DS1307

O chip DS1307 é um relógio de tempo real completo, que nos pemite programar segundo, minutos, horas, dias, mês e anos. Podemos
programar individualmente cada um dessas opções individualmente a qualquer momento em nosso programa via comunicação I2C. Este
relógio pode operar no formato de 12 ou 24 horas e AM/PM.

Características do relógio:

Contador de segundos, minutos, horas, dias, dias da semana, mês e anos com contagem pré programada pela Dallas até 2100, isso
representa que o próprio chip opera normalmente a contagem nos meses de 31 dias e 28 dias (fevereiro), incluindo também os dias
da semana (segunda feira, terça feira, quarta feira ...).
Entrada para bateria externa de 3.0V.
Protocolo I2C de fácil manipulação.
Consumo menor que 500nA da bateria (chip operando).
Temperatura de operação na faixa de -40ºC a +85ºC.
Tensão de operação: 5V DC
Frequência de Trabalho: 100KHz.

Diagrama interno do DS1307

Pinagens do DS1307:

DS1307
Pinagens Descrição
1 X1 pino de entrada do cristal oscilador de 32.768khz.
2 X2 pino de entrada do cristal oscilador de 32.768khz.
pino de entrada da tensão da bateria de backup. (bateria de 3V
3 Vbat
de lithium é requerida).
4 GND GND
5 SDA Entrada e saida serial DATA na comunicaçãos I2C
6 SCL Entrada do clock SCL na comunicação I2C
pino de saida de clock (1Hz, 4KHz, 8KHz, 32KHz) quando bit
7 SWQ/OUT
SQWE for igual a 1.
8 Vcc Alimentação 5V do chip

Nosso relógio possui internamento registradores especiais cuja função é de configurar o modo de trabalho dos temporizadores. Através
desse registradores podemos configurar os segundos, minutos, hora, dia da semana, mês e ano, assim como definir o modo de operação
do calendário, de 12 ou 24 horas, AM/PM.

Acompanhe o mapa dos registradores:

registradores responsáveis pela configuração do relógio:

Acompanhe o estudo mais detalhado desses registradores:

Através da descrição acima, já é possivel entender como devemos configurar nosso Relógio.

Escrevendo no relógio DS1307

Vamos analizar agora o gráfico que nos ensina a programar o DS1307 via I2C utilizando o mikroC.
Todos os dados de leitura e escrita que manipularmos no DS1307 devem necessáriamente estar no formato BCD.
(caso tenha dúvida com relação ao formato BCD leia novamente o material complementar da primeira unidade).

O relógio DS1307 enquadra-se na categoria em que seu endereço no barramento I2C é 1101000X.

Exemplo de programa no mikroC:

Vamos elaborar um programa no mikroC que grave o seguinte calendário no DS1307.

Hora: 16h: 25mm : 27 s


Dia: 12/03/07
Dia da semana: quinta-feira

I2C_Start(); //inicializa a comunicação I2c


I2C_Wr(0xD0); //endereço fixo para a categoria do DS1307: 1101000X, onde x = 0 é para gravação, e X = 1 para leitura.
I2C_Wr(0); //endereço onde iremos começar a programação do relógio, neste caso é o endereço dos segundos.
I2C_Wr(0x27); //inicializa a contagem dos segundos apartir dos 14 segundos.
I2C_Wr(0x25); //inicializa a contagem apartir de 25 minutos.
I2C_Wr(0x16); //inicializa a contagem dos hora apartir das 16:00hs (formato 24 horas).
I2C_Wr(0x05); //inicializa a contagem dos dias da semana apartir da quinta feira
I2C_Wr(0x12); //inicializa a contagem apartir do dia 12 do mês
I2C_Wr(0x03); //inicializa a contagem apartir do mês de março
I2C_Wr(0X07); //inicializa a contagem dos anos apartir de 07.
I2C_Stop();

Lendo o relógio DS1307

Bom, agora que já aprendemos a escrever no relógio I2C, faz necessário lermos as informações que estão sendo processadas pelo nosso relógio.
Para isso devemos compreender o funcionamento do protocolo I2C no processo de leitura, acompanhe:

Protocolo de leitura do relogio DS1307.


Exemplo de programa no mikroC:

Vamos elaborar um programa no mikroC que leia os dados dos registradores do relógio DS1307.

programa:

I2C_Start(); //inicializa a comunicação I2c


I2C_Wr(0xD0); //endereço fixo para a categoria do DS1307: 1101000X, onde x = 0 é para gravação, e X = 1 para
leitura.
I2C_Wr(0); //endereço onde iremos começar a ler o relógio, neste caso é o endereço dos segundos.
I2C_Repeated_Start(); // reestart no barramento i2C
I2C_Wr(0xD1); //endereço fixo para a categoria do DS1307: 1101000X, onde x = 0 é para gravação, e X = 1 para
leitura.
segundo = I2C_Rd(1); //lê os segundos (de 0 a 60)
minuto = I2C_Rd(1); //lê os minutos (de 0 a 60)
horas = I2C_Rd(1); //lê as horas (0 a 12horas ou de 0 a 24 horas, vai depender do estado do 6 bit desse
registrador de horas
dias_semana= I2C_Rd(1); //lê o dia da semana (segunda, terça, querta. Valores de 01 a 07.)
data = I2C_Rd(1); //lê o dia da semana (de 01 a 31)
mes = I2C_Rd(1); //lê os meses ( de 01 a 12)
ano = I2C_Rd(0); //lê o ano ( de 00 a 99)
I2C_Stop(); //gera condição de finalização de comunicação

Repare que alguns dos comandos possuem um o indicativo numérico 1 (I2C_Rd(1)), isso nos representa que iremos ler mais dados no
barramento I2C. Agora o último comando ano = I2C_Rd(0) utilizamos o indicador numérico 0 (zero) para informar ao DS1307 que não iremos
mais ler nenhum byte.

Agora chegou o momento de elaborarmos nosso primeiro programa utilizando os recursos do mikroC para programarmos o relógio DS1307.

Vamos elaborar um programa que escreva o seguinte horário no DS1307, e apartir disso, leremos o valor corrente das horas no DS1307 e
escreveremos esses valores no visor do LCD 16X2.:

Horas a serem programadas:

08:44:25 (8horas, 44 minutos e 25 segundos).

Esquema elétrico:
Programa:

/*******************************************************************************
Microgenios Soluções Eletrônica Ltda
Programa Exemplo: RTC
Autor: Fernando Simplicio
Objetivo: programar e ler o relógio DS1307 utilizando comunicação I2C.
Cristal: 8Mhz.
******************************************************************************/

//*********************** define variáveis globais do programa *****************


unsigned short segundo, minuto, horas;
unsigned short var1, var2;

//********************* Rotina de conversão de dados para BCD ******************


void BCD(unsigned short x, unsigned short y,unsigned short var) {
var1 = (var >> 4) + 0x30; //converte o primeiro nibble em BCD e após isso, em string
LCD_custom_Chr(x,y,var1); //escreve caractere no LCD
var2 = (var & 0x0F) + 0x30; //converte o segundo nibble em BCD e após isso, em string
LCD_custom_Chr_Cp(var2); //escreve caractere no LCD
}
//lembre-se que nosso relógio somente trabalha com dados no formato BCD e é por esse motivo que devemos converter

//todos os dados de leitura e escrita para este formato.

//******************** subrotina de leitura do DS1307 **************************

void leitura_rtc() { //rotina de leitura do DS1307

I2C_Start(); //inicializa comunicação i2c


I2C_Wr(0xD0); //endereço fixo para a categoria do DS1307: 1101000X, onde x = 0 é para gravação, e X = 1 para leitura.
I2C_Wr(0); //endereço onde iremos começar a programação do relógio, neste caso é o endereço dos segundos.
I2C_Repeated_Start(); // Issue I2C signal repeated start
I2C_Wr(0xD1); //endereço fixo para a categora do DS1307: 1101000X com o ultimo bit acionado (1) para leitura
segundo = I2C_Rd(1); //lê o primeiro byte segundos e informa que ainda leremos mais dados através do sinalizador (1)
minuto = I2C_Rd(1); //lê o segundo byte minutos e informa que ainda leremos mais dados através do sinalizador (1)
horas = I2C_Rd(0); //lê o terceiro byte horas e encerra as leituras de dados, graças ao sinalizador (0)
I2C_Stop(); //finaliza comunicação I2C
}

//******************** subrotina que grava temporização no DS1307 ***************

void programa_i2c(){
I2C_Init(100000); // iniciliza I2C com frequencia de 100KHz
I2C_Start(); //inicializa a comunicação I2c
I2C_Wr(0xD0); //endereço fixo para a categoria do DS1307: 1101000X, onde x = 0 é para gravação, e X = 1 para leitura.
I2C_Wr(0); //endereço onde iremos começar a programação do relógio, neste caso é o endereço dos segundos.
I2C_Wr(0x25); //inicializa a contagem dos segundos apartir de 25.
I2C_Wr(0x44); //inicializa a contagem apartir de 44 minutos.
I2C_Wr(0x08); //inicializa a contagem dos hora apartir das 08:00hs (formato 24 horas).
I2C_Stop(); //condição de finalização da comunicação I2C
}

//************ subrotina que escreve no LCD os dados lidos no DS1307 ***************

void display_lcd() {
BCD(1,1,horas); //chama subrotina de conversão da variável horas para BCD
LCD_custom_Chr_Cp(':'); //escreve no display LCD
BCD(1,4,minuto); //chama subrotina de conversão da variável minuto para BCD
LCD_custom_Chr_Cp(':'); //escreve no display LCD
BCD(1,7,segundo); //chama subrotina de conversão da variável segundo para BCD
}

//******************** programa principal **************************************


void main(){

trisd = 0; //define portd como saida


adcon1 = 0x06; //torna todos os pinos AD como i/o de uso geral
Lcd_custom_Config(&PORTD,7,6,5,4,&PORTE,2,0,1); //configura e inicializa LCD no modo 4 bits
Lcd_custom_Cmd(Lcd_CLEAR); // apaga display
Lcd_custom_Cmd(Lcd_CURSOR_OFF); // desliga cursor

programa_i2c();

while(1){ //inicializa loop


display_lcd(); //escreve no display lcd o valor
leitura_rtc(); //efetua leitura de segundo, minuto e horas do DS1307
//delay_ms(100); //gera delay de 100 milisegundos
}
}

Clique aqui para fazer download do programa acima:

Aproveitando o esquema eletrico do programa anterior, vamos elaborar um novo programa cuja finalizade seja prograr no DS1307 o seguinte
calendário:

Hora: 08: 44: 28 (8 horas, 44 minutos, 28 segundos)


Calendário: 14/10/2007
Dia da semana: sexta

e escrever esses valores no display LCD 16 x2

Programa:

Obs: o programa abaixo consome 2204 byte de memória de programa do PIC. Neste caso, este programa não será possivel ser compilado
na versão demo do mikroC.

/*******************************************************************************
Microgenios Soluções Eletrônica Ltda
Programa Exemplo: RTC
Autor: Fernando Simplicio
Objetivo: programar e ler o relógio DS1307 utilizando comunicação I2C.
Cristal: 8Mhz.
******************************************************************************/

//*********************** define variáveis globais do programa *****************


unsigned short segundo, minuto, horas, dias_semana, dia, mes, anos;
unsigned short var1, var2;
char *texto;

//********************* Rotina de conversão de dados para BCD ******************


void BCD(unsigned short x, unsigned short y,unsigned short var) {
var1 = (var >> 4) + 0x30; //converte o primeiro nibble em BCD e após isso, em string
LCD_custom_Chr(x,y,var1); //escreve caractere no LCD
var2 = (var & 0x0F) + 0x30; //converte o segundo nibble em BCD e após isso, em string
LCD_custom_Chr_Cp(var2); //escreve caractere no LCD
}

//******************** subrotina de leitura do DS1307 **************************


void encontra_dia_semana() {
switch (dias_semana) {
case 01: texto = "DOMINGO"; break; //caso dias_semana = 01 então..
case 02: texto = "SEGUNDA"; break; //caso dias_semana = 02 então..
case 03: texto = "TERCA"; break; //caso dias_semana = 03 então..
case 04: texto = "QUARTA"; break; //caso dias_semana = 04 então..
case 05: texto = "QUINTA"; break; //caso dias_semana = 05 então..
case 06: texto = "SEXTA"; break; //caso dias_semana = 06 então..
default: texto = "SABADO"; //se não for nenhum desses então...
}
}

//******************** subrotina de leitura do DS1307 **************************

void leitura_rtc() { //rotina de leitura do DS1307

I2C_Start(); //inicializa comunicação i2c


I2C_Wr(0xD0); //endereço fixo para a categoria do DS1307: 1101000X, onde x = 0 é para gravação, e X = 1 para leitura.
I2C_Wr(0); //endereço onde iremos começar a programação do relógio, neste caso é o endereço dos segundos.
I2C_Repeated_Start(); // Issue I2C signal repeated start
I2C_Wr(0xD1); //endereço fixo para a categora do DS1307: 1101000X com o ultimo bit acionado (1) para leitura
segundo = I2C_Rd(1); //lê o primeiro byte segundos e informa que ainda leremos mais dados através do sinalizador (1)
minuto = I2C_Rd(1); //lê o segundo byte minutos e informa que ainda leremos mais dados através do sinalizador (1)
horas = I2C_Rd(1); //lê o terceiro byte horas e encerra as leituras de dados, graças ao sinalizador (0)
dias_semana = I2C_Rd(1);
dia = I2C_Rd(1);
mes = I2C_Rd(1);
anos = I2C_Rd(0);
I2C_Stop(); //finaliza comunicação I2C
}

//******************** subrotina que grava temporização no DS1307 ***************


void programa_i2c(){
I2C_Init(100000); // iniciliza I2C com frequencia de 100KHz
I2C_Start(); //inicializa a comunicação I2c
I2C_Wr(0xD0); //endereço fixo para a categoria do DS1307: 1101000X, onde x = 0 é para gravação, e X = 1 para leitura.
I2C_Wr(0); //endereço onde iremos começar a programação do relógio, neste caso é o endereço dos segundos.
I2C_Wr(0x25); //inicializa a contagem dos segundos apartir de 25.
I2C_Wr(0x44); //inicializa a contagem apartir de 44 minutos.
I2C_Wr(0x08); //inicializa a contagem dos hora apartir das 08:00hs (formato 24 horas).
I2C_Wr(0x06); //inicializa a contagem da sexta feira da semana
I2C_Wr(0x14); //inicializa a contagem do dia 14 do mes
I2C_Wr(0x10); //inicializa a contagem do mês de setembro
I2C_Wr(0x07); //inicializa a contagem do ano 07.
I2C_Stop(); //condição de finalização da comunicação I2C
}

//************ subrotina que escreve no LCD os dados lidos no DS1307 ***************

void display_lcd() {
lcd_custom_cmd(lcd_clear); //apaga lcd
BCD(1,1,horas); //chama subrotina de conversão da variável horas para BCD
LCD_custom_Chr_Cp(':'); //escreve no display LCD
BCD(1,4,minuto); //chama subrotina de conversão da variável minuto para BCD
LCD_custom_Chr_Cp(':'); //escreve no display LCD
BCD(1,7,segundo); //chama subrotina de conversão da variável segundo para BCD
encontra_dia_semana(); //chama subrotina onde será encontrado o dia da semana
LCD_custom_out(1,10,texto); //escreve no display LCD
BCD(2,1,dia); //chama subrotina de conversão da variável horas para BCD
LCD_custom_Chr_Cp('/'); //escreve no display LCD
BCD(2,4,mes); //chama subrotina de conversão da variável horas para BCD
LCD_custom_out(2,6,"/20"); //escreve no display LCD
BCD(2,9,anos); //chama subrotina de conversão da variável horas para BCD
}

//******************** programa principal **************************************


void main(){

trisd = 0; //define portd como saida


adcon1 = 0x06; //torna todos os pinos AD como i/o de uso geral
Lcd_custom_Config(&PORTD,7,6,5,4,&PORTE,2,0,1); //configura e inicializa LCD no modo 4 bits
Lcd_custom_Cmd(Lcd_CLEAR); // apaga display
Lcd_custom_Cmd(Lcd_CURSOR_OFF); // desliga cursor

programa_i2c();

while(1){ //inicializa loop


display_lcd(); //escreve no display lcd o valor
leitura_rtc(); //efetua leitura de segundo, minuto e horas do DS1307
delay_ms(200); //gera delay de 200 milisegundos
}
}

Clique aqui para fazer download do programa acima:

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Teclado Matricial
1.0 Introdução

Introdução

Chegamos a última unidade de nosso curso. Nesta nova unidade estudaremos vários estudos de casos reais.

Bom, vamos estudar agora os recursos e técnicas de programação para leitura de teclados matriciais:

Os teclados Matriciais

Os teclados matriciais são muito utilizados nos dias de hoje, pois é através deles que podemos utilizar em nossa aplicações um número
grande de botões sem comprometer muitos pinos do microcontrolador. O teclado matricial, também conhecido como matriz de botões, se
consiste em um arranjo conhecido como teclado matriz C x L (C corresponde a coluna, e L corresponde a linha).

Podemos ver um exemplo clássico de teclado matricial 3 x 4 na figura abaixo:

Em uma matriz de teclado C x L, o que determina o número de pinos necessário para varrer o teclado é determinado pela soma do número
de linhas mais numero de colunas do teclado utilizado. Na figura anterior utilizaremos 7 pinos do microcontrolador para lermos as teclas.
Na figura abaixo apresentamos o teclado 3 x 4 do KIT PICGENIOS da Microgenios.

O Funcionamento:

Para compreendermos melhor, vamos analizar o esquema elétrico abaixo:


O rastreamento do teclado consiste em colocar nível lógico 0 em uma das colunas (RB0, RB1, RB2 e RB3) de cada vez e efetuar a leitura
das linhas RB4, RB5, RB6, RB7. Caso algumas dessas linhas estiverem em nivel lógico 0, representa que a tecla correspondente ao
cruzamento da linha x coluna lida esta pressionada.

Por exemplo:

Vamos supor que colocamos nivel lógico 0 na primeira coluna (pino RB0) enquanto as outras colunas RB1, RB2 e RB3 permanecem em
nível lógico 1. Após isso, realizamos uma leitura nas linhas (RB4, RB5, RB6 e RB7). Se todas as teclas estiverem soltas, todas as linhas
estarão com nível lógico 1. Se alguma linha estiver em nível lógico 0 enquanto a coluna estiver ressetada, representa que a tecla no
cruzamento C x L esta pressionada.
Somente uma coluna devem ser colocada em nível lógico 0 de cada vez, enquanto as demais colunas devem permanecer em nível lógico 1.

Rotina de varredura de teclados matriciais:

Vamos elaborar um simples programa utilizando teclado matricial.


O programa seguinte tem por objetivo ler duas teclas por varredura com o intuito de ascender um led1. A tecla1 acende o led1 e a tecla2
apaga este mesmo led1. A tecla5 acende o led2, enquanto a tecla6 apaga este mesmo led.

Esquema elétrico:

Programa:

/***************************************************************************
CENTRO DE TECNOLOGIA MICROGENIOS
ESTE PROGRAMA TEM POR OBJETIVO ESCREVER NO LCD O VALOR NUMÉRICO DE CADA
TECLA DO TECLADO MATRICIAL.
*****************************************************************************
*/

//**************************** subrotina de inicialização do lcd************


void inicializa_lcd(void) //
{
Lcd8_Config(&PORTE,&PORTD,2,1,0,7,6,5,4,3,2,1,0);
Lcd8_Cmd(Lcd_Clear);
Lcd8_Cmd(LCD_CURSOR_OFF);
delay_ms (100);
}

//****************** subrotina de escrita no lcd ****************************


void lcd(char var1[5])
{
trisd = 0 ;
Lcd8_Cmd(Lcd_Clear);
Lcd8_Out(1, 1, "Tecl. Matricial");
Lcd8_Out(2, 0, "TECLA N: ");
LCD8_out_cp(var1);
trisd = 255;
}
//*************************************************************************

//******************** programa principal *********************************

void main() {

char var2; //define variável


adcon1 = 0x06; //define pinos AD como I/O de uso geral
trisd = 255; //configura portd como entrada
trisa = 0; //configura porta como saida

inicializa_lcd(void); //chama subrotina de inicialização do lcd


lcd(" ");

portb = 255; //portb em FF


portd = 255; //portd em FF
trisd = 255; //configura portd como entrada
trisb = 0; //configura portb como saida
//-------------------------------------------------------------------------
//inicio do programa de varredura do teclado matricial
//--------------------------------------------------------------------------

do
{

portb.f0 = 0; //habilita primeira coluna do teclado

var2 = portd;

if (var2.f0 == 0) lcd("<---");
else if (var2.f1 == 0) lcd("7");
else if (var2.f2 == 0) lcd("4");
else if (var2.f3 == 0) lcd("1");

portb.f0 = 1; //desabilita primeira coluna do teclado


//--------------------------------------------------------------------------

portb.f1 = 0; //habilita segunda coluna do teclado

var2 = portd;

if (var2.f0 == 0) lcd("0");
else if (var2.f1 == 0) lcd("8");
else if (var2.f2 == 0) lcd("5");
else if (var2.f3 == 0) lcd("2");

portb.f1 = 1; //desabilita segunda coluna do teclado

//------------------------------------------------------------------------

portb.f2 = 0; //habilita terceira coluna do teclado

var2 = portd;

if (var2.f0 == 0) lcd("-->");
else if (var2.f1 == 0) lcd("9");
else if (var2.f2 == 0) lcd("6");
else if (var2.f3 == 0) lcd("3");

portb.f2 = 1; //desabilita terceira coluna do teclado

delay_ms(100);

}
while(1);
}
Clique aqui para fazer download do programa:

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores informações:
suporte@microgenios.com.br
Teclado PS2
1.0 Introdução
1.1 Como funciona um teclado de PC
1.2 Trabalhando com teclado PS/2 no mikroC
1.3 Exemplo 01
1.4 Exemplo 02

Introdução

Os teclados de PCs é uma ótima ferramenta para implementações e inserção de dados em equipamentos de diversos tipos.
Devido ao seu custo baixo, funcionalide, praticidade e eficiência, os teclados de PC's vem cada vez mais sendo utilizados
pelos projetistas eletrônicos.

Como funciona um teclado de PC?

O teclado e seu host estão conectados através de 4 fios (entenda-se “host” como o dispositivo que se comunica com o teclado
trocando informações com ele, que no caso de um PC é o próprio PC).

A conexão entre um teclado e o PC é realizada através de um conector DIN de 5 pinos em 180º ou através de um conector do
tipo PS/2 (mini-din de 6 pinos). Em ambos os casos uma linha transmite o sinal do clock, outra dos dados e os demais a
alimentação +5V e GND.

DIN 5 PINOS Mini DIN 6 Pinos PS/2

A comunicação entre o teclado e o host é em série com bit inicial, oito bits de dados, paridade ímpar e bit de parada. É bi-
direcional, o teclado informa ao host a atividade das teclas e o host envia os comandos para sua configuração, acendimento
de luzes (CapsLock, NumLock, etc) re-inicialização, re-envio, etc. Isto faz que o sinal de dados possa ser usado por ambas as
partes, enquanto que o clock controla sempre o teclado.

Transmissão: Teclado -> Host


Transmissão: Host -> Teclado

LSB: Less Significative Bit (Bit menos significativo)


MSB: Most Significative Bit (Bit mais significativo)
P Odd: Paridade Impar
ACK: Acknowledge (Reconhecimento)

Trabalhando com teclado PS/2 do mikroC:

O compilador mikroC possui funções pré-programada para tratamento de teclado PS/2. Através de simples comandos
podemos receber dados provenientes de um teclado e tratar essas informações para determinados fins. Os comandos de
tratamento do teclado PS/2 no mikroC são:

Ps2_Init
Ps2_Config
Ps2_Key_Read

A função Ps2_Init tem por objetivo inicializar o PORT para trabalhar com o teclado PS/2. Esta função normalmente é
utilizada quando desejamos utilizar os pinos pré-programados do port para comunicação SDA e SCL entre teclado e PIC.

sintaxe:

Ps2_Init(&PORT);

onde *PORT pode ser: PORTA, PORTB, PORTC, PORTD e PORTE.

Exemplo:

Ps2_Init(&PORTD);

A função Ps2_Config tem por objetivo inicializar e configurar os pinos do PIC que participarão da comunicação com o
teclado PS/2.

sintaxe:

Ps2_Config(&PORT_do_PIC, pino_de_clock, pino_de_data);

onde:
PORT_do_PIC = PORTA, PORB, PORTC, PORTD ou PORTE.
pino_de_clock = RX0 a RX7 (x referece ao PORT_DO_PIC . Ex: RA0)
pino_de_data = RX0 a RX7 (x referece ao PORT_DO_PIC . Ex: RA0)

Exemplo:

Ps2_Config( &PORTB, 4, 5); //configura os pinos RB4 (clock) e RB5 (data) do PORTB
para manipulação do teclado PS/2.

Ps2_Key_Read

Afunção Ps2_Key_Read tem por objetivo identificar se alguma tecla foi pressionada, receber o valor da tecla pressionada e
identificar se as teclas de funções especiais, tais como ESC, Enter, estão pressionadas. caso alguma tecla tenha sido
pressionada a função seus parametros retornará nivel 1, caso contrário, ou seja, caso nenhuma tecla tenha sido pressionada,
retorna nivel 0.

sintaxe:

Ps2_Key_Read(&valor_da_tecla, &tecla_especial, &bit_status))

onde:
&valor_da_tecla = retorna o valor da tecla pressionada no teclado PS/2.
&tecla_especial = verifica se alguma tecla especial foi pressionada.
&bit_status = verifica se alguma tecla foi pressionada. Caso verdadeira, retorna 1, caso falso,
retorna 0.

Exemplo:

if (Ps2_Key_Read(&valor_teclado, &tecla_especial, &bit_status)) {

Segue abaixo a tabela com os valores de retorno para cada tecla pressionada no teclado PS/2:
Key Value returned
F1 1
F2 2
F3 3
F4 4
F5 5
F6 6
F7 7
F8 8
F9 9
F10 10
F11 11
F12 12
Enter 13
Page Up 14
Page Down 15
Backspace 16
Insert 17
Delete 18
Windows 19
Ctrl 20
Shift 21
Alt 22
Print Screen 23
Pause 24
Caps Lock 25
End 26
Home 27
Scroll Lock 28
Num Lock 29
Left Arrow 30
Right Arrow 31
Up Arrow 32
Down Arrow 33
Escape 34
Tab 35

Observação 01: Para que possamos utilizar as funções de tratamento de teclados PS/2 no mikroC,
necessáriamente devemos utilizar em nosso projeto cristal oscilador igual ou maior que 6 MHz, caso contrário
nosso programa não funcionará corretamente.

Observação 02: Quando estivermos manipulando os comandos de tratamento de teclado PS/2 no mikro,
devemos necessáriamente desligar todas as interrupções do PIC.

Exemplo de programa 01:

Para entendermos melhor o funcionamento das funções de tratamento de teclados PS/2 no mikroC, vamos elaborar um
exemplo de programa simples, cuja função seja rebecer o valor lido de uma tecla qualquer do teclado e atribuir este valor ao
PORTD.

Esquema elétrico:

Programa:

/***************************************************************************
Centro de Tecnologia Microgenios
Programa: Ps/2_teclado_lcd
Compilador: mikroC 6.02
Kit: Kit PICGenios

Obejtivo: Este programa tem por objetivo ler um teclado de computador PS/2 e
enviar o valor de cada tecla para o portA do PIC.

Informações Importante: Para manipular teclados PS/2 com o mikroC é necessário


que cristal seja de 6MHz ou maior.

No kit PICgensio é necessário configurar o LCD no modo 4 bits para trabalhar


em conjunto com as bibliotecas PS2 do mikroC.
Toda vez que formos utilizar a biblioteca PS2, devemos desligar as interrupções
do PIC.

*/

unsigned short valor_da_tecla = 0, tecla_especial = 0, bit_status = 0;


//variáveis necessário para usar as funções PS2
void main() {

INTCON = 0; // desliga todas as interrupções


ADCON1 = 6; // configura pinos como i/o
TRISA = 0;
PORTA = 0;
Ps2_config(&PORTD, 2, 3); // inicializa PS2 (PINO RD2 CLOCK, RD3 DATA

while (1){
if (Ps2_Key_Read(&valor_da_tecla, &tecla_especial, &bit_status)) {
if ((bit_status ==1) && valor_da_tecla) {
PORTA = valor_da_tecla; //escreve valor da tecla pressionada no lcd
}
}
}
}

clique aqui para fazer downloads do programa acima;

Vamos analizar o programa acima com detalhes:

Acompanhe os trechos do programa:

Os comandos abaixo tem por função configurar os pinos RD2 como clock e RD3 como DATA, para tratamento do teclado
PS2.

Ps2_config(&PORTD, 2, 3); // inicializa PS2 (PINO RD2 CLOCK, RD3 DATA

A condição de tomada de decião IF abaixo é necessário para sabermos se alguma tecla do PC foi
pressionada. Neste caso a função será verdadeira quando o retorno da função Ps2_Key_Read for
diferente de zero.

if (Ps2_Key_Read(&valor_da_tecla, &tecla_especial, &bit_status))

O segundo teste de tomada de decisão IF realiza uma operação lógica AND entre os parâmetros bit_status e valor_da_tecla da
função, Ps2_Key_Read.
if ((bit_status ==1) && valor_da_tecla)

caso a condição If anterior seja verdadeira, é atribuida ao PORTA o valor recebido do teclado PS/2.

PORTA = valor_da_tecla; //escreve valor da tecla pressionada no lcd

Exemplo de programa 02:

Vamos agora elaborar um exemplo de programa um pouco mais sofisticado. Neste novo exemplo de programa utilizaremos o
display LCD como terminal de recepção dos dados lidos no teclado PS2.
Repare que estamos utilizando neste projeto cristal de 8 MHz, mas poderiamos utilizar qualquer outro cristal que não seja
menor do que 6MHz, caso contrário não poderiamos usufruir das funções de PS/2 do mikroC.
Nosso programa:

/***************************************************************************
Centro de Tecnologia Microgenios
Programa: Ps/2_teclado_lcd
Compilador: mikroC 6.02
Kit: Kit PICGenios

Obejtivo: Este programa tem por objetivo ler um teclado de computador PS/2 e
enviar o valor de cada tecla para o display LCD.

Informações Importante: Para manipular teclados PS/2 com o mikroC é necessário


que cristal seja de 6MHz ou maior.

No kit PICgensio é necessário configurar o LCD no modo 4 bits para trabalhar


em conjunto com as bibliotecas PS2 do mikroC.
Toda vez que formos utilizar a biblioteca PS2, devemos desligar as interrupções
do PIC.

****************************************************************************/

unsigned short
keydata = 0, special = 0, down = 0; //variáveis necessário para usar a
//biblioteca PS2

void main() {

INTCON = 0; // desliga todas as interrupções

ADCON1 = 6; // configura pinos como i/o

TRISE = 0; // programa porte como saida


TRISD = 0; // programa portd como saida

Lcd_custom_Config(&PORTD,7,6,5,4,&PORTE,2,0,1); //configura e inicializa LCD no modo 4 bits


Lcd_custom_Cmd(Lcd_CLEAR); // apaga display
Ps2_config(&PORTD, 2, 3); // inicializa PS2 (PINO RD2 CLOCK, RD3 DATA
Delay_ms(100); // delay de 100milisegundos

do {
if (Ps2_Key_Read(&keydata, &special, &down)) {
if (down && (keydata == 16)) {// tecla Backspace pressionada
Lcd_custom_Cmd(LCD_MOVE_CURSOR_LEFT);
}
else if (down && (keydata == 13)) {// tecla Enter pressionada
Lcd_custom_Cmd(LCD_RETURN_HOME);

}
else if (down && !special && keydata) {
Lcd_Custom_Chr_CP(keydata); //escreve valor da tecla pressionada no lcd
}
}
Delay_ms(10); // debounce
} while (1);
}

clique aqui para fazer downloads do programa acima;

O programa acima lê o teclado Ps/2 e escreve no display LCD os valores dessas teclas, o mais importante é que o valor lido é
em ASCII, e neste caso podemos enviar diretamente para o LCD, até mesmo pela serial do PC.

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem prévio aviso. Maiores
informações: suporte@microgenios.com.br
Acesso a memória EEPROM do PIC
1.0 Memória EEPROM
1.1 Informações Importantes

Memória EEPROM

Definição:

EEPROM (Electric Eraseble and Programable Read Only Memory) Memória somente de leitura de apagamento
e programação elétrica, essa memória revolucionou esse tipo de sistema, pois possibilitava a programação e
reprogramação de maneira elétrica sem a necessidade da retirada do componente do circuito, porém demanda de
grande complexidade de projeto o que reduz sua capacidade e eleva seu preço é utilizada tanto como memória de
programa quanto como memória de dados substituindo assim EPROMs e RAMs.

Nosso microcontrolador PIC18F442/452 possui internamente 256 byte de memória do tipo


EEPROM. Essa memória é muito utilizada quando desejamos armazenar dados não voláteis na
memório do PIC, tais como: tabelas, senhas, etc., valores que podem ser alterados ao longo do
tempo.

Com o mikroC, podemos escrever e e ler dados nesta memória com grandes facilidades, através
dos comandos:

EEprom_Write(endereço, dado);
EEprom_Read(endereço);

Onde:
endereço = endereço da memória EEPROM do PIC onde será salvo os dados.
dado = informação que será salvo na memória do PIC.

Exemplo:

EEprom_Write(152, variavel_A); //grava no endereço 152 da eeprom o valor da


variavel_A
variavel_A = EEprom_Read(152); //lê o endereço 152 da eeprom e salva o valor
lido na variável_A

Exemplo de programa:

O programa seguinte tem por objetivo escrever uma mensagem na EEPROM, no endereço 0
(zero), e posteriormente ler esse mesmo endereço de memória na EEPROM e enviar os dados
lidos para o display LCD.

/**************************************************************************
Centro de tecnologia Microgenios
Programa exemplo: LCD_modo_4bits_01.c
Este programa tem por objetivo escrever a mensagem "Teste EEPROM" na EEPROM
do
PIC (endereço 0) e posteriormente ler essa mensagem salvo na memória e
enviar para a primeira linha do LCD.
Cristal : 8MHz
***************************************************************************/
void main() {

ADCON1 = 6; // programa pinos de A/D como I/O de uso


geral

TRISE = 0; // programa porte como saida


TRISD = 0; // programa portD como saida
Lcd_custom_Config(&PORTD,7,6,5,4,&PORTE,2,0,1); //configura e inicializa
LCD no modo 4 bits
Lcd_custom_Cmd(Lcd_CLEAR); // apaga display
Lcd_custom_Cmd(Lcd_CURSOR_OFF); // desliga cursor

Eeprom_Write(0 , "Teste EEPROM"); //escreve string "Teste EEPROM" na


EEPROM do PIC
delay_ms(20); //delay de 20 ms necessário para tratamento da função de
manipulação de eeprom do mikroC.
Lcd_custom_Out(1, 1, Eeprom_Read(0)); //lê a EEPROM do PIC e escreve no
LCD

while(1);
}

EEprom_Write(45, X ); //grava no endereço 45 da EEPROM o valor da variavel x.


Y = EEprom_Read(45); //lê o endereço 45 da EEPROM e salva o valor lido na
variável Y
EEprom_Write(152, variavel_A); //grava no endereço 152 da eeprom o valor da
variavel_A
delay_ms(20); //delay de 20 milisegundos
variavel_A = EEprom_Read(152); //lê o endereço 152 da eeprom e salva o valor
lido na variável_A
delay_ms(20); //delay de 20 milisegundos
Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C
Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse
material sem prévio aviso. Maiores informações: suporte@microgenios.com.br
Introdução
Vamos colocar em prática alguns tópicos que aprendemos até agora no nosso curso. Desenvolveremos
alguns projetos eletrônicos com base nos exemplos de programas estudados anteriormente.

Nossos projetos serão realizados gradualmente, passo a passo.

PROJETO 01: Equipamento PROD-CNC - Parte 01

O equipamento que iremos desenvolver é chamado de Prod-CNC. Este equipamento deverá automatizar

uma linha de produção de embalagens em uma indústria .

Inicialmente, nosso equipamento deverá contar o número de caixas que passam em uma esteira de
linha de produção. Os produtos que iremos contar estão embalados em caixas de papelão, conforme
podemos ver na animação abaixo:

Os Pulsos de contagem são gerados por um sensor fotoelétrico posicionado na lateral da esteira de
produção, cuja função é que detecta a passagem das caixas de papelão. Cada pulso enviado pelo
sensor, representa que passou 1 caixa na esteira, e nosso equipamento deverá "ler" esse sinal e
contar. Inicialmente nosso projeto somente contará o número de caixas que passam na esteira e
mostrará no display LCD do equipamento;

O "Rosto" do nosso projeto:

Nosso equipamento, de início, terá um display LCD 16 x 2 e um led.


Nosso equipamento - Prod- CNC

O esquema eletrônico do Prod-CNC:

Figura -

Repare que os pulsos do sensor fotoelétrico chega até o contador externo RA4/T0CK1 do
microcontrolador PIC18F442. Sabemos que este pino tem a função de operar como I/O de uso geral,
conversor A/D ou como contador de pulsos externos. Neste caso, iremos programar este pino como
contador de pulsos externo, pois queremos contar os pulsos vindos do sensor da esteira de produção.

Etapas para elaboração do programa do microcontrolador PIC


Devemos seguir as seguintes etapas para elaborar este programa;

1. Definir as variáveis se for necessário;


2. Configurar os tipo de LCD utilizado e sua configuração de pinagem;
3. Programar o TIMER0 do microcontrolador para trabalhar como contador, habilitando assim a
contagem do pino T0CK1;
4. Inicilizar LCD com as mensagens solicitadas no anunciado do projeto;
5. Realizar rotina de loop para leitura dos pulsos do sensor e escrita no LCD;
6. Fim do programa;

O programa:

/**************************************************************************
Centro de tecnologia Microgenios
Programa exemplo: LCD_modo_4bits_01.c
Este programa tem por objetivo contar os pulsos recebidos atráves do contador T1CK1 e enviar valor da
contagem para o lcd.
**************************************************************************
*/
char *texto[16];
unsigned short contador;

void main()

ADCON1 = 6; // programa pinos de A/D como I/O de uso geral


contador = 0;
TRISA = 0; // programa portA como saída
TRISD = 0; // programa portD como saída
Lcd_custom_Config(&PORTD,7,6,5,4,&PORTE,2,0,1); //configura e inicializa LCD no modo 4 bits
Lcd_custom_Cmd(Lcd_CLEAR); // apaga display
Lcd_Custom_Cmd(LCD_CURSOR_OFF); //desliga cursor do lcd
Delay_ms(100); // delay de 100 milisegundos
Lcd_custom_Out(1, 1, "PROD - CNC"); // escreve texto "Programa LCD" na primeira linha, quarta
coluna do LCD

T1CON = 0B00000011; //liga TIMER1, prescaler 1:8, modo 8bits, contador externo
TMR1L = 0; //carrega valor de contagem baixa do TIMER1

do

{
contador = TMR1L + contador; //lê o valor do timer1 e soma a variável contador
TMR1L = 0; //zera contagem para receber novo pulso
wordToStr(contador, texto); //converte valor da conversão do ad0 para string
Lcd_custom_Out(2, 10,texto); // escreve variável texto na 2º linha, 10º coluna do LCD
Lcd_custom_Out(2, 1,"Numero: ");
delay_ms(50);

} while(1); // loop
}

Nosso programa detalhado:

Nas linhas de comandos abaixo definimos as variáveis do programa.

char *texto[16];
unsigned short contador;

Os comandos abaixo inicializam e configura o modo de operação do LCD 16x2 do equipamento PROD-
CNC.

void main()
{

ADCON1 = 6; // programa pinos de A/D como I/O de uso geral


TRISA = 0; // programa portA como saída

Lcd_custom_Config(&PORTD,7,6,5,4,&PORTE,2,0,1); //configura e inicializa LCD no modo 4 bits


Lcd_custom_Cmd(Lcd_CLEAR); // apaga display
Lcd_Custom_Cmd(LCD_CURSOR_OFF); //desliga cursor do lcd
Delay_ms(100); // delay de 100 milisegundos
Lcd_custom_Out(1, 1, "PROD - CNC"); // escreve texto "Programa LCD" na primeira linha, quarta
coluna do

As linhas de programa seguinte configuram o TIMER1 do PIC como contador externo.

T1CON = 0B00000011; //liga TIMER1, prescaler 1:8, modo de leitura/escrita em 8bits, contador
externo
TMR1L = 0; //carrega valor de contagem baixa do TIMER1

Nosso programa fica em loop constante graças aos comandos do-while. Neste laço de repetição, o valor
da contagem dos pulsos recebidos externamente no pino RA4 armazenados em TMR1L, são convertidos
em STRING e enviados para o LCD.

Desta maneira, cada pulso recebido no pino de contagem externa RA4/T1CK1 incrementa em 1 unidade
a variável "contador", sendo esta variável convertida em string e enviada para o lcd.

do

{
contador = TMR1L + contador; //lê o valor do timer1 e soma a variável contador
TMR1L = 0; //zera contagem para receber novo pulso
wordToStr(contador, texto); //converte valor da conversão do ad0 para string
Lcd_custom_Out(2, 10,texto); // escreve variável texto "Programa LCD" na 2º linha, 10º coluna
do LCD
Lcd_custom_Out(2, 1,"Contagem: ");
delay_ms(50);

} while(1); // loop

Voltar para o topo da página

Centro de Tecnologia Microgenios - Curso de Microcontroladores PIC - Programação em C


Microgenios © 1998 - 2007. Todos os direitos reservados. É proibido cópia parcial ou integral desse material sem
prévio aviso. Maiores informações: suporte@microgenios.com.br

Você também pode gostar