Forum Rule: Always post complete source code & details to reproduce any issue!

Thread: Timer interrupts > 1MHz ?

1. Timer interrupts > 1MHz ?

Is there a way to reliably run a routine at 0.5 usec intervals on a Teensy 3.2?

Can the IntervalTimer be configured to run at 2MHz (or faster)? How?

I want to generate a set of synchronized pulsed outputs, the fastest is 1MHz and the others have small offsets relative to the first.

Thank you

2. If you go with IntervalTimer on T3.2, lets say you configure for 120mhz.
Which by default has F_BUS of 60mhz. You can experiment setting F_BUS to 120mhz by editing Kinetis.h...

And if we look at where begin with float is defined:
Code:
```	bool begin(void (*funct)(), float microseconds) {
if (microseconds <= 0 || microseconds > MAX_PERIOD) return false;
uint32_t cycles = (float)(F_BUS / 1000000) * microseconds - 0.5;
if (cycles < 36) return false;
return beginCycles(funct, cycles);
}```
So if we reverse this for default F_BUS at that speed
36 = 60*ms -.5 =0.60833 microseconds. or about 1.64MHz... Assuming my math is correct.
Again you could maybe push F_BUS to the higher speed. Or you could enable faster than 120mhz on Teensy...
But again this is overclocking the chip so could overheat could be less reliable. Likewise if F_BUS at 120mhz ... 3.28mhz max...

At 96mhz (what I normally ran with).
F_BUS by default 48, can change to 96...
36 = 48*ms - .5 ... .76... or 1.31mhz... But again if you push F_BUS you could probably get 2...

But again you may need to doublecheck my math

3. Thank you, Can a project have a private version of kinetis.h? How?

4. Originally Posted by DrM
Thank you, Can a project have a private version of kinetis.h? How?
All TeensyDuino installed files are 'your' copy and you can edit them in place where installed for a private version.

However any reinstall or update with TeensyDuino Installer will overwrite that private copy, and there is no provided programmatic way around that.

Perhaps editing kinetis.h to be just :: #include "my_kinetis.h"
where "my_kinetis.h" is the private copy - it would likely survive the TeensyInstaller - and if it added a unique external variable it could break the build to let you know to check and replace the above text in "kinetis.h"?

5. "Perhaps editing kinetis.h to be just :: #include "my_kinetis.h""

I was hoping for something like a path or environment variable that I could set per project. Editing the working source tree for arduino/teensy would effect all of my projects.

6. Anyway ... I've dug into the code a bit, I wanted to see how analogwrite does it, and it seems like the answer is that it does it in hardware.

Experimenting with the interval timer, and the pwm, it seems that neither is fantastic for synchronizing multiple channels.

I started two PWM square waves, and oddly one is shifted from the other, and the pattern on the scope jumps around depending on which I use as the scope trigger.

7. With an UNZIP install - at least on WINDOWS - multiple IDE/Teensy versions can be maintained ... not sure if that is a helpful option to isolate projects.

PWM's run in groups off of timers associated with the pin at hand. AFAIK pins on the same timer group run in sync together? Two pins on alternate timers won't trigger in sync. KurtE and other have posted distilled/english versions of the groupings expressed in the code and manual. Any chance the pins in use are fed from unique timers?

Also in the table "PWM Frequency" here : pjrc.com/teensy/td_pulse.html

FrankB has a post on DEFS.h and a way to change the build adding in custom #defines that could be edited into #ifdef perhaps for conditional building? But where kinetis.h is used is systemic to many files for that T_3.x and as noted - not seen a way to alter that. Perhaps if using a makefile - but not even sure that would offer method and the granularity to change that between builds.

8. Hmm, yes they are different timers. I understood that to mean that the all of the pins on one timer have to output the same frequency. I should perhaps try setting frequencies individually across the same group and see what happens. The other thing though, is that for this application, one waveform needs to be slightly delayed from another waveform. And I need to be able to stop and start some of them. I didnt see the stop function yet in the library.

9. Originally Posted by DrM
Hmm, yes they are different timers. I understood that to mean that the all of the pins on one timer have to output the same frequency. I should perhaps try setting frequencies individually across the same group and see what happens.
YMMV - I only started reading on post #3 - saw p#1 earlier ... and skipped - and then KurtE's #2 reply.
but freq of a timer when giving sync'd output timing will also tie the frequency.

Having played with F_BUS in the past (mostly on T_3.6) it seemed harmless fun to use and ignore the 'OC caveat' in testing for other purposes.

10. Originally Posted by DrM
Thank you, Can a project have a private version of kinetis.h? How?
This type of per sketch customization is sort of difficult in the "pure" Arduino environment.

If you look in Kinetis.h you will see lines like:
Code:
```#elif (F_CPU == 96000000)
#define F_PLL 96000000
#ifndef F_BUS
#define F_BUS 48000000
//#define F_BUS 96000000
#endif
#define F_MEM 24000000```
So it only defines it if it is not already defined.
So for example if you can get the compile line options to include something like: -DF_BUS=96000000
Than it is defined and it will use that faster setting.

I believe in several of the other ways to build sketches like makefiles, VisualTeansy... You can probably easily customize the defines that get passed in like this.

Also some users like @FrankB has updated platform.txt to take defines out of some sketch specified file (defs.h) and add them to the build process.

And there are probably other hacks one can do with boards.txt... to hack on adding on another Optimize option like: Faster F_BUS where you copy the faster option and then on the commands list add it....

As for Synchronizing two PWM signals, not sure. I have not tried this. I know someone was working on something like this recently with T4. if it were me, I would probably look around and see what happens if I hack it.
That is I might look up which timer is associated with each of the pins you are trying to sychronize and see if I quickly try to set the FTMxCNT register of each of them to 0, will it get them more in sync? Or something like that depending on your actual needed offsets. like are the all multiples of each other.

I have done some servo controller code before, but with that I often used a timer and figure out delays to the next event... Sometime not ideal.

11. > Is there a way to reliably run a routine at 0.5 usec intervals on a Teensy 3.2

You can always turn off interrupts and poll ARM_DWT_CYCCNT. Jitter will be quite low.

12. Originally Posted by jonr
> Is there a way to reliably run a routine at 0.5 usec intervals on a Teensy 3.2

You can always turn off interrupts and poll ARM_DWT_CYCCNT. Jitter will be quite low.
yes, if the only purpose is the timed output control that will give best response - wasn't sure if that was only task at hand?

On T_3.2 the CYCNT isn't on by default.
Code:
```	if ( ARM_DWT_CYCCNT == ARM_DWT_CYCCNT ) {
// Enable CPU Cycle Count
ARM_DEMCR |= ARM_DEMCR_TRCENA;
ARM_DWT_CTRL |= ARM_DWT_CTRL_CYCCNTENA;
}```
Stay in a tight loop - or use loop() - but define an empty yield().

Mark code as FASTRUN to keep it in RAM.

digitalToggleFast() can work if the gating logic always just changes known state of known constant pin#.

13. Originally Posted by DrM
Is there a way to reliably run a routine at 0.5 usec intervals on a Teensy 3.2?
That is an interesting question.

That is if you are running at 120mhz and you want to run at .5us, if my quick and dirty calculations are correct, that gives you 60 clock cycles per iteration. So not much to work with.
So for example if you re trying to use an ISR, it has been awhile since I looked into or read on average how many cycles the overhead of the ISR takes, and I believe with T3.x, the Interval timer first calls to internal ISR, which clears the ISR and then calls you function.

So my guess is you will need to do like mentioned by others and then maybe use cycle count...

But the first thing I would do would be to write a version of your code and see if it can actually run fast enough?

Also just throwing straws here as have not seen actual requirements of what you are actually trying to do.

14. @defragster Thank you. That looks great. And yes that is the task at hand. In response to a command received through Serial,read(), I need to run three waveforms and read the adc at fixed intervals, all synchronized against each other, and then after some number of cycles, stop and send a record back over the Serial interface.

I take it ARM_DWT_CYCCNT is the cycle counter and the conditional in your first line of code is true if it is not running?

After that, I just loop over testing ARM_DWT_CYCCNT, and take the time as cycles divided by CPU clock speed (c.f. 96MHZ)?

Can i write to ARM_DWT_CYCCNT to zero it?

Is there any reason to leave it on, or turn it off, between invocations?

15. The ARM_DWT_CYCCNT when running ticks with each clock cycle - that test just gets it read once and works as observed to read again for the "==" test.

When the counter is not running the value is unchanging, when coded to read twice it changes between showing the clock is running with that: if ( ARM_DWT_CYCCNT == ARM_DWT_CYCCNT ) {

Not sure starting it often hurts - but only once in setup() is needed, and on T_4.x it is started before setup() - it is happy to run, may burn extra electrons?, and reads as fast as anything it seems.

No need to ZERO the ARM_DWT_CYCCNT. Just treat it like uint32_t of millis() or micros() - it is free running with rollover - and uint32_t math works to give abs diff when used like below:
Code:
``` // with counter running
uint32_t startA;
uint32_t startB;
uint32_t EndAll;

// Get marching orders to cycle the pins times on/off
// do some calc against F_CPU for T_3.2 to know how many cycles in the desired time periods
uint32_t someA = 9600; // these values in CPU CYCLES
uint32_t someB = 19600;
uint32_t someEnd = 35798642;

// for fun each of these like this will have a couple counts difference - re-order to start as needed and put same times in all : startA = startB = startEnd = ARM_DWT_CYCCNT;
startA = ARM_DWT_CYCCNT;
startB = ARM_DWT_CYCCNT;
startEnd = ARM_DWT_CYCCNT;
digitalWriteFast( pinA, HIGH );
digitalWriteFast( pinB, HIGH );

// Perhaps call a FASTRUN void headDownTiming() to do this - dealing with params or globals as your conscious allows
while ( ARM_DWT_CYCCNT - EndAll > someEnd ) {
if ( ARM_DWT_CYCCNT - startA > someA ) digitalPinToggle( pinA );
if ( ARM_DWT_CYCCNT - startB > someB ) digitalPinToggle( pinB );
}
// deed done tell the world```
Okay just pseudo coded that in browser - then re-read and saw you want three not two.

If this gives the needed speed and behavior it should run like clockwork once started without jitter from or overhead of interrupts. And interrupts could be disabled to help that.

16. Okay just pseudo coded that in browser - then re-read and saw you want three not two.
That's okay, that is certainly the idea. The small difference of course is unavoidable anyway, unless you have hardware that lets you write multiple pins in one register. Fortunately for this application it is also not very consequential.

Thank you to everybody, and I feel remiss to not mention @jonr also, I saw the comment on cyccnt there later.

17. Are you deciding whether to transmit 1 or 0 at the very instant you're in the ISR? If the waveform can be generated beforehand, I would look at DMA instead. You can easily get 2MHz without worrying about CPU utilization.

18. @Pilot DMA sounds interesting, it would surely be robust. Can it handle digital output on three pins simultaneously with analog input on a fourth?

19. +1 on DMA for fast digital outputs, even with multiple pins.