如何不 return 到中断前程序停止的地方

How to not return to where the program left off before an interrupt

我正在报警。我希望它在响起警报时播放一首曲子。我希望它通过按下按钮停止。

我的曲子在工作,它会在我需要时发出警报。但是当我按下按钮时,我无法让闹钟停止。

我在网上看到的关于这个主题的资料似乎不多。有些人说可以用“while”循环来完成,而另一些人说可以用“for”循环来完成,还有一些人说你需要检查代码中的特定点以查看按钮是否有中断后被按下。 (一个volatile变量会在中断期间改变)

我不认为在中断后为按下的按钮设置检查点是一个优雅的解决方案,而且文件太大而无法完整播放。 (96%的space已经用完全曲了)

我尝试了几次。

第一次尝试并没有尝试解决问题,而是一种解决方法。它基本上会让 Arduino 在中断期间无限期地忙碌。这是不好的做法,但至少可以停止警报。

int tonePin = 11;
boolean button_was_pressed = false;
void donothing(){
  while(button_was_pressed)
  {bool who_cares_this_is_just_to_keep_the_cpu_busy;}}
void isr()
{
  button_was_pressed = true;
  digitalWrite(13,LOW);
  donothing();
}

void setup() {
  attachInterrupt(digitalPinToInterrupt(2), isr, RISING);
  pinMode(13,OUTPUT);
  Serial.begin(9600);

}
void midi() {
  
  Serial.println("midi");
  Serial.println(millis());
    tone(tonePin, 207, 3595.840371094);
    delay(3627.200390625);
    delay(150.197988281);
    tone(tonePin, 220, 578.592360352);
    delay(609.044589844);
    delay(275.638066406);
    tone(tonePin, 207, 578.592360352);
    delay(609.044589844);
    delay(240.976992188);
      
  Serial.println("midi exit");
  Serial.println(millis());
//(the actual tune isnt so bland and short. this tune was for debugging purposes)
  
}



void loop() {
  
 unsigned long period = 15*1000L;//the period is 15 seconds (can easily be replaced with 24 hrs once the issue is solved)
 
  Serial.println("loop");
  Serial.println(millis());
  Serial.println(millis() % period);
  //wait till morning
  
digitalWrite(13,HIGH);
delay(100);
 //unsigned long hbhb = 15*1000L; //15 sec


 if(millis() % period < 100)
 {
  
    midi();
  
 }

我有其他尝试尝试调用不同的函数,但在中断之后,它总是 returns 到它停止的地方。

我怎样才能停止播放歌曲而不需要保持程序忙碌并且不干扰下一个闹钟?

首先,延迟取一个整数。没有小数位。如果你想要更精细的计时,你可以使用 delayMicroseconds 并使用微秒,但它仍然只需要整数值。音调的持续时间参数也是如此。

至于你的实际问题,用这段代码根本做不出来。您已经编写了阻塞代码。 Google那个词。当您使用延迟来处理时序时,这就是您所能做的。你无法阅读按钮。新手喜欢尝试通过中断来解决这个问题,但正如您刚刚了解到的那样,这是行不通的。您需要的是非阻塞代码。想想一个 midi() 函数,它不是被调用一次并一直播放到警报,而是每隔几微秒被一遍又一遍地调用,并且大部分时间只是检查是否是时候更改音符或者是否按钮被按下但什么都不做,但偶尔会看到是时候改变音符了,或者按钮被按下并改变了音符或完全停止了它。您将不得不从延迟更改为使用 millis() 处理时间。查看 Blink Without Delay 示例和数以千计的有关其工作原理的优秀教程中的任何一个,以获得有关如何执行此操作的一些灵感。

你的ISR也是个大问题。让我们看看那个。 ISR 应该超快。在几微秒内进出。但是你调用这个函数:

void donothing(){
  while(button_was_pressed)
  {bool who_cares_this_is_just_to_keep_the_cpu_busy;}}

因为 while 循环中没有任何东西改变 button_was_pressed 的值,这是一个无限循环。实际上,整个代码中没有任何内容会将 button_was_pressed 设置回 false。我不确定您希望它如何工作,但事实并非如此。

这个问题的正确解决方案根本不涉及中断。中断适用于发生得如此之快以至于可能被错过的超快速事件。请记住,在 ISR 内部时所有其他中断都已关闭,因此 millis 停止计数并且串行停止工作并且许多需要发生的事情不会在那里发生。

除非您可以在几微秒内按下并释放按钮(相比之下,眨眼大约需要 200,000 微秒),否则中断并不适合捕捉按钮按下。如果您这样做正确,您的代码中将不会涉及中断。在大多数情况下,您应该尽量避免使用中断。他们几乎总是让事情变得更难。将它们用作脉冲太短而无法捕捉的最后手段,而不是人类按下按钮。