PPM Library Questions

Status
Not open for further replies.
PulsePosition Library Questions

Question-1:
I've been struggling with the library for days. I need a single PPM input, without any PPM output. I can compile and run the loopback example without any problems. It runs, and I get the PPM input messages. But as soon as I disable the PulsePositionOutput and remove it, the input no longer works (no messages from PPM Input in loopback sample). I've added debug statements to the ISR and confirmed that no input frames are detected when the PPM output object isn't used.

So here's my question: what's the trick? Is PPM input supposed to be autonomous without any need for PPM output? (That's how I need it.)

Question-2:
The PPM sample page lists the criteria to support multiple input and output streams. Is that intended with a single PulsePositionInput/Output object with multiple calls to .Begin? Or is that with multiple object instances, each with its own call to .Begin?

Question-3:
Last but not least. Why care about the channelmask in the ISR? Shouldn't just reading the hardware be enough to detect if an interrupt has occurred? The way I'm thinking, the interrupt can't occur unless it was already programmed and enabled by .Begin ...so why the double check with channelmask?
 
Last edited:
Is PPM input supposed to be autonomous without any need for PPM output? (That's how I need it.)

It should be. Maybe this is a bug in the library? I don't know from only this message.

I'll need you to post the code to reproduce the problem. Even if it's only a minor edit of an example, please post the *exact* code you're compiling. Small details can matter.


The PPM sample page lists the criteria to support multiple input and output streams. Is that intended with a single PulsePositionInput/Output object with multiple calls to .Begin? Or is that with multiple object instances, each with its own call to .Begin?

Multiple objects

Question-3:
Why care about the channelmask in the ISR?

Saves CPU time in the common cases.
 
I've attached the files. PulsePosition.cpp, PulsePosition.h, and LoopBack.ino are exactly as they appear on the github (or should be exactly). These files, when compiled, generate output on the USB debugger to show PPM input.

Change to LoopBack_Bad.ino, and no PPM input occurs. The only difference (should be) commenting out the PulsePositionOutput objects.
 

Attachments

  • PulsePosition.cpp
    12.3 KB · Views: 170
  • PulsePosition.h
    2.9 KB · Views: 147
  • LoopBack.ino
    810 bytes · Views: 127
  • LoopBack_Bad.ino
    823 bytes · Views: 132
No solution, but additional info. I have used a maple and DUE to provide ppm pulses to the teensy LoopBack example.
ppmout pin to pin 6 of teensy and common ground. That worked with no changes to the LoopBack sketch. Here's what DUE is sending and what teensy reports (first channel increments, 3rd channel 2000, others are 1000), total of 8 channels

Code:
12 :  1407.81  1000.00  2000.00  1000.00  1000.00  1000.00  1000.00  1000.00  
13 :  1411.75  1000.00  2000.00  1000.00  1000.00  1000.00  1000.00  1000.00  
14 :  1415.67  1000.00  1999.98  1000.00  1000.00  1000.00  1000.00  1000.00  
15 :  1419.58  1000.00  2000.00  1000.00  1000.00  1000.00  1000.00  1000.00
All is good.

If I comment out the myOut stuff as described in this thread, then I still get output data reported, BUT, rate is slower, and instead of the 8 channels of output that the DUE was sending, the teensy (3.0) is claiming it's seeing 16 channels. And first channel is no longer monotonic, and channel 3 is not 2000 ???

Code:
13 :  1760.77  999.98  634.67  1000.00  1000.00  1000.00  1000.00  1000.00  1316.56  1756.85  1000.00  2000.00  1000.00  999.98  1000.00  1000.00  
14 :  1729.42  1000.00  634.65  1000.00  1000.00  1000.00  1000.00  1000.00  1347.92  1725.48  1000.00  2000.00  999.98  1000.00  1000.00  1000.00  
15 :  1733.31  1000.00  634.67  1000.00  1000.00  1000.00  1000.00  1000.00  1343.98  1737.23  1000.00  2000.00  1000.00  1000.00  1000.00  1000.00

something is weird ....?
 
I was honing in on a similar phenomenon right before leaving for the weekend. Even when myOut is disabled, output interrupts were still occurring. I did some experiments and found I could get the output interrupts to stop when I programmed its associated FTMx_CnSC register = 0 (saying that register from memory...not 100% sure). That tells me the power on defaults might be the problem here and further FTM hardware initialization may be required to make this work correctly. I also noticed in the FTM register spec that adjacent channels can be cascaded together to detect rising and falling edge conditions. I didn't have time to look up any of the defaults, but I started to wonder again about power on defaults. Unfortunately, I didn't have time to experiment further as I was leaving for the weekend.
 
Last edited:
If myOut stuff is disabled, I don't see any activity in Output isr ... channelmask sort of guarantees that.

Another symptom (but still no cure), I added a counter "else" in the Input isr in PulsePosition.cpp
Code:
  if (write_index < PULSEPOSITION_MAXCHANNELS) {
            pulse_width[write_index++] = count;
        }   else tom2++;

with myOut, the count stays at 0, but with myOut disabled, that counter climbs by 95 every ppm cycle, hence the 16 channels being reported (MAXCHANNELS is 16). the timers count value was 48195
the 100 extra interrupts per ppm cycle (22 ms), suggests that 488 hz cycle of the FTM PWM??
 
Last edited:
I hooked up an analyzer and added some pin-toggles to the isr's in PulsePosition.cpp. Here are some snapshots.

First trace is with myOut enabled (but no write's) in the LoopBack sketch, and input pulses coming from a Maple.
outon.jpg
Ignore the "SPI labels" for the pin toggling. Analyzer channel 0 is the FTM isr, channel 1 is in the timer-oflow "if", channel 2 are pulses coming in to teensy on pin 6, channel 3 is the output isr, channel 4 is the input isr. You can see the timer overflows (abou every 1.2ms), and the match of the input stream to the input isr. the output isr appears to passed all of the timer-overflow events. The sketch is logging "good" info on the Maple input stream

the following trace is with all myOut's disabled. the sketch logging is erroneous, reporting 16 channels?
outoff.jpg

In the trace above, there is no activity from the output isr (as expected), but there are no timer overflows, or anything during the 16.6 ms "sync" event separating the 6 servo events??

still only obersvations without understanding...
 
OK, in my opinion the timer overflow event must always happen (the isr keeps a running 32-bit count based on the overflow of the 16-bit FTM timer). PulsePosition.cpp is not enabling FTM_SC_TOIE -- I think it should. So I enabled FTM_SC_TOIE in various places and it appears to work both with myOut enabled and with it commented out of the LoopBack sketch. I'll await Paul's blessing and the official changes. Typical updated section looks like

Code:
    if (FTM0_MOD != 0xFFFF || FTM0_SC != ([B][COLOR="#0000FF"]FTM_SC_TOIE |[/COLOR][/B] FTM_SC_CLKS(1) | FTM_SC_PS(0))) {
        FTM0_SC = 0;
        FTM0_CNT = 0;
        FTM0_MOD = 0xFFFF;
        FTM0_SC = [B][COLOR="#0000FF"]FTM_SC_TOIE |[/COLOR][/B] FTM_SC_CLKS(1) | FTM_SC_PS(0);
        FTM0_MODE = 0;
    }

Logic analyzer trace also looks good.


Paul, while you are updating PulsePosition.cpp, I (as others have recommended) would recommend changing the default signal to 1500 (typical servo "idle")

#define TX_DEFAULT_SIGNAL 1500.0
 
Last edited:
Good catch. I just got back from my weekend, and was going to start back in where I left off. I was planning to go through the register specs as well -- because I figured I would find something exactly like this; in fact now that I think about it...this was the exact register setting I was going to investigate first. Good catch, and I have tested and confirmed the patch on my end as well.

BTW, I have a version of PPM that works on all the various FTM's (pins, 3, 4, 25, and 32). Not sure if anybody cares or wants it. But if they do, I can post it.
 
Last edited:
It will be interesting to hear Paul's view of the problem. He has code at the end of the Output ISR to schedule a timer interrupt at a width close to the FTM's overflow period -- suggesting he didn't want to enable the FTM_SC_TOIE interrupt ?? That code was effective as long as sketch did a PulsePositionOutput.begin(xx).
 
It will be interesting to hear Paul's view of the problem. He has code at the end of the Output ISR to schedule a timer interrupt at a width close to the FTM's overflow period -- suggesting he didn't want to enable the FTM_SC_TOIE interrupt ?? That code was effective as long as sketch did a PulsePositionOutput.begin(xx).

After looking at this further, I've concluded that is TOIE is needed. When looking much deeper into it, I found this strange line of code in both Input and Output ISRs.
if (val > 0xE000 && overflow_inc) count--;

That line of code is the clue that TOIE was originally intended to be enabled. Here's why.

The ISR was programmed to handle two sources of interrupt: 1) TOIE, and 2) Rising/Falling edges. TOIE runs independent and doesn't stop when the Rising/Falling interrupt occurs. However, when RISING/FALLING interrupt occurs, the counter value is immediately latched on the FTMx_CnV register. Even though count was latched, TOIE is still running. The line of code above is designed to prevent a race condition. That code was designed to handle the case where TOIE interrupt occurs after the count was latched AND before you get a chance to service the RISING/FALLING interrupt. One could probably simulate the overflow interrupt just by looking to see if the count wrapped around. But that's really ugly -- uglier than the code above. Because when you get down to it, even the code above is an approximation. If you approximate it wrong (0xE000), then you'll get an out-of-bounds PPM count value on some rare occasions.
 
Thanks for the insight on the rollover race-condition.

After paying more attention to the math, I realize Paul's code at end of output ISR is handling the issue that it takes more than one timer cycle to complete a PPM pulse of more than 1.3 ms (16-bit counter at 48 mhz) ... hence the "state" variable and more magic numbers 58000 and 60000.
 
Last edited:
After paying more attention to the math, I realize Paul's code at end of output ISR is handling the issue that it takes more than one timer cycle to complete a PPM pulse of more than 1.3 ms (16-bit counter at 48 mhz) ...

Yes, exactly, the user could request a pulse longer than a 16 bit wrap-around.

hence the "state" variable and more magic numbers 58000 and 60000.

Yes, these are arbitrary. If the number of required counts is approaching the 16 bit wrap-around limit (> 60000), it's broken into 2 compare matches. The actual target is set for a couple thousand less, so there'll (hopefully) always be time to set up the compare match for the remainder of the required counts. In other words, if the required time is 60001 counts, the compare match is programmed to generate an interrupt at 58000. That way, when the compare match is written again 58000 cycles later, the required target will be at least 2001 into the future. There's always some interrupt latency, so you don't want the target to be too soon, since you might write the target after the counter has already passed it, which would give far-too-long output.
 
Paul welcome back. Did you notice further up in the thread were some proposed changes to fix missing timer overflow interrupts?
 
New version of PulsePosition lib seems to be working! Thanks

I tested with teensy 3.0 and 3.1, using LoopBack example (with pin5 to pin6), and with maple providing pulses to pin6. maple test works with and without myOut stuff. Also tested shiftregisteroutput example hooked up to analyzer ( I don't have the 74HCT164 counter IC). Framepin and txpin looked good on analyzer.

Noted TX_DEFAULT_SIGNAL changed to 1500.

you do good work. ;)
ppmshift.jpg
 
Last edited:
I'm wondering if there might be a bug in ftm0_isr(). You set overflow_inc to TRUE in the intial "if", but you don't return out of that if-block, so you fall into the body of the isr, and at the end overflow_inc is set to FALSE ??

Edit: Upon further review,
code is probably OK, since the flag is handling the race condition that might occur in the input ISR.
 
Last edited:
I'm trying to use the PulsePosition library in Arduino IDE 1.0.5 and getting the errors shown below whenever I include PulsePosition
does it only work on the latest versions of Arduino?

I downloaded the latest version of pulseposition from GitHub.

#include <PulsePosition.h>

void setup() {
}

void loop() {
}

C:\arduino-1.0.5\libraries\PulsePosition\PulsePosition.cpp: In constructor 'PulsePositionOutput::pulsePositionOutput()':
C:\arduino-1.0.5\libraries\PulsePosition\PulsePosition.cpp:109:19: error: 'CLOCKS_PER_MICROSECOND' was not declared in this scope
C:\arduino-1.0.5\libraries\PulsePosition\PulsePosition.cpp: In constructor 'PulsePositionOutput::pulsePositionOutput(int)':
C:\arduino-1.0.5\libraries\PulsePosition\PulsePosition.cpp:119:19: error: 'CLOCKS_PER_MICROSECOND' was not declared in this scope
C:\arduino-1.0.5\libraries\PulsePosition\PulsePosition.cpp: In member function 'bool PulsePositionOutput::begin(uint8_t, uint8_t)':
C:\arduino-1.0.5\libraries\PulsePosition\PulsePosition.cpp:169:17: error: 'FRAME_PIN_SET' was not declared in this scope
C:\arduino-1.0.5\libraries\PulsePosition\PulsePosition.cpp:178:24: error: 'CSC_CHANGE' was not declared in this scope
C:\arduino-1.0.5\libraries\PulsePosition\PulsePosition.cpp: In member function 'bool PulsePositionOutput::write(uint8_t, float)':
 
Arduino 1.6 does not have this problem with the library , it compiles fine, however i cannot use arduino 1.6 because there are other problems with Arduino 1.6 .

I need to work with arduino 1.0.5 because that is what our system compiles on for arduino mega boards.
I have tried re-installing the pulse position library multiple times in arduino 1.0.5 but it still doesnt compile.

Darn. we are trapped here because if we cant use this we cant use teensy.
 
dont worry, problem solved, sorry to waste your time, i just upgraded to the last release of arduino 1.0.6 and it works fine. just missed that release , was still on 1.0.5
 
Status
Not open for further replies.
Back
Top