|
|
View previous topic :: View next topic |
Author |
Message |
gregoryg
Joined: 21 May 2009 Posts: 14
|
problem with digital servo motors |
Posted: Mon Jun 01, 2009 12:23 pm |
|
|
Hello,
I am trying to create 3 pwm outputs with pic 16f877A, compiler CCS 4.057
At start I worked with 3 regular servo motors which need 50 Hz PWM. I worked with this code and it worked perfectly.
Code: | #include <16F877A.h>
#fuses HS,NOWDT, NOLVP
#use delay(clock = 20000000)
#define PWM_PIN1 PIN_B1
#define PWM_PIN2 PIN_B2
#define PWM_PIN3 PIN_B4
#define LOOPCNT 400
static int16 width1,width2,width3;
static int16 loop = LOOPCNT;
static int16 pulse1,pulse2,pulse3;
//----------------------------------------------------
#INT_RTCC
void tick_interrupt(void);
void main()
{
width1=20; //10%
width2=20;
width3=20;
setup_counters(RTCC_INTERNAL | RTCC_8_BIT, RTCC_DIV_1); // (20MHz / (256*4*1)) = 20KHz
enable_interrupts(INT_RTCC); // equals 0.05ms per interrupt // 0.05ms*400 = 20ms // 0.05*400=20ms
enable_interrupts(GLOBAL);
//----------------------------------------------------------
width1=30; //15%
width2=30;
width3=30;
delay_ms(2000);
//----------------------------------------------------------
width1=40; // 20 %
width2=40;
width3=40;
delay_ms(2000);
width1=30;
width2=30;
width3=30;
}
//==================================
#INT_RTCC
void tick_interrupt(void)
{
static int16 loop = LOOPCNT;
static int16 pulse1,pulse2,pulse3;
if(--loop == 0 )
{
loop = LOOPCNT;
pulse1=width1;
pulse2=width2;
pulse3=width3;
}
if(pulse1)
{
output_high(PWM_PIN1);
pulse1--;
}
else
{
output_low(PWM_PIN1);
}
if(pulse2)
{
output_high(PWM_PIN2);
pulse2--;
}
else
{
output_low(PWM_PIN2);
}
if(pulse3)
{
output_high(PWM_PIN3);
pulse3--;
}
else
{
output_low(PWM_PIN3);
}
}
|
I changed my motors to more stronger digital motors which need 300 Hz PWM and 40/50/80% duty cycle. I modified my code like this:
Code: | #include <16F877A.h>
#fuses HS,NOWDT, NOLVP
#use delay(clock = 20000000)
#define PWM_PIN1 PIN_B1
#define PWM_PIN2 PIN_B2
#define PWM_PIN3 PIN_B4
#define LOOPCNT 60
static int16 width1,width2,width3;
static int16 loop = LOOPCNT;
static int16 pulse1,pulse2,pulse3;
//----------------------------------------------------
#INT_RTCC
void tick_interrupt(void);
void main()
{
width1=24; //40%
width2=24;
width3=24;
setup_counters(RTCC_INTERNAL | RTCC_8_BIT, RTCC_DIV_1); // (20MHz / (256*4*1)) = 20KHz
enable_interrupts(INT_RTCC); // equals 0.05ms per interrupt
enable_interrupts(GLOBAL); // 0.05*60=3ms
//---------------------------------------------------------
width1=30; //50%
width2=30;
width3=30;
delay_ms(2000);
//---------------------------------------------------------
width1=48; //80%
width2=48;
width3=48;
delay_ms(2000);
width1=30;
width2=30;
width3=30;
}
//=================================
#INT_RTCC
void tick_interrupt(void)
{
static int16 loop = LOOPCNT;
static int16 pulse1,pulse2,pulse3;
if(--loop == 0 )
{
loop = LOOPCNT;
pulse1=width1;
pulse2=width2;
pulse3=width3;
}
if(pulse1)
{
output_high(PWM_PIN1);
pulse1--;
}
else
{
output_low(PWM_PIN1);
}
if(pulse2)
{
output_high(PWM_PIN2);
pulse2--;
}
else
{
output_low(PWM_PIN2);
}
if(pulse3)
{
output_high(PWM_PIN3);
pulse3--;
}
else
{
output_low(PWM_PIN3);
}
}
|
But in the best case my motors are doing some pointless motion.
Is this code should work and I have maybe a problem in my circuit or do I need a different aproach to this?
Tnx for the help. |
|
|
bungee-
Joined: 27 Jun 2007 Posts: 206
|
|
Posted: Mon Jun 01, 2009 2:08 pm |
|
|
First try your servos with the old code. It should work anyway. Digital servo means that position could be updated faster. Other than this it should work same as analog servos.... even same signal.
In your code you just changed LOOPCNT and with this you changed the timings. And this timings are much off.
Digital servo also works on pulses between 1ms and 2ms long, only the pause between them is shorter. |
|
|
gregoryg
Joined: 21 May 2009 Posts: 14
|
|
Posted: Mon Jun 01, 2009 2:32 pm |
|
|
With the old code the digital servos didn't move a bit.
After I changed the LOOPCNT to 300Hz they started working, not as I intended but still..I also changed the duty cycle values because I saw it moves between 1.2 ms and 1.8 ms. Is it possible that TIMER0 can't handle 3 motors with this frequency? Maybe I need to change timer setups? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Jun 01, 2009 8:54 pm |
|
|
Post a link to the data sheet for the new motors.
Post a link to the schematic for your circuit. |
|
|
gregoryg
Joined: 21 May 2009 Posts: 14
|
|
|
FvM
Joined: 27 Aug 2008 Posts: 2337 Location: Germany
|
|
Posted: Tue Jun 02, 2009 2:22 am |
|
|
If you don't even have a datasheet, how can you know the required waveforms? |
|
|
Ttelmah Guest
|
|
Posted: Tue Jun 02, 2009 2:30 am |
|
|
Bungee is spot on.
Digital servos _accept_ a higher speed pulse train, but are designed to work happily on standard RC sets, with standard servo pulses. The HX12, is a bit 'famous' for being badly adjusted from the factory, and often being centred, rather to one side, when fed with normal pulses, but they should respond. The obvious questions are:
1) Have you actually tried the servos on a standard RC set?.
2) Are you dead sure you have got the connections right.
3) Are you sure your power supply can deliver the high current pulses these servos need, without sgnificant ripple?. There have been quite a few threads on standard RC groups with people having trouble with these (and similar) servos, put down to the currents involved. Scope tests, have shown pulses over 0.5A, with no load on the servos at all.
4) What voltage is the PIC running off?. 5.5v, is the maximum supply for the 16F877A, but these servos are designed for 6v operation. What is their input logic threshold?.
Generally, you should not have to change the pulse width times used at all to feed a digital servo. All you need do, is reduce the loop count. The pulse widths for given positions are 'constant', all these servos accept is a higher update rate.
Best Wishes |
|
|
gregoryg
Joined: 21 May 2009 Posts: 14
|
|
Posted: Tue Jun 02, 2009 4:39 am |
|
|
Thank you all for the help.
There was a problem with the servos, I took another threesome and they work just fine.
Ttelmah, does my LOOPCNT=60 will do the job or do I need to make another calculation? |
|
|
Ttelmah Guest
|
|
Posted: Tue Jun 02, 2009 9:10 am |
|
|
The problem is, without finding a data sheet for the HX12, we are in 'guessing' territory.
There are four 'standards' around:
The 'old' RC servo standard. 40 to 50Hz train, with 1 to 2mSec pulse widths.
High rate servos. Normally 250Hz, to 300Hz _max_. Same pulse widths. Gives faster responses. Many only accept 250Hz.
Narrow pulse servos. These accept a pulse train at the same rate as the high rate servos, but with a narrower pulse - there is a switch on modern sets designed to drive these, to enable the narrow pulse feature. The pulse is specified more accurately, and the duration falls to 50 to 200uSec. Gives higher precision, and the faster responses.
Now, both the latter servo types, are _required_ to also accept the original servo standard. Just being sold as a 'digital' servo, does not guarantee support for either the higher rate, or narrow pulse operation. You _will_ find that when running on 'standard' pulse trains, the digital servos are much more fussy about the pulse lengths, completely ignoring pulses outside a window, that is narrower than allowed on most traditional servos. If your pulse is outiside these narrower limits, the servo will just not move. Sounds like what you were seeing.
Then there is also another standard (only Futaba, and Hitec?), which allows the receiver to interrogate the actual position of the servo arm. Used on a couple of pitch gyros, and some sail winch systems. You send a really narrow pulse, then 'tri-state' the bus, and the servo responds with a pulse corresponding to the position.
The safest way to drive digital servos, is to keep to standard pulse widths, keep the range used low, and double the repetition rate.
Best Wishes |
|
|
gregoryg
Joined: 21 May 2009 Posts: 14
|
using microswitch |
Posted: Mon Jun 15, 2009 6:39 am |
|
|
Hello again,
I didn't want to open a new topic for another problem so I will post it here.
I am done writing my code for a sun tracking robot. It has 3 motors for moving and 2 motors for the solar panel.
This is the code :
Code: | #include <16F877A.h>
#DEVICE ADC=10
#fuses HS,NOWDT, NOLVP
#use delay(clock = 20000000)
#define PWM_PIN1 PIN_B1 //wheel 1
#define PWM_PIN2 PIN_B2 // wheel 2
#define PWM_PIN3 PIN_B4 //wheel 3
#define PWM_PIN4 PIN_B5 // motor y
#define PWM_PIN5 PIN_B6 // motor x
#define SWITCH_ PIN_C0 // micro switch
#define LOOPCNT 60 // digital servos
#define LOOPCNT2 390 // analog servo
#define XTAL_FREQUENCY 20000000
#define TIMER1_FREQUENCY (XTAL_FREQUENCY / 4) // 1 clock tick = 1 instr. cycle = crystal frequency / 4
static int32 Ticker;
static int8 Seconds=0;
static int16 width1,width2,width3,width4,width5;
static int16 loop = LOOPCNT;
static int16 loop2 = LOOPCNT2;
static int16 pulse1,pulse2,pulse3,pulse4,pulse5;
static int16 sample1,sample2,sample3,sample4;
static int8 i=0;
//----------------------------------------------------
#INT_RTCC
void tick_interrupt(void);
#int_TIMER1
void TIMER1_isr(void);
void reading(void);
//================================================
void Initialize_RTC(void)
{
Ticker = TIMER1_FREQUENCY; // initialize clock counter to number of clocks per second
setup_timer_1( T1_INTERNAL | T1_DIV_BY_1 ); // initialize 16-bit Timer1 to interrupt
// exactly every 65536 clock cycles
// (about 76 times per second)
enable_interrupts( INT_TIMER1 ); // Start RTC
}
//=======================================================
void reading (){
set_adc_channel(0); //set adc channel
delay_us(20); //a small delay is required after setting the channel //and before read
sample1=read_adc(); //starts the conversion and reads the result
//----------------------
set_adc_channel(1);
delay_us(20);
sample2=read_adc();
//------------------------
set_adc_channel(2);
delay_us(20);
sample3=read_adc();
//--------------------------
set_adc_channel(3);
delay_us(20);
sample4=read_adc();
//----------------------------
width4=30;
width5=30;
if (sample1>sample2) { // y axis
if ((sample1-sample2) > 205) // 1V difference between 2 sensors
width4=40; //rotate panel left
}
else {
if ((sample2-sample1)>=205)
width4=20; //rotate panel right
}
if (sample3>sample4) { // x axis
if ((sample3-sample4) > 205)
width5=20; // rotate panel left
}
else {
if ((sample4-sample3)>=205)
width5=40; //rotate panel right
}
}
//=====================================================
#int_TIMER1
void TIMER1_isr() // reading the sensors once in 1 second
{
Ticker -= 65536; // Decrement ticker by clocks per interrupt
if ( Ticker < 65536 ) // If second has expired
{ Ticker += TIMER1_FREQUENCY; // Increment ticker by clocks per second
seconds++; // Increment number of seconds
}
if (seconds==1)
{
seconds=0;
reading(); // read sensors
}
}
//==========================================================
void main()
{
setup_adc_ports(ALL_ANALOG); //enables the a/d module
setup_adc(ADC_CLOCK_INTERNAL ); //and sets the clock to internal adc clock
width1=30; // 1.5 ms duty cycle move the robot forward
width2=40; // 2 ms duty cycle
width3=20; // 1 ms duty cycle
width4=30; // panel is not moving
width5=30;
setup_counters(RTCC_INTERNAL | RTCC_8_BIT, RTCC_DIV_1); // (20MHz / (256*4*1)) = 20KHz
enable_interrupts(INT_RTCC); // equals 0.05ms per interrupt
Initialize_RTC(); // 0.05ms*390 = 19.5ms(almost 20ms) for analog servo
enable_interrupts(GLOBAL); // 0.05*60 ==3 ms for digital servos
delay_ms(5000);
//---------------------------------------------------------------
width1=20; //move the robot left
width2=20;
width3=20;
delay_ms(5000);
//----------------------------------------------------------------
width1=30; //move the robot right
width2=20;
width3=40;
delay_ms(5000);
width1=30; // stop the robot
width2=30;
width3=30;
delay_ms(2000);
}
//====================================
#INT_RTCC
void tick_interrupt(void) // generating pwm
{
if(--loop == 0 )
{
loop = LOOPCNT;
pulse1=width1;
pulse2=width2;
pulse3=width3;
pulse5=width5;
}
if(--loop2==0)
{
loop2=LOOPCNT2;
pulse4=width4;
}
if(pulse1)
{
output_high(PWM_PIN1);
pulse1--;
}
else
{
output_low(PWM_PIN1);
}
if(pulse2)
{
output_high(PWM_PIN2);
pulse2--;
}
else
{
output_low(PWM_PIN2);
}
if(pulse3)
{
output_high(PWM_PIN3);
pulse3--;
}
else
{
output_low(PWM_PIN3);
}
if(pulse4)
{
output_high(PWM_PIN4);
pulse4--;
}
else
{
output_low(PWM_PIN4);
}
if(pulse5)
{
output_high(PWM_PIN5);
pulse5--;
}
else
{
output_low(PWM_PIN5);
}
|
But, I don't want the panel to turn more than 360 degrees so I use a microswitch. When the microswitch is being pressed, I want to rotate the panel back to its previous position.
This is the code I made for the microswitch:
Code: | if(input(SWITCH_)){
width4=60-width4;
}
else{
}
|
The problem is that I am not sure where to insert this code so it won't slow down my program.
I also want to ignore every second push on the microswitch. I tried using counter but seems like it doesn't work.
Thanks in advance. |
|
|
|
|
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
|