Frequency of PWM

Discussion in 'Embedded Systems and Microcontrollers' started by ActivePower, Jul 19, 2012.

  1. ActivePower

    Thread Starter Member

    Mar 15, 2012
    155
    23
    How do you intuitively pick the PWM frequency for different applications?
    Does it depend on the device switching characteristics?
    For example, an LED could be turned ON and OFF relatively quickly compared to say, a DC motor.
    Does this dictate higher PWM frequency for a motor when compared to an LED?
    What other factors (if any) need to be taken into consideration?

    To begin with, here is my PWM code:

    Code ( (Unknown Language)):
    1. //main.c
    2. //To use PWM for dimming an LED
    3. //PWM Frequency: 19.53 MHz
    4. //CCP1 Register Pin=RC2
    5.  
    6. #include<htc.h>
    7. #define _XTAL_FREQ 20000000
    8.  
    9. __CONFIG(FOSC_HS & WDTE_OFF & DEBUG_ON & LVP_OFF & CPD_OFF & CP_OFF & PWRTE_OFF & BOREN_OFF);
    10.  
    11. void main()
    12. {
    13.     unsigned char duty;     //16 bit value
    14.     TRISC=0;            //set PORTC to output
    15.     PORTC=0;            //set PORTC low
    16.     PR2=0xFF;               //set TMR2 period register
    17.     T2CON=0b00000100;       //set postscaler to 1:1; prescaler to 1
    18.     CCP1CON=0b00001111;     //set the 2 LSB of duty cycle to 0 and initialize CCP module as PWM
    19.     duty=0;
    20.     while(1)
    21.     {          
    22.             while(duty<255)         //increase duty cycle from 0% to 100%
    23.             {
    24.                 CCPR1L=duty;
    25.                 duty++;
    26.                 #ifdef __DEBUG              //if debugging right now then skip the delay routine
    27.                 #else
    28.                 __delay_ms(300);
    29.                 #endif
    30.             }
    31.             while(duty>0)           //decrease duty cycle from 0% to 100%
    32.             {
    33.                 CCPR1L=duty;
    34.                 duty--;
    35.                 #ifdef __DEBUG
    36.                 #else
    37.                 __delay_ms(300);
    38.                 #endif
    39.             }
    40.     }  
    41. }
    42.  
    43.  
    44.  
    It compiles and builds correctly. However when I built the circuit the LED glows brightly instead of going to full brightness smoothly and back. The voltage across the LED is 2.5V.
    Does this imply that the frequency of switching is a bit too high (19.53 MHz) so that the voltage appears continuous to the LED?

    I have no prior experience with PWM much less using it alongside a microcontroller.

    Thanks!
     
  2. RogerAF

    New Member

    Jul 19, 2012
    1
    0
    If you want to see the LED ramp-up from off to full on, over a period of say 5 seconds, you should give it a longer time at the dim stage.

    I'd advise a lower PWM freq <1KHz, but I don't think that is your main problem. I think your code zips from dim to bright so quickly you can't see it.

    You could try to divide it into sections with duty cycles of 1%, 5 %, 10%, 20%, 50%, 75%, 90%, 99%, etc. Then check each by itself to see how dim the LED looks. Then you could step through the sections in such a way as to give the smoothest response.

    Remember that the LED is really turning off and on, but visual persistence of our eyes is such that we can't see that fast, so it looks like it is on the whole time. The shorter the on time (smaller duty cycle) the dimmer the appearance. But you probably won't see much change from 75% on up--it will look like full on. Also small changes like from 20% to 21% may not be apparent. That is why I suggest trying several different settings, to get a feel for how dim a specific duty cycle looks--the dimness is not likely to be linear.

    Once you establish what looks like equal steps, you can join them together with another loop.

    Hope this helps.
     
  3. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Generally set the frequency as high as you need for the desired effect but not much higher. For a LED you want it to look continuously on without flickering, which is around 100-200Hz.
    For a motor it's more complicated, most work well enough at a few hundred hertz, but at that frequency you can hear the switching frequency, which is annoying, so many motors are run at 20kHz.
    The higher the frequency, the more power is wasted in switching, which is the reason to keep it as low as possible.
     
    strantor likes this.
  4. MMcLaren

    Well-Known Member

    Feb 14, 2010
    759
    116
    I wonder if something like this (below) might work? Unfortunately, I'm not familiar with Hi-Tech C so I'm not sure how you setup a constant rom array.

    Good luck with your project.

    Cheerful regards, Mike

    Code ( (Unknown Language)):
    1. //main.c
    2. //To use PWM for dimming an LED
    3. //PWM Frequency: 19.53 kHz
    4. //CCP1 Register Pin=RC2
    5.  
    6. #include<htc.h>
    7. #define _XTAL_FREQ 20000000
    8.  
    9. __CONFIG(FOSC_HS & WDTE_OFF & DEBUG_ON & LVP_OFF & CPD_OFF & CP_OFF & PWRTE_OFF & BOREN_OFF);
    10.  
    11. const rom char gamma[] = {  0,  1,  2,  2,  2,  2,  3,  3,  3,  4,
    12.                             4,  5,  5,  6,  6,  7,  8,  8,  9, 10,
    13.                            11, 12, 13, 14, 15, 17, 18, 20, 21, 23,
    14.                            25, 27, 29, 31, 34, 36, 39, 42, 45, 49,
    15.                            53, 57, 61, 65, 70, 75, 81, 87, 93,100,
    16.                           107,114,123,131,140,150,161,172,183,196,
    17.                           209,224,239,255 };
    18.  
    19.  
    20. void main()
    21. {
    22.   unsigned char duty;       // 8 bit value
    23.   TRISC=0;          // set PORTC to output
    24.   PORTC=0;          // set PORTC low
    25.   PR2=0xFF;         // set TMR2 period register
    26.   T2CON=0b00000100;     // TMR2, pre 1, post 1, 200 ns ticks
    27.   CCP1CON=0b00001111;       // init CCP module as PWM
    28.   duty=0;
    29.   while(1)
    30.   {        
    31.     while(duty<64)          // increase duty cycle from 0% to 100%
    32.     {
    33.       CCPR1L= gamma[duty++];    //
    34.       __delay_ms(15);       // approx 1 second fade-up
    35.     }
    36.     duty--;
    37.     while(duty>=0)      // decrease duty cycle from 100% to 0%
    38.     {
    39.       CCPR1L= gamma[duty--];    //
    40.       __delay_ms(15);       // approx 1 second fade-down
    41.     }
    42.   }
    43. }
     
    Last edited: Jul 24, 2012
  5. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    Design it first, then write the code.

    I did a LED dimmer once using a PIC12HV615. With it running on it's internal 8MHz oscillator the slowest I could control the LED via the built in PWM was 200 Hz.

    Anything above about 30Hz is considered "good," though there are people like myself who see that flicker if the LED travels across our field of vision. I could not see that effect at 200Hz when I shook the unit across my test bench.

    As you go up in frequency you may have to take switching losses into effect. For a LED, I saw an inexpensive backyard solar powered lamp running the LED at 100KHz I believe (was 2-3 years back when I measured it). The LED seemed not to mind.

    A motor should look inductive for the most part, so I don't think it should "mind" a higher frequency, but my only experience here is controlling small DC motors on HO model trains. I believe I was running at 100Hz.

    Anyone else with better (any?) experience with motor is very welcome to add onto that.
     
  6. ActivePower

    Thread Starter Member

    Mar 15, 2012
    155
    23
    I am sorry I have not been able to compose a reply sooner. Being in a college dorm changes a few things as I don't have a PC with a serial port to support my programmer and need to wait the whole weekend for testing out my small programs in the lab the next week.
    Meanwhile, I have decreased the PWM frequency and made a few smaller changes. Just hoping it would turn out okay. I'll let you know what happens. Fingers crossed! ;)
     
  7. ActivePower

    Thread Starter Member

    Mar 15, 2012
    155
    23
    I tried the PWM program again today with little success (literally). I configured it first to 1.22 kHz just to test whether the frequency was playing the bad guy. (I know, stupid!)

    I changed the register configs and fiddled around with bits a little just to see if I am lucky and some change pops up! It did, in part, as I could see some flicker in the LED (or was I straining my eyes too hard?).

    All in all, it was a less than fruitful 90 minutes trying to get the thing work (but it was fun!).


    Here is the modified code (I realize I might have messed this up a lot and quite a lot of it might be wrong!)

    Code ( (Unknown Language)):
    1. //main.c
    2. //To use PWM for dimming an LED
    3. //PWM Frequency: 1.22 kHz
    4. //CCP1 Register Pin=RC2
    5.  
    6. #include<htc.h>
    7. #define _XTAL_FREQ 20000000
    8.  
    9. __CONFIG(FOSC_HS & WDTE_OFF & DEBUG_ON & LVP_OFF & CPD_OFF & CP_OFF & PWRTE_OFF & BOREN_OFF);
    10.  
    11. void main()
    12. {
    13.     unsigned int duty;      //16 bit value
    14.     unsigned int wtch_duty;
    15.     TRISC=0;            //set PORTC to output
    16.     PORTC=0;            //set PORTC low
    17.     PR2=0xFF;               //set TMR2 period register
    18.     T2CON=0b00000101;       //set postscaler to 1:1; prescaler to 16
    19.     CCP1CON=0b00001111;     //set the 2 LSB of duty cycle to 0 and initialize CCP module as PWM
    20.     duty=0;
    21.     while(1)
    22.     {          
    23.             while(duty<255)         //increase duty cycle from 0% to 100%
    24.             {
    25.                 CCPR1L=duty;
    26.                 duty++;
    27.                 unsigned char x=CCP1X;
    28.                 unsigned char y=CCP1Y;
    29.                 wtch_duty=(unsigned int) CCPR1L<<2;
    30.                 wtch_duty=wtch_duty + (unsigned int)CCP1X<<1;
    31.                 wtch_duty=wtch_duty + (unsigned int)CCP1Y<<1;
    32.                 #ifdef __DEBUG              //if debugging right now then skip the delay routine
    33.                 #else
    34.                 __delay_ms(150);
    35.                 #endif
    36.             }
    37.             while(duty>0)           //increase duty cycle from 0% to 100%
    38.             {
    39.                 CCPR1L=duty;
    40.                 duty--;
    41.                 #ifdef __DEBUG
    42.                 #else
    43.                 __delay_ms(250);
    44.                 #endif
    45.             }
    46.     }  
    47. }
    48.  
    49.  
    50.  
    Please ignore the CCPX/Y part, it was plain stupid. That is just an indication of how much I have yet to learn.

    A slightly unrelated remark: To add pain to the process though, I now realize why people go for In-Circuit Serial/USB programmers. I spent half my time shifting my PIC from the programmer to the circuit board!

    EDIT: I now realize it was a little naive of me to not have used the oscilloscope to see the output when I was busy probing the LED voltages with my multimeter.
     
  8. MMcLaren

    Well-Known Member

    Feb 14, 2010
    759
    116
    I just tested a version of the example I posted earlier and it fades up and down quite smoothly. I'm using the free/lite version of BoostC and a 12F1822 with INTOSC at 8 MHz and PWM frequency of ~7812.5 Hz. The gamma[] array is a simple mechanism for getting 64 linear brightness level steps spanning the non-linear 256 PWM brightness level steps.

    I hope a working example helps. Of course motor control is an entirely different matter. Good luck on your project.

    Cheerful regards, Mike

    Code ( (Unknown Language)):
    1.  
    2.    #include <system.h>
    3.  
    4.    #pragma DATA _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _MCLRE_ON
    5.    #pragma DATA _CONFIG2, _LVP_OFF & _PLLEN_OFF
    6.  
    7.    #pragma CLOCK_FREQ 8000000   // 8-MHz INTOSC
    8.  
    9.    #define r08 const rom unsigned char
    10.  
    11.  
    12.    r08 gamma[] = {  0,  1,  2,  2,  2,  2,  3,  3,  3,  4,
    13.                     4,  5,  5,  6,  6,  7,  8,  8,  9, 10,
    14.                    11, 12, 13, 14, 15, 17, 18, 20, 21, 23,
    15.                    25, 27, 29, 31, 34, 36, 39, 42, 45, 49,
    16.                    53, 57, 61, 65, 70, 75, 81, 87, 93,100,
    17.                   107,114,123,131,140,150,161,172,183,196,
    18.                   209,224,239,255 };
    19.  
    20.    void main()
    21.    { ansela = 0;                // make pins digital
    22.      trisa = 0b00000000;        // porta all outputs
    23.      porta = 0;                 // all output latches low
    24.      osccon = 0b01110010;       // initialize 8-MHz INTOSC
    25.      while(!oscstat.HFIOFS);    // wait until OSC stable
    26.  
    27.      ccp1con = 0b00001100;      // pwm mode, p1a active hi
    28.      ccpr1l = 0;                // 0% duty cycle initially
    29.      pr2 = 255;                 //
    30.      t2con = 1<<TMR2ON;         // pre 1, post 1, 7812.5 Hz
    31.  
    32.      while(1)                   //
    33.      { static char duty = 0;    //
    34.        while(duty < 64)         // fade up
    35.        { ccpr1l = gamma[duty++];
    36.          delay_ms(15);          // over period of ~1 sec
    37.        }                        //
    38.        duty--;                  //
    39.        while(duty > 0)          // fade down
    40.        { ccpr1l = gamma[duty--];
    41.          delay_ms(15);          // over period of ~1 sec
    42.        }                        //
    43.      }                          //
    44.    }
    45.  
     
    Last edited: Jul 24, 2012
  9. ActivePower

    Thread Starter Member

    Mar 15, 2012
    155
    23
    Sorry for not replying earlier. I finally got the thing to work today. The problem was the duty cycle which was changing way too fast for any fading effect to be visible. I set the fade up and fade down time to 0.5 ms and it worked!

    @MMcLaren: Thanks, your code helped me catch the delay problem :)
     
  10. ActivePower

    Thread Starter Member

    Mar 15, 2012
    155
    23
    I have an additional question related to PWM in PIC16F877A.

    The PWM duty cycle register is 10 bit but I could not figure out a way to access the two bits stored in CCPCON register leaving me effectively with a 8 bit PWM.

    Is there any way to use the duty cycle in its entirety?

    It'd blend in well with the 10 bit ADC mode as I would not have to convert the 10 bit ADC value to 8 bit for adjusting the duty cycle for a photoresistor based light dimmer I am trying to get working.

    Thanks!
     
  11. ErnieM

    AAC Fanatic!

    Apr 24, 2011
    7,386
    1,605
    Yes there is, it's not very clean but you have to pull out the least two significant bits from your PWM and place them into CCP1CON.CCP1X and CCP1CON.CCP1Y.

    Code ( (Unknown Language)):
    1. CCPR1L = PWM >> 2; // to shift out 2 LSB's
    2. CCP1CON.CCP1X = PWM & 0x2;   // 2nd least sig bit
    3. CCP1CON.CCP1Y = PWM & 0x1;   // least sig bit
     
    ActivePower likes this.
  12. Markd77

    Senior Member

    Sep 7, 2009
    2,803
    594
    Here it is in assembler if you are curious (duty_H and duty_L are not preserved):
    Code ( (Unknown Language)):
    1. ;H_byte:L_byte > CCPR1L+CCP1CON<5:4>
    2.     clrf temp
    3. ;    bcf STATUS,C ;not needed
    4.     rrf duty_H, F
    5.     rrf duty_L, F
    6.     rrf temp, F
    7.     rrf duty_H, F
    8.     rrf duty_L, F                    ;duty_L now contains highest 8 bits
    9.     rrf temp, F                        ;temp now contains low 2 bits in <7:6>
    10.     rrf temp, F
    11.     rrf temp, F                        ;temp now contains low 2 bits in <5:4>
    12.                                     ;phew
    13.      
    14.     movf duty_L, W
    15.     movwf CCPR1L
    16.      
    17.     movf temp, W                    ;low 2 bits to CCPR1CON <5:4> IOR with PWM on
    18.     iorlw b'00001100'
    19.     movwf CCP1CON
    20.  
     
    ActivePower likes this.
Loading...