5 Micro Parte 5 (PIC) Pratica PDF

Você também pode gostar

Você está na página 1de 197

Microcontroladores PIC

Prática

MSc. Gustavo Souto de Sá e Souza


Revisado por José Wilson Nerys
Introdução
O principal microcontrolador utilizado nesse estudo é o PIC18F4550,
cujas características principais são:
 Fontes de clock possíveis:
Cristal externo (4 modos);
Clock externo (2 modos. Até 48 MHz);
Oscilador interno (8 frequências diferentes: de 31 kHz a 8 MHz)
 Memórias: 32 K de memória Flash; 2 K de SRAM e 256 bytes de
EEPROM
 35 pinos de entrada/saída
 Conversor Analógico/Digital de 10 bits, 13 entradas multiplexadas

Obs.: É importante lembrar que as informações contidas aqui podem variar para outras
famílias PIC ou até mesmo para outros chips da mesma família.
Introdução
O principal microcontrolador utilizado nesse estudo é o PIC18F4550,
cujas características principais são:

 3 interrupções externas
 Capacidade de corrente nos pinos de I/O: 25 mA
 4 módulos temporizadores (Timer 0 a Timer 3)
 Até 2 módulos de Captura/Comparação/PWM (CCP)
 Unidade interna de USB (transceiver)
 Programação via canal serial com 2 pinos
 Canal serial universal melhorado (EUSART)
 Canal I2C (vários transdutores usam esse canal para a transferência
de dados. Exemplos: giroscópio e barômetro)
PIC18F4550
Linguagem C – compilador XC8

Inicialização da variável “variavel”:


 Bit ou boolean: bit variavel;
 Valor inteiro: int variavel;
 Valor inteiro com sinal (positivo ou negativo): signed int variavel;
 Caractere: char variavel;
 String (conjunto de, digamos, 10 caracteres): char variavel[10];
 Valor flutuante: float variavel;
Linguagem C – compilador XC8

Definição de variável:
 Decimal: variavel = 100;
 Binário: variavel = 0b1100100;
 Hexadecimal: variavel = 0x64;
 Caractere: variavel = “d”;
Linguagem C – compilador XC8
Operações:
 Definição de variável: variavel = 255;
 Soma: variavel = 15 + b;
 Subtração: variavel = 15 - b;
 Multiplicação: variavel = 15 * b;
 Divisão: variavel = 15 / b;
 Rotação de N bits para esquerda: variavel =
variavel << N;
 Rotação de N bits para a direita: variavel =
variavel >> N;
Linguagem C – compilador XC8
Operações:
 Operação E: variavel = variavel & 55;
 Operação OU: variavel = variavel | 55;
 Operação NÃO (inverte apenas 1 bit): variavel =
!variavel;
 Incrementar em 1: variavel++;
 Decrementar em 1: variavel--;
Linguagem C – compilador XC8
Condições (retornam 1 se verdadeiro, 0 se falso):
 Verificar se é igual: (variavel == b);
 Verificar se é diferente: (variavel != b);
 Verificar se é maior: (variavel > b);
 Verificar se é menor: (variavel < b);
 Verificar se é maior ou igual: (variavel >= b);
 Verificar se é menor ou igual: (variavel <= b);
 Condição E: (variavel <= b && variavel != 0);
 Condição OU: (variavel <= b || variavel != 0);
Linguagem C – compilador XC8

Definições:
 Define “_constante” como 5: #define _constante 5
 Define “PINO_DO_LED” como LATD1: #define PINO_DO_LED LATD1

Inclusões de bibliotecas:
 Inclui biblioteca do compilador: #include <stdlib.h>
 Inclui biblioteca da pasta local: #include “lcd.h”
Linguagem C – compilador XC8

Se:
 if:
if (variavel == 10) {
// executa se condição for verdadeira
} else {
// executa se condição for falsa
}
Linguagem C – compilador XC8

Se: Condição
 if:
if (variavel == 10) {
// executa se condição for verdadeira
} else {
// executa se condição for falsa
}
Linguagem C – compilador XC8

Loops:
 While:
while (variavel != 0) {
// código em loop
}
Linguagem C – compilador XC8

Condição (executa enquanto for 1)


Loops:
 While:
while (variavel != 0) {
// código em loop
}
Linguagem C – compilador XC8

Loops:
 for:
for (variavel = 1; variavel < 100; variavel++)
{
// código em loop
}
Linguagem C – compilador XC8
Valor inicial
Loops:
 for:
for (variavel = 1; variavel < 100; variavel++)
{
// código em loop
}
Linguagem C – compilador XC8
Condição (executa enquanto for 1)
Loops:
 for:
for (variavel = 1; variavel < 100; variavel++)
{
// código em loop
}
Linguagem C – compilador XC8
Incremento
Loops:
 for:
for (variavel = 1; variavel < 100; variavel++)
{
// código em loop
}
Linguagem C – compilador XC8

Loops:
 break:
for (variavel = 1; variavel < 100; variavel++)
{
// código em loop
if (variavel < 0) {
break;
}
}
Linguagem C – compilador XC8
Loops:
 break:
for (variavel = 1; variavel < 100; variavel++)
{
// código em loop
if (variavel < 0) {
break;
}
}
Finaliza e sai do loop aqui
Linguagem C – compilador XC8

Funções:
 Principal:
void main (void) {
// Código principal do programa vem aqui
}
Linguagem C – compilador XC8

Funções:
 Interrupção:
void interrupt int_func (void) {
// Código da interrupção
}
Linguagem C – compilador XC8

Funções:
 Interrupção de baixa prioridade:
void interrupt low_priority int_low_funcao
(void) {
// Código da interrupção de baixa prioridade
}
Linguagem C – compilador XC8

Funções:
 Secundárias:
void LigaTimer (void) {
TMR0ON = 1;
}
Linguagem C – compilador XC8

Funções:
 Secundárias com valores de entrada e saída:
int SomaDez (int valor_de_entrada) {
valor_de_entrada = valor_de_entrada + 10;
return valor_de_entrada;
}
Linguagem C – compilador XC8

Chamando Funções:
LigaTimer();

variavel = SomaDez(variavel);
Linguagem C – compilador XC8
Função de atraso por milissegundo:
__delay_ms(tempo_em_milissegundos);

!!! Requer que a velocidade do oscilador seja definido antes, por


meio da linha
#define _XTAL_FREQ 1000000 (para um oscilador de 1 MHz)

Também requer a library xc.h incluída por meio da linha:


#include <xc.h>
Pode causar erro se o valor de entrada for muito grande, relativo à
velocidade do oscilador.
Linguagem C – compilador XC8

Comentando o código:
TRISA = 0; // A parte comentada vem depois de
// duas barras
/* Ou você pode comentar
todo um trecho do código
usando asterisco e barra */
ok++;
Linguagem C – compilador XC8
sprintf: imprime e manipula strings e caracteres. Requer que a
biblioteca “stdio.h” seja incluída.

#include <stdio.h>
char linha1[16];

sprintf(linha1, “Hello, world!”);

// Grava o texto ‘Hello, world!’ na variável linha1


Linguagem C – compilador XC8

char linha1[16];
contador = 15;

sprintf(linha1, “Contagem: %i”, contador);

// Grava o texto ‘Contagem: 15’ na variável linha1


// %i imprime um número inteiro
Linguagem C – compilador XC8

char linha1[16];
contador = 15;
sprintf(linha1, “Contagem: %3.2i”, contador);

// Grava o texto ‘Contagem: 15.00’ na variável linha1


// %X.Yi imprime um número inteiro com X casas fixas
// antes do separador decimal e Y fixas casas depois
Linguagem C – compilador XC8

char linha1[16];
temperatura = 37.52;

sprintf(linha1, “Graus: %2.2f”, temperatura);

// Grava o texto ‘Graus: 37.52’ na variável linha1


// %f imprime um número de ponto flutuante
Linguagem C – compilador XC8

char linha1[16];
caractere_U = 0x55;

sprintf(linha1, “Letra U: %c”, caractere_U);

// Grava o texto ‘Letra U: U’ na variável linha1


// %c imprime um caractere correspondente à tabela
// ASCII
Linguagem C – compilador XC8
Definindo bits de Configuração:

O símbolo “#” precedido da configuração desejada é uma diretiva de


programa, que indica ao Compilador a ação a ser tomada antes da
execução do código do programa.
As 3 principais diretivas utilizadas nos exemplos são:
#include - inclui bibliotecas padrões e do usuário

#define - define constantes e variáveis antes da


execução do programa

#pragma config - define configurações em uma área


específica da memória flash, fora do código do programa
principal
Linguagem C – compilador XC8
Bits de Configuração essenciais (incluídos com #pragma config):

 FOSC: // Frequência do oscilador


Define a origem do oscilador principal do microcontrolador.
Mais usados:

#pragma config FOSC = INTIO; (oscilador interno)


#pragma config FOSC = XT; (cristal externo)
#pragma config FOSC = HS; (cristal externo rápido – High Speed)
Linguagem C – compilador XC8

Bits de Configuração essenciais:


 WDT: // No PIC18F4550

Watchdog Timer Enable. Habilita o reset automático do Watchdog


Timer. Caso o comando ClrWdt() não seja executado num dado
número de instruções, o microcontrolador será ressetado:

#pragma config WDT = OFF; // desabilita watchdog timer


#pragma config WDTPS = 32768;
Linguagem C – compilador XC8

Bits de Configuração essenciais:


 MCLRE:

Master Clear Enable. Habilita ou desabilita o pino de reset no


microcontrolador.

#pragma config MCLRE = OFF;


Linguagem C – compilador XC8

Bits de Configuração não tão essenciais (podem ficar no valor


padrão):
 PWRT:
Aguarda um tempo depois de ligar para iniciar o programa.
Habilitá-lo evita instabilidade no programa devido a oscilações na
alimentação e oscilador:
#pragma config PWRT = ON;
Linguagem C – compilador XC8

Bits de Configuração não tão essenciais (podem ficar no valor


padrão):
 BOREN:
Brown-out reset enable. Habilita o reset automático em caso de
baixa tensão de alimentação:
#pragma config BOREN = SBORDIS;
Linguagem C – compilador XC8
Bits de Configuração essenciais:
 PBADEN:

Habilita ou desabilita o conversor Analógico-Digital na porta B. Caso


for utilizar interrupção na porta B ou usá-la como entrada/saída digital,
este deve estar desabilitado. Por padrão é habilitado:

#pragma config PBADEN = OFF;


Linguagem C – compilador XC8

Registradores essenciais:
 OSCCON: Byte que define a frequência do oscilador interno do
PIC18F45K20:
OSCCON=0b01110000; // Frequência: 16 MHz
OSCCON=0b01100000; // Frequência: 8 MHz
OSCCON=0b01010000; // Frequência: 4 MHz
OSCCON=0b00110000; // Frequência: 1 MHz (padrão)
Linguagem C – compilador XC8
Registradores essenciais:
 OSCCON: Byte que define a frequência do oscilador interno do
PIC18F4550:
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
IDLEN IRCF2 IRCF1 IRCF0 OSTS IOFS SCS1 SCS0
Bits de seleção da
frequência

OSCCON=0b01110000; // Frequência: 8 MHz


OSCCON=0b01100000; // Frequência: 4 MHz
OSCCON=0b01010000; // Frequência: 2 MHz
OSCCON=0b01000000; // Frequência: 1 MHz (padrão)
OSCCON=0b00110000; // Frequência: 500 kHz
OSCCON=0b00100000; // Frequência: 250 kHz
Exemplos
Gerais
EXEMPLO – PISCAR LED

Inicio

Inverte sinal do pino


Configuração D0
Atrasa 100 ms
EXEMPLO – PISCAR LED
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz

#include <xc.h> // Biblioteca do compilador xc8

#pragma config FOSC = INTOSC // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void main(void) {
OSCCON = 0b01100000; // Define frequência do oscilador para 4MHz
TRISD = 0b00000000; // Habilita porta D como saída

while(1) { // Inicia loop infinito


LATDbits.LATD0 = !LATDbits.LATD0; // Inverte sinal do pino D0
__delay_ms(100); // Atraso de 100 ms
}
}

Fim de Código
EXEMPLO – PISCAR LED (VERSÃO 2)
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz
#define Led LATDbits.LATD0

#include <xc.h> // Biblioteca do compilador xc8

#pragma config FOSC = HS // Oscilador externo


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void main(void) {

TRISD = 0b00000000; // Habilita porta D como saída

while(1) { // Inicia loop infinito


Led = !Led; // Inverte sinal do pino Led
__delay_ms(100); // Atraso de 100 ms
}
}

Fim de Código
EXEMPLO – PISCAR LED – 1 SEGUNDO

Inicio

Configuração Inverte sinal do pino D0 Atrasa 100 vezes 10 ms


EXEMPLO – PISCAR LED – 1 SEGUNDO
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz

#include <xc.h>

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void SuperDelay(long counter) { // Função com valor de entrada “counter”


counter = counter / 10; // Divide o valor informado por 10
for (long i = 1; i <= counter; i++) { // E usa o resultado como base
__delay_ms(10); // Para repetir uma contagem de 10 ms
}
}

void main(void) {
OSCCON = 0b01100000; // Define velocidade do oscilador para 4MHz
TRISD = 0b00000000; // Habilita porta D como saída
EXEMPLO – PISCAR LED – 1 SEGUNDO

while(1) { // Inicia loop infinito


LATDbits.LATD0 = !LATDbits.LATD0; // Inverte sinal do pino D0
SuperDelay(1000); // Atraso de 1 s
}

Fim de Código
EXEMPLO 1 DE ROTAÇÃO DE LEDS
Inicio

Rotaciona 1 passo
Configuração
para a esquerda

não sim
PORTD=0? LATD = 1
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz
#include <xc.h>
#pragma config FOSC = HS // Cristal oscilador externo (clock externo)
#pragma config WDT= OFF // Watchdog Timer desligado
#pragma config MCLRE = ON // Define pino 1 como Reset

void SuperDelay(long counter) { // Função com valor de entrada ?counter?


counter = counter / 10; // Divide o valor informado por 10
for (long i = 1; i <= counter; i++) { // E usa o resultado como base
__delay_ms(10); // Para repetir uma contagem de 10 ms
}
}
void main(void) {
TRISD = 0; // Habilita porta D como saída
LATD = 0b00000001; // Liga o Led do pino 0 da porta D
while(1)
{
LATD = LATD << 1; // Rotacionando para a esquerda
SuperDelay(500);

if (PORTD == 0)
{
LATD = 1;
SuperDelay(500);
}
}
return; Fim de Código
}
EXEMPLO 2 DE ROTAÇÃO DE LEDS
Inicio

Rotaciona 1 passo
Configuração
para a esquerda

não sim
PORTD=0? LATD = 1
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz

#include <xc.h>

#pragma config FOSC = HS // Cristal oscilador externo (clock externo)


#pragma config WDT= OFF // Watchdog Timer desligado
#pragma config MCLRE = ON // Define pino 1 como Reset

void SuperDelay(long counter) { // Função com valor de entrada


?counter?
counter = counter / 10; // Divide o valor informado por 10
for (long i = 1; i <= counter; i++) { // E usa o resultado como base
__delay_ms(10); // Para repetir uma contagem de 10 ms
}

void main(void) {

TRISD = 0; // Habilita porta D como saída


LATD = 0b00000001; // Liga o primeiro pino da porta D
SuperDelay(500);
while(1)
{
while(LATD != 0b10000000)
{
LATD = LATD << 1; // Rotacionando para a esquerda
SuperDelay(500);
}
while(LATD != 1)
{
LATD = LATD >> 1; // Rotacionando para a direita
SuperDelay(500);
}
}
return;
}

Fim de Código
EXEMPLO 3 – ROTACIONAR LED
Inicio

sim LED aceso não


Rotaciona para a
Configuração na borda
esquerda
direita?

LED aceso
não na borda sim Rotaciona para a
esquerda direita
?
EXEMPLO – ROTACIONAR LED
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz
#include <xc.h>
#pragma config FOSC = INTIO // Oscilador interno
#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void main(void) {
TRISA = 0b00000000; // Habilita porta A como saída
LATA = 1; // Liga o primeiro pino da porta A
while(1) { // Inicia loop infinito
while(LATA != 0b00000001) {
LATA = (LATA >> 1 | LATA << 7); // Rotacionando com estilo pra esquerda
__delay_ms(100); // Atraso de 100 ms
}
while(LATA != 0b10000000) {
LATA = (LATA << 1 | LATA >> 7); // Rotacionando com estilo pra direita
__delay_ms(100); // Atraso de 100 ms
}
}
}
Fim de Código
EXEMPLO – ROTACIONAR LED - EXPLICAÇÃO
Digamos que LATA = 0b00000001

LATA >> 1 retorna o seguinte valor: 0b00000000, pois rotacionou o “1” para a direita e ele
caiu fora dos 8 bits. O oitavo bit é preenchido com 0.

LATA << 7 retorna o seguinte valor: 0b10000000, pois rotacionou o “1” um total de sete
bits para a esquerda e ele ficou no lugar do oitavo bit. Os 7 primeiros bits são
preenchidos com 0.

Fazendo a operação OU entre ambos, temos (LATA >> 1 | LATA << 7) = 0b10000000; Continuemos
com LATA = 0b10000000

LATA >> 1 retorna o seguinte valor: 0b01000000, pois rotacionou o “1” para a direita e ele
caiu no lugar do sétimo bit. O oitavo bit é preenchido com 0.

LATA << 7 retorna o seguinte valor: 0b00000000, pois rotacionou o “1” um total de sete
bits para a esquerda e ele saiu do espaço dos bits. Os 7 primeiros bits são preenchidos
com 0.

Fazendo a operação OU entre ambos, temos (LATA >> 1 | LATA << 7) = 0b01000000;
Display LCD
EXEMPLO – LCD
Inicio

Configuração Adiciona 1 em
contador

Atualiza LCD com


valor de contador
EXEMPLO – LCD
#define _XTAL_FREQ 1000000

#include <xc.h>
Conexão da Porta D #define RS LATD2 // < Pinos do LCD
no LCD #define EN LATD3
Os pinos D0, D1, D2 #define D4 LATD4
e D3 do LCD são #define D5 LATD5
conectados ao Terra #define D6 LATD6
#define D7 LATD7 // Pinos do LCD >

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado
Biblioteca local do
#include "lcd.h"
LCD #include <stdio.h>
EXEMPLO – LCD
char linha1[16]; // Variável linha1 com 16 caracteres
char linha2[16]; // Variável linha2 com 16 caracteres
int contador = 0; // Variável contador com valor inicial 0

void main(void) {
TRISD = 0; // Define porta D inteira como saída

Lcd_Init(); // Inicia o LCD


sprintf(linha1, "Hello world! "); // Grava texto em linha1
Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1
Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD

while(1) {
sprintf(linha2, "Contador: %i ",contador); // Grava texto em linha2
contador ++; // Incrementa contador
Lcd_Set_Cursor(2,1); // Posiciona o cursor na linha 2, caractere 1
Lcd_Write_String(linha2); // Escreve texto de linha2 no LCD
}
}

Fim de Código
EXEMPLO – LCD + CONTADOR FLOAT
Inicio

Adiciona 0.01 em
Configuração
contador

Atualiza LCD com


valor de contador
EXEMPLO – LCD + CONTADOR FLOAT

#define _XTAL_FREQ 1000000

#include <xc.h>

#define RS LATD2 // < Pinos do LCD


#define EN LATD3
#define D4 LATD4
#define D5 LATD5
#define D6 LATD6
#define D7 LATD7 // Pinos do LCD >

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

#include "lcd.h"
#include <stdio.h>
EXEMPLO – LCD + CONTADOR FLOAT
char linha1[16]; // Variável linha1 com 16 caracteres
char linha2[16]; // Variável linha2 com 16 caracteres
float contador = 0.0; // Variável contador com valor inicial 0.0

void main(void) {
TRISD = 0; // Define porta D inteira como saída

Lcd_Init(); // Inicia o LCD


sprintf(linha1, "Hello world! "); // Grava texto em linha1
Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1
Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD

while(1) {
sprintf(linha2, "Contador: %3.2f",contador); // Grava texto em linha2
contador = contador + 0.01; // Incrementa contador em 0.01
Lcd_Set_Cursor(2,1); // Posiciona o cursor na linha 2, caractere 1
Lcd_Write_String(linha2); // Escreve texto de linha2 no LCD
}
}

Fim de Código
Interrupções
Linguagem C – compilador XC8
Registradores importantes - interrupção:
 GIE: bit que habilita a interrupção global:
GIE = 1; // Habilita interrupção

 PBIE: bit que habilita a interrupção de periféricos (timer2, adc):


PEIE = 1; // Habilita interrupção de periféricos

 INTXIE: bit que habilita a interrupção externa X (X = 0, 1 ou 2):


INT0IE = 1; // Habilita interrupção externa 0
INT1IE = 1; // Habilita interrupção externa 1
INT2IE = 1; // Habilita interrupção externa 2
Linguagem C – compilador XC8
Registradores importantes - interrupção:

 ADIF: bit que habilita a interrupção do conversor AD:

ADIF = 1; // Habilita interrupção do ADC

 TXIE: bit que habilita a interrupção de transmissão da serial:

TXIE = 1; // Habilita interrupção do TX da serial

 RCIE: bit que habilita a interrupção de recepção da serial:

RCIE = 1; // Habilita interrupção do RX da serial


Linguagem C – compilador XC8
Registradores importantes - interrupção:

 TMRXIE: bit que habilita a interrupção do timer X


(X pode ser 0, 1, 2 ou 3):

TMR0IE = 1; // Habilita interrupção do TMR0


TMR1IE = 1; // Habilita interrupção do TMR1
TMR2IE = 1; // Habilita interrupção do TMR2
TMR3IE = 1; // Habilita interrupção do TMR3
Linguagem C – compilador XC8
Registradores importantes – interrupção (flags):
 INTXIF: bit que sinaliza a flag da interrupção externa X (X=0, 1, 2):
INT0IF = 0; // Limpa a flag do INT0

 TMRXIF: bit que sinaliza a flag de interrupção do timer X (X=0, 1, 2, 3):


TMR3IF = 0; // Limpa a flag do TMR3

 ADIF: bit que sinaliza a flag de interrupção do ADC:


ADIF = 0; // Limpa a flag do ADC
EXEMPLO – INTERRUPÇÃO (INT0)

Inicio

Aguarda
Configuração
interrupção

não
Interrupção sim
Inverte sinal do LED
ativada?
EXEMPLO – INTERRUPÇÃO (INT0)

#define _XTAL_FREQ 1000000

#include <xc.h>

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado
#pragma config PBADEN = OFF // Conversor AD da porta B desligado

void setupInt(void) {
GIE = 1; // Habilita interrupção global
INT0IE = 1; // Habilita Interrupção da INT0
INT0F = 0; // Zera a Flag de interrupção da INT0
INTEDG0 = 1; // Interrupção por borda crescente.
}

Para usar a interrupção INT0 (Pino RB0, da porta B), deve-se desabilitar o conversor AD dessa porta
EXEMPLO – INTERRUPÇÃO (INT0)
void interrupt interrupcao(void) { // Função de interrupção
if (INT0F) { // Caso a flag da INT0 esteja habilitada
LATAbits.LA0 = !LATAbits.LA0; // Inverte o sinal no pino A0
INT0F = 0; // Desabilita a flag da INT0
}
}

void main(void) {
TRISA = 0x00; // Porta A com todos pinos de saída
TRISB = 0x01; // Somente pino B1 como entrada (INT0)
setupInt(); // Função de inicializar Interrupção
while(1) { // Loop infinito
}
}

// O código acima inverte o sinal no pino A0 a cada pressionar de um botão ligado à INT0

Fim de Código
Conversor
Analógico/Digital

(10 bits)
Características do Conversor Analógico Digital (ADC):
10 bits
13 entradas multiplexadas
Registradores importantes:
Registrador Função
ADRESH Byte superior do resultado
ADRESL Byte inferior do resultado
ADCON0 Registrador de controle 0 – escolha de canais, liga/desliga/inicia conversão
ADCON1 Registrador de controle 1 – tensão de referência / configuração dos pinos
de entrada como analógico ou digital
ADCON2 Registrador de controle 2 – configura a fonte de clock e a taxa de
aquisição
Linguagem C – compilador XC8
Registradores importantes – ADC (PIC18F4550):

ADCON0: Registrador de Controle do ADC


Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
X X CHS3 CHS2 CHS1 CHS0 GO/DONE\ ADON
Bits de seleção do Canal Status da Habilita
Analógico conversão ADC

ADCON0bits.CHS = 0b0000  Seleção do Canal AN0

ADCON0bits.CHS = 0b0001  Seleção do Canal AN1

ADCON0bits.ADON = 1  Liga o ADC

ADCON0bits.GO = 1  Inicia a conversão A/D


Linguagem C – compilador XC8

Registradores importantes – ADC :

 ADCON0bits.GO: bit que inicia a conversão analógica:


ADCON0bits.GO = 1; // Inicia a conversão AD

 ADCON0bits.DONE: flag que sinaliza o fim da conversão analógica:


while (!ADCON0bits.DONE) {
} // Aguarda finalização da conversão AD
Linguagem C – compilador XC8

Registradores importantes – ADC (PIC18F4550):

ADCON1: Registrador de Controle do ADC

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0


X X VCFG1 VCFG0 PCFG3 PCFG2 PCFG1 PCFG0
Bits de
configuração
da tensão de
referência

ADCON1bits.VCFG = 0b00;  Tensões de referência: Vss e Vdd


Linguagem C – compilador XC8
Registradores importantes – ADC (PIC18F4550):
ADCON2: Registrador de Controle do ADC

Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0


ADFM - ACQT2 ACQT1 ACQT0 ADCS2 ADCS1 ADCS0
Formato do Bits de seleção do Tempo de Bits de seleção do Clock de
resultado Aquisição de dados conversão

ADCON2bits.ADCS = 0b110  Clock do AD: Fosc/64

ADCON2bits.ACQT = 0b010  Tempo de aquisição: 4 TAD

ADCON2bits.ADFM = 0b1  Formato do resultado: justificado à direita


Linguagem C – compilador XC8
Registradores importantes – ADC :

 ADRESL: byte que guarda os 8 bits menos significativos da


conversão AD:

 ADRESH: byte que guarda os 8 bits mais significativos da


conversão AD:

valor_convertido = (ADRESH * 0x0100) + ADRESL;


// guarda o valor da conversão AD na variável
// de 16 bits “valor_convertido”
EXEMPLO – CONVERSOR ANALÓGICO-
DIGITAL
Inicio

Inicia leitura da
Configuração
tensão no pino A0

não sim Grava valor da


Finalizou
leitura nos bits da
leitura?
porta C e D
EXEMPLO – CONVERSOR ANALÓGICO-
DIGITAL
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz

#include <xc.h>

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void main(void) {
OSCCON = 0b01100000; // Define velocidade do oscilador para 4MHz
TRISD = 0b00000000; // Habilita porta D como saída
TRISC = 0b00000000; // Habilita porta C como saída

TRISA = 0x00000001; // Habilita pino A0 como entrada

ADCON2 = 0b10010110; // Tempo Aquisição: 4TAD; Clock: Fosc/64


ADCON1 = 0b00000000; // Tensões de referência: Vss e Vdd

ADCON0bits.CHS = 0b0000; // Seleciona o canal AN0


EXEMPLO – CONVERSOR ANALÓGICO-DIGITAL

ADCON0bits.ADON = 1; // Habilita o conversor AD

while(1) { // Inicia loop infinito


ADCON0bits.GO = 1; // Inicia a conversão
while (!ADCON0bits.GODONE) { // Aguarda fim da conversão
}
LATD = ADRESL; // Transfere valor para porta D
LATC = ADRESH; // Transfere valor para porta C
__delay_ms(100); // Atraso de 100 ms
}

Fim de Código
EXEMPLO – ADC + LCD

Inicio

Inicia leitura da
Configuração
tensão no pino A0

não sim Calcula tensão no pino e


Finalizou
exibe valor lido e tensão
leitura?
calculada no LCD
EXEMPLO – ADC + LCD
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz

#include <xc.h>

#define RS LATD2 // < Pinos do LCD


#define EN LATD3
#define D4 LATD4
#define D5 LATD5
#define D6 LATD6
#define D7 LATD7 // Pinos do LCD >

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

#include "lcd.h"
#include <stdio.h>

char linha1[16]; // Variável linha1 com 16 caracteres


char linha2[16]; // Variável linha2 com 16 caracteres
EXEMPLO – ADC + LCD
int contador = 0; // Variável contador com valor inicial 0
float tensao = 0.0; // Variável tensao com valor inicial 0.0

void setupADC(void) {
TRISA = 0b00000001; // Habilita pino A0 como entrada

ADCON2bits.ADCS = 0b110; // Clock do AD: Fosc/64


ADCON2bits.ACQT = 0b010; // Tempo de aquisição: 4 Tad
ADCON2bits.ADFM = 0b1; // Formato: à direita
ADCON1bits.VCFG = 0b00; // Tensões de referência: Vss e Vdd

ADCON0bits.CHS = 0b0000; // Seleciona o canal AN0

ADCON0bits.ADON = 1; // Liga o AD
}

void main(void) {
OSCCON = 0b01100000; // Define velocidade do oscilador para 4MHz
TRISD = 0b00000000; // Habilita porta D como saída
EXEMPLO – ADC + LCD
setupADC();

Lcd_Init(); // Inicia o LCD

while(1) { // Inicia loop infinito


ADCON0bits.GO = 1; // Inicia a conversão A/D
while (!ADCON0bits.GODONE) { // Aguarda fim da conversão
}
contador = (ADRESH * 0x100) + ADRESL; // Transfere valor para variável
tensao = ((5 * contador) * 0.0009765625); // Calcula tensão real
sprintf(linha1, "Conversor: %4i ", contador); // Grava texto em linha1
sprintf(linha2, "Tensao: %1.2f ",tensao); // Grava texto em linha2
Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1
Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD
Lcd_Set_Cursor(2,1); // Posiciona o cursor na linha 2, caractere 1
Lcd_Write_String(linha2); // Escreve texto de linha2 no LCD
}

Fim de Código
EXEMPLO – ADC + LCD + DOIS CANAIS
Inicio

Lê tensão no pino Atualiza LCD com os


Configuração
A0 e guarda valores lidos

Lê tensão no pino
A1 e guarda
EXEMPLO – ADC + LCD + DOIS CANAIS
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz

#include <xc.h>

#define RS LATD2 // < Pinos do LCD


#define EN LATD3
#define D4 LATD4
#define D5 LATD5
#define D6 LATD6
#define D7 LATD7 // Pinos do LCD >

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

#include "lcd.h"
#include <stdio.h>

char linha1[16]; // Variável linha1 com 16 caracteres


char linha2[16]; // Variável linha2 com 16 caracteres
EXEMPLO – ADC + LCD + DOIS CANAIS

int contador = 0; // Variável contador com valor inicial 0


float tensao1 = 0.0; // Variável tensao com valor inicial 0.0
float tensao2 = 0.0; // Variável tensao com valor inicial 0.0

void setupADC(void) {
TRISA = 0b00000011; // Habilita pinos A0 e A1 como entrada

ADCON2bits.ADCS = 0b110; // Clock do AD: Fosc/64


ADCON2bits.ACQT = 0b010; // Tempo de aquisição: 4 Tad
ADCON2bits.ADFM = 0b1; // Formato: à direita
ADCON1bits.VCFG = 0b00; // Tensões de referência: Vss e Vdd

ADCON0bits.ADON = 1; // Liga o circuito AD


}

void main(void) {
OSCCON = 0b01100000; // Define velocidade do oscilador para 4MHz
EXEMPLO – ADC + LCD + DOIS CANAIS
TRISD = 0b00000000; // Habilita porta D como saída

setupADC();
Lcd_Init(); // Inicia o LCD

while(1) { // Inicia loop infinito


ADCON0bits.CHS = 0b0000; // Seleciona canal AN0
ADCON0bits.GO = 1; // Inicia a conversão
while (!ADCON0bits.GODONE) { // Aguarda fim da conversão
}
contador = (ADRESH * 0x100) + ADRESL; // Transfere valor para variável
tensao1 = ((5 * contador)/1023.0); // Calcula tensão real

ADCON0bits.CHS = 0b0001; // Seleciona canal AN1


ADCON0bits.GO = 1; // Inicia a conversão
while (!ADCON0bits.GODONE) { // Aguarda fim da conversão
}
contador = (ADRESH * 0x100) + ADRESL; // Transfere valor para variável
tensao2 = ((5 * contador)/1.023.0); // Calcula tensão real
EXEMPLO – ADC + LCD + DOIS CANAIS

sprintf(linha1, "Tensao 1: %1.2f ",tensao1); // Grava texto em linha1


sprintf(linha2, "Tensao 2: %1.2f ",tensao2); // Grava texto em linha2

Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1


Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD

Lcd_Set_Cursor(2,1); // Posiciona o cursor na linha 2, caractere 1


Lcd_Write_String(linha2); // Escreve texto de linha2 no LCD
}

Fim de Código
EXEMPLO – ADC + LCD + 4 CANAIS

Início

Atualiza LCD
(chama rotina que
Configuração lê tensão nos pinos
A0 a A4
automaticamente)
EXEMPLO – ADC + LCD + 4 CANAIS
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz

#include <xc.h>

#define RS LATD2 // < Pinos do LCD


#define EN LATD3
#define D4 LATD4
#define D5 LATD5
#define D6 LATD6
#define D7 LATD7 // Pinos do LCD >

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

#include "lcd.h"
#include <stdio.h>

char linha1[16]; // Variável linha1 com 16 caracteres


char linha2[16]; // Variável linha2 com 16 caracteres
EXEMPLO – ADC + LCD + 4 CANAIS
Seleção do clock
do AD

void setupADC(void) {

TRISA = 0b00001111; // Habilita pinos A0 a A3 como entrada

ADCON2bits.ADCS = 0b110; // Clock do AD: Fosc/64


ADCON2bits.ACQT = 0b110; // Tempo de aquisição automático: 16 Tad
ADCON2bits.ADFM = 0b1; // Formato: à direita
ADCON1bits.VCFG = 0b00; // Tensões de referência: Vss e Vdd

ADCON0bits.ADON = 1; // Liga o circuito AD


}

float leTensao(int canal_adc) {


ADCON0bits.CHS = canal_adc; // Seleciona canal
ADCON0bits.GO = 1; // Inicia a conversão
while (!ADCON0bits.GODONE) { // Aguarda fim da conversão
}
int contador = (ADRESH * 0x100) + ADRESL; // Transfere valor para variável
EXEMPLO – ADC + LCD + 4 CANAIS
float tensao = ((5 * contador)/1023.0); // Calcula tensão real
return tensao;
}

void main(void) {
OSCCON = 0b01100000; // Define velocidade do oscilador para 4MHz
TRISD = 0b00000000; // Habilita porta D como saída
setupADC();
Lcd_Init(); // Inicia o LCD
while(1) { // Inicia loop infinito
sprintf(linha1, "T0: %1.1f T1: %1.1f", leTensao(0), leTensao(1)); //Grava texto em linha1
sprintf(linha2, "T2: %1.1f T3: %1.1f", leTensao(2), leTensao(3)); //Grava texto em linha2

Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1


Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD
Lcd_Set_Cursor(2,1); // Posiciona o cursor na linha 2, caractere 1
Lcd_Write_String(linha2); // Escreve texto de linha2 no LCD
}
}

Fim de Código
EXEMPLO – ADC + LCD + 8 CANAIS + INT
Inicio

Atualiza LCD
com as variáveis
Configuração
tensão[0] a tensão[7];
(x = 0)
Inicia leitura do pino
ANx

Atualiza não
variável
Leitura tensão[x] com xé
finalizada o valor da maior
não ? sim tensão no pino que 7?
Ax;
Incrementa x sim
x=0
EXEMPLO – ADC + LCD + 8 CANAIS + INT
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz

#include <xc.h>

#define RS LATD2 // < Pinos do LCD


#define EN LATD3
#define D4 LATD4
#define D5 LATD5
#define D6 LATD6
#define D7 LATD7 // Pinos do LCD >

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

#include "lcd.h"
#include <stdio.h>

char linha1[16]; // Variável linha1 com 16 caracteres


char linha2[16]; // Variável linha2 com 16 caracteres
EXEMPLO – ADC + LCD + 8 CANAIS + INT
int canal = 0; // Variável que diz qual canal é lido atualmente
float tensao[8]; // Vetor que guarda a tensão em cada um dos canais
bit atualizado; // Flag que indica se todos canais já foram lidos

void setupADC(void) {
TRISA = 0b00101111; // Habilita pinos A0 a A3 e A5 como entrada
TRISE = 0b00000111; // Habilita pinos E0 a E2 como entrada
// São os pinos relativos a AN0 a AN7

ADCON2bits.ADCS = 0b110; // Clock do AD: Fosc/64


ADCON2bits.ACQT = 0b110; // Tempo de aquisição automático: 16 Tad
ADCON2bits.ADFM = 0b1; // Formato: à direita
ADCON1bits.VCFG = 0b00; // Tensões de referência: Vss e Vdd

ADCON0bits.ADON = 1; // Liga o circuito AD

}
EXEMPLO – ADC + LCD + 8 CANAIS + INT
void setupInterrupcao(void) {
GIE = 1; // Habilita interrupção global
PEIE = 1; // ADC exige interrupção de periféricos habilitada
ADIE = 1; // Liga interrupção pelo AD
}

void interrupt adc_interrupt(void) {


if (ADIF) {
int contador = (ADRESH * 0x100) + ADRESL; // Transfere valor para variável
tensao[canal] = ((5 * contador)/1023.0); // Calcula tensão real
if (canal == 7) { // Verificação para alternar
canal = 0; // o canal lido a cada interrupcao
ADCON0bits.CHS = canal; // Seleciona canal
atualizado = 1; // Marca a flag caso ja leu os 4 canais
} else {
canal++; // Atualiza o canal
ADCON0bits.CHS = canal; // Seleciona canal
ADCON0bits.GO = 1; // Inicia a conversão
}
ADIF = 0; // Desmarca flag da interrupção ADC
EXEMPLO – ADC + LCD + 8 CANAIS + INT
}
}

void main(void) {
OSCCON = 0b01010000; // Define velocidade do oscilador para 4MHz
TRISD = 0b00000000; // Habilita porta D como saída
Lcd_Init(); // Inicia o LCD
setupADC(); // Configura o ADC
setupInterrupcao(); // Configura a interrupção
atualizado = 0; // Marca a flag para atualizar os 8 canais
ADCON0bits.CHS = canal; // Seleciona canal
ADCON0bits.GO = 1; // Inicia a conversão

while(1) { // Inicia loop infinito


sprintf(linha1, "1:%1.0f 2:%1.0f 3:%1.0f 4:%1.0f",
tensao[0], tensao[1], tensao[2], tensao[3]); // Grava texto em linha1
sprintf(linha2, "5:%1.0f 6:%1.0f 7:%1.0f 8:%1.0f",
tensao[4], tensao[5], tensao[6], tensao[7]); // Grava texto em linha2
EXEMPLO – ADC + LCD + 8 CANAIS + INT

Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1


Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD
Lcd_Set_Cursor(2,1); // Posiciona o cursor na linha 2, caractere 1
Lcd_Write_String(linha2); // Escreve texto de linha2 no LCD
atualizado = 0; // Marca a flag para atualizar os 4 canais
ADCON0bits.GO = 1; // Inicia a conversão
}
}

Fim de Código
TEMPORIZADORES/
CONTADORES
Timer 0 configurado para 8 bits
(T08BIT = 1) ou 16 bits (T08BIT = 0)

Escolha entre temporizador (T0CS = 0)


ou contador (T0CS = 1)

No modo Contador, o Timer 0


incrementa seu registrador interno na
transição de 0 para 1 no pino RA4
(T0CKI), se T0SE = 0. Se T0SE = 1, o
incremento é na transição de 1 para
0
EXEMPLO – TEMPORIZADOR 0

Inicio

Configuração
(temporizador
Aguarda
configurado para
interrupção
gerar interrupção
a cada 50 ms)

não Interrupção sim


Inverte sinal do LED
ativada?
EXEMPLO – TEMPORIZADOR 0
#define _XTAL_FREQ 4000000 // Oscilador a 4 MHz. O número de instruções por
// segundo é de 1 milhão. O tempo para executar uma
#include <xc.h> // instrução (e do tick do timer) é de 1 us.

#pragma config FOSC = HS // Oscilador externo


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void setupInt(void) {
GIE = 1; // Habilita interrupção global
TMR0IE = 1; // interrupção do Timer 0
}

void setupTmr0() {
T08BIT = 0; // Modo 16 bits
T0CS = 0; // Source do clock (operando como temporizador, e não como contador
PSA = 1; // Desabilita Prescaler
TMR0H = 0x3C; // Começa a contar de 15535
TMR0L = 0xAF; // até 65535 (conta 50 mil vezes)
TMR0ON = 1; // Liga o timer
}
EXEMPLO – TEMPORIZADOR 0
void interrupt interrupcao(void) { // Função de interrupção
if (TMR0IF) { // Caso a flag do temporizador esteja ativa
LATDbits.LD0 = !LATDbits.LD0; // Inverte pino D0
TMR0H = 0x3C; // Começa a contar de 15535
TMR0L = 0xAF; // até 65535 (conta 50 mil vezes)
TMR0IF = 0; // Flag do timer 0 em 0
}
}

void main(void) {
TRISD = 0x00; // Porta D como saída
setupInt(); // Função de habilitar interrupção
setupTmr0(); // Função de configurar timer 0
while(1) { // Loop infinito
}
}
// O código acima inverte o sinal do pino D0 a cada 50000 us, via temporizador 0.

Fim de Código
EXEMPLO – TEMPORIZADOR 0 + PRESCALER
Inicio

Configuração
(temporizador
Aguarda
configurado para
interrupção
gerar interrupção
a cada 1s)

Interrupção
Inverte sinal do LED
ativada? sim
não
EXEMPLO – TEMPORIZADOR 0 +
PRESCALER
#define _XTAL_FREQ 4000000 // Oscilador a 4 MHz. O número de instruções por
// segundo é de 1 milhão. O tempo para executar uma
#include <xc.h> // instrução (e do tick do timer) é de 1 us.

#pragma config FOSC = HS // Oscilador externo


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void setupInt(void) {
GIE = 1; // Habilita interrupção global
TMR0IE = 1; // interrupção do Timer 0
}

void setupTmr0() {
T08BIT = 0; // Modo 16 bits
T0CS = 0; // Fonte do clock = interna
PSA = 0; // Habilita Prescaler
T0CONbits.T0PS = 0b100; // Multiplicador Prescaler: 32 x 31.250us = 1 s
EXEMPLO – TEMPORIZADOR 0 + PRESCALER PIC16F4550
TMR0H = 0x85; // Começa a contar de 34285
TMR0L = 0xED; // até 65535 (conta 31250 vezes)
TMR0ON = 1; // Liga o timer
}

void interrupt interrupcao(void) { // Função de interrupção


if (TMR0IF) { // Caso a flag do temporizador esteja ativa
LATDbits.LD0 = !LATDbits.LD0; // Inverte pino D0
TMR0H = 0x85; // Começa a contar de 34285
TMR0L = 0xED; // até 65535 (conta 31250 vezes)
TMR0IF = 0; // Flag do timer 0 em 0
}
}
void main(void) {
setupInt(); // Função de habilitar interrupção
setupTmr0(); // Função de configurar timer 0
TRISD = 0x00; // Porta D como saída
while(1) { // Loop infinito
}
}
// O código acima inverte o sinal do pino D0 a cada 1 s, via temporizador 0.

Fim de Código
Temporizador 2
Em operação normal, o Timer 2 começa a contar de TMR2 = 0 e, a cada ciclo de
contagem, compara os valores de TMR2 e PR2. Quando os dois valores forem iguais, ele
gera um sinal na saída do temporizador, além de zerar o registrador TMR2 e setar a flag
TMR2IF.
EXEMPLO – TEMPORIZADOR 2
Inicio

Configuração
(temporizador
Aguarda
configurado para
interrupção
gerar interrupção
a cada 10 ms)

Interrupção
Inverte sinal do LED
ativada? sim
não
EXEMPLO – TEMPORIZADOR 2
#define _XTAL_FREQ 4000000 // Oscilador a 4 MHz. O número de instruções por
// segundo é de 1 milhão. O tempo para executar uma
#include <xc.h> // instrução (e do tick do timer) é de 1 us.
#pragma config FOSC = HS // Oscilador Externo
#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void setupInt(void) {
GIE = 1; // Habilita interrupção global
PEIE = 1; // Timer 2 exige interrupção de periféricos habilitada
TMR2IE = 1; // interrupção do Timer 2
}

void setupTmr2() {
T2CKPS0 = 1; // Prescaler x 4
T2CKPS1 = 0; //
T2OUTPS0 = 0; // Postscaler x 10
T2OUTPS1 = 1; //
T2OUTPS2 = 0; // Conta 250 (PR2, abaixo) x 4 (prescaler) x 10 (postscaler) vezes
T2OUTPS3 = 1; // totalizando 10000 vezes (~10 ms) por interrupção
EXEMPLO – TEMPORIZADOR 2
TMR2 = 0x00; // Começa a contar de 0
PR2 = 249; // até 249 (conta 250 vezes + recarga automatica)
TMR2ON = 1; // Liga o timer
}

void interrupt interrupcao(void) { // Função de interrupção


if (TMR2IF) { // Caso a flag do temporizador esteja ativa
LATAbits.LD0 = !LATAbits.LD0; // Inverte pino D0
TMR2IF = 0; // Flag do timer 2 em 0
}
}

void main(void) {
setupInt(); // Função de habilitar interrupção
setupTmr2(); // Função de configurar timer 0
TRISD = 0x00; // Porta D como saída
while(1) { // Loop infinito
}
} // O código acima inverte o valor do pino D0 a cada 10 ms usando o Timer 2.

Fim de Código
EXEMPLO – ADC + LCD + TIMER
Inicio

Configuração
Atualiza LCD com o
(configura timer
valor da variável
para interromper
“tensão” e “contador”
a cada 10 ms)

não Interrupção sim Grava tensão do pino


do conversor AN0 na variável “tensão”;
AD? Incrementa “contador”;

não sim
Interrupção Inicia leitura no
do timer? conversor AD
EXEMPLO – ADC + LCD + TIMER
#define _XTAL_FREQ 4000000

#define RS LATD2 // < Pinos do LCD


#define EN LATD3
#define D4 LATD4
#define D5 LATD5
#define D6 LATD6
#define D7 LATD7 // Pinos do LCD >

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

#include <xc.h>
#include "lcd.h"
#include <stdio.h>

char linha1[16]; // Variável linha1 com 16 caracteres


char linha2[16]; // Variável linha2 com 16 caracteres
int contador = 0; // Variável contador com valor inicial 0
EXEMPLO – ADC + LCD + TIMER
float tensao = 0.0; // Variável que guarda a tensão lida no conversor AD
long contagem = 10000; // Variável que define quantos us serão contados a cada conversão

void interrupt interrupcao() {


if (ADIF) {
int leitura_adc = (ADRESH * 0x100) + ADRESL; // Transfere valor para variável
tensao = ((5 * leitura_adc) * 0.0009765625); // Calcula tensão real
contador++; // Incrementa contador
ADIF = 0; // Desmarca flag da interrupção ADC
}
if (TMR3IF) {
TMR3H = (0xFFFF - contagem) >> 8; // Cálculo do valor inicial do TMR3
TMR3L = ((0xFFFF - contagem) & 0xFF); // Cálculo do valor inicial do TMR3
LATDbits.LD0 = !LATDbits.LD0; // Inverte sinal no pino D0
TMR3IF = 0; // Limpa a Flag da interrupção
ADCON0bits.GO = 1; // Inicia conversao AD
}
}
EXEMPLO – ADC + LCD + TIMER
void setupTmr3() {
T3CKPS0 = 0; // Prescaler
T3CKPS1 = 0; // Prescaler
TMR3CS = 0; // Clock origina do clock interno

TMR3H = (0xFFFF - contagem) >> 8; // Cálculo do valor inicial do TMR3


TMR3L = ((0xFFFF - contagem) & 0xFF); // Cálculo do valor inicial do TMR3

TMR3ON = 1; // Liga o timer


}

void setupADC(void) {
TRISA = 0b00000001; // Habilita pino A0 entrada

ADCON2bits.ADCS = 0b1111; // Clock do AD: Fosc/64


ADCON2bits.ACQT = 0b110; // Tempo de aquisição automático: 16 Tad
ADCON2bits.ADFM = 0b1; // Formato: à direita
ADCON1bits.VCFG = 0b00; // Tensões de referência: Vss e Vdd
EXEMPLO – ADC + LCD + TIMER
ADCON0bits.CHS = 0b0000; // Seleciona canal AN0

ADCON0bits.ADON = 1; // Liga o circuito AD


}

void setupInt(void) {
GIE = 1; // Habilita interrupção global
PEIE = 1; // Habilita interrupção de periféricos
TMR3IE = 1; // Interrupção do timer 3
ADIE = 1; // Habilita interrupção do ADC
}

void main(void) {
OSCCON = 0b01010000; // Oscilador interno a 4 MHz
TRISA = 1; // A0 como entrada
TRISD = 0; // Define porta D inteira como saída
setupADC(); // Configuração do ADC
setupInt(); // Configuração da Interrupção
EXEMPLO – ADC + LCD + TIMER

setupTmr3(); // Configuração do Timer 3


Lcd_Init(); // Inicia o LCD

while(1) {
sprintf(linha1, "N: %i", contador); // Grava texto em linha1
Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1
Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD
sprintf(linha2, "Tensao: %3.2f", tensao); // Grava texto em linha2
Lcd_Set_Cursor(2,1); // Posiciona o cursor na linha 2, caractere 1
Lcd_Write_String(linha2); // Escreve texto de linha2 no LCD
}
}

Fim de Código
MÓDULOS DE CAPTURE/
COMPARE/PWM
(CCP)
Os módulos de Captura, Comparação e
PWM contém:
 1 registrador de 16 bits que opera como registrador de captura

 1 registrador de 16 bits para comparação ou

 1 registrador Mestre/Escravo para o Duty cycle de PWM


Configuração do Módulo CCP:
 Cada módulo (Captura, Comparação e PWM) está associado a um
registrador de controle (genericamente, CCPxCON) e um registrador
de dados (CCPRx)

 O registrador CCPRx é composto por dois registradores de 8 bits:


CCPRxL, para o byte inferior e CCPRxH, para o byte superior

 A Tabela a seguir mostra os temporizadores associados a cada modo


Modo de Captura:
 No modo de Captura, o par de registradores CCPRxH:CCPRxL captura
o valor de 16 bits dos registradores do Timer 1 ou do Timer 3, quando
ocorre um evento no pino CCPx correspondente

 Um evento é definido como uma das seguintes ocorrências:

 Cada transição decrescente

 Cada transição crescente

 Cada 4ª transição crescente

 Cada 16ª transição crescente


Modo de Comparação:
 No modo de Comparação, o valor do registrador CCPRx é
constantemente comparado com os valores dos pares de
registradores do Timer 1 ou do Timer 3

 Quando ocorre uma equivalência (igualdade), o pino CCPx pode ser:

 Levado ao nível lógico alto

 Levado ao nível lógico baixo

 Inverter o estado do pino (baixo para alto ou alto para baixo)

 Permanecer inalterado
Modo PWM
 No modo PWM (Modulação de
Largura de Pulso), o pino CCPx produz
uma saída PWM com resolução de
até 10 bits.

 Uma vez que o pino CCP2 é


multiplexado com um latch de dados
da Porta B ou da Porta C, o bit TRIS
apropriado deve ser zerado para
fazer o pino CCP2 um pino de saída.
Modo PWM (Período)
 O Período de PWM é definido através do registrador PR2

 O Período de PWM pode ser calculado usando a fórmula:

 A frequência de PWM é o inverso do período (1/PWM)

 Quando TMR2 é igual a PR2, os três eventos seguintes ocorrem no


próximo ciclo crescente:

 TMR2 é zerado

 O pino CCPx é setado (exceto se o duty cycle for 0%)

 O duty cycle do PWM é transferido de CCPRxL para CCPRxH


Modo PWM (Duty Cycle)
 O Duty Cycle do PWM é definido escrevendo-se no registrador
CCPRxL e nos bits 4 e 5 de CCPxCON.

 Uma resolução de até 10 bits está disponível

 O registrador CCPRxL contém dos 8 bits mais significativos e os dois


bits (4 e 5 de CCPxCON) são os bits menos significativos

 O valor de 10 bits é representado por: CCPRxL:CCPxCON<5:4>

 O Duty Cycle pode ser calculado usando a expressão:


Modo PWM
(Passos para a Configuração do PWM)
 Os seguintes passos devem ser executados quando configurando
o módulo CCPx para operação no modo PWM:

o Definir o período de PWM escrevendo no registrador PR2

o Definir o duty cycle escrevendo no registrador CCPRxL e nos bits


CCPxCON<5:4>

o Definir o pino CCPx como saída, através da instrução TRIS

o Definir o valor de pré-escala de TMR2, e então habitar o Timer 2,


escrevendo em T2CON

o Configurar o módulo CCPx para operação no modo PWM


Linguagem C – compilador XC8

Registradores importantes – PWM:


 CCPR2L: byte que define o duty cycle do PWM2:

CCPR2L = 26; // Define PWM com duty-cycle de 10%


CCPR2L = 255; // Define PWM com duty-cycle de 100%
CCPR2L = 128; // Define PWM com duty-cycle de 50%
CCPR2L = 77; // Define PWM com um duty-cycle de 30%
EXEMPLO – PWM

Inicio

Configuração
(configura
. . . É, não faz nada.
temporizador
e PWM)
EXEMPLO – PWM
#define _XTAL_FREQ 4000000

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

#include <xc.h>

void setupTmr2() {
TMR2 = 0x00; // Começa a contar de 0
PR2 = 249; // até 250 (conta 250 vezes + recarga automatica)
}

void setupPWM (void) {


TRISCbits.RC1 = 1; // "desliga" bit de saída
setupTmr2(); // Configura timer 2
CCP2CONbits.CCP2M = 0b1100; // Modo PWM ativo
CCPR2L = 128; // Duty cycle % do PWM (0 - 255), portanto 128 = 50%
EXEMPLO – PWM

TMR2IF = 0; // Limpa flag do TMR2


TMR2ON = 1; // Dispara o timer
TRISCbits.RC1 = 0; // "liga" bit de saída
}

void main(void) {
OSCCON = 0b01100000; // Oscilador interno a 4 MHz
setupPWM();

while(1) {
}
}

// Gera um sinal PWM na saída do pino RC1

Fim de Código
EXEMPLO – PWM + ADC
Inicio

Inicia conversão AD no
Configuração
pino AN0

Atualiza valor do “duty


Interrupção
cycle” do PWM baseado
do conversor
não sim no valor de tensão lido no
AD?
pino AN0
EXEMPLO – PWM + ADC
#define _XTAL_FREQ 4000000

#pragma config FOSC = HS // Oscilador externo High Speed


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

#include <xc.h>

long contagem = 0; // Variável auxiliar

void interrupt interrupcao() {


if (ADIF) {
contagem = (ADRESH * 0x100) + ADRESL; // Transfere a leitura do AD para a
contagem = contagem >> 2; // rotacional 2 posições à direita (divide por 4)
CCPR2L = contagem; // para ajustar aos aos 8 bits do PWM
ADIF = 0; // Desmarca flag da interrupção ADC
}
if (TMR2IF) { // Caso a flag do temporizador esteja ativa,
TMR2IF = 0; // desmarca a mesma
EXEMPLO – PWM + ADC
}
}

void setupTmr2() {
TMR2 = 0x00; // Começa a contar de 0
PR2 = 249; // até 250 (conta 250 vezes + recarga automatica)
}

void setupADC(void) {
TRISA = 0b00000001; // Habilita pino A0entrada

ADCON2bits.ADCS = 0b110; // Clock do AD: Fosc/64


ADCON2bits.ACQT = 0b010; // Tempo de aquisição: 4 Tad
ADCON2bits.ADFM = 0b1; // Formato: à direita
ADCON1bits.VCFG = 0b00; // Tensões de referência: Vss e Vdd

ADCON0bits.CHS = 0b0000; // Seleciona o canal AN0


ADCON0bits.ADON = 1; // Liga o AD
}
EXEMPLO – PWM + ADC
void setupInt(void) {
GIE = 1; // Habilita interrupção global
PEIE = 1; // Habilita interrupção de periféricos
TMR2IE = 1; // Interrupção do timer 2
ADIE = 1; // Habilita interrupção do ADC

void setupPWM (void) {


TRISCbits.RC1 = 1; // "desliga" bit de saída
setupTmr2(); // Configura timer 2
CCP2CONbits.CCP2M = 0b1100; // Modo PWM ativo
CCPR2L = 128; // Configura % do PWM (0 - 255)
TMR2IF = 0; // Limpa flag do TMR2
TMR2ON = 1; // Dispara o timer
EXEMPLO – PWM + ADC
TRISCbits.RC1 = 0; // "liga" bit de saída

void main(void) {
OSCCON = 0b01100000; // Oscilador interno a 4 MHz
TRISD = 0; // Define porta D inteira como saída
LATD = 1; // Acende o primeiro LED da porta D
setupADC(); // Configuração do ADC
setupInt(); // Configuração da Interrupção
setupPWM(); // Configuração do PWM

while(1) {
ADCON0bits.GO = 1; // Lê ADC, para recarregar valor no PWM
while (ADCON0bits.NOT_DONE) {
}
}
}
// Gera um sinal PWM na saída com ciclo variando de acordo com a tensão no pino A0

Fim de Código
TRANSDUTOR DE TEMPERATURA
+ LCD
LM35 + LCD
Inicio

Configuração Leitura do LM35


do ADC e do Inicia conversão AD no
LCD pino AN0

A tensão de referência
do AD é fundamental
porque a tensão máxima Converte leitura do AD
Acabou a
de saída do LM35 é 1,5 V, em temperatura
não conversão? sim
para uma temperatura Mostra valor no LCD
de 150ºC
LM35 + LCD
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz

#define RS LATD2 // < Pinos do LCD


#define EN LATD3
#define D4 LATD4
#define D5 LATD5
#define D6 LATD6
#define D7 LATD7 // Pinos do LCD >

#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include "lcd.h"

#pragma config FOSC = HS // Oscilador externo


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado
LM35 + LCD
char linha1[16]; // Variável linha1 com 16 caracteres
char linha2[16]; // Variável linha2 com 16 caracteres
long contador;
float temperatura;

void setupADC(void) {
TRISA = 0b00000001; // Habilita pino A0 como entrada

ADCON2bits.ADCS = 0b110; // Clock do AD: Fosc/64


ADCON2bits.ACQT = 0b010; // Tempo de aquisição: 4 Tad
ADCON2bits.ADFM = 0b1; // Formato: à direita
ADCON1bits.VCFG = 0b01; // Tensões de referência: Vss e Pino AN3

// ADCON0 = 0; // Seleciona o canal AN0


ADCON0bits.CHS = 0b0001; // Seleciona o canal AN1
ADCON0bits.ADON = 1; // Liga o AD
}

Com essa configuração, a tensão de referência positiva VREF+ para o AD está no pino AN3. Foi utilizada
uma fonte de 1,5 V nesse pino. Assim, a saída máxima do LM35 resulta na saída máxima do AD
LM35 + LCD
void main(void) {
OSCCON = 0b01100000; // Define velocidade do oscilador para 4MHz
TRISD = 0b00000000; // Habilita porta D como saída
setupADC();
Lcd_Init(); // Inicia o LCD

while(1) { // Inicia loop infinito


ADCON0bits.GO = 1; // Inicia a conversão A/D
while (!ADCON0bits.GODONE) { // Aguarda fim da conversão
}
contador = (ADRESH * 0x100) + ADRESL; // Transfere valor para variável
temperatura = ((1.5 * 100 * contador)/1023.0); // Calcula temperatura

sprintf(linha1, "Leitura AD: %4i ", contador); // Grava texto em linha1


sprintf(linha2, "Temperat.: %3.1f ",temperatura); // Grava texto em linha2
Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1
Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD
Lcd_Set_Cursor(2,1); // Posiciona o cursor na linha 2, caractere 1
Lcd_Write_String(linha2); // Escreve texto de linha2 no LCD
}
return; LM35: 10mV/ºC  leitura do AD é convertida para tensão e dividida por 0,01
} (multiplicada por 100)
COMUNICAÇÃO SERIAL
SERIAL EUSART

 O módulo de comunicação serial EUSART (Enhanced Universal Synchronous


Asynchronous Receiver Transmitter) do PIC18F4550 é um dos dois módulos de
comunicação serial. Ele pode ser configurado para operar nos seguintes
modos:
 Asynchronous full duplex com
Auto reativação, com sinal de parada
Calibração automática da taxa baud rate
Transmissão de caracteres de parada de 12 bits

 Synchronous Master (half duplex) com polaridade de clock selecionável


 Synchronous Slave (half duplex) com polaridade de clock selecionável
SERIAL EUSART
 Os pinos do módulo EUSART são multiplexados com a Porta C. Para
configurar os pinos RC6/TX/CK e RC7/RX/DT/SDO como uma EUSART, é
necessário:
Fazer SPEN = 1 (bit 7 do registrador RCSTA)
Definir os bits 6 e 7 da Porta C como entrada:
TRISCbits.RC6 = 1 e TRISCbits.RC7 = 1
Obs.: O controle do módulo EUSART fará a reconfiguração de entrada para
saída desses pinos, sempre que necessário.

 A operação do módulo EUSART é controlada através de 3 registradores


TXSTA – Transmit Status and Control
RCSTA – Receive Status and Control
BAUDCON – Controle de Baud Rate
SERIAL EUSART

Bit de seleção da fonte de SYNC = 1  modo síncrono


Bit de status do registrador
clock SYNC = 0  modo assíncrono
de deslocamento
No modo síncrono: TRMT = 1  TSR vazio
1 – Master (clock interno) Modo síncrono: bit irrelevante TRMT = 0  TSR cheio
0 – Slave (clock externo) Modo assíncrono:
No modo assíncrono: SENDB = 1  envia bit de parada Dado transmitido no 9º
irrelevante SENDB = 0  transmissão completada bit. Pode ser endereço,
dado ou paridade
TX9 = 1  transmissão de 9 bits Modo síncrono: não utilizado
TX9 = 0  transmissão de 8 bits Modo assíncrono:
BRGH = 1  baud rate alto
TXEN = 1  habilita transmissão BRGH = 0  baud rate baixo
TXEN = 0  desabilita transmissão
SERIAL EUSART

Habilita porta serial Habilita recepção contínua


SPEN = 1  configura os Modo assíncrono: Bit de erro de ultrapassagem
pinos RX/DT e TX/CK como CREN = 1  habilita
porta serial Modo síncrono: Dado recebido no 9º bit.
SPEN = 0  desabilita porta CREN = 1  habilita modo Pode ser endereço, dado
serial contínuo, até CREN = 0 ou paridade
Habilita detecção de endereço
RX9 = 1  recepção de 9 bits Modo 9 bits assíncrono:
RX9 = 0  recepção de 8 bits Habilita detecção de endereço e interrupção
Modo 8 bits assíncrono: Irrevante
Modo assíncrono  irrelevante
Modo síncrono:
SREN =1  habilita recepção simples
SREN = 0  desabilita recepção simples Bit de erro de quadro (framing)
SERIAL EUSART

Bit de status de rolagem na Bit de seleção da polidade dos


aquisição automática de dados transmitidos e do clock Bit que habilita a função “Wake-
baud rate Modo assíncrono: up”
TXCKP = 1  dado de TX invertido Modo assíncrono:
RCIDL = 1  operação de Modo síncrono: WUE = 1  EUSART continuará a
recepção está ociosa TXCKP = 1  clock invertido leitura do pino RX

Bit de seleção da polaridade dos dados Bit que habilita o registrador de baud rate de 16 bits
recebidos BRG16 = 1  gerador de baud rate de 16 bits
Modo assíncrono: habilitado
RXDTP = 1  dado de RX invertido
Modo síncrono: Bit que habilita auto-detecção
RXDTP =1  dado recebido é invertido de baud rate
SERIAL – Baud Rate O valor de “n” na
fórmula de cálculo do
baud rate
corresponde ao par:
SPBRGH:SPBRG

Carregando o valor
desejado nesses
registradores, o PIC
automaticamente
calcula a taxa de
transmissão/recepção
SERIAL – Baud Rate – alguns valores de SPBRGH:SPBRG
para algumas taxas de transmissão/recepção
Modo assíncrono de 8 bits de baixo
baud rate

Modo assíncrono de 8 bits de alto


baud rate

O valor de “n” a ser carregado em


SPBRG é 6, para gerar baud rate de
9600 bps (valor efetivo é 8.929 bps)

O valor de “n” a ser carregado em


SPBRG é 25, para gerar baud rate de
9600 bps (valor efetivo é 9.615 bps)
SERIAL – Passos para a transmissão serial assíncrona
1. Defina o valor de SPBRGH:SPBRG (chamado de “n” na fórmula usada). A fórmula a ser usada depende
dos valores de BRGH e BRG16. :

BRG16 BRGH Cálculo de n = SPBRGH:SPBRG


𝐹𝑂𝑆𝐶 /(𝐵𝑎𝑢𝑑_𝑟𝑎𝑡𝑒 𝑑𝑒𝑠𝑒𝑗𝑎𝑑𝑎)
0 0 𝑛=
64
−1

𝐹𝑂𝑆𝐶 /(𝐵𝑎𝑢𝑑_𝑟𝑎𝑡𝑒 𝑑𝑒𝑠𝑒𝑗𝑎𝑑𝑎)


0 1 𝑛= −1
16
2. Habilite a comunicação serial assíncrona fazendo SYNC = 0 e SPEN = 1.

3. Se se deseja inverter o sinal do pino TX, faz-se TXCKP = 1

4. Se quiser usar interrupção da transmissão, fazer TXIE = 1. É necessário também fazer GIE = 1 e PEIE =1

5. Para a transmissão de 9 bits, deve-se fazer TX9=1. O 9º bit deve ser carregado em TX9D

6. Para habilitar a transmissão serial fazer TXEN = 1, que setará também o bit TXIF.

7. A transmissão de dados começa automaticamente quando o dado a ser transmitido é carregado em


TXREG.
SERIAL – Passos para a recepção serial assíncrona
1. Defina o valor de SPBRGH:SPBRG (chamado de “n” na fórmula usada). A fórmula a ser usada depende
dos valores de BRGH e BRG16. :

BRG16 BRGH Cálculo de n = SPBRGH:SPBRG


𝐹𝑂𝑆𝐶 /(𝐵𝑎𝑢𝑑_𝑟𝑎𝑡𝑒 𝑑𝑒𝑠𝑒𝑗𝑎𝑑𝑎)
0 0 𝑛=
64
−1

𝐹𝑂𝑆𝐶 /(𝐵𝑎𝑢𝑑_𝑟𝑎𝑡𝑒 𝑑𝑒𝑠𝑒𝑗𝑎𝑑𝑎)


0 1 𝑛= −1
16

2. Habilite a comunicação serial assíncrona fazendo SYNC = 0 e SPEN = 1.


3. Se se deseja inverter o sinal do pino RX, faz-se RXDTP = 1
4. Se quiser usar interrupção da recepção, fazer RCIE = 1. É necessário também fazer GIE = 1 e PEIE =1
5. Para a recepção de 9 bits, deve-se fazer RX9=1. O 9º bit é recebido através do registrador RCSTA
6. Para habilitar a recepção serial fazer CREN = 1.
7. A flag RCIF será automaticamente setada quando a recepção estiver completa. Assim, se a
interrupção estiver habilitada (RCIE =1), desviará para a função de tratamento da interrupção.
8. O byte recebido via serial é carregado no registrado RCREG
EXEMPLO - SERIAL
Inicio

Configuração;
Envia texto pra Aguarda interrupção
porta serial;

não Interrupção sim Envia texto para a porta


da recepção serial com caractere
serial? digitado;
EXEMPLO - SERIAL
#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>
#define _XTAL_FREQ 4000000 // Oscilador interno de 4 MHz

#include <xc.h>

#pragma config FOSC = HS // Oscilador externo


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

char caracter;
bit flag_interrupcao = 0;

void interrupt RS232(void) //vetor de interrupção


{
caracter = RCREG; // Lê caractere recebido do registrador
flag_interrupcao = 1; // Habilita variável indicando que houve recepção
RCIF = 0; // Limpa flag de interrupção de recepção
}
SPEN RX9 SREN CREN ADDEN FERR OERR RX9D
RCSTA:
1 0 0 1 0 0 0 0

SERIAL
void inicializa_RS232(long velocidade,int modo)
{
RCSTA = 0X90; // Habilita porta serial, recepção de 8 bits em modo continuo, assíncrono.
int valor;
if (modo == 1) { // modo = 1, modo alta velocidade (BRGH = 1)
TXSTA = 0X24; // modo assíncrono, transmissão 8 bits.
valor =(int)(((_XTAL_FREQ/velocidade)-16)/16); // valor para gerar o baud rate
}
else { //modo = 0 ,modo baixa velocidade (BRGH = 0)
TXSTA = 0X20; //modo assincrono,trasmissao 8 bits.
valor =(int)(((_XTAL_FREQ/velocidade)-64)/64);
//calculo do valor do gerador de baud rate
}
SPBRG = valor; esse registrador, carregado com o “valor” calculado, define o baud rate
RCIE = 1; //habilita interrupção de recepção
TXIE = 0; //deixa interrupção de transmissão desligado
//(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}

CSRC TX9 TXEN SYNC SENDB BRGH TRMT TX9D


TXSTA:
0 0 1 0 0 1 0 0
SERIAL
void escreve(char valor)
{
TXIF = 0; // limpa flag que sinaliza envio completo.
TXREG = valor; // Envia caractere desejado à porta serial
while(TXIF ==0); // espera caractere ser enviado
}

void imprime(const char frase[])


{
char indice = 0; // índice da cadeia de caracteres
char tamanho = strlen(frase); // tamanho total da cadeia a ser impressa
while(indice < tamanho ) { // verifica se todos foram impressos
escreve(frase[indice]); // Chama rotina que escreve o caractere
indice++; // incrementa índice
}
}
SERIAL
void main(void)
{
OSCCON = 0b01100000; // Oscilador interno a 4 MHz
TRISB = 0X02; // configura portB B1 (pino RX) como entrada
PORTB = 0; // limpar as portas que estão configuradas como saidas
inicializa_RS232(9600,1); // modo de alta velocidade
GIE = 1; // GIE: Global Interrupt Enable bit
PEIE = 1; // habilita interrupção de periféricos do pic
imprime("Usando a serial MPLAB X XC8 \n\r");
imprime(“Digite algo: \n\r");
while (1) {
if(flag_interrupcao == 1) { //tem dados para ler
imprime(" \n\rCaractere digitado :");
escreve(caracter);
flag_interrupcao = 0;
}
} //loop infinito

Fim de Código
EXEMPLO – SERIAL + LCD
Inicio

Configuração;
Organiza posição do
Envia texto pra
Aguarda interrupção caractere na segunda
porta serial e
linha do LCD
LCD;

Interrupção Envia texto para a porta


da recepção serial e LCD com
não serial? sim caractere digitado;
EXEMPLO – SERIAL + LCD
#define _XTAL_FREQ 4000000 // Oscilador interno de 4 MHz

#include <xc.h>

#define RS LATD2 // < Pinos do LCD


#define EN LATD3
#define D4 LATD4
#define D5 LATD5
#define D6 LATD6
#define D7 LATD7 // Pinos do LCD >

#include "lcd.h"
#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDTEN = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado
EXEMPLO – SERIAL + LCD

char caracter;
char linha1[16]; // Variável linha1 com 16 caracteres
char linha2[16]; // Variável linha2 com 16 caracteres
bit flag_interrupcao = 0;

void interrupt RS232(void) //vetor de interrupção


{
caracter = RCREG; // Lê caractere recebido do registrador
flag_interrupcao = 1; // Habilita variável indicando que houve recepção
RCIF = 0; // Limpa flag de interrupção de recepção
}

void inicializa_RS232(long velocidade,int modo)


{
RCSTA = 0X90; // Habilita porta serial, recepção de
// 8 bits em modo continuo, assíncrono.
int valor;
if (modo == 1) { // modo = 1, modo alta velocidade
EXEMPLO – SERIAL + LCD
TXSTA = 0X24; // modo assíncrono, transmissão 8 bits.
valor =(int)(((_XTAL_FREQ/velocidade)-16)/16); // Cálculo do baud rate
} else { //modo = 0 ,modo baixa velocidade
TXSTA = 0X20; //modo assincrono,trasmissao 8 bits.
valor =(int)(((_XTAL_FREQ/velocidade)-64)/64);
//calculo do valor do gerador de baud rate
}
SPBRG = valor;
RCIE = 1; //habilita interrupção de recepção
TXIE = 0; //deixa interrupção de transmissão desligado
//(pois corre-se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}

void escreve(char valor)


{
TXIF = 0; // limpa flag que sinaliza envio completo.
TXREG = valor; // Envia caractere à porta serial
while(TXIF ==0); // espera caractere ser enviado
}
EXEMPLO – SERIAL + LCD
void imprime(const char frase[])
{
char indice = 0; // índice da cadeia de caracteres
char tamanho = strlen(frase); // tamanho total da cadeia a ser impressa
while(indice < tamanho ) { // verifica se todos foram impressos
escreve(frase[indice]); // Chama rotina que escreve o caractere
indice++; // incrementa índice
}
}

void main(void)
{
OSCCON = 0b01100000; // Oscilador interno a 4 MHz
TRISB = 0X02; // configura portB B1 (pino RX) como entrada
PORTB = 0; // limpar as portas que estão configuradas como saidas
inicializa_RS232(9600,1); // modo de alta velocidade
GIE = 1; // GIE: Global Interrupt Enable bit
PEIE = 1; // habilita interrupção de perifericos do pic
EXEMPLO – SERIAL + LCD

TRISD = 0x00; // configura portD como saída


Lcd_Init(); // Inicia o LCD
int posicao = 1; // Variável que guarda posição do caractere no LCD

imprime("Usando a serial MPLAB X XC8 \n\r"); // Envia texto para a serial


imprime("Digite algo: \n\r");

Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1


sprintf(linha1, "Digite algo:"); // Grava texto em linha1
Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD

while (1) {
if(flag_interrupcao == 1) { // Tem dados para ler
imprime(" \n\rCaractere digitado: ");
escreve(caracter);

Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1


sprintf(linha1, "Ultima tecla: %c", caracter); // Grava texto em linha1
Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD
EXEMPLO – SERIAL + LCD
Lcd_Set_Cursor(2,posicao); // Posiciona o cursor na linha 2, ultima posicao
sprintf(linha1, "%c", caracter); // Grava texto em linha2
Lcd_Write_String(linha1); // Escreve texto de linha2 no LCD

if (posicao == 16) {
posicao = 1;
} else {
posicao++;
}

flag_interrupcao = 0;
}
} //loop infinito

Fim de Código
EXEMPLO – SERIAL + ADC
Inicio

Configuração;
Inicia leitura da tensão
Envia texto pra
no pino AN0
porta serial;

Finalizou a Envia texto para a porta


leitura? sim serial com tensão lida;
não
EXEMPLO – SERIAL + ADC
#define _XTAL_FREQ 4000000 // Oscilador interno de 4 MHz

#include <xc.h>
#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#include <stdlib.h>

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDTEN = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

char caracter;
bit flag_interrupcao = 0;
char linha[22];
int contador;
float tensao;

void inicializa_RS232(long velocidade,int modo)


{
RCSTA = 0X90; // Habilita porta serial, recepção de
EXEMPLO – SERIAL + ADC
// 8 bits em modo continuo, assíncrono.
int valor;
if (modo == 1) { // modo = 1, modo alta velocidade
TXSTA = 0X24; // modo assíncrono, transmissão 8 bits.
valor =(int)(((_XTAL_FREQ/velocidade)-16)/16); // Cálculo do baud rate
} else { //modo = 0 ,modo baixa velocidade
TXSTA = 0X20; //modo assincrono,trasmissao 8 bits.
valor =(int)(((_XTAL_FREQ/velocidade)-64)/64);
//calculo do valor do gerador de baud rate
}
SPBRG = valor;
RCIE = 1; //habilita interrupção de recepção
TXIE = 0; //deixa interrupção de transmissão desligado
//(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}

void escreve(char valor)


{
TXIF = 0; // limpa flag que sinaliza envio completo.
EXEMPLO – SERIAL + ADC
TXREG = valor; // Envia caractere à porta serial
while(TXIF ==0); // espera caractere ser enviado
}

void imprime(const char frase[])


{
char indice = 0; // índice da cadeia de caracteres
char tamanho = strlen(frase); // tamanho total da cadeia a ser impressa
while(indice < tamanho ) { // verifica se todos foram impressos
escreve(frase[indice]); // Chama rotina que escreve o caractere
indice++; // incrementa índice
}
}

void setupADC(void) {
TRISA = 0b00000001; // Habilita pino A0 como entrada

ADCON2bits.ADCS = 0b111; // Tempo de aquisição: 4 Tad


ADCON2bits.ACQT = 0b110; // Clock do AD: Fosc/64
EXEMPLO – SERIAL + ADC
ADCON2bits.ADFM = 0b1; // Formato: à direita
ADCON1bits.VCFG = 0b00; // Tensões de referência: Vss e Vdd

ANSEL = 0x00000001; // Seleciona o canal AN0

ADCON0bits.ADON = 1; // Liga o AD
}

void SuperDelay(long counter) { // Função com valor de entrada “counter”


counter = counter / 10; // Divide o valor informado por 10
for (long i = 1; i <= counter; i++) { // E usa o resultado como base
__delay_ms(10); // Para repetir uma contagem de 10 ms
}
}

void main(void)
{
OSCCON = 0b01010000; // Oscilador interno a 4 MHz
EXEMPLO – SERIAL + ADC
TRISB = 0X02; // configura portB B1 (pino RX) como entrada
PORTB = 0; // limpar as portas que estão configuradas como saidas
inicializa_RS232(9600,1); // modo de alta velocidade
setupADC(); // Configuração do AD

sprintf(linha, "Tensão lida: 0.000"); // Grava texto em linha1


imprime(linha);

while (1) {
ADCON0bits.GO = 1; // Inicia leitura do ADC
while(ADCON0bits.NOT_DONE) { // Aguarda leitura do ADC
}
contador = (ADRESH * 0x100) + ADRESL; // Transfere valor para variável
tensao = ((5 * contador) * 0.0009765625); // Calcula tensão real
sprintf(linha, "\b\b\b\b\b%1.3f", tensao); // Grava texto em linha1
imprime(linha);
SuperDelay(1000);
} //loop infinito

Fim de Código
PROGRAMAS COM BUGS #1 – ROTAÇÃO DE LEDS

Comportamento esperado:
Os LEDs rotacionem no estilo
“bate-e-volta”.

Sintoma:
Ao iniciar o programa, os LEDs
começam a rotacionar
corretamente, mas depois de
várias rotações, ele volta a
rotacionar do primeiro LED.
PROGRAMAS COM BUGS #1 – ROTAÇÃO DE LEDS
#define _XTAL_FREQ 1000000 // Oscilador de 1 MHz
#include <xc.h>
#pragma config FOSC = INTIO67 // Oscilador interno
#pragma config WDTEN = ON // Watchdog Timer ligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void main(void) {
TRISA = 0b00000000; // Habilita porta A como saída
LATA = 1; // Liga o primeiro pino da porta A
while(1) { // Inicia loop infinito
while(LATA != 0b00000001) {
LATA = (LATA >> 1 | LATA << 7); // Rotacionando com estilo pra esquerda
__delay_ms(100); // Atraso de 100 ms
}
while(LATA != 0b10000000) {
LATA = (LATA << 1 | LATA >> 7); // Rotacionando com estilo pra direita
__delay_ms(100); // Atraso de 100 ms
}
}
}

Fim de Código
PROGRAMAS COM BUGS #2 – ROTAÇÃO DE LEDS

Comportamento
esperado:
Os LEDs rotacionem no
estilo “bate-e-volta”.

Sintoma:
Ao iniciar o programa,
nada acontece.
PROGRAMAS COM BUGS #2 – ROTAÇÃO DE LEDS

#define _XTAL_FREQ 1000000 // Oscilador de 1 MHz


#include <xc.h>
#pragma config FOSC = INTIO67 // Oscilador interno
#pragma config WDTEN = ON // Watchdog Timer ligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void main(void) {
TRISA = 0b00000000; // Habilita porta A como saída
LATA = 1; // Liga o primeiro pino da porta A
while(1) { // Inicia loop infinito
while(LATA != 0b00000001) {
LATA = (LATA >> 1 | LATA << 7); // Rotacionando com estilo pra esquerda
__delay_ms(100); // Atraso de 100 ms
}
while(LATA != 0b10000000) {
LATA = (LATA << 1 | LATA >> 7); // Rotacionando com estilo pra direita
__delay_ms(100); // Atraso de 100 ms
}
}
}

Fim de Código
PROGRAMAS COM BUGS #3 – ROTAÇÃO DE LEDS

Comportamento
esperado:
Os LEDs rotacionem no
estilo “bate-e-volta”.

Sintoma:
Ao iniciar o programa,
nada acontece. A
tensão nos pinos de
saída dos LEDs não
mostram nenhum valor
bem definido.
PROGRAMAS COM BUGS #3 – ROTAÇÃO DE LEDS

#define _XTAL_FREQ 1000000 // Oscilador de 1 MHz


#include <xc.h>
#pragma config FOSC = INTIO67 // Oscilador interno
#pragma config WDTEN = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void main(void) {
LATA = 1; // Liga o primeiro pino da porta A
while(1) { // Inicia loop infinito
while(LATA != 0b00000001) {
LATA = (LATA >> 1 | LATA << 7); // Rotacionando com estilo pra esquerda
__delay_ms(100); // Atraso de 100 ms
}
while(LATA != 0b10000000) {
LATA = (LATA << 1 | LATA >> 7); // Rotacionando com estilo pra direita
__delay_ms(100); // Atraso de 100 ms
}
}
}

Fim de Código
PROGRAMAS COM BUGS #4 – PISCAR LED

Comportamento esperado:
Piscar um LED na porta D0 a
cada 100 ms.

Sintoma:
O LED pisca, mas o
osciloscópio mostra que ele
pisca a cada 25 ms.
PROGRAMAS COM BUGS #4 – PISCAR LED
#define _XTAL_FREQ 1000000 // Oscilador de 1 MHz

#include <xc.h>

#pragma config FOSC = INTIO // Oscilador interno


#pragma config WDTEN = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void main(void) {
OSCCON = 0b01010000; // Define frequência do oscilador para 4MHz
TRISD = 0b00000000; // Habilita porta D como saída

while(1) { // Inicia loop infinito


LATDbits.LATD0 = !LATDbits.LATD0; // Inverte sinal do pino D0
__delay_ms(100); // Atraso de 100 ms
}

Fim de Código
PROGRAMAS COM BUGS #5 – PISCAR LED

Comportamento
esperado:
Piscar um LED na porta
D0 a cada 100 ms.

Sintoma:
O LED fica ligado o
tempo todo.
PROGRAMAS COM BUGS #5 – PISCAR LED
#define _XTAL_FREQ 4000000 // Oscilador de 4 MHz

#include <xc.h>

#pragma config FOSC = INTIO67 // Oscilador interno


#pragma config WDTEN = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void main(void) {
OSCCON = 0x01010000; // Define frequência do oscilador para 4MHz
TRISD = 0b00000000; // Habilita porta D como saída

while(1) { // Inicia loop infinito


LATDbits.LATD0 = !LATDbits.LATD0; // Inverte sinal do pino D0
__delay_ms(100); // Atraso de 100 ms
}

Fim de Código
PROGRAMAS COM BUGS #6 – LCD

Comportamento esperado:
Escrever “Hello, world!” e um
contador na tela do LCD. A
cada contagem, o LED da porta
D0 deve piscar.

Sintoma:
Ao iniciar o programa, o LCD
não mostra nada escrito. Apesar
disso, o LED pisca.
PROGRAMAS COM BUGS #6 – LCD
#define _XTAL_FREQ 1000000

#include <xc.h>

#define RS LATD2 // < Pinos do LCD ligados na porta D


#define EN LATD3
#define D4 LATD4
#define D5 LATD5
#define D6 LATD6
#define D7 LATD7 // Pinos do LCD ligados na porta D >

#pragma config FOSC = INTIO67 // Oscilador interno


#pragma config WDTEN = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

#include "lcd.h"
#include <stdio.h>
PROGRAMAS COM BUGS #6 – LCD
char linha1[16]; // Variável linha1 com 16 caracteres
char linha2[16]; // Variável linha2 com 16 caracteres
int contador = 0; // Variável contador com valor inicial 0

void main(void) {
Lcd_Init(); // Inicia o LCD, ligado na porta D

TRISD = 0; // Define porta D inteira como saída


sprintf(linha1, "Hello world! "); // Grava texto em linha1
Lcd_Set_Cursor(1,1); // Posiciona o cursor na linha 1, caractere 1
Lcd_Write_String(linha1); // Escreve texto de linha1 no LCD

while(1) {
sprintf(linha2, "Contador: %i ",contador); // Grava texto em linha2
contador ++; // Incrementa contador
Lcd_Set_Cursor(2,1); // Posiciona o cursor na linha 2, caractere 1
Lcd_Write_String(linha2); // Escreve texto de linha2 no LCD
LATDbits.LATD0 = !LATDbits.LATD0; // Pisca LED na porta D0
}
}

Fim de Código
PROGRAMAS COM BUGS #7 –
INTERRUPÇÃO

Comportamento esperado:
O LED deve acender ou apagar a
cada pressionar do botão ligado à
porta B0 (ou INT0)

Sintoma:
Nada acontece ao pressionar o
botão.
PROGRAMAS COM BUGS #7 – INTERRUPÇÃO

#define _XTAL_FREQ 1000000

#include <xc.h>

#pragma config FOSC = INTIO67 // Oscilador interno


#pragma config WDT = OFF // Watchdog Timer desligado
#pragma config MCLRE = OFF // Master Clear desabilitado

void setupInt(void) {
GIE = 1; // Habilita interrupção global
INT0IE = 1; // Habilita Interrupção da INT 0
INT0F = 0; // Limpa Flag de interrupção da INT 0
INTEDG0 = 1; // Interrupção por borda crescente.
}
PROGRAMAS COM BUGS #7 – INTERRUPÇÃO

void interrupt interrupcao(void) { // Função de interrupção


if (INT0F) { // Caso a flag da INT0 esteja habilitada
LATAbits.LA0 = !LATAbits.LA0; // Inverte o sinal no pino A0
INT0F = 0; // Desabilita a flag da INT0
}
}

void main(void) {
TRISA = 0x00; // Porta A com todos pinos de saída
TRISB = 0x01; // Somente pino B1 como entrada (INT0)
setupInt(); // Função de inicializar Interrupção
while(1) { // Loop infinito
}
}

Fim de Código
PROGRAMAS COM BUGS
Checklist contra a maioria dos bugs:

1. Verificar os bits de configuração:


• MCLRE – impede qualquer funcionamento;
• FOSC – impede qualquer funcionamento;
• WDTEN – causa resets inesperados pouco depois de ligado;
• PBADEN – impede leitura digital na porta B

2. Verificar se as portas estão definidas corretamente como entrada ou


saída na sequência correta:
• Impede leitura ou saída nas portas e funcionamento de periféricos.

3. Verificar velocidade do oscilador ou cristal (definição da frequência em


OSCCON) e se está condizente com a definição de _XTAL_FREQ:
• Causa função __delay_ms gerar atrasos diferentes do esperado;
PROGRAMAS COM BUGS

Checklist contra a maioria dos bugs:

4. Verificar se definições de variáveis ou registradores estão corretas:


Usar 0x10101010 é bem diferente de usar 0b10101010;

5. Ler o datasheet.

Você também pode gostar