Teensy 4.0 timer1, one second loop question

Status
Not open for further replies.

PickyBiker

Well-known member
The attached sketch should print a number every second but on my Teensy 4.0 it is printing almost 2 times per second.

Can someone tell me what I am doing wrong?

Code:
#include <TimerOne.h>

void setup()
{
  Serial.begin(115200);
  Timer1.initialize(100000);   // .1 second timer
  Timer1.attachInterrupt(ISR_Timer);
  Timer1.start();
}

void loop()
{
}

/***************************************************************************************************************/
void ISR_Timer()
{
  static int cnt = 0, cnt2 = 0;

  cnt += 1;
  if (cnt >= 10)
  {
    cnt = 0;
    cnt2 += 1;
    Serial.println(cnt2);
  }
}
 
I changed the code to move the Serial.print out of the ISR. It did not help. It is still printing almost twice per second instead of once.


Here is the modified code.
Code:
#include <TimerOne.h>

void setup()
{
  Serial.begin(115200);
  Timer1.initialize(100000);   // .1 second timer
  Timer1.attachInterrupt(ISR_Timer);
  Timer1.start();
}

volatile boolean go = false;

void loop()
{
  static int c = 0;

  if (go == true)
  {
    c += 1;
    go = false;
    Serial.println(c);
  }
}

/***************************************************************************************************************/
void ISR_Timer()
{
  static int cnt = 0;

  cnt += 1;
  if (cnt >= 10)
  {
    cnt = 0;
    go = true;
  }
}
 
So i changed the initialization to Timer1.initialize(1000); (1ms) and the counter to if (cnt >= 1000) (should = 1 sec.

It is still too fast. Does this mean the Timerlib code can't work correctly at any initialization at 600MHz?
 
@defragster - Maybe...

Here is a quick hacked up version that appears to work:
Code:
#include <TimerOne.h>

elapsedMillis em;

void setup()
{
  Serial.begin(115200);
  Timer1.initialize(10000);   // .1 second timer
  Timer1.attachInterrupt(ISR_Timer);
  Timer1.start();
  em=0;
}

volatile boolean go = false;
void loop()
{
  static int c = 0;

  if (go == true)
  {
    c += 1;
    go = false;
    Serial.print(c);
    Serial.print(" ");
    Serial.println(em, DEC);
    em = 0;
  }
}

/***************************************************************************************************************/
void ISR_Timer()
{
  static int cnt = 0;

  cnt += 1;
  if (cnt >= 100)
  {
    cnt = 0;
    go = true;
  }
  asm("dsb");
}
 
Yes that works

asm("dsb")???? I have never seen heard of a dsb instruction, I know it is assembly but dsb ???

I can't find a document for the 600 MHz Cortex-M7 processor that describes dsb.

Can someone explain what dsb is and why it fixes this problem?
 
Thanks for confirming @KurtE - never know when that helps and when it might be needed - but recognized the 'double count' - and luckily remembered the 'word' "dsb" to search and confirm it was spelled right for use.

Would be good for a WIKI TeensyUser/doc/wiki note - that and how to unset interrupt flag for those interrupts that need it.

Of course finding and remembering and searching the Wiki assume you know here to start and what you are looking for.

@luni - I don't find an _ISR section in the Wiki with a page search

I just woke a thread with links to maybe get more use from and life into the WIKI ::
Code:
[B][URL="https://forum.pjrc.com/threads/59987-Teensy-WIKI?p=249872&viewfull=1#post249872"]pjrc.com/threads/59987-Teensy-WIKI[/URL][/B]
 
Last edited:
Yes that works

asm("dsb")???? I have never seen heard of a dsb instruction, I know it is assembly but dsb ???

I can't find a document for the 600 MHz Cortex-M7 processor that describes dsb.

Can someone explain what dsb is and why it fixes this problem?



Quick BING search led to :: developer.arm.com/documentation/dui0646/c/the-cortex-m7-instruction-set/miscellaneous-instructions/dsb
Code:
Operation
DSB acts as a special data synchronization memory barrier. Instructions that come after the DSB, in program order, do not execute until the DSB instruction completes. The DSB instruction completes when all explicit memory accesses before it complete.

Even reading that it isn't clear - but there is some race condition if the _isr() completes too fast? This assures the processor does housekeeping in some fashion to be aware that interrupt was handled and prevents it from doubling.

One of those links led to this that may be a bit more informative: stackoverflow.com/questions/15491751/real-life-use-cases-of-barriers-dsb-dmb-isb-in-arm
Code:
If you are mainly a device driver developer, then examples are fairly straightforward to find - whenever there is a dependency in your code on a previous access having had an effect ([B][SIZE=3]cleared an interrupt source[/SIZE][/B], written a DMA descriptor) before some other access is performed (re-enabling interrupts, initiating the DMA transaction).

...

DSB - whenever a memory access needs to have completed before program execution progresses.

That poster "wrote a 3-part blog series that's a bit more lightweight, and finishes off with an ARM-specific view. First part is Memory access ordering - an introduction.":
community.arm.com/developer/ip-products/processors/b/processors-ip-blog/posts/memory-access-ordering---an-introduction

Wow - I cruised through post 12,000 and just noticed ...
 
asm("dsb")???? I have never seen heard of a dsb instruction, I know it is assembly but dsb ???

The problem is the resetting of the interrupt flag. The peripherals like timers etc and the ARM core run on different busses. Due to synchronization and caching it can take quite long until the resetting of the interrupt flag is actually seen by the timer. Thus, if your interrupt routine is short the flag appears still set when you leave your ISR. -> the ISR is called again right after it finished.

The dsb instruction prevents this by blocking further execution until all memory accesses are finished.

I had a look at the TimerOne lib. As discussed by Kurt and Defragster, it does not implement the required dsb indeed. That should definitely be added in the lib after invoking the callback. I can do some tests and a PR later this week.

Code:
#elif defined(__arm__) && defined(TEENSYDUINO) && defined(__IMXRT1062__)
void TimerOne::isr(void)
{
  FLEXPWM1_SM3STS = FLEXPWM_SMSTS_RF;
  Timer1.isrCallback();
}

Anyway, the TimerOne lib is just a compatibility layer for the AVR processors. In the Teensy world you would use an Intervaltimer instead. It handles resetting of the interrupt flag correctly. Alternatively you can have a look at the TeensyTimerTool (https://github.com/luni64/TeensyTimerTool/wiki) which gives you access to even more timers.

@luni - I don't find an _ISR section in the Wiki with a page search
That's true indeed. I can add something (at least some stubs for others to fill in). However, IMHO, the dsb stuff is something which should be handled by the libraries not by the user. It probably was forgotten for the seldom used TimerOne.
 
The problem is the resetting of the interrupt flag.
...
That's true indeed. I can add something (at least some stubs for others to fill in). However, IMHO, the dsb stuff is something which should be handled by the libraries not by the user. It probably was forgotten for the seldom used TimerOne.

Interrupt timer flag - boiled out in my notes - but more hand waving and less clear - good note.

A general section on _isr() usage and what NOT to do there would be good - and maybe a quick note ... " If DOUBLE hits are observed ... try the dsb "

Hopefully the post on the March WIKI thread will draw attention to it. Probably better to move to the empty STICKY thread FrankB made - or make one so the github.com/TeensyUser/doc/wiki is easier to find.
 
Hopefully the post on the March WIKI thread will draw attention to it.
I just had a look at the traffic on the wiki. It has some 250 views per week. So it seems to be actually used. However, there was not much new content over the last months...
 
After starting the WIKI thread in 2015 - then seeing your usable effort ... I keep forgetting about it as I reply to daily posts ... :( - and have to search to find the github link

Finding your March 2020 intro thread left dormant - and Franks' unused STICKY post ... activity on one of those threads could boost exposure and use and maybe item additions/update.

Think closing the startup thread and linking anf boosting the Sticky thread with updates would be good? Forum is awesome - but hard to find stuff ... so answering lots of new threads is just pulling link to an old thread ...

Have seen a couple 'Teensy not work' issues (charge cable or just "did i break it") and twice in the last ~week using the CYCCNT came up for timing.

Maybe on STICKY Teensy WIKI thread a good post on trouble shooting links and Check the Wiki link would help users get self help faster?
 
Status
Not open for further replies.
Back
Top