Проблема с прерываниями

Хорошо. Вот что я хотел сделать:

Составьте схему и закодируйте Arduino так, чтобы было два тактильных переключателя, каждый с предопределенным значением ШИМ. Всякий раз, когда нажимается какая-либо кнопка, светодиод должен работать с этим значением ШИМ, пока кнопка не будет отпущена. Есть также ультразвуковой датчик на том же ардуино, который вызывает прерывание на пороговом расстоянии, которое останавливает светодиод. Если ни одна кнопка не нажата или ультразвук не активен, светодиод должен гореть с другим значением ШИМ.

Вот код: (обновлено)

const int trig = 9;
const int echo = 10;
const int redswitch = 2;
const int yellowswitch = 3;
int redled = 6;
int greenled = 5;
volatile int pwm = 50;

volatile bool rs = LOW;      //RS — КРАСНЫЙ ПЕРЕКЛЮЧАТЕЛЬ
volatile bool ys = LOW;      //YS — ЖЕЛТЫЙ ПЕРЕКЛЮЧАТЕЛЬ
bool last_rs = LOW;
bool last_ys =  LOW;
volatile long distance=0;
long duration=0;
long temp=0;

void setup(){
  pinMode(trig,OUTPUT);
  pinMode(echo,INPUT);
  pinMode(redled,OUTPUT);
  pinMode(greenled,OUTPUT);
  pinMode(redswitch,INPUT);
  pinMode(yellowswitch,INPUT);
  Serial.begin(9600);

  attachInterrupt(digitalPinToInterrupt(redswitch), red,CHANGE);
  attachInterrupt(digitalPinToInterrupt(yellowswitch), green,CHANGE);

  digitalWrite(greenled,LOW);
  digitalWrite(redled,LOW);
}

boolean debounce(boolean temp,int switchPin)
{
  boolean current = digitalRead(switchPin);
  if (temp != current)
  {
    delay(5);
    current = digitalRead(switchPin);
  }
  return current;
}

void loop(){


  //Расчет расстояния до ультразвукового датчика
  digitalWrite(trig,LOW);
  delayMicroseconds(5);

  digitalWrite(trig,HIGH);
  delayMicroseconds(10);
  digitalWrite(trig,LOW);

  duration = pulseIn(echo, HIGH);
  temp = duration * 0.0343 / 2;
  noInterrupts();
  distance = temp;
  rs = debounce(last_rs,redswitch);
  ys = debounce(last_ys,yellowswitch);
  last_rs = rs;
  last_ys = ys;
  if(distance>8)interrupts();

  Serial.println(distance);  
  analogWrite(redled,pwm);
  analogWrite(greenled,pwm);
}

void red(){

  if(rs == HIGH) pwm = 150;
  else pwm = 0;


}

void green(){

  if(ys == HIGH) pwm = 250;
  else pwm = 0;

}

Теперь, когда Arduino подключен к моему ноутбуку, светодиод запускается с определенным значением. Но всякий раз, когда я нажимаю кнопку, они гаснут. Можете ли вы помочь мне выяснить, что здесь не так?

, 👍0

Обсуждение

удалите constrain(pwm,0,255);. это ничего не делает. функция constrain **возвращает** первый параметр, если значение находится в ограничениях, или один, если ограничения, если значение находится за пределами, @Juraj

в вашем коде слишком много проблем. вы используете одну переменную для обоих выходов. вы не дебажируете кнопки. вы сбрасываете ШИМ в каждом цикле (вы не позволяете ему работать). и я бы сказал, что вся концепция чтения кнопок с прерыванием неверна, @Juraj

@Юрай. Я использую одну переменную для обоих выходов, потому что хочу, чтобы оба светодиода имели одинаковое значение ШИМ. Про дебуцирование я совсем забыл. Извини. И я не понимаю этого - "вы сбрасываете ШИМ в каждом цикле". где мне это сделать? я не сбрасываю ШИМ в цикле(). Чтение кнопок с прерыванием. Я следил за онлайн-уроком. Я совершенно новичок в ардуино. Было бы очень полезно, если бы вы выразились немного яснее и точнее., @Kishore

но вы устанавливаете ШИМ на 0, если одна из кнопок не нажата. ШИМ запускается после запуска. он должен работать высоко, низко, высоко, низко, высоко... но если вы запустите его снова и снова, он будет работать высоко, и снова высоко и высоко. прерывания хороши для очень коротких событий. люди долго держат кнопки, @Juraj

Я думаю, что прерывания - правильный путь. Это позволяет вам выполнять любые медленные действия в основном цикле, в то время как кнопки все еще работают. Не выполняйте никаких операций чтения или записи в функциях прерывания — вы уже знаете, что кнопка была нажата!, и запись происходит в вашем основном цикле. Не делайте ничего, кроме изменения состояния изменчивых переменных. Добавьте дебаунс., @hpekristiansen

@hpekristinsen и Юрай. Большое спасибо за это. Я сделаю то, что вы сказали, и обновлю код. Добавлю дебаунс. И, Юрай, люди в этом случае кнопки долго держать не будут. это задание, данное мне, и они не должны этого делать. И еще: следует ли мне использовать HIGH или RISING внутри AttachInterrupt()? Я не совсем понимаю разницу., @Kishore


1 ответ


1

Посмотрите на эти строки:

distance = temp;
duration = pulseIn(echo,HIGH);
distance = duration * 0.0343 /2;

Вот что они делают:

  • Первая строка устанавливает для переменной distance нулевое значение (это значение temp).
  • Вторая строка ожидает, пока ультразвуковой импульс достигнет цели, отскочить от него и вернуться назад, одновременно рассчитывая время вся поездка. Это может занять длительное время (несколько миллисекунд). Шансы ваша программа тратит большую часть своего времени на выполнение этой строки.
  • Третья строка устанавливает для переменной distance какое-то разумное значение.

Это означает, что расстояние большую часть времени равно нулю (в то время как вторая строка выполняет). Когда срабатывает прерывание, существует очень высокая вероятность чтобы обработчик прерывания нашел нулевое расстояние и, таким образом, установил pwm до нуля.

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

Но есть одна загвоздка. Переменная имеет длину четыре байта и обновляется. требуется четыре инструкции. Существует небольшая вероятность того, что прервать запуск между этими инструкциями. Когда это произойдет, обработчик прерывания обнаруживает, что переменная distance наполовину обновлена, со смесью байтов из предыдущих и текущих измерений расстояния. Этот называется «состоянием гонки» и является одной из худших ошибок, с которыми вы сталкиваетесь. можно встретить, поскольку его очень сложно систематически воспроизводить.

Исправление состояния гонки заключается в обновлении переменной с помощью прерываний. неполноценный. Неправильный способ сделать это:

duration = pulseIn(echo, HIGH);
noInterrupts();
distance = duration * 0.0343 / 2;
interrupts();

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

duration = pulseIn(echo, HIGH);
temp = duration * 0.0343 / 2;
noInterrupts();
distance = temp;
interrupts();

Теперь прерывания блокируются только на время записи переменной distance.

Лично я не думаю, что читать кнопки — это хорошая идея. с прерываниями. Кнопки — чрезвычайно медленные периферийные устройства. Если ты сделаешь ваш код неблокируется, ваш loop() всегда будет достаточно быстрым, чтобы справиться с ними. Обработка кнопок в loop() означает, что вам не нужно беспокойтесь обо всех ошибках, которые могут легко проникнуть в управляемую прерываниями систему. код, как упомянутое выше состояние гонки.


Изменить: В исходной версии вашего кода прерывания использовались для читай переключатели. В отредактированной версии вы добавили дебаунсер, и вы больше не нужны прерывания. Фактически, способ использования этой версии прерывания вообще не имеют смысла. Самым проблематичным является тот факт, что что этот код может отключать прерывания на сколь угодно долгое время. Этот это нехорошо, и вы должны знать, что хронометраж Arduino функции полагаются на прерывания для выполнения своей работы.

Я предлагаю вам просто удалить из этого кода каждую строку, где вы видите слово «прерывание» (единственное или множественное число) и замените

if(distance>8)interrupts();

по

if (distance > 8) {
    red();
    green();
}

или, чтобы немного упростить ситуацию,

if (distance > 8) {
    if (ys == HIGH) pwm = 250;
    else if (rs == HIGH) pwm = 150;
    else pwm = 0;
}
,

Я обновил код в вопросе. Пожалуйста, проверь это. Есть большое улучшение. Но сейчас проблема, похоже, в том, что код делает прямо противоположное тому, что я хочу. Светодиод светится при определенном значении ШИМ, когда ни одна кнопка не нажата. При нажатии он выключается. я хочу, чтобы это произошло наоборот. И для этой части **на том же Arduino есть ультразвуковой датчик, который запускает прерывание на пороговом расстоянии, которое останавливает светодиод** - я использую if(distance>8)interrupts(). Это правильный путь?, @Kishore

@Kishore: см. исправленный ответ., @Edgar Bonet

дело в том, что я сейчас изучаю ардуино. это как задание, которое мне дали. Мне нужно использовать прерывания в этом коде. Это обязательно. У меня есть другая версия кода, в которой я не использую прерывания, и она все равно выполняет свою работу. Но мне приходится делать то же самое с использованием прерываний. Поэтому я не могу их удалить., @Kishore

@Kishore: Тогда забудьте о моих изменениях и следуйте по пути, указанному в первой части моего ответа. Вам придется удалить функцию debounce() и найти алгоритм, подходящий для устранения дребезга, управляемого прерываниями. Интересная проблема сама по себе., @Edgar Bonet