PIC12F683 PWM Problem

Discussion in 'Embedded Systems and Microcontrollers' started by krlngc, Jun 19, 2014.

  1. krlngc

    Thread Starter New Member

    Dec 9, 2013
    11
    0
    Hello,

    I aim to produce PWM with 20ms period and 2ms duty cycle. I use P12F683 @4MHz, MpLab xc8. I calculated the PR2 value given by data sheet;

    PWM Period = [(PR2) + 1] • 4 • Tosc • TMR2 Prescale Value

    However when I do the simulation on Proteus, PWM's period comes out shorter than I expect it to be. It's 3.7ms. I also tried some random values for PR2 in order to approach 20ms, none of them worked. I can't really understand what causes the problem. I'd be grateful if you could share your opinions.

    Here's the function I use for initializing PWM
    Code ( (Unknown Language)):
    1.  
    2. void InitPWM()
    3. {
    4.     OSCCONbits.SCS=1;   //Internal oscillator is used for system clock
    5.     OSCCONbits.IRCF=0b110; // Internal Oscillator Frequency 4 MHz
    6.     CCP1CON=0x2C;          // bit3-0 PWM Mode
    7.     TRISIO2=0;             // CCP1 output
    8.     T2CON=0x06;            // Timer2 is on , Prescale 1:16
    9.     PR2=1249;              // 20 ms = (PR +1) *4 * 250ns * TMR2 Prescale value
    10.     CCPR1L=0b01;           //
    11. }
    12.  
    Thank you
     
  2. MaxHeadRoom

    Expert

    Jul 18, 2013
    10,509
    2,369
  3. tshuck

    Well-Known Member

    Oct 18, 2012
    3,531
    675
    PR2 is an 8-bit register, and you've supplied 1249. This is truncated to the lower 8-bit value, so 1249 becomes 225. Putting 225 in your equation yields:

    Period = 250ns*4*[225 + 1]*TMR2 Prescale ~= 3.7ms.

    Recalculate your value for an 8-bit PR2...
     
  4. MaxHeadRoom

    Expert

    Jul 18, 2013
    10,509
    2,369
    The calculator shows the lower limit to be 250Hz with those numbers.
    Max.
     
  5. krlngc

    Thread Starter New Member

    Dec 9, 2013
    11
    0
    Oh I see now. Thank you very much for your replies.
     
  6. MMcLaren

    Well-Known Member

    Feb 14, 2010
    759
    116
    Setting up the PWM module for a very low frequency period, 50-Hz (20,000 usecs) in this case, is somewhat difficult. Instead, consider breaking up that 20,000 usec period into smaller PWM periods or "frames". For example, use twenty 1,000 usec (1-KHz) PWM period "frames", or use eighty 250-usec (4-KHz) PWM period "frames", to make up the much longer 20,000 usec period.

    Since the CCPR1L "duty cycle" register is double-buffered, that is, the value you place into it is used by the PWM module in the "next" PWM cycle, you can use a small ISR "helper" to load the duty cycle register and keep track of the overall duty cycle pulse and 20,000 usec period during each PWM interrupt cycle.

    This method can be used to produce a high resolution pulse width of 0 to 20,000 usecs in a very low 50-Hz period. Here's a quick-n-dirty (and untested) example in XC8 for 12F683 with 1-usec pulse width resolution;

    Code ( (Unknown Language)):
    1.  
    2. /********************************************************************
    3.  *                                                                  *
    4.  *  Project: 12F683 PWM #1                                          *
    5.  *   Source: 12F683_PWM_#1.c                                        *
    6.  *   Author: Mike McLaren, K8LH                                     *
    7.  *  (C)2013: Micro Application Consultants, All Rights Reserved     *
    8.  *     Date: 19-Jun-2014                                            *
    9.  *                                                                  *
    10.  *  12F683 PWM Demo, PWM output on GP2/CCP1 (pin 5)                 *
    11.  *                                                                  *
    12.  *                                                                  *
    13.  *      IDE: MPLAB v8.92 (tabs = 4)                                 *
    14.  *     Lang: Microchip XC8 v1.31                                    *
    15.  *                                                                  *
    16.  ********************************************************************/
    17.  
    18.    #include <xc.h>
    19.  
    20.    #pragma config FOSC = INTOSCIO, MCLRE = OFF, WDTE = OFF
    21.  
    22.  
    23.    #define _XTAL_FREQ 4000000
    24.  
    25.   /******************************************************************
    26.    *  function prototypes                                           *
    27.    ******************************************************************/
    28.   /******************************************************************
    29.    *  type definitions                                              *
    30.    ******************************************************************/
    31.   /******************************************************************
    32.    *  variables and constants                                       *
    33.    ******************************************************************/
    34.  
    35.    unsigned char frame = 1;     // ISR PWM "frame" number, 0..80
    36.    unsigned int pulse = 0;      // ISR PWM "pulse" work variable
    37.  
    38.    unsigned int width = 2000;   // pulse width, 0..20,000 usecs
    39.  
    40.   /******************************************************************
    41.    *  low level drivers                                             *
    42.    ******************************************************************/
    43.   /******************************************************************
    44.    *  functions                                                     *
    45.    ******************************************************************/
    46.  
    47.    void setdutycycle(unsigned int dcyvalue)
    48.    { INTCONbits.GIE = 0;        // suspend interrupts
    49.      width = dcyvalue;          // update 'width', 0..20000 (usecs)
    50.      INTCONbits.GIE = 1;        // restore interrupts
    51.    }                            //
    52.  
    53.   /******************************************************************
    54.    *  main init                                                     *
    55.    ******************************************************************/
    56.  
    57.    void main()
    58.    { ANSEL = 0;                 // make pins digital
    59.      TRISIO = 0;                // all outputs
    60.      GPIO = 0;                  // all output latches low
    61.      OSCCON = 0b01100010;       // initialize 4-MHz INTOSC
    62.      while(!HTS);               // wait until OSC stable
    63.   /*                                                                *
    64.    *  setup PWM and TMR2 for 250-usec PWM period "frames"           *
    65.    *                                                                */
    66.      TMR2 = 0;                  // clear TMR2
    67.      PR2 = 250-1;               // 250-usec period
    68.      PIE1 = 0;                  // clear peripheral interrupt enables
    69.      PIR1 = 0;                  // clear peripheral interrupt flags
    70.      T2CON = 0b00000000;        // 00000000
    71.                                 // -0000--- TOUTPS, postscale 1:1
    72.                                 // -----0-- TMR2ON, turn TMR2 off
    73.                                 // ------00 T2CKPS, prescale 1
    74.      CCP1CON = 0b00001100;      // 00001100
    75.                                 // --00---- DC1B, duty cycle lsb's
    76.                                 // ----1100 CCP1M, PWM active high
    77.      PIE1bits.T2IE = 1;         // enable TMR2 interrupts
    78.      INTCONbits.GIE = 1;        // enable global interrupts
    79.      INTCONbits.PEIE = 1;       // enable peripheral interrupts
    80.      T2CONbits.TMR2ON = 1;      // start TMR2
    81.  
    82.  
    83.   /******************************************************************
    84.    *  main loop                                                     *
    85.    ******************************************************************/
    86.  
    87.      while(1)                   //
    88.      {                          //
    89.      }                          //
    90.    }                            //
    91.  
    92.   /******************************************************************
    93.    *  interrupt service routine                                     *
    94.    ******************************************************************/
    95.  
    96.    void interrupt isr()         // 250-usec TMR2 interrupts
    97.    {                            //
    98.      PIR1bits.TMR2IF = 0;       // clear TMR2 interrupt flag
    99.      if(--frame == 0)           // if end of 20 msec period
    100.      { frame = 80;              // reset for new 20 msec period
    101.        pulse = width;           // reset 'pulse' work variable
    102.      }                          //
    103.      if(pulse > 250)            // if remaining pulse > 250 usecs
    104.      { CCPR1L = 250;            // do a 100% duty cycle frame
    105.        pulse -= 250;            // adjust balance of 'pulse'
    106.      }                          //
    107.      else                       // do a variable or 0% frame
    108.      { CCPR1L = pulse;          // 0..250 usecs
    109.        pulse = 0;               // force remaining frames to 0%
    110.      }                          //
    111.    }                            //
    112.  
    113.  
    Good luck on your project.

    Cheerful regards, Mike
     
    Last edited: Jun 19, 2014
  7. BobTPH

    Active Member

    Jun 5, 2013
    782
    114
    Alternately, you could just run the clock slower. That would allow for longer PWM periods. If you run at 500KHz you will be able to get a 20ms period.

    You will, however get better resolution on the angle of your servo by following the directions in the previous post.

    Bob
     
  8. krlngc

    Thread Starter New Member

    Dec 9, 2013
    11
    0

    I guess I need higher duty cycle. Because servo goes directly to -90 degree. How can I increase the duty cycle of each frame? I tried CCPR1L=250 but I couldn't get result

    Thank you
    Efe
     
    Last edited: Jun 20, 2014
  9. MMcLaren

    Well-Known Member

    Feb 14, 2010
    759
    116
    So you're using a Servo? Did you verify that you're producing a 2000-usec pulse? If so, what did you expect the Servo to do when driven by a 2000-usec pulse?

    Do you know the servo pulse width range? Five minutes of research should be enough to figure this out. Once you have the answer, adjust the "width" variable declaration for the Servo angle you want.

    Good luck on your project...

    Cheerful regards, Mike
     
    Last edited: Jun 20, 2014
  10. krlngc

    Thread Starter New Member

    Dec 9, 2013
    11
    0
    I've handled the problem. Thank you for your help.

    Kind regards,
    Efe
     
Loading...