입력이 변경 될 때 함수를 자동으로 호출 할 수 있습니까?


21

현재 스케치는 메인 루프를 돌 때마다 입력 핀을 확인하고 있습니다. 변경을 감지하면 사용자 정의 함수를 호출하여 이에 응답합니다. 다음은 코드입니다 (필수 항목으로 정리).

int pinValue = LOW;

void pinChanged()
{
    //...
}

void setup()
{
    pinMode(2, INPUT);
}

void loop()
{
    // Read current input
    int newValue = digitalRead(2);

    // Has the input changed?
    if (newValue != pinValue) {
        pinValue = newValue;
        pinChanged();
    }
}

불행히도, 이것은 입력이 아주 조금 변경되는 경우 (예 : 짧은 펄스), 특히 loop()조금 느리게 실행되는 경우 항상 제대로 작동하지 않습니다 .

Arduino가 입력 변경을 감지하고 자동으로 함수를 호출하는 방법이 있습니까?


1
당신이 찾고있는 것 외부 인터럽트
Butzke

답변:


26

외부 인터럽트를 사용하여이 작업을 수행 할 수 있습니다. 대부분의 Arduino는 제한된 수의 핀에서만 이것을 지원합니다. 자세한 내용은의 설명서를 참조하십시오 attachInterrupt().

Uno를 사용한다고 가정하면 다음과 같이 할 수 있습니다.

void pinChanged()
{
    //...
}

void setup()
{
    pinMode(2, INPUT);
    attachInterrupt(0, pinChanged, CHANGE);
}

void loop()
{
}

pinChanged()외부 인터럽트 0에서 변경이 감지 될 때마다 호출 됩니다. Uno에서는 GPIO 핀 2에 해당합니다. 외부 인터럽트 번호는 다른 보드와 다르므로 관련 문서를 확인하는 것이 중요합니다.

그러나이 방법에는 제한이 있습니다. 사용자 정의 pinChanged()기능이 ISR (Interrupt Service Routine)로 사용되고 있습니다. 이는 loop()호출이 실행되는 동안 나머지 코드 (에있는 모든 것 )가 일시적으로 중지됨을 의미합니다. 중요한 타이밍을 방해하지 않으려면 최대한 빨리 ISR을 만들어야합니다.

또한 ISR 중에는 다른 인터럽트가 실행되지 않습니다. 이는 인터럽트 (예 : 코어 delay()millis()기능) 에 의존하는 모든 것이 내부에서 제대로 작동하지 않을 수 있음을 의미합니다.

마지막으로, ISR이 스케치에서 전역 변수를 변경해야하는 경우 일반적으로 다음과 같이 선언해야합니다 volatile.

volatile int someNumber;

컴파일러에 값이 예기치 않게 변경 될 수 있음을 알리기 때문에 중요하지 않으므로 오래된 사본 / 캐시를 사용하지 않도록주의해야합니다.


문제에서 언급 된 "브리프 펄스"와 관련하여 핀이 인터럽트를 트리거하기위한 상태에 있어야하는 최소 시간이 있습니까? (분명히 그것은 루프에서 일어나는 다른 일에 달려있는 폴링보다 훨씬 적을 것입니다)
sachleen

1
@sachleen ISR 기능을 실행하는 동안 발생하지 않는 한 (답변에 설명 된대로) 작동합니다. 그렇기 pinChanged()때문에 가능한 한 짧아야합니다. 따라서 일반적으로 최소 시간은 pinChanged()함수 자체 를 실행하는 시간이어야 합니다.
jfpoilpret

2
인터럽트를 사용할 때주의해야 할 모든 중요한 것들을 포함하는이 매우 자세한 답변은 +1입니다!
jfpoilpret

3
공유 전역을 선언하는 것 외에도 volatile, 전역 변수가 1 바이트보다 넓은 경우 someNumber와 같이 프로그램이 바이트 액세스간에 발생하는 핀 변경 인터럽트로부터 보호해야합니다. 같은 문장 someNumber +=5;에는 캐리가 포함 된 낮은 바이트를 추가하고 높은 바이트를 추가하는 것이 포함됩니다. 이 두 개 (더 넓은 변수의 경우)는 인터럽트로 나눌 수 없습니다. (각각) 작업 전후에 인터럽트를 끄고 복원하는 것으로 충분합니다.
JRobert

@sachleen-최소 펄스 크기에 관한 것입니다. 데이터 시트에서 명확한 답을 찾기는 어렵지만 핀 변환 인터럽트 타이밍으로 판단하면 반 클럭 사이클 내에 래치됩니다. 인터럽트가 "기억"되면 ISR이 시작되어 처리 할 때까지 계속 기억됩니다.
Nick Gammon

5

디지털 입력으로 구성된 핀의 모든 변경 상태로 인해 인터럽트가 발생할 수 있습니다. INT1 또는 INT2에 의한 인터럽트 원인의 고유 벡터와 달리 PinChangeInt 기능은 공통 벡터를 사용한 다음이 벡터에 대한 ISR (Interrupt Service Routine)을 통해 변경된 핀을 결정해야합니다.

다행히 PinChangeInt 라이브러리를 사용하면이 작업을 쉽게 수행 할 수 있습니다.

PCintPort::attachInterrupt(PIN, burpcount,RISING); // attach a PinChange Interrupt to our pin on the rising edge
// (RISING, FALLING and CHANGE all work with this library)
// and execute the function burpcount when that pin changes

0

단순히 높음 또는 낮음이 아니라 임계 값을 통과하는 전압을 감지하려는 경우 아날로그 비교기를 사용할 수 있습니다. 스케치 예 :

volatile boolean triggered;

ISR (ANALOG_COMP_vect)
  {
  triggered = true;
  }

void setup ()
  {
  Serial.begin (115200);
  Serial.println ("Started.");
  ADCSRB = 0;           // (Disable) ACME: Analog Comparator Multiplexer Enable
  ACSR =  bit (ACI)     // (Clear) Analog Comparator Interrupt Flag
        | bit (ACIE)    // Analog Comparator Interrupt Enable
        | bit (ACIS1);  // ACIS1, ACIS0: Analog Comparator Interrupt Mode Select (trigger on falling edge)
   }  // end of setup

void loop ()
  {
  if (triggered)
    {
    Serial.println ("Triggered!"); 
    triggered = false;
    }

  }  // end of loop

이것은 입력에서 1V에서 2V 로의 변화를 감지해야하는 광 검출기와 같은 것들에 유용 할 수 있습니다.

회로 예 :

여기에 이미지 설명을 입력하십시오

현재 타이머 / 카운터 1 카운트를 저장하여 특정 입력의 정확한 시간을 기억하는 프로세서에서 입력 캡처 장치를 사용할 수도 있습니다. 이렇게하면 이벤트가 발생한 정확한 (거의 정확한) 순간을 저장할 수 있습니다. ISR을 사용하여 현재 시간을 캡처하기 전에 지연 (아마도 몇 마이크로 초)이 아니라 관심이 생겼습니다.

타이밍이 중요한 애플리케이션의 경우 정확도가 다소 향상 될 수 있습니다.

적용 예 : Arduino를 커패시터 테스터로 전환

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.