Servo Control Via Pulse Width Modulation

Recommended Posts

hello guys,

i am a newbie and i'm working on a c programming code to control servo motor via pwm. i'm using pic16f877A because it has 2 CCP pins that i can use to generate pwm with 20MHz osc. Since then i have a few problem to make the servo to turn clockwise, counterclockwise and stop in writing the program's code. The most problem i'm probably faced is how to set the timer value.

According to the PIC16F877A datasheet, it provide an equation which is

pwm period= [PR2+1]*4*Tosc*[TMR2 prescale value]

From the equation what is 'pwm period' , 'PR2' and 'TMR2 prescale value'?

I've found a c programming code on the internet where he use an equation

pwm period = (1/clock)*4*t2div*(period+1)

I think this equation might be same as the equation provided by the datasheet. From his calculations, he use 1 MHz osc to find the period where the period must be an 8-bit value due to the fact that the PIc use an 8-bit divisor which 0-255. Then he use pwm period=10ms , and t2div=16 since the t2div can have three possible values 1, 4, or 16). Finally he get the period=155 (in the range of 0-255).

Since i used osc=20MHz and pwm period=20 ms, i get the period= 6249 which is too big. Now i'm don't know what do. i've tried to search the solution through the internet but still not get it.

So, what is the possible solution that i should make this done? Which timer should i use, is it tmr0, tmr1 or tmr 2? and how to find the pwm value to stop, cw and ccw the servomotors?

The next problem is when i'm using source boost IDE (boostc) . How to define and set up the CCP pin and the timer? Should i have to use C2C-plus? And how to write the programming in c?

I hope that anyone can guide me and i'm really appreciate that..regards

systemchaos

Share on other sites

For actual details on using the PWM to control a motor you would probably be better off looking at some of the amateur robotics (Robot Wars!) type websites.

Pic manuals can be somewhat confusing due to the multifunction of the hardware.

The PWM controlled by Timer 2. The Timer2 module has an 8-bit period register, PR2. Timer2 increments from 00h until it matches PR2 and

then resets to 00h on the next increment cycle. When Timer2 matches PR2(in PWM mode) it sets the PWM out pins to 1. It also can generate an interrupt but you may ignore that if not required.

So timer 2 and PR2 set the PWM period, ie the time between two rising edges on the output of the pwm pins.

The prescalers of Timer2 combined with the system oscillator set the time for a one bit count of Timer2.

The duty cycle of the PWM is set by a 10 bit value 8 msbs into the CCPRxL register and 2 lsbs into CCPxCON bit 5 and 4. when Timer 2 matches the value in CCPRxL and the 2lsbs match the value of the 2 msbs in teh divider driving timer2, PWMx is cleared. Note that if thevalue in CCPRxL is greater than in PR2 then PWMx will remain high.

When designing a PWM system you should set the prescale of Timer 2 to give the largest possible value of PR2 to give a greater range of PWM control.

Setting up timer 2's period is straight forward, its base input is fosc/4 ie at 20MHz

it is being driven at 5MHz. The prescaler can further divide down the clock to timer2. Timer 2 also has a post scaler, this is used alter the trigger of timer2 interrupt.

So looking at pwm period= [PR2+1]*4*Tosc*[TMR2 prescale value]

Suppose we wanted a pwm period of 100us ie a frequencyof 10KHz

Tosc is 1/20,000,000. Times 4 to get the base rate is 4/20,000,000 ie 200ns.

Assume no prescaler divide our period by our base rate gives us 100us/ 200ns to give us PR2+1 which come to 500 so PR2 should be set at 500-1, 499. However PR2 is only 8 bits so we need a smaller value. lets apply prescaler of 16. 200ns*16 = 3.2us so 100us / 3.2 us gives 31.25, so load PR2 with 30. Mmh a bit coarse and 30+1 * 3.2 us give 99.2us or 10.08Khz. Lets apply prescale of 4, 200ns *4 = 800ns, 100us / 800 give 125 so load PR2 with 124, this gives exactly 10KHz or 100us and is the largest possible value of PR2 to get a 10KHz pwm with a 20MHz clock so gives the finest control of the pwm duty cycle.

Share on other sites

Well, partly it is necessary to realize that a 'servo' control signal is a 1 mSec pulse for full left, a 2 mSec pulse for full right, and a 1.5 mSec pulse for "center". This pulse MUST repeat every 20 mSec, to 'refresh' the servo positioning electronics.

Then, you need to realize that a "modified" servo used for servo based robot motors has had the "full-left" and "full-right" stops removed. This means, a pulse less than 1.5 mSec spins left, and a pulse more than 1.5 mSec spins right, and a 1.5 mSec pulse "stays still".

"True" PWM is typically used to control the speed of an H-Bridge interfaced electric motor. However, if you calculate the PWM signal properly (so that it repeats every 20 mSec) a 10% duty cycle is 2 mSec, and a 5% duty cycle is 1.0 mSec, so this will work.

Quite frankly, I got lost in your math above to figure out if that's what you've got. But that's what you should be trying to get.

Share on other sites
So looking at pwm period= [PR2+1]*4*Tosc*[TMR2 prescale value]

Suppose we wanted a pwm period of 100us ie a frequencyof 10KHz

Tosc is 1/20,000,000. Times 4 to get the base rate is 4/20,000,000 ie 200ns.

Assume no prescaler divide our period by our base rate gives us 100us/ 200ns to give us PR2+1 which come to 500 so PR2 should be set at 500-1, 499. However PR2 is only 8 bits so we need a smaller value. lets apply prescaler of 16. 200ns*16 = 3.2us so 100us / 3.2 us gives 31.25, so load PR2 with 30. Mmh a bit coarse and 30+1 * 3.2 us give 99.2us or 10.08Khz. Lets apply prescale of 4, 200ns *4 = 800ns, 100us / 800 give 125 so load PR2 with 124, this gives exactly 10KHz or 100us and is the largest possible value of PR2 to get a 10KHz pwm with a 20MHz clock so gives the finest control of the pwm duty cycle.

Can we use other value instead of 100us (1/10kHz) ? How about 20ms(1/50Hz), because the PWM signal will repeats for every 20ms, but probably the PR2 will be larger than 255. How it suppose to be? Please noticed that i'm using servomotor for locomotion purpose for my mobile robot. Like allanlane 5 just said, 'servo' control signal is a 1 mSec pulse for full left, a 2 mSec pulse for full right, and a 1.5 mSec pulse for "center". This pulse MUST repeat every 20 mSec, to 'refresh' the servo positioning electronics.

Edited by systemchaos
Share on other sites
Can we use other value instead of 100us (1/10kHz) ? How about 20ms(1/50Hz), because the PWM signal will repeats for every 20ms, but probably the PR2 will be larger than 255. How it suppose to be? Please noticed that i'm using servomotor for locomotion purpose for my mobile robot.  Like allanlane 5 just said, 'servo' control signal is a 1 mSec pulse for full left, a 2 mSec pulse for full right, and a 1.5 mSec pulse for "center". This pulse MUST repeat every 20 mSec, to 'refresh' the servo positioning electronics.

systemchaos,

I think you are using a continious rotation servo motor for your robot. As allanlane5 says , a pulse train of 1ms pulse width at a frequency of around 50Hz will cause your sero to rotate in clockwise & a 2ms pulse width at a repetative frequency of 50Hz will cause it to rotate in counter clockwise. & 1.5 ms pulse train will cause it to halt.

Now you can do this using the delay_ms(1), delay_ms(2) & delays_100us(15).

delay_100us(15) will give you a delay of 1500us which is equal to 1.5ms.

A simple code to rotate your servo in back & forth will look like this..

``` while(1)
{
clockwise:
for(x=0;x<30;x++)  // send a pulse train
{
portb.0=1;
delay_ms(1);          //1ms pulse width
portb.0=0;
delay_ms(18);       //at a periodicity of around 50hz
}
anticlockwise:
for(x=0;x<30;x++)
{
portb.0=1;
delay_ms(2);        //2ms pulse width
portb.0=0;
delay_ms(18);      //at a periodicity of around 50hz
}
}```

Try this code & check to see if your servo is working. If most of the other functions like object detection etc takes less then 1ms, you can interlace it with the drive command without changing the performance of the servo. It works fine from 47 hz to 62hz. If you still require to do more time consuming tasks then you can use the timer interrupts to do your timing requirements. There is no need to use the pwm.

Raghunathan.

Share on other sites

Are you sure your using a servo and not a stepper motor?

There are important differences between the two, the most important

being how many poles it has and how far apart they are.

If your pwm is timed wrong you will not get any movement as it will be

stuck inbetween the poles.

Share on other sites

hey ra68qi,

yes , i'm using servo motor that i have been modified for continuos rotation. The servo works well when i test it with servo tester (that using 556 Timer) and able to stop, turn cw and ccw. Now, what i want to do is to move the servos using PIC16F877A.

Anyway, thanx for ur codes and i will try it.. since i use two servo, how probably the codes are when i want to move servo 1 clockwise and servo 2 counterclockwise synchronously?

Share on other sites
hey ra68qi,

yes , i'm using servo motor that i have been modified for continuos rotation. The servo works well when i test it with servo tester (that using 556 Timer) and able to stop, turn cw and ccw. Now, what i want to do is to move the servos using PIC16F877A.

Anyway, thanx for ur codes and i will try it.. since i use two servo, how probably the codes are when i want to move servo 1 clockwise and servo 2 counterclockwise synchronously?

```while(1)
{
forward:
for(x=0;x<200;x++)
{
portb.0=1;          //right servo control
delay_ms(1);
portb.0=0;
portb.1=1;          //left servo control
delay_ms(2);
portb.1=0;
delay_ms(18);
}

turn_left:
for(x=0;x<50;x++)
{
portb.0=1;
delay_10us(150);
portb.0=0;
portb.1=1;
delay_ms(2);
portb.1=0;
delay_ms(18);
}

turn_right:
for(x=0;x<50;x++)
{
portb.0=1;
delay_ms(1);
portb.0=0;
portb.1=1;
delay_10us(150);
portb.1=0;
delay_ms(18);
}

reverse:
for(x=0;x<200;x++)
{
portb.0=1;
delay_ms(2);
portb.0=0;
portb.1=1;
delay_ms(1);
portb.1=0;
delay_ms(18);
}
}```

Note: no two servos are similar, say value 150 may or may not cause it to halt. You need to adjust it around this value say, 155 or 145 untill the sero stalls. Similarly the turning speed can be altered by changing the value of the drive from 150 to say 165 or 135. Just try with various value & see the path traced by it.

Raghunathan

Edited by ra68gi
Share on other sites

dear mr ra68qi,

again i need your help. I'm using Source Boost IDE (BoostC) and try your given code as well. But a problem occured. After i finish writing the codes and then i click on the Build Project icon to compile. From the output box it mentioned that the codes were fail to compile. here it is:

Building...

BoostC Optimizing C Compiler Version 6.60 (for PIC16 architecture)

http://www.sourceboost.com

Single user Lite License (Unregistered) for 0 node(s)

Limitations: PIC12,PIC16 max code size:2048 words, max RAM banks:2, Non commercial use only

servopic1.c

failure

D:\SourceBoost\Samples\C\BoostC\servopic1.c(100:6): error: unknown identifier 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(100:6): error: invalid operand 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(100:7): error: failed to generate expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(100:7): internal error: failed to generate 'for' expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(100:2): error: error in 'for' loop statement

D:\SourceBoost\Samples\C\BoostC\servopic1.c(108:7): error: unknown identifier 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(108:7): error: invalid operand 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(108:8): error: failed to generate expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(108:8): internal error: failed to generate 'for' expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(108:2): error: error in 'for' loop statement

D:\SourceBoost\Samples\C\BoostC\servopic1.c(116:7): error: unknown identifier 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(116:7): error: invalid operand 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(116:8): error: failed to generate expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(116:8): internal error: failed to generate 'for' expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(116:2): error: error in 'for' loop statement

"D:\SourceBoost\boostc.pic16.exe" servopic1.c -t PIC16F877

Exit code was 1.

Removing target: servopic1.obj

Failed to locate output file 'D:\SourceBoost\Samples\C\BoostC\servopic1.hex'

Done

Failed

It says that the error came from this code

for (x=0;x<30;x++)

so the x have to identify first.

do you know how to solve this?

I guess you already know that in boost c it will generate source code that use to setting I/O ports and other functions before we start writing the code. Does this source code need to edit?

Then, can u explain how this 'for (x=0;x<30;x++)' code function because i'm confused why u use many values such as 30, 50, 200.

That all..i'm very very appreciate ur help

regards,

systemchaos

Share on other sites
dear mr ra68qi,

again i need your help. I'm using Source Boost IDE (BoostC) and try your given code as well. But a problem occured. After i finish writing the codes and then i click on the Build Project icon to compile. From the output box it mentioned that the codes were fail to compile. here it is:

Building...

BoostC Optimizing C Compiler Version 6.60 (for PIC16 architecture)

http://www.sourceboost.com

Single user Lite License (Unregistered) for 0 node(s)

Limitations: PIC12,PIC16 max code size:2048 words, max RAM banks:2, Non commercial use only

servopic1.c

failure

D:\SourceBoost\Samples\C\BoostC\servopic1.c(100:6): error: unknown identifier 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(100:6): error: invalid operand 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(100:7): error: failed to generate expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(100:7): internal error: failed to generate 'for' expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(100:2): error: error in 'for' loop statement

D:\SourceBoost\Samples\C\BoostC\servopic1.c(108:7): error: unknown identifier 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(108:7): error: invalid operand 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(108:8): error: failed to generate expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(108:8): internal error: failed to generate 'for' expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(108:2): error: error in 'for' loop statement

D:\SourceBoost\Samples\C\BoostC\servopic1.c(116:7): error: unknown identifier 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(116:7): error: invalid operand 'x'

D:\SourceBoost\Samples\C\BoostC\servopic1.c(116:8): error: failed to generate expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(116:8): internal error: failed to generate 'for' expression

D:\SourceBoost\Samples\C\BoostC\servopic1.c(116:2): error: error in 'for' loop statement

"D:\SourceBoost\boostc.pic16.exe" servopic1.c -t PIC16F877

Exit code was 1.

Removing target: servopic1.obj

Failed to locate output file 'D:\SourceBoost\Samples\C\BoostC\servopic1.hex'

Done

Failed

It says that the error came from this code

for (x=0;x<30;x++)

so the x have to identify first.

do you know how to solve this?

I guess you already know that in boost c it will generate source code that use to setting I/O ports and other functions before we start writing the code. Does this source code need to edit?

Then, can u explain how this  'for (x=0;x<30;x++)'  code function because i'm confused why u use many values such as 30, 50, 200.

That all..i'm very very appreciate ur help

regards,

systemchaos

```void main()
{
trisb=0;    //initialize the port as output port.
portb=0;  //make all output portb pins low
char x;    //declare x as character to be used in for-loop.
while(1)
{

}
}```

Hope you have included the system file, the pragma directives & oscillator frequency.

Also see in my second code example i have increased the value of x from 30 to 200 in the forward & reverse loops & to 50 for the turns, so that your robot will traverse thro' a reasonable distance. The for-loop value x will decide the duration for which your servo will rotate in a particular direction. you can put any reasonable value you want. In a practical robot circuit you can have the for- loop

just for reverse and turning alone. The code would look more like ..

```while(1)
{
if(object detected)
{
reverse:
for(x=0;x<?;x++)
{
...;
...;
}
turn_right_or_left:
for(x=0;x<?;x++)
{
.....;
........;
}

}
else
{
forward:
...;
......;
}
}```

Raghunathan.

Edited by ra68gi

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

×   Pasted as rich text.   Paste as plain text instead

Only 75 emoji are allowed.