IF 문 내의 FOR 문

Aug 30 2020

지난 며칠 동안 IF 문 내에서 FOR 루프 반복에 문제가있었습니다.

Arduino에 의해 제어되는 L298N 모터 드라이버에 의해 구동되는 자기 인코더가있는 12V DC 기어드 모터가 있습니다. 대부분의 코드는 작동합니다. 버튼 2를 누르면 모터가 시계 방향으로 회전합니다. 버튼 32를 누르면 모터가 시계 반대 방향으로 회전합니다.

그러나 버튼 1을 누르면 모터가 시계 방향으로 지정된 한계 (위치가 인코더에서 Arudino에보고 됨)까지 회전 한 다음 시계 반대 방향으로 다른 지정된 한계까지 회전하고이 앞뒤 순서를 계속 반복합니다. . 이 FOR 루프 반복은 (앞뒤 시퀀스) 단독으로 void 루프 내부에 배치 된 경우 올바르게 작동하지만 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++;}
  }

새로운 공허 루프 :

 if( counter != temp ){

Serial.println (카운터); 온도 = 카운터; } 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);

}

답변

1 chrisl Aug 31 2020 at 00:57

이것은 완전한 답은 아니지만 코드 내부의 명백한 문제를 돕는 것입니다. 이것들은 현재 문제를 일으키지 않을 수도 있지만 지금 수정하지 않으면 곧 문제를 발견하게 될 것입니다.

  • counter위치로서의 변수 : 분명히 변수 counter가 현재 위치가되도록 의도합니다 . 이 경우 정당한 이유없이 변경해서는 안됩니다. for 루프를 사용하여 위치로 이동하고 있습니다. 모터가 회전하고 Arduino는 인터럽트를 통해 인코더 펄스를 얻습니다. 그리고 그것이 바로 위치 변수를 변경해야하는 곳입니다. for 루프에서 추가로 늘리거나 줄입니다. 루프의 실행 시간은 실제 위치와 관련이 없기 때문에 의미가 없습니다. for 루프에서 증가 / 감소와 변수의 첫 번째 설정을 제거하여이 문제를 해결할 수 있습니다. 이

      for(counter = 0; counter < 2000; counter++)
    

    된다

      for(; counter < 2000; )
    

    지금은 for 루프가 아니라 while 루프입니다. 그래서 대신 쓸 수 있습니다

      while(counter < 2000)
    

    이에 상응하는 CCV 회전.

  • ISR에서 변경되는 변수 사용 : ISR에서 변경 되는 변수를 읽을 때는주의해야합니다. ISR은 기본 코드 실행시 언제든지 발생할 수 있습니다. Arduino Uno long는 4 바이트가있는 과 같은 다중 바이트 변수 유형을 처리하기 위해 여러 명령이 필요 합니다. 따라서 다음과 같은 상황이 쉽게 발생할 수 있습니다. 코드는 counter변수가 2000보다 작 으면 확인하기 위해 변수 를 읽기 시작하고 첫 번째 바이트에서 시작한 다음 두 번째 바이트로 시작합니다. 그러나 두 번째 바이트가 처리 된 직후 펄스가 발생했기 때문에 ISR이 시작됩니다. 그런 다음 ISR은counter즉, 이제 세 번째와 네 번째 바이트가 다를 수 있습니다. 기본 코드는 새 값으로 이러한 바이트를 읽습니다. 이제 이전 값의 2 바이트와 새 값의 2 바이트가 있습니다. 즉, 데이터가 깨져서 정확한지 확신 할 수 없습니다.

    ISR을 사용하여 멀티 바이트 변수를 처리하려면 주요 코드의 해당 부분을 중요 섹션으로 묶어야합니다. 중요 섹션에서는 인터럽트가 비활성화됩니다. 물론 이러한 섹션은 가능한 한 짧아야합니다. 따라서 대부분은 해당 변수의 값을 지역 변수로 복사 한 다음 추가 계산에 사용합니다. 따라서 다음과 같이 보일 것입니다.

      noInterrupts();
      long local_counter = counter;
      interrupts();
      while(local_counter < 2000){
          clockwise();
          noInterrupts();
          local_counter = counter;
          interrupts();
      }
    
  • 로터리 엔코더를 읽기 : 정상적인 로터리 엔코더는 회전에 당신에게이 같은 신호를 줄 것이다 두 개의 핀이 있습니다 위키 피 디아 이미지 링크를

하나의 펄스는 하나의 틱에 해당합니다. 한 라인에 각각 2 개의 인터럽트를 사용하는 경우 동일한 펄스에 대해 두 번 반응합니다. 즉, 틱 수가 두 배가됩니다. 정말 하나의 핀에 대한 인터럽트 만 필요합니다. 다른 하나는 회전 방향을 결정합니다.


이러한 점 외에도 counterfor / while 루프 내부의 현재 값을 직렬 모니터 에 인쇄 할 수 있습니다 . 그러면 그 루프 안에서 무슨 일이 일어나는지 알 수 있습니다.