3

I discovered this issue when I tried to put the microcontroller to sleep and then wake it up, as an interrupt driven application. I noticed that my code did not resume from the line of code that was after my 'sleep' instruction.

When I manually trigger an interrupt while stepping through my code with a debugger it takes multiple steps (sometimes 2, sometimes 50 depending on the code) before it jumps to the ISR.

While trying to debug this issue I wrote this very simple piece of code which exhibits the issue:

#include <Arduino.h>
// Setup and Loop declared in Arduino core
void configInterrupt(void);
volatile uint32_t debug = 0;
uint32_t int_count = 0;

void EIC_Handler(void){
  int_count++;
  EIC->INTFLAG.reg = 1 << 0;
}

void configInterrupt(void){
  NVIC_DisableIRQ(EIC_IRQn);
  NVIC_ClearPendingIRQ(EIC_IRQn);
  NVIC_SetPriority(EIC_IRQn, 0);
  NVIC_EnableIRQ(EIC_IRQn);
  GCLK->CLKCTRL.reg = (uint16_t) (GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | GCLK_CLKCTRL_ID(GCM_EIC));
  EIC->WAKEUP.reg |= (1 << 0);
  EIC->CONFIG[0].reg |= 0x2;                    // falling edge
  pinConfig(16,INPUT,UP);                   // pin 16 as input with pullup 
  PORT->Group[0].PINCFG[16].bit.PMUXEN = 1; // enable peripheral muxing
  PORT->Group[0].PMUX[8].bit.PMUXE = 0x0;       // function A (EIC) = 0x0
  EIC->INTENSET.reg = EIC_INTENSET_EXTINT(1 << 0);
  EIC->CTRL.bit.ENABLE = 1;
}

void setup() {
  configInterrupt();                            
}

void loop() {
  for(int i = 0 ; i < 100 ; i++) debug++;   // volatile so that the compiler doesn't touch

  String debugstring = "";

  SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
  __DSB();
  __WFI();
}

I'm debugging using an external interrupt that I trigger myself with a jumper wire so that I know when it should have been triggered. What I'm noticing is that while I debug the code and step through it, if I trigger the external interrupt manually then it doesn't jump to the ISR straight away. The interrupt becomes 'pending' in the NVIC but exception entry isn't carried out until later in the code.

I've read a lot about interrupts and exceptions in the SAMD21 datasheet, the Cortex M0+ Generic User Guide and the ARM Architecture manual. Supposedly the the Cortex M series has low latency interrupts with no instruction overhead and so it seems like the code should jump to the ISR relatively quickly after triggering an interrupt.

I've read 2.3.6 of the Cortex M0+ Generic Guide multiple times as well as B1.3.2 of the ARM Architecture manual, which both cover exception entry in quite a bit of detail. The SAMD21 datasheet doesn't seem to have much low level information.

I've tried to isolate the problem and identify any patterns in the device's behavior and I've noticed a few things.

It only jumps to the ISR at specific lines of code. For example in the code above, if the external interrupt is triggered at start of 'loop()' it will jump to the ISR when it reaches the String declaration, regardless of how many iterations in the 'for' loop. If I move the String declaration up above the 'for' loop then it will jump to the ISR almost immediately (after 2 or 3 debugging steps).

I've tried inserting delays, NOPs and ISBs which don't effect how long it takes or make it jump instantly. When I set a pending interrupt in software through the ISPR register in the NVIC the same problem occurs. I've kept track of the base FLASH memory in Atmel Studio and noticed that "stack" onto which the processor's current 'state' is pushed, doesn't change immediately either. It only changes when I get to the first line of code in the ISR.

Other pieces of code that I've noticed act similar to a String declaration and cause the code to jump to the ISR is the endTransmission function of the Wire library, some functions in the SD card library, the Arduino delay function.

Could it be related to the fact that I'm using a debugger in the first place which interferes / doesn't play nice with interrupts? I'm fairly sure the problem occurred before I got the debugger out though. Edit: reading through the Cortex M0+ Technical Reference Manual and ARMv6 Manual I've found a register called DHCSR which allows the debugger to mask interrupts, but I can't work out how to access these registers.

Main Question: Aside from PRIMASK and global/individual enable register bits, what else could be preventing a pending interrupt from being executed?

Edit: left out an important piece of information, although I'm working in Atmel Studio the project uses the Arduino core.

EDIT: I have noticed that after I manually trigger an interrupt, it becomes pending in the NVIC->ISPR register during my next debugging step. This leads me to believe the interrupts are being masked somewhere (I've checked PRIMASK, global enable and individual enable so far with no luck).

4

1 回答 1

0

String debugstring = "";是这些东西实际上是C++的线索。因此,您必须将 IRQ 处理程序声明为纯 C 函数:

extern "C" {
void EIC_Handler(void);
}
void EIC_Handler(void){
  int_count++;
  EIC->INTFLAG.reg = 1 << 0;
}

否则,该函数是 C++ 并且不能用作 IRQ 处理程序,因为它不能被链接器识别。

于 2016-06-23T15:19:37.280 回答