segunda-feira, 11 de julho de 2016

Usando 2 portas seriais no mesmo microcontrolador - 16F1947

         Olá, as vezes queremos usar 2 portas seriais ao mesmo tempo a melhor forma de se fazer isso e usar um microcontrolador que possua 2 portas UART(COM) em seu hardware, graças a compatibilidade entre os microcontroladores e com poucas alterações e possível fazer com que um mesmo código execute as configurações da serial e imprima nas duas portas sem grandes dificuldades.
          Neste exemplo tudo que é digitado em qualquer uma das portas é retornado na mesma porta. Veja o código fonte do aquivo "Main.c" e do "serial16F1947.c". A simulação do código é vista na figura a seguir, veja que neste exemplo na serial 1 pressionei F e na serial 2 pressionei H e então recebi o mesmo carácter de volta ficando FF e HH.


O código fonte:


/*
 *               testando hardware do microcontrolador - 2 serial        
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F1947
 * Autor: aguivone
 * Versão: 1
 * Data :  11/07/2016*/
#include <xc.h>
#include <stdio.h>
#include "serial16F1947.c"

#define _XTAL_FREQ 16000000//usado para rotinas de  delays
#define LED               LATDbits.LATD4
#define LER_LED           PORTDbits.RD4

//
///////////configuração dos fuses bits/////////////////////////////////////////////
// CONFIG1
#pragma config FOSC = HS        // Oscillator Selection (HS Oscillator, High-speed crystal/resonator connected between OSC1 and OSC2 pins)
#pragma config WDTE = OFF       // Watchdog Timer Enable (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable (PWRT disabled)
#pragma config MCLRE = ON       // MCLR Pin Function Select (MCLR/VPP pin function is MCLR)
#pragma config CP = OFF         // Flash Program Memory Code Protection (Program memory code protection is disabled)
#pragma config CPD = OFF        // Data Memory Code Protection (Data memory code protection is disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable (Brown-out Reset disabled)
#pragma config CLKOUTEN = OFF   // Clock Out Enable (CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin)
#pragma config IESO = ON        // Internal/External Switchover (Internal/External Switchover mode is enabled)
#pragma config FCMEN = ON       // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor is enabled)

// CONFIG2
#pragma config WRT = OFF        // Flash Memory Self-Write Protection (Write protection off)
#pragma config VCAPEN = OFF     // Voltage Regulator Capacitor Enable (VCAP pin functionality is disabled)
#pragma config PLLEN = OFF      // PLL Enable (4x PLL disabled)
#pragma config STVREN = OFF     // Stack Overflow/Underflow Reset Enable (Stack Overflow or Underflow will not cause a Reset)
#pragma config BORV = LO        // Brown-out Reset Voltage Selection (Brown-out Reset Voltage (Vbor), low trip point selected.)
#pragma config LVP = OFF        // Low-Voltage Programming Enable (High-voltage on MCLR/VPP must be used for programming)
//////////////////////////////////////////////////////////////////////////////////

//************************************************************************************/
//VARIAVEIS GLOBAIS
//************************************************************************************/

////variaveis da porta serial
char   gcCaracter;
//*******************************************************************************/
//===================================================================================
//Função:    _interrupcoes
//Descrição: usada para tratar as interrupções geradas pelo microcontrolador.
//===================================================================================
void interrupt interrupcoes(void)//vetor de interrupção
 {
      if(RC1IF)//verifica interrupção da serial na COM1
    {//interrupção da serial
        gcCaracter = RC1REG;   
        escreve_RS232(gcCaracter,0);//finaliza string
        RC1IF = 0;//  limpa flag de interrupção de recepção
    }
    if(RC2IF)//verifica interrupção da serial na COM2
    {//interrupção da serial
        gcCaracter = RC2REG;
        escreve_RS232(gcCaracter,1);//finaliza string
        RC2IF = 0;//  limpa flag de interrupção de recepção
    }
}

//===================================================================================
//Função:    _config_CPU
//Parâmetros: não tem
//Retorno:   não tem retorno.
//Descrição: inicializa os registradores do microcontrolador.
//===================================================================================
void config_CPU()
{
    //configura portas logicas
    TRISA = 0X00;
    TRISB = 0X00;
    TRISC = 0x80;
    TRISD = 0x00;
    TRISE = 0x00;
    TRISF = 0x00;//todos entrada e ainda são as entrasas analogicas
    TRISG = 0x04;
    // inicializa portas logicas configuradas como saida para 0
    LATA = 0;
    LATB = 0;
    LATC = 0;
    LATD = 0;
    LATE = 0;
    LATF = 0;
    LATG = 0;
    inicializa_RS232(9600,1,0);//modo de alta velocidade inicializa COM1
    inicializa_RS232(9600,1,1);//modo de alta velocidade inicializa COM2
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit  
    __delay_ms(100);
    
}
void main(void) 
{
    config_CPU(); 
    imprime_RS232("Imprimindo: COM 1!\r\n",0); 
    imprime_RS232("Digite um caracter:\r\n",0); 
    imprime_RS232("Imprimindo: COM 2!\r\n",1);  
    imprime_RS232("Digite um caracter:\r\n",1);
    for(;;)
    { 
       //faz nada          
    }
}

O código do arquivo "serial16F1947.c"

/*
 *                          FUNÇOES USADAS NA SERIAL
 *
 * Compilador :      MPlabXC8
 * Microcontrolador: 16F1947
 * Autor:            Aguivone
 * Versão:           1
 * Data de criação:  11 de julho de 2016.
 */

#include <xc.h>
#include <stdio.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#define _XTAL_FREQ 16000000//usado para rotinas de  delays

//===================================================================================
//Função:    _inicializa_RS232
//Parâmetros: unsigned long ulVelocidade
//          : unsigned int uiModo
//          : porta serial(1 ou 0) 
//Retorno:   não tem retorno.
//Descrição: usada para iniciar a porta serial.
//===================================================================================
void inicializa_RS232(unsigned long ulVelocidade,unsigned int uiModo,int porta)
{//// por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.
    // verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
    unsigned long valor;
    if(porta == 0)
    {
        RC1STA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.        
            if(uiModo == 1)
            {//modo = 1 ,modo alta velocidade
             TX1STA = 0X24;//modo assincrono,trasmissao 8 bits.
             valor =(((_XTAL_FREQ/ulVelocidade)-16)/16);//calculo do valor do gerador de baud rate
            }
            else
            {//modo = 0 ,modo baixa velocidade
             TX1STA = 0X20;//modo assincrono,trasmissao 8 bits.
             valor =(((_XTAL_FREQ/ulVelocidade)-64)/64);//calculo do valor do gerador de baud rate
            }
        SP1BRG = valor;
        RC1IE = 1;//habilita interrupção de recepção
        TX1IE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)

    }
    else
    {
       RC2STA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.
        if(uiModo == 1)
        {//modo = 1 ,modo alta velocidade
         TX2STA = 0X24;//modo assincrono,trasmissao 8 bits.
         valor =(((_XTAL_FREQ/ulVelocidade)-16)/16);//calculo do valor do gerador de baud rate
        }
        else
        {//modo = 0 ,modo baixa velocidade
         TX2STA = 0X20;//modo assincrono,trasmissao 8 bits.
         valor =(((_XTAL_FREQ/ulVelocidade)-64)/64);//calculo do valor do gerador de baud rate
        }
    SP2BRGL = valor;
    SP2BRGH = valor<<8;
    RC2IE = 1;//habilita interrupção de recepção
    TX2IE = 0;//deixa interrupção de transmissão desligado(pois corre se o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
    }
    
}
//===================================================================================
//Função:    _escreve1_RS232
//Parâmetros: char cValor, int porta
//Retorno:   não tem retorno.
//Descrição: usada para escrever 1 caracter.
//===================================================================================
 void escreve_RS232(char cValor,int porta)
{
     if(porta == 0)
    {
        TX1IF = 0;//limpa flag que sinaliza envio completo.
        TX1REG = cValor;
        while(TX1IF ==0);//espera enviar caracter
    }
    else
    {
        TX2IF = 0;//limpa flag que sinaliza envio completo.
        TX2REG = cValor;
        while(TX2IF ==0);//espera enviar caracter
    }
     __delay_us(10);//só pra garantir
}
 
//===================================================================================
//Função:    _imprime_RS232
//Parâmetros: const char ccFrase[], int porta
//Retorno:   não tem retorno.
//Descrição: usada para escrever uma string(vetor de caracteres).
//===================================================================================
 void imprime_RS232(const char ccFrase[],int porta)
{
     unsigned char indice = 0;
     unsigned char tamanho = strlen(ccFrase);     
      while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas
       {
           escreve_RS232(ccFrase[indice],porta);
           indice++;
       }
}


Controlando PWM conforme a variação de temperatura(sensor LM35).

         Olá, pessoal hoje vou deixar um exemplo de como alterar a largura de pulso de um sinal(PWM) conforme varia a temperatura.Para fazer este controle simples  criei 2 variáveis que define a partir de quando se deve reduzir ou aumentar a largura do pulso, ficando ainda uma região em que não aumenta e nem diminuí(zona de operação).veja afigura a seguir:



        Com a ideia deste exemplo é possível  controlar uma resistência elétrica,ventilador (ou cooler), células peltier, motores,transformadores e uma infinidades de circuitos que precisem de usar PWM para seu controle e que dependam da temperatura, neste exemplo foi usado o sensor de temperatura LM35. na imagem abaixo é visualizado a simulação no Proteus:



O Código Fonte:

/*
 *                 Fazendo um controle sim-ples de temperatura X PWM
 *
 * Compilador :      MPlabXC8
 * Microcontrolador: 18F13K22
 * Autor:            Aguivone
 * Versão:           1
 * Data de criação:  11/07/2016.
 * Exemplo de como fazer um controle simples de PWM com o LM35 
 */

#include <string.h> //para usar funçoes de string 
#include <xc.h>
#define led               LATBbits.LATB4
#define ler_led           PORTBbits.RB4
#define _XTAL_FREQ        16000000//usado para rotinas de  delays

//************************************************************************************/
//VARIAVEIS GLOBAIS
//************************************************************************************/
char   gcCaracter;
long   glTemperatura = 0;// em °C
long   glTemp_min = 18;// menor temperatura em °C 
long   glTemp_max = 20;// maior temperatura em °C
long   glCorrente = 0;// em A
//pwm
long    uiMax_passos = 0;//frequencia em khz
int    uiDuty = 30;//25% da largura de pulso
int    uiTempo = 0;

//serial 
unsigned char gucBufferSerial[5];     // Buffer da serial
volatile unsigned int  guiTamBuffer;        // Usado para contar o tamanho do buffer de recepção.
volatile unsigned char gucRecBufferStatus = 'S';

/*******************************configuraçôes do pic ****************************************************/

// CONFIG1H
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config PLLEN = OFF      // 4 X PLL Enable bit (PLL is under software control)
#pragma config PCLKEN = ON      // Primary Clock Enable bit (Primary clock enabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRTEN = OFF     // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)
#pragma config BORV = 19        // Brown Out Reset Voltage bits (VBOR set to 1.9 V nominal)

// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer Enable bit (WDT is controlled by SWDTEN bit of the WDTCON register)
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config HFOFST = ON      // HFINTOSC Fast Start-up bit (HFINTOSC starts clocking the CPU without waiting for the oscillator to stablize.)
#pragma config MCLRE = OFF      // MCLR Pin Enable bit (RA3 input pin enabled; MCLR disabled)

// CONFIG4L
#pragma config STVREN = ON      // Stack Full/Underflow Reset Enable bit (Stack full/underflow will cause Reset)
#pragma config LVP = OFF         // Single-Supply ICSP Enable bit (Single-Supply ICSP enabled) desliga programação em baixa voltage
#pragma config BBSIZ = OFF      // Boot Block Size Select bit (512W boot block size)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection bit (Block 0 not code-protected)
#pragma config CP1 = OFF        // Code Protection bit (Block 1 not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 not write-protected)
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot block not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot block not protected from table reads executed in other blocks)

//todas as interrupções são tratadas aqui
void interrupt interrupcoes(void)//vetor de interrupção
 {
    if(ADIF)
    {//se interrupção do modulo analogico!
        ADIF = 0;//limpa flag
    }
   /*
    * interrupções que não serão usada,mas fica aqui para caso alguém precise usar
    * 
    *  if(RCIF )
    {//interrupção da serial
      RCIF = 0;//  limpa flag de interrupção de recepção 
    }  
    if(TMR2IF)
    {//interrupção do timer2
      TMR2IF = 0;//não será usado      
    }
    if(CCP1IF)
    {//interrupção do ccp1
      CCP1IF = 0;//não será usado
    }   */  
 }
//inicializa a serial 
void inicializa_RS232(unsigned long ulVelocidade,unsigned int uiModo)
{//// por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.
    // verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
    RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.
    int valor;
        if(uiModo == 1)
        {//modo = 1 ,modo alta velocidade
         TXSTA = 0X24;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/ulVelocidade)-16)/16);//calculo do valor do gerador de baud rate
        }
        else
        {//modo = 0 ,modo baixa velocidade
         TXSTA = 0X20;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/ulVelocidade)-64)/64);//calculo do valor do gerador de baud rate
        }
    SPBRG = valor;
   // RCIE = 1;//habilita interrupção de recepção
    RCIE = 0;//desabilita interrupção de recepção - neste exemplo não vamos receber nada
    TXIE = 0;//deixa interrupção de transmissão desligado(pois corre o risco de ter uma interrupção escrita e leitura ao mesmo tempo)
}
//cria função pra enviar 1 caracter
 void escreve_RS232(char cValor)
{
    TXIF = 0;//limpa flag que sinaliza envio completo.
    TXREG = cValor;
    while(TXIF ==0);//espera enviar caracter
    __delay_ms(1);
}
 //cria a funcão semelhante ao "print"(pois a funcão nativa não funciona tão bem)  
 void imprime_RS232(const char ccFrase[])
{
     unsigned char indice = 0;
     unsigned char tamanho = strlen(ccFrase);
      while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas
       {
           escreve_RS232(ccFrase[indice]);
           indice++;
       }
}
 //convete um numero para uma string de numeros, com escolha de quantos digitos será usado
void long_to_char_RS232(unsigned long  ulQuant,int iTam)
{
        char cTexto[7];
        int  iValor = 0;
        while(iValor < 6 )
        {
             cTexto[iValor]='0';
             iValor++;
        }
         while(ulQuant>=10000)
            {
             ulQuant=ulQuant-10000;
             cTexto[6]++;
             }
         while(ulQuant>=10000)
            {
             ulQuant=ulQuant-10000;
             cTexto[5]++;
             }
         while(ulQuant>=1000)
            {
             ulQuant=ulQuant-1000;
             cTexto[4]++;
             }
          while(ulQuant>=100)
            {
             ulQuant=ulQuant-100;
             cTexto[3]++;
             }
           while(ulQuant>=10)
            {
             ulQuant=ulQuant-10;
             cTexto[2]++;
             }
           while(ulQuant>=1)
            {
             ulQuant=ulQuant-1;
             cTexto[1]++;
             }
        iValor = iTam ;
          while(iValor != 0 )
           {
              escreve_RS232(cTexto[iValor]);              
              iValor--;
           }
}
//usado para ler o LM35
unsigned long ler_adc_un(void)
{
    unsigned long ultensao;
    ADCON0bits.GO = 1; //inicia conversão
    while (ADCON0bits.GO_DONE == 1); //espera finalizar leitura
    ultensao = ADRES;//(ADRESH << 8) | ADRESL;
    return(ultensao);
}
//usado para deixar as amostras mais confiaveis
void ler_ADC(int giMedia)//numero de amostras
{
      int iMedia=0;      
      glTemperatura = 0;
      while(iMedia < giMedia)
      {
        glTemperatura = glTemperatura +  ler_adc_un();
        __delay_us(2);//tempo minino para aquisições
        iMedia++;
      } 
      glTemperatura = glTemperatura/giMedia;
      if(glTemperatura <= 1)
        {
        glTemperatura = 0;   
        }
        else
        {
          glTemperatura = glTemperatura/2 ;
          //agora é só corrigir a não linearidade do sensor
          if(glTemperatura>24)
          {
           glTemperatura--;
          }
           if(glTemperatura>64)
          {
            glTemperatura--;
          }
          if(glTemperatura>103)
          {
             glTemperatura = 100;//valor maximo aceitavel
          }
        }
}
//configura o pwm
void inicializa_PWM1(char prescaler,char pr2,char postcaler)//prescaler / PR2 / postcaler
{
    uiMax_passos =(int) pr2;
    switch(prescaler)
    {
        case 1:
        {
           prescaler = 0;
        }break;
        case 4:
        {
           prescaler = 1;
        }break;
        case 16:
        {
           prescaler = 3;
        }break;
    }
    T2CON = (postcaler - 1)<<3;
    T2CON = T2CON + 4 + prescaler;//o 4 é para habilitar o timer 2
    PR2 = pr2;
    CCP1CON = 0X0F;//habilita pwm
   // desabilita interupções de ccp e timer 2
    CCP1IE = 0;//habilita modulo ccp1
    TMR2IE = 0; //habilita desliga interrupcçaõde  timer 2
}

//copnfigurações iniciais
void inicializa_cpu(void)
{
    TRISC = 0xDF;
    TRISA = 0XFF;//configura portA como entrada
    TRISB = 0X20;//configura portB  B5 (pino RX) como entrada
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    inicializa_RS232(9600,1);//modo de alta velocidade
    ADCON0 = 0X21;//liga modulo analogico digital e configura   entrada de sinal para o pino RA2(AN2)
    ADCON1 = 0X00;// Vref- seta ao Vss e  Vref+ ao Vdd
    ADCON2 = 0b10111110;// justificado a direita// tempo de aquisição 20TAD // clk Fosc/64    
    ANSEL = 0x00;//desabilita portas analogicas(para não atrapalhar recepção no pino)/hab ra2
    ANSELH = 0x01;//em alguns casos não funciona nem a interrupção de recepção.
    imprime_RS232("Controlador LM35 X PWM. \n\r");
    imprime_RS232("Temperatura :\n\r");
    inicializa_PWM1(1,40,1); //100khz veja no comentario abaixo "SUGESTÃO DE DEFINES :"    
    CCPR1 = uiDuty;
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
}
//*******************************Rotina principal*********************************************/
void main(void)
{
  inicializa_cpu();
  for(;;)
    {
       if(uiTempo == 0)
       {
           long_to_char_RS232(glTemperatura,3); 
           imprime_RS232("\n\r");
           ler_ADC(6);//pega 6 amostras para tirar media  
           if(glTemperatura > glTemp_max)
           {
                if(uiDuty > 25)
                        {// limita um valor maximo para variar, mas se quiser pode por até 0;
                           uiDuty --;//decrementa variavel  
                        }
                        CCPR1 = uiDuty;
           }           
           if(glTemperatura < glTemp_min)
           {
              if(uiDuty < 40)//o max é 40 passos nessa frequencia
               {
                  uiDuty++;//incrementa variavel
               } 
               CCPR1 = uiDuty;  
           }              
           led = ~ler_led;
           uiTempo = 50;//seta tempo           
       }
       else
       {
          uiTempo--; 
       } 
       __delay_ms(1);
    }//loop infinito
}

/*calculos para pwm
    *
    * periodo do pwm
    *
    *  Tpwm = [PR2+1]*4*Tosc*prescaler(do timer2)
    *
    * duty cycle
    *             <bits 5e4>
    * D = (CCPR1L + CCP1CON) * Tosc * prescaler (do timer2)
    *
    * numero de passos do pwm
    *
    * N = (Fosc/(Fpwm * 4 * prescaler)) 
    */


/*
SUGESTÃO DE DEFINES :
 * use estes valores como referencia
 *
//clock de 4mhz
#define PWM_250HZ_CLK4         inicializa_PWM1(16, 250, 1) //
#define PWM_500HZ_CLK4         inicializa_PWM1(16, 125, 1) //
#define PWM_1KHZ_CLK4          inicializa_PWM1(4, 250, 1)  //
#define PWM_1K25HZ_CLK4        inicializa_PWM1(16, 50, 1)  //
#define PWM_2KHZ_CLK4          inicializa_PWM1(4, 125, 1)  //
#define PWM_2K5HZ_CLK4         inicializa_PWM1(4, 100, 1)  //
#define PWM_4KHZ_CLK4          inicializa_PWM1(1, 250, 1)  //
#define PWM_5KHZ_CLK4          inicializa_PWM1(1, 200, 1)  //
#define PWM_8KHZ_CLK4          inicializa_PWM1(1, 125, 1)  //
#define PWM_10KHZ_CLK4         inicializa_PWM1(1, 100, 1)  //
#define PWM_20KHZ_CLK4         inicializa_PWM1(1,  50, 1)  //
#define PWM_25KHZ_CLK4         inicializa_PWM1(1,  40, 1)  //
#define PWM_100KHZ_CLK4        inicializa_PWM1(1, 10, 1)   //
//clock de 8mhz
#define PWM_500HZ_CLK8         inicializa_PWM1(16, 250, 1) //
#define PWM_1KHZ_CLK8          inicializa_PWM1(16, 125, 1) //
#define PWM_2KHZ_CLK8          inicializa_PWM1(4, 250, 1)  //
#define PWM_2K5HZ_CLK8         inicializa_PWM1(16, 50, 1)  //
#define PWM_4KHZ_CLK8          inicializa_PWM1(4, 125, 1)  //
#define PWM_5KHZ_CLK8          inicializa_PWM1(4, 100, 1)  //
#define PWM_8KHZ_CLK8          inicializa_PWM1(1, 250, 1)  //
#define PWM_10KHZ_CLK8         inicializa_PWM1(1, 200, 1)  //
#define PWM_16KHZ_CLK8         inicializa_PWM1(1, 125, 1)  //
#define PWM_20KHZ_CLK8         inicializa_PWM1(1, 100, 1)  //
#define PWM_40KHZ_CLK8         inicializa_PWM1(1,  50, 1)  //
#define PWM_50KHZ_CLK8         inicializa_PWM1(1,  40, 1)  //
#define PWM_200KHZ_CLK8        inicializa_PWM1(1, 10, 1)   //
//clock de 16mhz
#define PWM_1KHZ_CLK16         inicializa_PWM1(16, 250, 1) //
#define PWM_2KHZ_CLK16         inicializa_PWM1(16, 125, 1) //
#define PWM_4KHZ_CLK16         inicializa_PWM1(4, 250, 1)  //
#define PWM_5KHZ_CLK16         inicializa_PWM1(16, 50, 1)  //
#define PWM_8KHZ_CLK16         inicializa_PWM1(4, 125, 1)  //
#define PWM_10KHZ_CLK16        inicializa_PWM1(4, 100, 1)  //
#define PWM_16KHZ_CLK16        inicializa_PWM1(1, 250, 1)  //
#define PWM_20KHZ_CLK16        inicializa_PWM1(1, 200, 1)  //
#define PWM_32KHZ_CLK16        inicializa_PWM1(1, 125, 1)  //
#define PWM_40KHZ_CLK16        inicializa_PWM1(1, 100, 1)  //
#define PWM_80KHZ_CLK16        inicializa_PWM1(1,  50, 1)  //
#define PWM_100KHZ_CLK16       inicializa_PWM1(1,  40, 1)  //
#define PWM_400KHZ_CLK16       inicializa_PWM1(1, 10, 1)   //
*/




quarta-feira, 25 de maio de 2016

Usando CI MAX31855K(para termopar) com PIC16F648A

    Olá,
    Para esta semana preparei um exemplo que vale por 2, pois vou demonstrar como comunicar
via SPI com um microcontrolador que não possui hardware pra isso(ou seja vou emular)
outro detalhe é usar o chip max31855K.
       O maior limitante deste projeto foi a memoria de programa pequena do microcontrolador para
fazer cálculos(tive que limitar o range de -25° a 300°c), por não conhecer o sensor termopar julguei
erradamente que o sinal seria totalmente linear foi não era linear,quando percebi que era necessário
montar um polinômio para que o resultado se aproxime do valor o mais próximo possível, então foi 
montado varias equações para cobrir todo o range.
        Logo depois percebi que conforme a temperatura ambiente muda isso interfere também na leitura
 da temperatura,mas dai já havia montado as equações para uma temperatura ambiente de 25°C,então
foi montado a função "compensa_vari_ambiente() " para que a equação se comportasse de forma
dinâmica ou seja independente da temperatura e mesmo que o valor lido no sensor fosse variado a 
temperatura sempre ficava próximo do esperado.A função "debugue(int val)" serve para visualizar
o pacote recebido do chip(para isso este bloco do programa deve ser descomentado e chamado).
       O circuito montado para simulação foi este:

    
  Depois de tudo funcionando no proteus coloquei o circuito pra funcionar na pratica e então 
pra minha surpresa o valor lido era completamente diferente da simulação, embora a temperatura
ambiente tenha ficado correta.Enfim não sei se o termopar tipo K que estou usando é diferente 
ou se o chip max31855K não está coerente, vou investigar para descobrir o que aconteceu.Mas 
resolvi postar o código aqui para haver uma troca de experiencia e espero que de alguma forma 
essas informações seja uteis pra alguém.
O código fonte: 

/*
 *                              sensor de temperatura termopar com max31855K
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 16F648A
 * Autor: aguivone
 * Versão: 1
 * Data :  29/04/2016
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h> //para usar funçoes de string deve se adicionar este header
#define _XTAL_FREQ 4000000    // oscilador interno de 20 Mhz
#include <xc.h>
/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
#define SPI_DADOS  PORTBbits.RB0
#define SPI_CLCK   PORTBbits.RB3
#define SPI_CS     PORTBbits.RB5
#define LED_STATUS PORTBbits.RB7
#define TEMPO_LED  1000

// CONFIG
#pragma config FOSC = INTOSCIO  // Oscillator Selection bits (INTOSC oscillator: I/O function on RA6/OSC2/CLKOUT pin, I/O function on RA7/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF      // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is digital input, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config LVP = OFF        // Low-Voltage Programming Enable bit (RB4/PGM pin has digital I/O function, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EE Memory Code Protection bit (Data memory code protection off)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

unsigned long gulTempo_us=0;
unsigned long gulTempo_ms=0;
unsigned long gulLed_status_tempo=0;
unsigned long gulTempo_aux_ms=0;
unsigned long gulTemp_termopar=0;
unsigned long gulTemp_chip=0;


unsigned char gucSinal_termopar=' ';//se + não precisa de colocar nada 
unsigned char gucSinal_termp_chip=' ';//se + não precisa de colocar nada

bit gbErro_ausente=0;
bit gbErro_curto_vcc=0;
bit gbErro_curto_gnd=0;
bit gbIndica_erro =0;

void interrupt interrupcao(void)//vetor de interrupção
 {
    if(T0IF)
    {//interrupção do timer0 - estoura quando atinge 255(0XFF)
        gulTempo_us++;
        if(gulTempo_us == 13)
        { //acontece a cada 1ms
            gulTempo_us =0;
            gulLed_status_tempo++;            
            gulTempo_ms++;
            if(gulTempo_aux_ms>0)
            { gulTempo_aux_ms--;}
            if(gulTempo_ms == 1000)
            {//acontece a cada 1s
              gulTempo_ms = 0;  
            }
        }
        TMR0 = 254;//reinicia timer com o valor calculado
        T0IF = 0;
        //62.5us + os delay da rotinas  = 75us
    }
 }

void inicializa_timer0()
{
    OPTION_REG = 0X03;//timer0 com prescaler dividido por 16
    T0IE = 1;//habilita interrupção do timer0
    TMR0 = 254;//zera registrador de contagem
}
/////////////////////////////////funçoes usadas pela uart //////////////////////////////////////////////////////
void inicializa_RS232(long velocidade,int modo)
{////por padrão é usado o modo 8 bits e sem paridade, mas se necessario ajuste aqui a configuração desejada.
    //verifique datasheet para ver a porcentagem de erro e se a velocidade é possivel para o cristal utilizado.
    RCSTA = 0X90;//habilita porta serial,recepção de 8 bit em modo continuo,assincrono.
    int valor;
        if(modo == 1)
        {//modo = 1 ,modo alta velocidade
         TXSTA = 0X24;//modo assincrono,trasmissao 8 bits.
         valor =(int)(((_XTAL_FREQ/velocidade)-16)/16);//calculo do valor do gerador de 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 = 0;//não habilita interrupção de recepção - se habilitar e não tratar a recpção trava o chip(se deixar o pino em aberto quando habilitado é pior))
    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(char valor)
{
    TXIF = 0;//limpa flag que sinaliza envio completo.
    TXREG = valor;
    NOP();
    NOP();//espera 2 ciclos
    while(TXIF == 0);//espera enviar caracter
}
void imprime(const char frase[])
{
     char indice = 0;
     char tamanho = strlen(frase);
      while(indice < tamanho ) ///veja que o programa pode travar se aqui não tiver as duas aspas
       {
           escreve_char(frase[indice]);
           indice++;
       }
}
void long_to_char(long ulQuant,int virgula)
{
       char cTexto[4]={'0','0','0','0'};
       /* char cTexto[5]={'0','0','0','0','0'};
          while(ulQuant>=10000)
            {
             ulQuant=ulQuant-10000;
             cTexto[4]++;
             }
        escreve_char(cTexto[4]);*/
        if(virgula == 4){escreve_char(',');}
         while(ulQuant>=1000)
            {
             ulQuant=ulQuant-1000;
             cTexto[3]++;
             }
          escreve_char(cTexto[3]);
          if(virgula == 3){escreve_char(',');}
          while(ulQuant>=100)
            {
             ulQuant=ulQuant-100;
             cTexto[2]++;
             }
          escreve_char(cTexto[2]);
          if(virgula == 2){escreve_char(',');}
           while(ulQuant>=10)
            {
             ulQuant=ulQuant-10;
             cTexto[1]++;
             }
          escreve_char(cTexto[1]);
          if(virgula == 1){escreve_char(',');}
           while(ulQuant>=1)
            {
             ulQuant=ulQuant-1;
             cTexto[0]++;
             }
          escreve_char(cTexto[0]);
}
void delay_ms(long tempo)
{
 gulTempo_aux_ms = 2;//tempo pra garantir que chip está pronto 
 while(gulTempo_aux_ms>0);
}
/*
 * usado para ver o pacote inteiro do pacote
 * void debugue(int val)
{
    switch(val)
        {
            case 14:
            {
             escreve_char('_');    
            }break;
            case 16:
            {
             escreve_char('_');    
            }break;
            case 28:
            {
             escreve_char('_');    
            }break;
        }
        if(SPI_DADOS == 1)
        {
           escreve_char('1'); 
        }
        else
        {
          escreve_char('0'); 
        }
}*/
void Ler_sensor()
{//lê e já trata informação
    SPI_CS = 0;
    int contador=0;
    long multiplicador =1;
    delay_ms(2);    
    gbErro_ausente=0;///zera variaveis
    gbErro_curto_vcc=0;
    gbErro_curto_gnd=0;
    gbIndica_erro =0;
    gucSinal_termp_chip =' ';//se + não precisa de colocar nada
    gucSinal_termopar =' ';//se + não precisa de colocar nada
    gulTemp_termopar = 0;
    gulTemp_chip=0;
    while(contador < 32)
    {
        SPI_CLCK = 1;
        delay_ms(20);
        if(SPI_DADOS == 1)//só há processamento quando o bit é 1.
        {//processa dados
            if(contador == 0)
            {//pega sinal da temperatura do termopar
                gucSinal_termopar ='-';
            }
            else
            {
             if(contador <15)
               {//pega temperatura do termopar
                 //2^12 = 4096, pois a contagem comeca em zero e  1 bit é o sinal
                   gulTemp_termopar = gulTemp_termopar + (4096/multiplicador);
                   multiplicador = multiplicador*2; 
               }
               else
               {
                  if((contador > 16)&&(contador < 29))
                    {//pega sinal da temperatura ambiente
                      //2^10 = 1024, pois a contagem comeca em zero e  1 bit é o sinal
                       gulTemp_chip = gulTemp_chip + (1024/multiplicador);
                       multiplicador = multiplicador*2;
                    }
                  if(contador == 16)
                    {//pega sina da temperatura do chip(temperatura ambiente)
                       gucSinal_termp_chip ='-';
                       multiplicador =1;//para pegar a segunda temperatura
                    }
                   if((gbIndica_erro == 1)&&(contador>28))
                   {//se tem erro checa estas possibilidades
                     switch(contador)
                       {
                           case 29:
                           {
                             gbErro_curto_gnd=1;    
                           }break;
                           case 30:
                           {
                            gbErro_curto_vcc=1;    
                           }break;
                           case 31:
                           {
                            gbErro_ausente=1;   
                           }break;
                       } 
                   }
                   else
                   {if(contador == 15)
                   {gbIndica_erro =1;}
                   }
               }
            }
            
                       
        }
        else
        {
                    if(contador == 16)
                    {
                        multiplicador =1;//para pegar a segunda temperatura
                    }
                    if(((contador > 0)&&(contador < 15))||((contador > 16)&&(contador < 29)))
                    {//pega temperatura do termopar
                        multiplicador = multiplicador*2; 
                    }
        }
        ///so pra testar o codigo, se quiser não precisa chamar essa função
      //  debugue(contador);
        /////////////////////
        SPI_CLCK = 0; 
        
        contador++;  
    }
    SPI_CS = 1;
}
/*
 * usado para simular
 * void compensa_vari_ambiente()
{//essa função compensa a variação de temperatura da junta fria do sensor
      long_to_char(gulTemp_termopar,0);
      escreve_char('(');
      if(gulTemp_chip>404)
      {//maior que 25°C
         gulTemp_termopar = gulTemp_termopar-((long)((gulTemp_chip - 404)*0.247));
         long_to_char(gulTemp_termopar,0);
      }
      else
      {//menor que 25°c
         if(gulTemp_chip<404)
          { 
             gulTemp_termopar = gulTemp_termopar+((long)((404 - gulTemp_chip)*0.247));
             long_to_char(gulTemp_termopar,0);
          }
      }
      escreve_char(')');
}*/
void compensa_vari_ambiente()
{//essa função compensa a variação de temperatura da junta fria do sensor
      if(gulTemp_chip>404)
      {//maior que 25°C
         gulTemp_termopar = gulTemp_termopar-((long)((gulTemp_chip - 404)*0.247));
      }
      else
      {//menor que 25°c
         if(gulTemp_chip<404)
          {gulTemp_termopar = gulTemp_termopar+((long)((404 - gulTemp_chip)*0.247));}
      }
}

void trata_termopar()
{///funcionou de -25°C até 300°C - na simulação
      compensa_vari_ambiente();;
         if(gulTemp_termopar>100)
            {              
            gulTemp_termopar = (long)(((gulTemp_termopar/0.395)-250)+ 3.5);//equação base        
            if((gulTemp_termopar>0)&&(gulTemp_termopar<69))//0 até 7
                {
                  gulTemp_termopar = gulTemp_termopar - 2 ;
                  return;//isso garante que depois de processado vá executar os proximos passos
                }
            if((gulTemp_termopar>500)&&(gulTemp_termopar<1008))//50 até 100
                {
                  gulTemp_termopar = (long)gulTemp_termopar*0.995;
                  return;//isso garante que depois de processado vá executar os proximos passos
                }
            /*
             * para reduzir o numero de memoria usada juntei 2 intevalos num só
             * 
            if((gulTemp_termopar>500)&&(gulTemp_termopar<900))//50 até 93
                {
                  gulTemp_termopar = (long)gulTemp_termopar*0.998;
                  return;//isso garante que depois de processado vá executar os proximos passos
                }
            if((gulTemp_termopar>899)&&(gulTemp_termopar<1008))//93 até 100
                {
                  gulTemp_termopar = (long)gulTemp_termopar*0.992;
                  return;//isso garante que depois de processado vá executar os proximos passos
                }
             */
            if((gulTemp_termopar>1007)&&(gulTemp_termopar<1500))//100 até 150passa 
                {
                  gulTemp_termopar = gulTemp_termopar-9;
                  return;//isso garante que depois de processado vá executar os proximos passos
                }
            if((gulTemp_termopar>1499)&&(gulTemp_termopar<1981))//150 até 200passa a ignorar o vem depois da virgula
                {
                  gulTemp_termopar = (long)(gulTemp_termopar/10)*10;
                  return;//isso garante que depois de processado vá executar os proximos passos
                }
            if((gulTemp_termopar>2100)&&(gulTemp_termopar<2490))//200 até 250passa a ignorar o vem depois da virgula
                {
                  gulTemp_termopar = gulTemp_termopar+4;
                  return;//isso garante que depois de processado vá executar os proximos passos
                }
            /*
             * para reduzir o numero de memoria usada
             * if((gulTemp_termopar>2490)&&(gulTemp_termopar<3500))//250 até 300passa a ignorar o vem depois da virgula
                {
                  gulTemp_termopar = gulTemp_termopar+8;
                  return;//isso garante que depois de processado vá executar os proximos passos
                }*/
         }
         else
         {
             if(gulTemp_termopar==99)
              {
                gulTemp_termopar = 0;  
              }
             else
              {
                 escreve_char('-'); 
                 gulTemp_termopar = (long)(251 - (gulTemp_termopar/0.395));//equação base 
                 if((gulTemp_termopar>40)&&(gulTemp_termopar<96))//-1 até -8
                 {
                  gulTemp_termopar = gulTemp_termopar+2;
                  return;//isso garante que depois de processado vá executar os proximos passos
                 }
                 if((gulTemp_termopar>95)&&(gulTemp_termopar<126))//-8 até -13passa a ignorar o vem depois da virgula
                 {
                  gulTemp_termopar = gulTemp_termopar+4;
                  return;//isso garante que depois de processado vá executar os proximos passos
                 }
                 if((gulTemp_termopar>125)&&(gulTemp_termopar<300))//-13 até 150passa a ignorar o vem depois da virgula
                 {
                  gulTemp_termopar = gulTemp_termopar+8;
                  return;//isso garante que depois de processado vá executar os proximos passos
                 }
                 
              }
         }
} 
//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////

void main(void) 
{
    TRISB = 0X03;//configura portB 
    TRISA = 0XFF;//configura portA
    PORTB = 0;  // limpar as portas que estão configuradas como saidas
    PORTA = 0; 
    CMCON = 7;//desabilita comparadores
    inicializa_RS232(9600,1);//modo de alta velocidade
    inicializa_timer0();
    PEIE = 1;//habilita interrupção de perifericos do pic
    GIE = 1; //GIE: Global Interrupt Enable bit
    SPI_CLCK = 0; 
    SPI_CS = 1;
     for(;;)
    {
        if(gulLed_status_tempo > TEMPO_LED)
            {   
              LED_STATUS = ~LED_STATUS;
              gulLed_status_tempo = 0;
             // imprime("\r\n Pacote recebido : \r\n "); ///so pra testar o codigo na aplicação pode ser removido essa linha
              Ler_sensor();
              imprime("\r\nT= ");
              //////converte temperatura do termopar////////////////////////////////////////
            /*  if(gucSinal_termopar == '-')
              {//não será usado aqui 
                // escreve_char('-');
              }
              else
              {
                 trata_temp_positiva();
                  escreve_char('/');
              } */
              trata_termopar();
              long_to_char(gulTemp_termopar,1);
              imprime(" °C\r\nChip= ");
              ///converte temperatura do chip //////////////////////////////////
              if(gucSinal_termp_chip == '-')
              {//converte a parte negativa da temperatura
                gulTemp_chip = (long)gulTemp_chip*6.2;
                gulTemp_chip =(long) 12698 - gulTemp_chip;
                escreve_char('-');  
              }
              else
              {
                gulTemp_chip = (long)gulTemp_chip*6.2;  
              }
              long_to_char(gulTemp_chip,2);
              ///////////////////////////////////////////////////////////////////
              imprime(".");
              if(gbIndica_erro)
              {//tem erro
                  imprime("\r\n Falha:");
                  if(gbErro_ausente)
                  {imprime("\r\n Sensor aberto ou inexistente!");}
                  if(gbErro_curto_vcc)
                  {imprime("\r\n Sensor em curto com o gnd!");}
                  if(gbErro_curto_gnd)
                  {imprime("\r\n Sensor em curto com o vcc!");}                  
              }              
            }
    }//loop infinito

}

quinta-feira, 19 de maio de 2016

Adeus 16F877A ! um substituto para ele.

         Olá, provavelmente muitos que visitam o blog já sabem que o microcontrolador PIC 16F877 e 16F877A será descontinuado (http://www.microchip.com/wwwproducts/en/PIC16F877A ) para suprir a falta deste microcontrolador em projetos antigos e em novos projeto temos a opção de usar o 16F887 (ou suas variantes).Apesar de ser compatível pino a pino conforme mostrado na imagem a seguir,tem algumas pequenas alterações em seu hardware.
         Dentre as alterações as mais relevantes estão; não tem a porta paralela,o pino de "mclr" pode ser usado como um pino de IO,o PORTB tem entradas analógicas totalizando 14 entradas,o oscilador interno é de 8MHZ e o PORTB tem entradas com interrupções de pino, além de uma memoria de programa maior.
         Ou seja é uma troca boa usar o 16F887 em vez do nosso velho amigo 16F877.

terça-feira, 26 de abril de 2016

O código do sensor AMF16 - Lendo tensões AC com o PIC12F675.

             Olá, hoje vou disponibilizar o código do sensor AMF16 (descrito aqui: http://microcontroladores-c.blogspot.com.br/2015/12/medindo-tensao-ac-com-o-sensor-amf-16.html , ainda tenho algumas pcbs que eu havia mandado fazer para este projeto vendo pelo preço que paquei RS = 10,00, caso alguém se interesse), vamos começar pelo o esquemático do circuito:
             O algoritmo usado verifica as tensões e joga em um vetor acumulador e ao encontrar uma decida do sinal por meio de comparações de valores,a probabilidade de ser um pico de tensão é muito grande,veja na figura a seguir como isso ocorre(supondo haver somente 3 pontos pra identificar o pico de tensão AC):
             Todas as especificações estão no manual que montei deem uma olhada neste link http://microcontroladores-c.blogspot.com.br/2015/12/medindo-tensao-ac-com-o-sensor-amf-16.html,
             O microcontrolador usado é o PIC 12F675 como ele não tem serial, foi usado o artificio de emular uma serial,ele apenas envia os dados num intervalo pré definido, sendo então o sinal elétrico convertido em luz através do optoacoplador para que seja ligado diretamente a um microcontrolador secundário sem risco de queimar.

O código fonte:
/*
 *                                    voltimetro via serial          
 *
 * Compilador : MPlabXC8
 * Microcontrolador: 12F675
 * Autor: aguivone
 * Versão: 1
 * Data :  15/09/2015
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>//para trabalhar com string
#include <xc.h>
#define _XTAL_FREQ 20000000

/////////////////////////////////////////////////////////configuraçôes//////////////////////////////////////////////////
// CONFIG
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator: High speed crystal/resonator on GP4/OSC2/CLKOUT and GP5/OSC1/CLKIN)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-Up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = OFF     // GP3/MCLR pin function select (GP3/MCLR pin function is digital I/O, MCLR internally tied to VDD)
#pragma config BOREN = OFF      // Brown-out Detect Enable bit (BOD disabled)
#pragma config CP = OFF         // Code Protection bit (Program Memory code protection is disabled)
#pragma config CPD = OFF        // Data Code Protection bit (Data memory code protection is disabled)

///parametros da porta serial emulada////////////////////////////////////////////////////////////////
#define  TIME_BAUD  97//9600 bps - 20MHZ
#define  BAUD_RX    95//9600 bps - 20MHZ 
/////para simulação do proteus
//#define  TIME_BAUD  202//4800 bps - 20MHZ
//#define  BAUD_RX    198//4800 bps - 20MHZ 

#define  TX  GP2//configura o pino que será TX
#define  RX  GP1//configura o pino que será RX

#define  TAM_MAX  10//tamanho maximo do vetor de amostragem(numero de pontos)
#define  NUM_MED  6//numero de intens para formar a media dos picos
//#define  TEMPO_ENVIO  110//(aprox. 15ms)tempo para enviar os dados(em tempo de rotina))
//#define  TEMPO_ENVIO  1100//(aprox. 150ms)tempo para enviar os dados(em tempo de rotina))
#define  TEMPO_ENVIO  7000//(aprox. 1s)tempo para enviar os dados(em tempo de rotina))

int vetor[TAM_MAX+1];
int tensao=0;
char flag_libera=0;

int ler_adc_un()
{
        int ultensao;
        PIR1bits.ADIF = 0;//limpa flag
        ADCON0bits.GO = 1; //inicia conversão
        while (PIR1bits.ADIF == 0); //espera finalizar leitura
        ultensao = (ADRESH << 8) | ADRESL;
        return(ultensao);
}

/////////////////////Funçoes utilizadas pela serial/////////////////////////////////////////////////////////////
void escreve_char(char dados) {
    int contagem = 0;
    TX = 0;
    while (contagem < 8) //envia dados + stop bit
    {
        __delay_us(TIME_BAUD);
        if ((dados & 0X01) == 0X01) //testa bit menos significativo (LSB)
        {
            TX = 1;
        } else {
            TX = 0;
        }
        dados = dados >> 1; //rotaciona bit
        contagem++;
    }
    __delay_us(TIME_BAUD); //tempo do ultimo bit
    TX = 1; //volta pra nivel alto (fim de transmissão de caracter)
    __delay_us(TIME_BAUD); //stop bit
}
///é interessante observar que está rotina irá trabalhar melhor se for 
///usado na rotina de interrupção de estado no pino, aqui vou deixar na rotina principal por 
// ser um exemplo simples
char ler() {
    int contagem = 0;
    char RX_dados = 0X00;
    if (RX == 0)//tem dados pra ler
    {
        RX_dados = 0X00;
        while (contagem < 8) //recebe dados + stop bit
        {
            __delay_us(BAUD_RX); //compensa algum delay na recepção 
            if (RX == 1) {
                RX_dados = (RX_dados | 0X80); //seta bit mais significativo (MSB)
            }
            RX_dados = RX_dados >> 1;
            contagem++;
        }
        contagem = 0;
        while ((RX == 0) && (contagem < 4)) {
            contagem++; //time out para não travar,tempo de 4 bits
            __delay_us(TIME_BAUD);
        }
    }
    return (RX_dados);
}

void escreve_frase(char *frase) {
    int tam_frase = strlen(frase);
    int contagem = 0;
    while (contagem < tam_frase)           
    {
        escreve_char(frase[contagem]);
        contagem++;
    }

}

void long_to_char(int ulQuant)
{
        char cTexto[4]={'0','0','0','0'};
         while(ulQuant>=1000)
            {
             ulQuant=ulQuant-1000;
             cTexto[3]++;
             }
          escreve_char(cTexto[3]);
          while(ulQuant>=100)
            {
             ulQuant=ulQuant-100;
             cTexto[2]++;
             }
          escreve_char(cTexto[2]);
           while(ulQuant>=10)
            {
             ulQuant=ulQuant-10;
             cTexto[1]++;
             }
          escreve_char(cTexto[1]);
           while(ulQuant>=1)
            {
             ulQuant=ulQuant-1;
             cTexto[0]++;
             }
          escreve_char(cTexto[0]);
          escreve_char('-');
}
void gira_vetor()
{
    int aux=0;
    while(aux<TAM_MAX)
    {
        vetor[aux] = vetor[aux+1];
        aux++;
    } 
}


//////////////////////////////////////////////////////Rotina principal///////////////////////////////////////////////////////////////
void main(void) {
    CMCON = 7;//desabilita comparadores
    ANSEL = 0X31;//usa AN0 como entrada analogica e usa o clock interno do chip(2 a 6 us)
    WPU = 0X00;//desabilita pull ups
    TMR0 = 0;
    ADCON0 = 0X80;//justificado a esquerda , ref ligado ao vdd,usando AN0
    OSCCAL = 0XFF;//configura o oscilador interno para frequencia maxima(4mhz)
    OPTION_REG = 0X81;//pull up desabilitado/preescaler ligado ao timer0(dividido por 4)
    PIR1 = 0X00; //desabilita interrupções não necessaria
    TRISIO = 0X03;//configura gp0 e gp1 como entrada
    INTCON = 0XC0;//desabilita interrupção do timer 0 e habilitainterrupção global e de perifericos
    ADCON0bits.ADON = 1;//liga modulo de conversão
    __delay_ms(300);//debounce e tempo para preparar hardware
    //logo a frequencia de interrupção é 250khz
    char tempo_media = 0;
    int tempo_de_envio = 0;
    int flag=0;
    for(;;)
    {  //
        __delay_us(1); //para garantir uma leitura estavel
        vetor[TAM_MAX] = ler_adc_un();
        if((flag_libera == 1))
        {
             if(vetor[TAM_MAX]<vetor[0])//localizou o pico de tensão
            {
                int valor =0;
                int aux=0;
                while(valor<TAM_MAX)
                {//procura o maior valor
                    if(aux < vetor[valor])
                    {
                        aux = vetor[valor];
                    }
                   valor++;  
                }
              tensao = aux + tensao;//sempre pega a média da leitura de pico atual pela anterior
              if(tempo_media >= NUM_MED)
              {
                 tensao =tensao/(NUM_MED + 2);//sempre pega a média da leitura de pico atual pela anterior
                 if(tempo_de_envio >= TEMPO_ENVIO)
                 {
                  long_to_char(tensao);
                  tempo_de_envio = 0;
                 }
                 tempo_media =0;
              }
              else
              {
              tempo_media++;
              }
              flag_libera=0;
            }   
        }
        else
        {//flag de liberação desligado
           if(vetor[TAM_MAX] > vetor[0])//localizou o inicio de subida
            {
               flag_libera=1;
            }
        }            
        gira_vetor();        
        tempo_de_envio++;        
    }//loop infinito
}


//NOTAS DE PARAMETRIZAÇÃO:

//***********************************************************************************************************
//oscilador interno do pic 12F675(testado fisicamente )//////////////////////////////////////////////////////
// configure assim o fuse do oscilador interno => #pragma config FOSC = INTRCIO
// configure assim o fuse do cristal de 4MHZ => #pragma config FOSC = XT
//
//#define  TIME_BAUD  14//19200 bps - 4MHZ - usando cristal 
//#define  TIME_BAUD  16//19200 bps - 4MHZ - usando oscilador interno
//#define  BAUD_RX    12//19200 bps - 4MHZ - osc. interno - recepção não fica confiavel 

//#define  TIME_BAUD  68//9600 bps - 4MHZ - usando oscilador interno
//#define  BAUD_RX    58//9600 bps - 4MHZ - cristal 4MHZ - recepção funcionou bem(houve pouquissimo erros) - no proteus esse valor não funciona
//#define  BAUD_RX    60//9600 bps - 4MHZ - recepção funcionou bem(houve pouquissimo erros)

//#define  TIME_BAUD  175//4800 bps - 4MHZ 
//#define  BAUD_RX    165//4800 bps - 4MHZ - recepção funcionou muito bem(100% confiavel fisicamente, mas no proteus apresenta falhas)


//*************************************************************************************************
// oscilador com cristal de 20MHZ (testado fisicamente funcionou ok)
// configure assim o fuse do cristal => #pragma config FOSC = HS
//#define  TIME_BAUD  45//19200 bps - 20MHZ
//#define  BAUD_RX    42//19200 bps - 20MHZ 

//#define  TIME_BAUD  97//9600 bps - 20MHZ
//#define  BAUD_RX    95//9600 bps - 20MHZ 

//#define  TIME_BAUD  202//4800 bps - 20MHZ
//#define  BAUD_RX    198//4800 bps - 20MHZ