Go Down

Topic: Millis Function in Due (Read 915 times) previous topic - next topic

parnal

Hello ,

I would like to know if its possible to disable millis() timer interrupt only ?

Thanks :)

ard_newbie

I suspect an XY problem.

However, you can selectively block interrupts with priority number higher or equal to a prefixed number set with the priority group and the NVIC_SetPriority() CMSIS function.

Beware: Priority groups, Priority levels of interrupt handlers, premption and subpriority are a bit tricky.

millis() uses a tickcount activated by SysTick_Handler(). If you want to stop SysTick_Handler() from running in background, you have to:
- Give a low prirority to SysTick_Handler()
- Set BasePri to a higher urgency level (i.e. a lower number) as SysTick_Handler()

Here is an example sketch :

Code: [Select]

void setup() {
  Serial.begin(250000);
  /* Set SysTick interrupt preemption priority to 3 */
  NVIC_SetPriority(SysTick_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 3, 0));

  /*
    If you want to selectively block interrupts with priority number higher or equal to 3,
    you could use the following code:and the NVIC_SetPriority() CMSIS function.
  */
  __set_BASEPRI(3 << (8 - __NVIC_PRIO_BITS));  // priority shifted left !!


}


void loop() {

  Serial.println(millis());  // millis() will not change anymore
  for (uint64_t i; i < 0xFFFFFFFFFFFFFF; i++);
}


westfw

Or you could stop the systick timer itself, or disable its ability to cause interrupts.

parnal

Thank you so much for your reply.

@ard_newbie :  1) The aim is to generate a pulse profile with the function micros() on Due without allowing miilis  to generate any interruption in order to generate a jitter free pulse .
2) Where can I read more about Systick timer?


@westfw : Can you please elaborate , how can it be done ?

westfw

Micros() uses systick as well, so you wouldn't want to turn it off entirely.

ard_newbie


micros(), millis() and delay() are all using tickcount increment. And tickcount is incremented each ms thru SysTick_Handler().  Stopping SysTick_Handler() from running in background is not the solution to your problem...

If you need a 1 us pulse, a Timer Counter interrupt is the right solution.  If you are not familiar with Timer Counters on a DUE, there is an easy workaround, use TRNG peripheral.  And in parallel, give the lowest priority to SysTick_Handler() so that SysTic interrupt can't preempt TRNG interrupt.

The True Random Number Generator, once enabled, generates a 32_bit True Random Number each and every 1 us. Here is an example sketch:

Code: [Select]

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);

  NVIC_SetPriority(SysTick_IRQn, 0xFF); // Give SysTick interrupt the lowest priority
 
  PMC->PMC_PCER1 |= PMC_PCER1_PID41;    // TRNG power ON
  TRNG->TRNG_CR = TRNG_CR_ENABLE | TRNG_CR_KEY(0x524e47);
  TRNG->TRNG_IER = TRNG_IER_DATRDY;
  NVIC_SetPriority(TRNG_IRQn, 0);       // Be sure that TRNG interrupt has the highest priority
  NVIC_EnableIRQ(TRNG_IRQn);

}

void loop() {

}

/*     Fires every 1 us   ****/
void TRNG_Handler(void) {

  static int counter = 1000000;
  TRNG->TRNG_ISR;

  if (counter-- == 0) {
    blink();
    counter = 1000000;
  }

}

void blink(void) {

  static boolean led;
  digitalWrite(LED_BUILTIN, led = !led);
}


parnal

@ard_newbie : will this allow the micros to function properly?

ard_newbie

What prevents you from trying ? :)

parnal

Well your code allows both micros and millis to function and I am again on the initial stage, where I want to use only micros and not millis .(Micros - enable, Millis - disable ).

ard_newbie


The aim is to generate a pulse profile with the function micros() (WHY would you need micros() for that ?) on Due without allowing miilis  to generate any interruption in order to generate a jitter free pulse.

As I told you, both micros() and millis(), and delay() use tickcount increment generated by a SysTick interrupt every 1 ms. Do you understand that ?

parnal

It cannot be true that micros() is updated only every 1ms, you can easily try out yourself and see that it counts with 1us resolution.
The only problem is that I get (sporadic or systematic) delays, supposedly generated by the millis() timer every 1ms, with the effect that the system stops for up to 4us.
My intention is to use the existing micros (1us) timer as timebase, but somehow eliminate the interruptions that cause the additional delays (stop times). If these delays are caused by the 1ms interrupt then I want to turn this off. If it is caused by some other interrupt, that one needs to be turned off.
All I want is a 32bit counter running with 1us resolution in the background, and have the possibility to read its actual value whenever I want - without being interrupted by anything.
If it's not possible to use micros() function for this purpose, can I somehow set up another timer for this purpose?
A random number won't help me here. And I'm not keen on creating interrupt service routines, as they will slow down my program.
(BTW: I'd like to have a solution for both, Arduino Due and Arduino MkrZero.)

AdderD

You are free to disable interrupts entirely and then read the systick timer directly. It runs at CPU speed (so, 84Mhz) and I believe the default configuration was for it to overflow after 84000 ticks which is 1ms. If you turn off interrupts I don't know if it still resets at that point, it probably does. You can read the value and time things that way. You can also still create precise delays by executing NOP instructions in a loop. I can't find it right now but in the past I had a delayNanoseconds() function I wrote that would create very precise delays by NOP'ing for the proper number of times.

westfw

Code: [Select]
 SysTick->CTRL  &= ~SysTick_CTRL_ENABLE_Msk;
Would turn off systick interrupts, while still leaving it counting.

Code: [Select]
 SysTick->CTRL  |= SysTick_CTRL_ENABLE_Msk;
Would turn it back on.

It will still reload every millisecond, so while the interrupts are turned off, micros()  probably will not be accurate for more than 1ms...


Quote
It cannot be true that micros() is updated only every 1ms, you can easily try out yourself and see that it counts with 1us resolution.
micros() does math using the current millisecond time (increment every millisecond, by an interrupt), AND the current value of the SysTick count (decrements every clock cycle.)  Something like
Code: [Select]
  return millis*1000 + (84000-SysTick->VAL)*(1/84000000);
(More complicated than that because reasons.)
If you're using micros() for timing and the SysTick counter has wrapped around more than once, you won't get the right value.  It looks like the code might handle one wrap OK...

parnal

 So basically ,

SysTick->CTRL  &= ~SysTick_CTRL_ENABLE_Msk ; ---- > this corresponds to noInterrupts();

and SysTick->CTRL  |= SysTick_CTRL_ENABLE_Msk; -----> this corresponds to interrupts();
 

westfw

Quote
SysTick->CTRL  &= ~SysTick_CTRL_ENABLE_Msk ; ---- > this corresponds to noInterrupts();
and SysTick->CTRL  |= SysTick_CTRL_ENABLE_Msk; -----> this corresponds to interrupts();
Those ONLY disable the timer used for millis(); other interrupts (serial, perhaps?) can still occur.

Due DOES include it's own version of interrupts() and noInterrupts()
Maybe you should just use those?


Go Up