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

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

Составьте схему и закодируйте 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


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;
}
,