Prévia do material em texto
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: 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 edo 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 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 Configuração Inverte 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ção Rotaciona 1 passo para a esquerda PORTD=0? LATD = 1 nã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ção Rotaciona 1 passo para a esquerda PORTD=0? LATD = 1 nã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ção Rotaciona para a esquerda LED aceso na borda esquerda ? Rotaciona para a direita LED aceso na borda direita? não não sim simEXEMPLO – 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> #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 – 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 Configuração Adiciona 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ção Aguarda interrupção Interrupção ativada? Inverte sinal do LED não sim 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ção Inicia 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ção Inicia 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 + 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 Configuração Lê 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 VddADCON0bits.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 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 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 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 canaisADCON0bits.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 2 Inicio 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 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 (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? sim Inicia 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; // Limpaa 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ção Inicia conversão AD no pino AN0 Atualiza valor do “duty cycle” do PWM baseado no valor de tensão lido no pino AN0 nã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 + 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 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 deslocamento TRMT = 1 TSR vazio TRMT = 0 TSR cheio Dado transmitido no 9º bit. Pode ser endereço, dado ou paridade SERIAL EUSART Habilita porta serial SPEN = 1 configura os pinos RX/DT e TX/CK como porta serial SPEN = 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 bits BRG16 = 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” Modoassí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í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 𝑛 = 𝐹𝑂𝑆𝐶/(𝐵𝑎𝑢𝑑_𝑟𝑎𝑡𝑒 𝑑𝑒𝑠𝑒𝑗𝑎𝑑𝑎) 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í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 𝑛 = 𝐹𝑂𝑆𝐶/(𝐵𝑎𝑢𝑑_𝑟𝑎𝑡𝑒 𝑑𝑒𝑠𝑒𝑗𝑎𝑑𝑎) 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 } 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) } 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ão sim 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 umainterrupçã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 } } }