elapsedMicros: how to disable almost all interrupts?

Status
Not open for further replies.

gwideman

Well-known member
When using elapsedMicros, (such as looping until a certain time) it would improve precision if unneeded interrupts could be disabled.

However, elapsedMicros itself relies on at least some interrupts running, so it seems one can't just CLI.

So what is the best approach to disabling all interrupts (which may not be individually known to the application programmer) except for the needed ones?
 
There are sveral macros - you can write a loop to iterate trough all interrupts-numbers and check if the are enabled or not (and save this some how - a linked list or array), and disable them.
Later you can use your list to re-enable them.
Code:
void listInterrupts() {
    
#if defined(__MK66FX1M0__)
const char isrName[][24] = {
    "dma_ch0","dma_ch1","dma_ch2","dma_ch3","dma_ch4","dma_ch5","dma_ch6","dma_ch7",
    "dma_ch8","dma_ch9","dma_ch10","dma_ch11","dma_ch12","dma_ch13","dma_ch14","dma_ch15",
    "dma_error","mcm","flash_cmd","flash_error","low_voltage","wakeup","watchdog",
    "randnum","i2c0","i2c1","spi0","spi1","i2s0_tx","i2s0_rx","unused 46","uart0_status",
    "uart0_error","uart1_status","uart1_error","uart2_status","uart2_error","uart3_status",
    "uart3_error","adc0","cmp0","cmp1","ftm0","ftm1","ftm2","cmt","rtc_alarm","rtc_seconds",
    "pit0","pit1","pit2","pit3","pdb","usb","usb_charge","unused","dac0","mcg_isr","lptmr",
    "porta","portb","portc","portd","porte","software (audio)","spi2","uart4_status","uart4_error",
    "unused","unused","cmp2","ftm3","dac1","adc1","i2c2","can0_message","can0_bus_off",
    "can0_error","can0_tx_warn","can0_rx_warn","can0_wakeup","sdhc","enet_timer","enet_tx",
    "enet_rx","enet_error","lpuart0_status","tsi0","tpm1","tpm2","usbhs_phy","i2c3","cmp3",
    "usbhs","can1_message","can1_bus_off","can1_error","can1_tx_warn","can1_rx_warn","can1_wakeup"};
#endif    
    
  unsigned adrFaultNMI = (unsigned)_VectorsRam[3];
  unsigned adrUnusedInt = (unsigned)_VectorsRam[IRQ_FTFL_COLLISION + 16];//IRQ_FTFL_COLLISION is normally unused
  unsigned adr;

  Serial.println("Interrupts in use:");
  
#if 0  
  Serial.println("NMI (non-maskable):");
  for (unsigned i = 1; i < 16; i++) {
    adr = (unsigned)_VectorsRam[i];
    if (adr != adrUnusedInt) {
      Serial.print(i);
      Serial.print(": \t");
      if (adr == adrFaultNMI) {
        Serial.print("Fault NMI");
      } else {
        Serial.print("\t");
      }      
      Serial.print("\t0x");
      Serial.print(adr, HEX);      
      Serial.println();
    }
     
  }
#endif
  
  Serial.println("IRQ:");
  for (unsigned i = 0; i < NVIC_NUM_INTERRUPTS; i++) {
    adr = (unsigned)_VectorsRam[i + 16];
    if (adr != adrUnusedInt) {
      Serial.print(i);
      Serial.print(": ");
      Serial.print("\tPriority:");
      Serial.print(NVIC_GET_PRIORITY(i));
      Serial.print("\t0x");
      Serial.print(adr, HEX); 
      if (adr < 0x10000000) Serial.print("\t");
      Serial.print("\t");
      Serial.print(isrName[i]);
      if (NVIC_IS_ENABLED(i)) Serial.print("\t is enabled");
      Serial.println();
    }
  }
  Serial.println();
}
This is a little routine i wrote some time ago to display all used interrupts (names only for T3.6 defined). NVIC_IS_ENABLED(i) tells you, if an interrupt - is enabled or not.
There are several other of these macros defined by the Teensy-Core-lib:
Code:
// Nested Vectored Interrupt Controller, Table 3-4 & ARMv7 ref, appendix B3.4 (page 750)
#define NVIC_STIR            (*(volatile uint32_t *)0xE000EF00)
#define NVIC_ENABLE_IRQ(n)    (*((volatile uint32_t *)0xE000E100 + ((n) >> 5)) = (1 << ((n) & 31)))
#define NVIC_DISABLE_IRQ(n)    (*((volatile uint32_t *)0xE000E180 + ((n) >> 5)) = (1 << ((n) & 31)))
#define NVIC_SET_PENDING(n)    (*((volatile uint32_t *)0xE000E200 + ((n) >> 5)) = (1 << ((n) & 31)))
#define NVIC_CLEAR_PENDING(n)    (*((volatile uint32_t *)0xE000E280 + ((n) >> 5)) = (1 << ((n) & 31)))
#define NVIC_IS_ENABLED(n)    (*((volatile uint32_t *)0xE000E100 + ((n) >> 5)) & (1 << ((n) & 31)))
#define NVIC_IS_PENDING(n)    (*((volatile uint32_t *)0xE000E200 + ((n) >> 5)) & (1 << ((n) & 31)))
#define NVIC_IS_ACTIVE(n)    (*((volatile uint32_t *)0xE000E300 + ((n) >> 5)) & (1 << ((n) & 31)))
#ifdef KINETISK
#define NVIC_TRIGGER_IRQ(n)    NVIC_STIR=(n)
#else
#define NVIC_TRIGGER_IRQ(n)    NVIC_SET_PENDING(n)
#endif

You may find NVIC_ENABLE_IRQ(n) and NVIC_DISABLE_IRQ(n) useful.
 
Last edited:
Before answering, I'd like to point out IntervalTimer with higher interrupt priority might be a better solution for timing sensitive applications. If you need really high precision, these tips should help.

I believe almost everyone agrees seeing C source code filled with goto & setjmp & longjmp should make any reasonable programmer cringe. Likewise, this sort of interrupt disable for timing purposes should probably be considered really poor practice, on par with goto-based "spaghetti" code. Then again, delays with interrupts disabled are sometimes needed (*cough* Adafruit_NeoPixel *cough*), despite the terrible incompatibility they tend to inflict upon everything else.

With that in mind.....

Frank mentioned the ARM interrupt masks. Those are 32 bit registers, each masking 32 of the normal (non-exception) interrupts. You could just make a backup and then write them all to zero to shut off all the interrupts. ARM SysTick, which is one of the 16 exceptions not controlled by those registers, is all you need for elapsedMicros.

Another option might be the BASEPRI register. It can be used to mask all interrupts below a certain priority level. By default Teensyduino assigns priority 32 to SysTick, where 0 is the highest and 255 is the lowest priority.

The best documentation of the ARM registers and features is this book.

https://www.amazon.com/dp/0124080820
 
Wow Frank and Paul -- highly useful answers and links to other highly useful threads and docs. Thanks!
 
In general, I think it's a bad idea to presume that there are "application programmers" that "don't know all the bits" of a timing critical system running on a single core in an embedded form factor. Especially an embedded system that doesn't generally have a user-installable separately compiled application model.
 
In general, I think it's a bad idea to presume that there are "application programmers" that "don't know all the bits" of a timing critical system running on a single core in an embedded form factor. Especially an embedded system that doesn't generally have a user-installable separately compiled application model.

I don't understand your point.
 
Status
Not open for further replies.
Back
Top