View previous topic :: View next topic |
Author |
Message |
palazzolojoe
Joined: 04 Feb 2012 Posts: 7
|
My Servo Function |
Posted: Wed Feb 15, 2012 1:59 pm |
|
|
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
|
PWM data |
Posted: Wed Feb 15, 2012 2:13 pm |
|
|
It's all in the data sheet. Prescaler, output period, post scaler, the lot.
Mike |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Wed Feb 15, 2012 2:30 pm |
|
|
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
|
|
Posted: Wed Feb 15, 2012 2:31 pm |
|
|
Quote: |
The program works flawlessly
|
Reasonable people might disagree with that assertion.
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
|
|
Posted: Wed Feb 15, 2012 2:45 pm |
|
|
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
|
|
Posted: Wed Feb 15, 2012 2:54 pm |
|
|
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
|
|
Posted: Wed Feb 15, 2012 3:18 pm |
|
|
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.
and a SINGLE iteration for a subsequent update. |
|
|
Mike Walne
Joined: 19 Feb 2004 Posts: 1785 Location: Boston Spa UK
|
Instruction manuals |
Posted: Wed Feb 15, 2012 3:47 pm |
|
|
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 |
|
|
|