En muchos casos necesitamos que nuestra señal PWM tenga una frecuencia específica, el problema consiste en que necesitamos determinar el valor del Prescaler
T2DIV y del registro PR2 a partir de la siguiente Fórmula.
$$Tpwm = 4\,Tosc\,\,T2DIV\,\left( {PR2 + 1} \right)
es decir:
$$Fpwm = {{Fosc} \over {4\,T2DIV\,\left( {PR2 + 1} \right)}}
Por ejemplo, queremos que
Fpwm=1khz para un PIC16F887 que tiene Fosc=8MHz, y sabemos que este PIC posee 3 prescaler
T2DIV=1, 4 y 16, por lo tanto el primer paso será despejar el valor de PR2 de la formula anterior.
$$PR2 = {{Fosc} \over {4\,Fpwm\,T2DIV}} - 1
el segundo paso sera sustituir valores y probar con T2DIV = 1.
$$PR2 = {{8MHz} \over {4\, \times 1kHz \times 1}} - 1 = 1999
Como el valor anterior es mayor al valor máximo de PR2, es decir 255, debemos aumentar el valor de T2DIV.
$$PR2 = {{8MHz} \over {4\, \times 1kHz \times 4}} - 1 = 499
Como aún el valor anterior es mayor al valor máximo de PR2, es decir 255, debemos aumentar el valor de T2DIV.
$$PR2 = {{8MHz} \over {4\, \times 1kHz \times 16}} - 1 = 124
En este caso si podemos darle este valor a PR2.
Muchas veces resulta tedioso estar calculando este valor, por eso he realizado la siguiente librería que realizará esto por nosotros.
/////////////////////////////////////////////////////////////////////////////////
//Autor: Luis Alberto Vargas Tijerino.
//Alias: Bigluis.
//Fecha: 26-Nov-2010.
//Pais: Nicaragua.
/////////////////////////////////////////////////////////////////////////////////
// PWM_FREQ.C
// Libreria para ingresar la frecuencia PWM sin necesidad de calcularla.
//
// Para utilizarla es necesario definir al inicio del programa Fosc
//
//Ejemplo:
//!#define Fosc 8000000
//
// Luego es posible utilizar la funcion
// set_pwm_freq(Fpwm) Esta configurara T2DIV y PR2 para obtener
// Fpwm.
/////////////////////////////////////////////////////////////////////////////////
#ifndef Fosc
#define Fosc 8000000
#endif
#use delay (clock=Fosc)
#if !defined(__PCD__) //Si no se ha definido un pic de 24 bits
#define set_pwm_freq(Fpwm) /*definimos el macro set_pwm_freq(Fpwm) de la siguiente manera
*/(Fosc/(Fpwm*1*4)-1<0xFF)? /*Si (Fosc/(Fpwm*1*4)-1 es menor a 0xFF
*/ setup_timer_2(T2_DIV_BY_1, (int8)(Fosc/(Fpwm*1*4))-1, 1): /*el macro set_pwm_freq(Fpwm) sera igual a setup_timer_2(T2_DIV_BY_1, Fosc/(Fpwm*1*4)-1, 1)
*/(Fosc/(Fpwm*4*4)-1<0xFF)? /*De lo contrario si (Fosc/(Fpwm*4*4)-1 es menor a 0xFF
*/ setup_timer_2(T2_DIV_BY_4, (int8)(Fosc/(Fpwm*4*4))-1, 1): /*el macro set_pwm_freq(Fpwm) sera igual a setup_timer_2(T2_DIV_BY_4, Fosc/(Fpwm*4*4)-1, 1)
*/(Fosc/(Fpwm*16*4)-1<0xFF)? /*Si (Fosc/(Fpwm*16*4)-1 es menor a 0xFF
*/ setup_timer_2(T2_DIV_BY_16, (int8)(Fosc/(Fpwm*16*4))-1, 1): /*el macro set_pwm_freq(Fpwm) sera igual a setup_timer_2(T2_DIV_BY_16, Fosc/(Fpwm*16*4)-1, 1)
*/ setup_timer_2(T2_DIV_BY_16, 0xFF, 1); //De lo contrario el macro set_pwm_freq(Fpwm) sera igual a setup_timer_2(T2_DIV_BY_1, 0xFF, 1)
#else //Si no se ha definido un pic de menos de 24 bits
#define set_pwm_freq(Fpwm) /*Definimos el macro set_pwm_freq(Fpwm) de la siguiente manera
*/(Fosc/(Fpwm*1*4)-1<0xFFFF)? /*Si (Fosc/(Fpwm*1*4)-1 es menor a 0xFFFF
*/ setup_timer2(TMR_INTERNAL | TMR_DIV_BY_1, (int16)(Fosc/(Fpwm*1*4))-1): /*el macro sera igual a setup_timer2(TMR_INTERNAL | TMR_DIV_BY_1, (Fosc/(Fpwm*1*4))-1)
*/(Fosc/(Fpwm*8*4)-1<0xFFFF)? /*Si (Fosc/(Fpwm*8*4)-1 es menor a 0xFFFF
*/ setup_timer2(TMR_INTERNAL | TMR_DIV_BY_8, (int16)(Fosc/(Fpwm*8*4))-1): /*el macro sera igual a setup_timer2(TMR_INTERNAL | TMR_DIV_BY_8, (Fosc/(Fpwm*8*4))-1)
*/(Fosc/(Fpwm*64*4)-1<0xFFFF)? /*Si (Fosc/(Fpwm*64*4)-1 es menor a 0xFFFF
*/ setup_timer2(TMR_INTERNAL | TMR_DIV_BY_64, (int16)(Fosc/(Fpwm*64*4))-1):/*el macro sera igual a setup_timer2(TMR_INTERNAL | TMR_DIV_BY_8, (Fosc/(Fpwm*16*4))-1)
*/ setup_timer2(TMR_INTERNAL | TMR_DIV_BY_64, 0xFFFF); //De lo contrario el macro sera igual a setup_timer2(TMR_INTERNAL | TMR_DIV_BY_8, 0xFFFF)
#endif
Y a continuación muestro el código de Ejemplo para usar la librería.
///////////////////////////////////////////////////////////////////////////////
//Autor: Luis Alberto Vargas Tijerino.
//Alias: Bigluis.
//Pais: Nicaragua.
//Fecha: 24-Nov-10.
///////////////////////////////////////////////////////////////////////////////
#include <16f887.h>//pic a utilizar
#FUSES NOWDT //No Watch Dog Timer
#FUSES HS //High speed Osc (> 4mhz for PCM/PCH) (>10mhz for PCD)
#FUSES NOPUT //No Power Up Timer
#FUSES MCLR //Master Clear pin enabled
#FUSES NOPROTECT //Code not protected from reading
#FUSES NOCPD //No EE protection
#FUSES NOBROWNOUT //No brownout reset
#FUSES IESO //Internal External Switch Over mode enabled
#FUSES FCMEN //Fail-safe clock monitor enabled
#FUSES NOLVP //No low voltage prgming, B3(PIC16) or B5(PIC18) used for I/O
#FUSES NODEBUG //No Debug mode for ICD
#FUSES NOWRT //Program memory not write protected
#FUSES BORV40 //Brownout reset at 4.0V
#FUSES RESERVED //Used to set the reserved FUSE bits
#define Fosc 8000000 //Fosc=8Mhz
#include <PWM_FREQ.c>
#include <Puertos.c>
void main(){
signed int sel_Fpwm=0;
unsigned int Duty=127;
TRISB=0XFF; //Todos los pines de PORTB seran entradas.
setup_comparator(NC_NC); //Desconectamos los comparadores Analogicos
SETUP_ADC_PORTS(NO_ANALOGS); //Solo AN0 sera analogico los demas digitales.
SETUP_ADC(ADC_OFF); //Apagamos el ADC
setup_ccp1(CCP_PWM); //Configuramos CCP1 en modo PWM simple.
while(TRUE){ //Inicio del ciclo Infinito
while(!PORTB); //Espera a que se active un pin del PORTB.
delay_ms(10); //Retardo de 10ms para eliminar rebotes.
if(RB0) //Si se activo RB0
if(++sel_Fpwm>3) //preincrementamos sel_Fpwm y si es mayor que 3
sel_Fpwm=0; //igualamos sel_Fpwm a 0.
if(RB1) //Si se activo RB1
if(--sel_Fpwm<0) //predecrementamos sel_Fpwm y si es menor que 0
sel_Fpwm=3; //igualamos sel_Fpwm a 2.
if(RB2)Duty+=4; //Si se activo RB2 incrementamos Duty en 5.
if(RB3)Duty-=4; //Si se activo RB3 decrementamos Duty en 5.
set_pwm1_duty(duty); //El %DT de la senial sera Duty.
switch(sel_Fpwm) {
case 0 : //Si sel_Fpwm es 0
set_pwm_freq(500); //Fpwm sera 500
break;
case 1 : //Si sel_Fpwm es 1
set_pwm_freq(1000); //Fpwm sera 1000
break;
case 2 : //Si sel_Fpwm es 2
set_pwm_freq(1500); //Fpwm sera 1500
break;
default: //De lo contrario
set_pwm_freq(2000); //Fpwm sera 2000
}
while(PORTB); //Espera a que se desactiven los pines de PORTB
}
}
La librería "Puertos.c" la pueden encontrar
aquíY finalmente adjunto los archivos de la simulación y el código.