вывод становится высоким, не позволяя аналоговому чтению измениться

У меня возникла следующая проблема:

Мой arduino получает аналоговое значение Read(A0) от потенциометра, положение которого я не могу отрегулировать напрямую.

Когда AnalogRead(AO) достигает значения X, я хочу, чтобы на выводе 8 был ВЫСОКИЙ уровень, это приводит к размыканию цепи. и остановить A0 становится выше. поэтому я делаю оператор if:

if(analogRead(AO)> X) {

digitalWrite(pin, HIGH)  \\opens the circuit} 

else {
digitalWrite(pin, LOW)  \close the circuit
 }

Проблема, с которой я столкнулся, заключается в том, что когда цепь разомкнута, я хотел бы изменить значение потенциометра (A0), который управляется двигателем, который также находится в цепи: но не зависит от платы Arduino. Учитывая, что я не могу контролировать A(0), схема останется на A(0)>X навсегда.

Система переходит на контакт LOW или замыкает цепь после того, как она достигает контакта HIGH в течение 2 секунд, это было бы здорово, но затем программа переходит к началу и понимает, что все еще A(0)> X , и возвращается к открытому состояние цепи, поэтому я больше не могу иметь замкнутую цепь, чтобы сделать A(0) < X и оставьте цепь замкнутой. Может быть, что-то, что циклически включает и выключает вывод на t мс? но это не идеально, так как приведет к сильному износу системы

Отредактировал сообщение, чтобы оно было более точным. Надеюсь, теперь стало понятнее
спасибо


спасибо за ваши ответы, но после того, как вы некоторое время поиграли с вашими кодами и идеями, лежащими в их основе, все еще не можете заставить его работать.

например, Крисл с вашим первым:

когда потенциометр находится в положении > X, он размыкает цепь, что хорошо, но все равно не отключается, пока оба потенциометра не перейдут в положение < X и 3 секунды истекают, но этого в любом случае невозможно достичь, потому что значение потенциометра зафиксировано на X

Это то, что мне нужно-

если (PinState == ВЫСОКОЕ)

через 3 секунды перейти на низкий уровень,

и не становиться ВЫСОКИМ до тех пор, пока не будут выполнены хотя бы эти два условия; AnalogRead переместился в < X , а затем AnalogRead > X , и в этот момент мы вернулись к
PinState ВЫСОКИЙ возможно ли добиться чего-то подобного?

схема примерно такая:

схема

ОБНОВЛЕНИЕ: Я использую этот код, который делает почти то, что я хочу: если (аналоговое чтение (A0) > X) { unsigned long currentMillis = millis();

if ((unsigned long)(currentMillis - previousMillis) >= interval) {

   pinState = !pinState; 
   digitalWrite(pin, pinState)
   // сохраняем "текущее" время
   previousMillis = millis();
}}

Когда он доходит до AnalogRead(A0)> X, проблема этого кода заключается в том, что он постоянно включает и выключает вывод,

но вместо того, чтобы выключать/включать его по истечении интервала времени, я хочу, чтобы он всегда был НИЗКИМ. Конечно, до тех пор, пока время больше, чем интервал, и не достигнет ВЫСОКОГО значения, пока оно не вернется к аналоговому чтению (A0)> X, а время меньше интервала. И повторите процесс... Когда время превысит интервал, он перейдет от того места, где он был digitalWrite(pin, HIGH), к новому состоянию digitalWrite(pin, LOW)

Я сделал эту таблицу более понятной

Таблица: AnalogRead(A0) > X?

1-yes -->теперь проверьте, меньше ли время в этом состоянии, чем interval = 3000:

1.1-ДА, ---> digitalWrite(pin, HIGH)
1.2- если нет (время больше интервала) ---> digitalWrite(pin, LOW)

2- Нет, AnalogRead(A0) < ИКС . Итак --> сохраняйте цифровую запись (pin, LOW)

НОВОЕ ОБНОВЛЕНИЕ:

Диаграмма состояний для новых диапазонов должна выглядеть примерно так. Я надеюсь, что это ясно. Я также удалил задержки и заменил (V < X) на (V < Y), все отлично работает. Спасибо, Эдгар.

Z на диаграмме является переменной для нового диапазона, Z << Х

ПОСЛЕДНЕЕ ОБНОВЛЕНИЕ:

Вопрос, решенный Эдгаром Боне

, 👍1

Обсуждение

Трудно понять, что именно должен делать код. Пожалуйста, постарайтесь объяснить это более понятно. Временные вещи лучше всего делать с помощью millis(), как в примере BlinkWithoutDelay, который поставляется с Arduino IDE., @chrisl

спасибо, я пытался использовать его, но не работал над тем, чего пытался достичь, я отредактировал пост, надеясь, что теперь он более понятен., @Fraunhofer

Итак, вы хотите физически повернуть потенциометр с двигателем, чтобы отрегулировать значение ниже X? Затем мотор должен управляться с Ардуино., @chrisl

возможно, этот способ мог бы работать, каждый раз, когда система переходит на ВЫСОКИЙ уровень, он остается там в течение 3 секунд, затем замыкает цепь или замыкает контакт НИЗКИЙ. однако в фоновом режиме переменная A0 будет продолжать читать, но не будет влиять на контакт 8 или ситуацию замыкания цепи, и когда система превысит X, она снова сделает оператор IF..else, HIGH (3 секунды) остановит IF..ELSE , на фоне читается А0 и если больше Х все повторяется, @Fraunhofer

проблематично управлять двигателем напрямую, потому что это зависит от других частей схемы., @Fraunhofer

Непонятно, что вы подразумеваете под "высоким уровнем". Пожалуйста, нарисуйте четкую таблицу со всем, что должно произойти. Нам нужно четкое и подробное описание желаемого поведения, @chrisl

@chrisl Я обновил исходный пост, большое спасибо за ваше время, @Fraunhofer


3 ответа


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

0

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

Допустим, система может находиться в любом из этих трех состояний:

  • В состоянии V_LOW входное напряжение ниже порогового значения X и выход LOW. Если ввод превышает X, мы устанавливаем вывод в HIGH и перейти в состояние V_HIGH.

  • В состоянии V_HIGH ввод выше порогового значения X, а вывод HIGH. Если ввод падает ниже X, мы возвращаемся к Состояние V_LOW. Если мы остаемся в V_HIGH в течение трех секунд, мы переключаемся вывод в LOW и переход в состояние TIMEOUT.

  • В состоянии TIMEOUT ввод превышает пороговое значение, но выход LOW. Когда ввод падает ниже X, мы переходим к V_LOW состояние.

Ниже показан график состояния этой машины. Штаты с серым фон имеет вывод LOW, тогда как состояние с белым background имеет вывод HIGH. Каждый возможный переход изображается как стрелка, помеченная условием, запускающим переход:

график состояния

Это то поведение, которое вы имеете в виду? Если нет, попробуйте описать поведение, которое вы хотите с точки зрения конечного автомата, как я сделал здесь. Если да, то я предлагаю вам последовать совету Крисла и реализовать некоторый гистерезис из-за разных порогов входа и выхода из V_LOW.

Во всяком случае, ниже приведен перевод вышеприведенного FSM на C++. А может и не быть самый эффективный способ написать это, но я считаю, что это просто достаточно, чтобы не требовать дополнительных объяснений:

void update_output()
{
    static enum { V_LOW, V_HIGH, TIMEOUT } state;
    static uint32_t time_entered_v_high;
    int V = analogRead(A0);

    switch (state) {
    case V_LOW:
        if (V >= X) {
            digitalWrite(pin, HIGH);
            state = V_HIGH;
            time_entered_v_high = millis();
        }
        break;
    case V_HIGH:
        if (V < X) {
            digitalWrite(pin, LOW);
            state = V_LOW;
        } else if (millis() - time_entered_v_high >= 3000) {
            digitalWrite(pin, LOW);
            state = TIMEOUT;
        }
        break;
    case TIMEOUT:
        if (V < X) {
            state = V_LOW;
        }
        break;
    }
}

Обновление 1: Вот как реализовать гистерезис: вам нужно выбрать второй, нижний порог, для возврата в состояние V_LOW. Давай позвоним это Y, который меньше, чем X. Тогда как на схеме, так и на кода, замените каждый экземпляр «V < X» на «V < Й». Вот и все. Есть абсолютно нечего больше менять.


Правка 2: обращение к обновленному вопросу. Теперь вывод должен идти HIGH, когда входные данные выходят за пределы определенного диапазона. добавлю гистерезис сразу, используя четыре порога V0 < V1 < V2 < V3 :

  • ввод в пределах диапазона, если он находится в пределах [V1, V2)

  • ввод вне диапазона, если он не в пределах [V0, V3)

  • диапазоны [V0, V1) и [V2, V3) обеспечивают гистерезис.

Обратите внимание, что для согласованности я предпочитаю закрывать все интервалы на та же сторона. Я также переименую состояния V_LOW и V_HIGH в IN_RANGE и OUT_OF_RANGE соответственно. Вот обновленный код:

void update_output()
{
    static enum { IN_RANGE, OUT_OF_RANGE, TIMEOUT } state;
    static uint32_t time_exited_range;
    int V = analogRead(A0);

    switch (state) {
    case IN_RANGE:
        if (V < V0 || V >= V3) {
            digitalWrite(pin, HIGH);
            state = OUT_OF_RANGE;
            time_exited_range = millis();
        }
        break;
    case OUT_OF_RANGE:
        if (V >= V1 && V < V2) {
            digitalWrite(pin, LOW);
            state = IN_RANGE;
        } else if (millis() - time_exited_range >= 3000) {
            digitalWrite(pin, LOW);
            state = TIMEOUT;
        }
        break;
    case TIMEOUT:
        if (V >= V1 && V < V2) {
            state = IN_RANGE;
        }
        break;
    }
}
,

Дорогой Эдгар, я не могу отблагодарить вас в достаточной мере. Это именно то, что я хотел, так благодарен вам. Эта проблема свела меня с ума. Я просто добавил несколько задержек в конце каждого случая и теперь он работает отлично без каких-либо мерцаний при переключении состояний. Согласно вашему предложению о гистерезисе., @Fraunhofer

Просто вопрос. Если я хочу добавить диапазон, например ( case V_LOW , case V_HIGH , case TIMEOUT ), начиная с ( ( (V >= X) , (V <X), V <X)) до (( V >= X && V <= X ), ( V < X && V > Y ), ( V < X && V > Y )) где Y является минимальным значением. Как это может работать? . Я пытался добавить диапазоны, больше if's, больше случаев, но ни то, ни другое не сработало. Я также попытался создать функцию: например, bool inRange(int V, int минимальное, int максимальное) и заменить диапазон ( V >= X && V <= X ), ( V < X && V > Y ), ( V < X && V > Y )) с функциями inRange(V, X, Y), но тоже не сработало., @Fraunhofer

@Fraunhofer: 1. С этим FSM ожидается мерцание всякий раз, когда вход зашумлен и близок к порогу. Гистерезис - это обычный способ исправить это. Задержки — это временное решение, которого лучше избегать. 2. Я не мог понять ваши диапазоны. Не могли бы вы изобразить свою идею в виде диаграммы состояний, подобной той, которую нарисовал я? Затем вы можете добавить его к вопросу., @Edgar Bonet

Я добавил диаграмму состояний. Большое спасибо, @Fraunhofer

Большое спасибо за всю вашу помощь и объяснения. Теперь все идеально., @Fraunhofer


0

Использование различных пороговых значений для переключения состояний — более высокое пороговое значение для перехода в ВЫСОКОЕ и более низкое значение для перехода в НИЗКОЕ — называется гистерезисом. Это создает мертвую зону между двумя значениями переключения, что предотвращает дрожание или мерцание светодиода в этом случае, что могло бы произойти с одной точкой переключения, и аналоговое значение было бы очень близко к нему.

Для этого вам просто нужно, чтобы второе предложение вашего блока if, теперь else, стало else if. Таким образом, это будет выглядеть так (в псевдокоде):

if analog > 5,
   open the circuit;
else if analog < 4
   close the circuit;
// иначе должно быть >4, но <5, так что ничего не делайте.

на 3 секунды я замыкаю цепь ... когда система снова проходит больше, чем X, она делает IF..else заявление еще раз

Обновление:

То есть вместо гистерезиса вы хотите замкнуть цепь на фиксированное время? Затем попробуйте что-нибудь еще:

if millis() - stop_time > 0 && analog > 5,
   save stop_time = (millis()+3000);
   open the circuit;
end
,

я сделал это, но все еще не то, что я хочу. что, если я сохраняю оператор if, и в течение 3 секунд я заставляю замыкание цепи переходить в состояние LOW, однако на заднем плане переменная A0 будет продолжать читать, но не влияет на контакт 8 или ситуацию замыкания цепи, и когда система снова переходит более чем на X , он снова выполняет оператор IF..else, @Fraunhofer


0

Как я писал в своем комментарии, вы можете использовать millis() для решения своей проблемы. Во-первых, вы должны отказаться от оператора else, так как вы не хотите, чтобы аналоговый сигнал переключал вывод.

unsigned long timestamp=0;
unsigned long interval = 3000;

void loop(){
    if(millis() - timestamp > interval){
        digitalWrite(pin, LOW);
        if(analogRead(A0) > X){
            timestamp = millis();
            digitalWrite(pin, HIGH);
        } 
    }
}

Этот код проверяет, превышает ли разница во времени между текущим моментом и последней активацией 3 сек. Если да, он сначала установит выход на НИЗКИЙ уровень. Затем он проверяет, находится ли аналоговый вход выше X. Если да, выход будет установлен на HIGH, а метка времени будет установлена на текущее время. На следующей итерации цикла разница во времени будет меньше 3 с (поскольку мы только что установили временную метку). Таким образом, ничего не изменится, сохраняя выход на ВЫСОКОМ уровне в течение 3 секунд. После этого выход будет установлен на НИЗКИЙ, а код снова станет разумным на А0.

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

unsigned long timestamp=0;
unsigned long interval = 3000;
byte flag=1;

void loop(){
    if(millis() - timestamp > interval){
        digitalWrite(pin, LOW);
        if(flag && analogRead(A0) > X){
            timestamp = millis();
            flag = 0;
            digitalWrite(pin, HIGH);
        } 
    }
}

Флаг изначально установлен на 1, что активирует часть analogRead(). Затем он обнуляется, что деактивирует его.

Обратите внимание, что этот код относительно прост и содержит некоторые оговорки, которые могут иметь для вас значение, а могут и не иметь:

  • Код не будет реагировать на изменения в течение первых 3 секунд после запуска, так как временная метка изначально установлена равной нулю.
  • Примерно через 50 дней работы счетчик millis() переполнится. Таким образом, если внутренний оператор if не сработает в течение этого времени, код снова не сможет отреагировать на 3 секунды (пока время не пройдет 3 секунды после метки времени).
,