Pages: 1 ... 6 7 [8] 9 10 ... 15   Go Down
Author Topic: 3 axis auto stabilized platform  (Read 20279 times)
0 Members and 1 Guest are viewing this topic.
London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.  

Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 26
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks,
You're right. That code (#74)  appears to sequentially pulse the Servos.
(Does it include a reset Delay)
I'll try it.
Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 26
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
#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);  
}

 
Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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 !

« Last Edit: June 02, 2008, 10:49:12 pm by mem » Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 26
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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
Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 26
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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.

Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 26
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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:
//***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
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 26
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Save as ServoInput2.cpp (in Library ServoInput2)

Code:
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;
}
 
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 26
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

And Here 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 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        

  
}

 
Logged

London
Offline Offline
Faraday Member
**
Karma: 8
Posts: 6240
Have fun!
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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;
}
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 26
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

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

0
Offline Offline
Newbie
*
Karma: 0
Posts: 26
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Turns out I had tried declaring the class with a () at the end.
Logged

Pages: 1 ... 6 7 [8] 9 10 ... 15   Go Up
Jump to: