# Servos Program. ( Concurrent needed ? )

#### wk7au

Joined Apr 16, 2013
15
void main(void)
{
unsigned int i,a;

TRISB = 0b00000011;
PORTB = 0b00000000;

while(1)
{

RB7=1;
for(i=0;i<50;i++)
{
RB5=1;
DelayUs(157);

RB5=0;
DelayMs(6);
DelayUs(250);
DelayUs(250);
DelayUs(9);

}

for(i=0;i<50;i++)
{
RB4=1;
DelayUs(250);
DelayUs(250);
DelayUs(86);

RB4=0;
DelayMs(6);
DelayUs(80);

}

This program is running with MPLab. My project is to construct a robot arm, this program works fine but when external force applied (due to the weight of my arm) then the servos will not capable to resist the forces when servos not powered then it will just "fell" off.Hence, I want to know how to send a continuous pulse to instruct the servo stay in position.

Concurrent program ? But how ?

THE MAIN POINT IS BOTH RB5 & RB4 CAN RUN TOGETHER

Thank you.

#### Markd77

Joined Sep 7, 2009
2,806
Just combine the two loops:
pulse on RB4
delay
pulse on RB5
delay
then repeat

I heard that the time between pulses (of each servo) should be 20ms, but if it's working for you use a delay of about 3ms in between each servo pulse.

#### wk7au

Joined Apr 16, 2013
15
I tried but the force of motor applied stopped after 1-2 seconds so yea the arm fell too so I am seeking for any other possible solutions perhaps if no then maybe I will try again by combining two loops

#### tubeguy

Joined Nov 3, 2012
1,157
You may know this, but servos normally expect a pulse width which varies from 1 ms - 2ms. 1.5ms width would be the centered position.

#### wk7au

Joined Apr 16, 2013
15
I know, I have calibrate all the possible angles for my servo , between 2ms-0.5ms , 45 degree,90 degree etc etc , i have found it all . BUT the problem now is the servo can not receive the continuous pulse which allow my servo to be powered all the time and able to resist the weight of my robot arm then it just tend to move to the position I instruct but after 2 seconds it just fell bcause it doesn't receive anymore pulse (WHY FELL? BECAUSE THE WEIGHT OF MY ROBOT ARM ). THE PROGRAM ABOVE IS BASED ON 1ms 2ms 1.5ms theory and YEAH I KNOW THE THEORY BUT THIS DOESN'T RELATED WITH MY PROBLEM .

#### tubeguy

Joined Nov 3, 2012
1,157
We don't know what you know without asking...

#### tubeguy

Joined Nov 3, 2012
1,157
RB5=1;
DelayUs(157);

RB5=0;
...
This is why I mentioned the pulse width specs.

Last edited:

#### wk7au

Joined Apr 16, 2013
15
Ummm hmm, currently i just want to know how to send the continuous pulse to the servos tgt at the same time, can u suggest the way?

#### tshuck

Joined Oct 18, 2012
3,534
I think this task is better suited toward interrupts. If you know about them, use them, if not, well, then it's probably time to learn.

With an interrupt, you can interrupts every Xμs. and turn various pins on or off, according to a set of desired servo settings. So, something like:
Rich (BB code):
Servo1Value = X;
Servo2Value = K;
Counter = 0;

loop: <- occurs every 100μs.

Counter = Counter + 1;
if (Servo1Value < Counter)
Servo1 = 1;
else
Servo1 = 0;
if (Servo2 Value < Counter)
Servo2 = 1;
else
Servo2 = 0;
end loop
, could work. 20ms is a long time for a uC to do things....

#### tshuck

Joined Oct 18, 2012
3,534
Ummm hmm, currently i just want to know how to send the continuous pulse to the servos tgt at the same time, can u suggest the way?
PWM module will do it, but you have issues with the resolution and adding another servo, depending on how you do it....

#### MMcLaren

Joined Feb 14, 2010
846
Which PIC are you using? If it's an 18F device with two PWM channels you can use them to drive both servos. More on that later. If it's a 16F device you could use the CCP module with timer 1 and implement an interrupt driven 8-channel "soft" servo driver. Here's the basic "engine" code (you'd need to initialize the CCP module and timer 1 in "special event trigger" mode, setup ports, turn on interrupts, etc.);

Rich (BB code):
/*****************************************************************
*  K8LH Crazy-8 'Soft' 8-channel Servo Controller variables	 *
*****************************************************************/

unsigned int Pulse [] = { 1500, 1500, 1500, 1500,
1500, 1500, 1500, 1500,
20000 };
Rich (BB code):
 /****************************************************************
*  K8LH Crazy-8 Hi-Rez Soft 8-channel (PORTB) Servo Algorithm  *
*                                                              *
*  1 usec steps, 600-2400 usecs, prescale 1,2,4 (4,8,16 MHz)   *
****************************************************************/
void interrupt()
{ static char n = 0;          //
static char servo = 1;      //

if (pir1.CCP1IF == 1)       // if "special event" interrupt
{ pir1.CCP1IF = 0;          // clear the interrupt flag
LATB = Servo;             // output new Servo pulse
CCPR1 = Pulse[n++];       // setup the CCP1 "match" value

Pulse[8] -= CCPR1;        // adjust end-of-cycle off time
Servo <<= 1;              // and prep for next channel

if (n == 9)               // if end of a 20-msec period
{ n = 0; Servo = 1;       // reset "n" and "Servo"
Pulse[8] = 20000;       // reset the 20.0-msec period
}
}
}
This example 8-channel servo "engine" sends each servo pulse out one at a time in succession while keeping track of the amount of time used so that the last (ninth) period can be used to fill out the balance of the 20 msec period. The tricky part about a soft servo driver is that you really should disable interrupts momentarily in your main program when you want to update any individual 16-bit servo pulse width value. Unfortunately, this could interfere with an interrupt and cause jitter during one servo pulse output.

Hope this helps.

Cheerful regards, Mike

Last edited:

#### MMcLaren

Joined Feb 14, 2010
846
I mentioned using the PWM module in the previous post where you could use both CCP modules on an 18F device to drive two servos at hi-rez with zero pulse and period jitter. While many programmers will suggest that it's difficult to obtain long PWM periods with adequate pulse width resolution, I found that it's relatively easy if you break up the much longer servo period into several much shorter PWM "frames". This requires using a small interrupt driven "helper" program and I'd be happy to elaborate if anyone is interested.

Another interesting feature, PWM Steering, has turned up on some recent PIC devices. This feature allows you to direct a single PWM output onto any of four output pins. Earlier this year I wrote a hi-rez jitter free 4-channel Servo demo for one of these devices, a fourteen pin (enhanced mid-range) PIC16F1823. While the program is written in assembly language, there are pseudo C code comments which may help if you want to convert the demo program into your favorite version of C. For example, here are the pieced together (BoostC) pseudo C code comments for the interrupt driver;

Rich (BB code):
;
;  void interrupt()             // 250-uS (1000 cycle) interrupts
;  { pir1.TMR2IF = 0;           // clear TMR2 interrupt flag
;    if(width >= 250)           // if width >= 250 usecs
;      ccpr1l = 250;            // use a 100% duty cycle
;    else                       // otherwise
;      ccpr1l = width;          // use balance of width or 0%
;    width -= ccpr1l;           // adjust balance
;
;    pstr1con = steer | 1<<STR1SYNC;
;
;    if(--frame == 0)           // if end of 5 ms period
;    { frame = 20;              // reset 5 ms counter and
;      if(!(steer >>= 1))       // if last channel (20 msecs)
;        steer = 8;             // reset for channel 0
;      width = servo[index];    // new servo 'width'
;      index++; index.2 = 0;    // next channel index, 0..3
;    }
;  }
;
I've attached the 16F1823 demo program for anyone who is interested.

Cheerful regards, Mike

#### Attachments

• 3.2 KB Views: 10
Last edited:

#### wk7au

Joined Apr 16, 2013
15
I think this task is better suited toward interrupts. If you know about them, use them, if not, well, then it's probably time to learn.

With an interrupt, you can interrupts every Xμs. and turn various pins on or off, according to a set of desired servo settings. So, something like:
Rich (BB code):
Servo1Value = X;
Servo2Value = K;
Counter = 0;

loop: <- occurs every 100μs.

Counter = Counter + 1;
if (Servo1Value < Counter)
Servo1 = 1;
else
Servo1 = 0;
if (Servo2 Value < Counter)
Servo2 = 1;
else
Servo2 = 0;
end loop
, could work. 20ms is a long time for a uC to do things....

I will take a try on this

#### wk7au

Joined Apr 16, 2013
15
Which PIC are you using? If it's an 18F device with two PWM channels you can use them to drive both servos. More on that later. If it's a 16F device you could use the CCP module with timer 1 and implement an interrupt driven 8-channel "soft" servo driver. Here's the basic "engine" code (you'd need to initialize the CCP module and timer 1 in "special event trigger" mode, setup ports, turn on interrupts, etc.);

Rich (BB code):
/*****************************************************************
*  K8LH Crazy-8 'Soft' 8-channel Servo Controller variables	 *
*****************************************************************/

unsigned int Pulse [] = { 1500, 1500, 1500, 1500,
1500, 1500, 1500, 1500,
20000 };
Rich (BB code):
 /****************************************************************
*  K8LH Crazy-8 Hi-Rez Soft 8-channel (PORTB) Servo Algorithm  *
*                                                              *
*  1 usec steps, 600-2400 usecs, prescale 1,2,4 (4,8,16 MHz)   *
****************************************************************/
void interrupt()
{ static char n = 0;          //
static char servo = 1;      //

if (pir1.CCP1IF == 1)       // if "special event" interrupt
{ pir1.CCP1IF = 0;          // clear the interrupt flag
LATB = Servo;             // output new Servo pulse
CCPR1 = Pulse[n++];       // setup the CCP1 "match" value

Pulse[8] -= CCPR1;        // adjust end-of-cycle off time
Servo <<= 1;              // and prep for next channel

if (n == 9)               // if end of a 20-msec period
{ n = 0; Servo = 1;       // reset "n" and "Servo"
Pulse[8] = 20000;       // reset the 20.0-msec period
}
}
}
This example 8-channel servo "engine" sends each servo pulse out one at a time in succession while keeping track of the amount of time used so that the last (ninth) period can be used to fill out the balance of the 20 msec period. The tricky part about a soft servo driver is that you really should disable interrupts momentarily in your main program when you want to update any individual 16-bit servo pulse width value. Unfortunately, this could interfere with an interrupt and cause jitter during one servo pulse output.

Hope this helps.

Cheerful regards, Mike

I am using 16F877A . Hmm so it seems like interrupt is the only possible way?

#### MMcLaren

Joined Feb 14, 2010
846
I am using 16F877A . Hmm so it seems like interrupt is the only possible way?
No, you don't have to use interrupts. There are probably hundreds of other ways to accomplish the same task.