teensy 3.5 interrupt using timer ovf

Status
Not open for further replies.

demkat1

Member
Hello,
I was using arduino so far and this is my first project in teensy.
In fact I have a code running on arduino and want to transfer it (or modify accordingly) so to use teensy.
The whole task is to (mainly) calculate the product of 2 trigonometric numbers , do some conversions and output the result (for use with a DAC).
Because the floats are out of discussion (too slow on arduino), i used integer calculations instead (for example: uint16_t(sin(a)*65535)).
But even with this approach I cant go faster than 20usec

the project uses an ovf interrupt every 30usec (to redirect flow to the main routine), on a mega2560.

The code for the i/r is
Code:
void setup()
{
  ....
  pinMode(40, OUTPUT); //oscillo
  pinMode(41, OUTPUT); //oscillo
  for (x = 22; x < 38; x++) pinMode(x, OUTPUT); //outputs
  
  cli();
  // use timer5
  // ( initialise  values)
  TCCR5A = 0;    // set entire TCCR1A register to 0
  TCCR5B = 0;    // set entire TCCR1B register to 0

  TIMSK0 &= ~(1 << TOIE0); //disable millis(), avoid jittering
  TIMSK5 |= (1 << TOIE1 ); // Enable overflow interrupt
  sei();

  TCNT5 = LoadValue; // Preload timer with precalculated value

  TCCR5B |= (1 << CS10 ); // Set up timer at Fcpu /1
  
  
}
//---------------------------------------------------

ISR ( TIMER5_OVF_vect )
{ 
//show on osc "start of isr"
PORTG = PORTG & B11111101;
  ....
//show on osc "end of isr"
  PORTG = PORTG | B00000010;
}

I have done all the changes for the input, calculation and output, (for example instead of PORTA ->GPIOA_PDDR and GPIOA_PDOR ), no help is needed for this part
But I cannot find a solution for the i/r
Some help would be much appreciated
thank you.
 
Note, the Teensy 3.5 and 3.6 have hardware single precision (float) support. It is much faster than the software emulation on an Arudino AVR. The AVR processor used in Arduinos is much slower than a 3.5 running at 120Mhz even without doing floating point.

You do need to use the single precision version of the math functions (i.e. sinf, cosf, logf, etc.) to make sure the compiler does not convert the values to double precision (which is emulated).

You probably want to use the 'f' suffix on all floating point constants (Teensy actually uses an option to make normal FP constants single precision, but it is a good habit to get into to to declare constants explicitly single precision).
 
ok, thanks for the float tip.
i was reading the manual and found the following example in page 1092
Code:
// turn on PIT
PIT_MCR = 0x00;
// Timer 1
PIT_LDVAL1 = 0x0003E7FF; // setup timer 1 for 256000 cycles
PIT_TCTRL1 = TIE; // enable Timer 1 interrupts
PIT_TCTRL1 |= TEN; // start Timer 1

It is clear, well ALL -1: when time elapse and interrupt "set" , how to call "?function?"? Is there a specific name?
[in arduino, above code ,by default (for timer5 ovf), is "ISR ( TIMER5_OVF_vect )" ]
 
Does this give the needed timer setup and usage: pjrc.com ... teensy ... IntervalTimer.html

Paul used it for TALKIE library update to DAC on Teensy for sound updates in :: ...\hardware\teensy\avr\libraries\Talkie\Talkie.cpp

About lines 143 and 180 following "timerInterrupt" shows 'IntervalTimer' being setup and used.

That PIT timer code comes from :: ...\hardware\teensy\avr\cores\teensy3\IntervalTimer.cpp
 
thank you.
In general, IntervalTimer seems to be ok, but not here. Ill explain.
Reminder: teensy 3.5 @120Mhz
What is first needed is a about 5 usec interval. This can be done with IntervalTimer (although havent test, but i believe it can). Secondly, Every about 250 intervals the project will receive an syncronizinig signal from "outer space" and must be able to adjust the interval so : 250*interval=synchronising period. To do so there must be capability of "fine trimming" the interval to steps of 5usec/250=20nsec. (ok, if 20 cannot be done I go as low as possible by changing the 250 stepnumber).
After this calculation, i believe NO library can do it. Thats why I need "direct" interrupt programming (or, at least, for my thinking) .
 
Included was the source file location for the IntervalTimer usage of the PIT timer . . . if the IntervalTimer interface won't allow the fine tuning then the sources will show how to set up the _isr function and complete the usage of the RAW PIT timer as requested in post #3.
 
Included was the source file location for the IntervalTimer usage of the PIT timer . . . if the IntervalTimer interface won't allow the fine tuning then the sources will show how to set up the _isr function and complete the usage of the RAW PIT timer as requested in post #3.

I saw the "links". But the talkie.cpp has about 204lines, and no "timerInterrupt" (to see after). Anyway I didnot find any kind of "raw" as you say, except ISR(TIMER1_COMPA_vect). (this is also on the "interrupt" tips list)
Because I do not have a setup yet to test, you are kindly requested to reply with a "y" or "n", if this is the whole story. it will save me lot of time just looking, without knowing specific details of the teensy
thank you
 
I saw the "links". But the talkie.cpp has about 204lines, and no "timerInterrupt" (to see after). Anyway I didnot find any kind of "raw" as you say, except ISR(TIMER1_COMPA_vect). (this is also on the "interrupt" tips list)
Because I do not have a setup yet to test, you are kindly requested to reply with a "y" or "n", if this is the whole story. it will save me lot of time just looking, without knowing specific details of the teensy
thank you

That PIT timer code comes from :: ...\hardware\teensy\avr\cores\teensy3\IntervalTimer.cpp

Talkie uses PIT timer through IntervalTimer ... above on your install is where the RAW code for IntervalTimer.cpp is located and shows setting up the function for the _isr().

The Talkie code interestingly enough starts a timer with a small buffer. The buffer is exhausted after each 200 of the 8000Hz timer updates. That was the part I added to allow it to be non blocking on Teensy - and play queued next sounds. Wholly different and slower than your needs, but that is what is in Talkie.

Linked was to the PJRC summary page for IntervalTimer - the bottom line there suggests FTM usage for shorter intervals.

y? / n? ... YMMV
 
What is first needed is a about 5 usec interval. This can be done with IntervalTimer (although havent test, but i believe it can). Secondly, Every about 250 intervals the project will receive an syncronizinig signal from "outer space" and must be able to adjust the interval so : 250*interval=synchronising period. To do so there must be capability of "fine trimming" the interval to steps of 5usec/250=20nsec. (ok, if 20 cannot be done I go as low as possible by changing the 250 stepnumber).
After this calculation, i believe NO library can do it. Thats why I need "direct" interrupt programming (or, at least, for my thinking) .

IntervalTimer can give you most of this. The minimum interval is 36 cycles of F_BUS, which is 60 MHz on Teensy 3.5. (but whether you can do anything useful in your ISR function during such a short time is another matter) You can use the update() function to change the interval. The timer runs from F_BUS, so the granularity for the interval time is increments of 16.7 ns. The PIT timers used by IntervalTimer have 24 bit counters, so IntervalTimer gives a very easy way to get highly precise intervals which are configurable over quite a wide range.

But how you'll receive that sync pulse is a good question. The very best way to measure the timing of when an edge is actually received is by input capture on the FTM timers. Maybe you could use the FreqMeasure library for this? It will give you the elapsed time since the last pulse. It also runs from F_BUS, so the granularity of the time measurement is increments of 16.7 ns.

If you don't need exact phase alignment between the sync pulse and timer interrupts, you can probably just use FreqMeasure & IntervalTimer without messing with low-level hardware registers. FreqMeasure can give you highly accurate measure of the time between those pulses and IntervalTimer can call your interrupt at a periodic rate that's very well controlled. But the delay between these 2 will depend on software, even if you get the IntervalTimer running at a precise multiple of the measured frequency.

If you need them precisely in phase, I believe you can get that too, but it's going to mean messing with the FTM timer instead of using the IntervalTimer. There are many possible way you might try to implement this. I would personally recommend starting with the FreqMeasure code, which configures the FTM timer to count up from 0 to 65535 at 60 MHz. In uses both the capture interrupt to get a 16 bit timestamp of when the pulse begins, and the overflow to extend the count to 32 bits. Getting that 32 bits extension is tricky, so I highly recommend using the FreqMeasure code because that hard part is done and very well tested.

To generate your periodic interrupt, I'd suggest using another of the FTM channels in compare mode. Within each interrupt, you'd reprogram the compare register to a new 16 bit count of when you want the next interrupt to occur. For example, suppose your last 2 input captures gave 24531 and 49014. That means the time for you sync pulse is 24483 counts on the FTM timer. To get periodic interrupts at 250 times this rate, you'd need to interrupt every 97.932 timer cycles. Maybe you just round that up to 98. Or maybe you alternate between 97 and 98 timer cycles in some pattern which works out to 97.932 over the long run. In any case, the first thing your compare interrupt code would do is clear the interrupt flag and then add 97 or 98 to the compare register. The timer will already have counted somewhat, but it doesn't matter as long as it's not too much and your ISR code is fast enough to get the new compare number written. The next interrupt will happen with the timer reaches that number.

I just picked some arbitrary numbers for this example, only to explain. In practice the numbers will likely be much larger. That's why you'll need the 32 bit extended timestamps for when each pulse started. You might also encounter an issue if your interrupts are needed slower than 916 Hz. But you said every 5 us. Still, the issue is the 16 bit count rolls over 915.5 times per second. So if you want to have an interrupt in the future at a precise moment the timer reaches a particular count, it has to be a 16 bit number. Probably the simplest way to deal with this issue is generating a dummy interrupt. If you need an interrupt in 80000 cycles in the future (relative to the timer generating the current interrrupt), you'd probably have a dummy interrupt at 40000 cycles in the future, and then in that interrupt set the compare for another 40000 timer cycles.

This way, you can measure your pulse with 16.7 ns precision and schedule interrupts to occur at precise moments relative to the same 16 bit timer counts.

You still might have slightly variable interrupt latency from the moment of the compare interrupt to when your ISR code actually runs. You can minimize this by setting the FTM timer to the highest interrupt priority and using FASTRUN on the function so it's placed in no-wait-state RAM rather than the flash memory (where timing varies slightly due to cache misses).

If the ISR latency is still an issue (likely only under 10 cycles of the 120 MHz clock, but only if no code is disabling interrupts), depending on what you're actually doing in that ISR, you might also be able to leverage the DMA controller to automatically do memory transfers upon the timer compare, and then instead have the DMA channel generate the interrupt so you can work a microsecond later with whatever the DMA transfer did at the precise timer compare moment. But that's getting into quite a lot of very advanced low-level hardware stuff. The good news is the hardware does have these very advanced capabilities, but it's quite challenging to do such low-level programming.

As a start, I'd suggest at least giving IntervalTimer and FreqMeasure a try. Over and over on this forum we hear from people determined to do low-level AVR stuff to get AVR level performance, but Teensy 3.5 is so fast that it's pretty easy to get that AVR level of performance using the easy libs. Even if you're skeptical they could ever perform well, I'd still suggest at least trying the easy libs first. Many Arduino libs have a reputation for bloat or poor performance, but these Teensy libs are quite efficient. Often they do work out very well, needing a lot less effort than messing with the very powerful but much-more-complex-than-AVR hardware.
 
Last edited:
Hello, Im back just returned from lab.
First of all, many many thanks to all, especially to Paul for the above analysis.

intervalTimer did the job!. After some noinsense (first time is always first time) and burn-out 3-4 outputs (didnt see that there were shorts on breadboard from previous setup), with interval 100usec the "call" function run at 19usec with float calculations. Didnt have internet to read - remember post#2 above so I tried with one uint16 and one float fixed values. Got something around 1.5us!!!.
Then I used .update() and ....relaxed to the back of my chair wathing on oscilloscope, interval sweeping from 6 down to 3 usec at steps of 20ns (perhaps 40 but its ok) and back again!!! And my function running on every period....
Excellent
Lots of thanks
 
Back again
yesterday I had this code
Code:
#include <avr/io.h>
#include <avr/interrupt.h>
#include <Math.h>
#include <kinetis.h>


union
{
  struct
  { // 4096->24bits
    uint16_t garbage16LSB;
    uint8_t MyLSB;
    uint8_t MyMSB;
  };
  volatile uint32_t val1;
} sample1;

union
{
  struct
  { // 4096->24bits
    uint16_t garbage16LSB;
    uint8_t MyLSB;
    uint8_t MyMSB;
  };
  volatile uint32_t val2;
} sample2;

union
{
  struct
  { // 4096->24bits
    uint16_t garbage16LSB;
    uint8_t MyLSB;
    uint8_t MyMSB;
  };
  volatile uint32_t val3;
} sample3;

int x = 0;

int16_t newangle = 2995;
volatile int16_t angle1 = 2995;
volatile int16_t angle2 = 3150;
volatile int16_t angle3 = 1950;
volatile float longval1 = 0.0;
volatile float longval2 = 0.0;
volatile float longval3 = 0.0;
volatile float Outval1 = 0.0;
volatile float Outval2 = 0.0;
volatile float Outval3 = 0.0;
//volatile uint32_t Val = 0;
volatile float LoadValue = 15.0; // 5000;//65300 65500 ~20us, 65000 ~37us 65250 ~23 65350
volatile uint16_t point = 0;


volatile byte LoopsSinceLastZero = 0;
volatile uint16_t IdealLoops = 100; //change in setup
volatile uint8_t OutSignal = 1;
volatile byte photo1 = 0;
volatile byte photo2 = 0;
volatile byte previousphoto1 = 0;
volatile boolean NewAngleFlag = false;
//volatile unsigned long Strt[10];
//volatile unsigned long Eend[10];

char inchar = ' ';

String txtmsg = "";
IntervalTimer myTimer;
void setup()
{
  Serial.begin(9600);
  pinMode(0, OUTPUT);
  GPIOA_PDDR = B11111111; //  DDR A
  GPIOA_PDOR  = 0; //PORT A
  longval1 = cos(radians(angle1));

  myTimer.begin(Routine, LoadValue);  //Loadvalue
}
void loop() {

  ReadSerial();

}

void Routine(void) {
  // ****************  VOLATILES ********************

  //  digitalWrite(0, LOW);
  GPIOA_PDOR = B11111111; // pin24 A5// PORT A ==PORTA | B00000001;//  PORTG = PORTG & B11111101;

  //new cycle


  LoopsSinceLastZero++;
  point ++;

  if (point > 360) point = 0;
// *******************************THIS PART
  sample1.val1 = uint32_t(40000 * longval1 * 32767 + 32767)  ;
// ************************************************************
  GPIOA_PDOR  = B00000000; // pin24 A5
  //  digitalWrite(0, HIGH);


}

void ReadSerial(void) {

  if (Serial.available() > 0) {

    inchar = Serial.read();
    txtmsg += inchar;
  }

  if (inchar == 10) {//line feed?

    Serial.println(txtmsg);
    if (txtmsg.substring(0, 5) == "angle")      {
      newangle = uint16_t( txtmsg.substring(5, 9).toInt());
      if (newangle != angle1) {
        longval1 = cos(radians(angle1));
        NewAngleFlag = true;
      }
    }
    else if (txtmsg.substring(0, 4) == "load")
    { LoadValue = float(txtmsg.substring(4, 5).toInt());
      LoadValue += float(txtmsg.substring(6, 8).toInt()) / 100.0;
      myTimer.update(min(LoadValue, 100));
    }
    else if (txtmsg.substring(0, 5) == "point")
      point = uint16_t(txtmsg.substring(5, 9).toInt());
    txtmsg = "";
  }
}

then I changed the above marked section (and corresponding type of variables) EDIT : CosVal1 is longval1 renamed
Code:
  Valpoint = sinf(radians(point / 10.0) * 32787.5 + 32787.5);
  sample1.val1 = uint32_t(Valpoint * (CosVal1 * 32787.5 + 32787.5));
and everything crashed.
First of all, I admit its a rather long routine but I cannot split it. If i "flag" and go out to execute, need to put a routine(); every 2 statements.
Although I had an indication that "routine" was running, there was no output on "GPIOA" nor I could change LoadValue through SerialMonitor (ReadSerial()) at the "gaps" when routine was not unning.
After some changes I realised that this happened at Intervals <30 usec, cant say why. Interrupts while routine running?
Anyway, Im happy making calculations with integers16 &32 @<2usec. With that routine process time and execution 800 "points" an update of 16.7ns gives royghly 13usec expansion, but ill update 1 every 4 points (and back to loadvalue) so its ok.

Q Paul : "Sync from outer space" will come from outside hardware, perhaps zero-crossing and precise delay balance on next period, perhaps use a nano to send a signal to teensy and "read" that pin in process.
 
Hmmmm. it seems that the problem was here
Code:
...
GPIOA_PDOR = B11111111; 
...
GPIOA_PDOR = B00000000;

some bits of GPIOA is used for usb (Serial), correct?
 
some bits of GPIOA is used for usb (Serial), correct?

No. The USB serial pins are dedicated pins not shared by any of the GPIO pins.

Serial1 (not USB) is shared with PTB16 & PTB17. But those are not port A. And even if they were, the pin mux controls which peripheral connects to the pin. So unless you change the mux, Serial1 would control those 2 pins regardless of what you do within the GPIO peripheral. This isn't like AVR where the GPIO registers still have some effects on the pins while they're in use by peripherals. In these ARM chips, a mux for each pin sets which peripheral controls the pin. GPIO is treated the same as any other peripheral. When the mux routes to the pin to anything else, the GPIO has no connection to the pin. But that's for Serial1.

With USB Serial, the D+ and D- signals are dedicated pin. Those pins aren't shared with any other functions.
 
and everything crashed.

Perhaps "crashed" means your code is now taking longer than 15 microseconds to perform all the extra work?

Since the interrupt happens every 15 us, if you take too long your interrupt function will prevent anything else from running.
 
that about port A is my mistake, not configured properly.

Perhaps "crashed" means your code is now taking longer than 15 microseconds to perform all the extra work?

Since the interrupt happens every 15 us, if you take too long your interrupt function will prevent anything else from running.

It has been a long time since that happened but it is always good to hear something relevant.
Yes, in fact I had minimised time to some 1us and re-adjust the interrupt accordingly , without thinking that a "insert a readserial()" ...and you are correct. The program worked at speeds of about ["minimum time for IR procedure" + about 0.7us].
As a matter of fact the whole experience with teensy gave other ideas concluding to a very sharp hardware turn. The project is redesigned on a different basis and works now on a mega 2560.

Of course teensy 3.5 speed is something unbelieve-able (in respect to 2560)
thank you
 
Status
Not open for further replies.
Back
Top