Greetings to all members of the Forum. I would like to express my deepest gratitude, with respect to the title of this post. The last week I am trying to capture and decode a PPM Signal from an RC transmitter. Although I have seen many different approaches and codes online, using methods like Input Capture Mode and Pin Change Interrupts I would like to seek some help on my approach. Most of the codes I have found online present glitches and instabilities while trying to read the time between the rising pulses. I have tried to read and understand how to set up the Input Capture registers on the Atmega 32u4 chip, aka Arduino Pro Micro / Lepnardo, but I cannot find a proper way of defining the registers as well as the ISR (_vec) function. In the next few lines I will try to explain the code I have written so far, but I will need some help with respect to the initialization of registers. Please bare with me, as any help will be much appreciated. Without further ado, let me introduce you to the following lines.
#define STICK_CENTER 1500
#define STICK_HALFWAY 500
#define THRESHOLD 15
#define MIN_PULSE_WIDTH (STICK_CENTER - STICK_HALFWAY - 15)
#define MAX_PULSE_WIDTH (STICK_CENTER + STICK_HALFWAY + 15)
#define RC_CHANNELS_COUNT 8
#define PPM_CAPTURE_PIN 4
#define NEWFRAME_PULSE_WIDTH 2000
#define TIMER_COUNT_DIVIDER 2
void rc_setup_ppm() {
setupPins();
initTimer();
}
void setupPins(void) {
// Set up the Input Capture pin
pinMode(PPM_CAPTURE_PIN, INPUT);
digitalWrite(PPM_CAPTURE_PIN, 1); // enable the pullup
}
void initTimer(void) {
cli();
TIFR = (1 << ICF1); //Clear any Interrupt flags
PRR |= (0 << PRTIM1); //Enable Timer1
TCCR1A = 0; //Clear TCCR1A
TCCR1B = 0; //Clear TCCR1B
TCCR1C = 0; //Clear TCCR1C
TCCR1B = (1 << ICNC1) | (1 << ICES1) | (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10);
TIMSK1 |= (1 << ICIE1); //Enable input capture
TIFR1 = (1 << ICF1); //Clear interrupt flag
sei();
}
uint16_t adjust(uint16_t diff, uint8_t chan) {
return diff / TIMER_COUNT_DIVIDER;
}
ISR(TIMER1_CAPT_vect) {
union twoBytes {
uint16_t word;
uint8_t byte[2];
} timeValue;
uint16_t now, diff;
static uint16_t last = 0;
static uint8_t chan = 0;
timeValue.byte[0] = ICR1L; // grab captured timer value (low byte)
timeValue.byte[1] = ICR1H; // grab captured timer value (high byte)
now = timeValue.word;
diff = now - last;
last = now;
//all numbers are microseconds multiplied by TIMER_COUNT_DIVIDER (as prescaler is set to 1/8 of 16 MHz)
if (diff > (NEWFRAME_PULSE_WIDTH * TIMER_COUNT_DIVIDER)) {
chan = 0; // new data frame detected, start again
}
else {
if (diff > (MIN_PULSE_WIDTH * TIMER_COUNT_DIVIDER - THRESHOLD)
&& diff < (MAX_PULSE_WIDTH * TIMER_COUNT_DIVIDER + THRESHOLD)
&& chan < RC_CHANNELS_COUNT)
{
rc_values[chan] = adjust(diff, chan); //store detected value
}
chan++; //no value detected within expected range, move to next channel
}
}
Given the above I would like to help setting the initialization routine of the registers. Some information about my signal:
PPM includes 8 different channels as a summed signal. In total 9 pulses while each one, has a time duration from 998 - 2012 microsecs.
The time frame of each pulse is easy to be changed though the transmitter settings. Also the total period of the frame can be changed. By the first I mean the HIGH time of each pulse, and by the second I mean the total frame period after the 8 channels have been scanned. Given these, the time periods are 26.5 mills and 350 microsecs.
Enough with these. Going back to the actual code you will see the cli(); and sei(); functions. Do I really have to set these during that part of the code? Also do I need to reset or delete any active flags? Should I include any flags (e.g. Overflow flag after the counter has reached its peack value)
At this point I would also like to mention that I am using a prescaler of 1/8, which results to a tick period of apx. 0.5 microsecs. Also, I am capturing only the rising part of the pulses. And I am using an Input canceler, but given the datasheet specifications I have seen that the cancel frame is 4 periods long. Is there any possible way to use a low latency filtering scheme?
Please take some time to read this post, as I am confident enough that any responses will help others too clarify these topics. I know that I have included many different aspects and I feel sorry for this.
Many thanks, Koutsoukos Theodosios