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