09 – Generando señales PWM con el PIC18F4550

Inicialmente, no profundizaré respecto a que es una señal PWM y sus utilidades ya que considero que hay una amplia bibliografía al respecto. Nos concentraremos en la puesta en marcha del módulo PWM.

Típicamente, los microcontroladores utilizan  temporizadores para generar señales PWM. En los PIC de 8 bits, el módulo encargado de proveer la señal PWM se llama Capture/Compare/PWM. En el PIC18F4550 hay dos salidas PWM: CCP1 y CCP2.

CCP

CCP1, se encuentra conectado a RC2 y CCP2 a RC1, aunque RB3 puede utilizarse como una salida CCP2 alternativa.
Cuando el módulo Capture/Compare/PWM funciona en modo PWM los pines CCPX pueden producir una salida PWM con una resolución 10 bits, siempre que se utilice el TRIS apropiado para PORTC y PORTB.

Librerías especiales XC8 para Timer y PWM

Para generar señales PWM se requiere de una base de tiempos que es proporcionada por el Timer2 del PIC, por lo que se requiere de:

OpenTimer2(): este método inicializa el Timer 2 del microcontrolador. Se le debe especificar como argumento el prescaler que puede ser:

prescaler_tm2.png

Por lo general, se utiliza el prescaler 1:16. Una vez configurado Timer2, se requiere de la librería pwm.h para poder utilizar los siguientes métodos:

OpenPWMx(): Inicializa el módulo PWM 1 o 2. Se le debe especificar como argumento el período (en hexadecimal) de la señal PWM que se desea generar.

SetDCPWMx(): Establece el ciclo de trabajo de la señal PWM. Se debe especificar como argumento el ciclo de trabajo deseado (debe ser un número entero).

Cálculo del período y del ciclo de trabajo.

Ecuación para encontrar el período de la señal PWM que será el argumento de OpenPWMx():

periodoPWM.png

Ecuación para calcular el ciclo de trabajo del 100% de la señal PWM, servirá como argumento de SetDCPWMx():

cicloTPWM

Ejemplo de aplicación:

EjemploPWM

 

El código del programa sería el siguiente:

#include <xc.h>
#include <plib/timers.h>
#include <plib/pwm.h>

//BITS DE CONFIGURACION...
#pragma config PLLDIV = 5, CPUDIV = OSC1_PLL2, USBDIV = 2
#pragma config FOSC = HSPLL_HS, FCMEN = OFF, IESO = OFF
#pragma config PWRT = OFF, BOR = OFF, VREGEN = OFF
#pragma config WDT = OFF, WDTPS = 32768
#pragma config MCLRE = ON, LPT1OSC = OFF, PBADEN = OFF
#pragma config STVREN = ON, LVP = OFF, ICPRT = OFF, XINST = OFF

#define _XTAL_FREQ 48000000

void main(){
TRISCbits.TRISC2 = 0;
unsigned char prescaler = T2_PS_1_16;
OpenTimer2(prescaler);
OpenPWM1(0x95);
while(1){
SetDCPWM1(300);
}
}

Simulamos y observamos con el osciloscopio la señal generada:

simulacionPWM

Variación de ciclo de trabajo PWM + Timer

Como se puede apreciar, la generación de señales PWM es muy simple. Sin embargo, la técnica PWM requiere de la variación del ciclo de trabajo para “regular” la energía que se entrega a una carga. En el siguiente ejemplo, observaremos como generar un ciclo de aumento y disminución del CT de la señal PWM anteriormente generada.

Según los cálculos PWM realizados, el argumento para SetDCPWM1 es de 600 para un CT del 100%, de esta manera necesitamos incrementar de uno en uno desde 0 hasta 600 y posteriormente decrementar de uno en uno hasta llegar a 0, esto se repetirá infinitamente.

Como ya hemos aprendido a utilizar temporizadores, trabajaremos con el Timer0 para proveer una base de tiempo de 0.1 segundos para efectuar los incrementos/decrementos (ya no debemos utilizar delays). El código sería el siguiente:

#include <xc.h>
#include <plib/timers.h>
#include <plib/pwm.h>

#pragma config PLLDIV = 5, CPUDIV = OSC1_PLL2, USBDIV = 2
#pragma config FOSC = HSPLL_HS, FCMEN = OFF, IESO = OFF
#pragma config PWRT = OFF, BOR = OFF, VREGEN = OFF
#pragma config WDT = OFF, WDTPS = 32768
#pragma config MCLRE = ON, LPT1OSC = OFF, PBADEN = OFF
#pragma config STVREN = ON, LVP = OFF, ICPRT = OFF, XINST = OFF

#define _XTAL_FREQ 48000000

int dutyCicle;
unsigned int accion;

void main(){
accion = 0; //Aumentar
dutyCicle = 0;
TRISCbits.TRISC1 = 0;
unsigned char Timer0Config;
unsigned char prescaler = T2_PS_1_16;
Timer0Config = TIMER_INT_ON &amp; T0_16BIT &amp; T0_SOURCE_INT &amp; T0_PS_1_256;
OpenTimer0(Timer0Config);
WriteTimer0(0xEDAF);
TMR0IF = 0;
OpenTimer2(prescaler);
OpenPWM1(0x95);
ei();
while(1){}
}

void interrupt high_isr(){
if(TMR0IF){
if(accion == 0){
dutyCicle++;
SetDCPWM1(dutyCicle);
if(dutyCicle == 600){
accion = 1;
}
}
else if(accion == 1){
dutyCicle--;
SetDCPWM1(dutyCicle);
if(dutyCicle == 0){
accion = 0;
}
}
TMR0IF = 0;
WriteTimer0(0xEDAF);
}
}

Los invito a comprobar el funcionamiento del módulo PWM y probar distintos escenarios.

Un saludo,