How to synchronise PWM with external source

Status
Not open for further replies.

achrn

New member
Hello, I've not posted before, though I've intermittently lurked for several years. However, I've now got something where I haven't found the answer previously discussed, and I think it will be an easy one for someone:

I'm using a teensy 3.2. I'm generating PWM on pins 3, 4, 9 & 10 (so I'm using both FTM0 and FTM1), and may use more in future. This is at only about 50 Hz, and my pulses are all in the 1.0mS to 2.0mS range (those pulses control ordinary radio-control components - servos and motor speed controllers).

However, I'm calculating my desired pulse width on the basis of a number of inputs, one of which is the output from a radio-control receiver, which provides a data packet at 18mS intervals (so about 55.5Hz). I redo my calculation every time I get a receiver data packet, and it would be convenient to generate a new set of output pulses each time I recalculate. The problem is synchronising the PWM output with when I've done the calculation - and if the frequencies don't match (which they don't exactly) I do my calculation just too late for the update to affect the PWM outputs, and then my output pulses lag nearly a whole cycle behind the input data that caused them.

So the ways this might work are:

1: Does the PWM library or the timers it sits on top of have a 'synchronise to external signal' option?

2: Is there a library a bit like PWM analogWrite which lets me specify a different pulse width for each of several pins, but I get one pulse of specified width on a pin when triggered, not on a regular cycle (though it would be triggered fairly regularly, immediately after each calculation). If not a library already, can someone point me to what part of the manual might let me achieve this?

3: It seems to me this might be possible by setting up for PWM at sure-to-be-lower-than-I-need frequency (say 40 Hz), but then when I want a pulse, manually writing to the relevant timer count register to make it think the time has come for a new pulse. That is, if it start a pulse each time a 16 bit timer overflows, when I wanted a pulse I'd write the duration wherever it needs to be, then 65535 into that timer's count register.

I've been reading the Flextimer chapter in the reference manual, and the PWM code in ...cores/teensy3/pins_teensy.c but I'm really not fluent at C or tried reading the manual before, and I'm finding it a bit baffling. Some pointers of where I should be focusing would be great (especially if the answer is not one of these two places). Thanks.
 
PWM is always synchronized inside an FTM, so you should in a first step swap pins to use up to 8 channels from the same FTM. An update of all output signals happens always after the main counter reaches the mod value and is reset to 0. Thus, it is just important to update the mod value and all channel compare values during the same cycle. If you want to be sure to achieve this, having enough time to update everything, add a timer overflow interrupt and look during this, if new calculated values are available. If yes, write these to the corresponding registers which won‘t yet have an effect. At the moment when the current PWM cycle finishes, all your new values will be latched and a new cycle will start with the new values on all channels.
 
Thanks for the response. I think that's answering a different issue (one I hadn't even thought of yet) to what I was considering. I'm looking to synchronise the teensy-generated PWMs with an external repeating pulse, rather than looking to ensure changes to the teensy generated PWMs all happen in the same wave. My synchronise doesn't need to be tight (or simultaneous) with the external, but I'd like a fairly constant offset and the main issue is I want one teensy-generated pulse soon after each external pulse, even if the frequencies drift differently.

Thus, I think I want either a way to make a one-off (or single shot) PWM pulse or a way to bring forward a scheduled PWM pulse to sooner than it would left to its own devices. Having done more digging through forums, and read the FTM chapter several more times, I don't think that's possible with FTM - I was hoping to be able to either get a one-shot pulse or set it up to run slower than I want and restart it whenever I want a pulse, but I'm not finding a way to do that (though I am getting some unexpected pulse patterns on my logic analyser, so I still think maybe there's something to be done).

I'm thinking now I either use IntervalTimer or https://github.com/luni64/TeensyDelay but if I've overlooked something obvious (like a 'onepulse' library) it would be good to hear of it.
 
How to synchronise PWM with external source - SOLVED

This turns out to be embarrassingly easy (for my use case anyway).

After a lot of trying to persuade the various sync functions in FTM to do what they are not apparently intended to do, I find that all you need to do is switch it off and switch it on again.

Less facetiously, to trigger a new pulse on FTM0 pins (assuming you already have PWM up and running on the relevant pins, probably by the usual analogWriteFrequency() and analogWrite() functions:
Code:
// stop FTM by clearing CLKS bits
uint8_t FTM0ClksBits=FTM0_SC & 0x18;
FTM0_SC=FTM0_SC & 0xE7;
// reload counters
FTM0_CNT=0;
// start FTM by putting same bits back
FTM0_SC=FTM0_SC | FTM0ClksBits;

Or, if you know you are using F_TIMER (not the alternative 31250Hz clock):
Code:
FTM0_SC=FTM0_SC & 0xE7;
FTM0_CNT=0;
FTM0_SC=FTM0_SC | 0x08;

There are some 'gotchas' though, mainly relating to buffering of the changes made to the counter registers under the default configuration. In particular, it seems that changes made to FTMx_CnV registers (being what determines the duration of the output pulse) only get copied into place in the FTMx module at the loadpoint at the end of the PWM period. If you reset the FTM before getting there, your value doesn't make it to the FTM until later.

Since the FTMs are initialised in analogWriteFrequency() with a zero duty cycle, i.e. FTMx_CnV=0, this doesn't work as might be expected:
Code:
// set up PWM on pin 9
analogWriteFrequency(9, 40);  // 40Hz, quite slow 
analogWriteResolution(16);
analogWrite(9, 1000);

/* do less than 25mS of stuff, ie still in first PWM period */

// trigger a new pulse on pin 9 (which is on FTM0)
FTM0_SC=FTM0_SC & 0xE7;
FTM0_CNT=0;
FTM0_SC=FTM0_SC | 0x08;

The analogWriteFrequency() sets the timer running with zero duty cycle, the analogWrite(9, 1000) is buffered, but because the timer is switched off and on again before getting to a loadpoint (when FTM1 gets to its MOD value and resets) the value from the analogWrite doesn't get written when you supposedly trigger a new one and you don't get a pulse (though it does turn up 25mS later when the FTM counter does finally get to MOD). Rather than producing a pulse early, the code above doesn't generate output until later than might be expected. If the 'other stuff' is 20mS, say, you don't get output until 45mS - analogWriteFrequency(9, 40) sets a 40Hz zero duty cycle running (so no output) the 'trigger a new pulse' sets another 40Hz zero duty cycle running at 20mS and the first pulse with a non-zero duty cycle only starts when the counter for that second one resets at 45mS.

The fix is to set or reset the duty cycle inside the switch-it-off-and-on-again. This gets you a 1000 duration pulse immediately, even if you've only just configured the PWM and it's still in a first zero duty cycle period:
Code:
// trigger a new pulse on pin 9 (which is on FTM0)
FTM0_SC=FTM0_SC & 0xE7;
analogWrite(9, 1000);
FTM0_CNT=0;
FTM0_SC=FTM0_SC | 0x08;

My examples are all FTM0. FTM1 works the same. I expect (but have not tested) FTM2 does as well - the manual suggest these behaviours are the same in all the FTMs.
 
Status
Not open for further replies.
Back
Top