Arduino Forum

Forum 2005-2010 (read only) => Software => Syntax & Programs => Topic started by: axileon on Feb 26, 2008, 11:06 am

Title: 3 axis auto stabilized platform
Post by: axileon on Feb 26, 2008, 11:06 am
hey guys i'm doing a project on 3 axis auto stabilized camera platform.. i'm using the arduino as the prototyping platform.

so what i am using is IDG300 n ADXRS300 as gyros..... i need to integrate this together with the R/C transmitter.. so that I can control it using RC as well as it has auto stabilized capability as well at the same time...

can somebody share some insights on how the sequence of the program shld be like? i'm currently having problems....
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Feb 27, 2008, 03:58 am
hey..ok.. maybe i post more description about my blog...

you see, i am using the pulseIn function to read my R/C
Quote
void loop()
{
 
 xtime = pulseIn(2,HIGH);
 
 digitalWrite(6,HIGH);
 delayMicroseconds(xtime);
 digitalWrite(6,LOW);
 
 ytime = pulseIn(3,HIGH);
 
 digitalWrite(7,HIGH);
 delayMicroseconds(ytime);
 digitalWrite(7,LOW);
 
 ztime = pulseIn(4,HIGH);
 
 digitalWrite(8,HIGH);
 delayMicroseconds(ztime);
 digitalWrite(8,LOW);
 
}


and then i am reading in the pulse from the RC receiver and into my arduino and using it to pulse my servos...

it works fine with only this code.

but thenext step of my projet is to integrate it together with the sensors..... but i dont know how to configure the interrupt.... how do i pulse the servos and at the same time not affect my program code for obtaining the sensor data?
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 03, 2008, 05:57 am
hi can any one help? i am looking to pulse the servo simultaneously....

anyway to do this efficient? either using interrupt or a function?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 03, 2008, 11:46 am
Input capture on pin 8 is the most efficient and accurate way of measuring pulse widths at the same time your sketch is doing something else. There is only one Input Capture pin so you would need to measure the composite signal where each channel pulses in sequence followed by a relatively long (at least 4 millisecond) sync gap to indicate the start of the next sequence of channel pulses.

A code fragment for Input Capture was posted in this thread http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1201890734/12 It would need enhancing to recognize the sync delay and then store the widths of the channel pulses.

Assuming you are driving this from a radio control receiver then you could 'or' the channels together to get a composite signal to feed into the Arduino input capture pin.

Or you could use a receiver that has the composite signal somewhere on the PCB. I have been poking around with the Hitec  HF-05s receiver  (http://www.hitecrcd.com/receivers/show?name=Micro+05S)  recently and if you can do the precision soldering to the tiny surface mount pin, this should work. These receivers are small, reliable and inexpensive; versions seem to be available in every country where RC equipment is sold.

This receiver uses a Pic  16F676  (http://ww1.microchip.com/downloads/en/DeviceDoc/40039E.pdf
) microcontroller to  decode the pulses from the radio circuit and drive the servo pins.
One of the Pic's pins has the composite signal at digital levels suitable for driving an Arduino if you can solder a tiny wire to the correct place on the board to get at it.

Pin 11 (RA3) has the composite pulse train at digital levels. The radio circuit powers the Pic  through a 5v to 3.3 volt regulator so the output pulses are 0v low  and around 3v high.

FYI, Pin 13 (RA0) is the composite input signal from the radio circuit.

I don't have any more code than I have already posted so unless someone else has done the work and can publish it here, you would be pioneering this. I would be glad to provide advice and I am sure there are others with similar applications  would benefit if you published your results.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 03, 2008, 01:05 pm
hi.. actually i'm using the futaba PCM1024 receiver...and i'm using pitch, roll, yaw, 3 channels at one time...

i'm directly feeding it to digital pins, 2,3,4... but i'm having problems.... the whole loop takes 28 msecs and when i time them using digitalwrite separately, i get 14 msecs for each channel pulsein function.. but when i time them 3 channels together.... roll gets 14 ms... but yaw n pitch gets only 7 ms.. so i'm thinking whether is it becos of lack of processing time from the atmel chip..

sorry for asking.. but how do i actually connect it to the same pin 8? that means i short the 3 channels together?

i'm in a rush actually... have a couple of wks to finish everyhting.. plus integration with the gyro signals and everything...

this is the same of the code i'm using

Code: [Select]
void loop()
{
 
 digitalWrite(10,HIGH);
 
 xtime = pulseIn(2,HIGH);
 pin = servoRoll;
 pulseWidthx = xtime;
 update_servo(pin, pulseWidthx);
 
 //digitalWrite(11,HIGH);
 ytime = pulseIn(3,HIGH);
 //digitalWrite(11,LOW);
 pin = servoPitch;
 pulseWidthy = ytime;
 
 //digitalWrite(12,HIGH);
 ztime = pulseIn(4,HIGH);
 //digitalWrite(12,LOW);
 pin = servoYaw;
 pulseWidthz = ztime;
 update_servo(pin, pulseWidthz);
 
 digitalWrite(10,LOW);

}

void update_servo(int pin,long pulsewidth)
{
 long pulse = 0;
 int pin_num = 0;
 
 pin_num = pin;
 pulse = pulsewidth;

 if(millis()- lastPulse >= RefreshTime)
 {
     digitalWrite(pin_num, HIGH);  
     delayMicroseconds(pulse);
     digitalWrite(pin_num, LOW);
     lastPulse = millis();
 }

}
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 03, 2008, 01:53 pm
You could build a diode logic 'or' gate with yr three receiver outputs connected to diodes so the output will be high when any of the inputs is high. Here is some more info: http://en.wikipedia.org/wiki/Diode_logic
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 05, 2008, 02:55 am
if we were to use an OR logic gate.. how is the micro p going to differentiate within the 3 channels? i.e. which is which channel, so that it can pulse the servo?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 05, 2008, 03:42 am
The channels are pulsed in order and channel 1 is the first channel after the long sync delay.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 05, 2008, 04:01 am
actually i'm testing it out for 1 channel right now.. here's the code.... but i'm getting erratic pulses coming out from my pin 7...

Code: [Select]
volatile unsigned int Ticks;         // holds the pulse count as .5 us ticks
int icpPin = 8;                       // this interrupt handler must use pin 8
int servoRoll = 7;
long pulsewidthX = 0;
int RefreshTime = 20;
long lastPulse = 0;

ISR(TIMER1_CAPT_vect){
  if( bit_is_set(TCCR1B ,ICES1)){       // was rising edge detected ?  
      TCNT1 = 0;                        // reset the counter      
  }
  else {                                // falling edge was detected  
       Ticks = ICR1;
  }      
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge    
}

void setup()                    // run once, when the sketch starts
{
 Serial.begin(9600);  
 pinMode(icpPin,INPUT);
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 Ticks = 0;             // default value indicating no pulse detected
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}

int getTick() {
 int akaTick;       // holds a copy of the tick count so we can return it after re-enabling interrupts
 cli();             //disable interrupts
 akaTick = Ticks;
 sei();             // enable interrupts
 return akaTick;  
}

void loop()                     // run over and over again
{
 static int prevTick = 0;

 if( getTick()  != prevTick) {
     prevTick = getTick();
       pulsewidthX = prevTick;
       update_servo(servoRoll,pulsewidthX);
             
//Serial.println( prevTick/2);     // print the tick value only when it changes  value of tick/2 to get in microseconds  

 }
}

void update_servo(int pin,long pulsewidth)
{
 long pulse = 0;
 int pin_num = 0;
 
 pin_num = pin;
 pulse = pulsewidth;

 if(millis()- lastPulse >= RefreshTime)
 {
     digitalWrite(pin_num, HIGH);  
     delayMicroseconds(pulse);
     digitalWrite(pin_num, LOW);
     lastPulse = millis();
 }

}


the problem is .. sometimes there is a pulse.. sometimes there isnt..
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 05, 2008, 03:53 pm
There are a few issues with that sketch. Its not waiting for the sync pulse so the routine has no idea what channel it is measuring. Also the code fragment it is based on was for a sony remote control that synchronises with a pulse that is longer then the data pulses, your receiver synchronises using a long space between the data pulses

I have modified the original sketch I posted to accommodate these differences and added some code to provide debug output on the channel pulse widths.

I have not run this code and its likely to need some fixing to get it to work so make sure the debug output gives the results you expect (pulses between 1000us and 2000us that vary with the transmitter controls) before you risk connecting any servos to this.

The most likely problem you will hit is that the detection on the rising and falling edges may need to be reversed. Its easy to do but we need to see what the debug looks like with your receiver to see if that change is necessary.

Looking forward to seeing the debug output.

Code: [Select]
#define icpPin            8         // this interrupt handler must use pin 8
#define TICKS_PER_uS      2          // number of timer ticks per microsecond
#define MAX_CHANNELS    8         // maximum number of channels we can store  
#define SYNC_GAP_LEN      (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)
volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)
volatile uint8_t State;         // this will be one of the following states:
#define NOT_SYNCHED_state  0    // the system is not synched so the data is random
#define ACQUIRING_state  1      // one sync pulse detected but not all channels have been received
#define READY_state     2       // synched and all channel data is valid

                 
ISR(TIMER1_CAPT_vect){
  if(! bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
      TCNT1 = 0;               // reset the counter      
      if(Channel <= MAX_CHANNELS) {
          Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
       }      
  }
  else {                          // rising  edge was detected  
       TCNT1 = 0;               // reset the counter      
       if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
           Channel = 1;       // if so, reset the channel counter to 1      
             if(State == NOT_SYNCHED_state)
                 State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
             else if( State == ACQUIRING_state)    
                  State = READY_state;           // this is the second sync so flag that channel data is valid
       }    
  }    
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge    
}

void setup()                    // run once, when the sketch starts
{
 Serial.begin(9600);  
 pinMode(icpPin,INPUT);
 Channel = 1;            
 State = NOT_SYNCHED_state;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}

int GetChannelPulseWidth( uint8_t channel) {
 // this is the access function for channel data
 int result;  
 if( (State == READY_state)  && (channel > 0) && (channel <=  MAX_CHANNELS)  ) {
    cli();             //disable interrupts
    result =  Pulses[channel] ;
    sei();             // enable interrupts
 }
 else
    result = 0;        // return 0 if no valid pulse is available  

 return result;

}

void loop()                     // run over and over again
{
int pulsewidth;

  // print the decoder state
  if(State == NOT_SYNCHED_state)
      Serial.println("The decoder has not detected a synch pulse ");  
  else if ( State == ACQUIRING_state)
      Serial.println("The decoder has detected one synch pulse and has started filling channel data");  
  else if( State == READY_state)
    Serial.println("The decoder is synched and the channel data is valid");  
  else
    Serial.println("Unknown decoder state, this should never happen!");
 

 // now print the channel pulse widths
 // they should be 0 if the state is not ready
 for ( int i =1; i <=4; i++ ){ // print the status of the first four channels
     Serial.print("Channel ");
     Serial.print(i);
     Serial.print(" has width ");
     pulsewidth = GetChannelPulseWidth(i);
     Serial.println(pulsewidth);
 }
 delay(100); // update 10 times a second        
}

Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 05, 2008, 05:03 pm
hi thanks. i will try that later at school...

anyway.. i didnt use a diode. instead i used a 8 to 1 mutiplexer to control the pulse coming in...

had problems intregrating it with the servos too.. hope your code will help. i will debug it later..

and post the results. thanks a lot!
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 05, 2008, 05:27 pm
Try to resist connecting the servos till the code is debugged, there will be some things that need fixing and it would be a shame to burn out your servos.  

Edit: I wonder how you will gate your multiplexer? I think you will be much better off with an or gate.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 05, 2008, 05:50 pm
ya sure. i always test read the pulse on the scope before connecting.  :)

actually i still dont really understand something. if you OR all the pulses, how will the microP be able to pick up the signal? since all 3 falls under 1 signal.

i read the 3 channels on the oscilloscope. the 1st 2 actually overlaps.. the 3rd pulse is somewhere in btween the other 2.

regarding the multiplexer. i had to control the channel (OUT) by pulsing 3 pins, a combination of either HIs or LOWs to get the pins i want. so basically i did that 3 times in a loop to get the 3 pulses.

is that the correct way to do?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 05, 2008, 05:56 pm
Can you use that receiver in PPM mode?

If your receiver can't output the channels sequentially then you will have difficulty decoding the channels. I think you will have a lot less aggravation if you can find a cheap PPM receiver to use for your project.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 05, 2008, 06:05 pm
I am not really sure. I will ask someone tomo.

thanks for your help. will post the results when i go to school later in the morning.
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 05, 2008, 06:07 pm
There is some info on this site http://www.mp.ttu.ee/risto/rc/electronics/radio/signal.htm about receiver pulse output  that has what looks like the wavform for a Futaba PCM1024 receiver that appears to be sequential. I believe Futaba have updated the design in this model over time without changing the model number so yours may be different.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 06, 2008, 04:45 am
actually i read that website before... the pulse that I am getting is actually 1.5 ms pulse with regular intervals.  i read it on the scope.. it doesnt look sequential.

i tried your code... the output i get is kinda not really correct.

somehow only the 1st channel is read correctly but not all the time.
and channel 2/3 is just 1 or 2 numbers at time..

maybe using the multiplexer would be a better approach?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 06, 2008, 05:47 am
How about posting the debug output so I can see what the software thinks is happening. Do a run with only one channel connected and another run with three channels connected if you can.

That code expects the pulses to be sequential so if they are not then you could use the multiplexer but that would mean that your data will be updated less frequently, every 3 frames (60ms) for 3 channels but that may be ok in your application.



Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 06, 2008, 06:16 am
hi

the 3 channels debug is available at this url http://axileon.com/blog/debug_3channels.jpg

the 1 channel is here. http://axileon.com/blog/debug_1channel.jpg

for the 1 channel.. i noticed something strange.

the 1st channel will output the correct pulsewidth if i turn off the transmitter. but when i turn it on,it gets the output similar to the image above. this is so if I pass the signal thru the diode...

but If i drive the whole thing directly by the signal from the receiver. everything is fine.

i also tried using the mutiplexer method... for 3 channels...

however i only got readings for the channel 1. the other 2 channels are zero. for 3 channels.
i.e channel 1 has width 1450,(this is actually the value for channel 3)
channel 2 has width 0.
channel 3 has width 0.

Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 06, 2008, 12:16 pm
It does look like the channel pulses are not sequential so you will probably need to use the multiplexer with your receiver. The challenge is to get the switching of the multiplexer synchronized so that you don't miss part of a pulse. To do this you need to ensure that you only switch the multiplexer to the next channel after that channels pulse goes low (that's the easy part) and you need to wait before taking the next pulse width measurement until you are sure that channels pulse has not already gone high.

One way to do this is to take two readings for each channel before moving on to the next one. It won't matter if the first measurment was in error because it will be overwritten by the second. The disadvantage of this technique is that it takes twice as long for changing pulse widths to be recognized but that may not matter in your application.

If you use this method then you can ignore the existing code to detect the sync pulse and replace that with a counter that increments the channel counter after the second trailing edge is detected.

If that isn't clear then I will try to post some pseudo code.

Or, if it is ok to miss some pulses then perhaps you should reconsider using pulsein with the multiplexer. The logic would be similar to the above:  Wait for the trailing edge of the previous pulse, then store the next pulse in the channel array, increment the channel and repeat.
The advantage of the interrupt version is that it will never miss a pulse edge no matter what your code is doing but it will only work if the pulses are sequential. As yours are not than perhaps the simpler PulseIn route is better for your app.

Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 06, 2008, 06:11 pm
hm.. actually i tried the pulseIn with the multiplexer today.... the code got stucked at the 1st pulseIn
i really have no idea why... i'm very puzzled on that..

sorry to bother you. but how do you actually change your code to read 3 channels, 1 after another? i have been looking at your code but i still dont understand the ISR part..

maybe.. if possible can you explain it further to me? so that I can make any further changes I want if possible..

for your alternative solution, you're saying we call pulseIn 3 times in the loop? and then go to do something else then loop again?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 06, 2008, 07:44 pm
Here is a modification of the interrupt sketch for the multiplexer logic (I have indicated where you need to add the code that will write the two pins gating the multiplexer to select the current channel.

A difficulty with the output of your receiver is that there is no way of knowing when the next channels pulse will start. We overcome this by waiting until we detect the end of the previous frames pulse before capturing the pulse width and moving on to the next channel.

This is implemented using a 'Ready' flag that is toggled on an off on successive pulses and ensuring that the measurement only happens every other trailing edge. Another flag is used to indicate that enough pulses have been detected to ensure that all channel data is correct.

here is the logic:

In setup,  channel is set to 1 and Ready flag and DataAvailable flags to set to false

When the first falling edge is detected Ready will be toggled so it will be set to true

When the rising edge is detected and Ready is true then the measurement proper starts

When a falling edge is detected and the Ready flag is true then the measurement ends,
the channel is incremented. If the channel count after incrementing is greater than the number of channels then the DataAvailable flag is set true and pulse data can be accessed
The ready flag will be reset back to false because it is toggled on every trailing edge.


I hope that is clear enough for you to think about a PulseIn version if you prefer that route.

Code: [Select]

#define icpPin            8   // this interrupt handler must use pin 8
#define TICKS_PER_uS      2    // number of timer ticks per microsecond
#define MAX_CHANNELS    3         // maximum number of channels we want  
volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)

boolean DataAvailable; // set to true when we have received data for all channels
boolean Ready;         // true when we are ready to detect the leading edge of a channel

// here is the logic of how this code works :
// to start, channel is set to 1 and ready to false and the DataAvailable flag is false
// when a negative edge is detected ready is set to true
// when a positive edge is detected and ready is true then the measurement starts
// when a negative edge is detected and the ready flag is true then the measurement ends, the ready flag is reset back to false
//  and the channel is incremented. If the channel count after incrementing is greater than the number of channels then
//  the DataAvailable flag is set true and pulse data can be accessed

                 
ISR(TIMER1_CAPT_vect){
  if( !bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
        if(Ready ) {
             Pulses[Channel] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
             if(++Channel > MAX_CHANNELS)
                  Channel = 1;       //reset the channel counter to 1      
             // Add code here to gate the multiplexer to the current channel          
        }          
        Ready = !Ready;  //toggle the ready flag
  }
  else {                       // rising  edge was detected  
       TCNT1 = 0;               // reset the counter      
                         
  }    
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge    
}

void setup()                    // run once, when the sketch starts
{
 Serial.begin(9600);  
 pinMode(icpPin,INPUT);
 Channel = 1;            
 Ready = false;
 DataAvailable = false;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}

int GetChannelPulseWidth( uint8_t channel) {
 // this is the access function for channel data
 int result;  
 if( DataAvailable  && (channel > 0) && (channel <=  MAX_CHANNELS)  ) {
    cli();             //disable interrupts
    result =  Pulses[channel] ;
    sei();             // enable interrupts
 }
 else
    result = 0;        // return 0 if no valid pulse is available  

 return result;

}

void loop()                     // run over and over again
{
int pulsewidth;

  // print the decoder state
  if(DataAvailable == false)
      Serial.println("The decoder has not detected all channels ");  
  else  
       Serial.println("The the channel data is valid");  


 // now print the channel pulse widths
 // they should be 0 if the state is not ready
 for ( int i =1; i <=4; i++ ){ // print the status of the first four channels
     Serial.print("Channel ");
     Serial.print(i);
     Serial.print(" has width ");
     pulsewidth = GetChannelPulseWidth(i);
     Serial.println(pulsewidth);
 }
 delay(100); // update 10 times a second        
}
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 08, 2008, 06:17 am
sorry to trouble you again.
i tried the code with my code inserted the place where you told me to ....

however there was no output.... the debug keeps giving me the decoder has detected no channels and channels has width zero.


     
Code: [Select]
// mutiplexercode
                  if(Channel == 1)
                 
                   digitalWrite(g,LOW);
                   digitalWrite(a,LOW);
                   digitalWrite(b,LOW);
                   digitalWrite(c,LOW);
               
                 else if(Channel == 2)
               {
                    digitalWrite(g,LOW);
                   digitalWrite(a,HIGH);
                   digitalWrite(b,LOW);
                   digitalWrite(c,LOW);
               }  
                 else if(Channel == 3)
               {
                  digitalWrite(g,LOW);
                   digitalWrite(a,LOW);
                   digitalWrite(b,HIGH);
                   digitalWrite(c,LOW);
               }  


i tested the signal(to digital pin 8) on the scope. the signal looks fine but when i connect it to the arduino... nothing happens. do you know what's the problem?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 08, 2008, 06:38 am
You could test it with just one receiver channel connected directly to pin 8 to verify that the interrupt logic works. If you get output pulse data between 1 and 2 ms and it follows the transmitter control then the problem may on the multiplexer side.

Have you tried a  sketch without the interrupt code,  just your multiplexer code and a simple digitlaRead on pin 8.  You could connect resistors to provide high or low signals to the inputs to check that the multiplexer is working as you expect.

One comment on the multiplexer code, you should gate the multiplexer after the digitalWrites to the select  inputs, otherwise you will get spurious pulses that will confuse the logic.
If g is the gate and abc are the inputs:
 digitalWrite(a,LOW);
 digitalWrite(b,LOW);
 digitalWrite(c,LOW);
 digitalWrite(g,LOW); // enable the gate after selecting inputs

I hope that helps.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 08, 2008, 07:05 am
hi.. i dont think its the interrupt problem

i pulse the pin 8 directly with the receiver pulse... nothing is detected...

so i think the problem is with the interrupt code...
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 08, 2008, 08:45 am
Try it with the following modification to the interrupt handler:

if(++Channel > MAX_CHANNELS)
{  // add the braces
        Channel = 1;       //reset the channel counter to 1      
             // Add code here to gate the multiplexer to the current channel          
         DataAvailable = true;  // this line is needed to indicate that the channel data is valid!!!!
}

edit: posted fix to set DataAvailable to true
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 08, 2008, 08:50 am
yup. i'm running the latest code that you have given me. exactly the same as in post 21.

i think maybe there's problems with interrupt code
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 08, 2008, 08:52 am
see my edited post above
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 08, 2008, 09:15 am
hey its working now!...

but i do have a question. right now i'm having the correct output with only 1 channel. but the thing is when i connect 2/3  channels, they all output the same thing as the 1st channel..

i do think the problem lies with the multiplexer. actually my question is, does the variable channel means the channel number?so its ranges 1 to 3 right?

i tested the if(channel == 1) code... its not working.... i cannot configure the multiplexer according to the channel number.
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 08, 2008, 09:25 am
Good to hear its starting to work  :)

The Channel variable is a channel number, but because there is nothing in this verison to indicate which channel is channel 1 then it will indicate channel 1 as the first channel it happens to detect. But the three channels should have different values if you are sending different values on your transmitter.

edit: I just reread what I wrote above and its not correct. The multiplexer will select the correct channel if its gated correctly.

Could you post the sketch that includes the multiplexer code.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 08, 2008, 09:29 am
alright. here it is.

Code: [Select]
 if(++Channel > MAX_CHANNELS)
           {
                   Channel = 1;       //reset the channel counter to 1      
               
                   if(Channel == 1)
                 {
                   digitalWrite(a,LOW);
                   digitalWrite(b,LOW);
                   digitalWrite(c,LOW);
                   digitalWrite(g,LOW);
                 }
                    else if(Channel == 2)
               {
                   digitalWrite(a,HIGH);
                   digitalWrite(b,LOW);
                   digitalWrite(c,LOW);
                   digitalWrite(g,LOW);
               }  
                  else if(Channel == 3)
                 {
                 
                   digitalWrite(a,LOW);
                   digitalWrite(b,HIGH);
                   digitalWrite(c,LOW);
                   digitalWrite(g,LOW);
                 }
                   DataAvailable = true;  // this line is needed to indicate that the channel data is valid!!!!
               }
               


the debug screen actually prints all 3 channels with the same value(channel 1)....

so, how do i actually configure the code to print all the 3 different channel values then?
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 08, 2008, 09:56 am
hi i changed my code. i managed to get it working right now... just that there appear to be another problem.

i have connected the receiver in sequence 0,1,2

however the debug screen prints
chn 1 as receiver chn 2
chn 2 as receiver chn 0
chn 3 as receiver chn 1

Code: [Select]
Pulses[Channel] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
              if(Channel == 1)
                 {
                   digitalWrite(a,LOW);
                   digitalWrite(b,LOW);
                   digitalWrite(c,LOW);
                   digitalWrite(g,LOW);
                 }
                    else if(Channel == 2)
               {
                   digitalWrite(a,HIGH);
                   digitalWrite(b,LOW);
                   digitalWrite(c,LOW);
                   digitalWrite(g,LOW);
               }  
                  else if(Channel == 3)
                 {
                 
                   digitalWrite(a,LOW);
                   digitalWrite(b,HIGH);
                   digitalWrite(c,LOW);
                   digitalWrite(g,LOW);
                 }
                 if(++Channel > MAX_CHANNELS)
           {
                   Channel = 1;       //reset the channel counter to 1      
               
               
                   DataAvailable = true;  // this line is needed to indicate that the channel data is valid!!!!
               }
               


also.. i'm using the serial servo program to update the servos.. but the gap in in between pulses is too long... do you  know of a better way to pulse the servos accurately?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 08, 2008, 10:09 am
I composed the following while you where posting the above. Have a read of this while I read your post:

modify the code as below so that the multiplexer channel selection is outside the  brackets. I have moved it to a separate function of convenience. This code also turns the output of the multiplexer off when selecting.

Code: [Select]
if(++Channel > MAX_CHANNELS)  
{  
    Channel = 1;       //reset the channel counter to 1                  
    DataAvailable = true;  // this line is needed to indicate that the channel data is valid!!!!
}
SelectChannel( Channel)



void SelectChannel( int channel){
   digitalWrite(g,HIGH);  // turn the output off while twiddling select bits
   if(Channel == 1)
   {
      digitalWrite(a,LOW);
      digitalWrite(b,LOW);            
   }
   else if(Channel == 2)
   {
      digitalWrite(a,HIGH);
      digitalWrite(b,LOW);
   }
    else if(Channel == 3)
    {
      digitalWrite(a,LOW);
      digitalWrite(b,HIGH);
    }
    digitalWrite(c,LOW);   // c is always low

    digitalWrite(g,LOW);  // turn output on    
}


edit: looks like you made the fix to move the Channel Selection outside the brackts. But you should add the code to gate the multiplexer off when writing the selection pins.

If you post your servo code will see if I can help. But in principle you want to update the servo every 20 milliseconds using whatever data is in the channel array after the DataAvailable flag is set to true  

Note that the channel array is updated only every 40 ms or so but the servos should be pulsed every 20ms.
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 08, 2008, 10:29 am
Here is a suggestion that I hope helps:

Code: [Select]


void PulseServo( int channel, int pulsewidth ){

//   your code here to pulse the servo for the given channel with the given pulse width
// you should check that pulsewidth is within the valid range for your servos

}

void loop()                     // run over and over again
{

  if(DataAvailable == false)
      Serial.println("The decoder has not detected all channels ");  
  else{

      PulseServo(1,GetChannelPulseWidth(1);
      delay(6);
      PulseServo(2,GetChannelPulseWidth(2);
      delay(6);
      PulseServo(3,GetChannelPulseWidth(3);
      delay(6);
  }  
}
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 10, 2008, 03:15 am
hi... thanks for your help so far.. i managed to get it working already.  :)

Code: [Select]
void PulseServo(int channel,int pulsewidth)
{
 //   your code here to pulse the servo for the given channel with the given pulse width
 // you should check that pulsewidth is within the valid range for your servos

 int pulse = 0;
 int pin_num = 0;
 
 pin_num = channel + 1;
 pulse = pulsewidth;

 if ((pulse <= 2200) && (pulse >= 600))
 {
   digitalWrite(pin_num, HIGH);  
   delayMicroseconds(pulse);
   digitalWrite(pin_num, LOW);
 }

}


however... the servo is a litter jerky..... i check the pulse on the scope.... when everything is stationary... the PWM seems a bit unstable.. the pulses keeps moving left right right left... though the time between pulses is still ok..

do you have any idea what is causing the pulses to be moving?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 10, 2008, 08:40 am
You could send the pulse width to the serial port so you can inspect what is being sent to the servo.

Something like this would do the trick

Code: [Select]


void PulseServo(int channel,int pulsewidth)
{
 // note that pulse variable is not needed and has been removed

 int pin_num = 0;
 pin_num = channel + 1;

 if ((PulseWidth <= 2200) && (PulseWidth >= 600))   // 600 is a little low, try it with 800
 {
   digitalWrite(pin_num, HIGH);  
   delayMicroseconds(PulseWidth);
   digitalWrite(pin_num, LOW);
 }
 else {
     ; you may want to add code here to blink a LED if Pulse WIdth is out of range
 }
     
 Serial.print("chan ");
 Serial.print(Channel);
 Serial.print("=");
 Serial.print(PulseWidth);
 Serial.print(", ");

}
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 10, 2008, 09:39 am
hey. thanks.. actually i'm using timer 0 and timer 2 for do a PWM for the servos.... timer 1 will be used for the ICP.....

the code is as follow...

Code: [Select]
#include <avr/io.h>
#include <avr/interrupt.h>

int servoRoll = 6;
int servoPitch = 5;
int servoYaw = 11;

void setup()
{

 Serial.begin(9600);

 pinMode(servoRoll, OUTPUT);
 pinMode(servoPitch, OUTPUT);
 pinMode(servoYaw, OUTPUT);

/* setup for timer 0 */    
 TCCR0A = _BV(COM0A1) |  _BV(COM0B1) | _BV(WGM01) | _BV(WGM00);  // clear both A n B on compare match, set fast PWM mode.
 TCCR0B = _BV(CS02) | _BV(CS00); // set prescaler of 1024 for 8 bit timer - 0 to 255.  
 
 TIMSK0 = _BV(OCIE0B) | _BV(OCIE0A) | _BV(TOIE0) ; // enables the interrupt for the
 
 OCR0A = OCR0B = 0x18; // 0x18 = 24 = 1.5 ms

/* end of setup for timer 0 */

/* setup for timer 2 */  
 
 TCCR2A = _BV(COM2A1) | _BV(WGM21) | _BV(WGM20);  // clear both A on compare match, set fast PWM mode.
 TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20); // set prescaler of 1024 for 8 bit timer - 0 to 255.  
 
 TIMSK2 = _BV(OCIE2A) | _BV(TOIE2) ; // enables the interrupt for the

 OCR2A = 0x18; // 0x18 = 24 = 1.5 ms

/* end of setup for timer 2 */
 
 sei();
 
}

void loop()
{
 cli();
 OCR2A= 16;
 sei();
 delay(17);
   cli();
 OCR2A = 32;
 sei();
 delay(17);

}


i discovered that i can only use 1 of the timers for this purpose.. not two. if i use two and one of them is correct, the other has something wrong with the pulse.

actually we cant keep changing the OCRXX values right? but my counter will overflow after 16 ms right? that means I should be able to vary the pulse for the motor right?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 10, 2008, 02:19 pm
The resolution is pretty low using the 8 bit counters and that may explain why the servos are not smooth.

Any reason you can't use a simple delay instead of the timers for driving the servos similar to reply #33 above? You have at least 14 milliseconds to do any calculations between pulsing the servos and because your data is changing every two frames you can spread this over 28 milliseconds if you need to.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 10, 2008, 02:34 pm
the waveform of the servo is too inconsistent if i use other methods rather than interrupt to generate them...

but i thought timers are supposed to be more accurate than if we use digitalwrite hi's and low's?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 10, 2008, 02:51 pm
If an 8 bit timer count of 24 is 1.5ms then each step is over 60 microseconds. It should be within a microsecond if you use delayMicrosecond. Have you tried it using the delay instead of the timer?
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 10, 2008, 02:59 pm
i am aware of the low resolution if i use an 8 bit counter.. but my professor advises me to use interrupt for the timing of the servos....

i will try the delay thing again tomo... i tried it lasttime... the gap between pulses is too long... and its also quite jerky... maybe its the cycling of the timer..

if i throw in the code for the gyros.. then most probably everything will be haywire again...
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 10, 2008, 03:28 pm
If this is for a school project then keeping your professor happy is an important requirement  ;)

My suggestion is that this is a good time to try and determine how much processing time you need per frame. If this time is more than around 15 milliseconds but less than around 30ms then consider if it is acceptable to use two frames for your calculations (the channel inputs are only updated every other frame because of the way we have implemented the multiplexer). If you need a lot more than 15 milliseconds and the data does need to be refreshed every frame (20 ms),  or you need more than 30ms to do your calculations  using two frames,  then you are getting close to exceeding the Arduino's processing capability even if you could run the servos and input capture all on interrupts.


for example, the following pulses the servos every 20ms and give around 30ms of calculation time every 50ms (two frames):


start of frame n
 pulse three servos in sequence (4-6 ms)
 start calculating new pulse width (15ms)
end of frame n

start of frame n+1
 pulse three servos in sequence using previous frame data (4-6 ms)
 finish calculating new pulse width (15ms)
end of frame n+1

repeat...
Title: Re: 3 axis auto stabilized platform
Post by: bloke on Mar 12, 2008, 12:05 am
Hi, Sorry to join this thread so late in the piece. I am slowly making progress on a similar project but to stabilise a quad copter. I have gathered snippets of code and ideas from all sorts of places, most notably a UAVP project in Europe. It is worth a google of UAVP and a look at their site if you have not already done so. They make a lot of their code available and there are lots of clues there on how to decode the stream and in fact to deal with sensors you mentioned above. I am now at the stage where I have the raw RC 'stick' values captured and have mastered comms with the sensors. When you get to the point of dealing with PID code etc, would love to compare notes.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 12, 2008, 02:51 am
hi.. i tried the usual way of digitally writing the servos... i found that the problem is when the program is looping... sometimes there's a gap which has no pulses

i think that's the reason why its resulting in the servos reacting this way...

i'm thinking whether is it because of the IF loop?

Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 12, 2008, 02:53 am
Quote
Hi, Sorry to join this thread so late in the piece. I am slowly making progress on a similar project but to stabilise a quad copter. I have gathered snippets of code and ideas from all sorts of places, most notably a UAVP project in Europe. It is worth a google of UAVP and a look at their site if you have not already done so. They make a lot of their code available and there are lots of clues there on how to decode the stream and in fact to deal with sensors you mentioned above. I am now at the stage where I have the raw RC 'stick' values captured and have mastered comms with the sensors. When you get to the point of dealing with PID code etc, would love to compare notes.


hi.. thanks for replying.. i took a look at hte opensource thing... actually i'm quite rushing for time to finish the project.. i look at the source code of the quadcopter.. but its too complicated for me. haha..
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 12, 2008, 09:35 am
Quote
hi.. i tried the usual way of digitally writing the servos... i found that the problem is when the program is looping... sometimes there's a gap which has no pulses

i think that's the reason why its resulting in the servos reacting this way...

i'm thinking whether is it because of the IF loop?

do you mean this code?
Code: [Select]

 if ((PulseWidth <= 2200) && (PulseWidth >= 600))   // 600 is a little low, try it with 800
 {
   digitalWrite(pin_num, HIGH);  
   delayMicroseconds(PulseWidth);
   digitalWrite(pin_num, LOW);
 }
 else {
        ; you may want to add code here to blink a LED if Pulse WIdth is out of range
 }

If so, did you add the code to see if you are sending values that are out of range? It will help if you can determine if your problem is the calculation of the servo pulse width or the servo driving code.

Also, if you post the entire sketch you are testing then there may be some tips from people here to help you further.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 12, 2008, 09:39 am
i checked the pulsewidth... its alright... everything is within range...

arlight.. here's the code..

Code: [Select]
#define icpPin            8   // this interrupt handler must use pin 8
#define TICKS_PER_uS      2    // number of timer ticks per microsecond
#define MAX_CHANNELS    3         // maximum number of channels we want  
volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
volatile uint8_t Channel;      // number of channels detected so far in the frame (first channel is 1)

boolean DataAvailable; // set to true when we have received data for all channels
boolean Ready;         // true when we are ready to detect the leading edge of a channel

int RefreshTime = 2;
int lastPulse;

int a = 13;
int b = 12;
int c = 11;
int g = 6;

// here is the logic of how this code works :
// to start, channel is set to 1 and ready to false and the DataAvailable flag is false
// when a negative edge is detected ready is set to true
// when a positive edge is detected and ready is true then the measurement starts
// when a negative edge is detected and the ready flag is true then the measurement ends, the ready flag is reset back to false
//  and the channel is incremented. If the channel count after incrementing is greater than the number of channels then
//  the DataAvailable flag is set true and pulse data can be accessed

ISR(TIMER1_CAPT_vect){
  if( !bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
        if(Ready) {
             Pulses[Channel] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
           
                 if(++Channel > MAX_CHANNELS)
           {
                   Channel = 1;       //reset the channel counter to 1      
                 DataAvailable = true;  // this line is needed to indicate that the channel data is valid!!!!
               }
                SelectChannel( Channel);
             // Add code here to gate the multiplexer to the current channel          
        }          
        Ready = !Ready;  //toggle the ready flag
  }
  else {                       // rising  edge was detected  
       TCNT1 = 0;               // reset the counter      
                         
  }    
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge    
}

void setup()                    // run once, when the sketch starts
{
 Serial.begin(9600);  
 
 pinMode(icpPin,INPUT);
   
 pinMode(g,OUTPUT);
 pinMode(a,OUTPUT);
 pinMode(b,OUTPUT);
 pinMode(c,OUTPUT);
 pinMode(2,OUTPUT);
 pinMode(3,OUTPUT);
 pinMode(4,OUTPUT);
 pinMode(7,OUTPUT);
 
 Channel = 1;            
 Ready = false;
 DataAvailable = false;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set)
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
 
}

void SelectChannel( int channel){
   digitalWrite(g,HIGH);  // turn the output off while twiddling select bits
   if(Channel == 1)
   {
      digitalWrite(a,LOW);
      digitalWrite(b,LOW);            
   }
   else if(Channel == 2)
   {
      digitalWrite(a,HIGH);
      digitalWrite(b,LOW);
   }
    else if(Channel == 3)
    {
      digitalWrite(a,LOW);
      digitalWrite(b,HIGH);
    }
    digitalWrite(c,LOW);   // c is always low

    digitalWrite(g,LOW);  // turn output on    
}


int GetChannelPulseWidth(uint8_t Channel) {
 // this is the access function for channel data
 int result;  
 if( DataAvailable  && (Channel > 0) && (Channel <=  MAX_CHANNELS)  ) {
    cli();             //disable interrupts
    result =  Pulses[Channel] ;
    sei();             // enable interrupts
 }
 else
    result = 0;        // return 0 if no valid pulse is available  

 return result;

}

void loop()                     // run over and over again
{
 
int pulsewidth;

 
     // print the decoder state
  if(DataAvailable == false)
     {// Serial.println("The decoder has not detected all channels ");  
}
  else  
       {

      PulseServo(1,GetChannelPulseWidth(1));
      delay(6);

      PulseServo(2,GetChannelPulseWidth(2));
      delay(6);
      PulseServo(3,GetChannelPulseWidth(3));
      delay(6);
  }    

 
 
 // now print the channel pulse widths
 // they should be 0 if the state is not ready
 for ( int i =1; i <=3; i++ ){ // print the status of the first three channels
     /*Serial.print("Channel ");
     Serial.print(i);
     Serial.print(" has width ");*/
     pulsewidth = GetChannelPulseWidth(i);
       //Serial.println(pulsewidth);
 }
 
   
 
}

void PulseServo(int channel,int pulsewidth)
{
 //   your code here to pulse the servo for the given channel with the given pulse width
 // you should check that pulsewidth is within the valid range for your servos

 int pin_num = 0;
 
 pin_num = channel + 1;

 if ((pulsewidth <= 2200) && (pulsewidth >= 600))
 {
   digitalWrite(pin_num, HIGH);  
   delayMicroseconds(pulsewidth);
   digitalWrite(pin_num, LOW);
 }

/*  Serial.print("chan ");
 Serial.print(channel);
 Serial.print("=");
 Serial.print(pulsewidth);
 Serial.print(", ");
 Serial.println();*/

}


by the way, can i use the timer1 for the RC and also the servo timing also? would it be possible?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 12, 2008, 10:01 am
Are the pulse widths stable? ( can you attach a few seconds worth of the debug output?)

The Timer1 counter is reset on the input pulses so can't be used for output as well. But the pulseServo function should work ok as is. Why not verify this by modifying GetChannelPulseWidth to just return a value from a potentiometer connected to an analog pin. The pulse width would be the analogRead value + 1000.  


Also, are you powering the servos from the arduino 5v line? If so, try it using an external 5v source for the servos.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 12, 2008, 10:50 am
hi i posted the snapshot of the debug screen here....

http://www.axileon.com/blog/untitled.JPG

the pulsewidth is pretty consistent...

i am using an dc adaptor from the mains....
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 12, 2008, 11:11 am
Try it with the interrupt code completly removed to be sure interrupts are not running.
make sure this line is removed: TIMSK1 = _BV(ICIE1);   // enable input capture interrupt

Just modify GetChannelPulseWidth to return hard coded values similar to those you just posted and see if you get the same servo behavour.

If so, it may be a power issue. If the problem goes away then we can look to see if the interrupt is causing the problem or if it is something else

You could also try a test with no serial print statments to verify that those are not affecting the timings.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 13, 2008, 01:32 pm
hey i think i have found the solution to the problem.... i tried fiddling with the code..

power supply is alright... interrupt is not causing the problem.

the problem is with the delay function of arduino.. if i want to get very decent regular pulses, i have to use the delaymicroseconds function.

However, this creates another problem. delaymicroseconds disables interrupts.. i cant use it too.. so any more recommendations? i'm damn heck frustrated now.
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 13, 2008, 03:06 pm
You could use timer2 to time the pulses at suitable precision (i.e. .5us per count, the same as timer1 in yr sketch). It will overflow every 128us  so you will need a variable that counts the number of overflows.  The logic could be something like this:

Set an output channel index counter to first channel

Get the pulse width for this channel      
   Divide pulse width (in microseconds) by 128 to set an overflow counter variable
   Take the integer remainder  (i.e. modulus) to get the output compare value for the timer when the overflow count has been reached the calculated value.
  Pulse the pin for this channel high,  reset the timer and enable interrupts
  In the timer2 interrupt handler, decrement the overflow counter when the counter reachs 256
  When the overflow counter reaches zero, set the output compare register to the remainder value you calculated earlier.
When the output compare interrupt occurs, set the channel pin low.
Repeat this loop for all channels

When all channels have been pulsed, you need to wait for the start of the next frame before repeating. (pulsing a typical analog servo with insufficient gap is not good for the servos)

One way of doing this is as follows:
   Store the millis() count just before you start pulsing the first channel
When all channels have been pulsed and any processing for the next frame is completed, note the millis() count. You want to call delay() for as many milliseconds as necessary for 20 milliseconds to have elapsed since your start of pulsing time. Note this interframe time delay is not critical and I doubt moderate variation would have any impact on the performance of your servos.

I hope these suggestions are  helpful.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 13, 2008, 03:14 pm
hm.. but the specs for atmega168 says that timer2 is a 8 bit timer too....

u're meaning i can scale the clock for timer2 to be the same as timer1?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 13, 2008, 03:19 pm
Yes, you can have a prescale of 8 giving 0.5us per count with 16mhz arduino on timer2 just like timer1. But, timer2 overflows in 128us, timer1 overflows in 32ms with the same prescale of 8.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 17, 2008, 12:37 pm
Quote

Set an output channel index counter to first channel

Get the pulse width for this channel      
   Divide pulse width (in microseconds) by 128 to set an overflow counter variable
   Take the integer remainder  (i.e. modulus) to get the output compare value for the timer when the overflow count has been reached the calculated value.
  Pulse the pin for this channel high,  reset the timer and enable interrupts
  In the timer2 interrupt handler, decrement the overflow counter when the counter reachs 256
  When the overflow counter reaches zero, set the output compare register to the remainder value you calculated earlier.
When the output compare interrupt occurs, set the channel pin low.
Repeat this loop for all channels

When all channels have been pulsed, you need to wait for the start of the next frame before repeating. (pulsing a typical analog servo with insufficient gap is not good for the servos)

I hope these suggestions are  helpful.


hi, i tried implement your solution but i have difficulty getting the interrupts to work.

here is the code.

Code: [Select]
//#include <avr/io.h>
//#include <avr/interrupt.h>

#define INIT_TIMER_COUNT 0
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT

int servo = 3;
int servoRoll = 5;
int servoPitch = 6;
int servoYaw = 11;
int counter = 0;
int compare = 0;
 
ISR (TIMER2_OVF_vect)
{
 counter--;
 if (counter == 0)
 {
   OCR2A = compare;
   }
 
}

ISR (TIMER2_COMPA_vect)
{
digitalWrite(2,LOW);
}

void setup()
{
 Serial.begin(9600);

 pinMode(2, OUTPUT);
 pinMode(servo, OUTPUT);
 pinMode(servoRoll, OUTPUT);
 pinMode(servoPitch, OUTPUT);
 pinMode(servoYaw, OUTPUT);


 /* setup for timer 2 */

 TCCR2A = _BV(COM2A1) | _BV(COM2B1)| _BV(WGM21) | _BV(WGM20);  // clear both A on compare match, set fast PWM mode.
 TCCR2B = _BV(CS21); // set prescaler of 1024 for 8 bit timer - 0 to 255.  

 TIMSK2 = _BV(OCIE2A) | _BV(TOIE2) ; // enables the interrupt for the
/* end of setup for timer 2 */

}

void loop()
{
  int pulsewidth = 1500;

  counter = pulsewidth/128;
//   Serial.println(counter);
  compare = pulsewidth%128;
//  Serial.println(compare);
   
   
  digitalWrite(2,HIGH);

  RESET_TIMER2;  
 
  sei();
 
}


actually what you are suggesting to me is that I use the timer 2 to pulse the servos channel by channel right? so I am not using OCR2A's interrupt pin right?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 17, 2008, 03:02 pm
Here is some code that is closer to what you want. It is untested and probably has errors that will need fixing. For example I am not sure what will happen if the output compare value is set for 0 (i.e. if it doesn't trigger immediately then your code needs to handle this)

You can test it with NBR_CHANNELS 1 and it will (or should when you get it going ;) ) just pulse the servo on pin 3
Code: [Select]


#define NBR_CHANNELS 4
#define DEFAULT_PULSE_WIDTH  1500

byte servo = 3;          // these could also be: #define servo 3
byte servoRoll = 5;
byte servoPitch = 6;
byte servoYaw = 11;

// arrays for channel info, note that the first channel is at index 0
byte counter[NBR_CHANNELS];   // holds pulse width / 128
byte compare[NBR_CHANNELS];   // holds pulse width % 128
byte pins[NBR_CHANNELS] = {servo,servoRoll,servoPitch,servoYaw};  //holds pins associated with servos

volatile byte Channel = 0;  // counter holding the channel being pulsed

ISR (TIMER2_OVF_vect)
{
 --counter[Channel];
 if (counter[Channel] == 0)
 {
   OCR2A = compare[Channel];  
   TIMSK2 = _BV(OCIE2A);  // enable the output compare interrupt.
 }  
}

ISR (TIMER2_COMPA_vect)
{
digitalWrite(pins[Channel],LOW);
Channel++;  // now increment to the next channel
if(Channel >= NBR_CHANNELS)  {    
   TIMSK2 = 0; // we have done all channels, so disable interrupts
   Channel = 0;      
}
else{
   digitalWrite(pins[Channel],HIGH); // pulse the next channel high
   TCNT2 = 0;  // reset the count;
   TIMSK2 =  _BV(TOIE2) ; // enable the overflow interrupt
 }
}

void SetPulse(byte channel, int pulsewidth){
// store the values for the given channel  
     counter[channel] = pulsewidth / 128;    
     compare[channel] = pulsewidth% 128;  
}

void PulseServos(){
 // start the frame pulsing each servo in turn once
   Channel = 0;  // start from the first channel
   TIMSK2 =  _BV(TOIE2) ; // enable the overflow interrupt
}


void setup()
{
 Serial.begin(9600);
 for(int i=0; i < NBR_CHANNELS; i++) {
      pinMode( pins[i], OUTPUT) ;  // set servo pins to output
      SetPulse(i, DEFAULT_PULSE_WIDTH);  // store default values for counter and compare
 }
 pinMode(2, OUTPUT);

 /* setup for timer 2 */
 TCCR2A = _BV(COM2A1) | _BV(COM2B1)| _BV(WGM21) | _BV(WGM20);  // clear both A on compare match, set fast PWM mode.
 TCCR2B = _BV(CS21); // set prescaler of 1024 for 8 bit timer - 0 to 255.  
}

void loop()
{
 unsigned long endtime = millis() + 20;  //each frame is 20 milliseconds
 PulseServos();  // start the interrupt handler pulsing the servos
 delay( endtime - millis()); // wait until end of frame  
  // todo the above delays need enhancing to detect millis rollover
  // note that it assumes that servos will have finished pulsing within 20ms
  // you may want to also check that Channel equals 0 just to make sure            
}



You could add something like this in loop if you want to test setting the servo position via a pot.
     SetPulse( 0, analogRead(potPin) + 1000 );  // set pulse width on the first channel according to the pot value
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 17, 2008, 03:58 pm
hi.. i tried compiling the code.. but i keep gettting an error... i think there's something wrong with the variable name..

this error is expected primary expression before [ token;
Code: [Select]
ISR(TIMER2_OVF_vect)
{ <<<<------- error points to here
 --counter[Channels];
 if (counter[NBR_CHANNELS] == 0)
 {
   OCR2A = compare[Channel];  
   TIMSK2 = _BV(OCIE2A) // enable the output compare interrupt.
 }  
}


i've been cracking my brains but i dont see anything wwrong with the code?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 17, 2008, 04:32 pm
I just tried pasting the code I posted above in a sketch and it compiles ok. Try another cut and paste. If that doesn't work, paste the code you are compiling into a reply here and I will have a look.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 18, 2008, 09:59 am
hi... the i have response for channel 5,6 .. but the pulses are incorrect.... also channel 10 , the last channel i have a  HIGH signal thru out...

// todo the above delays need enhancing to detect millis rollover
 // note that it assumes that servos will have finished pulsing within 20ms
 // you may want to also check that Channel equals 0 just to make sure      

sorry i wish to know what do u mean by the paragraph above? cos i dont really understand it.

the code i have used is the same as the one that you have ...

Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 18, 2008, 10:41 am
Can you say a little more about how the pulses are incorrect. Also, did you mean pin 11 rather than 10? what about pin 3?  

Try it with just one channel (#define NBR_CHANNELS 1) to see what that does.

You could also put some debugging statements in the code to see whats happening. Here is an example, but change this as required so you can see what the code is doing:
Edit: serial print statements updated with byte values cast to ints

Code: [Select]


#define NBR_CHANNELS 4
#define DEFAULT_PULSE_WIDTH  1500

byte servo = 3;          // these could also be: #define servo 3
byte servoRoll = 5;
byte servoPitch = 6;
byte servoYaw = 11;

// arrays for channel info, note that the first channel is at index 0
byte counter[NBR_CHANNELS];   // holds pulse width / 128
byte compare[NBR_CHANNELS];   // holds pulse width % 128
byte pins[NBR_CHANNELS] = {servo,servoRoll,servoPitch,servoYaw};  //holds pins associated with servos

volatile byte Channel = 0;  // counter holding the channel being pulsed

ISR (TIMER2_OVF_vect)
{
 --counter[Channel];
 if (counter[Channel] == 0)
 {
   OCR2A = compare[Channel];  
   TIMSK2 = _BV(OCIE2A);  // enable the output compare interrupt.
 }  
 Serial.print(" ISR count=");
 Serial.print((int)counter[Channel]);  
}

ISR (TIMER2_COMPA_vect)
{
digitalWrite(pins[Channel],LOW);
Channel++;  // now increment to the next channel
if(Channel >= NBR_CHANNELS)  {    
   TIMSK2 = 0; // we have done all channels, so disable interrupts
   Channel = 0;      
}
else{
   digitalWrite(pins[Channel],HIGH); // pulse the next channel high
   Serial.print(" ISR cmp=");
   Serial.println((int)TCNT2);
   TCNT2 = 0;  // reset the count;
   TIMSK2 =  _BV(TOIE2) ; // enable the overflow interrupt
 
 }
}

void SetPulse(byte channel, int pulsewidth){
// store the values for the given channel  
     counter[channel] = pulsewidth / 128;    
     compare[channel] = pulsewidth% 128;  
}

void PulseServos(){
 // start the frame pulsing each servo in turn once
   Channel = 0;  // start from the first channel
   TIMSK2 =  _BV(TOIE2) ; // enable the overflow interrupt
}


void setup()
{
 Serial.begin(38400);        // note higher baud rate
 for(int i=0; i < NBR_CHANNELS; i++) {
      pinMode( pins[i], OUTPUT) ;  // set servo pins to output
//       SetPulse(i, DEFAULT_PULSE_WIDTH);  // store default values for counter and compare
        SetPulse(i, 1000 + (i * 129)); // this should ensure that each of the channels have different values;
 }
 pinMode(2, OUTPUT);

 /* setup for timer 2 */
 TCCR2A = _BV(COM2A1) | _BV(COM2B1)| _BV(WGM21) | _BV(WGM20);  // clear both A on compare match, set fast PWM mode.
 TCCR2B = _BV(CS21); // set prescaler of 1024 for 8 bit timer - 0 to 255.

 for(int i=0; i < NBR_CHANNELS; i++) {
   Serial.print("Chan ");
   Serial.print(i);
   Serial.print(", Counter = ");
   Serial.print((int)counter[i]);
   Serial.print(", Compare = ");
   Serial.println((int)compare[i]);    
 }  
}

void loop()
{
 unsigned long endtime = millis() + 20;  //each frame is 20 milliseconds
 PulseServos();  // start the interrupt handler pulsing the servos
 delay( endtime - millis()); // wait until end of frame  
  // todo the above delays need enhancing to detect millis rollover
  // note that it assumes that servos will have finished pulsing within 20ms
  // you may want to also check that Channel equals 0 just to make sure            
}




Note the increased the baud rate in that code. You may want it even higher to keep the processing time down in the interrupt handler.

Don't worry about things like millis rollover for now, those comments were to remind us to handle exceptional situations. We can easily address that when the code is working as expected.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 18, 2008, 11:04 am
hi.. i tried changing the #define NBR_CHANNELS 4 to #define NBR_CHANNELS 1
it doesnt work unless i change this line

byte pins[NBR_CHANNELS] = {servo}

however pin 3has no output.... i wonder is it because timer 2 affect pin 3 and 11?

also.. i note the serialprint debug statements are printing symbols instead of numbers.. its because its defined as byte right?

Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 18, 2008, 11:17 am
We don't want the timer to control the pins in hardware, so this needs to be turned off. But one problem that needs to be fixed first is that the interrupt code is changing the values of the counter and compare arrays so the timing values are only correct the first time through. We need to use a new variable to decrment in the interrupt handler.

Edit: added code to use counter variable:

Code: [Select]

volatile byte ISRCount;        // counter used in the interrupt routines;  << add this

ISR (TIMER2_OVF_vect)
{

 if (++ISRCount > counter[Channel] )   //<< change ISR code to increment this variable
 {
   OCR2A = compare[Channel];  
   TIMSK2 = _BV(OCIE2A);  // enable the output compare interrupt.
 }  
 
}

ISR (TIMER2_COMPA_vect)
{
Channel++;  // now increment to the next channel
ISRCount = 0; // reset the isr iteration counter          << add this line here
if(Channel >= NBR_CHANNELS)  {    
   TIMSK2 = 0; // we have done all channels, so disable interrupts
   Channel = 0;      
}
else{
   digitalWrite(pins[Channel],HIGH); // pulse the next channel high

   TCNT2 = 0;  // reset the count;
   TIMSK2 =  _BV(TOIE2) ; // enable the overflow interrupt  
 }
}


This will fix one of the issues with the current code, I expect there will be more to do to get it going.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 18, 2008, 12:24 pm
right now i'm having a pulse of 1 us for every 128 us... for channel 3.

there seem to be another pulse that is appearing beside that 1us pulse... so its like 2 pulses every 128 us.

the rest of the channels all are high all the way...

Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 18, 2008, 01:26 pm
This code should get you going. The major problem was the digitalWrites were not being done in the correct place. I also simplifed the interrupt code.

Code: [Select]


#define NBR_CHANNELS 4
#define DEFAULT_PULSE_WIDTH  1500

byte servo = 3;          // these could also be: #define servo 3
byte servoRoll = 5;
byte servoPitch = 6;
byte servoYaw = 11;

// arrays for channel info, note that the first channel is at index 0
byte counter[NBR_CHANNELS];   // holds pulse width / 128
byte compare[NBR_CHANNELS];   // holds pulse width % 128
byte pins[NBR_CHANNELS] = {servo,servoRoll,servoPitch,servoYaw};  //holds pins associated with servos

volatile byte Channel = 0;  // counter holding the channel being pulsed
volatile byte ISRCount;        // counter used in the interrupt routines;

ISR (TIMER2_OVF_vect)
{

 if (++ISRCount == counter[Channel] )   // are we on the final iteration for this channel
 {    
   TCNT2 = 256 - compare[Channel];    // yes, so we count the remainder here
// you may want to rename 'compare' to 'remainder' if that seems clearer                            
 }  
 else if(ISRCount > counter[Channel])
 {
        // here if we have finished pulsing this channel !          
     digitalWrite(pins[Channel],LOW); // pulse this channel low
     Channel++;  // now increment to the next channel
     ISRCount = 0; // reset the isr iteration counter
     if(Channel >= NBR_CHANNELS)  {    
          TIMSK2 = 0; // we have done all channels, so disable interrupts
      Channel = 0;      
     }
     else
       digitalWrite(pins[Channel],HIGH); // pulse the next channel high      
 }  
}

void SetPulse(byte channel, int pulsewidth){
// store the values for the given channel  
     counter[channel] = pulsewidth / 128;    
     compare[channel] = pulsewidth% 128;  
}

void PulseServos(){
 // start the frame pulsing each servo in turn once
   Channel = 0;  // start from the first channel
   ISRCount = 0;  // reset the value of the ISR counter;
   digitalWrite(pins[Channel],HIGH); // pulse the first channel high
   TIMSK2 =  _BV(TOIE2) ; // enable the overflow interrupt
}


void setup()
{
 Serial.begin(38400);        // note higher baud rate
 for(int i=0; i < NBR_CHANNELS; i++) {
      pinMode( pins[i], OUTPUT) ;  // set servo pins to output
//       SetPulse(i, DEFAULT_PULSE_WIDTH);  // store default values for counter and compare
        SetPulse(i, 1000 + (i * 129)); // this should ensure that each of the channels have different values;
 }
 pinMode(2, OUTPUT);

 /* setup for timer 2 */
 TIMSK2 = 0;  // disable interrupts
 TCCR2A = 0;  // normal counting mode
 TCCR2B = _BV(CS21); // set prescaler of 8  
}

void loop()
{
//static  unsigned long endtime;
 unsigned long endtime = millis() + 20;  //each frame is 20 milliseconds
 PulseServos();  // start the interrupt handler pulsing the servos

 delay( endtime - millis()); // wait until end of frame  
  // todo the above delays need enhancing to detect millis rollover
  // note that it assumes that servos will have finished pulsing within 20ms
  // you may want to also check that Channel equals 0 just to make sure            
}


Still left to do is to check that the counting is done correctly i.e. that we do the correct number of iterations of the compare for the given pulse width.

After that, we can make add the code to handle the case where millis() rolls over back to zero.

Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 18, 2008, 03:24 pm
sure. thanks a lot. i will try it tom morning..

anyway.. i wish to know why must we increase the baud rate? what effect does it has on the response of the program?

if i integrate it with my gyros at the latter stage. will it affect it? and also the RC signal?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 18, 2008, 03:42 pm
The increase in baud rate is just to reduce the amount of time the debug printing spent in the interrupt handler.

Here is working code without the debug printing. The timings look ok for driving servos and are consistent, although the exact pulse widths are 25microseconds less than the calculated amount, that shouldn't be a problem in the real world.

Here is the latest code. It still needs testing and the overrun stuff.

Code: [Select]


#define NBR_CHANNELS 4
#define DEFAULT_PULSE_WIDTH  1500

#define PULSING_COMPLETE  (Channel >= NBR_CHANNELS)   // true if all channels have been pulsed

byte servo = 3;          // these could also be: #define servo 3
byte servoRoll = 5;
byte servoPitch = 6;
byte servoYaw = 11;

// arrays for channel info, note that the first channel is at index 0
byte counter[NBR_CHANNELS];   // holds pulse width / 128
byte remainder[NBR_CHANNELS];   // holds pulse width % 128
byte pins[NBR_CHANNELS] = {servo,servoRoll,servoPitch,servoYaw};  //holds pins associated with servos

volatile byte Channel = 0;  // counter holding the channel being pulsed
volatile byte ISRCount;        // counter used in the interrupt routines;

ISR (TIMER2_OVF_vect)
{
 if (++ISRCount == counter[Channel] ) // are we on the final iteration for this channel
 {
   TCNT2 = 256 - remainder[Channel];   // yes, so count down the remainder
 }  
 else if(ISRCount > counter[Channel])  
 {
     // we have finished timing the channel so pulse it low and move on
     digitalWrite(pins[Channel],LOW); // pulse this channel low      
     Channel++;  // now increment to the next channel
     ISRCount = 0; // reset the isr iteration counter
     TCNT2 = 0;
     if(Channel >= NBR_CHANNELS){    // check if we have done all channels  
          TIMSK2 = 0; // all channels pulsed, so disable interrupts          
     }
     else
       digitalWrite(pins[Channel],HIGH); // not done yet so pulse the next channel high      
 }  
}

void SetPulse(byte channel, int pulsewidth){
// store the values for the given channel  
     counter[channel] = pulsewidth / 128;    
     remainder[channel] = pulsewidth % 128;  
}

void PulseServos(){
 // start the frame pulsing each servo in turn once
   Channel = 0;  // start from the first channel
   ISRCount = 0;  // reset the value of the ISR counter;
   TCNT2 = 0;
   TIFR2 = _BV(TOV2);  // clear pending interrupts;
   TIMSK2 =  _BV(TOIE2) ; // enable the overflow interrupt
   digitalWrite(pins[Channel],HIGH); // pulse the first channel high
}


void setup()
{
 Serial.begin(38400);        // note higher baud rate
 for(int i=0; i < NBR_CHANNELS; i++) {
      pinMode( pins[i], OUTPUT) ;  // set servo pins to output
//       SetPulse(i, DEFAULT_PULSE_WIDTH);  // store default values for counter and compare
        SetPulse(i, 1000 + (i*200));    // test pulses from 1ms to 1.6ms in 200us steps
 }
 pinMode(2, OUTPUT);

 /* setup for timer 2 */
 TIMSK2 = 0;  // disable interrupts
 TCCR2A = 0;  // normal counting mode
 TCCR2B = _BV(CS21); // set prescaler of 8  
}

void loop()
{
//static  unsigned long endtime;
 unsigned long endtime = millis() + 20;  //each frame is 20 milliseconds
 PulseServos();  // start the interrupt handler pulsing the servos

 delay( endtime - millis()); // wait until end of frame  
  // todo the above delays need enhancing to detect millis rollover
  // note that it assumes that servos will have finished pulsing within 20ms
  // There is a macro: PULSING_COMPLETE  that evaluates to false if the ISR is still pulsing   channels            
}

Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 19, 2008, 03:19 am
sorry. i have something to ask u about this section of the code.

Code: [Select]
if (++ISRCount == counter[Channel] ) // are we on the final iteration for this channel
 {
   //TCNT2 = 256 - remainder[Channel];   // yes, so count down the remainder
    TCNT2 = remainder[Channel];
 }


since we are using a remainder and counter, shouldnt the code be like what I have written in the above? we should be counting up instead of down right? since the counter is incrementing instead of decrementing... ?

i dont really understand this part of the code..can you explain it to me?

Code: [Select]
delay( endtime - millis()); // wait until end of frame  


oh by the way.. the pulses look fine to me. and i have managed to integrate the servo and the R/C together! ;D

thanks a lot.. now my next stage will be to integrate the gyros with the whole thing....
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 19, 2008, 11:51 am
i have combined the whole program but the R/C controls are good... but the gyro is causing the whole pulsewidth to go crazy...

is there a way to smooth the gyro data more efficiently?? i'm just doing an averaging of the incoming data..

Code: [Select]
int digital_smooth(int rawIn, int *smooth_array)
{
 long total = 0;                            // the running total

 static int i;
 int j;
 
 i = (i + 1) % no_of_samples;    // increment counter and roll over if necc. -  % (modulo operator) rolls over variable
 smooth_array[i] = rawIn;
 
 for ( j = 0; j < no_of_samples; j++)
 {
   total += smooth_array[j];
 }
 
 return total/no_of_samples;
 
}
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 19, 2008, 03:14 pm
That code looks like it should work, but the values will be out of range until you fill up the array. You can fix that with a static variable called count that increments on each call until it equals  the array size. count holds the number of valid entries in the array so returning total/count  would give the correct results even when the array is not full.

Code: [Select]

int count = 0; //  this variable holds the number of samples added to the array
#define SAMPLE_ARRAY_SIZE ?  // this holds the size of the arrry

int digital_smooth(int rawIn, int *smooth_array)
{
 long total = 0;                            // the running total

 static int i;
 int j;
 
  i = (i + 1) % SAMPLE_ARRAY_SIZE;     // increment counter and roll over if necc. -  % (modulo operator) rolls over variable
 smooth_array[i] = rawIn;

 if(count < SAMPLE_ARRAY_SIZE)
    count++
 
 for ( j = 0; j < no_of_samples; j++)
 {
   total += smooth_array[j];
 }  
 return total/count;  
}


But if your data does need more smoothing then consider a  Kalman filter. This is a very sophisticated technique and it may be overkill for your application. You can read more about Kalman filters including some ATmega168 source code here: http://forum.sparkfun.com/viewtopic.php?t=6186

To answer your earlier questions: the first version I posted used the output compare handler to do the remainder. The current one uses the overflow handler for all the counting. It times the remainder by setting the count so that it will overflow after the remainder number of ticks. Because it overflows above 255, the initial count is set to 255-remainder. Perhaps the comment should read something like: ' count up the remainder number of ticks'

We want to wait 20ms after starting to pulse the first servos before we start over again pulsing the next frame. Endtime is set when the servos start pulsing to be the starting time plus 20ms.  After pulsing, we calculating the difference between the current time and the end time and wait for that period. But, I am working on a version that does the end of frame timing in the interrupt as well, so there would be no need to use any of that delay code or worry about millis rollover. I will post it when its done.
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 19, 2008, 07:02 pm
Here is updated code for running the servos that runs completely in the interrupt handler. The pulse width timing on this version is accurate to around one percent. I changed the names of some of the functions in preparation for turning this into a library.

Code: [Select]


#define NBR_CHANNELS 4                   // the maximum number of channels.  
#define MIN_PULSE_WIDTH      750         // the shortest pulse sent to a servo  
#define MAX_PULSE_WIDTH      2250        // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH  1500        // default pulse width at startup

#define FRAME_SYNC_INDEX   NBR_CHANNELS  // frame sync delay is last entry in array
#define FRAME_SYNC_PERIOD  20000         // total frame sync time in us
#define FRAME_SYNC_DELAY   (FRAME_SYNC_PERIOD - ( NBR_CHANNELS * DEFAULT_PULSE_WIDTH))


byte servo = 3;          // these could also be: #define servo 3
byte servoRoll = 5;
byte servoPitch = 6;
byte servoYaw = 11;

// arrays for channel info, note that the first channel is at index 0
byte counter[NBR_CHANNELS+1];   // holds pulse width / 128
byte remainder[NBR_CHANNELS+1];   // holds pulse width % 128
byte pins[NBR_CHANNELS+1] = {servo,servoRoll,servoPitch,servoYaw};  //holds pins associated with servos

volatile uint8_t Channel = 0;  // counter holding the channel being pulsed
volatile uint8_t ISRCount;  // counter used in the interrupt routines;

ISR (TIMER2_OVF_vect)
{
 ++ISRCount; // increment the overlflow counter
 if (ISRCount == counter[Channel] ) // are we on the final iteration for this channel
 {
     TCNT2 = remainder[Channel];   // yes, set count for overflow after remainder ticks
 }  
 else if(ISRCount > counter[Channel])  
 {
     // we have finished timing the channel so pulse it low and move on
     digitalWrite(pins[Channel],LOW); // pulse this channel low      
     Channel++;    // now increment to the next channel
     ISRCount = 0; // reset the isr iteration counter
     TCNT2 = 0;    // reset the clock counter register
     if(Channel < NBR_CHANNELS){           // check if we need to pulse this channel    
         digitalWrite(pins[Channel],HIGH); // not done yet so pulse the next channel high  
     }
     else if(Channel == NBR_CHANNELS){     // check if this is the synch delay  
           // all channels pulsed, this is the sync delay, do nothing in this version
     }
     else {
        Channel = 0; // all done so start over              
         digitalWrite(pins[Channel],HIGH); // pulse the first channel high
     }
  }  
}

void ServoWrite(byte channel, unsigned int pulsewidth){
// calculate and store the values for the given channel
  if(channel <= NBR_CHANNELS) {   // ensure channel is valid
     if (channel < FRAME_SYNC_INDEX){    // if not frame sync, ensure pulse width is valid
        if( pulsewidth < MIN_PULSE_WIDTH )
            pulsewidth = MIN_PULSE_WIDTH;
        else if( pulsewidth > MAX_PULSE_WIDTH )
          pulsewidth = MAX_PULSE_WIDTH;      
     }
     counter[channel] = pulsewidth / 128;    
     remainder[channel] = 255 - (2 * (pulsewidth - (counter[channel] * 128)));  // the number of 0.5us ticks for timer overflow        
  }  
}

unsigned int ServoRead(byte channel){  
// returns the pulse width for the given channel  
  unsigned int pulsewidth = counter[channel] * 128 ;    
  pulsewidth +=  ((-remainder[channel] - 255) / 2);
  return pulsewidth;  
}

void ServoActivate(){
   for(int i=0; i < NBR_CHANNELS; i++) {
      pinMode( pins[i], OUTPUT) ;  // set servo pins to output
        ServoWrite(i, DEFAULT_PULSE_WIDTH);  // store default values          
   }
   ServoWrite(FRAME_SYNC_INDEX, FRAME_SYNC_DELAY);  // store the frame sync period  
 
   Channel = 0;  // start from the first channel  
   ISRCount = 0;  // clear the value of the ISR counter;
   
   /* setup for timer 2 */
   TIMSK2 = 0;  // disable interrupts
   TCCR2A = 0;  // normal counting mode
   TCCR2B = _BV(CS21); // set prescaler of 8
   TCNT2 = 0;     // clear the timer2 count
   TIFR2 = _BV(TOV2);  // clear pending interrupts;
   TIMSK2 =  _BV(TOIE2) ; // enable the overflow interrupt            
}

void setup()
{
 Serial.begin(19200);      

 for(int i=0; i < NBR_CHANNELS; i++) {
     ServoWrite(i, 1000 + (i*250));    // test pulses from 1ms to 1.75ms in 250us steps              
 }
 ServoActivate();  // start the interrupt handler pulsing the servos
}

void loop()
{
 unsigned int val = analogRead(0);  // use a pot to set the pulse width for the first servo
 ServoWrite(0,val + 1000);  // write the pot value + 1000 to the first servo
}
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 20, 2008, 01:20 pm
sorry.. what is the servoRead function used for in the sketch above? is it for the RC data?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 20, 2008, 01:33 pm
ServoRead returns the pulse width that has been set for that channel. it will be the last value written, or the default value if ServoWrite has not been called for this channel.

I have included it so that the library I am making will have a similar interface to the existing arduino servo libraries. Its useful if an application wants to either check the current value or to increase or decrease it by a fixed amount, for example the following code in loop would slowly pan a servo from its current position to the minimum  position :
    int newValue = ServoRead(chan) - 10;
    if( newValue >= MINIMUM_PULSE_WIDTH)
       ServoWrite(chan, newValue);
    delay(20);

Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 20, 2008, 01:37 pm
so you mean, its something like getting the existing pulsewidth that the servo is getting currently?

by the way, i tested your latest code. works like a charm

right now i'm working on the testing of the R/C signals together with your latest servo code. however, the different variable numbers is giving me an headache. i think it has to do with the channel variable..

Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 20, 2008, 02:03 pm
Exactly, Read returns the existing pulsewidth that is being pulsed to the servo

I have modified the code since I last posted to get it closer the version I will be using for the library. It now has channels starting from 1 (it uses chan 0 for the sync delay) . The library code will support  channel numbers or simple servo variables.


Something like this:
#include <ServoTimer2>

ServoTimer2 servoRoll;
ServoTimer2 servoPitch;
ServoTimer2 servoYaw;


In setup:
 servoRoll.Activate(rollPin);
 servoPitch.Activate(pitchPin);
 servoYaw.Activate(yawPin);


in loop:

  servoRoll.Write(analogRead(0) + 1000 ); // set the values from pots
  servoPitch.Write(analogRead(1)+ 1000 ) ;
  servoYaw.Write(analogRead(2)  + 1000 );


 
Additional methods using channel number arguments would be for apps where that is more convenient
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 21, 2008, 06:19 pm
Here is the first cut of the library if you want to use it. These file need to be in a new directory called ServoTimer2 in the same subdirectory as the other Arduino libraries.
Header:
Code: [Select]

/*
 ServoTimer2.h - Interrupt driven Servo library for Arduino using Timer2- Version 0.1
 Copyright (c) 2008 Michael Margolis.  All right reserved.

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Lesser General Public
 License as published by the Free Software Foundation; either
 version 2.1 of the License, or (at your option) any later version.

 This library is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Lesser General Public License for more details.

 You should have received a copy of the GNU Lesser General Public
 License along with this library; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
*/

/*
 This library uses Timer2 to drive up to 8 servos using interrupts so no refresh activity is required from within the sketch.
 The usage and method naming is similar to the Arduino software servo library http://www.arduino.cc/playground/ComponentLib/Servo
 except that pulse width is in microseconds for greater accuracy rather than degrees.
 (A future version may also support degrees if this was required)
 
 A servo is activated by creating an instance of the Servo class passing the desired pin to the attach() method.
 The servo is pulsed in the background to the value most recently written using the write() method

 Note that analogWrite of PWM on pins 3 and 11 is disabled when the first servo is attached

 The methods are:

  ServoTimer2 - Class for manipulating servo motors connected to Arduino pins.

  attach()    - Attaches a servo motor to an i/o pin.

  write()     - Sets the servo pulse width in microseconds.

  read()      - Gets the last written servo pulse width in microseconds.

  attached()  - Returns true if there is a servo attached.

  detach()    - Stops an attached servos from pulsing its i/o pin.
 

The library takes about 824 bytes of program memory and 32+(1*servos) bytes of SRAM.
The pulse width timing is accurate to within 1%

*/

// ensure this library description is only included once
#ifndef ServoTimer2_h
#define ServoTimer2_h

#include <inttypes.h>
#include <wiring.h>

#define MIN_PULSE_WIDTH       750        // the shortest pulse sent to a servo  
#define MAX_PULSE_WIDTH      2250        // the longest pulse sent to a servo
#define DEFAULT_PULSE_WIDTH  1500        // default pulse width when servo is attached
#define FRAME_SYNC_PERIOD   20000        // total frame duration in microseconds
#define NBR_CHANNELS 8                   // the maximum number of channels, don't change this

typedef struct  {
     uint8_t nbr        :5 ;  // a pin number from 0 to 31
     uint8_t isActive   :1 ;  // false if this channel not enabled, pin only pulsed if true
  } ServoPin_t   ;  

typedef struct {
 ServoPin_t Pin;
 byte counter;
 byte remainder;
}  servo_t;

class ServoTimer2
{
 public:
     // constructor:
     ServoTimer2();

     uint8_t attach(int);     // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
                              // the attached servo is pulsed with the current pulse width value, (see the write method)
   void detach();
   void write(int);         // store the pulse width in microseconds (between MIN_PULSE_WIDTH and MAX_PULSE_WIDTH)for this channel
   int read();                    // returns current pulse width in microseconds for this servo
     boolean attached();      // return true if this servo is attached
private:
      uint8_t chanIndex;      // index into the channel data for this servo

};


// the following ServoArrayT2 class is not implimented in the first version of this library
class ServoArrayT2
{
 public:
     // constructor:
     ServoArrayT2();

     uint8_t attach(int);     // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
                              // channels are assigned consecutively starting from 1
                              // the attached servo is pulsed with the current pulse width value, (see the write method)
   void detach(int);        // detach the servo on the given channel
     void write(int,int);     // store the pulse width in microseconds (between MIN_PULSE_WIDTH and MAX_PULSE_WIDTH)for the given channel
   int read(int);                    // returns current pulse width in microseconds for the given channel
     boolean attached(int);   // return true if the servo on the given channel is attached
private:
      uint8_t chanIndex;      // index into the channel data for this servo

};

#endif


Source code in next post:
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 21, 2008, 06:28 pm
Servo library source code (comments removed to reduce size, see header for copyright)
Code: [Select]
extern "C" {
 // AVR LibC Includes
 #include <inttypes.h>
 #include <avr/interrupt.h>
 #include "WConstants.h"
}

#include "ServoTimer2.h"
static void initISR();  
static void writeChan(uint8_t chan, int pulsewidth);

#define FRAME_SYNC_INDEX   0             // frame sync delay is the first entry in the channel array
#define FRAME_SYNC_PERIOD  20000         // total frame duration in microseconds
#define FRAME_SYNC_DELAY   ((FRAME_SYNC_PERIOD - ( NBR_CHANNELS * DEFAULT_PULSE_WIDTH))/ 128) // number of iterations of the ISR to get the desired frame rate
#define DELAY_ADJUST       8             // number of microseconds of calculation overhead to be subtracted from pulse timings  

static servo_t servos[NBR_CHANNELS+1];    // static array holding servo data for all channels

static volatile uint8_t Channel;   // counter holding the channel being pulsed
static volatile uint8_t ISRCount;  // iteration counter used in the interrupt routines;
uint8_t ChannelCount = 0;          // counter holding the number of attached channels
static boolean isStarted = false;  // flag to indicate if the ISR has been initialised

ISR (TIMER2_OVF_vect)
{
 ++ISRCount; // increment the overlflow counter
 if (ISRCount ==  servos[Channel].counter ) // are we on the final iteration for this channel
 {
     TCNT2 =  servos[Channel].remainder;   // yes, set count for overflow after remainder ticks
 }  
 else if(ISRCount >  servos[Channel].counter)  
 {
     // we have finished timing the channel so pulse it low and move on
     if(servos[Channel].Pin.isActive == true)           // check if activated
         digitalWrite( servos[Channel].Pin.nbr,LOW); // pulse this channel low if active  

       Channel++;    // increment to the next channel
     ISRCount = 0; // reset the isr iteration counter
     TCNT2 = 0;    // reset the clock counter register
     if( (Channel != FRAME_SYNC_INDEX) && (Channel <= NBR_CHANNELS) ){           // check if we need to pulse this channel    
         if(servos[Channel].Pin.isActive == true)         // check if activated
            digitalWrite( servos[Channel].Pin.nbr,HIGH); // its an active channel so pulse it high  
     }
     else if(Channel > NBR_CHANNELS){
        Channel = 0; // all done so start over              
     }
  }  
}

ServoTimer2::ServoTimer2()
{
  if( ChannelCount < NBR_CHANNELS)  
     this->chanIndex = ++ChannelCount;  // assign a channel number to this instance
  else
     this->chanIndex = 0;  // todo      // too many channels, assigning 0 inhibits this instance from functioning
}

uint8_t ServoTimer2::attach(int pin)
{
     if( isStarted == false)
      initISR();    
     if(this->chanIndex > 0)  
     {
      //debug("attaching chan = ", chanIndex);
      pinMode( pin, OUTPUT) ;  // set servo pin to output
      servos[this->chanIndex].Pin.nbr = pin;  
      servos[this->chanIndex].Pin.isActive = true;  
     }
     return this->chanIndex ;
}

void ServoTimer2::detach()  
{
   servos[this->chanIndex].Pin.isActive = false;  
}

void ServoTimer2::write(int pulsewidth)
{      
  writeChan(this->chanIndex, pulsewidth); // call the static function to store the data for this servo          
}

int ServoTimer2::read()
{
 unsigned int pulsewidth;
  if( this->chanIndex > 0)
     pulsewidth =  servos[this->chanIndex].counter * 128 + ((255 - servos[this->chanIndex].remainder) / 2) + DELAY_ADJUST ;
  else
      pulsewidth  = 0;
  return pulsewidth;  
}

boolean ServoTimer2::attached()
{
   return servos[this->chanIndex].Pin.isActive ;
}

static void writeChan(uint8_t chan, int pulsewidth)
{
  // calculate and store the values for the given channel
  if( (chan > 0) && (chan <= NBR_CHANNELS) )   // ensure channel is valid
  {
     if( pulsewidth < MIN_PULSE_WIDTH )                // ensure pulse width is valid
         pulsewidth = MIN_PULSE_WIDTH;
     else if( pulsewidth > MAX_PULSE_WIDTH )
         pulsewidth = MAX_PULSE_WIDTH;      
     
       pulsewidth -=DELAY_ADJUST;                   // subtract the time it takes to process the start and end pulses (mostly from digitalWrite)
     servos[chan].counter = pulsewidth / 128;    
     servos[chan].remainder = 255 - (2 * (pulsewidth - ( servos[chan].counter * 128)));  // the number of 0.5us ticks for timer overflow        
  }
}

static void initISR()
{  
     for(uint8_t i=1; i <= NBR_CHANNELS; i++) {  // channels start from 1    
        writeChan(i, DEFAULT_PULSE_WIDTH);  // store default values          
     }
     servos[FRAME_SYNC_INDEX].counter = FRAME_SYNC_DELAY;   // store the frame sync period      

     Channel = 0;  // clear the channel index  
     ISRCount = 0;  // clear the value of the ISR counter;
   
     /* setup for timer 2 */
     TIMSK2 = 0;  // disable interrupts
     TCCR2A = 0;  // normal counting mode
     TCCR2B = _BV(CS21); // set prescaler of 8
     TCNT2 = 0;     // clear the timer2 count
     TIFR2 = _BV(TOV2);  // clear pending interrupts;
     TIMSK2 =  _BV(TOIE2) ; // enable the overflow interrupt        
       
     isStarted = true;  // flag to indicate this initialisation code has been executed
}


edit: replaced missing bracket in constructor
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 21, 2008, 06:30 pm
Example sketch for library:

Code: [Select]

// this sketch cycles three servos at different rates

#include <ServoTimer2.h>  // the servo library

// define the pins for the servos
#define rollPin  2
#define pitchPin 3
#define yawPin   4

ServoTimer2 servoRoll;    // declare variables for up to eight servos
ServoTimer2 servoPitch;
ServoTimer2 servoYaw;

void setup() {
 servoRoll.attach(rollPin);     // attach a pin to the servos and they will start pulsing
 servoPitch.attach(pitchPin);
 servoYaw.attach(yawPin);
}


// this function just increments a value until it reaches a maximum
int incPulse(int val, int inc){
  if( val + inc  > 2000 )
     return 1000 ;
  else
      return val + inc;  
}

void loop()
{
int val;
 
  val = incPulse( servoRoll.read(), 1);
  servoRoll.write(val);

  val =  incPulse( servoPitch.read(), 2);
  servoPitch.write(val);
 
  val = incPulse(servoYaw.read(), 4);
  servoYaw.write(val);
 
  delay(10);  
}

Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 22, 2008, 06:19 am
so i place the folder under the lib/targets/libraries  folder right?

and i compile the first code to become the servotimer2.h

the 2nd chunk of code to become the servotimer2.c

and last chunk to the normal sketch.

but i'm having a whole list of errors when i try to compile. have i done something something wrongly?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 22, 2008, 09:16 am
the second file needs to be called ServoTimer2.cpp  not .c

my bad, the file name was in the first line of the comments but I had to remove  the comments from the cpp file to fit into the 5500 character per post limit here.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 22, 2008, 09:27 am
i changed it to .cpp and included the keyword.txt file but i am still having a whole list of errors.

this is the screenshot of the debug screen.

http://axileon.com/blog/servotimer2.JPG
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 22, 2008, 10:09 am
Perhaps there is some extra text that got pasted into  the start of the ServoTimer2.cpp code.

The first line should be:
extern "C" {


and the last lines are:
  isStarted = true;  // flag to indicate this initialisation code has been executed
}


also check the .h file does not have any extra text, it sould start with a comment and end with #endif

I tried pasting the source I posted and that compiles for me.

If you still have a problem, PM me an email address and I will email a zip file to you
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 22, 2008, 02:12 pm
i re paste the code and double checked as you suggested.. i'm still getting the same error

is it becos i'm using arduino0009? are you using the latest version 10?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 22, 2008, 02:21 pm
Yes, I am using version 10. Can you try it with that?
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 22, 2008, 03:05 pm
i'm still have the same problem after upgrading to the version 10... just to make sure i'm doing the correct thing.

i place the ServoTimer2 folder with the *.cpp and *.h file inside to this folder.

C:\Arduino\arduino-0010\hardware\libraries

is that the correct thing to do?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 22, 2008, 03:24 pm
That should work if the path to the subdirectory is:

 arduino-0010\hardware\libraries\ServoTimer2

and in that ServoTimer2 directory,  you have these two files (the keyword txt file is optional):

  ServoTimer2.cpp
  ServoTimer2.h


You could try to move those two files into the same directory as the ServoTimer test sketch.
The cpp file should then appear in a tab when you open the IDE.
You need to move the files out of the library directory so the IDE wont try to compile it as a library.

Or, If you want me to try compiling the same files as you have, I will PM you my email address and you
can send me a zip of those two files. and/or I can send you a zip of the files I am using here.

edit: problem was a missing bracket in the constructor that got lost when comments stripped to squeeze into post limit. Post now edited so it compiles ok.
thanks axileon for finding that.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 24, 2008, 03:50 am
ok.. i tested the servotimer2.. with the servo.

everything is ok. servo is running very smoothly. now i have to integrate it with the RC signal

:)  i managed to get everything working!

but then the code is almost 6000 bytes... i was thinking to slim down the code a little. also becos the code that i use to store the previous value of the gyro is too long...

what i'm doing is to start integration only if the signal of the gyro is larger than the noise of the gyro itself. then it will call a function to store the gyro signal(i.e. position of the gimbal). if the gyro is moved, it will update the program with the new reading. if not it will keep output the stored(last value)

also there's also problems with accumulation of errors in the gyro data... the code is too long for posting in the forum...

i have posted my code at this url
http://axileon.com/blog/first_version.txt

is there any efficient way to store and update the gyro value? so that i can make my code more efficient?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 26, 2008, 03:45 pm
If you need to reduce the either code size (although 6k is not a problem with the atmega168) or execution time, you could use longs instead of floating point in the places it is used.
For example:
last_yaw += 1.02*yaw_smooth*sample_period/10;
could be:
last_yaw += 102L*yaw_smooth*sample_period/1000; // this avoids floating point (note 102L to force long math).

Note that your gyros and servos are probably not accuate to within 2% so there may not be any real world difference if you just did:
 last_yaw += yaw_smooth*sample_period/10;

I am a little confused by the purpose of the call_gyro function.
there is no comment saying what the arguments last and status are and what is returned from this function. The function does something different if 2000ms have passed since the sketch started to run, is this what you intend? Could you say a little more about what you want the function to do.

Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 26, 2008, 03:55 pm
well basically what i'm trying to do is this...

at the start of the program, the values will take sometime to initialize... so the time is for the program to store the error that is present after initialization because of the gyro reading n integration.

so after the timing is up, the program will store the error for the 3 channels.

so each time the gyro moves, it will store the value into the call gyro function. and if it doesnt, the program will "remember" the angle that it is at presently and pulse the servo at that particular angle...

that's why i have 3 long variables inside it.. to store the 3 variables. it will not be accessible by the main loop function. do you have a clearer understanding now?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 26, 2008, 06:18 pm
Thanks for clarifying that. It would not be difficult to add a variable to count the number of values that have been added to the gyro smoothing arrays that would indicate if the results were stable, if that was something you wanted to do.

I am still a little unclear why you need to store the gyro values. Please correct me if I misunderstand, but I thought what you wanted to do is to drive the servos so their position always matches the values received from the radio control inputs for pitch, roll and yaw. That way the platform is always in the position directed by the controller. If this is so, then if you normalize the gyro values so they are 1500 when centered , 1000 when fully in one direction and 2000 when in the other, then the servo pulse width for that axis is the difference between the two.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 26, 2008, 06:28 pm
hm... i think i am using the wrong approach...

what you describe to me seems to be what i shld be doing..

think i need to work on project again..

i wish to know.. what do you mean by normalizing the gyro values? directly peg the gyro values to the pulsewidth? but that way we dont really integrate to get the position right? would that provide the wrong resolution or something?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 26, 2008, 07:06 pm
I am suggesting you multiply the gyro output (if necessary) so it ranges from 1000 to 2000, the same as the RC input. For example, if the gyro raw signal is 500 when centered, and 0 and 1000 at the extremes of tilt, then you would simply add 1000 to the gyro reading so the value changes to the gyro will correspond with the value changes in the RC signal and the servos.

Another example: if the gyro values change from say 250 to 750 at the extremes of tilt of your platform, then you would multiply the gyro output by two before adding 1000 to the value.

The idea is if the range of values of the gyro is similar to the range of the rc input , you can drive the servo so it always tries to equal the value from the rc input.


Please let me know if I have not explained this well enough.

edit:
I just had a thought that it may be easier to work with values centered on 0 rather than 1500. If you subtract 1500 from the RC input value, that will be 0 when centered. And if you subtract 512 from the raw gyro value that will also be centered at 0. You can then add the value of the gyro to the rc input and the difference is the offset value for the servo and is added to the current servo pulse width. You may need to subtract rather than add depending on whether the  servos increase or decrease tilt as the pulse width is increased or decreased.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 29, 2008, 07:17 am
but i tot since gyros measure the rate of turning of the motion, we should be using the formula

angle += angular rate * sample_period?

in this way we will match the resolution of the gyro?

from what i understand from your description, your idea is to normalize both my gyro and my RC value so that they become the same value?

but the 1 unit change in the gyro does not equal to 1 unit change in the PWM right? please correct me if i'm wrong. i remember reading somewhere that 11 units in PWM pulsewidth  = 1 degree change in the servo?

thanks for your help so far :)
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 29, 2008, 05:28 pm
I am suggesting that if your goal is to have the servos drive the platform to follow position commands from a radio control signal with the gyro output providing feedback on the actual position of the platform , then this may be easiest to implement if you think of the servo outputs added (or subtracted) from the control signal to be an error value that is used to drive the servos.

If the gyro output values change to a greater degree then the control signal, this will result in your feedback being highly responsive (perhaps too much so). If the gyro output changes to a lesser degree than the control signal then this will make the system less responsive (less jerky but perhaps with greater error). Getting the gyro feedback in the correct proportion is one of the challenges in designing your application, and this will probably need a lot of trail and error to get right.

But perhaps I am still unclear on how you are using the output from your gryo. Could you say a little more about how you are interpreting the gyro output to determine the platform position.
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Mar 29, 2008, 06:09 pm
actually what i am doing is not very accurate.

i am using the formula that i mentioned in my previous post. to integrate the gyro angle.

so i will manual check for the null of the gyro and then input the value into my code. this part of the code is for smoothing the sensor data... after which the value of the gyro is normalized to 0.

Code: [Select]
  roll_raw = analogRead(roll_analogPin);
  roll_smooth = digital_smooth(roll_raw, roll_smooth_array);
  roll_smooth -= 310; //value of null input here


so the next part of the code is for integrating to get the angle needed to compensate the servo

Code: [Select]
if (roll_smooth > 2 || roll_smooth < -2) //the noise of the IDG300 is less than 2 LSB of ADC
//so if signal is greater than noise, then we start to integrate.
   {
     status = 1; //status ==1 imply for roll
     last_roll += 2.4*roll_smooth*sample_period/10; //the value 2.4 is obtained by using the resolution of the ADC and the gyro's sensitivity, for the IDG its about 2.4. sample period is the time period between consecutive intervals when the gyro value is updated

     roll_angle = call_gyro(last_roll, status); //after calculation, the gyro angle is passed into call_gyro to be updated. that means the old angle value is overwritten by the new angle.

   }
       else
   {
         roll_angle = roll_angle; //if gyro does not move, roll angle is not updated.
   }
   Serial.print(roll_angle);
   Serial.print("\t");


i hope my explanation is more clear and you have a better understand of what i'm doing now.

basically, the integration causes a very large error right now... that's because the system does not react appropiately during updating of the gyro value.. sometimes it takes a while for the program to stop updating the value of the angle even though the gyro has stopped moving.
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 29, 2008, 06:37 pm
Thanks for clarifying that. Not sure I can provide specific help with improving the accuracy of the gyro integration, but I hope the following comments will be of use.

If you haven't already done so, try writing a sketch that just Serial.prints  the integrated angle so you can test the accuracy of the gyro by moving it by hand to various angles. If you can't get the output to be accurate enough, you may want to consider using another sensor.

If you have to stick with using gyroscopes, perhaps consider using multiple heading hold gyros like these:  http://www.bphobbies.com/view.asp?id=A3828158&pid=A3678070.  
They do the integration in hardware, but three will set you back over $100 !!

For around $20 and a little tinkering you can get a three axis accelerometer by disassembling a wii nunchuck. There is an article here on interfacing it to the arduino: http://www.windmeadow.com/node/42.  It works great, I used one to drive the control pulses for a RC transmitter, kind of a mirror image application to the one you are building.

I highly recommend you think about using the nunchuck accelerometer if you don't want to play around with writing your own Gyro smoothing and integration code.
Title: Re: 3 axis auto stabilized platform
Post by: captFuture on Mar 31, 2008, 11:34 am
Arduino + Nunchuck + servos + RF-Receiver ...
This is actually what I am trying to do now :-)

If you follow THIS blog entry from Tod E. Kurt http://todbot.com/blog/2008/02/18/wiichuck-wii-nunchuck-adapter-available/#more-224 - there is a library that he did based on the research of the winmeadow findings.

So what I'll try now is to use the ServoTimer library with the nunchuck_funcs.h from todbot and then try to bring in the third parameter which will be the signal from the RF Receiver.

It would be best to make this as a Library too and then do the magic in the Sketch.

Cheers Chris
(currently I'm lost with the integrating of teh RF Receiver read because maybe I didn't read this thread correctly)

You can see my initial Setup and the libraries and functions that I use here
http://www.vodkadoctors.at/captFuture/Arduino_balancer/
Title: Re: 3 axis auto stabilized platform
Post by: captFuture on Mar 31, 2008, 05:38 pm
... and in addition to this I am reading into "Kalman Filters" because what I recognized that the Heli is producing too much vibration to read the data acurately.

Links:
http://tom.pycke.be/mav/92/kalman-demo-application
http://diydrones.com/profiles/blog/show?id=705844%3ABlogPost%3A19829

The plan is to produce an additional Library or function where I just send the data and get a "filtered" value back.
(But first of all I have to understand this higher mathematic equation :-))
Title: Re: 3 axis auto stabilized platform
Post by: mem on Mar 31, 2008, 06:04 pm
captFuture,  a Kalmon filter library could be quite useful, I look forward to hearing more.

Axileon, I was thinking that although accelerometers (i.e. the nunchuck) would be more suitable for detecting pitch and roll, it won't help much with yaw. So you still may need to get the gyro output integrated for at least the yaw axis. I am not sure if you have said, I wonder how large an angle will the platform move in each axis and what is the platform mounted on and over what angle can that move in each axis?
Title: Re: 3 axis auto stabilized platform
Post by: captFuture on Apr 01, 2008, 02:51 pm
Hi guys,
My project is set up and I made a blog where i post my progress as well as the links to the files.

http://www.vodkadoctors.at/captFuture/blog/

see this post for the kalman filter solution of jordi (check comments)

http://www.diydrones.com/profiles/blog/show?id=705844%3ABlogPost%3A23188

Have a nice day  :)
Title: Re: 3 axis auto stabilized platform
Post by: mem on Apr 08, 2008, 07:38 am
Axileon, haven't heard from you in a while. How is your project coming along?
Title: Re: 3 axis auto stabilized platform
Post by: axileon on Apr 09, 2008, 11:57 am
hey mem, sorry.. i've been busy with another robotic autonomous vehicle project... some ball picker robot.... totally screwed up the programming.. in the end got knocked out after 1 round. haha...

anyway..  i have to submit my report nx week. i'm tidying up the code for the project.. n will post it up once i'm done.

it works pretty fine.... but not really reliable... cos of the intregration problem... think i just spoil 1 analog servo today again.

anyway, i did purchase a 5 DOF IMU from sparkfun but i din get it to work.. 3 DOF for rotation and 2 DOF for accelerometer. it seems spoil.. maybe i did somethign to spoil it or something...

actually for my project, the rate at which the platform will be turning is something like the rate at which those R/C planes .. dont really have to be that fast...

right now i'm working on my circuit. hopefully i can do some testing tomo n post some videos/pictures.. is there a way to upload code or pictures in this forum?
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 02, 2008, 02:03 am
It seems the most elegant solution to reinterpretting servo channels would be to capture the composite signal as here described - and then to re-inject a new composite signal back into the servo stage of the receiver - having the benefits of reusing the servo pins, and demux on the receiver, and using a single PWM timer to handle the signal generation on the Arduino.

This would seem to solve both in and out problems with very little cpu waitstates.

Given that the input capture appears to be a solved problem, the remaining is driving the PWM timer as if it were a shift-register of sorts, but in the analog time domain.

My initial concept would be to create an interrupt for each change of the PWM output which would set up the PWM for the next pulse.

(This requires only two data connections between the rc and the arduino while providing full receive and control capabilities.)
One could even imagine a fail-safe mode in which the re-injection pin goes to hi-z if the arduino powers down, or reboots.

Has someone done this before? and or is the PWM-interrupt strategy any good?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 02, 2008, 05:01 am
Quote

... driving the PWM timer as if it were a shift-register of sorts, but in the analog time domain.


I am not clear on what you mean by "driving the PWM timer as if it were a shift-register of sorts, but in the analog time domain."

If you are proposing that the Timer1 code is used to measure the pulse widths and the processed output pulses are fed into timer2 code and re-injected back into the receiver to drive the servos, you could probably do this with the code in this thread and the hardware mentioned in post 3 here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1204020386/3#3. Note that that receiver is tiny and you need a steady hand to make the connections securely.

The output code (servoTimer2) would need to be initialised so that all outputs where on the same pin. Additional code needed would be a 'for loop' to read the input data, process this to the desired output pulse width, and write the data to the output pulse library.

I wonder if this is what you want to do?  It would be very interesting to hear about how you get on.

Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 02, 2008, 02:13 pm
1. By PWM - I mean using the internal timers (PWM or Otherwise) to build a signal train which matches the expected composite signal. This should be possible using a hardware timer - toggling the output pin in an overflow ISR, and setting up the timer for the next pulse length.

2. The linked "post 3" appears to solve the input capture and connection to the receiver output. - I am proposing the OTHER half of the equation - to reinject the signal train.

This could solve the Arduino challenge of a. limit of 2 servos from Hardware, or b. Partially Software driven servos with jitter.

Does anyone have recommendations (or source) on generating an arbitrary square wave using hardware timers and interrupts?

Ben
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 02, 2008, 02:47 pm
Here's a first go at the pulse train generator. (note the time domain is human-blink-rate not servo pulse)

Assuming the current values for all Servos is held in Servos[]. Then at each interrupt the next servo is loaded into MsTimer2.
It needs a reset delay and calibration. (Attribution: the code base is the sample code for MsTimer2.)


Code: [Select]
#include <MsTimer2.h>

// Switch on LED on pin 13 each second

volatile int Servos[] = {500, 1000, 1900, 100, 2000};
#define numChans  5

void flash() {
 static boolean output = HIGH;
 static int SerPoint = 0;

 MsTimer2::stop();
 
 digitalWrite(13, output);
 output = !output;
 if (output) SerPoint +=1;
 if (SerPoint > numChans)  SerPoint = 0;
 int PulseWidth = Servos[SerPoint];
 Serial.print( PulseWidth);
 
 MsTimer2::set(PulseWidth, flash); // 500ms period
 MsTimer2::start();
 
}

void setup() {
 pinMode(13, OUTPUT);
 Serial.begin(9600);  

 MsTimer2::set(500, flash); // 500ms period
 MsTimer2::start();
}

void loop() {
}
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 02, 2008, 03:04 pm
Quote

1. By PWM - I mean using the internal timers (PWM or Otherwise) to build a signal train which matches the expected composite signal. This should be possible using a hardware timer - toggling the output pin in an overflow ISR, and setting up the timer for the next pulse length.

2. The linked "post 3" appears to solve the input capture and connection to the receiver output. - I am proposing the OTHER half of the equation - to reinject the signal train.

This could solve the Arduino challenge of a. limit of 2 servos from Hardware, or b. Partially Software driven servos with jitter.

Does anyone have recommendations (or source) on generating an arbitrary square wave using hardware timers and interrupts?

Ben
Hi Ben, it's a long thread so perhaps not surprising that you didn't see it, but the 'other half' is posted started from reply #74: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1204020386/74#74

It's a library called ServoTimer2 that uses interrupts from Timer2 to produce servo pulses for eight channels. Because you want to inject a composite pulse train, you would define a single pin for all of the channels.

Let me know if its use is not clear or if you think its missing something necessary for your application.  

Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 02, 2008, 05:10 pm
Thanks,
You're right. That code (#74)  appears to sequentially pulse the Servos.
(Does it include a reset Delay)
I'll try it.
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 02, 2008, 05:27 pm
Yes, it does do the delay.

With all 8 channels pulsing at 1.5ms  (the default values for all channels) it will delay 8ms after the eighth channel before starting over on the first. If all channels are pulsing at 2ms  the sync delay will be 4ms.

If you are going to use 8 channels and they all could be pulsed at the maximum value of 2.25ms, then increase the FRAME_SYNC_PERIOD constant from 20000 to 22000 so that there will be a minimum sync delay of 4ms even in the unlikely case where all channels are pulsed at 2.2ms. But if you are using 6 channels or less, there will always be at least this delay with the posted code.

Have fun and let me know how you get on.
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 03, 2008, 04:40 am
Mem,
I co-mingled ServoTimer2 with the InputCapture Code you mentioned above.
It compiled in any case, and appears to run, so I guess their are no hardware conflicts:

I imagine incorporating both of these into a Library - do you feel input capture is within the scope of ServoTimer2?

Also, I'd like to see a compatible storage scheme for the channel values.
I tend to favor arrays - as they can better lead to self-learning/healing algorithms.

Here's the co-mingled sketch
Code: [Select]
#define icpPin            8         // this interrupt handler must use pin 8
#define TICKS_PER_uS      2          // number of timer ticks per microsecond
#define MAX_CHANNELS    8         // maximum number of channels we can store  
#define SYNC_GAP_LEN      (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)
volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)
volatile uint8_t State;         // this will be one of the following states:
#define NOT_SYNCHED_state  0    // the system is not synched so the data is random
#define ACQUIRING_state  1      // one sync pulse detected but not all channels have been received
#define READY_state     2       // synched and all channel data is valid

                 
ISR(TIMER1_CAPT_vect){
  if(! bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
      TCNT1 = 0;               // reset the counter      
      if(Channel <= MAX_CHANNELS) {
          Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
       }      
  }
  else {                          // rising  edge was detected  
       TCNT1 = 0;               // reset the counter      
       if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
           Channel = 1;       // if so, reset the channel counter to 1      
             if(State == NOT_SYNCHED_state)
                 State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
             else if( State == ACQUIRING_state)    
                  State = READY_state;           // this is the second sync so flag that channel data is valid
       }    
  }    
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge    
}


int GetChannelPulseWidth( uint8_t channel) {
 // this is the access function for channel data
 int result;  
 if( (State == READY_state)  && (channel > 0) && (channel <=  MAX_CHANNELS)  ) {
    cli();             //disable interrupts
    result =  Pulses[channel] ;
    sei();             // enable interrupts
 }
 else
    result = 0;        // return 0 if no valid pulse is available  

 return result;

}



// this sketch cycles three servos at different rates




#include <ServoTimer2.h>  // the servo library

// define the pins for the servos
#define rollPin  13
#define pitchPin 13
#define yawPin   13

ServoTimer2 servoRoll;    // declare variables for up to eight servos
ServoTimer2 servoPitch;
ServoTimer2 servoYaw;

void setup() {
 servoRoll.attach(rollPin);     // attach a pin to the servos and they will start pulsing
 servoPitch.attach(pitchPin);
 servoYaw.attach(yawPin);
 
   Serial.begin(9600);  
 pinMode(icpPin,INPUT);
 Channel = 1;            
 State = NOT_SYNCHED_state;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1

 
}


// this function just increments a value until it reaches a maximum
int incPulse(int val, int inc){
  if( val + inc  > 2000 )
     return 1000 ;
  else
      return val + inc;  
}

void loop()
{
int val;
 
  val = incPulse( servoRoll.read(), 1);
  servoRoll.write(val);

  val =  incPulse( servoPitch.read(), 2);
  servoPitch.write(val);
 
  val = incPulse(servoYaw.read(), 4);
  servoYaw.write(val);


int pulsewidth;

  // print the decoder state
  if(State == NOT_SYNCHED_state)
      Serial.println("The decoder has not detected a synch pulse ");  
  else if ( State == ACQUIRING_state)
      Serial.println("The decoder has detected one synch pulse and has started filling channel data");  
  else if( State == READY_state)
    Serial.println("The decoder is synched and the channel data is valid");  
  else
    Serial.println("Unknown decoder state, this should never happen!");
 

 // now print the channel pulse widths
 // they should be 0 if the state is not ready
 for ( int i =1; i <=4; i++ ){ // print the status of the first four channels
     Serial.print("Channel ");
     Serial.print(i);
     Serial.print(" has width ");
     pulsewidth = GetChannelPulseWidth(i);
     Serial.println(pulsewidth);
 }
  delay(10); // update 10 times a second        

 
  delay(10);  
}

Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 03, 2008, 05:45 am
It would not be difficult to turn the input capture piece into a library, but to start, you could simply move the ISR and GetChannelPulseWidth function into a separate cpp or pde file in your sketch directory.

The underlying structure is already in the servoTimer2 library to do what you want, here are some ideas on making a new version of the servoTimer2 library so its more compatible with the input capture interface:

Add another constructor to initialize use of all channels on a single pin

ServoTimer2::ServoTimer2(int pin )
{
  ChannelCount = NBR_CHANNELS;  
  pinMode( pin, OUTPUT) ;  // set encoder pin to output
     
  for(uint8_t i=1; i <= NBR_CHANNELS; i++) {  // channels start from 1    
      writeChan(i, DEFAULT_PULSE_WIDTH);  // store default values      
      servos.Pin.nbr = pin;  
      servos.Pin.isActive = true;  
  }
}

writeChannel needs to be made a public member of the ServoTimer2 class to give your sketch direct access to the existing channel array.

You may want to modify attach() so it doesn't try to set a pin (passing a pin value of 0 would probably work as a hack), but attach still needs to start the ISR.

There is probably more tweaking that will be needed, but should not be difficult to do once you are clear on how the exisitng code works.

Note that your sketch would create one instance as follows:
#define encoderPin 2
ServoTimer2 RCencoder = ServoTimer2(encoderPin);

// pulse channel 1 at 2ms
RCencoder.writeChan(1,2000);  // note that writeChan needs to be declared as a public method of the class for this to work !

Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 03, 2008, 03:21 pm
mem,
Thank you for the code and the replies.

One neat feature of the current design is that servos can be added either to the composite feedback, or forwarded to a pin, so that even more servos could be added in addition to the capacity of the receiver.

I'm thinking perhaps:
1. #IfDef section to add the input capture to ServoTimer2
2. Public Access to the ServoArray
3. a recommended receiver with instructions for modification
4. a UAV shield with Receiver, Gyro, Accel, Gps, Alt, AirSpeed and pyrometer pins. (Existing Zigbee Shield for duplex)

Ben
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 03, 2008, 03:55 pm
The current design would need to be modified slightly if more than 8 servos (actual or composite) are used in order to ensure that interframe delay was at least 3 or 4 ms. If you want to try it with more than 8 channels I can help you make the change.

I would not recommend combining the servoTimer2 output code (the decoder) and the input code (the encoder) into the same library. I can't think of any advantage in doing so and it creates extra baggage for anyone that just needs to use half the functionality. Making a new library for the input side is not difficult and although some of the constants in the header files would be similar (i.e. min and max pulse widths and frame period), they don't need to be the same for all applications so better to have separate headers for the constants. If you need help creating the header file for the input libray, I will post something to get you started, but I won't have time to write and test a new library, but will be happy to advise from the sidelines

The only receiver I have experimented with integrating with the arduino is the Hitec hf-05s I mentioned it earlier in this thread http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1204020386/3#3
It's small, inexpensive and works well, but its not easy to make the connections because of the tiny size of its PCB so its recommended only to those with experience working on small SMT boards.
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 03, 2008, 04:10 pm
mem,
To your point of "extra baggage" I thought an #Ifdef section to turn on the decoder section would save any memory that would otherwise be unnecessary. I think the header info, the structures, and the constants - in addition the need to maintain conflict-free operation might weight in favor of a single lib. - We'll see - if the shield is ever realized - it would justify a suite of supporting libs.

Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 03, 2008, 04:30 pm
Because servoTimer2 uses an 8 bit timer and the input capture uses the 16 bit timer, the underlying data structures are actually somewhat different.

The 8 bit counter uses two separate bytes (a counter and a remainder) to keep track of the pulse width. The 16 bit input code is simpler because it uses a 16 bit integer to hold the pulse width.

I certainly don't want to stop you if you feel that combining them is better for your application, but I feel for general use, keeping the input and output separate libraries is more flexible in use and easier to maintain.
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 04, 2008, 02:29 pm
Here's the Library Attempt - Sadly it doesn't Compile

ERROR OUTPUT:
In function 'void loop()':
error: request for member 'getState' in 'servoInput', which is of non-class type 'ServoInput2 ()()'


Code: [Select]
//***Save As ServoInput2.h ************************************ServoInput2*************************************
//This add-on to ServoTimer2 provides for the reading of typical RC composite servo inputs
//To save the extra memory used by this add-on library include the line:  (sharp)define omitServoInput2

#ifndef omitServoInput2
#define omitServoInput2


#define icpPin            8         // this interrupt handler must use pin 8
#define TICKS_PER_uS      2          // number of timer ticks per microsecond
#define MAX_CHANNELS    8         // maximum number of channels we can store  
#define SYNC_GAP_LEN      (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)
#define NOT_SYNCHED_state  0    // the system is not synched so the data is random
#define ACQUIRING_state  1      // one sync pulse detected but not all channels have been received
#define READY_state     2       // synched and all channel data is valid

volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)
volatile uint8_t State;         // this will be one of the following states:


class ServoInput2
{
 public:
     ServoInput2(); //Constructor

     int GetChannelPulseWidth(uint8_t);  // this is the access function for channel data
     void attach(); //Initialize
     uint8_t getState(); //State Function

private:


};



#endif
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 04, 2008, 02:30 pm
Save as ServoInput2.cpp (in Library ServoInput2)

Code: [Select]
extern "C" {
 // AVR LibC Includes
 #include <inttypes.h>
 #include <avr/interrupt.h>
 #include "WConstants.h"
}


#include "ServoInput2.h"

ISR(TIMER1_CAPT_vect)
{
  if(! bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
      TCNT1 = 0;               // reset the counter      
      if(Channel <= MAX_CHANNELS) {
          Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
       }      
  }
  else {                          // rising  edge was detected  
       TCNT1 = 0;               // reset the counter      
       if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
           Channel = 1;       // if so, reset the channel counter to 1      
             if(State == NOT_SYNCHED_state)
                 State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
             else if( State == ACQUIRING_state)    
                  State = READY_state;           // this is the second sync so flag that channel data is valid
       }    
  }    
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge
}


ServoInput2::ServoInput2()
{
 pinMode(icpPin,INPUT);
 Channel = 1;            
 State = NOT_SYNCHED_state;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}



uint8_t getState();
{
     return State;

}



int ServoInput2::GetChannelPulseWidth( uint8_t channel)
{
 // this is the access function for channel data
 int result;  
 if( (State == READY_state)  && (channel > 0) && (channel <=  MAX_CHANNELS)  ) {
    cli();             //disable interrupts
    result =  Pulses[channel] ;
    sei();             // enable interrupts
 }
 else
    result = 0;        // return 0 if no valid pulse is available  

 return result;
}

Title: Re: 3 axis auto stabilized platform - the Sketch
Post by: bGatti on Jun 04, 2008, 02:31 pm
And Here the Sketch:
Code: [Select]

// this sketch cycles three servos at different rates

#include <ServoTimer2.h>  // the servo library
#include <ServoInput2.h>

// define the pins for the servos
#define rollPin  13
#define pitchPin 13
#define yawPin   13

ServoTimer2 servoRoll;    // declare variables for up to eight servos
ServoTimer2 servoPitch;
ServoTimer2 servoYaw;
ServoInput2 servoInput();

void setup() {
 servoRoll.attach(rollPin);     // attach a pin to the servos and they will start pulsing
 servoPitch.attach(pitchPin);
 servoYaw.attach(yawPin);


 Serial.begin(9600);  

 
}


// this function just increments a value until it reaches a maximum
int incPulse(int val, int inc){
  if( val + inc  > 2000 )
     return 1000 ;
  else
      return val + inc;  
}

void loop()
{
int val;
 
  val = incPulse( servoRoll.read(), 1);
  servoRoll.write(val);

  val =  incPulse( servoPitch.read(), 2);
  servoPitch.write(val);
 
  val = incPulse(servoYaw.read(), 4);
  servoYaw.write(val);


int pulsewidth;

  // print the decoder state
  if(servoInput.getState() == NOT_SYNCHED_state)
      Serial.println("The decoder has not detected a synch pulse ");  
  else if (servoInput.getState() == ACQUIRING_state)
      Serial.println("The decoder has detected one synch pulse and has started filling channel data");  
  else if(servoInput.getState() == READY_state)
    Serial.println("The decoder is synched and the channel data is valid");  
  else
    Serial.println("Unknown decoder state, this should never happen!");
 

 // now print the channel pulse widths
 // they should be 0 if the state is not ready
 for ( int i =1; i <=4; i++ ){ // print the status of the first four channels
     Serial.print("Channel ");
     Serial.print(i);
     Serial.print(" has width ");
     pulsewidth = servoInput.GetChannelPulseWidth(i);
     Serial.println(pulsewidth);
 }
  delay(10); // update 10 times a second        

 
}


Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 04, 2008, 02:39 pm
I haven't had a chance to look closely at the code but  this change should fix that compiler error

uint8_t ServoInput2::getState();
{
  return State;
}
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 04, 2008, 07:42 pm
Good catch, I left off the class:: prefix; however, that had no effect on the error message - somehow the object "ServoInput2" is being interpreted as a type rather than a class?
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 04, 2008, 07:46 pm
Turns out I had tried declaring the class with a () at the end.
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 04, 2008, 07:58 pm
New Problem - but a logical one:


I think there's a bit of a problem accessing the Pulses array from the ISR, the Lib, and the sketch - is there a scope modifier I need?
Ben

The Error Says:
o: In function `incPulse':
multiple definition of `Pulses'hardware\libraries\ServoInput2\ServoInput2.o:D:\arduino-0011/hardware\libraries\ServoInput2\ServoInput2.cpp:11: first defined here


o: In function `incPulse':
o: In function `incPulse':
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 04, 2008, 08:00 pm
Here are some other things to look at:

in the cpp file, after #include "ServoInput2.h" add:
#include <wiring.h>
#include <avr/interrupt.h>

move the declarations of the following into the cpp file and make them static as follows
static volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
static volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)
static volatile uint8_t State;         // this will be one of the following states:

remove the semicolon from the end of
 uint8_t ServoInput2::getState()

at the bottom of the file, make an instance of the library for the user. There can only be one instance (there is support for only a single input capture in the arduino chip) so lets make it a little easer for the user
ServoInput2 ServoInput = ServoInput2() ;

in the header file add
 #include <inttypes.h>
to the top of the header file

and add
 extern ServoInput2 ServoInput;  // make an instance for the user
to the bottom , just before the endif.
you will need to remove the following from the sketch
 ServoInput2 servoInput(); // remove this because its declared in the source file.

I think you will need to fix some inconsistent capitalization between the sketch and the source files but these changes should get you close.
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 04, 2008, 10:23 pm
Mem,
Great
That compiles. appears to run - I'll need to connect it to a receiver next.
(the creation of a user instance didn't work - but I remmed, and everything else appears ok.)
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 05, 2008, 04:01 am
Mem,
I found the signal I want on the Receiver.

The code compiles with one exception:

This sketch line:

Code: [Select]
 uint8_t chan = 1;
       servoRoll.writeChan(chan,pulsewidth);



Yields this error:

o: In function `loop':
undefined reference to `ServoTimer2::writeChan(unsigned char, int)'

Which is quite surprising - where does "char" come from?

Here's the h:

Code: [Select]
class ServoTimer2
{
 public:
     // constructor:
     ServoTimer2();

     uint8_t attach(int);     // attach the given pin to the next free channel, sets pinMode, returns channel number or 0 if failure
                              // the attached servo is pulsed with the current pulse width value, (see the write method)
       void detach();
       void write(int);         // store the pulse width in microseconds (between MIN_PULSE_WIDTH and MAX_PULSE_WIDTH)for this channel
       void writeChan(uint8_t,int);     // (Channel,Pulsewidth) store the pulse width in microseconds (between MIN_PULSE_WIDTH and MAX_PULSE_WIDTH)for the

given                                     //channel
       int read();                    // returns current pulse width in microseconds for this servo
     boolean attached();      // return true if this servo is attached
private:
      uint8_t chanIndex;      // index into the channel data for this servo

};


And the method code:
Code: [Select]

static void writeChan(uint8_t chan, int pulsewidth);


static void ServoTimer2::writeChan(uint8_t chan, int pulsewidth)
{
  // calculate and store the values for the given channel
... etc


So far you've lived up to your tagline...
Any ideas here?
Ben
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 05, 2008, 08:43 am
The compiler is complaining because
 static void writeChan(uint8_t chan, int pulsewidth)
is a static function, only accessible to code in the servoTimer2.cpp source file and not a member of the servoTimer2 class.

Making it a member method would be odd because it would allow any instance to change any other instances data. I suggest you create an array of servo instances and use that to access the data like this example sketch

Code: [Select]

#include <ServoTimer2.h>

#define NBR_SERVOS 8   // this must be between 1 and NBR_CHANNELS as defined in ServoTimer2.H
byte servoPins[NBR_SERVOS] = {2,3,4,5,6,7,8,9};  // pins our servos are attached to
ServoTimer2 Servos[NBR_SERVOS]  ;

int direction[NBR_SERVOS];   // 1 is forward, -1 is reverse

void setup() {
 for( int i =0; i < NBR_SERVOS; i++){
      direction[i] = 1;  //  forward
      Servos[i].attach(servoPins[i]);
      Servos[i].write( 800 + (i * 200));  // write some test data       
   }    
}
 
void loop() {
 int pulseWidth;

   //sweep all the servos back and forth
   for( int i=0; i <  NBR_SERVOS; i++){
       pulseWidth = Servos[i].read();        
       if(pulseWidth >= MAX_PULSE_WIDTH){
           direction[i] = -1;
       }
       if (pulseWidth <= MIN_PULSE_WIDTH) {
            direction[i] = 1;    
       }
      Servos[i].write(pulseWidth + direction[i]);      
   }
   delay(10);
}

uint8_t is another name for an unsigned char, it's short for u(unsigned)int(integer)8(eight bits)_t(it's a type). I tend to use byte instead of uint8_t these days but either more clearly express their purpose than 'a character without a sign'. All three forms are the same to the compiler
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 05, 2008, 03:48 pm
Great!
It compiles. I'll hook it up next and test some servos.
I changed uint8_t to byte universally.

one nice feature is that it supports 4 kinds of servo channels:

1. receiver-based servos
2. virtual servos (channels that get used to switch on the UAV code - or adjust software gyro gain for example) and
3. pin-tied servos - in excess of the number of channels supported by the receiver
4. non-servo peripherals - for example an ir transmitter that drives a camera based on a virtual servo.

simply assign servos to the feedback pin here (2), to nothing (0) or to some real pin

Code: [Select]
byte servoPins[NBR_SERVOS] = {2,2,2,2,0,0,8,9};  // pins our servos are attached to


More when its attached...
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 05, 2008, 04:23 pm
Good to hear your making progress, looking forward to seeing more  :)

0 is a real Arduino pin so if you attach a servo assigned to pin 0 it will write to that pin. Pin 0 is usually used  by the serial port on the Arduino so don't actually attach an instance using pin 0 if your board has a serial interface/USB.

Your sketch could check and not call the attach method if the pin is 0 (or 1)
Code: [Select]

void setup() {
 for( int i =0; i < NBR_SERVOS; i++){
     if(servoPins[i] >1)
         Servos[i].attach(servoPins[i]); // this will start pulsing the pin!
     Servos[i].write( 800 + (i * 200));  // writing to an unattched pin is ok
   }    
}
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 06, 2008, 06:06 am
Foiled for tonight.
I have a Hitec NFS-04MG 4 channel receiver; the output stage is a 4015 shift register. The composite signal appears on one topside trace, but it appears not to be separable. (It's not a straight shot into the shift - since the reset RC timer is all tangled up in the composite line.

I think i shouldn't waste more time on this receiver - it's old school - I'd like to "crack" a receiver which is more widely available. I've got a nice GWS nano 6 chan I think I'll break open next.
In order to use this model - it might work to add a reset line on a separate pin, but that misses the point...

Ben
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 07, 2008, 03:38 am
Mem,
I think you mentioned having a Hitec Micro O5S
- do you think it possible to cut the composite trace and get a small wire on both the PIC input pin, and the radio signal out?
I have some other receivers - but I don't have a clearly disambiguated composite trace isolated.
that receiver is $23 at Tower hobby and quite small. It would be an excellent candidate generally...

Ben
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 07, 2008, 05:49 am
The 05S should be suitable, you can find info on the composite signal locations in a post early in this thread. I tested the input capture code using the composit signal output from this receiver. I didn't try to inject a signal but I think you may be able to lift the pins of the SMT PIC chip if you have a small enough soldering iron. The traces are tiny so it will need a steady hand and good eyesight.
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 08, 2008, 04:36 pm
Hi to all!! Im new arduino fun, its one month i try to understand PPM/PWN and R/C transmission. I read MEM & Axeil post to know more about it, also i found an oscilloscope to investigate on my R/C Hitec Focus 4.
My idea is to create a UAV (QuadCopter) like other project on Internet ;-) but im not a good elettronic/programmer... so i follow the new post from Bgalliand as i can understand hw wants make the same goal of me.
At the moment my progress are:
1) Found the pin on receiver (Hitec Focus4) where arrived the PPM signal (4 Channel)
2) i used the old code from MEM/Axeil to read PPM without multiplex only attached to pin 8, but i can read all 4 CH without dirty and wrong order capture.

My idea is to use 4 Brushless motor to power the QuadCopter and an Accelorometer to correct the fly, perhaps the future is to have much more but at the moment is enough.
I ask for an help from where i need to move now, I can read the PPM and pulse the Brushless (with ESC controller) without other component? is need a multiplexer to have a good PPM signal?
Many thanks for your great post!!!
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 08, 2008, 05:35 pm
hi DiMiz, axileon needed the multiplexer because he wanted to use a receiver that did not output the channels in sequence. If your hitec 4 channel receiver works like my hitec 5 channel one then you can skip the multiplexer and use the recent decoder code, I think this is the latest:

ServoInput.h
Code: [Select]

//*****************ServoInput2.h*************************************
#ifndef ServoInput2_H
#define ServoInput2_H

#include <inttypes.h>

#define icpPin            8   // this interrupt handler must use pin 8
#define TICKS_PER_uS      2   // number of timer ticks per microsecond
#define MAX_CHANNELS    8   // maximum number of channels we can store  
#define SYNC_GAP_LEN      (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)
#define NOT_SYNCHED_state  0    // the system is not synched so the data is random
#define ACQUIRING_state  1      // one sync pulse detected but not all channels have been received
#define READY_state     2       // synched and all channel data is valid

class ServoInput2
{
 public:
     ServoInput2(); //Constructor
     int GetChannelPulseWidth(uint8_t);  // this is the access function for channel data
     uint8_t getState(); //State Function

private:
};

#endif


servoInput.cpp
Code: [Select]

//******** servoInput.cpp *********

#include "ServoInput2.h"
#include <wiring.h>
#include <avr/interrupt.h>


static volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
static volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)
static volatile uint8_t State;         // this will be one of the following states:



ISR(TIMER1_CAPT_vect)
{
  if(! bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
      TCNT1 = 0;               // reset the counter      
      if(Channel <= MAX_CHANNELS) {
          Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
       }      
  }
  else {                          // rising  edge was detected  
       TCNT1 = 0;               // reset the counter      
       if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
           Channel = 1;       // if so, reset the channel counter to 1      
             if(State == NOT_SYNCHED_state)
                 State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
             else if( State == ACQUIRING_state)      
                  State = READY_state;           // this is the second sync so flag that channel data is valid
       }    
  }      
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge
}


ServoInput2::ServoInput2()
{
 pinMode(icpPin,INPUT);
 Channel = 1;            
 State = NOT_SYNCHED_state;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}


uint8_t ServoInput2::getState()  
{
 return State;
}


int ServoInput2::GetChannelPulseWidth( uint8_t channel)
{
 // this is the access function for channel data
 int result;  
 if( (State == READY_state)  && (channel > 0) && (channel <=  MAX_CHANNELS)  ) {
   cli();             //disable interrupts
   result =  Pulses[channel] ;
   sei();             // enable interrupts
 }
 else
   result = 0;        // return 0 if no valid pulse is available  

 return result;
}
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 09, 2008, 01:54 pm
Hi MEM!! Many thanks for your fast reply to my help, I try this code this evening. As i can see this is function and simple Arduino skecth, how can i use it? Also i can debug with Serial output the PulseWidth?
Many thanks i will report my test.
Sincerelly
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 09, 2008, 02:01 pm
Reply #116 has a sketch that sends the pulse width to the serial port. If you can try to post an outline of the functionality you want for your sketch,  I or others here can help you more with the code.
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 09, 2008, 02:54 pm
Thanks Mem i will try the sketch from post 116, i think is what i need now to test R/C transmission and PPM conversion.
My generic outline for what i want is:
1- Grab R/C command from transmitter
2- Receive PPM signal decode it and match Accelorometer XYZ variation to stabilized the QuadCopter
3- Pulse 4 Brushless motor using the ESC circuit (The ESc is already done :-))

At the moment if can decode without problem the PPM signal i think is great step :-)
Many thanks this evening i will report my improvements, also you never made a Helicopter like my project?
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 10, 2008, 01:19 am
Ok is very late in Italy, but im very excited with my project so i describe what i have reached...not too much:)
I copy and paste your Libray in arduino-0011/hardware/libraries/ServoInput2
ServoInput2.h
Code: [Select]
//*****************ServoInput2.h*************************************
#ifndef ServoInput2_H
#define ServoInput2_H

#include <inttypes.h>

#define icpPin          8   // this interrupt handler must use pin 8
#define TICKS_PER_uS    2   // number of timer ticks per microsecond
#define MAX_CHANNELS    8   // maximum number of channels we can store  
#define SYNC_GAP_LEN    (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)
#define NOT_SYNCHED_state  0    // the system is not synched so the data is random
#define ACQUIRING_state  1      // one sync pulse detected but not all channels have been received
#define READY_state     2        // synched and all channel data is valid

class ServoInput2
{
 public:
       ServoInput2(); //Constructor
       int GetChannelPulseWidth(uint8_t);  // this is the access function for channel data
       uint8_t getState(); //State Function

private:
};

extern ServoInput2 ServoInput;  // make an instance for the user
#endif

and ServoInput2.cpp
Code: [Select]
//******** servoInput.cpp *********

extern "C" {
       // AVR LibC Includes
#include <inttypes.h>
#include <avr/interrupt.h>
#include "WConstants.h"
       }

#include "ServoInput2.h"
#include <wiring.h>
//#include <avr/interrupt.h>


static volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
static volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)
static volatile uint8_t State;         // this will be one of the following states:



ISR(TIMER1_CAPT_vect)
{
  if(! bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
      TCNT1 = 0;               // reset the counter      
      if(Channel <= MAX_CHANNELS) {
          Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
       }      
  }
  else {                          // rising  edge was detected  
       TCNT1 = 0;               // reset the counter      
       if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
           Channel = 1;       // if so, reset the channel counter to 1      
             if(State == NOT_SYNCHED_state)
                 State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
             else if( State == ACQUIRING_state)      
                  State = READY_state;           // this is the second sync so flag that channel data is valid
       }    
  }      
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge
}


ServoInput2::ServoInput2()
{
 pinMode(icpPin,INPUT);
 Channel = 1;            
 State = NOT_SYNCHED_state;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}


uint8_t ServoInput2::getState()  
{
 return State;
}


int ServoInput2::GetChannelPulseWidth( uint8_t channel)
{
 // this is the access function for channel data
 int result;  
 if( (State == READY_state)  && (channel > 0) && (channel <=  MAX_CHANNELS)  ) {
   cli();             //disable interrupts
   result =  Pulses[channel] ;
   sei();             // enable interrupts
 }
 else
   result = 0;        // return 0 if no valid pulse is available  

 return result;
}

ServoInput2 ServoInput = ServoInput2() ;


As you can see i have made some mod using your post #121 with bGatti because i found some undefined error when compile
and my sketch is
follow in the next post...
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 10, 2008, 01:24 am
ok my sketch is
Code: [Select]
// this sketch cycles three servos at different rates

#include <ServoTimer2.h>  // the servo library
#include <ServoInput2.h>


// define the pins for the servos
#define rollPin  13
#define pitchPin 13
#define yawPin   13

ServoTimer2 servoRoll;    // declare variables for up to eight servos
ServoTimer2 servoPitch;
ServoTimer2 servoYaw;
// ServoInput2 ServoInput();  // remove this because its declared in the source file.

void setup() {
 servoRoll.attach(rollPin);     // attach a pin to the servos and they will start pulsing
 servoPitch.attach(pitchPin);
 servoYaw.attach(yawPin);


 Serial.begin(9600);  

 
}


// this function just increments a value until it reaches a maximum
int incPulse(int val, int inc){
  if( val + inc  > 2000 )
     return 1000 ;
  else
      return val + inc;  
}

void loop()
{
int val;
 
  val = incPulse( servoRoll.read(), 1);
  servoRoll.write(val);

  val =  incPulse( servoPitch.read(), 2);
  servoPitch.write(val);
 
  val = incPulse(servoYaw.read(), 4);
  servoYaw.write(val);


int pulsewidth;

  // print the decoder state
  if(ServoInput.getState() == NOT_SYNCHED_state)
      Serial.println("The decoder has not detected a synch pulse ");  
  else if (ServoInput.getState() == ACQUIRING_state)
      Serial.println("The decoder has detected one synch pulse and has started filling channel data");  
  else if(ServoInput.getState() == READY_state)
     Serial.println("The decoder is synched and the channel data is valid");  
  else
     Serial.println("Unknown decoder state, this should never happen!");
 

 // now print the channel pulse widths
 // they should be 0 if the state is not ready
 for ( int i =1; i <=4; i++ ){ // print the status of the first four channels
     Serial.print("Channel ");
     Serial.print(i);
     Serial.print(" has width ");
     pulsewidth = ServoInput.GetChannelPulseWidth(i);
     Serial.println(pulsewidth);
 }
  delay(10); // update 10 times a second        

 
}


but my debug is:
---
The decoder has not detected a synch pulse

Channel 1 has width 0

Channel 2 has width 0

Channel 3 has width 0

Channel 4 has width 0

The decoder has not detected a synch pulse

---
About my R/C Signal:
1- i Have found from oscilloscope the PPM frame and seems of 10ms not the usually 20ms
2- the sync frame seems 440/450us not 3000 as you defined in ServoInput2.h, i have try to mod but not seems to affect nothing...

Many thanks for possible help, if you need other info please ask me!!
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 10, 2008, 01:36 am
Also some image of my PPM signal (http://img238.imageshack.us/img238/6467/ppmhitecfocu4os5.jpg)
and my Arduino & R/C receiver attached...
(http://img108.imageshack.us/img108/2816/arduinhitecfocu4receivebj2.jpg)

I hope my info is usefull
Thanks Dimi
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 10, 2008, 05:09 am
DiMaz,

I notice in your Oscope that your signal is an inversion of my signal.
This is not to say yours is upside down, only something to consider.

I think you might benefit from inverting the interupt code.

For reference, this version compiled:

Code: [Select]
//***Save As ServoInput2.h ************************************ServoInput2*************************************
//This add-on to ServoTimer2 provides for the reading of typical RC composite servo inputs
//To save the extra memory used by this add-on library include the line:  (sharp)define omitServoInput2

#ifndef omitServoInput2
#define omitServoInput2

#include <wiring.h>
#include <avr/interrupt.h>
#include <inttypes.h>

#define NOT_SYNCHED_state  0    // the system is not synched so the data is random
#define ACQUIRING_state  1      // one sync pulse detected but not all channels have been received
#define READY_state     2       // synched and all channel data is valid


class ServoInput2
{
 public:
     ServoInput2(); //Constructor

     uint8_t getState(); //State Function
     int GetChannelPulseWidth(uint8_t);  // this is the access function for channel data
     void attach(); //Initialize

private:


};

//extern ServoInput2 ServoInput;  // make an instance for the user

#endif


Code: [Select]

extern "C" {
 // AVR LibC Includes
 #include <inttypes.h>
 #include <avr/interrupt.h>
 #include "WConstants.h"
}


#include "ServoInput2.h"

#define icpPin            8         // this interrupt handler must use pin 8
#define TICKS_PER_uS      2          // number of timer ticks per microsecond
#define MAX_CHANNELS    8         // maximum number of channels we can store  
#define SYNC_GAP_LEN      (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)


static volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds  
static volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)
static volatile uint8_t State;         // this will be one of the following states:




ISR(TIMER1_CAPT_vect)
{
  if(! bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
      TCNT1 = 0;               // reset the counter      
      if(Channel <= MAX_CHANNELS) {
          Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
       }      
  }
  else {                          // rising  edge was detected  
        digitalWrite( 13,HIGH); //prove detect on high

       TCNT1 = 0;               // reset the counter      
       if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
           Channel = 1;       // if so, reset the channel counter to 1      
             if(State == NOT_SYNCHED_state)
                 State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
             else if( State == ACQUIRING_state)    
                  State = READY_state;           // this is the second sync so flag that channel data is valid
       }    
  }    
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge
}


ServoInput2::ServoInput2()
{
 pinMode(icpPin,INPUT);
 Channel = 1;            
 State = NOT_SYNCHED_state;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}



uint8_t ServoInput2::getState()
{
  return State;
}



int ServoInput2::GetChannelPulseWidth( uint8_t channel)
{
 // this is the access function for channel data
 int result;  
 if( (State == READY_state)  && (channel > 0) && (channel <=  MAX_CHANNELS)  ) {
    cli();             //disable interrupts
    result =  Pulses[channel] ;
    sei();             // enable interrupts
 }
 else
    result = 0;        // return 0 if no valid pulse is available  

 return result;
}

//ServoInput2 ServoInput = ServoInput2() ;





And the Sketch:
[code]

// this sketch cycles three servos at different rates

#include <ServoTimer2.h>  // the servo library
#include <ServoInput2.h>

// define the pins for the servos
#define rollPin 13
#define NBR_SERVOS 8   // this must be between 1 and NBR_CHANNELS as defined in ServoTimer2.H
byte servoPins[NBR_SERVOS] = {9,9,9,9,0,0,9,9};  // pins our servos are attached to
ServoTimer2 Servos[NBR_SERVOS]  ;
ServoInput2 ServoInput;

int direction[NBR_SERVOS];   // 1 is forward, -1 is reverse

void setup() {
 for( int i =0; i < NBR_SERVOS; i++){
       direction = 1;  //  forward
         if(servoPins >1)
           Servos.attach(servoPins); // this will start pulsing the pin!

//        Servos.write( 800 + (i * 200));  // writing to an unattched pin is ok

//       Servos.write( 800 + (i * 200));  // write some test data       
 Serial.begin(9600);  
   }    
}
 

void loop()
{
 
 // print the decoder state
  if(ServoInput.getState() == NOT_SYNCHED_state)
      Serial.println("The decoder has not detected a synch pulse ");  
  else if (ServoInput.getState() == ACQUIRING_state)
      Serial.println("The decoder has detected one synch pulse and has started filling channel data");  
  else if(ServoInput.getState() == READY_state)
    Serial.println("The decoder is synched and the channel data is valid");  
  else
    Serial.println("Unknown decoder state, this should never happen!");

 // now print the channel pulse widths
 // they should be 0 if the state is not ready
 int pulseWidth;

   //sweep all the servos back and forth
   for( int i=0; i <  NBR
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 10, 2008, 05:11 am
Looks like the Sketch was truncated:

Here again - the Sketch:

Code: [Select]


// this sketch cycles three servos at different rates

#include <ServoTimer2.h>  // the servo library
#include <ServoInput2.h>

// define the pins for the servos
#define rollPin 13
#define NBR_SERVOS 8   // this must be between 1 and NBR_CHANNELS as defined in ServoTimer2.H
byte servoPins[NBR_SERVOS] = {9,9,9,9,0,0,9,9};  // pins our servos are attached to
ServoTimer2 Servos[NBR_SERVOS]  ;
ServoInput2 ServoInput;

int direction[NBR_SERVOS];   // 1 is forward, -1 is reverse

void setup() {
 for( int i =0; i < NBR_SERVOS; i++){
       direction[i] = 1;  //  forward
         if(servoPins[i] >1)
           Servos[i].attach(servoPins[i]); // this will start pulsing the pin!

//        Servos[i].write( 800 + (i * 200));  // writing to an unattched pin is ok

//       Servos[i].write( 800 + (i * 200));  // write some test data       
 Serial.begin(9600);  
   }    
}
 

void loop()
{
 
 // print the decoder state
  if(ServoInput.getState() == NOT_SYNCHED_state)
      Serial.println("The decoder has not detected a synch pulse ");  
  else if (ServoInput.getState() == ACQUIRING_state)
      Serial.println("The decoder has detected one synch pulse and has started filling channel data");  
  else if(ServoInput.getState() == READY_state)
    Serial.println("The decoder is synched and the channel data is valid");  
  else
    Serial.println("Unknown decoder state, this should never happen!");

 // now print the channel pulse widths
 // they should be 0 if the state is not ready
 int pulseWidth;

   //sweep all the servos back and forth
   for( int i=0; i <  NBR_SERVOS; i++){
         pulseWidth = ServoInput.GetChannelPulseWidth(i);
       //pulseWidth = Servos[i].read();
       
       if(pulseWidth >= MAX_PULSE_WIDTH){
           direction[i] = -1;
       }
       if (pulseWidth <= MIN_PULSE_WIDTH) {
            direction[i] = 1;    
       }
      Servos[i].write(pulseWidth + direction[i]);      
   }
  delay(10); // update 10 times a second        
}



End Code
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 10, 2008, 11:46 am
Hi bGatti thanks for your reply, about my Oscope signal probably i invert the signal function ;-) im not sure last was very late so probably i have made some error on using my Oscope...but i will try your code to test my R/C receive.
Are try to create a QuadCopter or something similar? If im able to capture my R/C command my second step is use a accelorometer to stabilized the copter, have made some work on it?
Many thanks
Ps: Using your posted code are you able to Capture the signal without problem?
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 10, 2008, 12:15 pm
Hi DiMiz, looking at your scope trace I think the version below may work for you. Try it and let us know if if finds the sync and what output pulse widths are reported on the serial port

Code: [Select]


ISR(TIMER1_CAPT_vect)
{
 if(! bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
   TCNT1 = 0;               // reset the counter      
   if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
     Channel = 1;       // if so, reset the channel counter to 1      
     if(State == NOT_SYNCHED_state)
       State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
     else if( State == ACQUIRING_state)      
       State = READY_state;           // this is the second sync so flag that channel data is valid
   }    
   else if(Channel <= MAX_CHANNELS) {  // else its channel pulse so save it
     Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
   }      
 }
 else {                          // rising  edge was detected  
   TCNT1 = 0;               // reset the counter      

 }      
 TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge
}
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 10, 2008, 12:46 pm
This code is functionally similar to the above but I hope it is easier to understand and customize.  

Code: [Select]

#define PULSE_STARTS_ON_RISING_EDGE   // un-comment this if the output goes positive for duration of the pulse
//#define PULSE_STARTS_ON_FALLING_EDGE   // un-comment this if the output goes to 0 for duration of the pulse


ISR(TIMER1_CAPT_vect)
{
// we want to measure the time to the end of the pulse
#if defined PULSE_STARTS_ON_RISING_EDGE
 if(bit_is_clear(TCCR1B ,ICES1)){       // was falling edge detected ?  
#else  
 if( bit_is_set(TCCR1B ,ICES1)){       // was rising edge detected ?
#endif  
   TCNT1 = 0;               // reset the counter      
   if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
     Channel = 1;       // if so, reset the channel counter to 1      
     if(State == NOT_SYNCHED_state)
       State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
     else if( State == ACQUIRING_state)      
       State = READY_state;           // this is the second sync so flag that channel data is valid
   }    
   else if(Channel <= MAX_CHANNELS) {  // else its channel pulse so save it
     Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
   }      
 }
 else {        // the start of the pulse was detected  
   TCNT1 = 0;        // so reset the counter to start timeing

 }      
 TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge
}


I have not run the code in this form so please report if you have problems.
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 10, 2008, 01:45 pm
Many thanks Mem I will test it and report my progress this night, thanks so much!!!
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 10, 2008, 03:21 pm
Mem,
I noticed a shift in the pulse direction.
Do you think we could add "auto-shift-detection"? That's a pretty common feature of modern receivers.
I'm thinking measure each input change and set the shift bit based on the longest state.

Ben
Title: Re: 3 axis auto stabilized platform
Post by: bGatti on Jun 10, 2008, 03:25 pm
DiMiz,
Yes, I made good progress on 3 Axis Accel. I used the cheapest 3 Axis part from Sparkfun (~$19 on breakout board). I provided Sketch and hookup.

Also, your signal is probably inverted (not your scope). You'll need to use mem's invertible code.

Ben
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 10, 2008, 04:57 pm
Hi bGatti what is the part number of Accelometer, so i try to find a reseller in Italy?
Many thanks for help i test Mem code this night
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 10, 2008, 06:38 pm
Quote
Do you think we could add "auto-shift-detection"?
Ben

Here is a version that checks to see if an 'inverted' sync pulse is found when acquiring the signal and inverts the mode accordingly. Its completly untested but you can try it if you want.

Code: [Select]

#define PULSE_START_ON_RISING_EDGE  0
#define PULSE_START_ON_FALLING_EDGE 1
volatile byte pulseEnd = PULSE_START_ON_RISING_EDGE ; // default value

ISR(TIMER1_CAPT_vect)
{
// we want to measure the time to the end of the pulse

 if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == pulseEnd ){
   TCNT1 = 0;               // reset the counter      
   if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
     Channel = 1;       // if so, reset the channel counter to 1      
     if(State == NOT_SYNCHED_state)
       State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
     else if( State == ACQUIRING_state)      
       State = READY_state;           // this is the second sync so flag that channel data is valid
   }    
   else if(Channel <= MAX_CHANNELS) {  // else its channel pulse so save it
     Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
   }      
 }
 else {        // the start of the pulse was detected  
   TCNT1 = 0;        // so reset the counter to start timing
   if( State != READY_state && (ICR1 >= SYNC_GAP_LEN) ){   // are we trying to acquire a frame and have we found a sync pulse
       pulseMode ^= 1; // found sync on the wrong end of the pulse so invert the mode    
   }
 }      
 TCCR1B ^= _BV(ICES1);         // toggle bit value to trigger on the other edge
}

Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 10, 2008, 10:37 pm
Ok im back ;-)
I have tested every Mod post by Mem using my posted files (ServoInput2.h,ServoInput2.cpp) and also the files posted by bGatti, with the mod to detect inverted signal, but the same thing..
----
The decoder has not detected a synch pulse
Channel 1 has width 0
Channel 2 has width 0
Channel 3 has width 0
Channel 4 has width 0
----
How can i help to debug the problem?
I here to keep every suggest :-)
Thanks
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 10, 2008, 10:50 pm
Update!! Only to test my R/C receiver i used the old code

Code: [Select]
#define icpPin            8   // this interrupt handler must use pin 8
#define TICKS_PER_uS      2    // number of timer ticks per microsecond
#define MAX_CHANNELS    4         // maximum number of channels we want  
volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
volatile uint8_t Channel;      // number of channels detected so far in the frame (first channel is 1)

boolean DataAvailable; // set to true when we have received data for all channels
boolean Ready;         // true when we are ready to detect the leading edge of a channel

int RefreshTime = 2;
int lastPulse;

int a = 13;
int b = 12;
int c = 11;
int g = 6;

// here is the logic of how this code works :
// to start, channel is set to 1 and ready to false and the DataAvailable flag is false
// when a negative edge is detected ready is set to true
// when a positive edge is detected and ready is true then the measurement starts
// when a negative edge is detected and the ready flag is true then the measurement ends, the ready flag is reset back to false
//  and the channel is incremented. If the channel count after incrementing is greater than the number of channels then
//  the DataAvailable flag is set true and pulse data can be accessed

ISR(TIMER1_CAPT_vect){
  if( !bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
        if(Ready) {
             Pulses[Channel] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
                if (Pulses[Channel] > 10000)
                    {
                      Channel=1;
                     } else {
                       Channel++;
                     }  
                 if(Channel > MAX_CHANNELS)
                       //if((Pulses[Channel]< 10000) && (++Channel > MAX_CHANNELS)) //Add to check last edge DIMI
                   {
                   Channel = 1;       //reset the channel counter to 1      
                   DataAvailable = true;  // this line is needed to indicate that the channel data is valid!!!!
                   }
             //SelectChannel( Channel); // Remove by DIMI
             // Add code here to gate the multiplexer to the current channel            
        }
        Ready = !Ready;  //toggle the ready flag
  }
  else {                       // rising  edge was detected  
       TCNT1 = 0;               // reset the counter      
                         
  }      
  TCCR1B ^= _BV(ICES1);                 // toggle bit value to trigger on the other edge    
}

void setup()                    // run once, when the sketch starts
{
 Serial.begin(9600);  
 
 pinMode(icpPin,INPUT);
     
 pinMode(g,OUTPUT);
 pinMode(a,OUTPUT);
 pinMode(b,OUTPUT);
 pinMode(c,OUTPUT);
 pinMode(2,OUTPUT);
 pinMode(3,OUTPUT);
 pinMode(4,OUTPUT);
 pinMode(7,OUTPUT);
 
 Channel = 1;            
 Ready = false;
 DataAvailable = false;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set)
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
 
}

void SelectChannel( int channel){
   digitalWrite(g,HIGH);  // turn the output off while twiddling select bits
   if(Channel == 1)
   {
      digitalWrite(a,LOW);
      digitalWrite(b,LOW);            
   }
   else if(Channel == 2)
   {
      digitalWrite(a,HIGH);
      digitalWrite(b,LOW);
   }
     else if(Channel == 3)
     {
      digitalWrite(a,LOW);
      digitalWrite(b,HIGH);
     }
     digitalWrite(c,LOW);   // c is always low

     digitalWrite(g,LOW);  // turn output on    
}


int GetChannelPulseWidth(uint8_t Channel) {
 // this is the access function for channel data
 int result;  
 if( DataAvailable  && (Channel > 0) && (Channel <=  MAX_CHANNELS)  ) {
     cli();             //disable interrupts
     result =  Pulses[Channel] ;
     sei();             // enable interrupts
 }
 else
     result = 0;        // return 0 if no valid pulse is available  

 return result;

}

void loop()                     // run over and over again
{
 
int pulsewidth;

 
     // print the decoder state
  if(DataAvailable == false)
     {// Serial.println("The decoder has not detected all channels ");  
}
  else  
       {

/*       PulseServo(1,GetChannelPulseWidth(1));
      delay(6);
        PulseServo(4,GetChannelPulseWidth(4));
      delay(6); */
  }    
 
 // now print the channel pulse widths
 // they should be 0 if the state is not ready
 for ( int i =1; i <=MAX_CHANNELS; i++ ){ // print the status of the first three channels
       delay(360);
     Serial.print("Channel ");
     Serial.print(i);
     Serial.print(" has width ");
     pulsewidth = GetChannelPulseWidth(i);
     Serial.println(pulsewidth);
 }

}

void PulseServo(int channel,int pulsewidth)
{
 //   your code here to pulse the servo for the given channel with the given pulse width
 // you should check that pulsewidth is within the valid range for your servos

 int pin_num = 0;
 
 pin_num = channel + 1;

 if ((pulsewidth <= 2200) && (pulsewidth >= 600))
 {
   digitalWrite(pin_num, HIGH);  
   delayMicroseconds(pulsewidth);
   digitalWrite(pin_num, LOW);
 }

/*  Serial.print("chan ");
 Serial.print(channel);
 Serial.print("=");
 Serial.print(pulsewidth);
 Serial.print(", ");
 Serial.println();*/

}


and the Output is:
---
Channel 1 has width 872
Channel 2 has width 871
Channel 3 has width 874
Channel 4 has width 838
---
Seems to report the correct value of 0.8ms in null sticks position i also tested the width when i move and works not great but grab the signal :-) I try to go deep in the code..
Thanks
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 10, 2008, 10:51 pm
Quote
Ok im back ;-)
I have tested every Mod post by Mem using my posted files (ServoInput2.h,ServoInput2.cpp) and also the files posted by bGatti, with the mod to detect inverted signal, but the same thing..

How can i help to debug the problem?
I here to keep every suggest :-)
Thanks


add a serial write command after the following line

   if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
       Serial.writeln("normal sync"); // <- add this  
   
and if you are using the last version posted, add the serial write command as follows:
       
   if( State != READY_state && (ICR1 >= SYNC_GAP_LEN) ){  
        pulseMode ^= 1; // found sync on the wrong end of the pulse so invert the mode    
        Serial.writeln("inverted sync"); // <- add this line
   }    

then try a run and see if either of these messages are sent
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 10, 2008, 11:21 pm
Hi Mem!
I have tested your suggest debug line on your latest code (autodetct signal) but still no result:
---
Channel 1 has width 0
Channel 2 has width 0
Channel 3 has width 0
Channel 4 has width 0
The decoder has not detected a synch pulse
--------
Perhaps is possible some wrong call to ServoInput2.cpp or ServoInput2.h that prevent to use inside function? To test this idea i have put Serial Print on:
---
Code: [Select]

 if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == pulseEnd ){
   Serial.writeln("only to test"); // <- Debug Line
   TCNT1 = 0;               // reset the counter      

---
But i dont see nothing, also every time i mode the header i close Arduino IDE and reload.
Another idea the line:
---
#define SYNC_GAP_LEN      (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)
---
How is important? As i can understand this is the min Sync Time, in my PPM case seems 800us + 440us is possible?
Many thanks

UPDATE!!
if i remove the object file (ServoInput2.o) the IDE seems to re-parse the .cpp & .h files, and i found this mex error:
---
ServoInput2.cpp:34: error: 'Serial' was not declared in this scope
---
I dont know..
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 10, 2008, 11:49 pm
Quote

#define SYNC_GAP_LEN      (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)
---
How is important? As i can understand this is the min Sync Time, in my PPM case seems 800us + 440us is possible?


SYNC_GAP_LEN is defined as a pulse greater than 3000 microseconds, I think this is ok for your receiver?


An another point, it would be interesting to see what happens if in the code that worked could you change:    
Code: [Select]


ISR(TIMER1_CAPT_vect){
  if( !bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
     if(Ready) {
          Pulses[Channel] = ICR1 / TICKS_PER_uS;  // store pulse length as microseconds

         
to            
Code: [Select]

ISR(TIMER1_CAPT_vect){
//   if( !bit_is_set(TCCR1B ,ICES1)){       // was falling edge detected ?  
  if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == 0 ){
     if(Ready) {
          Pulses[Channel] = ICR1 / TICKS_PER_uS;  // store pulse length as microseconds


       
and see if both versions give the same results

I will be logging off now but will do some more work on this tomorrow
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 11, 2008, 12:16 am
Hi Mem thanks a lot
I have change the new line in the old code and produce the same output:
---
Channel 1 has width 869

Channel 2 has width 868

Channel 3 has width 877

Channel 4 has width 1292

----
Semi correct value, also i have change on new code the value SYNC_GAP_LEN to 80 and the output is
---
Channel 1 has width 40
Channel 2 has width 0
Channel 3 has width 0
Channel 4 has width 0
---
and when move the sticks change only channel 1...
Boh.. :-)
Many thanks for your interest and have nice night
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 11, 2008, 02:44 pm
I realized for autodetect to work, there are four possible modes that need to be recognized.

1.      channel pulses positive going, sync level also positive
2.      channel pulses positive, sync level is 0
3.      channel pulses go to 0 for duration, sync level also goes to 0
4.      channel pulses go to 0 for duration, sync goes positive.

The following code fragment should detect this if the sync level is at least 3000us and the channel pulses are at least 750us and less than 3000us.


Code: [Select]

#define MIN_PULSE_WIDTH (750 * TICKS_PER_uS) //a valid pulse must be at least 750us (note clock counts in 0.5 us ticks)
#define MAX_PULSE_WIDTH (2250 * TICKS_PER_uS) //a valid pulse must be less than  2250us

#define PULSE_START_ON_RISING_EDGE  0
#define PULSE_START_ON_FALLING_EDGE 1
volatile byte pulseEnd = PULSE_START_ON_RISING_EDGE ; // default value

static void processSync(){
 // Sync was detected so reset the channel to 1 and update the system state
 Channel = 1;       // if so, reset the channel counter to 1      
 if(State == NOT_SYNCHED_state)
   State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
 else if( State == ACQUIRING_state)      
   State = READY_state;           // this is the second sync so flag that channel data is valid
}

ISR(TIMER1_CAPT_vect)
{
 if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == pulseEnd ){
   // this is the end of the pulse
   TCNT1 = 0;               // reset the counter      
   if(ICR1 >= SYNC_GAP_LEN){   // process sync if the pulse is long enough
     processSync();
   }    
   else if(Channel <= MAX_CHANNELS) {  // else its channel pulse so save it
     Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
   }      
 }
 else {        // the start of the pulse was detected, we begin timing here  
   TCNT1 = 0;        //  reset the counter to start timing
   if(ICR1 >= SYNC_GAP_LEN)  // process sync if the pulse is long enough
     processSync();
   if( State != READY_state && (ICR1 >= MIN_PULSE_WIDTH) ){   // if the inter-pulse period is greater than minimum pulse width, we need to invert the signal test
     pulseEnd ^= 1; // invert the pulse mode
     State = NOT_SYNCHED_state; //reset the state to properly start measuring from the first channel
   }
 }      
 TCCR1B ^= _BV(ICES1);         // toggle bit value to trigger on the other edge
}



Its not tested, please let me know how it works.
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 11, 2008, 05:59 pm
EUREKA!!!!!!!
EUREKA!!!!!!!

Now works very great im able to capture the PPM and pilot Servo!!!
How i reach this? Simple i create an only one sketch without Library :-(
So my big problem i think is not negative or low sync PPM signal, but only something on library call...
ok my mix code from the great work of MEM & bGatti is..too long to post how can i do? I want post for other monkey guy as me :-)
And now i think is time to understand the my library is not working...but also buy accelorometer to test  it, how can i read for example using interrupt an infrared sensor? what can i say is how can integrate sensors on this skecth?
Many thanks MEM & bGatti for help i hope you can still help me to move over :-)
Sincerelly Dimitri
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 11, 2008, 07:38 pm
well done DiMiz

I just had a good look at the library and the reason it doesn't work is that the interrupt is initialised in the constructor but the Arduino startup code overwrites the settings, so the timings are wrong.

Here is a fixed version of the library.  I added a new method called begin that does the initialisation. I also changed some names.
The library is tested with my Futaba transmitter which outputs positive going channel and sync pulses, other modes not yet tested.

Here is the test sketch
Code: [Select]

// ServoDecodeTest

#include <ServoDecode.h>

char * stateStrings[] = {
 "NOT_SYNCHED", "ACQUIRING", "READY"};

void setup()                    // run once, when the sketch starts
{
 Serial.begin(9600);  
 ServoDecode.begin();
}


void loop()                     // run over and over again
{
 int pulsewidth;

 // print the decoder state
 if( ServoDecode.getState()!= READY_state) {
   Serial.print("The decoder is ");
   Serial.println(stateStrings[ServoDecode.getState()]);
 }
 else {
   // decoder is ready, print the channel pulse widths
   for ( int i =1; i <=4; i++ ){ // print the status of the first four channels
     Serial.print("Channel ");
     Serial.print(i);
     Serial.print(" has width ");
     pulsewidth = ServoDecode.GetChannelPulseWidth(i);
     Serial.println(pulsewidth);
   }
 }
 delay(500); // update 2 times a second        
}



the class should be saved in a directory called ServoDecode
The header file below should be saved as ServoDecode.h
Code: [Select]

// ServoDecode.h
//This library decodes typical RC composite servo inputs into individual channel pulse widths

#ifndef ServoDecode_H
#define ServoDecode_H

#include <inttypes.h>

#define icpPin            8         // this interrupt handler must use pin 8
#define TICKS_PER_uS      2          // number of timer ticks per microsecond
#define NOT_SYNCHED_state  0    // the system is not synched so the data is random
#define ACQUIRING_state  1      // one sync pulse detected but not all channels have been received
#define READY_state     2       // synched and all channel data is valid

#define MAX_CHANNELS    8         // maximum number of channels we can store
#define MIN_IN_PULSE_WIDTH (750 * TICKS_PER_uS) //a valid pulse must be at least 750us (note clock counts in 0.5 us ticks)
#define MAX_IN_PULSE_WIDTH (2250 * TICKS_PER_uS) //a valid pulse must be less than  2250us
#define SYNC_GAP_LEN      (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)


class ServoDecodeClass
{
 public:
     ServoDecodeClass(); //Constructor
     void begin(); // call this in setup
     int GetChannelPulseWidth(uint8_t);  // this is the access function for channel data
     uint8_t getState(); //State Function
private:
};

extern ServoDecodeClass ServoDecode;  // make an instance for the user

#endif
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 11, 2008, 07:40 pm
Here is the cpp file, it should be saved as  ServoDecode.cpp
Code: [Select]

// ServoDecode.cpp
#include "ServoDecode.h"
#include <wiring.h>
#include <avr/interrupt.h>

#define PULSE_START_ON_RISING_EDGE  0
#define PULSE_START_ON_FALLING_EDGE 1
volatile byte pulseEnd = PULSE_START_ON_RISING_EDGE ; // default value
static volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
static volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)
static volatile uint8_t State;         // this will be one of the following states:

static void processSync(){
// Sync was detected so reset the channel to 1 and update the system state
  Channel = 1;       // if so, reset the channel counter to 1      
  if(State == NOT_SYNCHED_state)
    State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array
  else if( State == ACQUIRING_state)      
    State = READY_state;           // this is the second sync so flag that channel data is valid
}

ISR(TIMER1_CAPT_vect)
{
 // we want to measure the time to the end of the pulse
 if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == pulseEnd ){
   TCNT1 = 0;               // reset the counter      
     if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
     processSync();
   }    
   else if(Channel <= MAX_CHANNELS) {  // else its channel pulse so save it
      Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
   }      
 }
 else {        // the start of the pulse was detected  
   TCNT1 = 0;        // so reset the counter to start timing
     if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC    
     processSync();
     }
   if( State != READY_state && (ICR1 >= MIN_IN_PULSE_WIDTH) ){   // if the 'off' pulse is greater than minimum pulse, we need to invert the signal test
     pulseEnd ^= 1; // invert the pulse mode
     State = NOT_SYNCHED_state; //reset the state to start measuring from channel 1
   }
 }      
 TCCR1B ^= _BV(ICES1);         // toggle bit value to trigger on the other edge
}

ServoDecodeClass::ServoDecodeClass()
{
}

void ServoDecodeClass::begin(){
pinMode(icpPin,INPUT);
 Channel = 1;            
 State = NOT_SYNCHED_state;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}

uint8_t ServoDecodeClass::getState()  
{
     return State;
}


int ServoDecodeClass::GetChannelPulseWidth( uint8_t channel)
{
 // this is the access function for channel data
 int result;  
 if( (State == READY_state)  && (channel > 0) && (channel <=  MAX_CHANNELS)  ) {
     cli();             //disable interrupts
     result =  Pulses[channel] ;
     sei();             // enable interrupts
 }
 else
     result = 0;        // return 0 if no valid pulse is available  

 return result;
}

// make one instance for the user
ServoDecodeClass ServoDecode = ServoDecodeClass() ;
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 12, 2008, 10:46 am
Hi Mem!!! Im very happy for this progress ;-) Also thanks for your help i will test your new library as soon as possible!!
What do you suggest for Accelerometer? Thanks
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 12, 2008, 11:50 am
I have run the code to test using an inverted signal (pulses start on negative going edge and end on positive edge) and found a bug. Here is the updated cpp code that will auto-sync at startup on a normal or inverted signal.
Code: [Select]

#include "ServoDecode.h"
#include <wiring.h>
#include <avr/interrupt.h>

#define PULSE_START_ON_RISING_EDGE  0
#define PULSE_START_ON_FALLING_EDGE (1<<ICES1)
volatile byte pulseEnd = PULSE_START_ON_RISING_EDGE ; // default value
static volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
static volatile uint8_t  Channel;      // number of channels detected so far in the frame (first channel is 1)
static volatile uint8_t State;         // this will be one of the following states:

static void processSync(){
 // Sync was detected so reset the channel to 1 and update the system state
 Channel = 1;       // reset the channel counter to 1      
 Pulses[0] = ICR1 / TICKS_PER_uS;  // save the sync pulse duration for debugging
 if(State != READY_state) {      
   if(State == NOT_SYNCHED_state){
     State++;  // this is the first sync pulse, we need one more to fill the channel data array
   }
   else if( State == ACQUIRING_state)      {
     State = READY_state;           // this is the second sync and all channel data is ok so flag that channel data is valid    
   }
 }
}

ISR(TIMER1_CAPT_vect)
{
 // we want to measure the time to the end of the pulse
 if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == pulseEnd ){
   TCNT1 = 0;               // reset the counter      
   if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC

     processSync();
   }    
   else if(Channel <= MAX_CHANNELS) {  // check if its a valid channel pulse and save it
#if defined FAIL_SAFE  // this is not yet implimented !!!
     if( (ICR1 < MAX_IN_PULSE_WIDTH)  || (ICR1 > MAX_IN_PULSE_WIDTH) ) // check for valid channel data
       State = FAIL_SAFE_state;  // use fail safe values if input data invalid
     else
#endif
       Pulses[Channel++] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
   }      
 }
 else {        // the start of the pulse was detected  
   TCNT1 = 0;        // so reset the counter to start timing
   if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC  
     processSync();
   }
   else if( (State != READY_state) && (ICR1 >= MIN_IN_PULSE_WIDTH) ){   // if the 'off' pulse is greater than minimum pulse, we need to invert the signal test
     pulseEnd = (pulseEnd == PULSE_START_ON_RISING_EDGE) ? PULSE_START_ON_FALLING_EDGE : PULSE_START_ON_RISING_EDGE ; //invert pulse mode to trigger on other edge
     State = NOT_SYNCHED_state; //reset the state to wait for sync
   }
 }      
 TCCR1B ^= _BV(ICES1);         // toggle bit value to trigger on the other edge
}

ServoDecodeClass::ServoDecodeClass()
{
}

void ServoDecodeClass::begin(){
 pinMode(icpPin,INPUT);
 Channel = 1;            
 State = NOT_SYNCHED_state;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1);   // enable input capture interrupt for timer 1
}

uint8_t ServoDecodeClass::getState()  
{
 return State;
}


int ServoDecodeClass::GetChannelPulseWidth( uint8_t channel)
{
 // this is the access function for channel data
 int result;  
 if( (State == READY_state)  && (channel > 0) && (channel <=  MAX_CHANNELS)  ) {
   cli();             //disable interrupts
   result =  Pulses[channel] ;
   sei();       // enable interrupts
 }
 else
   result = 0;        // return 0 if no valid pulse is available  

 return result;
}

// make one instance for the user
ServoDecodeClass ServoDecode = ServoDecodeClass() ;


The existing header and sketch should be ok as is but I found that my transmitter could generate pulses that were only 550us long so I modified the following in my header file to give the decode more tolerance:

#define MIN_IN_PULSE_WIDTH (500 * TICKS_PER_uS) //a valid pulse must be at least 500us
#define MAX_IN_PULSE_WIDTH (2500 * TICKS_PER_uS) //a valid pulse must be less than  2500us


FWIW, here is a screen grab of the output of my transmitter that was used to test. The inverted test was done by inverting this signal using a logic gate to flip the signal.
(http://
http://farm4.static.flickr.com/3276/2571995993_368aca9501.jpg?v=0
)


Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 12, 2008, 11:56 am
Quote
What do you suggest for Accelerometer? Thanks


I will shortly be looking into accelerometers for onboard attitude monitoring so nothing really to report yet.

So far I have only have experience with the accelerometer in the nunchuck to provide control inputs as a substitute for joysticks.
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 12, 2008, 02:28 pm
Wow Mem thanks, i have see yesterday this parameter and i try to mod it to suite my signal for example on my Hitec Focus4 the MIN_IN_PULSE_WIDTH=440us and MAX_IN_PULSE_WIDTH=1850us and the sync pulse is at least 4000 us.
I will test more parameter to see what affect, also i think the nunchuck accelorometer is good solution to stabilized the QuadCopter and there is a lot of example to interface it i try to look in.
Many Thanks
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 12, 2008, 03:17 pm
Quote
Wow Mem thanks, i have see yesterday this parameter and i try to mod it to suite my signal for example on my Hitec Focus4 the MIN_IN_PULSE_WIDTH=440us and MAX_IN_PULSE_WIDTH=1850us and the sync pulse is at least 4000 us.
I will test more parameter to see what affect, also i think the nunchuck accelorometer is good solution to stabilized the QuadCopter and there is a lot of example to interface it i try to look in.
Many Thanks

Bear in mind that MIN_IN_PULSE_WIDTH is the minimum width of a correctly formed channel pulse, its not the width of the space between pulses. Looking at the scope trace you posted earlier, it seems the space between channel pulses is 440us on that trace. If this is the case, you need to ensure that MIN_IN_PULSE_WIDTH is set higher than this, try it at the value I use, 500us.
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 12, 2008, 04:08 pm
Hi Mem your are right!! I confused it:-) i will try with correct one and report. About accelerometer try to look this http://diydrones.com/profiles/blog/show?id=705844%3ABlogPost%3A31713

Seems very good example!!
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 12, 2008, 04:41 pm
Quote
Hi Mem your are right!! I confused it:-) i will try with correct one and report. About accelerometer try to look this http://diydrones.com/profiles/blog/show?id=705844%3ABlogPost%3A31713

Seems very good example!!

I agree. That code was the inspiration for my nunchuck project.
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 12, 2008, 09:59 pm
Im not so normal guys...after i go out of my work i buy a nunchuk to test it ;-)
And now im here to work with arduino...
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jun 12, 2008, 10:04 pm
Quote
Im not so normal guys...after i go out of my work i buy a nunchuk to test it ;-)
And now im here to work with arduino...

Normal is a relative thing, and in present company you certainly qualify  :)
Title: Re: 3 axis auto stabilized platform
Post by: DiMiz on Jun 12, 2008, 10:40 pm
Thanks for encourage me ;-)
Ok i retest my PPM signal and the very Low Signal is 460us and the Max is 1280us and my very Low Sync is 2300 this value is with R/C trigger OFF.
Your Code Works very well!! only minor problem is when i turn OFF R/C transmitter i supposed to not have a sync signal but the output is:
---
Channel 1 has width 415
Channel 2 has width 20
Channel 3 has width 565
Channel 4 has width 425
Channel 1 has width 89
Channel 2 has width 1204
Channel 3 has width 1956
Channel 4 has width 112
Channel 1 has width 775
Channel 2 has width 245
Channel 3 has width 1046
Channel 4 has width 2085
---
On my Oscope is a burst signal only...
Now i want try the Nunchuck and as soon is possible i want buy the Brushless motor :-)
Thanks so muc
Title: Re: 3 axis auto stabilized platform
Post by: jamiemcshan on Jul 21, 2008, 08:54 pm
Has anyone used this with the VEX add on TX/RX set up? I am guessing it would work just the same as the VEX system has a standard PPM output but wanted to see if anyone else has tested the waters before I dive in.

Thanks
Jamie
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 21, 2008, 09:29 pm
Quote
Has anyone used this with the VEX add on TX/RX set up? I am guessing it would work just the same as the VEX system has a standard PPM output but wanted to see if anyone else has tested the waters before I dive in.

Thanks
Jamie


Hi Jamie, I am not familiar with the VEX system but a glance  at the web site indicates that the TX and Rx add on kit provide 4 analog and two digital outputs. The library discussed on this thread takes the combined output pulses from a conventional Radio Control transmitter receiver and decodes them in an arduino sketch.
If the VEX does output a standard PPM signal capable of driving an arduino input  then the software here should be able to decode it.
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 21, 2008, 09:42 pm
Here is the latest version of this code that has failsafe fully working.

Test Sketch

Code: [Select]

// ServoDecodeTest

#include <ServoDecode.h>

char * stateStrings[] = {
 "NOT_SYNCHED", "ACQUIRING", "READY", "in Failsafe"};

void setup()                    // run once, when the sketch starts
{
 Serial.begin(38400);  
 ServoDecode.begin();

   //setFailsafe()  can set a pulse width for a channel if the Tx signal is lost.  
   //By default all channels not explicitly set will hold the last good signal
 ServoDecode.setFailsafe(3,1234); // set channel 3 failsafe pulse  width
}


void loop()      
{
 int pulsewidth;

 // print the decoder state
 if( ServoDecode.getState()!= READY_state) {
   Serial.print("The decoder is ");
   Serial.println(stateStrings[ServoDecode.getState()]);
   for ( int i =0; i <=MAX_CHANNELS; i++ ){ // print the status of the first four channels
     Serial.print("Cx");
     Serial.print(i);
     Serial.print("= ");
     pulsewidth = ServoDecode.GetChannelPulseWidth(i);
     Serial.print(pulsewidth);
     Serial.print("  ");
   }
   Serial.println("");
 }
 else {
   // decoder is ready, print the channel pulse widths
   for ( int i =1; i <=MAX_CHANNELS; i++ ){ // print the status of the first four channels
     Serial.print("Ch");
     Serial.print(i);
     Serial.print("= ");
     pulsewidth = ServoDecode.GetChannelPulseWidth(i);
     Serial.print(pulsewidth);
     Serial.print("  ");
   }
   Serial.println("");
 }
 delay(500); // update 2 times a second        
}



The H file, save this as ServoDecode.h
Code: [Select]

// ServoDecodeClass.h
//This library decodes typical RC composite servo inputs into individual channel pulse widths

#ifndef ServoDecode_H
#define ServoDecode_H

#include <inttypes.h>
#include <wiring.h> // for boolean

#define icpPin            8         // this interrupt handler must use pin 8
#define TICKS_PER_uS      2          // number of timer ticks per microsecond


typedef enum {
   NULL_state=-1, NOT_SYNCHED_state, ACQUIRING_state, READY_state, FAILSAFE_state    
} decodeState_t;

#define MAX_CHANNELS    8         // maximum number of channels we can store, don't increase this above 8
#define MIN_IN_PULSE_WIDTH (750 * TICKS_PER_uS) //a valid pulse must be at least 750us (note clock counts in 0.5 us ticks)
#define MAX_IN_PULSE_WIDTH (2250 * TICKS_PER_uS) //a valid pulse must be less than  2250us
#define SYNC_GAP_LEN      (3000 * TICKS_PER_uS) // we assume a space at least 3000us is sync (note clock counts in 0.5 us ticks)
#define FAILSAFE_PIN   13  // if defined, this will set the given pin high when invalid data is received


class ServoDecodeClass
{
 public:
     ServoDecodeClass(); //Constructor
     void begin();
     decodeState_t getState(); //State Function
     int GetChannelPulseWidth(byte chan);  // this is the access function for channel data
     void setFailsafe(byte chan, int value); // pulse width to use if invalid data, value of 0 uses last valid data
     void setFailsafe();// setFailsafe with no arguments sets failsafe for all channels to their current values
                      // useful to capture current tx settings as failsafe values
     byte getChanCount();
private:

};

extern ServoDecodeClass ServoDecode;  // make an instance for the user

#endif
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 21, 2008, 09:45 pm
The cpp file, save this as ServoDecode.cpp

Code: [Select]

#include "ServoDecode.h"
#include <wiring.h>
#include <avr/interrupt.h>

//#define ISR_DEBUG // uncomment this to send low level ISR debug to the serial port

#define PULSE_START_ON_RISING_EDGE  0
#define PULSE_START_ON_FALLING_EDGE (1<<ICES1)
#define ACQUISITION_COUNT  8  // must have this many consecutive valid frames to transition to the ready state.
volatile byte pulseEnd = PULSE_START_ON_RISING_EDGE ; // default value
static volatile unsigned int Pulses[ MAX_CHANNELS + 1]; // array holding channel pulses width value in microseconds
static volatile unsigned int Failsafe[MAX_CHANNELS + 1]; // array holding channel fail safe values
static volatile byte Channel;      // number of channels detected so far in the frame (first channel is 1)
static volatile byte NbrChannels; // the total number of channels detected in a complete frame
static volatile decodeState_t State;         // this will be one of the following states:
static volatile byte stateCount;         // counts the number of times this state has been repeated

static void processSync(){
// Sync was detected so reset the channel to 1 and update the system state      
  Pulses[0] = ICR1 / TICKS_PER_uS;  // save the sync pulse duration for debugging
  if(State == READY_state) {  
        DEBUG_OUT('R');
        if( Channel != NbrChannels){  // if the number of channels is unstable, go into failsafe
              State = FAILSAFE_state;
        }
  }
  else{
    if(State == NOT_SYNCHED_state){
            State = ACQUIRING_state;        // this is the first sync pulse, we need one more to fill the channel data array    
            stateCount = 0;
      }
      else if( State == ACQUIRING_state)      {
         if(++stateCount > ACQUISITION_COUNT) {
           State = READY_state;           // this is the second sync and all channel data is ok so flag that channel data is valid    
               NbrChannels = Channel; // save the number of channels detected
           }
    }
      else if( State == FAILSAFE_state)      {  
            if(Channel == NbrChannels){  // did we get good pulses on all channels
                 State = READY_state;
           }
      }
  }
  Channel = 0;       // reset the channel counter
}

ISR(TIMER1_OVF_vect){
 if(State == READY_state){
   State = FAILSAFE_state;  // use fail safe values if signal lost  
   Channel = 0; // reset the channel count
 }
}

ISR(TIMER1_CAPT_vect)
{
 // we want to measure the time to the end of the pulse
 if( (_SFR_BYTE(TCCR1B) & (1<<ICES1)) == pulseEnd ){         
   TCNT1 = 0;       // reset the counter
   if(ICR1 >= SYNC_GAP_LEN){   // is the space between pulses big enough to be the SYNC
     processSync();
   }    
   else if(Channel < MAX_CHANNELS) {  // check if its a valid channel pulse and save it
     if( (ICR1 >= MIN_IN_PULSE_WIDTH)  && (ICR1 <= MAX_IN_PULSE_WIDTH) ){ // check for valid channel data                  
       Pulses[++Channel] = ICR1 / TICKS_PER_uS;  // store pulse length as microsoeconds
     }
     else if(State == READY_state){
       State = FAILSAFE_state;  // use fail safe values if input data invalid  
       Channel = 0; // reset the channel count
     }
   }      
 }
}

ServoDecodeClass::ServoDecodeClass(){
}

void ServoDecodeClass::begin(){
 pinMode(icpPin,INPUT);
 Channel = 0;            
 State = NOT_SYNCHED_state;
 TCCR1A = 0x00;         // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
 TCCR1B = 0x02;         // 16MHz clock with prescaler means TCNT1 increments every .5 uS (cs11 bit set
 TIMSK1 = _BV(ICIE1)|_BV (TOIE1);   // enable input capture and overflow interrupts for timer 1
}

decodeState_t ServoDecodeClass::getState(){
 return State;
}

byte ServoDecodeClass::getChanCount(){
 return NbrChannels;
}

void  ServoDecodeClass::setFailsafe(byte chan, int value){
// pulse width to use if invalid data, value of 0 uses last valid data
 if( (chan > 0) && (chan <=  MAX_CHANNELS)  ) {
      Failsafe[chan] = value;
 }
}
void  ServoDecodeClass::setFailsafe(){
// setFailsafe with no arguments sets failsafe for all channels to their current values
// usefull to capture current tx settings as failsafe values
 if(State == READY_state)
   for(byte chan = 1; chan <=  MAX_CHANNELS; chan++) {
       Failsafe[chan] = Pulses[chan];
   }
}

int ServoDecodeClass::GetChannelPulseWidth( uint8_t channel)
{
 // this is the access function for channel data
 int result = 0; // default value
 if( channel <=  MAX_CHANNELS)  {
    if( (State == FAILSAFE_state)&& (Failsafe[channel] > 0 ) )
        result = Failsafe[channel]; // return the channels failsafe value if set and State is Failsafe
      else if( (State == READY_state) || (State == FAILSAFE_state) ){
        cli();       //disable interrupts
        result =  Pulses[channel] ;  // return the last valid pulse width for this channel
        sei(); // enable interrupts
      }
 }
 return result;
}
// make one instance for the user
ServoDecodeClass ServoDecode = ServoDecodeClass() ;
Title: Re: 3 axis auto stabilized platform
Post by: jamiemcshan on Jul 21, 2008, 10:07 pm
Mem,

Thanks, I am really really new to all this.

From an actual hardware perspecitve, can you show how you have this wired up?

Jamie
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 21, 2008, 10:48 pm
If your connecting to a transmitter, the PPM signal (make sure its not more than 5 volts, check the spec of your Transmitter if you don't have a scope) connects to Arduino pin 8. The transmitter ground connects to the Arduino ground. That should be it.

The cpp and h files need to be in the arduino library directory called ServoDecode. Copy the test sketch into the Arduino IDE and running it  should display the status and pulse widths on the Arduino serial monitor (set the baud rate for 38400)  
Title: Re: 3 axis auto stabilized platform
Post by: jamiemcshan on Jul 22, 2008, 05:30 am
Mem,

Since I already seem to be abusing your great talents...   ::)  what are the next steps if I want to actually take and control servos with this instead of just displaying the stream readings?

And thanks again for this,  I have been looking around for this type of information for weeks now!  

My plan long term is to take and create routines so that when I hit ch 5 on the TX then the Arduino will listen and what ever I send next is the script it will run. So if I hold 5, then 3, it will send servo 1 high for 5 second, send servo 2 high for 2 sec, then back to 0 in reverse order. if that makes sense.

Jamie
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 22, 2008, 09:08 am
I am not really sure what you want to do, is it something like the sketch below?

There is a library earlier in this thread called ServoTimer2 that can be used for the  servo control in the sketch.

Code: [Select]

// ServoDecodeTest
#include <ServoDecode.h>
#include <ServoTimer2.h>  // the servo library


// define the servo pins
#define servo1Pin  2   // the pin the servo is attached to
#define servo2Pin  3

ServoTimer2 servo1;    // declare variables for the servos
ServoTimer2 servo2;  

void setup()
{
 Serial.begin(38400);
 servo1.attach(servo1Pin);      
 servo2.attach(servo2Pin);  
 ServoDecode.begin();
}


void loop()
{
 int pulsewidth[MAX_CHANNELS];

 if( ServoDecode.getState()!= READY_state) {
   Serial.println("The decoder is not ready");
 }
 else {
   // decoder is ready, do somthing with the channel pulse widths
   if( ServoDecode.GetChannelPulseWidth(1) > 1500){  // run script 1 if pulse width greater than 1500 ;
     int argumentA = (2250 - ServoDecode.GetChannelPulseWidth(2)) / 200; // some values proportional to stick position
     boolean argumentB = ServoDecode.GetChannelPulseWidth(2) < 1500; // true if channel 2 less than than 1500
     runScript1(argumentA, argumentB);
   }  
 }
 delay(100); // update 10 times a second      
}

void  runScript1(int argumentA, boolean argumentB){
 // do something here to control servo...  
 servo1.write(2000);
 delay(5000);  // note that all servo are pulsed by the interrupt handler with their current value every 20ms
 if(argumentA)
    servo2.write(2000);    
 delay(argumentB * 10); //delay a time determined by the transmitter stick position
 servo1.write(1000);  
 servo2.write(1000);  
}
Title: Re: 3 axis auto stabilized platform
Post by: jamiemcshan on Jul 26, 2008, 03:05 am
Mem,

Just wanted to say thanks. When I asked the questions for your help, I didn't even actually own an Arduino yet... but all that has changed now. I got my Arduino in the mail today, loaded up the code that you provided here and wala.... I was reading my VEX receiver.  Now just got to figure out how to put servos on this thing... I think this is a great first day of Arduino'ing!

Thanks so much for your help.

Jamie
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 26, 2008, 07:52 am
Jamie, glad to hear you got it going.

There are a few libraries in the playground for controlling servos: http://www.arduino.cc/playground/Main/InterfacingWithHardware#Output

Note that you can't use ServoTimer1 because that uses the same timer that we are using to measure the transmitter pulses. If you need your servos driven in background from an interrupt you may want to look at the code  posted in a thread starting from here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1204020386/74#74
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 26, 2008, 09:19 am
Thx a lot mem

But i still got a problem with your sketch.

Code: [Select]
// ServoDecodeTest

#include <ServoDecode.h>

char * stateStrings[] = {
 "NOT_SYNCHED", "ACQUIRING", "READY", "in Failsafe"};

void setup()                    // run once, when the sketch starts
{
 Serial.begin(38400);  
 ServoDecode.begin();

   //setFailsafe()  can set a pulse width for a channel if the Tx signal is lost.  
   //By default all channels not explicitly set will hold the last good signal
 ServoDecode.setFailsafe(3,1234); // set channel 3 failsafe pulse  width
}


void loop()      
{
 int pulsewidth;

 // print the decoder state
 if( ServoDecode.getState()!= READY_state) {
   Serial.print("The decoder is ");
   Serial.println(stateStrings[ServoDecode.getState()]);
   for ( int i =0; i <=MAX_CHANNELS; i++ ){ // print the status of the first four channels
     Serial.print("Cx");
     Serial.print(i);
     Serial.print("= ");
     pulsewidth = ServoDecode.GetChannelPulseWidth(i);
     Serial.print(pulsewidth);
     Serial.print("  ");
   }
   Serial.println("");
 }
 else {
   // decoder is ready, print the channel pulse widths
   for ( int i =1; i <=MAX_CHANNELS; i++ ){ // print the status of the first four channels
     Serial.print("Ch");
     Serial.print(i);
     Serial.print("= ");
     pulsewidth = ServoDecode.GetChannelPulseWidth(i);
     Serial.print(pulsewidth);
     Serial.print("  ");
   }
   Serial.println("");
 }
 delay(500); // update 2 times a second        
}


I do get this error:

o: In function `loop':
undefined reference to `ServoDecode'o: In function `setup':


I've put the libary into the ServoDecode folder...

Thx for your help
Andy
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 26, 2008, 10:16 am
Hi Andy,

That message indicates that the build is failing because it can't find the compiled ServoDecode library. Are there any other compiler messages that might indicate why ServoDecode is failing to build?

If not then the build environment may not be finding the ServoDecode source. Check that the directory just below the Arduino libraries directory is called ServoDecode and that at least the following two files are in the directory with exactly these names and extensions:

ServoDecode.cpp
ServoDecode.h
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 26, 2008, 10:34 am
hmm strange they are in the right place: Now i get this one:

ServoDecode.cpp: In function 'void processSync()':
ServoDecode.cpp:22: error: 'DEBUG_OUT' was not declared in this scope
o: In function `loop':
undefined reference to `ServoDecode'o: In function `setup':

Andy
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 26, 2008, 10:43 am
Andy, remove the following line in the processSync method of the cpp file
 DEBUG_OUT('R');

You can remove any other occurrence of that line, I thought I had removed them all in the version I posted but it looks like one sneaked through. Its debug code thats not needed in the runtime version.
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 26, 2008, 10:48 am
Thx a lot now it does work!

Hmm but i do get a NOT-SYNCHED output....

I have to check this out...

Andy
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 26, 2008, 12:15 pm
Quote
Thx a lot now it does work!

Hmm but i do get a NOT-SYNCHED output....

I have to check this out...

Andy

What transmitter (or receiver) are you using and how is it connected to the input on pin 8?
Title: Re: 3 axis auto stabilized platform
Post by: jamiemcshan on Jul 27, 2008, 05:54 am
Mem,

I am trying to integrate in the ServoTime2 code, but I get this message when I try to make a call to it.

I am a total newbie, so any direction on what to check would be great!

o: In function `__static_initialization_and_destruction_0':
undefined reference to `ServoTimer2::ServoTimer2()

And when I try the code from post 175, I get this.

o: In function `__static_initialization_and_destruction_0':
undefined reference to `ServoTimer2::ServoTimer2()'o: In function `runScript1(int, unsigned char)':
o: In function `setup':

Jamie
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 27, 2008, 07:22 am
Jamie,  it looks like the ServoTimer2 library code is not being found. Can you check that the header file is named ServoTimer2.h and the source file is named ServoTimer2.cpp and that both are in a directory called ServoTimer2 in a subdirectory just below hardware/libraries.
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 27, 2008, 10:32 am
Quote

What transmitter (or receiver) are you using and how is it connected to the input on pin 8?


I'm using a Graupner receiver and the Futaba FF10 as a transmitter..
I followed this tutorial to get the PPM signal from the Transmitter.
After that I could read out the different values for all the channels.
The wire from the inside of the transmitter where I found the PPM signal is directly connected to pin 8 and the ground to the ground on the Arduino.

Andy
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 27, 2008, 10:39 am
> I followed this tutorial to get the PPM signal from the Transmitter.
> After that I could read out the  different values for all the channels.

Andy, are you saying that it works when connected to the transmitter but not when connected to the receiver?

If so, can you say how you are getting the ppm signal from the receiver
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 27, 2008, 10:46 am
hmm yes I do get the PPM signal directly from the receiver (from the chip inside the receiver).
Sorry I forgot to post the link
With this code I can monitor every channel and if I push anything on the transmitter I get  different values for this channel. Just really great [ch61514]

Code: [Select]
#define channumber 8 //How many channels have your radio?
int value[channumber];

void setup()
{
Serial.begin(57600); //Serial Begin
pinMode(3, INPUT); //Pin 3 as input
}
void loop()
{
while(pulseIn(3, LOW) < 5000){} //Wait for the beginning of the frame
for(int x=0; x<=channumber-1; x++)//Loop to store all the channel position
{
value[x]=pulseIn(3, LOW);
}
for(int x=0; x<=channumber-1; x++)//Loop to print and clear all the channel readings
{
Serial.print(value[x]); //Print the value
Serial.print(" ");
value[x]=0; //Clear the value afeter is printed
}
Serial.println(""); //Start a new line
}


I got all the information from this site.
http://diydrones.com/profiles/blog/show?id=705844%3ABlogPost%3A38393

By the way. You don't get the PPM Signal directly from the reciever? How do you do that?

Andy
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 27, 2008, 11:01 am
Andy, do you have ServoDecode working with your transmitter?

When you connect the receiver, are you using pin 8?

The receiver I use has a pic micro-controller to process the ppm signal and I take a feed off the appropriate pin. I posted details earlier in this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1204020386/3#3
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 27, 2008, 11:03 am
No ServoDecode does not work...
yes I'm using pin 8

Andy
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 27, 2008, 11:14 am
ServoDecode uses a sync gap length of 3ms, your working test code uses 5ms. I doubt that is the cause of your problem but you could try to either change your test code and see if that stops it working:
 while(pulseIn(3, LOW) < 3000){} // <- was 5000

or change the following in ServoDecode.h
#define SYNC_GAP_LEN      (5000 * TICKS_PER_uS) // <- this was 3000
Don't forget to delete ServoDecode.o and see if that works

Another thing you could try is to change the following line in ServoDecode.cpp
volatile byte pulseEnd = PULSE_START_ON_RISING_EDGE ; // default value
to
volatile byte pulseEnd = PULSE_START_ON_FALLING_EDGE ; // try this

Again, don't forget to delete ServoDecode.o
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 27, 2008, 11:26 am
yep the ServoDecode does now work. It does even work with
 while(pulseIn(3, LOW) < 3000){} // or 5000 doesn't matter.

So I plug now a servo to the Arduino....
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 27, 2008, 11:29 am
Good to hear you have it going, what made it start working?
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 27, 2008, 11:31 am
I treid the ServoDecode just once and I think I pluged it to pin 7 or 9....
can I now remap the receiver signal to the servo signal with map()?

Or what do you suggest?

Andy
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 27, 2008, 11:38 am
What is the relationship you want between the input pulses and the output pulses?
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 27, 2008, 11:41 am
Hmm I thought that my Hitec servos need a Pulse between 900 and 2100.

One thing when i test the "// ServoDecodeTest" it does show me the error
Not_Synched. But whne I siwtch the transmitter of it does show "Failsafe".

Why not_snyched?
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 27, 2008, 11:47 am
sorry the values are right i can use them for controlling the servo without
map()....
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 27, 2008, 12:01 pm
Quote

One thing when i test the "// ServoDecodeTest" it does show me the error
Not_Synched. But whne I siwtch the transmitter of it does show "Failsafe".

Why not_snyched?


Not synched means the decoder has never received a valid signal. A valid signal consists of all channel pulses within the minimum and maximum values followed by a valid sync pulse, with the time values defined in the header file. It will only enter the fail-safe state on an invalid signal after has detected at least one complete valid frame.

More complex detection of an invalid signal is awaiting some spare coding time. Any volunteers for this are welcome  ;)
Title: Re: 3 axis auto stabilized platform
Post by: GekoCH on Jul 27, 2008, 12:13 pm
Ah ok thx!

Just one thing the values from the different channels
they always jump (like 1201, 1202 , 1203) so the Servo
is always vibrating.
Do you know a way to get a stable pulse?

PS: Thx for all your help!!!

Andy
Title: Re: 3 axis auto stabilized platform
Post by: mem on Jul 27, 2008, 12:25 pm
You can filter the pulse data if you need to, but first make sure that the servos are not drawing more power than the arduino regulator can supply . If you need it, I  think there is some filter code to do a moving average in the playground.
Title: Re: 3 axis auto stabilized platform
Post by: jamiemcshan on Jul 27, 2008, 11:49 pm
Mem,

Thank you, yes, I had put the ServoTimer2 code in the ServoDecode folder... I told you I was new to all this!

Thanks so much for your help. I got it running and doing a simple routine with 3 servos. I can't wait to put this into my robot so that door opens, claw lowers, claw raises, door closes!!! This is great and I never could have done this without your incredible code!

Jamie
Title: Re: 3 axis auto stabilized platform
Post by: boomii on Dec 28, 2008, 03:41 pm
Hi,

ServoTimer2 seems very great.
Could you please release your librarie ServoTimer2 ?
Thanks!
Title: Re: 3 axis auto stabilized platform
Post by: mem on Dec 28, 2008, 05:06 pm
I have posted the library code and a test sketch for ServoTimer2 here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1230479947
Title: Re: 3 axis auto stabilized platform
Post by: aerodolphin on Mar 06, 2009, 06:03 pm
Anyone have the 3 axis auto stabilized platform working? If yes, it have kalman filtering?

I bought acel. and gyros from sparfun. I'm very interested in this project.
Title: Re: 3 axis auto stabilized platform
Post by: bob.titus on Jun 04, 2009, 06:59 pm
Hello,

I'm new to this forum and could use a little help for my Arduino project.
I'm trying to get my receiver working with some code I got, but my talents are close to 0, so here I am :-(
Title: Re: 3 axis auto stabilized platform
Post by: bob.titus on Jun 04, 2009, 07:08 pm
in fact, I have some code for a 4 propellers heli (Quad) but my radio does not want to work with it, the code is only working with Spektrum receivers.
I have worked for over two weeks trying to get it work with bits and pieces I found everywhere, but as I am very new to Arduino, and code is not my strength, I have been searching more and found out about this forum and noticed that mem's interventions are very documented and talented.
After lurking for a while, I decided to register and try to get help on this board.
Could someone help me please, I'm close to the edge :'(
Title: Re: 3 axis auto stabilized platform
Post by: Frisken on Oct 04, 2009, 10:04 pm
Hi!

I have some trobble getting the code that mem wrote to compile in the arduino 17 enviroment. Has anyone succesfully compiled that code in the 17 version???

If so, I whould like to now how.
Title: Re: 3 axis auto stabilized platform
Post by: Frisken on Oct 05, 2009, 10:01 am
OK, now I got the arduino 17 to at least start to compile but I get all theese errors:
f:/arduino-0017/hardware/tools/avr/lib/gcc/../../avr/include/stdlib.h:111: error: expected unqualified-id before 'int'

f:/arduino-0017/hardware/tools/avr/lib/gcc/../../avr/include/stdlib.h:111: error: expected `)' before 'int'

f:/arduino-0017/hardware/tools/avr/lib/gcc/../../avr/include/stdlib.h:111: error: expected `)' before 'int'

I have tested the same code in arduino 11 and it works just fine. But in 17....
Is there a problem in the stdlib for arduino 17??
Title: Re: 3 axis auto stabilized platform
Post by: mem on Oct 05, 2009, 11:20 am
Frisken, have you tried the code posted in this thread: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1228137503/0

it has the fix for the problem introduced in releases later than 0011.
Title: Re: 3 axis auto stabilized platform
Post by: Frisken on Oct 05, 2009, 04:06 pm
thank you mem
it compiled like a charm.