miércoles, 9 de noviembre de 2011

Generacion de PWM con PIC (Pic C de CSS)

Generacion de PWM con PIC (Pic C de CSS)

En muchas ocasiones necesitamos generar una onda PWM, y lo unico que deseamos ver en esa onda, no queremos calcular nada, no queremos rompernos la cabeza pensando en calcular Periodos y frecuencias, lo único que deseamos es ver en un osciloscopio un tren de pulsos PWM y ya!!!!!

Pues aquí el Santo Remedio!!!!!!!!!!!!!!!!!

En primera para que no les digan y no les cuenten, se esta usando la IDE PCWHD de CCS t programando en Pic C.

Usamos un Pic 16f627a, sus características son modestas, y también su costo, esto como parte de un proyecto más grande que después presentaré.





Generacion Rapida de PWM con PIC 
Frecuencia Constante y DutyCicle Variable


#include <16f627a.h>
#fuses INTRC, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP 
#use delay(clock=4000000)

void main()
{
   int i = 0;

   output_low(PIN_B3); //CCP1
   setup_ccp1(CCP_PWM);

   setup_timer_2(T2_DIV_BY_16, 255, 1);


   while(1)
   {
     
      for(i=0; i < 255; i++)
      {
         set_pwm1_duty(i);
         delay_ms(5);
      }
      for(i = 255; i > 0; i--)
      {
         set_pwm1_duty(i);
         delay_ms(5);
      }      
   }
}

Esquemático:
Esquematico en Proteus Isis
Descarga: Diseño de Isis Proteus Simulado


INFO:


Explico el código arriba presentado:

  • La función setup_timer_2(T2_DIV_BY_16, 255, 1) programa el timer2 del micro,  el primer parámetro T2_DIV_BY_16 es un preescaler, es decir, un divisor, divide la frecuencia del micro entre 16, así como T2_DIV_BY_4 divide entre 4. 
  • El segundo parámetro (255), es un numero de 8bits (de 0 a 255) que equivale al periodo   que deseas de tu PWM, si colocamos 255 quiere decir que deseamos todo el periodo que tu oscilador divido por el preescaler pude dar, si colocamos 123 indicamos que queremos la mitad del mismo y así.
  • El tercer parámetro son las veces que el timer se reiniciará antes de una interrupción, esto es, las veces que habrá una transición de nivel bajo a alto de tu PWM antes de una interrupción.



Generación Rapida de PWM con PIC 
Frecuencia Variable y DutyCicle Constante (50%)


#include <16f627a.h>
#fuses INTRC, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP 
#use delay(clock=4000000)

void main()
{
   int i = 0;

   output_low(PIN_B3); //CCP1
   setup_ccp1(CCP_PWM);
   setup_timer_2(T2_DIV_BY_16, 255, 1); 


   while(1)
   {
       for(i=0; i < 255; i++)
      {
         setup_timer_2(T2_DIV_BY_16, i, 1);
         set_pwm1_duty(i/2);
         delay_ms(5);
      }      
   }
}



DESCARGA: DISEÑO ISIS PROTEUS SIMULACION


De aqui puedes descargar las referencias del CCS (CCS manual):
http://hzsquare.blogspot.com/2011/10/libros-de-pics.html
o de su pagina oficial

33 comentarios:

  1. Hola. Aquí un compañero mecatrónico de fime.

    Excelente blog, en relidad ayuda mucho que con tu facilidad para desarrollar este tipo de proyectos, nos orientes a los que batallamos un poco, pero que queremos aprender.

    Una sugerencia, sería genial si te tomas el espacio de explicar el porqué los registros van con tal valor.

    Por ejemplo, veo que utilizas CCS, y este tiene funciones integradas para inicializar el PWM o cualquier otro periférico, pero yo por ejemplo uso MikroC, y pues algunas funciones tienen otros nombres, o simplemente, el compilador no cuenta con una función para ello.

    Por eso creo que es importante mencionar los registros a configurar para cada dispositivo (PWM, ADC, interrupciones), porque si no, el trabajo de los que entran aqui apurados por una solución, se reduce a copy-paste, y ya no entiendieron el porque hay un registro con tal valor.

    Bueno, sigue con tu excelente trabajo, y saludos desde la tierra de osos hacia la tierra de osos.

    ResponderEliminar
  2. Coloqué una pequeña info, describiendo la función, los registros que se modifican vienen en los .h del pic usado en C:\Program Files (x86)\PICC\Devices, y para una mejor información acerca de la función y demás referencias del lenguaje puedes consultar el manual de CCS, siento no ayudar por cuestiones de tiempo :/, je chao

    ResponderEliminar
  3. hola quiero hacer un convertidor de voltaje a freciencia por lo que veo es que tengo que junta los dos programa en una sola soy novato espero que alguien me pueda ayudar mi correo es temoc_008@hotmail.com salidos mi voltaje quiero que verie de 0-5 volts y frenciancia de 0-100 HZ

    ResponderEliminar
    Respuestas
    1. Necesitas forzosamente que sea con un PIC?, lo que mencionas que quieres hacer es un Oscilador controlado por voltaje o VCO.

      Checa este link, funcionará mejor si es analógico todo:

      http://www.falstad.com/circuit/e-vco.html

      Saludos artegaer!!

      Eliminar
  4. muy buen blog sobre todo que cuando andas con prisa no tienes tiempo de leer tanta teoria y la simplicidad de tus explicaciones es muuuy buena sigue apoyando

    ResponderEliminar
  5. si necesito forzozamente que sea con un pic amigo

    ResponderEliminar
  6. mira este codiho es el que llevo #include <16f877a.h>
    #fuses NOWDT,HS,NOPUT, NOPROTECT,NODEBUG,NOBROWNOUT, BROWNOUT, NOLVP ,NOCPD,NOWRT
    #use delay(clock=12000000)

    void main()

    {
    setup_adc_ports(AN0_AN1_AN3);
    setup_adc(ADC_CLOCK_INTERNAL);
    setup_spi(SPI_SS_DISABLED);
    setup_timer_0(RTCC_INTERNAL|RTCC_DIV_1);
    setup_timer_1(T1_DISABLED);
    setup_timer_2(T2_DIV_BY_1, 7, 1);
    setup_ccp1(CCP_PWM);
    setup_comparator(NC_NC_NC_NC);

    int duty;
    set_adc_channel(0); // elegimos el canal que vamos a leer.
    output_low(PIN_C2);
    while(1)
    {
    for(duty=10; duty<64 ; duty++)
    {
    setup_timer_2(T2_DIV_BY_1, duty, 1);

    duty=read_adc(); // leemos la tension en el canal que elegimos.
    delay_us(10);
    set_pwm1_duty(duty/2);
    }

    }
    }
    lo que necesito que haga es que cuando mi volts valla de 0-5V y me entregue una frecuencia de 36.5kHz -370kHz. ya hace la conversion me falta para metrizarlo luego hace algo mal cuando tengo 5V me da frecuencia menor y debe de ser alreves cuando tengo el voltaje maximo me entregue frecuencia maxima se lo agradeceria bastante si alguien me puede ayudar saludos

    ResponderEliminar
  7. se puede hacer con todos los canales o solamente con b3

    ResponderEliminar
    Respuestas
    1. Segun el Pic será el Pin usado, debe ser el CCP (Capture, Compare and PWM)

      Eliminar
  8. Hola una pregunta como puedo generar una señal senoidal para que en ves de tener una modulacion pwm tenga una spwm??? espero me puedas ayudar muchas gracias.

    ResponderEliminar
    Respuestas
    1. Lo siento no he investigado al respecto, checa este link:
      ftp://www.elex.camosun.bc.ca/browningi/ELEX242/old/Notes/Inverters.pdf

      Saludos

      Eliminar
    2. Ntc muchas gracias, esta muy buena la información!!!

      Eliminar
  9. esto me ayudo mucho, gracias y sigue asi! n_n

    ResponderEliminar
    Respuestas
    1. De nada amigo Anonimo, un placer ayudar a la comunidad

      Eliminar
  10. Amigo gracias por la información.
    Me falla mucho un programa que hice similar al tuyo, la idea es que pulsando un botón de una vuelta con los ciclos que le mando. Pero parece que no me responde el servomotor, tienes alguna idea en qué estoy mal? Saludos y muchas gracias aquí el código:

    #include "C:\Users\minihp\Documents\aaaaaaaaaaaaaaaaaaaaaaaaaa\PRUEBA SERVO\main.h"



    void main()
    {

    long ct=0;
    setup_adc_ports(NO_ANALOGS|VSS_VDD);
    setup_adc(ADC_CLOCK_DIV_2|ADC_TAD_MUL_0);
    setup_wdt(WDT_OFF);
    setup_timer_0(RTCC_INTERNAL);
    setup_timer_1(T1_DISABLED);
    setup_timer_2(T2_DIV_BY_4,124,1);//16,255,8-original--------y---funciona con 4,124,1
    setup_ccp1(CCP_PWM);
    set_pwm1_duty(0);
    port_b_pullups(TRUE); //NO INTERFIERE CON LA SALIDA PWM
    setup_oscillator(OSC_4MHZ|OSC_INTRC);
    // TODO: USER CODE!!
    set_tris_a(0b0000001);

    for(;;)
    {
    if(input(PIN_A0))
    {
    for(ct=0;ct<1023;ct++) //For desde ct=0 hasta ct<=254
    {
    set_pwm1_duty(ct);
    delay_ms(5);//tenia 20
    }


    delay_ms(3000);
    }




    else
    {
    for(ct=1023;ct>0;ct--) //For desde ct=0 hasta ct<=254
    {
    set_pwm1_duty(ct);
    delay_ms(5);//tenia 20
    }

    delay_ms(3000);
    }
    }
    }

    ResponderEliminar
  11. Por cierto, el código no me manda error, pero no sé en qué me estoy equivocando o si es posible como te digo, hay que utilizar alguna librería de servos? De antemano gracias.

    ResponderEliminar
    Respuestas
    1. Lo siento bro, no he podido checar tu programa, he estado con mucho jale ultimamente.
      Solo te puedo decir que no es necesaria una librería de servos, el servo no recibe mas que un PWM que dependiendo su DutyCicle será el ángulo a mover, así que podrías verificar el funcionamiento con un osciloscopio, ver si estas variando de buena manera tu PWM.

      Saludos

      Eliminar
  12. Hola buenas noches quería saber si me puedes ayudar a generar un pwm para controlar les RGB gracias

    ResponderEliminar
  13. hola amigo, vi tu programa de pwm con frecuencia variable y duty constante. y me gustaria saber si puedas ayudarme en mi proyecto, el cual se basa en un pwm con frecuencia variable y un duty del 50%, pero la frecuencia debe ser ingresada por medio de teclado. La frecuencia debe variar de 50hz a 1khz. Por favor me urge, mi correo es alvaynez@hotmai.com

    ResponderEliminar
    Respuestas
    1. Hola, que clase de ayuda ocupas?
      Por "teclado" te refieres a introducir los datos usando una pc?
      Lo puedes hacer conectando un FTID al PIC para establecer una conexión serial entre la pc y el micro, después, usando hyperterminal, termite o programando tu interfaz tú puedes mandar comandos vía serial al PIC.
      Saludos

      Eliminar
    2. Lo siento debi ser mas especifica, se trata de un keypad 4x4 o incluso un 3x4. la funcion del keypad es ingresar la frecuencia en 4 digitos (es decir 0050 a 1000), y de acuerdo a ello variar su periodo, pero el duty o semiperiodo debe equivaler al 50% del periodo.

      Eliminar
  14. Recomiendo cablear el keypad a un puerto del pic y colocar una interrupción que se desencadene cuando el usuario deja de presionar la tecla. Es decir en el falling edge de la señal.
    Cada vez que la interrupción se desencadane debes leer el valor del puerto y almacenarlo en alguna variable.
    Cuando tengas tua cuatro dígitos aplicale alguna clase de conversión para saber a que frecuencia corresponde, etc...

    ResponderEliminar
    Respuestas
    1. Este comentario ha sido eliminado por el autor.

      Eliminar
    2. el programa del teclado ya lo tengo, incluso los calculos del semiperiodo ya los presenta en la lcd, el problema que tengo es que no se como generar el pulso.
      Mira este es el programa que realice:
      #include <16F877.h>
      #fuses xt
      #use delay (clock=4000000)
      #define use_portb_lcd TRUE
      #define use_portb_kbd TRUE
      #include
      #include
      #include
      void main(){
      char a=0,b=0,c=0,d=0,confirmar=0;
      int j;
      double e,f,g,h,i,k,l,m,n,o,p;
      lcd_init ();
      kbd_init ();
      port_b_pullups (TRUE);
      printf(lcd_putc,"\fFREC:");
      delay_ms(50);
      while (1){
      while (a==0){
      a=kbd_getc();}
      e=(a-48);
      lcd_gotoxy (7,1);
      printf(lcd_putc,"%c",a);
      delay_ms(80);
      i=(e*1000);
      printf(lcd_putc,"\n%e",i);
      delay_ms(80);
      a=0;
      while (b==0){
      b=kbd_getc();}
      f=(b-48);
      lcd_gotoxy (8,1);
      printf(lcd_putc,"%c",b);
      delay_ms(80);
      k=(f*100);
      printf(lcd_putc,"\n%e",k);
      delay_ms(80);
      b=0;
      while (c==0){
      c=kbd_getc();}
      g=(c-48);
      lcd_gotoxy (9,1);
      printf(lcd_putc,"%c",c);
      delay_ms(80);
      l=(g*10);
      printf(lcd_putc,"\n%e",l);
      delay_ms(80);
      c=0;
      while (d==0){
      d=kbd_getc();}
      h=(d-48);
      lcd_gotoxy (10,1);
      printf(lcd_putc,"%c",d);
      delay_ms(80);
      m=(h);
      printf(lcd_putc,"\n%e",m);
      delay_ms(80);
      d=0;
      n=(i+k+l+m);
      printf(lcd_putc,"\fCONFIRMAR FRECUENCIA");
      delay_ms(200);
      printf(lcd_putc,"\n%e",n);
      delay_ms(400);
      o=(1/(2*n));
      printf(lcd_putc,"\fSEMIPERIODO:");
      printf(lcd_putc,"\n%e",o);
      delay_ms(500);
      confirmar=0;
      printf(lcd_putc,"\fINICIANDO...");
      delay_ms(250); //Hasta ahi el programa trabaja de maravilla.
      while (1){
      output_high(pin_a0);
      delay_ms(o);
      output_low(pin_a0);
      delay_ms(o);}
      }}
      Intente producir el pulso mediante las lineas anteriores; si me genera pulsos pero no los esperados, en su mayoria me genera pulsos de 400us.
      Me gustaria saber si puedes ayudarme a acoplarlo a el programa que tu realizaste o si hay alguna otra manera de realizarlo, te agradeceria mucho la ayuda.
      Creo que hice mucho relajo para el keypad, pero fue la unica manera en que pude ingresar los 4 digitos.

      Eliminar
    3. en los tres #include que no tienen complemento se les escribio
      lcd.c, kbd.c y math.h....no entiendo el porque no se imprimieron en mi comentario anterior

      Eliminar
    4. u.u sorry, no tengo mucho chance de andar revisando código tan exhaustivamente, es demasiado por hacer, aparte ocupo el circuito aquí para debuggear

      Eliminar
  15. Exelente post me ayudo, gracias

    ResponderEliminar
  16. MUITO OBRIGADO, EU ESTAVA PRECISANDO.

    ResponderEliminar
  17. BUSCANDO POR TANTOS LADOS Y TU MI AMIGO LO EXPLICAS TAN SENCILLO QUE RAYOS DEBO DARTE LAS GRACIAS. MUCHAS GRACIAS!!

    ResponderEliminar
  18. muchas gracias por tu aporte amigo!! de verdad

    ResponderEliminar
  19. Solo una sugerencia, cambia el fondo de tu página porque no se puede distinguir el texto

    ResponderEliminar