segunda-feira, 11 de julho de 2016

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)   //
*/




Nenhum comentário :

Postar um comentário

olá,digite aqui seu comentário!