RC transmiter with telemetry need help for PPM output

Status
Not open for further replies.

Francois

Member
I would like to share one of my projects, and ask for help at the same time.

I am building my own RC transmitter for long range flying with telemetry. This means that I need to
1 - generate a ppm output (transmitter)
2 - read a serial from xbee (telemetry) and
3 - generate serial (for touch screen)
4 - and maybe, read ppm input (like with a trainer port, or head tracker...) ALL at the same time!

My fisrt version was using a AVR (arduino) but it lacked power when you need to do all three tasks at the same time (plus serial library use cli() witch makes ppm stream buggy, I lost quite some time tracking this problem down). Then I found the Teensy! Lots of power and small size, perfect for my application! I immediately bought two Teensy 3.1, and everything worked immediately.

Here is what my transmitter looks like:

photo_1.jpg

My problem is that I do not understand very well the structure of the interrupts (on ARM processors) to generate ppm output stream.

*** The easy solution is to use PulsePosition.h but i do not understand everything... So I can't modify it for my needs, for example i need to generate two types of ppm streams either positive or negative shift.

fig1-02.gif (Carrier is at 3V for both graphs)

*** Another solution would be to write my own ppm library (that I can understand, lol). I already did that, but for the AVR architecture. Off course, I can't port the code to the Teensy because I do not understand the timers on the ARM architecture. I read this: https://www.pjrc.com/teensy/interrupts.html#names , but i am not smart enough to apply it.

Here is my code works (actually it is not really mine, I copied big chunks of it):

1. Setup a timer on the due & Start a timer
2. Set a presacaler
3. Change the match register (trigger)
4. Run into a loop every time the trigger is reached and turn on (or off) the ppm pin and go back to step 3


Here is the actual code:
Code:
//////////////////////CONFIGURATION///////////////////////////////
#define chanel_number 8           //set the number of chanels
#define default_servo_value 1500  //set the default servo value
#define PPM_FrLen 22500           //set the PPM frame length in microseconds (1ms = 1000µs)
#define PPM_PulseLen 300          //set the pulse length
#define onState 1                 //set polarity of the pulses: 1 is positive, 0 is negative
#define sigPin 10                 //set PPM signal output pin on the arduino
//////////////////////////////////////////////////////////////////

/*this array holds the servo values for the ppm signal
 change theese values in your code (usually servo values move between 1000 and 2000)*/
int ppm[chanel_number];

void setup(){  
  //initiallize default ppm values
  for(int i=0; i<chanel_number; i++){
    ppm[i]= default_servo_value;
  }

  pinMode(sigPin, OUTPUT);
  digitalWrite(sigPin, !onState);  //set the PPM signal pin to the default state (off)
  
  cli();
  TCCR1A = 0; // set entire TCCR1 register to 0
  TCCR1B = 0;
  
  OCR1A = 100;              // compare match register, change this
  TCCR1B |= (1 << WGM12);   // turn on CTC mode
  TCCR1B |= (1 << CS11);    // 8 prescaler: 0,5 microseconds at 16mhz
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  sei();
}

void loop(){
  
}

ISR(TIMER1_COMPA_vect){  
  static boolean state = true;
  
  TCNT1 = 0;
  
  if(state) {                                 //start pulse
    digitalWrite(sigPin, onState);
    OCR1A = PPM_PulseLen * 2;
    state = false;
  }
  else{                                       //end pulse and calculate when to start the next pulse
    static byte cur_chan_numb;
    static unsigned int calc_rest;
  
    digitalWrite(sigPin, !onState);
    state = true;

    if(cur_chan_numb >= chanel_number){
      cur_chan_numb = 0;
      calc_rest = calc_rest + PPM_PulseLen;// 
      OCR1A = (PPM_FrLen - calc_rest) * 2;
      calc_rest = 0;
    }
    else{
      OCR1A = (ppm[cur_chan_numb] - PPM_PulseLen) * 2;
      calc_rest = calc_rest + ppm[cur_chan_numb];
      cur_chan_numb++;
    }     
  }
}

Any help, comments will be appreciated.

Thank you in advance for your help,

Francois
 
Last edited:
Thank you very much Paul, I used the library and it works great, but I do not understand how it works... So i can't modify it :confused:

if I could learn to do what is shown below for AVR (blue, code for AVR)

1. Setup a timer on the Teensy 3.1 & Start a timer
cli();
TCCR1A = 0; // set entire TCCR1 register to 0
TCCR1B = 0;
OCR1A = 100; // compare match register
TCCR1B |= (1 << WGM12); // turn on CTC mode
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
sei();


2. Set a presacaler
TCCR1B |= (1 << CS11); // 8 prescaler: 0,5 microseconds at 16mhz

3. Change the match register (trigger)
OCR1A = XXX // what every value i decide

4. Run into a loop every time the match register is reached
ISR(TIMER1_COMPA_vect)

That would be amazing!
 
Last edited:
Also when I read your library info (PulsePosition.h), I have to say it is quite amazing, especially the synchronization of the pins.

If I could modify it to generate both positive and negative shift ppm, I would be awesome.
 
Regarding your question on the timer, the info you seek is in the reference manual.

http://www.pjrc.com/teensy/K20P64M72SF1RM.pdf

The FTM0 timer (FlexTimer Module) is documented in chapter 36, starting on page 769. However, the FlexTimer is far more advanced and complex than the 16 bit AVR timer. The AVR and FlexTimer do not have a simple 1:1 correspondence, so there's no easy way to translate AVR code to FlexTimer code.
 
yes, negative shift is the only thing missing. You wrote a very good library.

But it missing for both PulsePositionOutput and PulsePositionInput.

Thank you for your help!
 
I've added polarity options to PulsePositionInput and PulsePositionOutput. For both objects, you can specify either RISING or FALLING edges. For example:

Code:
// Simple loopback test: create 1 output to transmit
// test pulses, and 1 input to receive the pulses
PulsePositionOutput myOut(FALLING);
PulsePositionInput myIn(FALLING);

I've updated the web page and code on github.

I did only a couple quick tests, using my oscilloscope. Please let me know how this works for you?
 
Thank you so much Paul! :) :D

It is working great, easy and simple. I'll post pictures of the project when I am done!

Thank you again,

Francois
 
I'll post pictures of the project when I am done!

Great. I'm looking forward to seeing it.

This PulsePosition library is still very new, so any photos and other practical info about applying it to RC projects would really help others to know what they could do with it.
 
My transmitter is going well! The teensy is awesome.

Here is a picture:

IMG_1460.jpg

I have question: I have a small buzzer (http://www.adafruit.com/products/1536) and I would like the buzzer to bip once every second or so (speed of the biping will depend on other parameter, like in this video


So I need to turn a pin on every X ms and off every Y ms (X and Y varies). I am not sure how to do that, taking into account that I am already using the pulse position library to generate ppm signal. So i do not want the interrupt used for the buzzer to mess up the pulse position library.

Any idea?

Thank you

Francois
 
Last edited:
So I need to turn a pin on every X ms and off every Y ms (X and Y varies). I am not sure how to do that, taking into account that I am already using the pulse position library to generate ppm signal. So i do not want the interrupt used for the buzzer to mess up the pulse position library.

I had never seen the PulsePosition library before. Very interesting.

I did note that the documentation says "PulsePosition is designed ... with tolerance for significant interrupt latency caused by other libraries" so it might still be worth trying analogWriteFrequency(), the tone library, or even IntervalTimer.

Good luck! I look forward to reading more about this project.
 
Looks like a fun project.

I have also played around some with remote controls. Originally up on Lynxmotion Jim Frye built a few RC based remote controls for a few of us as a thank you gift. Later I converted mine from RC to XBee. Back then we used a Basic Atom Pro 28 for the processor (threads about this up on Lynxmotion forum). More recently I converted my prototype one over to using a Teensy 3.1 on one of my own boards. More details up on the thread: http://www.robotshop.com/forum/diy-remote-xbee-going-to-teensy-3-1-t9220

Sorry I am not trying to hijack the thread.

Looking at the buzzer from Adafruit, I am not sure that is what you really want. It looks like a real simple one frequency buzzer. That is you supply 3-5v to it and it plays a 2K sound for as long as you supply voltage to it. What I use on most of my boards is: http://www.digikey.com/product-search/en/audio-products/buzzers/720967?k=102-1155-nd
While I will be honest and say I have cheated and on several other boards like: (Basic Micro Arc32, ArbotixM, ...), I have cheated and simply connect the speaker up directly to IO pin using a simple RC extender cable and have not blown a board yet, on my own boards, I usually connect them through transister, cap, diode...

If you actually want the simple one tone buzzer, then it should be real simple coding wise to set it up. All you need to do is a simple digitalWrite(pin, HIGH) when you want the sound to start and like wise set it LOW when you want it to end. Many different ways to do that depending on your code and how accurate you wish for it to be. Example depending on your main loop you could simply test the system clock (millis) to see if it is time to start/stop...

If you instead use a speaker, If I were you, I would probably start off using the Tone library and see if this works for you. On some of my projects I use some brute force code to generate tones, which ain't pretty, eats up the processor, but does not touch any of the interrupts... It simply does a delayMicroseconds for half of the frequency time and toggles the pin...

Again this looks like a fun project!

Kurt
 
Hello everyone, I am also using the ppm library to send data from my RC to a flight controller card. Maybe you could help me with some guidelines to implement the iBus communication. Thank you for any help you can give me, best regards.
There is a code implemented for arduino that generates error when using the ISR timer1 in teensy 3.2
 
Status
Not open for further replies.
Back
Top