10 PWM

Transcripción

10 PWM
9- PWM
9 MODULACIÓN DE ANCHO DE PULSO (PWM)
La técnica de PWM consiste en producir un pulso rectangular con un ciclo de
trabajo determinado, este ciclo de trabajo puede variar de 0 a 100%. En la Figura 9-1 se
muestra un pulso con un ciclo de trabajo del 50%, es decir Ton/T = 0.5.
Figura 9-1. Ciclo de trabajo de 50% [16]
En la Figura 9-2 se muesta un pulso con un ciclo de trabajo del 10%; Ton/T = 0.1.
Figura 9-2. Ciclo de trabajo del 10% [16]
Un ciclo de trabajo del 0% significa que la señal siempre está nivel bajo; y un ciclo
de trabajo del 100% significa la señal siempre en nivel alto, el número de casos
intermedios posibles es un número finito llamado resolución del PWM y se expresa
como Log2(número de casos). Por ejemplo si puede haber 256 ciclos de trabajo posibles
se dice que el PWM tiene una resolución de 8 bits.
Si conectamos una señal rectangular a un analizador de espectros para ver su
contenido de frecuencias, veríamos que está formada por tres partes (Figura 9-3):
-
Una componente de DC, cuya amplitud es proporcional al ciclo de trabajo.
-
Una sinusoide en la frecuencia fundamental (f=1/T).
-
Un número infinito de armónicas cuyas frecuencias son múltiplos de la
fundamental (2f, 3f, 4f, 5f, 6f, …).
Ing. Juan Ramon Terven Salinas
139
9- PWM
Figura 9-3. Espectro de la señal PWM
De tal forma que si conectamos un filtro “ideal” pasa bajas a la salida del PWM
para remover todas las frecuencias de la fundamental para arriba, obtendríamos un señal
de CD limpia, cuya amplitud sería directamente proporcional al ciclo de trabajo. Aquí
radica la importancia del PWM.
Podemos aproximar este filtro pasabajas con un simple filtro pasivo RC (de primer
orden) o un filtro activo de un orden superior. En la Figura 9-4 se muestra la
componente de CD (línea punteada) para una señal con un ciclo de trabajo de 50% y
otra con un ciclo de trabajo de 10%.
Figura 9-4. Salida analogica del PWM y el filtro pasa bajas
Ing. Juan Ramon Terven Salinas
140
9- PWM
Módulo Output Compare (OC)
La familia PIC32 posee un módulo llamada Output Compare, el cual es usado para
generar un pulso o un tren de pulsos en respuesta a eventos seleccionados [10] . Este
módulo tiene varios modos de funcionamiento, entre ellos uno llamado Modo PWM en
el cual el PIC32 genera un pulso rectangular con un ciclo de trabajo establecido por
nuestro código.
9.1.1 Pasos para configurar el modo PWM
1. Ajuste el periodo del PWM escribiendo en el registro de periodo del timer
seleccionado (PRx). (se puede usar el Timer2 o el Timer3)
2. Ajuste el ciclo de trabajo del PWM escribiendo en el registro OCxRS.
3. Escriba el registro OCxR con el ciclo de trabajo inicial.
4. Habilite las interrupciones para el timer usado y el módulo OCx usado (en caso de
ser necesarias). La interrupción del módulo OC es necesaria para el modo PWM con
protección de falla.
5. Configure el módulo Output Compare para algún modo de operación de PWM
escribiendo en los bits OCM (OCxCON<2:0>).
6. Seleccione el Timer a usar en el bit OCTSEL (OCxCON<3>). Si OCTSEL = 0, se
usa el Timer2, si OCTSEL = 1 se usa el Timer3.
7. Ajuste el prescaler del TMRx y habilitelo activando el bit TON (TxCON<15>).
Tome en cuenta lo siguiente:
•
El registro OCxR debe ser inicializado antes de que el módulo Output
Compare sea habilitado.
•
El registro OCxR se convierte de solo-lectura cuando el módulo Output
Compare opera en modo PWM.
•
El valor en OCxR se convierte en el ciclo de trabajo del primer periodo del
PWM.
•
El contenido del registro OCxRS se transfiere a OCxR cuando ocurre una
coincidencia del Timer.
En la Figura 9-5 se muestra el funcionamiento del PWM del módulo Output Compare.
Ing. Juan Ramon Terven Salinas
141
9- PWM
Figura 9-5. Funcionamiento del PWM [10]
1) El Timerx es puesto en 0 y se carga un nuevo ciclo de trabajo de OCxRS a OCxR.
2) El valor del Timer iguala al valor de OCxR, el pin OCx pasa a nivel bajo.
3) El Timerx termina de contar (iguala a PRx), se carga el valor de OCxRS a OCxR, el
pin OCx pasa a nivel alto. Se genera una interrupción del Timerx.
Ejemplo 1. Generación de voltaje analógico usando el módulo OC en
modo PWM.
En este ejemplo se configura el registro de periodo del Timer3 con un valor de
2000, este valor a su vez representa la resolución del PWM, ya que podemos tener 2000
valores de voltajes diferentes. Si nuestro PBCLK es de 80MHz, entonces tendremos un
pulso rectangular de 5kHz, podemos conectar un filtro pasabajas a la salida del PWM
como se muestra en la Figura 9-6. El filtro pasabajas de la figura tiene una frecuencia de
corte de 15.9 Hz atenuando de manera efectiva los componentes armónicos del PWM.
Figura 9-6. Filtro Pasa bajas a la salida del PWM
De esta manera podemos usar el PWM como un convertidor digital-análogo o DAC.
Ing. Juan Ramon Terven Salinas
142
9- PWM
Programa 9-1. Salida de voltaje analógico
/*
* Uso del modulo Output Compare en modo PWM
* Utiliza el OC1, sale por el pin RD0
*/
#include <p32xxxx.h>
#include <plib.h>
//Bits de configuracion
#pragma config POSCMOD = HS, FNOSC = PRIPLL, FPLLMUL = MUL_20
#pragma config FPLLIDIV = DIV_2, FPLLODIV = DIV_1
#pragma config FPBDIV = DIV_1, FWDTEN = OFF, UPLLEN = ON
#pragma config UPLLIDIV = DIV_2, FVBUSONIO = ON, FUSBIDIO = ON
#pragma config FSOSCEN = OFF, CP = OFF, FCKSM = CSECMD
//PBCLK = 80MHz
#define FPB 80000000L
int main(void)
{
//Frecuencia del PWM
int freqPWM = 10000;
// inicializa el modulo OC1 con Timer2
OpenOC1( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
// inicializa Timer2 y PR2
//La siguiente función inicializa el valor de PR2 = FPB/freqPWM
//en este ejemplo PR2 = 80,000,000/10,000 = 8000
//Esto quiere decir que tenemos 8000 ciclos de trabajo distintos
OpenTimer2( T2_ON | T2_PS_1_1 | T2_SOURCE_INT,FPB/freqPWM);
//Ajusta el ciclo de trabajo al 50%, es decir OC1RS = 4000
OC1RS = PR2/2;
while(1);
return 0;
}
Ing. Juan Ramon Terven Salinas
143
9- PWM
Ejemplo 2. Generación de una señal diente de sierra
El ejemplo anterior provoca un voltaje fijo por el pin RD0, sin embargo podemos
hacer que este voltaje cambie con el tiempo. Podemos aprovechar la rutina de
interrupción del Timer usado para cambiar el ciclo de trabajo del PWM como se
muestra en el siguiente ejemplo.
Programa 9-2. Generación de señal diente de sierra
/*
* Uso del modulo Output Compare en modo PWM
* para generar una señal diente de sierra
* Utiliza el OC1, sale por el pin RD0
*/
#include <p32xxxx.h>
#include <plib.h>
//Bits de configuracion
#pragma config POSCMOD = HS, FNOSC = PRIPLL, FPLLMUL = MUL_20
#pragma config FPLLIDIV = DIV_2, FPLLODIV = DIV_1
#pragma config FPBDIV = DIV_1, FWDTEN = OFF, UPLLEN = ON
#pragma config UPLLIDIV = DIV_2, FVBUSONIO = ON, FUSBIDIO = ON
#pragma config FSOSCEN = OFF, CP = OFF, FCKSM = CSECMD
//PBCLK = 80MHz
#define FPB 80000000L
//Frecuencia del PWM
#define freqPWM 10000
// VARIABLES GLOBALES
//Periodo del PWM (FPB/freqPWM = 8000)
short valorMax = FPB/freqPWM;
//Contador usado para generar el diente de sierra
short count = 0;
int main(void)
{
// inicializa el modulo OC1 con Timer2
OpenOC1( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
// inicializa Timer2 y PR2
//La siguiente función inicializa el valor de PR2 = FPB/freqPWM
//en este ejemplo PR2 = 80,000,000/10,000 = 8000
//Esto quiere decir que el diente de sierra subira
//desde 0 hasta 8000 (valorMax)
OpenTimer2( T2_ON | T2_PS_1_1 | T2_SOURCE_INT,valorMax);
//Ajusta la interrupción de Timer2 con prioridad de 2
IPC2bits.T2IP = 2; //prioridad 2
IPC2bits.T2IS = 1; //subprioridad 1
IFS0bits.T2IF = 0; //Limpia la bandera de interrupcion
// Activa el modo MultiVectored
INTEnableSystemMultiVectoredInt();
Ing. Juan Ramon Terven Salinas
144
9- PWM
IEC0bits.T2IE = 1;
//Activa la interrupcion del Timer2
//Ajusta el ciclo de trabajo incial a 0%, es decir OC1RS = 0
OC1RS = 0;
while(1);
return 0;
}
//RUTINA DE INTERRUPCIÓN DE TIMER2
//Cambia el ciclo de trabajo del PWM para generar un diente de sierra
//de 125Hz (1/8ms)
//Esta interrupción se ejecuta cada 100us, y el voltaje
//sube linealmente desde 0 hasta el máximo en 80 interrupciones
//por lo tanto el periodo del diente de sierra es de 8ms (80 x 100us)
void __ISR(_TIMER_2_VECTOR, ipl2) Timer2Handler(void)
{
//Ajusta el Nuevo ciclo de trabajo
OC1RS = count;
count+=100;
//Si el contador llega a valorMax, regresa a 0
if (count >= valorMax)
count = 0;
//Limpia la bandera de interrupcion del Timer2
IFS0bits.T2IF = 0;
}
Esta rutina producirá una señal diente de sierra de aproximadamente VDD de
amplitud con una rampa gradual del ciclo de trabajo de 0 a 100% en 80 pasos, seguida
de una caida abrupta a 0. Esta señal tendrá una frecuencia de 125 Hz.
Ing. Juan Ramon Terven Salinas
145
9- PWM
Ejemplo 3. Generación de un Senoidal
El siguiente ejemplo es idéntico al anterior solo cambia la rutina de interrupción en
la cual se usa la función sin de la librería math.h para generar cada valor de voltaje
de una señal senoidal.
Programa 9-3. Generación de una señal senoidal usando PWM
/*
* Uso del modulo Output Compare en modo PWM
* para generar una señal senoidal
* Utiliza el OC1, sale por el pin RD0
*/
#include <p32xxxx.h>
#include <plib.h>
#include <math.h>
//Bits de configuracion
#pragma config POSCMOD = HS, FNOSC = PRIPLL, FPLLMUL = MUL_20
#pragma config FPLLIDIV = DIV_2, FPLLODIV = DIV_1
#pragma config FPBDIV = DIV_1, FWDTEN = OFF, UPLLEN = ON
#pragma config UPLLIDIV = DIV_2, FVBUSONIO = ON, FUSBIDIO = ON
#pragma config FSOSCEN = OFF, CP = OFF, FCKSM = CSECMD
//PBCLK = 80MHz
#define FPB 80000000L
//Frecuencia del PWM
#define freqPWM 10000;
//Periodo del PWM (FPB/freqPWM = 8000)
short valorMax = FPB/freqPWM;
//Contador usado para generar la señal senoidal
short count = 0;
int main(void)
{
// inicializa el modulo OC1 con Timer2
OpenOC1( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
// inicializa Timer2 y PR2
//La siguiente función inicializa el valor de PR2 = FPB/freqPWM
//en este ejemplo PR2 = 80,000,000/10,000 = 8000
OpenTimer2( T2_ON | T2_PS_1_1 | T2_SOURCE_INT,valorMax);
//Ajusta la interrupción de Timer2 con prioridad de 2
IPC2bits.T2IP = 2; //prioridad 2
IPC2bits.T2IS = 1; //subprioridad 1
IFS0bits.T2IF = 0; //Limpia la bandera de interrupcion
// Activa el modo MultiVectored
INTEnableSystemMultiVectoredInt();
IEC0bits.T2IE = 1;
//Activa la interrupcion del Timer2
//Ajusta el ciclo de trabajo incial a 0%, es decir OC1RS = 0
OC1RS = 0 ;
while(1);
Ing. Juan Ramon Terven Salinas
146
9- PWM
return 0;
}
//RUTINA DE INTERRUPCIÓN DE TIMER2
//Cambia el ciclo de trabajo del PWM para generar una senoidal
//de 100Hz (1/10ms)
//Esta interrupción se ejecuta cada 100us, y el voltaje
//cambia senoidalmente generando un periodo completo cada
//100 interrupciones (2pi/100 = pi/50)
//por lo tanto el periodo de la senoidal 10ms (100 x 100us)
void __ISR(_TIMER_2_VECTOR, ipl2) Timer2Handler(void)
{
// calcula el nuevo valor de la senoidal
OC1RS = PR2/2 + PR2/2 * sin(M_PI/50*count);
count++;
//Limpia la bandera de interrupcion del Timer2
IFS0bits.T2IF = 0;
}
La función seno de la librería math.h tarda bastante tiempo en ser calculada (unos
cuantos milisegundos), de tal manera que si se desea generar una senoidal de alta
frecuencia no es recomendable usar la función de la librería. En su lugar, podríamos
generar un arreglo con los valores del seno y simplemente mandarlos llamar.
Ing. Juan Ramon Terven Salinas
147
9- PWM
Ejemplo 4. Generación de una señal senoidal usando una tabla de
datos
Para el siguiente ejemplo se uso una hoja de cálculo para generar una tabla de 100
valores que representan 1 ciclo de una señal senoidal. Estos valores se mapearon a un
rango de 0 a 160, en donde 160 será el valor del 100% del ciclo de trabajo (PR1 = 160).
Figura 9-7. Generación de datos
Una vez teniendo los datos se pasan a una tabla que se almacena en la memoria Flash
del PIC32 y dentro de la RSI del TMR2 se usa cada uno de estos valores para generar
una senoidal de aproximadamente 1kHz.
Observe que podemos usar la memoria de programa (Flash) para almacenar datos
constantes y asi no malgastar la preciada memoria RAM. Para hacer esto simplemente
declare su variable de tipo constante con el modificador const y el compilador
automáticamente la guarda en memoria Flash.
Ing. Juan Ramon Terven Salinas
148
9- PWM
Programa 9-4. Generación de senoidal a partir de una tabla de datos
/*
* Uso del modulo Output Compare en modo PWM
* para generar una señal senoidal a partir de
* una tabla de datos
* Utiliza el OC1, sale por el pin RD0
*/
#include <p32xxxx.h>
#include <plib.h>
#include <math.h>
//Bits de configuracion
#pragma config POSCMOD = HS, FNOSC = PRIPLL, FPLLMUL = MUL_20
#pragma config FPLLIDIV = DIV_2, FPLLODIV = DIV_1
#pragma config FPBDIV = DIV_1, FWDTEN = OFF, UPLLEN = ON
#pragma config UPLLIDIV = DIV_2, FVBUSONIO = ON, FUSBIDIO = ON
#pragma config FSOSCEN = OFF, CP = OFF, FCKSM = CSECMD
//PBCLK = 80MHz
#define FPB 80000000L
//Contador usado para generar la señal senoidal
short count = 0;
//Tabla de datos, representa 1 ciclo completo de una señal senoidal
const short tabla[100] = {
80,85,90,95,100,105,109,114,119,123,127,131,135,138,142,145,148,150,15
2,154,156,157,159,159,160,160,160,159,159,157,156,154,152,150,148,145,
142,138,135,131,127,123,119,114,109,105,100,95,90,85,80,75,70,65,60,55
,51,46,41,37,33,29,25,22,18,15,12,10,8,6,4,3,1,1,0,0,0,1,1,3,4,6,8,10,
12,15,18,22,25,29,33,37,41,46,51,55,60,65,70,75};
int main(void)
{
// inicializa el modulo OC1 con Timer2
OpenOC1( OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0);
// inicializa Timer2 y PR2
//La siguiente función inicializa el valor de PR2 = 160
OpenTimer2( T2_ON | T2_PS_1_1 | T2_SOURCE_INT,160);
//Ajusta la interrupción de Timer2 con prioridad de 2
IPC2bits.T2IP = 2; //prioridad 2
IPC2bits.T2IS = 1; //subprioridad 1
IFS0bits.T2IF = 0; //Limpia la bandera de interrupcion
// Activa el modo MultiVectored
INTEnableSystemMultiVectoredInt();
IEC0bits.T2IE = 1;
//Activa la interrupcion del Timer2
//Ajusta el ciclo de trabajo incial a 0%, es decir OC1RS = 0
OC1RS = 0;
while(1);
return 0;
}
//RUTINA DE INTERRUPCIÓN DE TIMER2
//Cambia el ciclo de trabajo del PWM para generar una senoidal
Ing. Juan Ramon Terven Salinas
149
9- PWM
//de 5kHz (1/200us)
//Esta interrupción se ejecuta cada 2us, y el voltaje
//cambia senoidalmente generando un periodo completo cada
//100 interrupciones
//por lo tanto el periodo de la senoidal es de 200us (2us x 100)
void __ISR(_TIMER_2_VECTOR, ipl2) Timer2Handler(void)
{
// Obtiene el nuevo valor de la senoidal
// de la tabla de datos
OC1RS = tabla[count];
count++;
//Si llega al final del arreglo, reinicia
if (count > 99)
count = 0;
//Limpia la bandera de interrupcion del Timer2
IFS0bits.T2IF = 0;
}
Generación de Audio
Podemos aprovechar el ejemplo anterior y conectar una bocina a la salida del PWM
para escuchar el tono límpio de una senoidal de 5kHz.
Podriamos agregarle amplificador operacional y un potenciómetro que varie la ganancia
para que funcione como control de volumen. Sin embargo un amplificador operacional
no es capaz por si solo de entregar corrientes muy grandes por la salida, por lo que no
podemos conectarles directamente un altavoz y oir el tono; necesitamos entonces añadir
algún dispositivo que sea capaz de ampliar esa corriente. Podemos usar el arreglo que se
muestra en la Figura 9-8.
El amplificador operacional empleado es un LM833, especial para audio. La etapa de
transistores formada por Q1 y Q2 tiene como única finalidad suministrar la corriente
que el operacional no puede. Los transistores empleados son los BC547 y BC557, Estos
no son de gran potencia por lo que se deberá usar una bocina pequeña.
Ing. Juan Ramon Terven Salinas
150
9- PWM
Figura 9-8. Amplificador sencillo de Audio
Ing. Juan Ramon Terven Salinas
151