FOR оператор цикла с условием IF
Последние несколько дней у меня были проблемы с итерацией цикла FOR в операторе IF.
У меня есть мотор-редуктор постоянного тока 12 В с магнитным энкодером, приводимым в действие драйвером двигателя L298N, который управляется Arduino. Большая часть кода работает, так как при нажатии кнопки 2 двигатель вращается по часовой стрелке; при нажатии кнопки 32 двигатель вращается против часовой стрелки.
Однако, если кнопка 1 нажата, я хочу, чтобы двигатель вращался по часовой стрелке до указанного предела (для которого положение сообщается кодировщику Arudino), затем против часовой стрелки до другого указанного предела и постоянно повторял эту последовательность взад и вперед. Эта итерация цикла FOR будет корректно работать, если она (последовательность туда-сюда) одна помещена в пустой цикл, но если она вложена в оператор IF, она правильно вращается по часовой стрелке, но затем неправильно вращается непрерывно против часовой стрелки (никогда не возвращаясь к направлению по часовой стрелке).
Код приведен ниже, любая помощь приветствуется, и я приношу извинения за многословное объяснение.
#define enA 9
#define in1 6
#define in2 7
const int button1Pin = 8;
const int button2Pin = 12;
const int button3Pin = 13;
int button1State = 0;
int button2State = 0;
int button3State = 0;
int rotDirection = 0;
volatile long temp, counter = 0;
void setup() {
Serial.begin (9600);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
attachInterrupt(0, ai0, RISING);
attachInterrupt(1, ai1, RISING);
pinMode(enA, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
pinMode(button1Pin, INPUT);
pinMode(button2Pin, INPUT);
pinMode(button3Pin, INPUT);
}
void loop() {
analogWrite(enA, 255);
if( counter != temp ){
Serial.println (counter);
temp = counter;
}
button1State = digitalRead(button1Pin);
button2State = digitalRead(button2Pin);
button3State = digitalRead(button3Pin);
if (button1State == HIGH) {
for (counter = 0; counter < 2000; counter++) {
clockwise();
}
for (counter = 2000; counter > 0; counter--) {
counterclockwise();
}
}
if (button2State == HIGH) {
clockwise();
}
if (button3State == HIGH) {
counterclockwise();
}
}
void ai0() {
if (digitalRead(3) == LOW) {
counter++;
} else {
counter--;
}
}
void ai1() {
if (digitalRead(2) == LOW) {
counter--;
} else {
counter++;
}
}
void clockwise () {
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
rotDirection = 1;
delay(20);
}
void counterclockwise () {
digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);
rotDirection = 0;
delay(20);
}
Более простой код (без каких-либо других вводов кнопок), который позволял постоянно переключаться вперед и назад в обоих направлениях, можно увидеть здесь:
#define enA 9
#define in1 6
#define in2 7
const int button1Pin = 8;
const int button2Pin = 12;
const int button3Pin = 13;
int button1State = 0;
int button2State = 0;
int button3State = 0;
int rotDirection = 0;
volatile long temp, counter = 0;
void setup() {
Serial.begin (9600);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
attachInterrupt(0, ai0, RISING);
attachInterrupt(1, ai1, RISING);
pinMode(enA, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
pinMode(button1Pin, INPUT);
pinMode(button2Pin, INPUT);
pinMode(button3Pin, INPUT);
}
void loop() {
analogWrite(enA, 255);
if( counter != temp ){
Serial.println (counter);
temp = counter;
}
button1State = digitalRead(button1Pin);
button2State = digitalRead(button2Pin);
button3State = digitalRead(button3Pin);
for (counter =0; counter<2000; counter++) {
digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);
rotDirection = 1;
delay(20);
}
// If button is pressed - change rotation direction
for (counter= 2000; counter>0; counter--) {
digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);
rotDirection = 0;
delay(20);
}
}
void ai0() {
if(digitalRead(3)==LOW) {
counter++;}
else{
counter--;}
}
void ai1() {
if(digitalRead(2)==LOW) {
counter--;}
else{
counter++;}
}
New Void Loop:
if( counter != temp ){
Serial.println (counter); temp = counter; } long local_counter = counter;
attachInterrupt(digitalPinToInterrupt(0), ai0, RISING);
attachInterrupt(digitalPinToInterrupt(1), ai1, RISING);
while(local_counter <2000){
analogWrite(enA, 255);
clockwise();
detachInterrupt(digitalPinToInterrupt(0));
detachInterrupt(digitalPinToInterrupt(1));
local_counter = counter;
attachInterrupt(digitalPinToInterrupt(0), ai0, RISING);
attachInterrupt(digitalPinToInterrupt(1), ai1, RISING);
}
@wickedhurricane, 👍0
Обсуждение1 ответ
Обратите внимание, что это не полный ответ, а просто помощь в решении очевидных проблем внутри вашего кода. Это может не вызвать вашу текущую проблему, но вы столкнетесь с ними очень скоро, если не исправите их сейчас.
Счетчик переменных
в положении: Очевидно, вы хотите
, чтобы счетчик переменных
был вашей текущей позицией. В этом случае вы не должны изменять его без уважительной причины. Вы используете цикл for для перемещения на свою позицию. Двигатель вращается, и Arduino получает импульсы кодера через прерывания. И это именно то место, где необходимо изменить переменную положения. Вы дополнительно увеличиваете или уменьшаете его в цикле for. Это не имеет смысла, так как время выполнения цикла не имеет никакого отношения к фактической позиции. Вы можете решить эту проблему, просто удалив увеличение/уменьшение и первую настройку переменной в цикле for. Этотfor(counter = 0; counter < 2000; counter++)
становится
for(; counter < 2000; )
Хотя теперь это не столько цикл "для", сколько цикл "время". Так что вы можете вместо этого просто написать
while(counter < 2000)
Для вращения CCV соответственно.
Используя переменные, которые изменяются в ISR: Вам нужно быть осторожным с чтением переменной, которая изменяется в ISR. ISR может произойти в любое время при выполнении вашего основного кода. Arduino Uno нуждается в нескольких командах для обработки многобайтового типа переменной, например
long
, который имеет 4 байта. Таким образом, легко может произойти следующее: код начинает считывать переменнуюсчетчика
, чтобы проверить, меньше ли она 2000, и начинается с первого байта, затем со второго. Но сразу после обработки второго байта включается ISR, потому что произошел импульс. Затем ISR изменяет переменнуюсчетчика
, что означает, что третий и четвертый байты теперь могут отличаться. Основной код будет считывать эти байты с их новыми значениями. Итак, теперь у вас есть 2 байта от старого значения и 2 байта нового значения. Это может означать, что ваши данные могут быть искажены, и вы не можете быть уверены, правильно ли это.Для обработки многобайтовых переменных с помощью ISR необходимо поместить соответствующую часть основного кода в критический раздел. В критическом разделе прерывания отключены. Конечно, вы хотите, чтобы эти разделы были как можно короче. Поэтому в основном вы просто копируете значение из рассматриваемой переменной в локальную переменную, а затем используете ее в дальнейших вычислениях. Так что это выглядело бы примерно так:
noInterrupts(); long local_counter = counter; interrupts(); while(local_counter < 2000){ clockwise(); noInterrupts(); local_counter = counter; interrupts(); }
Чтение поворотного энкодера: Обычный поворотный энкодер имеет два контакта, которые при вращении дадут вам сигнал, подобный этой ссылке на изображение википедии
Один импульс соответствует одному тику. Если вы используете 2 прерывания, каждое для одной строки, вы реагируете дважды на один и тот же импульс. Это означает, что вы получаете в два раза больше тиков. Вам действительно нужно прерывание только для одного контакта; другой тогда просто определяет направление вращения.
Помимо этих пунктов, вы можете попытаться вывести текущее значение счетчика
внутри цикла for/while на последовательный монитор. Тогда вы сможете увидеть, что происходит внутри этого цикла.
Ваше объяснение помогает и очень ценится. Я начинаю с основ и переписываю свой цикл void с циклом while; однако двигатель постоянно вращается, не останавливаясь на 2000. Я опубликую новый цикл void в своем первоначальном посте., @wickedhurricane
- Данные DHT11 из Arduino UNO в Firebase через ESP8266
- Объяснение кода MPU6050
- Как я могу прервать задержку() при нажатии кнопки?
- Проблема с открытием нескольких текстовых файлов одновременно/по порядку с использованием <SD.h>
- Как остановить серводвигатель в текущем положении при использовании последовательного порта? Я хочу, чтобы он оставался в одном положении при получении «0».
- Как установить управляющий регистр в модуле RTC DS3231 для Arduino UNO R3?
- Как повторить другое действие внутри цикла?
- Клиент веб-сокета Arduino CC3000
Почему вы увеличиваете/уменьшаете переменную
счетчик
в цикле for, когда вы уже делаете это внутри ISR? Это не имеет смысла. Кроме того, установив там переменную, вы потеряете некоторые шаги, которые двигатель мог бы сделать в положении 2000, @chrislИ вам не нужно 2 прерывания, чтобы прочитать кодер. Одного достаточно. Один вход получает прерывание, другой определяет направление тика, @chrisl
Когда я помещаю итерацию FOR только в цикл void (без каких-либо других команд или запрошенных кнопок) , она работает правильно (или, по крайней мере, мне нравится) с уменьшением счетчика. В таком случае, не будет ли моя текущая проблема связана с тем, как команда FOR вложена в мой оператор IF?, @wickedhurricane
Вы разместили оба цикла for без остальных? Или только в одном направлении. Трудно сказать, что именно будет делать ваш код. Возможно, что проблема только с одним циклом for в направлении ccv. Пожалуйста, также предоставьте полный код для вашего рабочего тестового примера (где он действительно работал). В противном случае я могу только указать на проблемы, которые я вижу в вашем текущем коде, @chrisl
Извините, я добавил код с помощью команды just the FOR в цикле VOID в свой исходный пост, но не смог сделать это в правильном формате. Однако более простой код допускал правильное вращение вперед и назад., @wickedhurricane
Пожалуйста, сделайте правильный отступ в коде. Это трудно читать и понимать., @the busybee