Buscar

Apostila do Curso Básico de Microcontroladores

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 64 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 64 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 64 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

Microcontroladores
Introdução aos Microcontroladores
Os equipamentos controlados por microprocessadores são normalmente constituídos por vários circuitos integrados ou chips, cada um com a sua função: o microprocessador (CPU), uma memória EPROM com o programa, uma memória RAM para armazenamento de dados e interfaces de entrada/saída (I/O input/output) para ligação ao exterior. 
Pelo contrário, os sistemas baseados em Microcontroladores possuem um único chip de pequenas dimensões que integra uma CPU, Memória RAM, Memória ROM e circuitos de interface. A grande variedade destes componentes possibilita que o mesmo fabricante ofereça modelos com mais ou menos RAM, com outros dispositivos como Portas de Comunicação, Conversores Analógico/Digitais, etc. 
As circunstâncias que nos deparamos hoje no campo dos Microcontroladores têm os seus primórdios no desenvolvimento da tecnologia dos circuitos integrados. Este desenvolvimento tornou-se possível armazenar centenas de milhares de transistores num único chip. Isso constituiu um pré-requisito para a produção de microprocessadores. Os primeiros computadores foram construídos adicionando periféricos externos tais como Memórias, Barramentos de Entrada e Saída, Temporizadores e outros. 
Um crescente aumento do nível de integração permitiu o aparecimento de circuitos integrados contendo simultaneamente o processador e seus periféricos. E assim, surgiu o primeiro chip contendo um microcomputador que mais tarde seria designado por Microcontrolador. Com este, economizamos espaço na placa e tempo de desenvolvimento, entre outros.
Os Microcontroladores podem ser utilizados para controlar uma variedade de equipamentos, desde máquinas de lavar, automóveis, máquinas industriais, etc. Por outro lado, o controle de dispositivos como motores, relés e lâmpadas exigem adaptação de sinal. Para medirmos a tensão proveniente de nossas tomadas residenciais, seja ela de 127V ou 220V, por exemplo, é necessário que o sinal de tensão passe por um circuito auxiliar que irá condicionar este sinal de tensão nos níveis suportados pelo Microcontrolador. Uma boa parte deles suporta um nível de tensão que esteja entre 0V e 5V.
Fabricantes de Microcontroladores
Existem diversos fabricantes de Microcontroladores no mercado mundial. Cada um possui suas especialidades como velocidade de operação, custo/benefício, periféricos, etc. Dentre os principais fabricantes, podemos citar:
Atmel;
Dallas Semiconductor;
Intel;
Philips;
Microchip;
Hitachi;
Motorola;
Zilog.
Os Microcontroladores PIC, objeto de nossos estudos, são fabricados pela empresa Microchip.
Classificação de acordo com Set de instruções dos Microcontroladores PIC
Duas arquiteturas no “mundo” dos Microcontroladores são bem comuns: a arquitetura de Harvard e a de von-Neumann’s. A arquitetura de Harvard adveio da necessidade de pôr o Microcontrolador a trabalhar mais rápido. A Memória de Dados está separada da Memória de Programa. Assim, é possível uma maior fluência de dados através da unidade central de processamento e uma maior velocidade de funcionamento. Em geral, a arquitetura Harvard possui um conjunto com menos instruções que a de von-Neumann's.
Figura 1: Arquitetura Harvard versus von-Neumann's.
Os Microcontroladores com a Arquitetura Harvard são também designados por Microcontroladores RISC. RISC provém de “Computador com um Conjunto Reduzido de Instruções” (Reduced Instruction Set Computer). Os Microcontroladores com uma Arquitetura von-Neumann’s são designados por Microcontroladores CISC. O nome CISC deriva de “Computador com um Conjunto Complexo de Instruções” (Complex Instruction Set Computer).
Diante das definições, temos as seguintes características:
CISC - Arquitetura von-Neumann’s:
Possui um grande conjunto de instruções e geralmente possuem funções específicas e de alto nível, como multiplicação e divisão;
As instruções são processadas mais lentamente. Porém o “hardware” do Microcontrolador é mais simplificado.
RISC – Arquitetura Harvard:
Possui um conjunto pequeno de instruções básicas e fundamentais;
Mais apropriado para ser programado através de compiladores. O código pode ser muito otimizado de acordo com o compilador;
As instruções são processadas rapidamente, mas necessitam que o “hardware” do Microcontrolador seja mais complexo.
Os Microcontroladores PIC trabalham com a Arquitetura Harvard (RISC).
Ciclo de Máquina
É também chamado de Ciclo de Instrução. Como indica o próprio nome, o Ciclo de Máquina é o tempo gasto para efetuar uma instrução. Nos Microcontroladores PIC o Ciclo de Máquina é de 4 Ciclos de Clock. 
Para que o programa escrito no Microcontrolador seja executado é necessário que seja conectado ao mesmo um Oscilador. O Oscilador é o componente que gerará pulsos de Clock em um contador especial, denominado Contador de Programa. O Contador de Programa aponta para cada posição da Memória de Programa, onde o Programa do Usuário é gravado, a fim de executar a instrução gravada nesta posição.
Portanto, aproveitando-se dos conceitos de Contador de Programa e de Oscilador, se utilizarmos um oscilador com freqüência de 20MHz (20 Milhões de Ciclos por Segundo) teremos efetivamente 5 Milhões de Instruções por Segundo. Os fabricantes fornecem este dado em termos de Velocidade Máxima de Operação, dada em MIPS (Milhões de Instruções Por Segundo). 
Apenas exemplificando, o Microcontrolador que será utilizado ao longo do nosso curso é o PIC16F877A e, de acordo com o seu fabricante, MICROCHIP, ele possui Velocidade Máxima de Operação de 5MIPS.
Arquitetura Básica dos Microcontroladores PIC
Os principais componentes de um Microcontrolador são: Memórias, CPU, Unidades de Entrada e Saída de Dados e Barramento de Dados.
Figura 2: Esquema Simplificado de um Microcontrolador.
Dentre o bloco Memórias, destaca-se: Memória de Programa, onde está gravado todo o programa do usuário; Memória RAM, utilizada para a manipulação de valores, armazenamento de resultados de operações matemáticas e booleanas, etc. (todas as variáveis que são criadas pelo usuário são armazenadas na Memória RAM); dentre o bloco Memórias temos os Registradores de Funções Especiais. Estes auxiliam na manipulação dos módulos internos do Microcontrolador. Cada módulo de Comunicação, Timer, Conversor A/D, entre outros, possui um ou mais registros de configurações. 
A Unidade Central de Processamento (CPU) é o ”cérebro” de um Microcontrolador. Essa parte é responsável por extrair a instrução, descodificá-la e, finalmente, executá-la. A unidade central de processamento interliga todas as partes do Microcontrolador de modo que este se comporte como um todo. Uma de sua funções mais importante é descodificar as instruções do programa. 
O código de uma instrução extraído da Memória de Programa, tem que ser descodificado pela unidade central de processamento (CPU). Cada uma das instruções do Microcontrolador corresponde a um conjunto de ações para realizar uma determinada tarefa. Estas ações podem envolver transferências de dados de um local de memória para outro, de um local de memória para as portas, ou até mesmo cálculos. Concluímos, portanto, que a CPU tem que estar ligado a todas as partes do Microcontrolador.
As Unidades de Entrada e Saída conectam os Módulos internos do Microcontrolador ao “mundo externo”. Se for necessário ler o estado de um sensor ou acionar um motor precisamos atuar neste módulo.
O Barramento de Dados é o responsável por conectar cada um dos módulos que compõem o Microcontrolador. Todo o tráfego de dados das Unidades de Entrada e Saída, das Memórias e de outros periféricos ocorre através deste barramento.
Linguagem C para Microcontroladores PIC: Compilador CCS
Características da linguagem C
Entre as principais características do C, podemos citar:
O C é uma linguagem de alto nível com uma sintaxe bastante estruturada e flexível tornando sua programação bastante simplificada;
O C compartilha recursos tanto de alto quanto de baixo nível, pois permite acesso e programação direta do microprocessador.Com isto, rotinas cuja dependência do tempo é crítica, podem ser facilmente implementadas usando instruções em “Assembly”. Por esta razão o C é a linguagem preferida de boa parte dos programadores;
O C é uma linguagem estruturalmente simples e de grande portabilidade. O compilador C gera códigos mais enxutos e velozes do que muitas outras linguagens;
Embora estruturalmente simples (poucas funções intrínsecas) o C não perde funcionalidade, pois permite a inclusão de uma farta quantidade de rotinas do usuário. Os fabricantes de compiladores fornecem uma ampla variedade de rotinas pré-compiladas em bibliotecas.
Estrutura básica de um programa em C
Um programa C consiste em uma ou várias funções. Dentre elas existe uma das quais precisa ser denominada “main”. O programa sempre começará executando a função “main”. Definições de funções adicionais podem preceder ou suceder a função “main”.
Podemos citar outras regras para a linguagem C:
Toda função deve ser iniciada por uma chave de abertura { e encerrada por uma chave de fechamento };
Toda função é procedida de parênteses ( );
As linhas de código são sempre encerradas por um ponto e vírgula;
A formatação dos programas é completamente livre, mas temos por conveniência manter a legibilidade;
Os comandos são executados na ordem em que foram escritos;
Os comentários devem ser delimitados por /* no início e */ no final.
Podem ser usados também os caracteres // para comentários de uma linha.
Variáveis
Todas as variáveis e constantes usadas no programa devem ser definidas com um nome e um tipo. Os dados básicos podem ser de 8, 16 e 32 bits de comprimento, e devido as características peculiares dos microcontroladores, variáveis de 1 bit também podem ser definidas.
Para declararmos uma variável devemos seguir algumas regras. São elas:
Todas as variáveis utilizadas no programa devem ser declaradas previamente;
A declaração indica, no mínimo, o nome e o tipo de cada uma delas;
Na ocorrência de mais de uma variável do mesmo tipo podemos declará-las de uma única vez, separando seus nomes por vírgula;
Todo nome de variável é iniciado por uma letra (a – z ou A – Z) ou o caracter traço baixo ( _ ). O restante pode conter letras, traço baixo ou números;
A Linguagem C é “case sensitive”. Isso significa que as letras maiúsculas são diferenciadas das letras minúsculas. Ou seja: uma variável declarada como TESTE e outra como TesTe possuem endereços de memória diferentes pois possuem o nome diferente.
Porém, o CCS não é “case sensitive”. Ou seja. Se tentarmos declarar uma variável com o nome TESTE e outra como TesTe elas serão interpretadas pelo compilador como sendo de mesmo nome.
Tipos de Variáveis
Variáveis de 1 bit: podem assumir apenas dois valores distrintos: “0” ou “1”. No CCS este tipo de variável é nomeado por “int1” e “short”.
Variáveis de 8 bits sem sinal: podem armazenar valores inteiros que vão de “0” a “256”, para o caso dos tipos “unsigned int”, “unsigned int8” e “byte”. 
Variáveis de 8 bits com sinal: armazenam valores inteiros que vão de “-127” até “128”. São os tipos: “signed int” e “signed int8”.
Variável de 8 bits padrão ASCII (manipulação de caracteres): “char”.
Variáveis de 16 bits: “long int”, “int16”.
Variável de 32 bits: “int32”.
Variável de 32 bits para valores fracionários: “float”.
Declarando Variáveis
A forma de declaração de variáveis é a seguinte: 
Tipo_da_variável Nome_da_variável;
Inicializando Variáveis
Pode-se inicializar uma variável no momento de sua declaração, ou em qualquer instante do programa.
Variáveis locais e variáveis globais
Existem duas maneiras principais para a alocação de variáveis em memória:
Variáveis Locais: as variáveis locais tem “alcance” apenas dentro das rotinas ou funções onde foram criadas. Isto significa que apenas a rotina ou função onde a mesma foi criada poderá utilizá-la.
Variáveis Globais: as variáveis globais possuem “alcance” para todas as rotinas e funções existentes no programa. Isto significa que qualquer rotina ou função poderá utilizá-la.
Expressões Numéricas e Caracteres
Vamos ver neste item os tipos de expressões permitidas para a manipulação de números e caracteres. As expressões são:
Números Decimais: Não podem começar por “0” (zero)
Exemplos: 123; 2; 14534; 3.14; ...
Números Octais: Devem começar por “0”’ (zero)
Exemplos: 045; 09;...
Números Binários: Devem iniciar por “0b”
Exemplo: 0b10101010
Números Hexadecimais: Devem iniciar por “0x”
Exemplo: 0x32; 0xA9; ...
Caracter: Devem estar entre Apóstrofos ‘ ‘
Exemplo: ‘z’; ‘A’; ‘a’; ....
Tipos de Operadores
Operador de Atribuição
A operação de atribuição é a operação mais simples do C. Consiste de atribuir valor de uma expressão a uma variável. A sintaxe da operação de atribuição é a seguinte:
	identificador = expressão;
onde “identificador” é o nome de uma variável e “expressão” é uma expressão válida ou outro identificador. No exemplo a seguir são mostradas algumas atribuições válidas:
Operadores Aritméticos
Existem cinco operadores aritméticos em C. Cada operador aritméticos está relacionado ao uma operação aritmética elementar: adição, subtração, multiplicação e divisão. Existe ainda um operador (%) chamado operador de módulo cujo significado é o resto da divisão inteira. Os símbolos dos operadores aritméticos são:
Operador de soma: + (mais)
Operador de subtração: - (menos)
Operador de multiplicação: * (asterisco)
Operador de divisão: / (barra)
Operador de resto de divisão: % (porcentagem)
A sintaxe de uma expressão aritmética é:
	operando operador operando;
onde “operador” é um dos caracteres mostrados acima e “operando” é uma constante ou uma variável.
Precedência de operadores
Quando mais de um operador se encontram em uma expressão aritmética as operações são efetuadas uma de cada vez respeitando algumas regras de precedência. Estas regras de precedência são as mesmas da matemática elementar.
Os operadores de multiplicação (*), divisão (/) e módulo (%) tem precedência sobre os operadores de adição (+) e subtração (-). Entre operadores de mesma precedência as operações são efetuadas da esquerda para a direita.
A ordem de precedência dos operadores pode ser quebrada usando-se parênteses: ( ). Os parênteses são, na verdade, operadores de mais alta precedência e são executados primeiro. Parênteses internos são executados primeiro que parênteses externos.
Operadores relacionais
Operadores relacionais verificam a relação de magnitude e igualdade entre dois valores. São seis os operadores relacionais em C:
Operador “maior que”: > (maior)
Operador “menor que”: < (menor) 
Operador “maior ou igual”: >= (maior/igual)
Operador “menor ou igual”: <= (menor/igual)
Operador “igual”: == (igual/igual)
Operador “diferente”: != (exclamação/igual)
A sintaxe das expressões lógicas é a seguinte:
expressão_1 operador expressão_2
onde “expressão_1” e “expressão_2” são duas expressões numéricas quaisquer, e “operador” é um dos operadores relacionais.
O resultado de uma expressão lógica é sempre um valor numérico que pode variar entre dois valores: uma expressão avaliada como verdadeira recebe o valor 1 e uma expressão lógica avaliada como falsa recebe o valor 0.
Operadores lógicos
São três os operadores lógicos de C: &&, || e !. Estes operadores possuem o mesmo significado, respectivamente, dos operadores lógicos Booleanos AND, OR e NOT.
A sintaxe de uso dos operadores lógicos é:
expressão_1 && expressão_2
expressão_1 || expressão _2
! expressão
onde “expressão _1” , “expressão _2” e “expressão” são expressões quaisquer.
Observe que os operadores lógicos atuam sobre expressões de quaisquer valores. Para estes operadores todo valor numérico diferente de 0 é considerado 1.
Operadores de Atribuição Aritmética
Muitas vezes é necessário alterar o valor de uma variável realizando alguma operação aritmética, como por exemplo: 
Embora seja perfeitamente possível escrever estas instruções, foi desenvolvido na linguagem C instruções otimizadas com o uso de operadores ditosoperadores de atribuição aritmética. Os símbolos usado são (+=, -=, *=, /= , %=). Deste modo as instruções acima podem ser rescritas como: 
Operadores Incrementais
Em programação existem instruções muito comuns chamadas de incremento e decremento. Uma instrução de incremento adiciona uma unidade ao conteúdo de uma variável. Uma instrução de decremento subtrai uma unidade do conteúdo de uma variável. 
Existem, em C, operadores específicos para realizar as operações de incremento (++) e decremento (--). Eles são genericamente chamados de operadores incrementais.
A sintaxe dos operadores incrementais é a seguinte:
	Identificador ++; (equivalente à identificador = identificador + 1;)
Identificador --; (equivalente à identificador = identificador - 1;)
onde “Identificador” é o nome da variável da qual se quer incrementar ou decrementar um unidade. 
Estruturas Relacionais
Decisão de um bloco (if...)
A estrutura de decisão de um bloco permite que se execute (ou não) um bloco de instruções conforme o valor de uma condição seja verdadeiro ou falso. O fluxograma desta estrutura é mostrado abaixo.
Figura 3: Fluxograma da estrutura de decisão if...
A Sintaxe de Decisão com um bloco é apresentada a seguir:
if(condição)
{ 
 bloco;
}
onde:	“condição” é uma expressão lógica ou numérica. “bloco” é um conjunto de instruções.
Se a condição for verdadeira o bloco será executado. Caso contrário, o bloco não é executado.
Decisão de dois blocos (if...else)
Também é possível escrever uma estrutura que execute um entre dois blocos de instruções. O fluxograma seguinte correspondente a esta estrutura de decisão.
Figura 4: Fluxograma da estrutura de decisão if...else
A Sintaxe de Decisão de dois blocos é:
if(condição)
{
 bloco_1;
}
else
{
 bloco_2;
}
onde:	“condição” é uma expressão lógica ou numérica. “bloco_1” e “bloco_2” são conjuntos de instruções.
Se a “condição” for verdadeira apenas o “bloco_1” é executado. Caso contrário, somente o “bloco_2” será executado.
O exemplo mostrado acima está incompleto. Não basta somente o trecho de código mencionado acima para que o estudante possa verificar os resultados. O exemplo é apenas ilustrativo para que o estudante tenha uma base de como utilizar a expressão de Decisão.
No código acima verificamos a utilização de uma função até aqui desconhecida. Ela serve para imprimir textos. Esta função está detalhada em um capítulo relacionado à Funções do CCS. 
Decisão de múltiplos blocos (if...else if...else if...)
Também é possível escrever uma estrutura que execute um entre múltiplos blocos de instruções. A figura seguinte mostra o fluxograma correspondente a esta estrutura de decisão.
Figura 5: Fluxograma da estrutura de decisão if...else if.
A Sintaxe da Decisão de múltiplos blocos:
if(condição_1)
{
 Bloco_1;
}
else if(condição_2)
{
 Bloco_2;
}
...
Else
{
 Bloco_N;
}
onde:	“condição_1” e “condição_2” são expressões lógicas ou numéricas. “Bloco_1”, “Bloco_2” e “Bloco_N” são conjuntos de instruções.
Se a “condição_1” for verdadeira o “Bloco_1” é executado. Caso contrario, a “condição_2” é avaliada. Se a “condição_2” for verdadeira o “Bloco_2” é executado. Caso contrário, as condições que podem vir logo abaixo serão avaliadas. E assim sucessivamente. Se nenhuma condição é verdadeira o “Bloco_N” é executado. Neste tipo de estrutura, é importante observar que somente um dos blocos será executado.
No exemplo mostrado acima, a função “captura_tecla()” é apenas ilustrativa. Podemos entender que esta função irá esperar uma tecla a ser digitada e a tecla que for digitada será armazenada na variável “tecla”. De acordo com o valor da tecla digitada será impresso um determinado texto. Na próxima seção veremos que o mesmo exemplo poderá ser feito a partir de outra estrutura relacional: a “switch...case”.
Estrutura switch...case
A estrutura switch...case é uma estrutura de decisão que permite a execução de um conjunto de instruções a partir de pontos diferentes conforme o resultado de uma expressão de controle. O resultado desta expressão é comparado ao valor de cada um dos rótulos, e as instruções são executadas a partir desde rótulo. A figura abaixo mostra o fluxograma lógico desta estrutura.
Figura 6: Fluxograma da estrutura switch...case.
Esta estrutura possui a seguinte sintaxe:
switch(expressão)
{
 case rótulo_1: conjunto_1;
 case rótulo_2: conjunto_2;
 ...
 case rótulo_n: conjunto_n;
 default: conjunto_d;
}
onde: “expressão” é uma expressão inteira. “rótulo_1”, ”rótulo_2” e “rótulo_n” e rótulo_d são constantes inteiras.	“conjunto_1”, “conjunto 2”, “conjunto n” e “conjunto d” são conjuntos de instruções.
O valor de “expressão” é avaliado e o fluxo lógico será desviado para o conjunto cujo rótulo é igual ao resultado da expressão e todas as instruções abaixo deste rótulo serão executadas. Caso o resultado da expressão seja diferente de todos os valores dos rótulos então o “conjunto_d” é executado. Os rótulos devem ser expressões constantes inteiras diferentes entre si. O rótulo default é opcional.
É importante observar que a estrutura “switch...case” permite maior organização do que quando é utilizado a estrutura “if...else if...else if...”. Quando a estrutura “switch...case” for aplicável, como é o caso do nosso exemplo acima, recomenda-se a utilização da mesma.
A instrução “break” será explicada em um tópico adiante.
Estruturas de Repetição
Estrutura do...while
Esta é uma estrutura básica de repetição condicional. Permite a execução de um bloco de instruções repetidamente. Sua sintaxe é a seguinte:
do
{
 bloco
}while(condição);
onde:	“condição” é uma expressão lógica ou numérica. “bloco” é um conjunto de instruções.
Esta estrutura faz com que o bloco de instruções seja executado pelo menos uma vez. Após a execução do bloco, a condição é avaliada. Se a condição é verdadeira o bloco será executado novamente. Caso contrário a repetição é terminada. O fluxograma desta estrutura é mostrado na figura:
Figura 7: Fluxograma da estrutura do...while.
No exemplo acima, a função “captura_numero()” é apenas ilustrativa para que o estudante possa visualizar o funcionamento da estrutura “do...while” e possa se familiarizar com um caso típico de aplicação para a estrutura de repetição em questão.
Estrutura while
A estrutura de repetição condicional while é semelhante a estrutura do...while. Sua sintaxe é a seguinte:
while(condição)
{
 bloco
}
onde:	“condição” é uma expressão lógica ou numérica. “bloco” é um conjunto de instruções.
Esta estrutura faz com que a condição seja avaliada em primeiro lugar. Se a condição é verdadeira o bloco é executado uma vez e a condição é avaliada novamente. Caso a condição seja falsa a repetição é terminada sem a execução do bloco. Observe que nesta estrutura, ao contrário da estrutura do...while, pode ocorrer do bloco de instruções não ser executado nenhuma vez. Para isto basta que a condição seja inicialmente falsa. 
O fluxograma desta estrutura é mostrada na figura seguinte:
Figura 8: Fluxograma da estrutura while.
Estrutura for
A estrutura for é muito semelhante as estruturas de repetição vistas anteriormente, entretanto costuma ser utilizada quando se quer um número determinado de ciclos. A contagem dos ciclos é feita por uma variável chamada de contador. A estrutura for é, as vezes, chamada de estrutura de repetição com contador. Sua sintaxe é a seguinte:
for(inicialização; condição; incremento)
{
 bloco
}
onde:	“inicialização” é uma expressão de inicialização do contador. “condição” é uma expressão lógica de controle de repetição. “incremento” é uma expressão de incremento do contador. “bloco” é um conjunto de instruções a ser executado.
Esta estrutura executa um número determinado de repetições usando um contador de iterações. O contador é inicializado na expressão de “inicialização” antes da primeira iteração. Então o bloco é executado e depois de cada iteração, o contador é incrementado de acordo coma expressão de “incremento”. Então a expressão de “condição” é avaliada: se a condição for verdadeira, o “bloco” é executado novamente e o ciclo recomeça, se a condição é falsa termina-se o laço. Esta condição é, em geral, uma expressão lógica que determina o ultimo valor do contador.
A função “captura_numero()” é apenas ilustrativa afim de facilitar o exemplo mencionado.
A instrução break.
Esta instrução serve para terminar a execução das instruções de um laço de repetição (for, do...while, while) ou para terminar um conjunto switch...case. 
Quando em um laço de repetição, esta instrução força a interrupção do laço independentemente da condição de controle.
Manipulação de Funções
Funções de Biblioteca
Uma função é um sub-programa (também chamado de rotina). Esta função recebe informações, processa e retorna outra informação. Por exemplo: podemos ter uma função que receba um valor numérico, calcule seu logaritmo decimal e retorne o valor obtido. 
Existem dois tipos de funções: funções de biblioteca e funções de usuário. Funções de biblioteca são funções escritas pelos fabricantes do compilador e já estão pré-compiladas, isto é, já estão escritas em código de máquina. Funções de usuário são funções escritas pelo programador. Nesta seção trataremos somente das funções de biblioteca.
Incluindo uma Biblioteca de Funções
As bibliotecas das funções que serão usadas no programa devem ser incluídas no código fonte antes da função principal.
Para incluir uma biblioteca do compilador (que está na pasta include do compilador) usa-se a diretiva:
#include <nome da biblioteca>
Para incluir uma biblioteca que está em outra pasta usa-se:
#include “endereço\nome da biblioteca”
Estrutura das Funções de Usuário
É possível ao programador escrever suas próprias rotinas. São as chamadas de Funções de Usuário ou simplesmente Rotinas de Usuário. Deste modo pode-se segmentar um programa grande em vários programas menores. Esta segmentação é chamada de modularização e permite que cada segmento seja escrito, testado e revisado individualmente sem alterar o funcionamento do programa como um todo. Permite ainda que um programa seja escrito por vários programadores ao mesmo tempo, cada um escrevendo um segmento separado. Neste capítulo, veremos como escrever funções de usuário em C.
A estrutura de uma função de usuário é muito semelhante à estrutura dos programas que escrevemos até agora. Uma função de usuário constitui-se de um bloco de instruções que definem os procedimentos efetuados pela função, um nome pelo qual a chamamos e uma lista de argumentos passados a função. Chamamos este conjunto de elementos de definição da função. 
O código mostrado abaixo é uma função definida pelo usuário para calcular a média aritmética de dois números reais:
	
No exemplo acima definimos uma função do tipo “float” chamada “media” que recebe dois argumentos tipo “float”: “a” e “b”. A média destes dois valores é calculada e armazenada na variável “med” declarada dentro da função “media”. A função retorna, para o programa que a chamou, um valor também do tipo “float”: o valor da variável “med”. Este retorno de valor é feito pela instrução “return” que termina a execução da função e retorna o valor de “med” para o programa que a chamou. 
Depois de definirmos uma função, podemos usá-la dentro de um programa qualquer. Dizemos que estamos fazendo uma chamada à função.
No exemplo abaixo chamamos a função “media()” dentro de um programa principal. 
Vale lembrar que a função “captura_numero()” é apenas ilustrativa afim de tornar os exemplos mais claros e fáceis de entender. Porém, dado a definição de Funções de Usuário, após a introdução de funções específicas do CCS para manipulação dos Microcontroladores, o estudante estará apto a escrever uma função específica para ler um teclado matricial ou uma porta específica.
Definição de Funções
De modo formal, a sintaxe de uma função é a seguinte:
tipo_de_retorno nome_da_função(tipo_1 arg_1, tipo_2 arg_2, ...)
{
 Bloco_de_instruções_da_função
}
A primeira linha da função contém a declaração da função. Na declaração de uma função se define o nome da função, seu tipo de retorno e a lista de argumentos que recebe. Em seguida, dentro de chaves {}, definimos o bloco de instruções da função.
O tipo de retorno da função especifica qual o tipo de dado retornado pela função, podendo ser qualquer tipo de variável: “int”, “float”, etc. Se a função não retorna nenhum valor para o programa que a chamou devemos definir o retorno como “void”, ou seja, um retorno ausente.
 Vale notar que existe apenas um valor de retorno para funções em C. Não podemos fazer o retorno de dois ou mais valores. Porém isto não é uma limitação séria, pois o uso de ponteiros contorna o problema.
A lista de argumentos da função especifica quais são os valores que a função recebe. As variáveis da lista de argumentos são manipuladas normalmente no corpo da função.
A chamada de uma função termina com a instrução return que transfere o controle para o programa que realizou a chamada da função. Esta instrução tem duas finalidades: determina o fim lógico da rotina e o valor de retorno da função. O argumento de return será retornado como valor da função.
Localização das Funções
Existem basicamente duas posições possíveis para escrevermos o corpo de uma função: antes ou depois do programa principal. Podemos ainda escrever uma função no mesmo arquivo do programa principal ou em arquivos separados.
Função escrita antes do programa principal e no mesmo arquivo
Sintaxe:
tipo nomef(...) // definição da função
{ 
 [corpo de função]
}
void main() // programa principal
{ ...
 var = nomef(...) // chamada da função
 ...
}
Corpo da função depois do programa principal (no mesmo arquivo)
Quando escrevemos a definição de uma função depois do programa principal e no mesmo arquivo deste, devemos incluir um protótipo da função chamada. Um protótipo é uma instrução que define o nome da função, seu tipo de retorno e a quantidade e tipo dos argumentos da função. O protótipo de uma função indica ao compilador quais são as funções usadas no programa principal e os tipos. A sintaxe geral para isto é a seguinte:
Sintaxe:
tipo nomef(...); // protótipo da função
void main(){ // programa principal
 ...
 var = nomef(...) // chamada a função
 ...
}
tipo nomef(...){ // definição da função
 [corpo de função]
}
Corpo da função escrito em arquivo separado
Em C, como em muitas outras linguagens, é permitido que o usuário crie uma função em um arquivo e um programa que a chame em outro arquivo distinto. Esta facilidade permite a criação de bibliotecas de usuário: um conjunto de arquivos contendo funções escritas pelo usuário. Esta possibilidade é uma grande vantagem utilizada em larga escala por programadores profissionais. 
Quando escrevemos a definição de uma função em arquivo separado do programa principal devemos incluir este arquivo no conjunto de arquivos de compilação do programa principal. Esta inclusão é feita com a diretiva “#include”.
Sintaxe:
#include ”path” // inclusão da função
void main() // programa principal
{ 
 ...
 var = nomef(...) // chamada à função
 ...
}
Na diretiva “#include” indicamos, entre aspas duplas, o caminho de localização do arquivo onde está definida a função chamada.
Variáveis Compostas 
Vetores
Em muitas aplicações queremos trabalhar com conjuntos de dados que são semelhantes em tipo. Por exemplo: o conjunto das alturas dos alunos de uma turma, ou um conjunto de seus nomes. Nestes casos, seria conveniente poder colocar estas informações sob um mesmo conjunto, e poder referenciar cada dado individual deste conjunto por um índice numérico. Em programação, a este tipo de estrutura de dados é dado o nome de “Vetor”.
Declaração de Vetores
Em C, um vetor é um conjunto de variáveis de um mesmo tipo que possuem um nome identificador e um índice de referência.
A sintaxe para a declaração de umvetor é a seguinte:
tipo nome[tam];
onde: “tipo” é o tipo dos elementos do vetor: “int”, “float”, “char”, etc. “nome” é o identificador do vetor. “tam” é o tamanho do vetor, isto é, o número de elementos que o vetor pode armazenar.
As regras de nomenclatura de vetores são as mesmas usadas em variáveis.
Referência a Elementos de um Vetor
Cada elemento do vetor é referenciado pelo nome do vetor seguido de um índice inteiro. O primeiro elemento do vetor tem índice “0” e o último tem índice “tam - 1”. Ou seja, se declararmos um vetor de 20 posições, o acesso aos elementos são feitos da posição “0” até a posição “19”.
;
Inicialização de Vetores
Assim como podemos inicializar variáveis (por exemplo: int j = 3;), podemos inicializar os vetores. A sintaxe para a inicialização dos elementos de um vetor é:
tipo nome[tam] = {lista de valores};
onde: “lista de valores” é uma lista de números, separada por vírgulas, dos valores de cada elemento do vetor.	
Preenchendo um Vetor
Para se preencher um vetor é bastante simples. Basta utilizar da Estrutura de Repetição “for”. O exemplo logo abaixo ilustra o procedimento:
Passando Vetores para Funções
Vetores, assim como variáveis, podem ser usados como argumentos de funções. Vejamos como se declara uma função que recebe um vetor e como se passa um vetor para uma função. 
Na passagem de vetores para funções usamos a seguinte sintaxe:
nome_da_função(nome_do_vetor)	
onde: “nome_da_função” é o nome da função que está sendo chamada. “nome_do_vetor” é o nome do vetor que queremos passar. Indicamos apenas o nome do vetor, sem índices. 
Na declaração de funções que recebem vetores, temos:
tipo_função nome_função(tipo_vetor nome_vetor[])
{
...
}
onde: “tipo_função” é o tipo de retorno da função. “nome_função” é o nome da função. “tipo_vetor” é o tipo de elementos do vetor. “nome_vetor” é o nome do vetor. Observe que depois do nome do vetor temos um índice vazio [] para indicar que estamos recebendo um vetor. 
O exemplo acima ilustra como se passa vetores como parâmetros de funções. Comento mais uma vez que a função “captura_numero()” é apenas simbólica afim de facilitar os nossos exemplos.
Matrizes
Vetores podem ter mais de uma dimensão, isto é, mais de um índice de referência. Podemos ter vetores de duas, três, ou mais dimensões, nestes casos, estes são chamados de matrizes. Podemos entender um vetor de duas dimensões (por exemplo) associando-o aos dados de uma tabela.
Declaração e Inicialização de Matrizes
A declaração e inicialização de vetores de mais de uma dimensão é feita de modo semelhante aos vetores unidimensionais.
A sintaxe para declaração de vetores multidimensionais é:
tipo nome[tam_1][tam_2]...[tam_N]={{lista},{lista},...{lista}};
onde: “tipo” é o tipo dos elementos do vetor. “nome” é o nome do vetor. “[tam_1][tam_2]...[tam_N]” é o tamanho de cada dimensão do vetor. “{{lista},{lista},...{lista}}” são as listas de elementos.
Veja algumas declarações e inicializações de vetores de mais de uma dimensão. Observe que a inicialização de nota gera a tabela do exemplo do início desta seção.
Passagem de Matrizes para Funções
A sintaxe para passagem de matrizes para funções é semelhante a passagem de vetores unidimensionais: chamamos a função e passamos o nome do vetor, sem índices.
Na declaração de funções que recebem matrizes:
tipo_f função(tipo_m matriz[tam_1][tam_2]...[tam_n])
{
 ...
}
Observe que depois do nome da matriz temos os índices contendo os tamanhos de cada dimensão da matriz. 
Observe o exemplo abaixo:
Strings
String é uma das mais importantes formas de dados em C e é usada para armazenar e manipular textos como palavras, nomes e sentenças. String é um vetor do tipo char terminado pelo caracter NULL (\0), ou seja, string é uma série de caracteres armazenados em seqüência, onde cada um ocupa um byte de memória, toda string é terminada por um byte de valor zero (\0). Cada caracter é um elemento independente e pode ser acessado através de um índice.
	
Como strings são vetores, tudo relacionado a vetores poderá ser aplicado para as strings.
Apresentação do CCS
O Compilador CCS
O CCS é o principal programa a ser utilizado ao longo do nosso curso. Possui um ambiente de programação e ferramentas de desenvolvimento bastante intuitivo. É um Compilador em Linguagem C com ferramentas necessárias para a programação dos Microcontroladores PIC.
Figura 9: Tela Principal do CCS.
Diversos Módulos internos dos Microcontroladores PIC como, por exemplo, Comunicação Serial RS232, I2C, SPI, Conversores A/D, Timers e EEPROM, são de fácil utilização devido o CCS conter Bibliotecas Específicas para manipulação dos mesmos, facilitando a programação e o desenvolvimento de projetos.
Funções para utilização de diversos circuitos integrados como Relógio de Tempo Real, Conversores D/A, Memórias Flash, Potenciômetros Digitais, Módulos de Comunicação Ethernet, entre outros, também fazem parte do Pacote de Funções do CCS. Bibliotecas de Funções Matemáticas também estão inclusas.
Abrindo e Salvando Arquivos
Para abrir e salvar arquivos relacionados com o CCS faz-se da mesma forma que em qualquer programa no formato padrão do Windows. As opções para Abrir (“Open”) e Salvar (“Save”) arquivos são acessados através do ícone em formato de “pasta”, no canto superior esquerdo da Tela Principal do CCS.
Figura 10: Abrindo e Salvando arquivos no CCS.
Compilando os Programas
Para compilar o programa, basta acessar a Aba “Compile”. Um conjunto de opções será fornecido e, dentre elas, basta clicar no ícone denominado “Compile”.
Figura 11: Compilando os Programas.
Ferramentas Auxiliares de Desenvolvimento
Dentre as várias Ferramentas de Desenvolvimento do CCS podemos citar duas que vão ser de uso freqüente pelo estudante e/ou programador. Para acessá-las basta clicar sobre a aba “Tools”. Dentre as opções disponíveis as ferramentas que mais nos interessam são: “Numeric Converter” (Conversor Numérico) e o “Serial Port Monitor” (Monitor de Porta Serial). Essas duas ferramentas serão utilizadas posteriormente quando for necessário executar a conversão de valores Decimais para Hexadecimal ou Binário, para o caso do “Numeric Converter”. Quando for necessária a utilização de Comunicação Serial RS232 entre Microcontrolador e Computador utilizaremos o “Serial Port Monitor”.
Figura 12: Ferramentas Auxiliares de Desenvolvimento.
O “PIC Wizard”
CCS possui um Assistente de Configurações denominado “PIC Wizard”. Ele nos permite configurar vários dos recursos e módulos disponíveis no Microcontrolador, tais como: Freqüência de Operação, Interrupções, Comunicação, Timers, Conversor A/D, entre outros. Isso tudo de uma maneira bastante intuitiva e de simples operação. 
O “PIC Wizard” será tratado em um tópico posterior denominado “Criando um Novo Projeto – o Assistente de Configurações “PIC Wizard””.
Tópicos de Ajuda do CCS
Para acessar os Tópicos de Ajuda do CCS basta clicar sobre o ícone , localizado no canto superior esquerdo da Tela Principal e, logo em seguida, clicar em “Contents”.
Figura 13: Tópicos de Ajuda.
Criando Projetos no CCS
A Escolha do Microcontrolador (MCU)
Ao desenvolver aplicações com MCU é necessário conhecer o Processo no qual o mesmo irá atuar. Assim, também é de extrema importância que o programador saiba, antes da escolha do MCU, como o MCU irá interferir no Processo a ser controlado. 
Por exemplo: Controle de Temperatura de uma Estufa para Plantas. Faixa de Operação: 28 a 32 graus Celsius. A temperatura será mostrada em um Gráfico no Computador. A comunicação entre Computador e MCU será realizada via Comunicação Serial RS232.
Nesse processo, sabemos que vamos medir a temperatura de uma Estufa. Sendo assim, precisamos escolher um Sensor de Temperatura que se enquadre no processo, respeitando características de temperatura do ambiente a ser monitorado. Assim, escolhemos um sensor analógico e utilizamos um Módulo Conversor A/D ou utilizamos um Sensor de Temperatura Digital com comunicação SPI ou I2C,entre outros tipos. Como vamos medir temperaturas relativamente baixas (próximas a 30 graus Celsius) podemos utilizar um sensor de temperatura digital com comunicação “SPI”, o que é muito comum no mercado. Como não se consegue uma grande variação da grandeza física Temperatura em um pequeno intervalo de tempo, no nosso exemplo medir a temperatura de 5 em 5 segundos já nos é o suficiente. Sendo assim, necessitamos de uma base de tempo que nos ajude a calcular este tempo de 5 segundos. Isso é realizado a partir de um Módulo Temporizador. A comunicação entre Computador e MCU será realizada a partir de um Módulo RS232.
Com base no processo acima, devemos escolher um MCU que possua os Módulos: Temporizador (Timer), Comunicação Serial RS232 (USART – Transmissor e Receptor Universal Síncrono e Assíncrono) e SPI/I2C (SPI, I2C ou SSP – SPI + I2C).
Características como o tamanho da Memória de Programa, Memória RAM, Consumo de Energia (no caso de equipamentos alimentados à bateria) e velocidade do Processador também devem ser consideradas na escolha de um MCU. Projetos que envolvam algum tipo de comunicação com Protocolos “densos” ou programas que executem muitos Cálculos Matemáticos exigem maior velocidade de processamento e maior capacidade de memória. Na prática, nesse caso, teríamos: um Processador de 5MIPS (Milhões de Instruções Por Segundo) ou mais; Memória RAM igual ou superior a 1KB; Memória de Programa igual ou superior a 16KB. Esta configuração, dependendo do processo, seria uma boa aproximação para a escolha do MCU. No caso de aplicações mais simples um Processador trabalhando a 1MIPS, Memória de Programa de 8KB e Memória RAM de 368bytes é o suficiente.
Em resumo, para a escolha de um MCU, podemos citar:
Características do Processo a ser empregado;
Custo das Ferramentas de Desenvolvimento;
Custo do Microcontrolador;
Quantidade de I/O’s Digitais;
Quantidade de I/O’s Analógicas;
Periféricos de Comunicação;
Capacidade de Memória de Programa;
Capacidade de Memória RAM;
Velocidade de Operação;
Fontes de Interrupção (internas e externas);
Facilidade de compra;
Facilidades no Desenvolvimento.
Após alguns projetos realizados, o estudante e/ou programador irá adquirir experiência o bastante para dimensionar o melhor MCU para a sua aplicação. Graças à Flexibilidade da Linguagem C, boa parte do programa poderá ser escrito antes mesmo de se ter certeza absoluta de qual MCU será utilizado. Podemos escrever um programa voltado para o PIC16F84A e, caso o mesmo não nos atenda em função do tamanho da Memória requerida pelo nosso aplicativo, alteramos poucas linhas de código para mudarmos para o PIC16F877A, por exemplo.
Criando um Novo Projeto – o Assistente de Configurações “PIC Wizard”
Como o nosso curso está voltado para aplicações com o PIC16F877A e ele atende todas as especificações do nosso exemplo acima (Controle de Temperatura de uma Estufa para Plantas), vamos mostrar como criar um Projeto para o PIC16F877A.
Vamos dar ênfase somente aos itens principais do CCS e que serão utilizados com maior freqüência durante o curso. Tudo o que está relacionado neste tópico poderá ser aplicado a qualquer Microcontrolador PIC. Assim, fica de encargo do estudante e/ou programador desfrutar de todas as outras opções e ferramentas do programa em questão. 
No canto superior direito da Tela Inicial, na aba “Project”, clique no ícone “PIC Wizard”.
Figura 14: O Assistente de Configurações “PIC Wizard”.
Logo em seguida abrirá uma tela com o nome “Salvar Como”. Nesta tela você indicará o nome do Projeto e onde serão salvos os arquivos gerados pelo CCS.
Figura 15: Escolha do Nome e Diretório do Projeto.
Logo após indicar o diretório dos arquivos, abrirá uma nova janela como o nome “PIC Wizard”. É nela em que fazemos as principais configurações do MCU. O “PIC Wizard” possui ferramentas necessárias para efetuarmos boa parte das configurações do MCU.
Figura 16: Tela Inicial do “PIC Wizard”.
Na tela inicial do “PIC Wizard”, à esquerda da tela, temos um conjunto de Opções. Na Opção “General” faz-se a seleção do MCU a ser utilizado e configuramos parte do seu Hardware. 
Selecione o MCU a ser utilizado na aplicação através da Opção “Device”. No nosso caso selecionaremos o PIC16F877A.
Na opção “Oscilator Frequency” insira o valor do Cristal a ser utilizado. No caso do Kit de Desenvolvimento utilizado deixaremos o valor default de 20.000.000Hz (20 Mega Hertz).
O campo “Fuses” contém diversas propriedades do MCU como características do Oscilador, Detecção de Baixa Tensão, reset dos timers ao se energizar o MCU, reset por MCLR habilitado, etc. Essas propriedades mudam de acordo com o MCU selecionado em “Device”.
Para o nosso exemplo, vamos escolher a opção “High speed Osc (> 4mhz)” e marcar a opção “Power Up Timer”.
Figura 17: Seleção do MCU e Configurações.
Na aba “Code”, visualizamos o código em C gerado pelo “PIC Wizard”. Isto é bastante útil, pois muitas vezes já temos um projeto em andamento e necessitamos alterar algumas das configurações do MCU. Caso não saibamos qual é o código a ser inserido, basta recorrermos ao “PIC Wizard” e copiarmos o código em questão.
Figura 18: Código em C das configurações do MCU gerado.
À esquerda da tela do “PIC Wizard”, temos a Opção “Communications”. Nesta fazemos a configuração das Comunicações RS232 e I2C. 
Por default, a opção “Use RS232” está marcada (habilita o hardware de comunicação RS232). Caso não se queira utilizar a porta serial RS232 do PIC esta opção deverá ser desmarcada. O campo “Baud” indica a velocidade de transmissão e recepção de dados do hardware. Existem diversas opções como: 9600, 19200, 38400 e 115200, entre outros. No nosso caso utilizaremos o “Baud” de 115200. Através das opções “Transmit” e “Receive”, selecionamos os pinos de transmissão (TX) e recepção (RX) de dados, respectivamente. Esses são pinos específicos de comunicação do MCU. No caso do MCU em questão, os pinos de TX e RX são os pinos RC6 e RC7, respectivamente. Logo, em “Transmit” deixaremos como “C6” e em “Receive” o “C7”. No campo “Stream”, damos nome à porta de comunicação em questão. Quando utilizamos em um projeto mais de uma porta de comunicação RS232 (existem MCUs com 4 USART), esta opção é de extrema importância pois configura o canal de comunicação a ser utilizado para se transmitir dados.
Por default, a opção “Use I2C” está desabilitada (hardware I2C desabilitado). A princípio não utilizaremos o hardware I2C. Portanto, deixaremos esta opção desmarcada.
Ressalto novamente que para visualizar o código gerado basta clicar na aba “Code”.
Figura 19: Configurações do Hardware de Comunicação Serial RS232.
Para realizarmos a leitura do nosso sensor de temperatura através da SPI temos que configurá-la. Isso é feito a partir da opção “SPI and LCD”. Os parâmetros de configuração da SPI são mostrados na tela logo após clicarmos na opção “SPI and LCD”. Feito isso, habilite a opção “Hardware SPI#1” e selecione a opção “Master”. Isso habilitará o Hardware SPI do MCU e o mesmo trabalhará como Mestre, ou seja, será responsável por comandar o processo de comunicação SPI, que no nosso exemplo ocorre entre sensor de temperatura e MCU. As opções “Spi Mode” e “Clock” são fundamentais para que a comunicação entre MCU funcione corretamente. Como estamos apenas em um exemplo de como se criar um projeto não trataremos esta informação neste instante. Isso será abordado em um tópico adiante relacionado à Comunicação SPI.
Figura 20: Configurações do Hardware SPI.
Os Timers são configurados a partir da opção “Timers”. No campo “WDT” (Watch Dog) marque a opção “Not used”. No campo “Timer 1” (timer de 16 bits) habilite a opção “Internal” e selecione a resolução de “1.6us 104ms”. Nesta configuração, se contarmos até 50 obtemos um intervalo de aproximadamente 5 segundos (50 * 104ms = 5200ms = 5,2s) para leitura do sensor de temperatura citado no nosso exemplo. O Timer utilizado e a sua resolução são escolhidos também em conseqüência do processo. Existem Timers de 8 bits e de 16 bits.A melhor escolha deverá ser feita pelo programador, diante de cada aplicação.
Figura 21: Configurações dos Timers.
Para habilitarmos as interrupções, clicamos na opção “Interrupts”. Assim, todas as fontes de interrupção do MCU serão listadas. Para o nosso exemplo vamos habilitar as interrupções “RS232 receive data available” (dado disponível no buffer da RS232) e “Timer 1 Overflow” (estouro do Timer 1 – se executarmos esta rotina 50 vezes obteremos o tempo de 5 segundos necessário para a nossa aplicação).
Figura 22: Habilitando Interrupções.
Desta forma, configuramos o MCU para trabalhar no nosso exemplo: Controle de Temperatura de uma Estufa para Plantas. Ao clicarmos no botão “OK”, finalizamos o processo de Configuração do MCU. Logo em seguida a janela do “PIC Wizard” será fechada e um Editor de Texto, contendo o Código Fonte gerado e o nome do Projeto em questão, será aberto na janela principal do CCS. 
Figura 23: Editor de Texto do CCS.
Na primeira linha temos o seguinte código: 
#include “diretório_do_arquivo\nome_do_arquivo.h”. 
Desta forma, se mudarmos o diretório do projeto uma série de erros poderá ocorrer, pois pode ser que o arquivo não exista no diretório mencionado pela diretiva “#include”. Por isso recomendamos substituir todo o texto que faz referência ao diretório do arquivo por um “.” (ponto). Desta forma, se mudarmos o diretório de todo o projeto, não teremos nenhum tipo de problema. Teremos, portanto:
#include “.\nome_do_arquivo.h”
Em resumo, se observarmos o código em C gerado, na primeira linha temos:
#include "C:\Documents and Settings\TPA\Desktop\Novo Projeto.h"
O código após ser alterado ficará da seguinte forma:
#include ".\Novo Projeto.h"
Figura 24: Editor de Texto do CCS com o diretório do #include alterado.
Parte do código gerado das configurações do MCU é escrito no arquivo apontado na primeira linha do código fonte. Na Figura X, o arquivo “Novo Projeto.h” é apontado pela diretiva “#include”. Se abrirmos este arquivo visualizaremos o código em questão.
Figura 25: Editor de Texto do CCS.
Todo o procedimento de como se criar um novo projeto no CCS está listado e ilustrado neste capítulo. E o mesmo procedimento poderá ser seguido a fim de se configurar qualquer outro MCU da família PIC. 
Diversas características dos módulos internos do PIC foram omitidas, pois este capítulo visa apenas apresentar o assistente de configurações “PIC Wizard” e suas principais ferramentas. Características exclusivas de cada módulo e exemplos de outros módulos internos, que não foram mencionados neste capítulo e que são possíveis de serem configurados pelo “PIC Wizard”, serão apresentados em tópicos posteriores. 
É importante que o estudante e/ou programador saiba que cada MCU possui as suas especialidades como, por exemplo, quantidade de Timers de 8 bits e Timers de 16 bits disponíveis, número de entradas analógicas, tipos de comunicação, quantidade de Entradas/Saídas, etc. Portanto, nem todos os módulos disponíveis num determinado MCU serão possíveis de serem configurados a partir do “PIC Wizard”. Quando houver a necessidade de se utilizar um módulo específico do MCU que não seja configurável pelo assistente de configurações “PIC Wizard”, o estudante e/ou programador deverá recorrer aos Tópicos de Ajuda do CCS.
Funções do Compilador CCS
O CCS possui um pacote imenso de funções. Iremos tratar neste capítulo apenas as principais funções que serão utilizadas ao longo do nosso curso. Outras funções serão apenas comentadas para que o estudante e/ou programador saiba da existência das mesmas. 
O estudante e/ou programador será encarregado de buscar novas funções ou sanar dúvidas recorrendo aos Tópicos de Ajuda do CCS.
Funções para Configurações das Entradas e Saídas Digitais
O CCS nos fornece diversas funções que nos permitem configurar as portas do MCU. Em destaque temos:
Port_a_pullups(CONDIÇÃO): habilita ou desabilita os resistores de Pull-Up da porta A. Somente para dispositivos que tenham esta possibilidade.
Port_b_pullups(CONDIÇÃO): habilita ou desabilita os resistores de Pull-Up da Porta B. Somente para dispositivos que tenham esta possibilidade.
Set_tris_x(VALOR): configura os pinos de I/O da Porta “x” como Entrada ou Saída Digital. Para esta função, temos que: “0” configura o pino como saída e “1” como entrada. O bit menos significativo de “VALOR” representa o bit menos significativo da Porta X.
Get_tris_x(VALOR): verifica a configuração dos pinos da Porta “x”. Para esta função, temos o seguinte: “0” o pino está configurado como saída e “1” como entrada. O bit menos significativo de “VALOR” representa o bit menos significativo da Porta X.
Input_state(PINO): verifica o nível lógico do pino. Esta função não muda a configuração do pino. Se o pino em questão estiver configurado como saída digital, por exemplo, ele permanecerá configurado como saída digital. Assim, a função retornará: “0” se a saída está em nível baixo ou “1” se a saída estiver em nível alto.
Acionamento de Saídas Digitais
Por default, ao utilizar qualquer uma destas funções, o CCS configura todos os pinos em questão como Saída Digital.
Output_low(PINO): coloca o “PINO” especificado em nível lógico baixo (0 Volts).
Output_high((PINO):coloca o “PINO” especificado em nível lógico alto (5 Volts).
Output_toggle((PINO): inverte o nível lógico do “PINO” especificado.
Output_bit((PINO,VALOR): coloca o “PINO” especificado em nível lógico baixo, com “VALOR” igual a “0”, ou alto, com “VALOR” igual a “1”.
Output_x(VALOR): coloca um byte determinado por “VALOR” na Porta “x”.
Leitura de Entradas Digitais
Por default, ao utilizar qualquer uma destas funções, o CCS configura todos os pinos em questão como Entrada Digital.
Input(PINO): lê o nível lógico do “PINO” especificado.
Input_x(): lê o nível lógico da Porta “x”.
Funções de Tempo
Quando precisamos aguardar um determinado tempo para efetuarmos uma determinada tarefa necessitamos de Funções de Tempo (Delay).
Delay_cycles(TEMPO): gera um delay com resolução de ciclos de máquina. O valor do delay, dado em ciclos, é especificado por “TEMPO”.
Delay_us(TEMPO): gera um delay com resolução de de micro segundos. O valor do delay, dado em micro segudos, é especificado por “TEMPO”.
Delay_ms(TEMPO): gera um delay com resolução de de mili segundos. O valor do delay, dado em mili segudos, é especificado por “TEMPO”.
Funções para Conversor Analógico/Digital
Para manipularmos o Conversor Analógico/Digital do MCU necessitamos de um conjunto de configurações. As principais funções para este fim são:
Set_adc_channel(CANAL): seleciona o canal analógico a ser lido pela função “read_adc()”.
Read_adc(): realiza a leitura do Canal Analógico selecionado pela função “set_adc_channel()”. O pino a ser lido deverá estar configurado como entrada analógica. Isto é feito a partir da função “setup_adc_ports()”.
Setup_adc_ports(CONFIGURAÇÕES): configura os pinos como entradas analógicas ou não. Estas “CONFIGURAÇÕES” dependem exclusivamente de cada MCU. Assim o programador deverá estar atento para não escolher um ajuste errado para um determinado modelo. Recomenda-se recorrer ao Datasheet e/ou consultar o arquivo .h do MCU em questão.
Funções para EEPROM interna
Para acessar a EEPROM interna do MCU utilizamos as seguintes funções: 
Read_eeprom(ENDEREÇO): lê uma posição da EEPROM interna determinada por “ENDEREÇO”.
Write_eeprom(ENDEREÇO,VALOR): escreve na posição “ENDEREÇO” da EEPROM interna o “VALOR” indicado.
Funções para os Módulos Timer’s
Os Módulos Timer’s são utilizados quando é necessário uma base de tempo para se tratar dentro do processo a ser monitorado. Outra importante aplicação dos Módulos Timer’s é a de trabalhar como contadores. Nesta configuração, um pino externo específico é responsável por incrementar o valor do Timer. 
As principais funções utilizadas são:
Setup_timer_x(CONFIGURAÇÕES): “CONFIGURAÇÕES” do Módulo Timer “x”.
Get_timerx(): lê o valor do Módulo Timer “x”.
Set_timerx(VALOR):inicializa o “VALOR” do Timer “x”.
Funções para Comunicação Serial RS232
Para usar as funções de Comunicação Serial RS232 é necessário utilizar a diretiva “#use rs232”. Esta diretiva será vista em um tópico adiante. 
Dentre as funções que manipulam a Comunicação Serial RS232, destacam-se as seguintes:
Getc(PORTA): lê o buffer (apenas 1 byte) da “PORTA” serial do MCU. 
Putc(DADO,PORTA): envia um “DADO” (apenas 1 byte) na “PORTA” serial do MCU.
Set_uart_speed(BAUD,PORTA): configura a velocidade de transmissão da “PORTA” através do parâmetro “BAUD”.
Printf(STRING): envia uma cadeia de dados no formato ASCII determinada em “STRING”.
Funções para manipulação de variáveis
O CCS nos fornece um conjunto de funções que nos permite manipular de diversas formas as variáveis que criamos ao longo de nossos programas. Dentre elas podemos citar:
Bit_clear(VARIÁVEL,BIT): coloca nível lógico baixo (“0”) no “BIT” especificado da ”VARIÁVEL”.
Bit_set(VARIÁVEL,BIT): coloca nível lógico alto (“1”) no “BIT” especificado da ”VARIÁVEL”.
Bit_test(VARIÁVEL,BIT): retorna o valor do “BIT” especificado da “VARIÁVEL”.
Isdigit(DADO): verifica se “DADO” é um valor numérico.
Atoi(STRING): converte uma cadeia de caracteres, parâmetro “STRING”, em um valor do tipo “INT”.
Atof(STRING): converte uma cadeia de caracteres, parâmetro “STRING”, em um valor do tipo “FLOAT”.
Make16(BYTE_HIGH,BYTE_LOW): monta um valor do tipo “LONG” a partir dos 2 bytes passados como parâmetros.
Make8(VALOR,INDEX): extrai um byte de “VALOR”.
Funções para manipulação de Interrupções
	Para o tratamento de interrupções, as principais são:
Enable_interrupts(INTERRUPÇÃO): habilita a “INTERRUPÇÃO” passada como parâmetro.
Disable_interrupts(INTERRUPÇÃO): desabilita a “INTERRUPÇÃO” passada como parâmetro.
Clear_interrupt(INTERRUPÇÃO): limpa o “Flag” indicador de ocorrência de “INTERRUPÇÃO”.
Ext_int_edge(BORDA,EXTx): configura a “BORDA” de ocorrência da Interrupção Externa “x”.
Programação Aplicada a Microcontroladores PIC com o Compilador CCS
Introdução
Neste capítulo trataremos de diversas funcionalidades do Compilador CCS e de diversos Módulos Internos do Microcontrolador PIC16F877A, objeto de nossos estudos. Todos os exemplos que serão ilustrados neste capítulo são baseados no Kit de Desenvolvimento 
É de extrema importância que o estudante tenha uma boa base teórica da Linguagem C, abordada em outros tópicos, e que compreenda o que for abordado em cada seção, pois tópicos futuros dependerão exclusivamente desses nossos primeiros e simples programas.
Controlando I/O’s Digitais
O primeiro programa que iremos demonstrar é uma espécie de pisca-pisca. É um exemplo bastante simples, mas será útil para o programador ao longo de qualquer programa, independente de sua complexidade.
Independente se vai acender um LED, acionar um Relé ou partir um Motor Trifásico, os comandos para estes fins são os mesmos. Sendo assim, se o estudante e/ou programador entender os procedimentos tratados neste exemplo estará apto à atuar em qualquer um dos dispositivos mencionados acima. Os principais comandos utilizados para controle de Saídas Digitais são: 
output_low(pino);
 output_high(pino);
output_toggle(pino);
output_bit(pino,valor);
output_a(valor);
output_b(valor);
...
Para leitura das entradas digitais, temos:
Input(pino);
Input_a();
Input_b();
...
Para este programa, só usaremos as configurações gerais (general) do PIC Wizard. Crie um novo projeto no CCS utilizando o “PIC Wizard”. Qualquer dúvida de como se criar um novo projeto consulte os Tópicos relacionados ao “PIC Wizard” do CCS.
Ao criar o novo projeto, altere as seguintes configurações:
Device: PIC16F877A
Fuses: High speed osc (>4MHz)
Oscilator Frequency: 20.000.000
Ao finalizar, insira no projeto a biblioteca de “auto-programação” (bootloader.h). Logo em seguida, escreva o código relativo ao pisca-pisca, logo abaixo da linha de “configurações do MCU”.
Exemplo de um LED piscando a 0,5Hz:
#include ".\nome_do_projeto.h" //Biblioteca dos Fuses do Projeto
#include <bootloader.h> //Biblioteca para Auto-Programação
void main()
{ 
 //configurações do MCU
 setup_adc_ports(NO_ANALOGS);
 setup_adc(ADC_OFF);
 setup_psp(PSP_DISABLED);
 setup_spi(FALSE);
 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
 setup_timer_1(T1_DISABLED);
 setup_timer_2(T2_DISABLED,0,1);
 setup_comparator(NC_NC_NC_NC);
 setup_vref(FALSE);
 //código para o pisca-pisca
 while(true){
 output_low(PIN_D4);
 delay_ms(1000);
 output_high(PIN_D4); 
 delay_ms(1000); 
 }
}
Existe uma função que já inverte o valor do pino. Abaixo segue o código do loop infinito com esta função:
 //código para o pisca-pisca
 while(true){
 output_toggle(PIN_D4);
 delay_ms(1000);
 }
Exemplo: acionando um LED a partir do comando de uma chave.
#include ".\nome_do_projeto.h"
#include <bootloader.h>
void main()
{
 setup_adc_ports(NO_ANALOGS);
 setup_adc(ADC_OFF);
 setup_psp(PSP_DISABLED);
 setup_spi(FALSE);
 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
 setup_timer_1(T1_DISABLED);
 setup_timer_2(T2_DISABLED,0,1);
 setup_comparator(NC_NC_NC_NC);
 setup_vref(FALSE);
 while(true){
 if(input(PIN_D0))
 output_high(PIN_D4);
 else
 output_low(PIN_D4);
 }
}
O código acima também pode ser feito utilizando-se da função “output_bit”.
 
while(true){
 output_bit(PIN_D4,input(PIN_D0));
 }
Controle de Display de LCD de Caracteres
O Display de LCD é um componente eletrônico digital que pode ser controlado utilizando as mesmas funções de controle de I/O’s Digitais mencionadas anteriormente.
Figura 26: Display de LCD 16x2 Caracteres.
Figura 27: Descrição da Pinagem do Display.
Figura 28: Diagrama de Tempos.
As rotinas de controle do display são desenvolvidas a partir dos ciclos de escrita mostrados logo acima. Existem bibliotecas com estas rotinas implementadas e facilmente encontradas na internet. No entanto, usaremos uma biblioteca desenvolvida na TPA Engenharia que é a “Visor.h”.
Antes de incluir a biblioteca devemos definir os pinos do MCU para cada pino do display LCD (RS, E, D4 ao D7) , incluir a biblioteca “visor.h”. Esta biblioteca deve ser inserida na pasta do projeto a ser executado. Para se utilizar o display, deve-se executar as rotinas lcd_ini() e liga_display() no programa principal.
Principais funções disponíveis na biblioteca “visor.h”:
lcd_escreve(“a”) - deve ser passada como parâmetro da função printf. O parâmetro a podem ser caracteres ou variáveis
pos_linha(a) - Posiciona o cursos na linha “a”
limpa_linha(a) - Limpa a linha “a”
LCD_pos_xy(a,b) - Posiciona o cursor na coluna “a” e na linha “b”
Exemplo: Escrevendo Mensagem no Display
#include ".\nome_do_projeto.h"
#include <bootloader.h>
#define lcd_d4 PIN_A2
#define lcd_d5 PIN_A4
#define lcd_d6 PIN_A5
#define lcd_d7 PIN_E0
#define lcd_enable PIN_E2
#define lcd_rs PIN_E1
#define lcd_type 2
#define lcd_seg_lin 0x40
#include ".\visor.H"
void main()
{
 setup_adc_ports(NO_ANALOGS);
 setup_adc(ADC_OFF);
 setup_psp(PSP_DISABLED);
 setup_spi(FALSE);
 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
 setup_timer_1(T1_DISABLED);
 setup_timer_2(T2_DISABLED,0,1);
 setup_comparator(NC_NC_NC_NC);
 setup_vref(FALSE);
 
 lcd_ini();
 delay_ms(10); 
 liga_display();
 lcd_pos_xy(3,1);
 printf(lcd_escreve,"Hello World");
 pos_linha(2);
 printf(lcd_escreve,"From PIC16F877A");
 
 delay_ms(3000);
 
 limpa_linha(1);
 limpa_linha(2);
 pos_linha(1);
 printf(lcd_escreve,"Bootloader no");
 pos_linha(2);
 printf(lcd_escreve,"pino RD0");
 while(true){}
}
Entradas Analógicas
O PIC16F877A possui 8 Canais Analógicos multiplexados em um único Conversor Analógico/Digital de 10 bits. As tensões de referência do conversor podem ser externas, através de pinos específicos, ou internas (própria alimentação do MCU). Parao caso de referência externa, esta deve respeitar os limites da alimentação que, no nosso caso, estará entre 0 e 5volts.
Os principais comandos para manipulação dos conversores A/D são:
Set_adc_channel(canal);
Read_adc();
Exemplo: Leitura de um Potenciômetro
Usaremos a seguinte configuração para os canais analógicos (analog):
A0
Units 0-1023
Internal 2-6us
Código fonte:
#include ".\nome_do_projeto.h"
#include <bootloader.h>
#define lcd_d4 PIN_A2
#define lcd_d5 PIN_A4
#define lcd_d6 PIN_A5
#define lcd_d7 PIN_E0
#define lcd_enable PIN_E2
#define lcd_rs PIN_E1
#define lcd_type 2
#define lcd_seg_lin 0x40
#include ".\visor.H"
void main()
{
 int16 conversao;
 setup_adc_ports(AN0);
 setup_adc(ADC_CLOCK_INTERNAL);
 setup_psp(PSP_DISABLED);
 setup_spi(FALSE);
 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
 setup_timer_1(T1_DISABLED);
 setup_timer_2(T2_DISABLED,0,1);
 setup_comparator(NC_NC_NC_NC);
 setup_vref(FALSE);
 
 lcd_ini();
 delay_ms(10); 
 liga_display();
 set_adc_channel(0); // seleciona o canal A/D
 delay_us(10);
 while(true){
 conversao = read_adc();
 limpa_linha(1);
 pos_linha(1);
 printf(lcd_escreve,"AD: %04lu",conversao);
 delay_ms(100); 
 }
}
Interrupções
São rotinas especiais que interrompem a execução do programa para o tratamento de determinados eventos. O programador não sabe o momento em que a interrupção irá ocorrer, mas ele sabe o que a rotina realizará quando ela acontecer. 
Existem dois tipos de interrupção: 
Interrupções Externas: a fonte da interrupção é um evento externo ao microcontrolador (por exemplo um teclado).
Interrupção Interna: a fonte da interrupção é um evento interno do microcontrolador (por exemplo um timer).
As interrupções mais usadas no PIC16F877A são:
External (pino RB0)
On B Change (Pinos RB4 a RB7)
RDA (recebimento de dado na serial)
Timer0, Timer1, Timer2
Para se usar uma determinada interrupção a mesma deve ser habilitada com o comando: enable_interrupts(nome da interrupção);
Além de habilitar a interrupção individualmente, o flag global também deve ser habilitado: 
enable_interrupts(GLOBAL);
As interrupções também podem ser desabilitadas com o comando: 
disable_interrupts(nome da interrupção);
Também podem ser desabilitadas todas de uma só vez através do comando: 
disable_interrupts(GLOBAL);
As interrupções podem ser habilitadas e desabilitadas a qualquer momento. Toda interrupção possui um flag que sinaliza a sua ocorrência. Após a execução da rotina da interrupção deve-se limpar o seu flag de ocorrência com o comando:
clear_interrupt(nome da interrupção);
Normalmente a troca de informações entre as rotinas de interrupções e o programa principal é feita através de variáveis globais. (declaradas fora do void main)
Exemplo: Interrupção no pino RB0 em borda de subida.
Para este exemplo, ao criar o projeto pelo “PIC Wizard” devemos marcar, dentro da guia “interrupts”, a opção “External Interrupt”.
#include ".\exemplo_curso.h"
#include <bootloader.h>
#define lcd_d4 PIN_A2
#define lcd_d5 PIN_A4
#define lcd_d6 PIN_A5
#define lcd_d7 PIN_E0
#define lcd_enable PIN_E2
#define lcd_rs PIN_E1
#define lcd_type 2
#define lcd_seg_lin 0x40
#include ".\visor.H"
int16 contador=0;
#int_EXT
EXT_isr() 
{
 contador++;
 clear_interrupt(INT_EXT);
}
void main()
{
 setup_adc_ports(NO_ANALOGS);
 setup_adc(ADC_OFF);
 setup_psp(PSP_DISABLED);
 setup_spi(FALSE);
 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
 setup_timer_1(T1_DISABLED);
 setup_timer_2(T2_DISABLED,0,1);
 setup_comparator(NC_NC_NC_NC);
 setup_vref(FALSE);
 enable_interrupts(INT_EXT);
 enable_interrupts(GLOBAL);
 delay_ms(50);
 lcd_ini();
 delay_ms(10);
 liga_display();
 ext_int_edge( L_TO_H ); 
 while(true){
 pos_linha(1);
 printf(lcd_escreve,"Contador:%04ld",contador);
 }
}
Exemplo: Teclado Matricial com interrupção On B Change
Para este exemplo, ao criar o projeto pelo “PIC Wizard” devemos marcar, dentro da guia “interrupts”, a opção “Port B any change on B4-B7”.
#include ".\nome_do_projeto.h"
#include <bootloader.h>
#define X1 PIN_B1
#define X2 PIN_B2
#define X3 PIN_B3
#define Y1 PIN_B4
#define Y2 PIN_B5
#define Y3 PIN_B6
#define Y4 PIN_B7
#include ".\visor.h"
char tecla='N';
#int_RB
RB_isr() 
{ 
 output_high(X1);
 output_low(X2);
 output_low(X3);
 if(input(Y1)){
 tecla='1';
 goto FIM;
 }
 if(input(Y2)){
 tecla='4';
 goto FIM;
 }
 if(input(Y3)){
 tecla='7';
 goto FIM;
 }
 if(input(Y4)){
 tecla='*';
 goto FIM;
 }
 output_low(X1);
 output_high(X2);
 output_low(X3);
 if(input(Y1)){
 tecla='2';
 goto FIM;
 }
 if(input(Y2)){
 tecla='5';
 goto FIM;
 }
 if(input(Y3)){
 tecla='8';
 goto FIM;
 }
 if(input(Y4)){
 tecla='0';
 goto FIM;
 }
 output_low(X1);
 output_low(X2);
 output_high(X3);
 if(input(Y1)){
 tecla='3';
 goto FIM;
 }
 if(input(Y2)){
 tecla='6';
 goto FIM;
 }
 if(input(Y3)){
 tecla='9';
 goto FIM;
 }
 if(input(Y4)){
 tecla='#';
 goto FIM;
 }
 
FIM:
 output_high(X1);
 output_high(X2);
 output_high(X3);
 delay_ms(50);
 clear_interrupt(INT_RB);
 
}
void main()
{
 setup_adc_ports(NO_ANALOGS);
 setup_adc(ADC_OFF);
 setup_psp(PSP_DISABLED);
 setup_spi(FALSE);
 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
 setup_timer_1(T1_DISABLED);
 setup_timer_2(T2_DISABLED,0,1);
 setup_comparator(NC_NC_NC_NC);
 setup_vref(FALSE);
 enable_interrupts(INT_RB);
 enable_interrupts(GLOBAL);
 
 delay_ms(50);
 lcd_ini();
 delay_ms(10);
 liga_display();
 output_high(X1);
 output_high(X2);
 output_high(X3);
 pos_linha(1);
 while(true){
 if(tecla!='N'){
 if(tecla=='#'){
 limpa_display();
 pos_linha(1);
 } 
 else{
 printf(lcd_escreve," %c",tecla);
 }
 tecla='N';
 }
 }
}
Exemplo: Piscando LED com Interrupção por Timer
Iremos utilizar o Timer 1 para o nosso exemplo. Ao criar um novo projeto, deveremos configurar o Timer 1 da seguinte forma:
Timer 1: 
Internal
Resolution 1.6us (overflow 104ms)
Configurações de interrupções (interrupts)
Timer 1 overflow
Código fonte:
#include ".\nome_do_projeto.h"
#include <bootloader.h>
#define lcd_d4 PIN_A2
#define lcd_d5 PIN_A4
#define lcd_d6 PIN_A5
#define lcd_d7 PIN_E0
#define lcd_enable PIN_E2
#define lcd_rs PIN_E1
#define lcd_type 2
#define lcd_seg_lin 0x40
#include ".\visor.H"
void main()
{
 int16 conversao;
 setup_adc_ports(AN0);
 setup_adc(ADC_CLOCK_INTERNAL);
 setup_psp(PSP_DISABLED);
 setup_spi(FALSE);
 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
 setup_timer_1(T1_DISABLED);
 setup_timer_2(T2_DISABLED,0,1);
 setup_comparator(NC_NC_NC_NC);
 setup_vref(FALSE);
 
 lcd_ini();
 delay_ms(10); 
 liga_display();
 set_adc_channel(0); // seleciona o canal A/D
 delay_us(10);
 while(true){
 conversao = read_adc();
 limpa_linha(1);
 pos_linha(1);
 printf(lcd_escreve,"AD: %04lu",conversao);
 delay_ms(100); 
 }
}
Comunicação Serial
Para se estabelecer comunicação entre Computador e Microcontrolador através da interface RS232, devemos inserir um circuito de interface entre eles.
Figura 29: Circuito comunicação RS232.
Os principais comandos relativos à porta serial RS232 para o PIC16F877A são:
Putc(dado)
Getc()
Printf(mensagem);
Exemplo: Enviando e Recebendo Dados pela Porta Serial
Usaremos a configuração de comunicação (communications) padrão:
Use RS-232
Baud 9600
Parity None
Transmit - C6
Receive - C7
Bits – 8
Usaremos a configuração de interrupções (interrupts)
RS232 receive data available
Código Fonte:#include ".\nome_do_projeto.h"
#include <bootloader.h>
#define led PIN_D4
#int_RDA
RDA_isr() 
{
 int dado_recebido;
 dado_recebido = getc();
 if(dado_recebido == 'L') // acende o led D4 ao receber
 output_high(led); // o dado 'L'
 else if(dado_recebido == 'D')
 output_low(led); // apaga o led ao receber o 'D'
}
void main()
{
 int i=0;
 setup_adc_ports(NO_ANALOGS);
 setup_adc(ADC_OFF);
 setup_psp(PSP_DISABLED);
 setup_spi(FALSE);
 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
 setup_timer_1(T1_DISABLED);
 setup_timer_2(T2_DISABLED,0,1);
 setup_comparator(NC_NC_NC_NC);
 setup_vref(FALSE);
 enable_interrupts(INT_RDA);
 enable_interrupts(GLOBAL);
 printf("Hello World from serial rs232 ");
 putc('A');
 putc(66);
 printf("C");
 while(true){
 putc(i);
 i++;
 delay_ms(1000);
 }
}
EEPROM
O PIC16F877A possui uma memória não volátil de 256 bytes do tipo EEPROM , ou seja, que após ser desligado os dados não são perdidos.
Os comandos para leitura e escrita dessa memória são:
Write_eeprom(endereço,dado);
Read_eeprom(endereço);
Exemplo: Gravando o valor do Potenciômetro na EEPROM
Usaremos a seguinte configuração para os canais analógicos (analog):
A0
Units 0-1023
Internal 2-6us
#include ".\exemplo_curso.h"
#include <bootloader.h>
#define lcd_d4 PIN_A2
#define lcd_d5 PIN_A4
#define lcd_d6 PIN_A5
#define lcd_d7 PIN_E0
#define lcd_enable PIN_E2
#define lcd_rs PIN_E1
#define lcd_type 2
#define lcd_seg_lin 0x40
#include ".\visor.H"
#define GRAVAR PIN_B0
#define POTENCIOMETRO 0
void main()
{
 int16 valor;
 setup_adc_ports(AN0);
 setup_adc(ADC_CLOCK_INTERNAL);
 setup_psp(PSP_DISABLED);
 setup_spi(FALSE);
 setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
 setup_timer_1(T1_DISABLED);
 setup_timer_2(T2_DISABLED,0,1);
 setup_comparator(NC_NC_NC_NC);
 setup_vref(FALSE);
 delay_ms(50);
 lcd_ini();
 delay_ms(10);
 liga_display();
 set_adc_channel(POTENCIOMETRO);
 while(true){
 valor=read_adc();
 if(input(GRAVAR)){
 write_eeprom(8,valor/256);
 write_eeprom(9,valor%256);
 delay_ms(100);
 }
 pos_linha(1);
 printf(lcd_escreve,"Atual:%04ld",valor);
 pos_linha(2);
 printf(lcd_escreve,"Gravado:%04ld",read_eeprom(8)*256+read_eeprom(9));
 delay_ms(100);
 }
}
Interface com Microcontroladores
Para que um Microcontrolador (MCU) possa atuar em um determinado processo é necessário que haja uma “comunicação” entre ele e o mundo externo, cujo qual o mesmo irá monitorar ou controlar. A esta “comunicação” damos o nome de Interface.
Por exemplo: se desejamos atuar em um Contator com tensão nominal de 220V ou acionar um Motor DC de 24V haverá a necessidade de um Circuito de Potência interfaceando o MCU com a carga a ser acionada, já que o mesmo trabalha com tensão baixa (o mais comum é trabalhar com 5Volts). 
Neste capítulo trataremos sobre alguns circuitos de interface comuns e utilizados na prática.
Interface com LED’s
Os pinos do MCU podem fornecer correntes de até 25 mA. Isso significa que é possível ligar diretamente um LED a um pino de saída. Para limitar a corrente utiliza-se em série uma resistência de 330 ohms. 
Figura 30: Circuito de Interface para LED’s
Para acionar o LED basta colocar o pino conectado ao mesmo em nível lógico alto (“1”). Para desligá-lo colocamos o pino em nível lógico baixo (“0”).
Interface para Motores DC e Relés
Muitos projetos requerem a utilização de motores de corrente contínua (CC) para criar movimento circular. Existem várias possibilidades de interfacear motores ao MCU. A interface com esse tipo de motor é a mesma utilizada para acionar pequenos relés.
O circuito abaixo funciona bem com cargas de baixa potência. No entanto, cargas indutivas, como motores e relés, introduzem uma grande quantidade de ruídos na linha de alimentação. Por esta razão, utilizamos um Diodo em Anti-paralelo com a carga indutiva, afim de minimizar os ruídos provenientes das tensões reversas geradas no desligamento da carga.
Figura 31: Interface de Acionamento para Motor DC.
Para minimizar ainda mais os ruídos, pode-se também conectar um Capacitor de 220nF aos terminais da carga indutiva.
Interface com Interruptores
Figura 31: Chaves Fim-de-Curso e Interruptores em Geral.
Existe disponível no mercado mundial uma enorme variedade de interruptores. Mas a maior parte possui dois contatos que: estão “Normalmente Abertos” (NA) ou “Normalmente Fechados” (NF). Abaixo temos um exemplo de ligação para esses tipos de interruptores.
Figura 32: Interface para Interruptores em Geral.
Interface com Sensores Resistivos
Para todo sensor do tipo resistivo, ou seja, que varia a resistência entre seus terminais para indicar a variação de uma grandeza física qualquer, podemos utilizar o circuito ilustrado logo abaixo.
Figura 33: Interface com Sensores Resistivos.
Entre os tipos de sensores resistivos podemos citar dois tipos comuns: Termistores (sensores de temperatura) e os LDR’s (Resistor Dependente de Luz). Afim de simular um Sensor do tipo Resistivo, é comum utilizarmos de um Potenciômetro Analógico.
Figura 34: Potenciômetro Analógico.
TPA Engenharia – www.tpaengenharia.com.br 				63
F
V
F
V
F
V
. . .
. . .
bloco 3
3?
Condição 
bloco 2
2?
Condição 
bloco 1
1?
Condição

Outros materiais