вывод становится высоким, не позволяя аналоговому чтению измениться
У меня возникла следующая проблема:
Мой 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 << Х
ПОСЛЕДНЕЕ ОБНОВЛЕНИЕ:
Вопрос, решенный Эдгаром Боне
@Fraunhofer, 👍1
Обсуждение3 ответа
Лучший ответ:
Описание, которое вы даете для желаемого поведения, очень нечеткое и тяжело следовать. Я собираюсь перефразировать то, что, как мне кажется, я понял в с точки зрения конечного автомата, что является правильным способом описание такого рода систем. Пожалуйста, ознакомьтесь с описанием ниже, скорее всего, я не совсем понял, чего вы на самом деле хотите.
Допустим, система может находиться в любом из этих трех состояний:
В состоянии
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
Использование различных пороговых значений для переключения состояний — более высокое пороговое значение для перехода в ВЫСОКОЕ и более низкое значение для перехода в НИЗКОЕ — называется гистерезисом. Это создает мертвую зону между двумя значениями переключения, что предотвращает дрожание или мерцание светодиода в этом случае, что могло бы произойти с одной точкой переключения, и аналоговое значение было бы очень близко к нему.
Для этого вам просто нужно, чтобы второе предложение вашего блока 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
Как я писал в своем комментарии, вы можете использовать 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 секунды после метки времени).
- (Код ультразвукового датчика: такого файла или каталога нет)
- Несколько неблокирующих таймеров обратного отсчета?
- Датчик HC-SR505 PIR выдает только HIGH уровень
- Отправка данных из ESP8266 в PHP
- Определение уровня заряда с помощью датчика тока (ACS758) с arduino uno
- Использование YS-IRTM с Arduino Uno
- Как объединить два разных скетча датчика в один полный скетч?
- Как использовать фотодиод для arduino?
Трудно понять, что именно должен делать код. Пожалуйста, постарайтесь объяснить это более понятно. Временные вещи лучше всего делать с помощью
millis()
, как в примере BlinkWithoutDelay, который поставляется с Arduino IDE., @chrislспасибо, я пытался использовать его, но не работал над тем, чего пытался достичь, я отредактировал пост, надеясь, что теперь он более понятен., @Fraunhofer
Итак, вы хотите физически повернуть потенциометр с двигателем, чтобы отрегулировать значение ниже X? Затем мотор должен управляться с Ардуино., @chrisl
возможно, этот способ мог бы работать, каждый раз, когда система переходит на ВЫСОКИЙ уровень, он остается там в течение 3 секунд, затем замыкает цепь или замыкает контакт НИЗКИЙ. однако в фоновом режиме переменная A0 будет продолжать читать, но не будет влиять на контакт 8 или ситуацию замыкания цепи, и когда система превысит X, она снова сделает оператор IF..else, HIGH (3 секунды) остановит IF..ELSE , на фоне читается А0 и если больше Х все повторяется, @Fraunhofer
проблематично управлять двигателем напрямую, потому что это зависит от других частей схемы., @Fraunhofer
Непонятно, что вы подразумеваете под "высоким уровнем". Пожалуйста, нарисуйте четкую таблицу со всем, что должно произойти. Нам нужно четкое и подробное описание желаемого поведения, @chrisl
@chrisl Я обновил исходный пост, большое спасибо за ваше время, @Fraunhofer