sexta-feira, 11 de novembro de 2016

Funções de tempo


 A medição de tempo é feita por contagem de ciclos de instrução (TCI), cujo valor, em segundos depende da frequência do oscilador de clock. O tempo correspondente ao ciclo de instrução (TCI), é dado pela equação:





Para uma frequência de 48MHz temos




xc.h

A bliblioteca xc.h disponibiliza alguma funções que permitem gerar atrasos.

É necessário "chamar" a biblioteca xc.h
#include <xc.h>

Para o correto funcionamento, uma vez que as funções são baseadas em ciclos de instrução do microcontrolador é necessário indicar a frequência em Hz de trabalho do microcontrolador
#define _XTAL_FREQ 48000000

Obs.: Nas funções e macros descritas a seguir o argumento a ser passado ("cycles" ou "x") não pode ser um valor elevado, pois o compilador "entende" que desta forma o programa poderia estar "travado" ou em loop infinito, então para obter tempos maiores, é necessário chamar várias vezes as funções ou realizar inteirações (laços de repetição como "for", "while").

 Fonte: Guia do Usuário do compilador XC8 - Microchip

_delay

Gera um atraso de um número de ciclos que é especificado como argumento "cycles"

Sintaxe:  void _delay(unsigned long cycles);

Uso: _delay(10);   // gera um atraso de 10 ciclos de instrução aproximadamente 833ns (10 x 83,3ns)


   

_delay3


Gera um atraso de 3 vezes o número de ciclos que é especificado como argumento "cycles"

Sintaxe:  void _delay3(unsigned char cycles);

Uso: _delay3(10); // atraso de 30 ciclos de instrução



__delayus


É uma macro baseada na função _delay e gera um atraso em us que é especificado no argumento "x"

Sintase: void __delay_us(x);

Uso: __delay_us(1);  // gera uma atraso de 1us



   __delayms


É uma macro baseada na função _delay e gera um atraso em ms que é especificado no argumento "x"

Sintase: void __delay_ms(x);

Uso: __delay_ms(1);  // gera uma atraso de 1ms   


Entendendo PWM



Datasheet capítulo 15 (PWM)

O que é PWM (MEC071)

Modulação por Largura de Pulso - UDESC

PULSE WIDTH MODULATION CONCEITOS E CIRCUITO-EXEMPLO

EletrônicaDigital PWM - Modulação Por Largura de Pulso - Mecaweb





Exemplo de funcionamento




Código de exemplo para acionamento das saídas 1 e 2



/*
 * File:   PWM_1_exemplo.c
 * Author: Pilger
 *
 * Created on 16 de Novembro de 2017, 08:35
 */

#define _XTAL_FREQ 48000000
#define TMR2PRESCALE 16

// relação de frequencia x prescaler para xtal de 48M
// para reslução de 1000 -> 2^10 = 1023
// ps   F pwm
// 1    48KHz
// 4    12KHz
// 16    3 KHz

#include <xc.h>
#include "C:\h\config_PIC18F4550.h"

//variáveis globais
long freq;

// protótipos de funções
void delay_ms(int i);
int PWM_Max_Duty(void);
void PWM1_Init(long fre);
void PWM2_Init(long fre);
void PWM1_Duty(unsigned int duty);
void PWM2_Duty(unsigned int duty);
void PWM1_Start(void);
void PWM1_Stop(void);
void PWM2_Start(void);
void PWM2_Stop(void);


void main(){
  unsigned int i=0,j=0;
  PWM1_Init(3000);
  PWM2_Init(3000);
  TRISD = 0xFF;
  TRISC = 0;
  PWM1_Duty(0);
  PWM2_Duty(0);
  PWM1_Start();
  PWM2_Start();
  while (1)  {
    if(PORTDbits.RD0 == 0 && i<1000){
        while(PORTDbits.RD0==0);  
      i=i+100;
    }
    if(PORTDbits.RD1 == 0 && i>0){
        while(PORTDbits.RD1==0);
      i=i-100;
    }
    if(PORTDbits.RD2 == 0 && j<1000){
        while(PORTDbits.RD2==0);
      j=j+100;
    }
    if(PORTDbits.RD3 == 0 && j>0){
      while(PORTDbits.RD3==0);
        j=j-100;
    }
    PWM1_Duty(i);
    PWM2_Duty(j);
    delay_ms(10); // 10ms  
  }
}

// função que gera atraso em mlisegundos
void delay_ms(int i){
    for ( int x = 0; x < i; x++ )     { 
    __delay_ms(1); 
    }
}

int PWM_Max_Duty(void){
  return(_XTAL_FREQ/(freq*TMR2PRESCALE);
}

void PWM1_Init(long fre){
  PR2 = (_XTAL_FREQ/(fre*4*TMR2PRESCALE)) - 1;
  freq = fre;
}

void PWM2_Init(long fre){
  PR2 = (_XTAL_FREQ/(fre*4*TMR2PRESCALE)) - 1;
  freq = fre;
}

void PWM1_Duty(unsigned int duty){
  if(duty<1024) {
    duty = ((float)duty/1023)*PWM_Max_Duty();
    CCP1CONbits.DC1B1 = duty & 2;
    CCP1CONbits.DC1B0 = duty & 1;
    CCPR1L = duty>>2;
  }
}

void PWM2_Duty(unsigned int duty){
  if(duty<1024) {
    duty = ((float)duty/1023)*PWM_Max_Duty();
    CCP2CONbits.DC2B1 = duty & 2;
    CCP2CONbits.DC2B0 = duty & 1;
    CCPR2L = duty>>2;
  }
}

void PWM1_Start(void) {
    CCP1CONbits.CCP1M3 = 1;
    CCP1CONbits.CCP1M2 = 1;
  #if TMR2PRESCALE == 1
      T2CONbits.T2OUTPS0 = 0;
      T2CONbits.T2OUTPS1 = 0 ;
  #elif TMR2PRESCALE == 4
      T2CONbits.T2OUTPS0 = 1;
      T2CONbits.T2OUTPS1 = 0 ;
  #elif TMR2PRESCALE == 16
      T2CONbits.T2OUTPS0 = 1;
      T2CONbits.T2OUTPS1 = 1 ;
  #endif
  T2CONbits.TMR2ON = 1;
  TRISCbits.TRISC2 = 0;
}

void PWM1_Stop(void) {
    CCP1CONbits.CCP1M3 = 0;
    CCP1CONbits.CCP1M2 = 0;
}

void PWM2_Start(void){
    CCP2CONbits.CCP2M3 = 1;
    CCP2CONbits.CCP2M2 = 1;
  #if TMR2PRESCALE == 1
      T2CONbits.T2OUTPS0 = 0;
      T2CONbits.T2OUTPS1 = 0;
  #elif TMR2PRESCALE == 4
      T2CONbits.T2OUTPS0 = 1;
      T2CONbits.T2OUTPS1 = 0;    
  #elif TMR2PRESCALE == 16
      T2CONbits.T2OUTPS0 = 1;
      T2CONbits.T2OUTPS1 = 1;
  #endif
  T2CONbits.TMR2ON = 1;
  TRISCbits.TRISC1 = 0;
}

void PWM2_Stop(void){
    CCP2CONbits.CCP2M3 = 0;
    CCP2CONbits.CCP2M2 = 0;
}




Atividade 1


1.1 Entendendo o PWM 

  • Criar um projeto e compilar o exemplo
  • Observar no simulador
  • Colocar no hardware
  • Ligar um led na saída e observar
  • Ligar um osciloscópio e analisar a saída

1.2 Adicionando recursos de informação 

  • Modificar o programa para
  • Adicionar o módulo LCD e indicar o percentual de PWM em cada sinal
  • Modificar o incremento e decremento para saltos de 1%

1.3 Ligando uma carga 



  • Ligar uma carga em uma das saídas, um motor DC de 12V (pode ser uma ventoinha de fonte de computador)













sexta-feira, 7 de outubro de 2016

Entendendo sensores

Sensores são dispositivos, também chamados de transdutores, são elementos que realizam a interface entre os circuitos e o mundo real convertendo grandezas físicas em elétricas.

Fontes de consulta:



– Analógico: o sinal de saída deste tipo de sensor vai variar ao longo do tempo, de forma a assumir valores dentro da sua faixa de operação. Geralmente, os sinais mais utilizados são 4… 20 mA ou 0… 10 V. Mas ele poderá variar de acordo com a sua distância de acionamento ou de acordo com o movimento de um atuador, por exemplo.
– Digital: é um tipo de sensor que pode assumir apenas dois valores em seu sinal de saída, que poderão ser interpretados como 0 (zero) ou então 1.

Existem sensores que podem atuar tanto de forma analógica como digital, e isso vai depender da sua aplicação.







Sensor LDR como entrada digital



A ligação pode ser invertida

Sugere-se a aplicação de um comparador pada adequar o sinal


Atividade 1

Elaborar um programa para acionar uma saída a partir da ação do sensor




Sensor infra-vermelho









  





Atividade 2.1

Implementar o circuito acima e realizar as medidas de tensão na saída do sensor para as situações abaixo:


Descreva incidência alta de luminosidade:
Descreva incidência baixa de luminosidade:

Avaliação: Mostrar os resultados obtidos.






Atividade 2.2

Inclua junto ao circuito da Atividade 1.1 o circuito abaixo. O Amplificador operacional funciona como comparador, podendo ser utilizado o LM339, LM393, ou qualquer operacional que funcione como comparador.

Calcule os valores de R6 e R7 para construir um divisor de tensão para o comparador de forma que consiga atender todas as situações da tabela da atividade 1.1.


Avaliação: Mostrar os resultados obtidos.








Atividade 2.3

Elabore um programa para contar a passagem de elementos em uma esteira hipotética

Avaliação: Apresentar hardware e software em funcionamento



Memória - Atividade 2 - Cofre digital













   


Implementação prática



Atividade 2.1

Baixe o programa “Mem_2_1.c” (listado mais abaixo). Adapte as faixas das teclas para o seu teclado. Lembrar que as teclas estão representadas pelo seu código ASCII

Implemente o hardware mostrado na imagem acima:
  • Teclado com 16 teclas matricial por conversor A/D e interrupção
  • Sensor de porta fechada. Utilizar circuito de um botão simples simulando um sensor de contato
  • Acionamento de tranca: Saída com led indicativo e circuito para acionamento de trava elétrica. Utilizar circuito sugerido na imagem acima com acoplador ótico e acionamento por relé (permite acionamento AC ou DC).

Avaliação: Apresentar hardware e software em funcionamento

EFEITO MAGNÉTICO (TRAVA ELETROMAGNÉTICA)






   


Atividade 2.2

Modifique o programa da atividade 2.1 para:
  • Solicitar a senha para operação de trancar a porta (utilizar a mesma senha de abertura)
  • Verificar se a porta está fechada antes de acionar a tranca

Avaliação: Apresentar hardware e software em funcionamento

Fluxograma de sugestão para implementação da atividade 2.2:





Programa Mem_2_1.c
  
/*
 * File:   Mem_2_1.c
 * Author: Pilger
 *
 * Created on 4 de Outubro de 2017, 10:17
 *
 * Programa cofre digital
 * Rotina de leitura de 3 teclas para gerar um senha com valor entre 0 e 255
 * Realiza a troca de senha
 *      Solicita 2 vezes a digitação de senha e testa se menor que 255
 *      compara so 2 valores se forem iguai e menor que 255 grava no endereço 0x00 da EEPROM
 * Abertura de porta
 *      Solicita senha
 *      Senha == 255 -> troca de senha
 *      Senha <255 testa se senha é a mesma da eeprom
 *      Senha errada, mensagem de erro
 *      Senha correta
 *          destrava porta
 *          espera abrir
 *          espera fechar
 *          tranca a porta
 * 
 */


#define _XTAL_FREQ 48000000

#include <stdio.h>
#include <xc.h>
#include "C:\h\Config_PIC18F4550.h"
#include "c:\h\biblioteca_lcd_2x162_48M_XC.h"

#define tranca LATBbits.LATB7
#define sensor_porta PORTBbits.RB6

//prototipos de funções
void inicia_regs(void);
void interrupt ISR_alta_prioridade(void);
void config_int(void);
void config_int0(void);
void config_AD(void);
int conv_AD(void);
void delay_ms(int i);
int le_3_tec(unsigned char ns1);
unsigned char testa255(unsigned char ns);
void troca_senha(void);
void Eeprom_write(char addr, char data);
unsigned char Eeprom_read(unsigned char addr);


unsigned char fl_tec = 0, tec = 0;
/* fl_tec: flag que indica que chegou uma tecla
 * tec: valor asc da tecla recebida
 */



void main(void) {
    unsigned char buffer[16];
    unsigned char senha_digitada = 0, senha_eeprom = 0;
    inicia_regs();
    config_int();
    config_int0(); // configura as interrupções
    config_AD(); // configura e inicializa o conversor AD
    lcd_inicia(0x28, 0x0f, 0x06); // incializa o LCD com 4 linhas
    lcd_LD_cursor(0); // Desliga o cursor
    tranca = 1; // tranca inicialmente ativada
    while (1) {
        lcd_limpa_tela();
        lcd_posicao(1, 1);
        imprime_string_lcd(" Cofre digital ");
        delay_ms(2000);
        senha_digitada = testa255(0);
        lcd_posicao(2, 1);
        sprintf(buffer, "Senha   = %03d  ", senha_digitada);
        imprime_buffer_lcd(buffer, 15);
        if (senha_digitada == 255) {
            troca_senha();
        } else {
            senha_eeprom = Eeprom_read(0);
            if (senha_digitada == senha_eeprom) {
                tranca = 0; // abre tranca
                lcd_limpa_tela();
                lcd_posicao(1, 1);
                imprime_string_lcd("   Destravado   ");
                lcd_posicao(2, 1);
                imprime_string_lcd(" Porta Fechada ");
                while (!sensor_porta); //espera a porta abrir
                lcd_posicao(1, 1);
                imprime_string_lcd(" Porta Aberta  ");
                lcd_posicao(2, 1);
                imprime_string_lcd("Feche p/travar ");
                while (sensor_porta); // aguarda porta fechar
                tranca = 1; // fecha tranca
                lcd_posicao(1, 1);
                imprime_string_lcd(" Cofre digital ");
                lcd_posicao(2, 1);
                imprime_string_lcd(" Porta Fechada ");
                delay_ms(2000);
            } else {
                lcd_limpa_tela();
                lcd_posicao(1, 1);
                imprime_string_lcd("ERRO -   Senha ");
                lcd_posicao(2, 1);
                imprime_string_lcd("     nao confere");
                delay_ms(2000);
            }
        }
    }
}

// função de configuração do conversor A/D

void config_AD(void) {
    ADCON0 = 0b00000001; /* canal AN0 selecionado <5:2> 0000
                                   Flag GO - /DONE desligado <1> 0
   Módulo conversor ligado <0> 1 */
    ADCON1 = 0b00111110; /* Vref- = GND <5> 0 RA2 <5> 1
   Vref+ = VDD <4> 0
   pino RA0/AN0 analógico e demais digitais <3:0> 1110 */
    ADCON2 = 0b10110110; /* Resultado justificado a direita <7> 1
* Tempo de Aquisição de 16 TDA <5:3> 110
* TDA = 1,33us <2:0> 110
                                 * 48MHz/64 = 750kHz = 1,33us */
}


// função que efeuta uma conversão A/D e retorna um inteiro com o valor convertido

int conv_AD(void) {
    int result_AD; // Variável local para armazenar o resultado da conv
    ADCON0bits.GO = 1; // inicia a conversão
    while (ADCON0bits.GO); // Aguarda a o fim da conversão
    result_AD = (((int) ADRESH) << 8) | (ADRESL);
    return result_AD;
}

// função que inicia os registradores I/O

void inicia_regs(void) {
    // Configura todas as portas multiplexadas com o módulo conversor A/D, como I/O digital. (cap13)
    ADCON1 = 0x0F;
    TRISA = 0b00001101;
    TRISB = 0b01000001; // define a B0 como entrada (INT0)  e B6 sensor da porta
    TRISD = 0x00; // define a porta D como saída      - onde está o LCD
    TRISE = 0x00; // define a porta E como saída      - onde está o Led que indica que passou pela interrupção
    PORTA = 0;
    PORTB = 0;
    PORTC = 0;
    PORTD = 0;
    PORTE = 0; // zeroa todos os pinos
}

// função de interrupção de alta prioridade

void interrupt ISR_alta_prioridade(void) {
    delay_ms(100); // atraso de 0,1s
    // este atraso é importante para evitar pulos do teclado, pois o up roda muito rápido 48MHz
    int vc;
    //    unsigned char buffer[16];
    fl_tec = 1;
    tec = 0;
    vc = conv_AD();
    lcd_posicao(2, 1);
    if (vc > 965 && vc < 1003) tec = 47;
    else if (vc > 929 && vc < 965) tec = 57;
    else if (vc > 899 && vc < 929) tec = 56;
    else if (vc > 865 && vc < 899) tec = 55;
    else if (vc > 821 && vc < 865) tec = 120;
    else if (vc > 755 && vc < 821) tec = 54;
    else if (vc > 737 && vc < 775) tec = 53;
    else if (vc > 665 && vc < 737) tec = 52;
    else if (vc > 575 && vc < 665) tec = 45;
    else if (vc > 509 && vc < 575) tec = 51;
    else if (vc > 453 && vc < 509) tec = 50;
    else if (vc > 369 && vc < 453) tec = 49;
    else if (vc > 260 && vc < 369) tec = 43;
    else if (vc > 163 && vc < 260) tec = 35;
    else if (vc > 70 && vc < 163) tec = 48;
    else if (vc > 20 && vc < 70) tec = 42;
    //    sprintf(buffer, "  Conv= %04d", vc);
    //    imprime_buffer_lcd(buffer, 15);
    INTCONbits.INT0IF = 0; // Limpa o flag de ativação da interrupção
}


// função de configuração geral de interrupções

void config_int(void) { // configurações gerais a todas as interrupçãoes
    RCONbits.IPEN = 1; // Habilita interrupção com nível de prioridade. End 0x08 - alta e 0x18 - baixo
    INTCONbits.GIEH = 1; // Habilita todas as interrupções de alta prioridade
    INTCONbits.GIEL = 0; // Desabilita todas as interrupções de baixa prioridade
}

// função de configuração da Interrupção externa 0

void config_int0(void) { // configurações específicas a INT0
    INTCONbits.INT0IE = 1; // Ativa a inerrupção externa INT0 (RB0)
    INTCON2bits.INTEDG0 = 0; // Interrupção externa INT0 na borda de descida
    INTCONbits.INT0IF = 0; // Limpa o flag bit da interrupção externa INT0
}

// função que gera atraso em mlisegundos
void delay_ms(int i){
    for ( int x = 0; x < i; x++ )     { 
    __delay_ms(1); 
    }
}

unsigned char testa255(unsigned char ns) {
    // ns = 0 escreve "senha"
    // ns = 1 escreve "senha 1" ou 2 escreve "senha 2"
    int pass = 999;
    unsigned char fl_testa = 1;
    while (fl_testa) {
        pass = le_3_tec(ns);
        if (pass > 255) {
            lcd_limpa_tela();
            lcd_posicao(1, 1);
            imprime_string_lcd("ERRO");
            lcd_posicao(2, 1);
            imprime_string_lcd("Senha > 255");
            delay_ms(2000);
        } else fl_testa = 0;
    }
    return (char) pass;
}

/*
 * Função que lê 3 num do teclado de 0 a 9
 * Backspace tecla *
 * OK tecla # Saída da função e retorna um num de 0 a 999
 */
int le_3_tec(unsigned char ns1) {
    // ns1 = 0 escreve "senha"
    // ns1 = 1 escreve "senha 1" ou 2 escreve "senha 2"
    unsigned char i = 0, fl_3 = 1, num_asc[4] = {48, 48, 48, 48};
    int num = 0;
    unsigned char buffer[16];
    lcd_posicao(1, 1);
    if (ns1 != 0) sprintf(buffer, "Digite senha %d ", ns1);
    else sprintf(buffer, "Digite senha   ");
    imprime_buffer_lcd(buffer, 15);
    //    imprime_string_lcd("Digite senha   ");
    lcd_posicao(2, 1);
    imprime_string_lcd("      * <- # OK ");
    imprime_buffer_lcd(buffer, 3);
    while (fl_3) {
        if (fl_tec) {
            fl_tec = 0; // desliga flag que avisa que tecla chegou
            //            lcd_posicao(1, 14);
            //            sprintf(buffer, "%d", i+1);
            //            imprime_buffer_lcd(buffer, 1);
            if (tec == 35) { // tec # OK
                if (i == 1) {
                    num_asc[0] = num_asc[2];
                    num_asc[2] = 48; //num_asc[1]=48;
                } else if (i == 2) {
                    num_asc[0] = num_asc[1];
                    num_asc[1] = num_asc[2];
                    num_asc[2] = 48;
                }
                if (i < 4) i = 4;
            } else if (tec == 42) { // tec * Backspace
                if (i == 1) num_asc[2] = 48;
                if (i == 2) num_asc[1] = 48;
                if (i == 3) num_asc[0] = 48;
                if (i > 0) i--;
                else i = 0;
            } else if (i < 3) {
                if (tec < 58 && tec > 47) { // garante que só vale teclas de 0 a 9 para registrar
                    num_asc[2 - i] = tec;
                    i++;
                }
            }
            if (i == 4) { //se # - OK foi pressionado
                fl_3 = 0;
                num = (num_asc[2] - 48)*100 + (num_asc[1] - 48)*10 + (num_asc[0] - 48); // converte asc para num
            }
            lcd_posicao(2, 1);
            if (i == 0) sprintf(buffer, "   ");
            if (i == 1) sprintf(buffer, "%d  ", num_asc[2] - 48);
            if (i == 2) sprintf(buffer, "%d%d ", num_asc[2] - 48, num_asc[1] - 48);
            if (i == 3) sprintf(buffer, "%d%d%d", num_asc[2] - 48, num_asc[1] - 48, num_asc[0] - 48);
            imprime_buffer_lcd(buffer, 3);
        }
    }
    return num;
}

void troca_senha(void) {
    unsigned char fl_4 = 1;
    unsigned char buffer[16];
    int senha1 = 0, senha2 = 0;
    while (fl_4) {
        lcd_limpa_tela();
        lcd_posicao(1, 1);
        imprime_string_lcd("Troca de senha ");
        delay_ms(2000);
        lcd_posicao(1, 1);
        imprime_string_lcd("Digite         ");
        lcd_posicao(2, 1);
        imprime_string_lcd("Senha 1        ");
        delay_ms(1000);
        senha1 = testa255(1);
        lcd_posicao(2, 1);
        sprintf(buffer, "Senha 1 = %03d  ", senha1);
        imprime_buffer_lcd(buffer, 16);
        delay_ms(2000);
        lcd_posicao(1, 1);
        imprime_string_lcd("Digite         ");
        lcd_posicao(2, 1);
        imprime_string_lcd("Senha 2        ");
        delay_ms(1000);
        senha2 = testa255(2);
        lcd_posicao(2, 1);
        sprintf(buffer, "Senha 2 = %03d  ", senha2);
        imprime_buffer_lcd(buffer, 16);
        delay_ms(2000);
        if (senha1 == senha2) {
            if (senha1 == 255) {
                lcd_limpa_tela();
                lcd_posicao(1, 1);
                imprime_string_lcd("ERRO senha     ");
                lcd_posicao(2, 1);
                imprime_string_lcd(" Nao pode 255  ");
                delay_ms(2000);
            } else {
                lcd_limpa_tela();
                lcd_posicao(1, 1);
                imprime_string_lcd("Senha 1=Senha 2");
                lcd_posicao(2, 1);
                imprime_string_lcd("Gravando senha ");
                Eeprom_write(0, senha1);
                delay_ms(2000);
                fl_4 = 0;
            }
        } else {
            lcd_limpa_tela();
            lcd_posicao(1, 1);
            imprime_string_lcd("ERRO de senhas ");
            lcd_posicao(2, 1);
            sprintf(buffer, "  %03d <> %3d  ", senha1, senha2);
            imprime_buffer_lcd(buffer, 16);
            delay_ms(2000);
        }
    }
}

void Eeprom_write(char addr, char data) {
    char intcon_temp;
    while (EECON1bits.WR); // se uma escrita estiver em andamento, aguarda
    EEADR = addr; // configura o endereço inicial da escrita
    EECON1bits.EEPGD = 0; // Seleciona  área de mem = eeprom
    EECON1bits.CFGS = 0; // Seleciona  área de mem = eeprom   
    EECON1bits.WREN = 1; // escrita permitida (1))
    EEDATA = data; // escreve no registrador EEDATA o dado a ser gravado
    intcon_temp = INTCON; // Guarda o valor do registrador INTCON
    INTCON = 0; // Erase INTCON e disable interrups
    EECON2 = 0x55; // senha de escrita em memória
    EECON2 = 0xAA;
    EECON1bits.WR = 1; // habilita a escrita , volta a zero no fim da op
    while (EECON1bits.WR); // aguarda o fim da escrita, pois leva 4ms
    INTCON = intcon_temp; // restaura o valor do registrador INTCON
}

unsigned char Eeprom_read(unsigned char addr) {
    EEADR = addr; // EEADR recebe o endereço passado para a função
    EECON1bits.EEPGD = 0; // Seleciona  área de mem = eeprom
    EECON1bits.CFGS = 0; // Seleciona  área de mem = eeprom
    EECON1bits.FREE = 0; // modo de apagamento de flash não selecionada (0)
    EECON1bits.WREN = 0; // escrita não permitida (0))
    EECON1bits.RD = 1; // habilita opção de leitura, volta a zero no fim da op
    return (EEDATA); // retorna o conteúdo lido (de EEDATA)
}