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.