Go Down

Topic: 3 axis auto stabilized platform (Read 31035 times) previous topic - next topic

bGatti

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':

mem

#121
Jun 04, 2008, 08:00 pm Last Edit: Jun 04, 2008, 08:01 pm by mem Reason: 1
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.

bGatti

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

bGatti

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

mem

#124
Jun 05, 2008, 08:43 am Last Edit: Jun 05, 2008, 08:50 am by mem Reason: 1
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

bGatti

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

mem

#126
Jun 05, 2008, 04:23 pm Last Edit: Jun 05, 2008, 04:27 pm by mem Reason: 1
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
   }    
}

bGatti

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

bGatti

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

mem

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.

DiMiz

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

mem

#131
Jun 08, 2008, 05:35 pm Last Edit: Jun 08, 2008, 05:36 pm by mem Reason: 1
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;
}

DiMiz

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

mem

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.

DiMiz

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?

Go Up