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?
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?
... 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.
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.
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?
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.)
#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() {
}
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.
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?
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.
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.
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
#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);
}
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 !
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:
#IfDef section to add the input capture to ServoTimer2
Public Access to the ServoArray
a recommended receiver with instructions for modification
a UAV shield with Receiver, Gyro, Accel, Gps, Alt, AirSpeed and pyrometer pins. (Existing Zigbee Shield for duplex)
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.
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.
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.
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 ()()'
//***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
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;
}
// 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
}
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?