Как не вернуться к тому месту, где программа остановилась до прерывания - PullRequest
0 голосов
/ 10 июля 2020

Я бью тревогу. Я хочу, чтобы она сыграла мелодию, когда придет время бить тревогу. Я хочу, чтобы он был остановлен нажатием кнопки.

У меня работает мелодия, и она подает сигнал тревоги, когда я хочу. Но когда я нажимаю кнопку, я не могу отключить будильник.

Кажется, не так много, что я могу увидеть в Интернете на этом топи c. Некоторые сбили меня с толку, сказав, что это можно сделать с помощью "while" l oop, а другие говорят, что это можно сделать с "for" l oop, а другие говорят, что вам нужно проверить в определенных c точках в вашем коде, чтобы увидеть, была ли нажата кнопка после прерывания. (изменяемая переменная будет изменена во время прерывания)

Я не думаю, что установка контрольных точек для кнопки, нажатой после прерывания, является элегантным решением, а размер файла был бы слишком большим, чтобы удерживать полную настройку. (96% места было использовано при полной настройке)

У меня было несколько попыток.

первая попытка не пыталась исправить проблему, но была обходным решением. это в основном будет держать 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();
  
 }

У меня были другие попытки вызвать другую функцию, но после прерывания она всегда возвращается туда, где остановилась.

как я могу остановить воспроизведение песни, не отвлекая программу и не вмешиваясь в следующий будильник?

1 Ответ

1 голос
/ 10 июля 2020

Во-первых, задержка принимает целое число. Без десятичных знаков. Если вам нужен более точный тайминг, вы можете использовать delayMicroseconds и микросекунды, но он по-прежнему принимает только целые значения. То же самое касается аргумента длительности для тона.

Что касается вашего фактического вопроса, это просто невозможно сделать с помощью этого кода. Вы написали код блокировки. Погуглите этот термин. Когда вы используете задержку для обработки времени, это все, что вы сможете сделать. Вы не можете читать кнопки. Новички любят пытаться решить эту проблему с помощью прерывания, но, как вы только что узнали, это не работает. Вам нужен неблокирующий код. Подумайте о функции midi (), которая вместо того, чтобы вызываться один раз и проигрывать всю тревогу, вместо этого вызывается снова и снова каждые несколько микросекунд, и большую часть времени просто проверяет, пришло ли время изменить ноту или кнопка нажата и ничего не делает, но иногда видит, что пора сменить ноту или что кнопка нажата и изменяет ноту или полностью останавливает ее. Вам нужно будет перейти от задержки к обработке времени с помощью millis (). Взгляните на пример Blink Without Delay и любое из тысяч отличных руководств о том, как это работает, чтобы получить некоторое вдохновение, как это сделать.

ISR также представляет собой огромную проблему. Посмотрим на это. ISR должен быть очень быстрым. Вход и выход за несколько микросекунд. Но ваш вызывает эту функцию:

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

Поскольку ничто внутри этого, в то время как l oop никогда не изменяет значение button_was_pressed, это бесконечное l oop. На самом деле, во всем коде нет ничего, что могло бы вернуть button_was_pressed обратно в false. Я не уверен, как вы ожидали, что это сработает, но это не так.

Правильное решение этой проблемы вообще не будет включать прерывания. Прерывания предназначены для сверхбыстрых событий, которые происходят так быстро, что их можно упустить. Помните, что все остальные прерывания отключены, пока находится внутри ISR, поэтому миллисекунды перестают считать, а последовательный порт перестает работать, и многие вещи, которые должны произойти, не происходят там.

Если вы не можете нажать и отпустить кнопку в течение микросекунд (для сравнения, моргание глазом занимает около 200 000 микросекунд), прерывание не совсем подходит для отслеживания нажатия кнопки. Если вы сделаете это правильно, ваш код не будет прерываться. В большинстве случаев вам следует избегать использования прерывания. Они почти всегда усложняют задачу. Используйте их в крайнем случае для импульсов, которые невозможно уловить, а не людей, нажимающих кнопки.

...