Использовать прерывания с аналоговыми выводами в Arduino или другое возможное решение?

У меня очень простая схема, но моя программа несколько сложна.

Вот принципиальная схема:

Вот код:

/*
  01 - GND -------- GND
  02 - +5V -------- 5V
  03 - V0  -------- Potentiometer Middle
  04 - Reset ------ 9
  05 - Read/Write - GND
  06 - Enable ----- 8
  07 - Data0 ------ NC
  08 - Data1 ------ NC
  09 - Data2 ------ NC
  10 - Data3 ------ NC
  11 - Data4 ------ 10
  12 - Data5 ------ 11
  13 - Data6 ------ 12
  14 - Data7 ------ 13
  15 - +LCD ------- 5V
  16 - -LCD ------- GND
*/

//подключаем библиотеку LCD
#include <LiquidCrystal.h>

//Инициализируем ЖК-объект
/*Pins should be mentioned in this order:
  Reset
  Enable
  Data4
  Data5
  Data6
  Data7
*/
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);
int onTimePin = A0;
int onTime = 0;
int offTimePin = A1;
int offTime = 0;
int ledPin = 13;

void setup()
{

  pinMode(onTimePin, INPUT);
  pinMode(offTimePin, INPUT);

  pinMode(ledPin, OUTPUT);

  //Запускаем ЖК-интерфейс
  lcd.begin(16, 2);

  lcd.print("ON  TIME : ");
  lcd.setCursor(0, 2);
  lcd.print("OFF TIME : ");

}

void loop()
{
  onTime = map(analogRead(onTimePin), 0, 1023, 1, 3);
  lcd.setCursor(11, 0);
  lcd.print(onTime);

  offTime = map(analogRead(offTimePin), 0, 1023, 1, 9);
  lcd.setCursor(11, 1);
  lcd.print(offTime);

  digitalWrite(ledPin, HIGH);
  delay(onTime * 1000);

  digitalWrite(ledPin, LOW);
  delay(offTime * 1000);
}

Приведенный выше код работает. Но есть небольшая проблема:

Когда я поворачиваю дворник потенциометра, происходит задержка.

Это очевидно, потому что после завершения цикла отображается измененное значение потенциометра. Итак, я решил сделать то же самое, используя прерывания. Но прерывания есть только у цифровых выводов.

Я хотел бы узнать решение по использованию прерываний на аналоговом входе.

, 👍2

Обсуждение

Задержка происходит потому, что вы задерживаетесь., @Ignacio Vazquez-Abrams

Я бы поместил мигание светодиода в таймер (-прерывание). Или используйте технику, описанную в [моргайте без задержки](https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay)., @Gerben

@IgnacioVazquez-Abrams Я знаю, что есть задержка, и получаю ожидаемый результат. Короче говоря, я хочу, чтобы светодиод мигал в соответствии со значениями, заданными потенциометром., @Vishal


1 ответ


Лучший ответ:

3

Избегайте задержек.

Очень упрощенная альтернатива:

unsigned long lastLedToggleTime;
unsigned long currentLedToggleInterval;

void loop() {
  ...

  unsigned long now = millis();

  if ( (now - lastLedToggleTime) > currentLedToggleInterval) {
    lastLedToggleTime = now;
    // Теперь нам нужно переключить светодиод.
    if ( digitalRead( ledPin ) == LOW ) {
      // Светодиод был НИЗКИМ, поэтому мы установили его ВЫСОКИЙ и настроили следующее переключение после onTime.
      digitalWrite(ledPin, HIGH);
      currentLedToggleInterval = (onTime * 1000);
    } else {
      // Светодиод был ВЫСОКИМ, поэтому мы установили его НИЗКИЙ и настроили следующее переключение после времени выключения.
      digitalWrite(ledPin, LOW);
      currentLedToggleInterval = (offTime * 1000);
    }
  } 
  ...
}

Обратите внимание, что здесь мы имеем дело с 32-битными целочисленными значениями без знака, поэтому этот код будет работать, даже если/когда millis() перейдет в 0. В частности, now - LastLedToggleTime не только всегда будет возвращать положительное значение (поскольку оно приведено к беззнаковому типу данных), но также будет возвращать правильное значение из-за дополнения до двух. представление и арифметическое основание по модулю 2^32.

Пример: для простоты давайте посмотрим на 8-битное значение:

Скажем, lastLedToggleTime находился на последнем такте 8-битного значения таймера, т.е. lastLedToggleTime == 255. В следующем такте таймера now переполнится до 0, поэтому далее у нас будет now - lastLedToggleTime = 0 - 255 = -255. В дополнении до двух для представления этого значения потребуется (как минимум) 9 бит (двоичный код: 1 0000 0001), но поскольку у нас есть только 8 бит памяти, бит знака (MSB) просто отбрасывается, в результате чего 0000 0001 двоичный = 1. Мы видим, что 1 — правильное значение. чтобы количество тиков изменилось с 255 до 0; это ровно один тик.

,

Ваше решение потерпит неудачу, когда millis() перевернется. Хотя это может быть и не очень серьезная проблема, нет смысла сохранять эту ошибку, тем более что ее очень легко избежать. См. [Учебное пособие по Blink Without Delay Arduino](https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay), чтобы узнать, как правильно написать код., @Edgar Bonet

millis() ["переполнится (вернётся к нулю) примерно через 50 дней."](https://www.arduino.cc/en/Reference/Millis), @JimmyB

Вот почему я говорю: «Возможно, это не очень серьезная проблема». Но все же нет веской причины показывать ошибочный код в ответе, когда избежать ошибки _так просто_. Псевдокод на странице, на которую вы ссылаетесь, также верен., @Edgar Bonet

@EdgarBonet Вы заметили, что OP имеет как переменное, так и независимое время *включения* и *выключения*?, @JimmyB

@EdgarBonet Поскольку, похоже, вас это действительно беспокоит, я сейчас отредактировал предложенное вами решение., @JimmyB

Спасибо. Я проверил образец кода, и он работает нормально., @Vishal

@EdgarBonet Это также не удастся с кодом мигания без задержки. Как только millis() перейдет через вывод в приведенном примере, он больше никогда не станет ВЫСОКИМ. Оно останется низким. После прокрутки вы больше никогда не получите результат, превышающий *интервал*. Вам нужно абсолютное значение интервала... *abs(интервал)*, @Fred Hendricks

@FredHendricks, ты не понимаешь, что такое переворот. прочитайте https://arduinoprosto.ru/q/12587/how-can-i-handle-the-millis-rollover, @Juraj

@FredHendricks Обратите внимание, что здесь мы имеем дело с *беззнаковыми* 32-битными целыми числами, что означает, что мы неявно имеем арифметику по модулю. Вот почему now -lastLedToggleTime всегда будет возвращать правильное значение, даже если lastLedToggleTime больше, чем now, потому что now снова переполнился до 0., @JimmyB