microcontroladores pic - emcjwilson/pdf/5_micro_parte_5_(pic)_pratica.pdf · conversor...
TRANSCRIPT
Microcontroladores PICPrá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:
if:
if (variavel == 10) {
// executa se condição for verdadeira
} else {
// executa se condição for falsa
}
Condição
Linguagem C – compilador XC8
Loops:
While:
while (variavel != 0) {
// código em loop
}
Linguagem C – compilador XC8
Loops:
While:
while (variavel != 0) {
// código em loop
}
Condição (executa enquanto for 1)
Linguagem C – compilador XC8
Loops:
for:
for (variavel = 1; variavel < 100; variavel++)
{
// código em loop
}
Linguagem C – compilador XC8
Loops:
for:
for (variavel = 1; variavel < 100; variavel++)
{
// código em loop
}
Valor inicial
Linguagem C – compilador XC8
Loops:
for:
for (variavel = 1; variavel < 100; variavel++)
{
// código em loop
}
Condição (executa enquanto for 1)
Linguagem C – compilador XC8
Loops:
for:
for (variavel = 1; variavel < 100; variavel++)
{
// código em loop
}
Incremento
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
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
Definindo bits de Configuração:
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 WatchdogTimer. 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
ConfiguraçãoInverte sinal do pino
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
ConfiguraçãoRotaciona 1 passo
para a esquerda
PORTD=0? LATD = 1não sim
#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
ConfiguraçãoRotaciona 1 passo
para a esquerda
PORTD=0? LATD = 1não sim
#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);
Fim de Código
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;
}
EXEMPLO 3 – ROTACIONAR LED
Inicio
ConfiguraçãoRotaciona para a
esquerda
LED aceso
na borda
esquerda
?
Rotaciona para a
direita
LED aceso
na borda
direita?
não
não
sim
sim
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ÇÃODigamos 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çãoAdiciona 1 em
contador
Atualiza LCD com
valor de contador
EXEMPLO – LCD
#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>
Conexão da Porta D
no LCD
Os pinos D0, D1, D2
e D3 do LCD são
conectados ao Terra
Biblioteca local do
LCD
EXEMPLO – LCDchar 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
ConfiguraçãoAdiciona 0.01 em
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
ConfiguraçãoAguarda
interrupção
Interrupção
ativada?Inverte sinal do LED
nãosim
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)
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
Características do Conversor Analógico Digital (ADC):
10 bits
13 entradas multiplexadas
Registradores importantes:
Linguagem C – compilador XC8
Registradores importantes – ADC (PIC18F4550):
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
Analógico
Status da
conversão
Habilita
ADC
ADCON0: Registrador de Controle do 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):
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
ADCON1: Registrador de Controle do ADC
ADCON1bits.VCFG = 0b00; Tensões de referência: Vss e Vdd
Linguagem C – compilador XC8
Registradores importantes – ADC (PIC18F4550):
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
resultado
Bits de seleção do Tempo de
Aquisição de dados
Bits de seleção do Clock de
conversão
ADCON2: Registrador de Controle do ADC
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
ConfiguraçãoInicia leitura da
tensão no pino A0
Finalizou
leitura?
Grava valor da
leitura nos bits da
porta C e D
não sim
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
ConfiguraçãoInicia leitura da
tensão no pino A0
Finalizou
leitura?
Calcula tensão no pino e
exibe valor lido e tensão
calculada no LCD
não sim
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 + LCDsetupADC();
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
ConfiguraçãoLê tensão no pino
A0 e guarda
Atualiza LCD com os
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 CANAISTRISD = 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
Configuração
Atualiza LCD
(chama rotina que
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
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
Seleção do clock
do AD
EXEMPLO – ADC + LCD + 4 CANAISfloat 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
Configuração
(x = 0)
Atualiza LCD
com as variáveis
tensão[0] a tensão[7];
Inicia leitura do pino
ANx
Leitura
finalizada
?
Atualiza
variável
tensão[x] com
o valor da
tensão no pino
Ax;
Incrementa x
não sim
x é
maior
que 7?
sim
x = 0
não
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
Escolha entre temporizador (T0CS = 0)
ou contador (T0CS = 1)
Timer 0 configurado para 8 bits
(T08BIT = 1) ou 16 bits (T08BIT = 0)
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
configurado para
gerar interrupção
a cada 50 ms)
Aguarda
interrupção
Interrupção
ativada?Inverte sinal do LED
não sim
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
configurado para
gerar interrupção
a cada 1s)
Aguarda
interrupção
Interrupção
ativada?Inverte sinal do LED
não sim
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 2Inicio
Configuração
(temporizador
configurado para
gerar interrupção
a cada 10 ms)
Aguarda
interrupção
Interrupção
ativada?Inverte sinal do LED
não sim
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 2TMR2 = 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
(configura timer
para interromper
a cada 10 ms)
Atualiza LCD com o
valor da variável
“tensão” e “contador”
Grava tensão do pino
AN0 na variável “tensão”;
Incrementa “contador”;
não simInterrupção
do conversor
AD?
Interrupção
do timer?
simInicia leitura no
conversor AD
não
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
temporizador
e PWM)
. . . É, não faz nada.
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
ConfiguraçãoInicia conversão AD no
pino AN0
Atualiza valor do “duty
cycle” do PWM baseado
no valor de tensão lido no
pino AN0não sim
Interrupção
do conversor
AD?
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
do ADC e do
LCD
Leitura do LM35
Inicia conversão AD no
pino AN0
Converte leitura do AD
em temperatura
Mostra valor no LCDnão sim
Acabou a
conversão?
A tensão de referência
do AD é fundamental
porque a tensão máxima
de saída do LM35 é 1,5 V,
para uma temperatura
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 + LCDchar 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 + LCDvoid 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
clock
No modo síncrono:
1 – Master (clock interno)
0 – Slave (clock externo)
No modo assíncrono:
irrelevante
TXEN = 1 habilita transmissão
TXEN = 0 desabilita transmissão
TX9 = 1 transmissão de 9 bits
TX9 = 0 transmissão de 8 bits
SYNC = 1 modo síncrono
SYNC = 0 modo assíncrono
Modo síncrono: bit irrelevante
Modo assíncrono:SENDB = 1 envia bit de parada
SENDB = 0 transmissão completada
Modo síncrono: não utilizado
Modo assíncrono:BRGH = 1 baud rate alto
BRGH = 0 baud rate baixo
Bit de status do registrador
de deslocamentoTRMT = 1 TSR vazio
TRMT = 0 TSR cheio
Dado transmitido no 9º
bit. Pode ser endereço,
dado ou paridade
SERIAL EUSART
Habilita porta serialSPEN = 1 configura os
pinos RX/DT e TX/CK como
porta serialSPEN = 0 desabilita porta
serial
Modo assíncrono irrelevante
Modo síncrono: SREN =1 habilita recepção simples
SREN = 0 desabilita recepção simples
RX9 = 1 recepção de 9 bits
RX9 = 0 recepção de 8 bits
Habilita recepção contínua
Modo assíncrono:CREN = 1 habilita
Modo síncrono:CREN = 1 habilita modo
contínuo, até CREN = 0
Habilita detecção de endereço
Modo 9 bits assíncrono:
Habilita detecção de endereço e interrupção
Modo 8 bits assíncrono: Irrevante
Bit de erro de quadro (framing)
Bit de erro de ultrapassagem
Dado recebido no 9º bit.
Pode ser endereço, dado
ou paridade
SERIAL EUSART
Bit de status de rolagem na
aquisição automática de
baud rate
Bit de seleção da polaridade dos dados
recebidos
Modo assíncrono:RXDTP = 1 dado de RX invertido
Modo síncrono: RXDTP =1 dado recebido é invertido
RCIDL = 1 operação de
recepção está ociosa
Bit de seleção da polidade dos
dados transmitidos e do clock
Modo assíncrono:TXCKP = 1 dado de TX invertido
Modo síncrono:TXCKP = 1 clock invertido
Bit que habilita o registrador de baud rate de 16 bitsBRG16 = 1 gerador de baud rate de 16 bits
habilitado
Bit que habilita auto-detecção
de baud rate
Bit que habilita a função “Wake-
up”
Modo assíncrono:WUE = 1 EUSART continuará a
leitura do pino RX
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
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)
Modo assíncrono de 8 bits de baixo
baud rate
Modo assíncrono de 8 bits de alto
baud rate
SERIAL – Passos para a transmissão serial assíncrona1. 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 𝑛 =𝐹𝑂𝑆𝐶/(𝐵𝑎𝑢𝑑_𝑟𝑎𝑡𝑒 𝑑𝑒𝑠𝑒𝑗𝑎𝑑𝑎)
16− 1
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íncrona1. 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 𝑛 =𝐹𝑂𝑆𝐶/(𝐵𝑎𝑢𝑑_𝑟𝑎𝑡𝑒 𝑑𝑒𝑠𝑒𝑗𝑎𝑑𝑎)
16− 1
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
porta serial;
Aguarda interrupção
Envia texto para a porta
serial com caractere
digitado;
não simInterrupção
da recepção
serial?
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
}
SERIALvoid 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)
}
SPEN RX9 SREN CREN ADDEN FERR OERR RX9D
1 0 0 1 0 0 0 0
CSRC TX9 TXEN SYNC SENDB BRGH TRMT TX9D
0 0 1 0 0 1 0 0
RCSTA:
TXSTA:
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;
Envia texto pra
porta serial e
LCD;
Aguarda interrupção
Envia texto para a porta
serial e LCD com
caractere digitado;nãosim
Interrupção
da recepção
serial?
Organiza posição do
caractere na segunda
linha do LCD
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;
Envia texto pra
porta serial;
Inicia leitura da tensão
no pino AN0
Envia texto para a porta
serial com tensão lida;não sim
Finalizou a
leitura?
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
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 #4 – 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 #5 – PISCAR LED
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
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 #7 – INTERRUPÇÃO
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.