3 axis auto stabilized platform

The channels are pulsed in order and channel 1 is the first channel after the long sync delay.

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…

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…

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.

#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        
}

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!

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.

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?

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.

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.

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.

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?

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.

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.

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.

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?

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.

#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        
}

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.

 // 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?

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.

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

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

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