15. PWM – (Ancho por Modulación de Pulso)

15. PWM – (Ancho por Modulación de Pulso)
4.6 (91.11%) 9 votes

El PWM, es una de las señales más utilizadas para realizar control con nuestros microcontroladores.

Para poder generar esta señal con nuestro PIC, se hace uso de los módulos CCP (Comparador, Captura y PWM). Dicho módulo permite realizar tres funciones básicas.

Comparar: Compara el valor del temporizador con el valor de un registro y provoca una acción en el PIC.

Captura: Obtiene el valor del temporizador en un momento dado, fijado por la acción de un terminal del PIC.

PWM: Genera una señal modulada por ancho de pulso.

En esta entrada, nos vamos a centrar en esta ultima, en el PWM. Para nuestro caso en especifico, como estamos usando el PIC16F887, dicho microcontrolador tiene 2 modulos CCP como se puede apreciar en la siguiente figura, los cuales corresponden a los PINES 16 y 17 (Correspondientes al puerto C, RC1 y RC2).

Pines PIC16F887 voltaje de referencia
Distribución de los Pines del PIC16F887

El módulo PWM (Pulse With Modulation), permite obtener de los pines CCP1 (Pin 17) y CCP2 (Pin 16) una señal periódica (Es decir que se repite en el tiempo) la cual podemos modificar su ciclo de trabajo (Duty Cycle en ingles). Dicho PWM puede tener una resolución máxima de 10 BITS. En otras palabras. Como sabemos que el PIC trabaja con voltajes binarios (0V o +5V), podemos configurar el PWM para que trabaje un determinado tiempo en +5V (Ton) frente al tiempo que está en nivel bajo 0V (Toff), tal y como lo podemos apreciar en la siguiente figura.

PWM PIC

De esta manera, la tensión media aplicada a la carga, es proporcional al tiempo en que la señal estuvo en +5V (Ton) y asi podemos por  ejemplo controlar la luminosidad de lamparas, o la velocidad de un motor.

PWM con PIC

 

La resolución de salida del modulo CCP es de 10 bits, y para que funcione correctamente, no debemos olvidar configurarlo como salida en el TRIS C.

MODULO CCP EN C

El compilador nos suministra dos instrucciones basicas para el manejo del PWM.

Para configurar el módulo CCP:

Donde modo hace referencia a los bits CCPxM3:CPxM0 del registro CCPxCON y en PIC C puede ser configurado según la siguiente tabal:

CCP

El ciclo de trabajo para el PWM se define como:

 

donde el x hace alusion al modulo CCP a utilizar, en el caso del PIC16F887 solo tiene CCP1 y CCP2, así que si queremos usar el PWM del PIN 17 ponemos set_pwm1_duty(valor); y si queremos usar el PWM del PIN 16 ponemos set_pwm2_duty(valor);

Valor es un dato de 10 bits que determina el ciclo de trabajo o ancho de pulso, es decir ese valor va a determinar el porcentaje (En bytes) en que la señal de PWM se va a mantener encendida. Este valor junto con el valor del preescaler del TMR2 definen el ciclo de trabajo. En la configuración del TIMER 2 el postcaler debe valer 1.

Como comenté anteriormente el ancho de Pulso ó Ciclo de trabajo para el modulo CCP del PIC  puede tener como máximo un valor de 10 bits, eso si, ésta resolución depende exclusivamente del periodo del PWM que nosotros queramos implementar.

Como uso estas instrucciones?

Para generar una señal de PWM con el PIC, tenemos que utilizar el TIMER 2, el cual es un Timer de 8 Bits.

Internamente el PIC, usa el TIMER 2 como base de tiempo para la modulación del PWM utilizando el módulo CCP.

Con la siguiente formula podemos calcular la frecuencia de salida

 

PWM_{Frecuencia}=\dfrac{Frecuencia\ del\ Cristal}{(Carga\ del\ Timer2\ +1)*(Preescaler\ del\ TIMER\ 2)*4}

 

El Prescaler del TIMER 2 Puede ser 1, 4 o 16.

La carga del Timer 2 (conocido como PR2), también podría ser llamada como el Periodo de la Señal y puede tomar valores entre 0 y 255, debido a que es un registro de 8 bits.

Considerando que vamos a utilizar un cristal de cuarzo de 4000Mhz, calculemos cual seria la frecuencia mínima para dicho cristal.
Valor mínimo:

 

\dfrac{4000 MHz}{(255 + 1) * 16 * 4}=\dfrac{4,000,000}{16,384}=244Hz

 

Valor Máximo:

 

\dfrac{4000 MHz}{(1 + 1) * 1 * 4}=\dfrac{4,000,000}{8}=500000Hz

 

Recordando claro, que éstos son valores teóricos.

Listo, ya sabemos que lo primero que debemos encontrar es el valor de carga del timer 2 (PR2) y que éste depende del tipo de cristal que estamos usando y depende de los valores de preescaler que nosotros seleccionemos (1,4 o 16).

 

Ahora vamos a calcular la Resolución que vamos a usar en nuestro PWM. Para eso, sabemos que la máxima resolución del PWM es 10 bits o 1024 bytes. Si observamos la ecuación de frecuencia del PWM PWM_{Frecuencia} en su denominador los dos parámetros {(Carga\ del\ Timer2\ +1)*4} hacen referencia a la resolución que se puede alcanzar con el PWM, note que si la carga del timer2 fuera su valor máximo (255) el resultado de esa operación nos daría 1024 ({(255 +1)*4=1024}). Entonces para saber cuantos bits tenemos en una señal PWM a la frecuencia que nosotros deseamos, podriamos representar ese termino como  {(Carga\ del\ Timer2\ +1)*4=2^n-1}, donde n es el numero de bits que como máximo sería 10 bits.

 

Así, podriamos reescribir la ecuación de la frecuencia del PWM como:
PWM_{Frecuencia}=\dfrac{Frecuencia\ del\ Cristal}{(2^n-1)*(Preescaler\ del\ TIMER\ 2)}

 

Y como lo que queremos es determinar la resolución, simplemente despejamos n de la ecuación anterior aplicando logaritmos a ambos lados de la igualdad:

 

2^n-1=\dfrac{Frecuencia\ del\ Cristal}{PWM_{Frecuencia}*(Preescaler\ del\ TIMER\ 2)}

 

n*log(2)=log\left (\dfrac{Frecuencia\ del\ Cristal}{PWM_{Frecuencia}*(Preescaler\ del\ TIMER\ 2)} \right ) + log(1)

 

n=\dfrac{log\left (\dfrac{Frecuencia\ del\ Cristal}{PWM_{Frecuencia}*(Preescaler\ del\ TIMER\ 2)} \right )}{log(2)}

Con ese valor de resolución de ancho de pulso (n) sabremos cuanto es el BYTE que debemos colocar en la variable valor de la instrucción que habíamos comentado en la parte de arriba:

donde:

valor=2^n

NOTA IMPORTANTE

El valor de la Carga del Timer 2 NUNCA puede ser mayor que el ancho de pulso (Variable valor).

Vamos ahora a configurar el PIC utilizando un ejemplo Práctico.

Si te está sirviendo de algo la información de la pagina, podrías invitarme a un café y ayudarme a seguir manteniendo en pie el sitio WEB. Solo cuesta $2USD y me ayudarías enormemente a seguir con mi trabajo. Muchas Gracias por tu visita.




Ejemplo 1

Vamos a generar una señal cuadrada de 1Khz utilizando el TIMER 2 y el módulo CCP1 del PIC usando nuestro compilador CCS C.

Solución:

Para generar una señal cuadrada de 1Khz es necesario calcular primero el periodo (Inverso de la frecuencia) f=1Khz=1000hz.

T=\dfrac{1}{1000}=1mS

 

Periodo de 1mS (Mili segundo) o 0,001 segundos donde T=Periodo.

 

Ahora necesitamos calcular el desbordamiento del TIMER 2 con la siguiente formula.

 

T_{PWM}=(Carga\ Timer2+1)*4*(Preescaler\ del\ TIMER\ 2)*T_{Osc}*Postcaler

 

Reemplzando valores tenemos que:

 

0,001S=(X+1)*4*(4)*\frac{1}{4000000}*1

 

Obtenemos que el valor de  X=249. (Recordando que X tiene que estar entre 0-255, por eso se escoge un preescaler de 4)

 

El Timer 2 quedaría así:

Ahora solo nos falta determinar el valor de resolución para saber cuanto es lo máximo que podremos variar el ancho de pulso, aplicando la formula de resolución tenemos:

 

n=\dfrac{log\left (\dfrac{4000000}{1000*4} \right )}{log(2)}=9.9658\ bits

Así, el máximo ancho de pulso lo voy a obtener colocando la variable valor:

valor=2^{9.9658}=1000

El  duty cycle es  configurado entonces por la siguiente instrucción:

 

Donde si valor vale 0, tendre un ciclo de trabajo del 0%, si valor vale 1000, tendré un ciclo de trabajo de 100%, si valor vale 500, tendré un ciclo de trabajo de 50%

 

Ejemplo 2

Si se necesita una frecuencia de la señal PWM de 4Khz, y se utiliza con el microcontrolador PIC con un oscilador o cristal de 4000Mhz, se tendrían que realizar los siguientes cálculos:

 

Primero se calculará la resolución para ver si se cuentan con suficientes bits para todo el ancho de pulso o ciclo de trabajo de la señal que se quiere obtener con el módulo CCP PIC modo PWM, la resolución no tiene que tener más de 10 bits, si en el cálculo salen mas  de 10 bits hay que elegir otra frecuencia para la señal PWM, o un oscilador o cristal con una frecuencia diferente de tal forma que la resolución no sea mayor a los 10 bits, también hay que elegir el prescaler que se utilizará con el timer2 para obtener la PWMFrecuencia buscada, por ejemplo si se elige un prescaler de 16 en este caso para la resolución se tendrá:

n=\dfrac{log\left (\dfrac{4000000}{4000*16} \right )}{log(2)}=5,9658\ bits

Lo que es menor a los 10bits máximos, eso indica que es posible obtener una PWMFrecuencia de 4Khz utilizando un cristal de 4000Mhz, ademas el valor cargado en Carga del Timer2 no tiene que ser mayor a 25,9658 = 62,5 para obtener el máximo ancho de pulso para la PWMFrecuencia de 4Khz, luego el máximo valor cargado en Carga del Timer2 para que la PWMFrecuencia sea de 4Khz tiene que ser a lo mucho 63, de lo contrario la señal obtenida en el pin CCPx utilizado mediante el módulo CCP PIC modo PWM no será de los 4khz que se quieren.

valor=2^{5,9658}=63

El  duty cycle es  configurado entonces por la siguiente instrucción:

Donde si valor vale 0, tendre un ciclo de trabajo del 0%, si valor vale 63, tendré un ciclo de trabajo de 100%, si valor vale 32, tendré un ciclo de trabajo de 50%

 

Para generar una señal cuadrada de 4Khz es necesario calcular primero el periodo (Inverso de la frecuencia) f=4Khz=4000hz.

 

T=\dfrac{1}{4000}=0,250mS

 

Periodo de 0,250mS (Mili segundo) donde T=Periodo.

 

Ahora necesitamos calcular el desbordamiento del TIMER 2 con la siguiente formula.

 

T_{PWM}=(Carga\ Timer2+1)*4*(Preescaler\ del\ TIMER\ 2)*T_{Osc}*Postcaler

 

Reemplzando valores tenemos que:
0,000250S=(X+1)*4*(16)*\frac{1}{4000000}*1

 

Obtenemos que el valor de  X=14,625. (Recordando que X tiene que estar entre 0-63 para no ser mayor que el ancho de pulso, por eso se escoge un preescaler de 16). Así como necesitamos un valor entero, redondeamos X=15.

 

El Timer 2 quedaría así:

 

Código de Implementación

Vamos a llevar a la practica el concepto del PWM del PIC:

EJEMPLO

Existen muchos procesos electrónicos o industriales donde necesitamos controlar la velocidad de un Motor DC. Una manera simple de lograr este objetivo es utilizar un control por PWM. Para esto vamos a diseñar el siguiente circuito y el código del microcontrolador por medio del compilador CCS C con el fin de poder regular la velocidad de un Motor DC por medio de un potenciometro conectado al Terminal RA0 (Para esto sera necesario utilizar el concepto de conversión análogo digital visto en el Post anterior). En el post de interrupciones el Ejemplo 2 es una mejora de esta práctica donde además de regular la velocidad del motor, se mide la velocidad del mismo a través de un encoder. (Click aqui para ir al post de Interrupciones).

PWM Circuit

Para poder controlar el motor de 12 V necesitamos utilizar un Mosfet.

Los mosfets de potencia (power mosfets) son componentes electrónicos que nos permiten de controlar corrientes muy elevadas. Como en el caso del los mosfets comunes, tienen tres terminales de salida que se llaman: Drain, Source y Gate (D, S y G). La corriente principal pasa entre Source y Drain (ISD) mientras que el control de esta corriente se obtiene aplicando una tensión sobre el terminal Gate (respecto al terminal Source), conocida como VGS.

Código de Implementación:

A continuación se presenta el código para que lo copies y lo pegues en tu compilador y puedas reproducirlo. Recuerda que para ver el código debes compartir o darle me gusta al contenido de este post para que más personas se beneficien de esta información.


Si te sirvió de algo la información de la pagina, podrías invitarme a un café y ayudarme a seguir manteniendo en pie el sitio WEB. Solo cuesta $2USD y me ayudarías enormemente a seguir con mi trabajo. Muchas Gracias por tu visita.




Esta entrada tiene 18 comentarios

  1. Sin animo de ofender solo de informar esto que usted tiene puesto “PWM: Genera una señal modulada en amplitud de pulso.” es incorrecto, lo correcto es modulación en ancho de pulso no de amplitud (PWM = pulse width modulation)

    1. Es correcto Miguel. Gracias por la observación.

  2. Hola Sergio muy bueno el blog, estuve leyendo los comentarios y en uno hay algo que me interesa, que es utilizar un filtro pasa bajo para obtener una salida de 0 a 5 V continua a partir de la señal PWM, me pasarias el link a esa info ? gracias

  3. Hola sergio , la verdad que hace dias estoy dando vueltas por la web y no encuentro el codigo fuente adecuado a mi proyecto , ya tengo un proyecto con pic 16f886 , se que debo activar el ccp1 y ahi me quedo porque el programa que tengo ya tiene otras funciones como enviar unos datos por I2c a un integrado y escribir en pantalla 16×2 pero quiero solo agregar la funcion pwm que se muestre de 0a 100% en mi display y genere de 0a5v en su salida ccp ,si me podrias dar una mano con eso te paso un correo , gracias

    1. Hola Rodrigo, no entendi, quieres que de la patilla CCP salga un voltaje directo de 0 a 5V ?? por ejemplo si tu pwm está en el 50% quieres que te salga 2.5V?, si es eso lo que quieres hacer, deber agregar un filtro pasa bajos a la salida del CCP, en esta entrada se muestra un ejemplo de como hacerlo (click aqui para verlo). Para mostrar el porcentaje en el LCD debes hacer una regla de 3 donde por ejemplo x es tu valor de porcentaje y duty es el valor del duty cicle actual: [latex]x=dfrac{duty*100}{1024}[/latex] y utilizar printf(lcd_putc,”porcentaje= %f “,x); como se utiliza en esta entrada (Click aqui)

      1. Hola Sergio , es correcto lo que me decis quiero que el valor de salida de mi ccp1 del pic 16F886 muestre un texto por ejemplo ( salida ) y % , entonces al subir y bajar en el display puedo ver de 0% a 100% mientras mi ccp1 entrega un valor de 0 a 5v luego de pasar por un filtro pasa bajos , hasta ahi todo bien , el problema es que como te comentaba yo agregaria el uso de ccp1 a un programa que manda I2c a otro integrado que es el principal trabajo y no se en que parte agregar el programa para que funcione el ccp como yo quiero sin afectar al programa principal , ahora bien mi circuito no usa cristal sino un oscilador interno de 4mhz (se puede hacer sin agregar cristal ).
        en realidad me gustaria que en una parte del programa que es para hacer servicio y ajustes yo encuentre este (salida%) ademas que el ccp al iniciar no arranque en cero sino donde se dejo por ultima vez que guarde en la ram del micro ese dato , se entiende lo que quiero realizar ? si me pasas un correo te mando el archivo asi lo ves ya que nisiquiera lo logre simular en proteus por falta de conocimiento estuve 2 dias y con todo bien conectado al darle play no hace nada , soy nuevo en esto si me podes dar una mano te agradezco , saludos

        1. ademas la idea es que el ciclo de trabajo del ccp cambie , (suba o Baje ) por pulsadores que estando en (modo servicio) se usan para modificar cosas y no por el uso de un potenciometro como en tu proyecto , gracias Sergio

          1. Si, puedes hacer que cuando se presione un pulsador incremente o decremente algún contador dentro de tu código y ese contador será el duty cicle de tu PWM (set_pwm1_duty(duty);), validando siempre dentro del código que este entre 0 y el valor máximo que has calculado para tu cristal interno. Entonces cuando decrementes si llega a cero, debes poner un if y hacer que se mantenga en cero, porque no tiene sentido que coloques números negativos, y lo mismo para el valor máximo.

        2. Bueno Rodrigo, según veo, tu has implementado el PWM al programa del PIC que ya tienes y no te está trabajando bien?. Porque para agregar el pwm es solo colocar los bits de configuración que se hace antes del While Infinito. Recuerda que debes configurar adecuadamente tu PWM para el cristal interno de 4MHz, para que obtengas la señal correcta. Mira que en los ejemplos de la entrada se hace con cristal externo de 4000MHz. El cristal interno no es tan preciso cuanto un cristal externo y obviamente el pic corre más lento, pero bueno pienso que no sea mucho problema. Solo agregando eso al programa ya deberia rodar el PWM, y puedes colocar el set_pwm1_duty(duty); para generar tu señal en cualquier parte del código.
          Si quieres que salte a un punto de servicio, puedes implementarlo con alguna interrupción, bien sea por algún pulsador o algo asi. Parece que estas bastante encaminado, aplica lo visto en la entrada de almacenamiento EEPROM interno para no perder el último valor del duty, y debes cargar dicho valor siempre al inicio del programa, antes de entrar en el while infinito y después de haber configurado el PWM.

  4. Muchas gracias el código me fue de gran ayuda para hacer un dimmer digital

  5. hola SERGIO…saludos …te comento que yo trabajo ya desde hace mas de 10 años con microcontroladores de microchip desde 8 ,18 ,28 , y 40 pin depende del proyecto que me pidan
    y siempre trabaje con ensamblador y nunca he tenido problemas….. ultima menta me dio la curiosidad de trabajar con otro lenguaje…probé con arduino ,,c++, y otros pero el que me llamo mucho la atención fue ccs compiler me siento bien con este lenguaje y he pasado varios proyectos que ya tengo trabajando a este nuevo programa…pero hay una cosa que no he podido implementar al cien por ciento la interrupción externa por el pin ( RB0 ) ya que al simular en ccs compiler esta interrupción externa se me pierden los datos …en ensamblador cuando interrumpía el programa con un swich conectado a RB0 por flanco de bajada tenia que guardad todos los datos por programa y al desconectar el swich estos regresan de donde salieron…el problema que tengo es que con ccs compiler no he podido con esta función de interrupción externa por RB0 se me pierde el programa o los datos los regresa incompletos…..le seguiré estudiando para logra sacar este detalle …….por tu atención gracias …..y si vale demaciado la pena invitarte un cafe

    1. Hola Mario, gracias por visitar la pagina. La verdad es que trabajar con ensamblador es mucho más efectivo para el microcontrolador, porque estas hablando casi que el mismo lenguaje que él, imagino ya tendras muchas rutinas casi listas en ese lenguaje. El problema del ensamblador es que es mucho más dificil de programar pues debes ir muy al detalle y gastas mucho tiempo, igual son cosas que con la experiencia se va mejorando. Hoy en dia los microcontroladores vienen con memoria suficiente para soportar lenguajes de Alto nivel como el lenguaje C que facilitan enormemente la vida del programador, lo que significa un ahorro sustancial de tiempo y la posibilidad de crear cosas mucho más sofisticadas, por lo menos es lo que yo pienso, yo también programé en ensamblador, pero ahora definitivamente me quedo con el lenguaje C. Tenés que ver como estas guardando los datos cuando entras en la interrupción, lo mas recomendable es que guardes esos datos en variables Globales, osea variables declaradas despues del encabezado, afuera de todas las funciones. Asi estas variables pueden ser usadas por todas las funciones de tu código. Saludos y éxitos en tus proyectos.

      1. hola SERGIO gracias por tu atención….y si al trabajar con ensamblador ya tengo varias librerías elaboradas y solo las tengo que llamar y te comentaba de la interrupción externa en el pin RB0 en ensamblador no tengo problemas con esta función he podido elaborar asta un segundo programa dentro de la interrupción y no tengo problemas al salir de la esta función lo voy a intentar implementar esta acción en ccs compiler y te comento después que paso………….. y nuevamente GRACIAS por tu atención

  6. Hola sergio, muchas gracias por toda esta información, estoy diseñando un carro a control remoto, he visto que usan el PWM para la dirección de giro de las ruedas, pero no logro entender si el pwm es constante, como logro determinar que el giro se haga hacia un lado o hacia el otro, o si puedes darme alguna idea de como generar este giro a traves de un servomotor, tengo en mente usar puente h para controlar el giro hacia adelante o hacia atras pero me sigue inquietando como generar el giro hacia derecha o izquierda, te agradezco cualquier ayuda o luz que me puedas dar.

    1. Daniela, pues depende de como sean tus ruedas, si estas tienen como incorporarle la dirección o si son ruedas estaticas, es decir que solo puedes hacerlas girar para adelante o para atras (Como si fuera un tanque, esos de guerra). Efectivamente para tu proyecto vas a necesitar el puente H para poder hacer la inversion de giro, una forma de hacer girar el carro para un lado, digamos a la derecha, es hacer que las dos ruedas de la derecha giren hacia adelante, y las dos ruedas de la izquierda giren hacia atras, asi haras que el Carro gire en su propio eje hacia la derecha. Saludos.

  7. Hola, soy Pedro Bazan, una pregunta ¿me podrás asesorar a montar este programa en un pic 16f886 en asm?

    1. Hola Pedro, hace muchisimos años programé microcontroladores en ASM, hoy lastimosamente ya no me acuerdo como programar en ASM, como es algo que nunca volví a utilizar inevitablemente se olvida. Espero que puedas hacer tu programa, busca por la web, que de seguro encuentras. Un saludo.

Deja un comentario

Menú de cierre