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

problem with digital servo motors

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



Joined: 21 May 2009
Posts: 14

View user's profile Send private message

problem with digital servo motors
PostPosted: Mon Jun 01, 2009 12:23 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Jun 01, 2009 2:08 pm     Reply with quote

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. Wink
gregoryg



Joined: 21 May 2009
Posts: 14

View user's profile Send private message

PostPosted: Mon Jun 01, 2009 2:32 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Jun 01, 2009 8:54 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Jun 02, 2009 1:34 am     Reply with quote

I am using development board olimex PIC-P40-USB
http://www.olimex.com/dev/index.html

I couldnt find the motor datasheet but its HX12K Hextronik
FvM



Joined: 27 Aug 2008
Posts: 2337
Location: Germany

View user's profile Send private message

PostPosted: Tue Jun 02, 2009 2:22 am     Reply with quote

If you don't even have a datasheet, how can you know the required waveforms?
Ttelmah
Guest







PostPosted: Tue Jun 02, 2009 2:30 am     Reply with quote

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

View user's profile Send private message

PostPosted: Tue Jun 02, 2009 4:39 am     Reply with quote

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







PostPosted: Tue Jun 02, 2009 9:10 am     Reply with quote

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

View user's profile Send private message

using microswitch
PostPosted: Mon Jun 15, 2009 6:39 am     Reply with quote

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.
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