CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

My Servo Function

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
palazzolojoe



Joined: 04 Feb 2012
Posts: 7

View user's profile Send private message

My Servo Function
PostPosted: Wed Feb 15, 2012 1:59 pm     Reply with quote

I have a HS-311 servo motor that I want to control with a pic16f877a.
The pulse range is from 1 to 2 ms with a period of 20ms. its operating angle is +/- 45 degrees (90 degrees in total). its speed is .19s/60degrees.

My function has two inputs the period and the degree you wish to turn to (0-90).

The input is coming froma self created vb terminal over RS232 communications.

The program works flawlessly but I wish to learn more about using an actual PWM with the CCP outputs. I was wondering if someone could give me advice on how to convert it.

Code:
#include <C:\Program Files\PICC\Devices\16F877A.h>
#include <STDLIB.H>
#fuses HS,NOWDT,NOPROTECT,PUT,NOBROWNOUT, NOLVP
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

//#define pin_sel pin_b1
void move_servo(int period, float dest);

char input[10];

void main(void)
{
   float test,value;
   char *ptr;
   puts("Welcome");
   while(1)
   {
      if (kbhit())
      {
         gets(input);
         value=strtod(input,&ptr);
         printf("%7.2f \r\n",value);
         move_servo(20,value);
      }   
   }
}

void move_servo(int period, float dest)
{
   long p, high_duty,low_duty;
   float temp;
   int j;
   p=(period*1000);   //turning period into ms
   temp=((float)(1.0*(dest/90.0))+1.0)*1000.0; //turning degree into pulse width
   high_duty=(temp+0.5); //rounding up
   low_duty=p-high_duty;
   for (j=0;j<30;j++) // used a for loop to count 30*20ms=0.6s, 0.6s is enough for the servo to travel over 189 degrees
   {
      output_high(pin_b1);
      delay_us(high_duty);
      output_low(pin_b1);
      delay_us(low_duty);
   }
}



I know that ccp uses timer 2 to set up the period
so the commands that i found that I probably need are:

setup_timer_2 (mode, period, postscale)
setup_ccp1(mode)
set_pwm1_duty(value)

correct me if im wrong, for the timer I believe that it references the oscillator with is 20mhz,
when your choosing mode if you select
T2_DIV_BY_1: you get 20Mhz/1
T2_DIV_BY_4 : you get 20Mhz/4
T2_DIV_BY_16: you get 20mhz/16

I am not sure if the above is correct or the reverse is true ex: T2_DIV_BY_4 : you get 4/20Mhz

for the period there is a 0 to 255 range, not sure what to put to make the period 20ms

i dont even know what I want to use the post scale for.

for setup_ccp1 I am guessing the obvious choice for mode is CCP_PWM

set_pwm1_duty(value) i am not sure what to put for value.

basically I want to have a period of 20ms
and a duty of 1ms/20ms to 2ms/20ms and am not sure how to manipulate the inputs of the required functions to get my desired output.

Any help is appreciated!
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

PWM data
PostPosted: Wed Feb 15, 2012 2:13 pm     Reply with quote

It's all in the data sheet. Prescaler, output period, post scaler, the lot.

Mike
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Feb 15, 2012 2:30 pm     Reply with quote

You can't do a 20ms period PWM cycle with the CCP in a 16F877A for
any oscillator frequency in the range of 4 MHz to 20 MHz.
For a 4 MHz oscillator, the lowest PWM frequency you can get is 244 Hz.
You will have to use software PWM for your servo.


Regarding hardware PWM in the PIC, this thread links on all aspects of it:
http://www.ccsinfo.com/forum/viewtopic.php?t=45968
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Wed Feb 15, 2012 2:31 pm     Reply with quote

Quote:

The program works flawlessly


Reasonable people might disagree with that assertion.
Very Happy Very Happy

For what is essentially controlling a SINGLE 1500usec nominal-zero-
RC hobby servo - you are close to reality - but I fear still quite a bit detached.

20 msec is actually a typical recurring REFRESH rate for many kinds of this
servo. But the way you calculate the LOW duty makes no sense at all -
as it is NOT required AFTER a move is complete.

But you NEVER refresh either do you ??

why all this FLOAT calc when you have beautiful TIMERS inside the PIC to
count off the microseconds or even 1/2 usecs for you ??

This approach would NEVER work right for more than one servo either
the very first command to MOVE is all you need.
And just allow enough time in MAIN to not step on the operation.

REPEATING the command and then quitting the quasi refresh when you exit the move function makes no sense either.

And while you MAY get away long term w/o refresh - if there is any torque on the servo output - it could drift away eventually - that ( at least in part) is what refresh is for ....

best regards
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Wed Feb 15, 2012 2:45 pm     Reply with quote

Code:

unsigned int16 motorcount[16];
//each active element is controlling ONE servo
// each value is  loaded with ((65536 minus how many 500 ns counts) -2) for overhead
// that we want for duration of output pulse to make   
// timer1 has a crystal and prescalar  set for input of 500 ns "tix"
// this can be done on interrupt - but here is a NON int version
// that sets one of the servo pulses with very high accuracy 
// pin B1 is multiplexed to up to 16 servos in the original design

          set_timer1(motorcount[c]);
          output_high(PIN_B1);
          tmr1IF=0;
          while (!Tmr1IF){};  // a wait loop based on timer
          output_low (pin_B1);
palazzolojoe



Joined: 04 Feb 2012
Posts: 7

View user's profile Send private message

PostPosted: Wed Feb 15, 2012 2:54 pm     Reply with quote

asmboy wrote:


20 msec is actually a typical recurring REFRESH rate for many kinds of this
servo. But the way you calculate the LOW duty makes no sense at all -
as it is NOT required AFTER a move is complete.


low_duty=total_duty-high_duty
low_duty=20000-1000
=19000
so pinb1 is high for approximately 1000us and low for 19000us making a 20ms period, repeating 30 times for approximately 600ms.
asmboy



Joined: 20 Nov 2007
Posts: 2128
Location: albany ny

View user's profile Send private message AIM Address

PostPosted: Wed Feb 15, 2012 3:18 pm     Reply with quote

Sorry but i think you are missing the point about what refresh is,
WHEN it is actually needed and why.

Once the servo is powered and initialized by the FIRST TWO move commands-
ONLY refresh is required there after -
and even the maximum refresh interval for most RC servos -
can be dictated by LOAD and other factors - not simply
the minimum delay you use now between refresh of the servo position.

Based on the way you do this - - then THIS loop is really all you need
for for a servo that was JUST cold powered on.

Code:

for (j=0;j<3;j++)

and a SINGLE iteration for a subsequent update.
Mike Walne



Joined: 19 Feb 2004
Posts: 1785
Location: Boston Spa UK

View user's profile Send private message

Instruction manuals
PostPosted: Wed Feb 15, 2012 3:47 pm     Reply with quote

There are restrictions on the parameters you can feed to delay_us() and delay_ms().

You don't appear to have read either the CCS manual or the PIC18F877A data sheet.

(1) Timer2 controls the PWM period.
(2) The crystal frequency is first divided by four to create the instruction cycle clock.
(3) The instruction cycle clock is then divided by the prescaler.
(4) The prescaler output is then divided by (period counter + 1).
(5) This is then the PWM period.
(6) The PWM period output is fed to the post scaler.
(7) When the post scaler overflows it sets an interrupt flag.

So with a 4MHz crystal you get a 1MHz instruction cycle i.e. 1us per.
With T2_DIV_BY_16 you get then 16us output from prescaler.
With max period of 255, prescaler output divides by 256, i.e. PWM period is 256 * 16us = 4.096 ms.
This equates to PCM programmer's 244Hz.

Like I said before, it's all in the manuals. Please read them FIRST.

If you want help from the experts, it's better not to argue with them unless you're absolutely sure of your ground.

Mike
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group