• 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.

PIC 16F877 > Timers > 2 servos

kawauso

New Member
Dear members,
I want to control 2 servos, independently.
After analysis, i guess i need the 3 timers of the PIC :
1) timer1 : 20 mS for period signal,
2) timer0 : 1 mS with a loop and a counter cnt0 to count x time 1 mS for Ton for servo#1,
3) timer2 : 1 mS with a loop and a counter cnt2 to count x time 1 mS for Ton for servo#2
... but I have no idea how to start the code.
I tried, but only one servo turns.
Merci beaucoup for your help and feed-backs.
Éric
 

ronsimpson

Well-Known Member
Most Helpful Member
When I started my software I could not see how to make many servos with only a small PIC and one timer.
Servo signal is high for 1 to 2mS out of 20mS.
In your case of only 2 servos so set a timer for 10mS. (interrupt)
Work Servo1 for 1 to 2mS out of 10mS, then work servo2 for 1 to 2mS out of 10mS. Loop.
 

alec_t

Well-Known Member
Most Helpful Member
Perhaps use the WDT and so free up one of the other timers? For that chip the datasheet says the WDT period is in the range 7mS to 33mS and the WDT has a programmable prescaler. Although the '20mS' timing would be a bit vague, the 20mS period isn't generally critical; even ~50mS can work with some servos by all accounts.
Btw, the PWM module has two independent channels, albeit with the same clock.
 

gophert

Well-Known Member
Most Helpful Member
Show us the code you have already..

This chip has a PWM module so one can be used with this..
the OP will have to reduce clock speed to about 512k Hz to get PWM down fo 20mSec refresh period (If I remember correctly).
also, the PIC 18f46k80 is very similar chip but four independent PWM modules (two separate PIR registers if I remember correctly)

 

rjenkinsgb

Well-Known Member
Most Helpful Member
When I've done servo control, I've done it using software PWM.

Timing is based on a high frequency "clock interrupt" running from a timer - anything from one to 100KHz or more depending on requirements. That's divided down in the interrupt routine to decade values for different uses, down to eg. 100Hz or all the way to seconds-hours-days etc.

In the 100Hz routine, increment a counter that sequences through the servo outputs. Look at the low bit as you only need two outputs.
Or just invert it.
For more than two servos, use an array to store the required servo time (ie. position) values and get the value via the servo counter.

Load the position needed for that specific servo in to a variable - "servo_pwm" in my example. Then set the output pin that controls the appropriate servo high.

In a high frequency stage of the interrupt divider, see if the "servo_pwm" variable is zero. If so, clear the output pins. If not, decrement it.

These are fragments from a working design that had two servos.

This is the "start" section - ignore the prescale part, that's the divider that takes it down to around 100Hz.

C:
    // Do low speed timing.
    prescale++;
    if(prescale > 95)
    {
        int i;
        // 100Hz.
        prescale = 0;
       
        if(servo_toggle) {
            servo_toggle = false;
            output_high(PIN_B2);
            i = servo_a + 9;
        }
        else {
            servo_toggle = true;
            output_high(PIN_B3);
            i = servo_b + 9;
        }

        if(i > 19) i = 19;
        servo_pwm = i;

And this is the actual PWM output fragment, which ran at 9600 Hz in this design - it only needed a few approximate positions for the servos. Running the interrupt faster would give higher precision - I've run software pwm at around 250 - 300KHz (256 step x mains half cycle rate) on some things using such as the 877, so it's definitely possible to get extremely fine precisions like this.

C:
    if(!servo_pwm) {
        output_low(PIN_B2);
        output_low(PIN_B3);
    } else
        --servo_pwm;
In this specific program, the first part runs later in the same timer_0 interrupt & timing divider chain, so there can never be a race condition where the outputs could be cleared before the servo_pwm variable is set.

If you use independent timing sources for the two sections, set the servo_pwm value before setting the output high, so the countdown interrupt can't mess things up if it occurs in the middle of the setup.


Another option for multiple servos, if you are short of pins on the device, is use a CMOS shift register IC.
Connect one servo to each shift register output and you can control up to eight (or more with a second IC) with just two pins on the MCU.
(For info, this is the principle pre-MCU radio control receivers used to produce the individual servo outputs).

At a low rate (eg 20 - 50Hz), clear the servo_select counter and set the MCU pin feeding the shift register data in pin high.
Load the first servo time value to servo_pwm and pulse the shift reg clock. That sets the first output high.
Set the data pin low.

Each time servo_pwm gets to zero, increment the servo_select counter, "AND" in with a mask to prevent it getting out of bounds for the servo time array [eg. 0x07 for 8 values, 0x0f for 16] and copy the appropriate servo time value to servo_pwm and pulse the shift reg clock. That "moves" the high signal to the next servo in sequence.

The high bit will fall out the end of the register after 8 counts and the PWM timing may repeat, doing nothing until the next restart, but it's easier to let it do that than add code to stop it - it's harmless.

You can add a second shift register and clock pin, with them sharing the D input, to double the number of servos with just a second servo_pwm counter. Or more registers for silly numbers...
 

kawauso

New Member
Bonjour rjenkinsgb,
Thank you so much for all these details.
I will try to work on my code this week-end with my self mind logic and I will let it on the forum next week to get your advice.
Merci beaucoup.
 

Pommie

Well-Known Member
Most Helpful Member
Using the Special Event Trigger function of the PWM module a single pic can drive 8 servos and be completely interrupt driven. The timing will vary by up to 2 clock cycles due to interrupt latency but you won't notice that on most servos.

Mike.
 

Latest threads

EE World Online Articles

Loading

 
Top