我不确定我是否在这里遗漏了一些明显的东西,但是其他人是否遇到过 Cortex M0+(或任何 Cortex M 系列)在被中断后从 ISR 返回到线程模式中的随机位置的任何问题从 sleep 模式唤醒(例如,不返回到 WFI 指令下方的行)。它似乎在代码中间醒来,就像在 for 循环的中间或函数的中间。我使用的是 ATSAMD218A 微 Controller ,并且一直在使用 Atmel Studio 7 中的调试器。我试图找出问题并注意到以下几点:
- 它仅在设备进入休眠状态时发生,当设备在唤醒状态下被中断时,ISR 总是返回到正确的位置。
- ISR 总是返回到代码中相同(不正确)的位置,如果我注释掉那个函数或一段代码,它就会开始返回到另一个不正确的位置
- 我未能成功插入延迟和 NOP 来尝试确定它是与时间相关还是与时钟周期相关,但它仍然总是返回到相同(不正确)的位置
- 我尝试过使用直接寄存器访问以及使用 Arduino 中断库来实现中断。
- 它发生在空闲和待机模式( sleep 和深度 sleep )
- 在调试期间查看汇编指令时,就在退出 ISR 之前,调用了“bx lr”指令,该指令应该跳转到链接寄存器。链接寄存器包含一个名为 EXC_RETURN 的值,它指示返回行为,在我的例子中是 0xFFFFFFF9(返回到线程模式)。但是我找不到它返回到任何地方的实际内存地址。内存地址不在任何核心寄存器 R0-R15 中。
我一直在阅读 ARMv6 架构引用手册以及 Cortex M0+ 通用用户指南。不太确定发生了什么,任何调试建议将不胜感激。有没有人对 Cortex M 系列的异常处理有更好的理解,并且可以指出正确的方向以找到线程模式在 ISR 后返回的内存地址。如果您愿意,我可以提供代码,但即使是一段简单的代码,除了在循环中计数、 sleep 然后醒来外什么都不做,也会造成麻烦。
编辑 我在下面添加了相关代码。它是最精简的版本 (*stripped) 仍然会导致问题。我没有包含 RTC 代码函数(使用 DS3231RTC 库),因为我相当确定它们没有任何效果。如果您认为我应该上传更多,请告诉我。
void configInterrupt(void){
NVIC_DisableIRQ(EIC_IRQn);
NVIC_ClearPendingIRQ(EIC_IRQn);
NVIC_SetPriority(EIC_IRQn, 0);
NVIC_EnableIRQ(EIC_IRQn);
// Enable GCLK for IEC (External Interrupt Controller)
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); // custom 'pinMode' function
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 EIC_Handler(void){
RTC_FLAG = 1; // my debug breakpoint is here, at this point the stack has already been pushed and I can see the PC value that will be popped off
int_count++;
EIC->INTFLAG.reg = 1 << 0;
}
void setup() {
configInterrupt();
configureRTC();
RTC_FLAG = 1;
}
void loop() {
if (RTC_FLAG == 1) {
RTC_FLAG = 0;
setNextAlarm();
}
for (int i = 0 ; i <= WINDOW-1 ; i++) {
String data = "";
rawVal = 0; // data gets read from sensor here (stripped)
data += String(rawVal);
data += ",";
distance = 0; // distance calculated from rawVal here (stripped)
data += String(distance);
data += ",";
mean = 0; // mean calculated in a function here (stripped)
data += String(mean);
data += ",";
data += String(int_count); // ISR returns here
// Data gets written to file here (stripped)
}
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__DSB();
__WFI();
}
最佳答案
我会尝试使用 Atmel ASF 框架实现相同的代码段,查看 API 设置的寄存器以及顺序。他们通常还会引用勘误表并实现变通办法。
加载低功耗示例项目,它在功耗模式之间切换,并在按下 xplained 板按钮时唤醒。如果它可以在您的电路板上的模式之间跳跃,您就知道它不是零件。您可能需要将唤醒引脚移动到与您的硬件相匹配的地方。
关于Cortex M0+中断 sleep 后返回线程模式错误位置,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37933962/