Continue to Site

Welcome to our site!

Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

  • Welcome to our site! Electro Tech is an online community (with over 170,000 members) who enjoy talking about and building electronic circuits, projects and gadgets. To participate you need to register. Registration is free. Click here to register now.

PWM frequency limitations!!

Status
Not open for further replies.

PIG80085

New Member
Hi,
I've been all over the internet and back and I've searched here and still haven't found an answer to this one.

I'm trying to control a servo using the PWM module of a PIC 16F627. According to what I can find most hobby servos have a neutral of about 1.5mS. But after doing the calculations given in the 16F62x data sheets (section 11.3.1) it seems I cannot get a PWM period of more than about 0.8 mS. That's with an 8-bit PR2 (at 255), a 20MHz Tosc, and a prescaler of 1, 4 or 16 (using 16). I've a feeling I'm missing something simple but I just can't find it.

Thanks in advance.
PIG80085

PS. Anyone know if it's possible / practical to control more than one servo independantly using one PWM module???
 
nope, pic pwm hw isn't usable for RC servos. min frequency is too high. the good news is that it is very simple to do pwm in software. Use 1 or 2 timers. timer2 is particularly convenient but you can use any of them. you can control multiple servos though it takes a bit more thinking and a few trade-offs.
 
Don't use the PWM modules, use software loops or timer interrupts, there's not really enough resolution in the PWM modules for driving servos. If you really want to (just for one servo with a 628) you can use interrupts and change the PWM settings every single pulse
 
eblc1388 said:
What's keeping you from using a 4MHz crystal to give longer PWM period?


Hmmm. Stupidity mostly...
That would give me a long enough period to control a servo. I'll just need to see if it's enough to control more than one. I'll look into software implementation too.

Thank you everyone for your replies!!:D :D
 
hmmm, from the datasheet, PMW period = (PR2+1) * Tosc * 4 * Timer2_prescale

using max values, PR2 = 255, Tosc = 250 nS, T2prescale = 16, you get 4.096 mS max PWM period.

didn't we want a 20 mS PWM period for the servo?
Hmmm. Stupidity mostly...
I think not...

there is a note about using the postscalar to change update rate but no detail beyond that.
 
Last edited:
As has been pointed out by several writers before on this forum, it is really the width of the "on" pulse, not the duty cycle per se that determines the position of model servos. They are will accept a wide range of PWM frequencies and not move unless the on period changes. I am currently using a 628A with 2.4 MHz crystal to give a PWM frequency of 146 Hz (about 6.8 ms period). Conventional hobby servos work fine at that frequency. I should add, however, that my application uses the servo as a latch. It is very steady, but I have not tested it in a more demanding role, such as elevator control on a fast airplane. I suspect it would be adequate for steering a relatively slow robot.
 
Yes, I've pointed that out myself. it would be worth while testing it at 4mhz. Note, however, that every servo model is a little different - at least with 20 mS period, it's guarranteed. SW PWM at that rate is pretty easy and low overhead.
 
The original program I used was at 4 MHz, and the servo worked. As I recall (from April of this year), I could make the servo make odd noises or start oscillating when physically displaced. Below 200 HZ, that was not a problem at all. In fact, the servos seemed to work better at frequencies slightly higher than 50 Hz, like 75 to 100 Hz. I use Hitec servos. It worked fine with the low-cost HS300 and the mid-cost HS85 and HS80 servos. I did not try it with high-end servos.

Im my particular case, Iwanted to use the PWM module, because I had a number of other things going on (e.g., sensor and button inputs) and didn't want to mess with making all of the possible loop times the same. The PWM just sort of sits there and doesn't react to what else is going on, until you change it. The device has been used all Summer in the field (outdoors) and has been rock solid. I have had no twitching, noise, or heating of the servo. John
 
It can be difficult coming up with a 20.0-msec period using the PWM module with higher clock frequencies but the CCP module "compare" mode can be used to provide a very simple interrupt driven hi-resolution Servo driver.

Here's example ISR code in C18;
Code:
/*                                                               *
 *  set Pulse to some value between 400 and 2400 (usecs)         *
 *                                                               *
 *  TMR1 prescaler is 2:1 for 1-usec 'ticks' (8-MHz clock)       *
 *                                                               */

unsigned int Pulse  =  1500;            // 1-usec TMR1 'ticks'
unsigned int Period = 20000;            // 1-usec TMR1 'ticks'

/*****************************************************************
 *  ISR (high)                                                   *
 *****************************************************************/
#pragma interrupt isr_hi

void isr_hi ()
{ if (PIR1bits.CCP1IF == 1)             // CCP1 interrupt?
  { PIR1bits.CCP1IF = 0;                // yes, clear int flag
    if (CCP1CONbits.CCP1M0 == 0)        // CCP1 is hi, make it go
    { CCP1CONbits.CCP1M0 = 1;           // lo next match interrupt
      CCPR1H += (Pulse/256);            // setup Pulse on time
      CCPR1L += (Pulse%256);            // match/interrupt value
    }
    else                                // CCP1 is lo, make it go
    { CCP1CONbits.CCP1M0 = 0;           // hi next match interrupt
      CCPR1H += ((Period-Pulse)/256);   // setup Pulse off time
      CCPR1L += ((Period-Pulse)%256);   // match/interrupt value
    }
  }
}
And here's the same algorithm in assembly language (untested);
Code:
;******************************************************************
;
        org     h'0004'

ISR_Vector
;
;  save main program context
;
        movwf   W_ISR           ; save W-reg                      |B?
        swapf   STATUS,W        ; doesn't change STATUS bits      |B?
        movwf   S_ISR           ; save STATUS reg                 |B?
        clrf    STATUS          ; force bank 0                    |B0
        movf    FSR,W           ;                                 |B0
        movwf   F_ISR           ; save FSR                        |B0
;
;  test and clear CCP1 interrupt flag bit
;
        btfss   PIR1,CCP1IF     ; CCP module interrupt?           |B0
        goto    ISR_XIT         ; no, branch, else                |B0
        bcf     PIR1,CCP1IF     ; clear CCP interrupt flag bit    |B0
;
;  setup next CCP1 pin transition and compare interrupt timing
;
        btfsc   CCP1CON,CCP1M0  ; is CCP1 pin hi or lo?           |B0
        goto    Pulse_Lo        ; lo, branch, else                |B0
;
;  pulse is hi so setup CCP1 to go lo on Pulse "on-time" match
;
Pulse_Hi
        bsf     CCP1CON,CCP1M0  ; setup CCP1 to go lo next match  |B0
        movf    PulseLo,W       ;                                 |B0
        addwf   CCPR1L,f        ; CCPR1L += (Pulse)%256           |B0
        movf    PulseHi,W       ;                                 |B0
        skpnc                   ;                                 |B0
        incf    PulseHi,W       ;                                 |B0
        addwf   CCPR1H,f        ; CCPR1H += (Pulse)/256           |B0
        goto    ISR_XIT         ;                                 |B0
;
;  pulse is lo so setup CCP1 to go hi on pulse "off-time" match
;
Pulse_Lo
        bcf     CCP1CON,CCP1M0  ; setup CCP1 to go hi next match  |B0
        movf    PulseLo,W       ;                                 |B0
        sublw   low  d'20000'   ;                                 |B0
        skpc                    ; borrow? no, skip, else          |B0
        decf    CCPR1H,f        ;                                 |B0
        addwf   CCPR1L,f        ; CCPR1L += (Period-Pulse)%256    |B0
        skpnc                   ;                                 |B0
        incf    CCPR1H,f        ;                                 |B0
        movf    PulseHi,W       ;                                 |B0
        sublw   high d'20000'   ;                                 |B0
        addwf   CCPR1H,f        ; CCPR1H += (Period-Pulse)/256    |B0
;
;  restore main program context
;
ISR_XIT
        movf    F_ISR,W         ;                                 |B0
        movwf   FSR             ; restore FSR                     |B0
        swapf   S_ISR,W         ;                                 |B0
        movwf   STATUS          ; restore STATUS                  |B?
        swapf   W_ISR,f         ; don't screw up STATUS           |B?
        swapf   W_ISR,W         ; restore W-reg                   |B?
        retfie                  ; return from interrupt           |B?
 
Status
Not open for further replies.

Latest threads

Back
Top