Arduino는 매초마다 중단됩니다. 연락 처리

그리고 Arduino 기능을 사용하는 예는 다음과 같습니다. 부착인터럽트().

인터럽트는 즉각적인 주의가 필요한 이벤트가 발생했음을 프로세서에 알리는 신호입니다. 프로세서는 현재 명령어의 실행을 중단하고 제어권을 인터럽트 핸들러(ISR, 인터럽트 서비스 루틴)로 전달하여 이 신호에 응답해야 합니다. 핸들러는 우리가 직접 작성하고 이벤트에 응답해야 하는 코드를 배치하는 일반 함수입니다.

ISR 인터럽트를 서비스한 후 함수는 작업을 완료하고 프로세서는 중단된 활동으로 행복하게 돌아갑니다. 즉, 중단된 부분부터 코드를 계속 실행합니다. 이 모든 것은 자동으로 발생하므로 우리의 임무는 아무것도 깨지거나 프로세서가 너무 자주 주의를 산만하게 하지 않고 인터럽트 핸들러를 작성하는 것입니다. 회로에 대한 이해, 연결된 장치의 작동 원리, 중단이 얼마나 자주 발생할 수 있는지, 중단 발생의 특징은 무엇인지에 대한 아이디어가 필요합니다. 이 모든 것이 중단 작업의 주요 어려움을 구성합니다.

하드웨어 및 소프트웨어 인터럽트

Arduino의 인터럽트는 여러 유형으로 나눌 수 있습니다.

  • 하드웨어 인터럽트. 마이크로프로세서 아키텍처 수준에서 중단됩니다. 이벤트 자체는 외부 장치에서 생산적인 순간에 발생할 수 있습니다(예: 키보드 버튼 누르기, 컴퓨터 마우스 움직이기 등).
  • 소프트웨어 인터럽트. 특별한 지침을 사용하여 프로그램 내에서 시작됩니다. 인터럽트 핸들러를 호출하는 데 사용됩니다.
  • 내부(동기) 인터럽트. 내부 인터럽트는 프로그램 실행의 변경 또는 위반으로 인해 발생합니다(예: 유효하지 않은 주소에 액세스하는 경우, 유효하지 않은 연산 코드 등).

하드웨어 인터럽트가 필요한 이유는 무엇입니까?

하드웨어 인터럽트는 외부 이벤트에 대한 응답으로 발생하며 외부 하드웨어 장치에서 발생합니다. Arduino는 4가지 유형의 하드웨어 인터럽트를 제공합니다. 인터럽트 핀의 신호는 모두 다릅니다.

  • 접점이지면으로 당겨집니다. 인터럽트 핸들러는 인터럽트 핀에 LOW 신호가 있는 동안 실행됩니다.
  • 접점의 신호를 변경합니다. 이 경우 Arduino는 인터럽트 핀에서 신호 변경이 발생할 때 인터럽트 핸들러를 실행합니다.
  • 핀의 신호를 LOW에서 HIGH로 변경 - 낮은 신호에서 높은 신호로 변경하면 인터럽트 핸들러가 실행됩니다.
  • 핀의 신호를 HIGH에서 LOW로 변경 - 높은 신호에서 낮은 신호로 변경하면 인터럽트 핸들러가 실행됩니다.

인터럽트는 타이밍 문제를 해결하는 데 도움이 되므로 Arduino 프로그램에서 유용합니다. 예를 들어, UART로 작업할 때 인터럽트를 사용하면 각 문자의 도착을 추적할 필요가 없습니다. 외부 하드웨어 장치가 인터럽트 신호를 발행하면 프로세서는 즉시 문자를 적시에 캡처하는 인터럽트 핸들러를 호출합니다. 이는 인터럽트 없이 UART 상태를 확인하는 데 소비되는 CPU 시간을 절약합니다. 대신에 필요한 모든 작업은 기본 프로그램에 영향을 주지 않고 인터럽트 핸들러에 의해 수행됩니다. 하드웨어 장치에는 특별한 기능이 필요하지 않습니다.

인터럽트를 호출해야 하는 주요 이유는 다음과 같습니다.

  • 출력 상태의 변화를 감지하는 단계;
  • 타이머 인터럽트;
  • SPI, I2C, USART를 통한 데이터 인터럽트;
  • 아날로그-디지털 변환;
  • EEPROM, 플래시 메모리 사용 의지.

Arduino에서 인터럽트가 구현되는 방법

인터럽트 신호를 받으면 동작이 정지됩니다. 인터럽트 시 실행되도록 선언된 함수의 실행이 시작됩니다. 선언된 함수는 입력 값을 받을 수 없으며 종료 시 값을 반환할 수 없습니다. 인터럽트는 메인 프로그램 루프의 코드 자체에는 영향을 미치지 않습니다. Arduino에서 인터럽트를 사용하려면 표준 기능이 사용됩니다. 부착인터럽트().

다양한 Arduino 보드의 인터럽트 구현 차이점

특정 마이크로컨트롤러 모델의 하드웨어 구현에 따라 여러 가지 인터럽트가 있습니다. Arduino Uno 보드에는 두 번째와 세 번째 핀에 2개의 인터럽트가 있지만, 2개 이상의 출력이 필요한 경우 보드는 특수 "핀 변경" 모드를 지원합니다. 이 모드는 모든 핀의 입력을 변경하여 작동합니다. 입력 변경 인터럽트 모드의 차이점은 8개 핀 중 어느 핀에서든 인터럽트가 생성될 수 있다는 것입니다. 이 경우 각 연락처의 최신 상태를 추적해야 하므로 처리가 더 어렵고 길어집니다.

다른 보드에서는 인터럽트 수가 더 높습니다. 예를 들어, 보드에는 외부 인터럽트를 처리할 수 있는 6개의 핀이 있습니다. 모든 Arduino 보드의 경우, attachmentInterrupt 함수(인터럽트, 함수, 모드)로 작업할 때 Inerrupt 0 인수는 디지털 핀 2와 연결됩니다.

Arduino 언어의 인터럽트

이제 실제적으로 프로젝트에서 인터럽트를 사용하는 방법에 대해 이야기해 보겠습니다.

구문 AttachInterrupt()

AttachInterrupt 함수는 인터럽트 작업에 사용됩니다. 외부 인터럽트를 핸들러에 연결하는 역할을 합니다.

호출 구문: attachmentInterrupt(인터럽트, 함수, 모드)

함수 인수:

  • Interrupt – 호출할 인터럽트 번호(표준 0 – 두 번째 핀의 경우, Arduino Uno 보드의 경우 1 – 세 번째 핀의 경우)
  • function – 중단될 때 호출될 함수의 이름(중요 - 함수는 어떤 값도 받아들이거나 반환해서는 안 됩니다),
  • 모드 - 인터럽트를 트리거하는 조건입니다.

다음 트리거 조건을 설정할 수 있습니다.

  • LOW – 접점 값이 0일 때 신호 레벨이 낮을 때 수행됩니다. 중단은 주기적으로 반복될 수 있습니다(예: 버튼을 누를 때).
  • CHANGE – 에지에서 신호가 하이에서 로우로 또는 그 반대로 변경될 때 중단이 발생합니다. 신호 변경에 대해 한 번 실행됩니다.
  • RISING – 신호가 LOW에서 HIGH로 바뀔 때 인터럽트를 한 번 실행합니다.
  • FALLING – 신호가 HIGH에서 LOW로 변경될 때 인터럽트를 한 번 실행합니다.4

중요 사항

인터럽트 작업 시 다음과 같은 중요한 제한 사항을 고려해야 합니다.

  • 핸들러 기능은 너무 오랫동안 실행되어서는 안 됩니다. 문제는 Arduino가 동시에 여러 인터럽트를 처리할 수 없다는 것입니다. 핸들러 기능이 실행되는 동안 다른 모든 인터럽트는 무시되며 중요한 이벤트를 놓칠 수 있습니다. 뭔가 큰 일을 해야 한다면 이벤트 처리를 메인 루프() 루프로 옮기면 됩니다. 핸들러에서는 이벤트 플래그만 설정할 수 있고, 루프에서는 플래그를 확인하고 처리할 수 있습니다.
  • 변수에는 매우 주의해야 합니다. 지능형 C++ 컴파일러는 프로그램을 "재최적화"할 수 있습니다. 즉, 불필요한 변수를 제거할 수 있습니다. 컴파일러는 한 부분에서 일부 변수를 설정하고 다른 부분에서 사용하는 것을 보지 못합니다. 기본 데이터 유형의 경우 이러한 가능성을 제거하려면 휘발성 키워드(예: 휘발성 부울 상태 = 0)를 사용할 수 있습니다. 그러나 이 방법은 복잡한 데이터 구조에서는 작동하지 않습니다. 그러므로 항상 경계해야 합니다.
  • 많은 수의 인터럽트를 사용하는 것은 권장되지 않습니다(6-8개 이상 사용하지 마십시오). 다양한 이벤트가 많으면 코드가 심각하게 복잡해지기 때문에 오류가 발생합니다. 또한 중단 횟수가 많은 시스템에서는 실행의 시간적 정확성에 대해 이야기 할 수 없다는 점을 이해해야합니다. 중요한 명령 호출 사이의 간격이 무엇인지 정확히 이해하지 못할 것입니다.
  • 핸들러에서 지연()을 사용하는 것은 엄격히 금지되어 있습니다. 지연 간격을 결정하는 메커니즘은 타이머를 사용하며 핸들러가 차단할 인터럽트에서도 작동합니다. 결과적으로 모든 사람이 모두를 기다리게 되고 프로그램이 정지됩니다. 같은 이유로 인터럽트 기반 통신 프로토콜(예: i2c)은 사용할 수 없습니다.

AttachInterrupt 사용 예

연습을 시작하고 인터럽트를 사용하는 간단한 예를 살펴보겠습니다. 이 예에서는 Arduino Uno의 핀 2의 신호가 변경될 때 전통적으로 LED를 연결하는 핀 13의 상태를 전환하는 핸들러 기능을 정의합니다.

#define PIN_LED 13 휘발성 부울 actionState = LOW; void setup() ( pinMode(PIN_LED, OUTPUT); // 인터럽트 설정 // myEventListener 함수는 // 핀 2에서(인터럽트 0이 핀 2에 연결됨) // 신호가 변경될 때 호출됩니다. 방향) attachmentInterrupt (0, myEventListener, CHANGE); void loop() ( // 모든 이벤트 처리 코드가 myEventListener 함수에 있으므로 루프 함수에서는 아무 작업도 수행하지 않습니다.) void myEventListener() ( actionState != actionState ; // / / LED를 켜거나 끄는 등의 다른 작업 수행 digitalWrite(PIN_LED, actionState )

타이머와 버튼에 대한 보다 복잡한 인터럽트와 해당 핸들러의 몇 가지 예를 살펴보겠습니다.

바운싱 방지 기능이 있는 버튼을 누르면 중단됩니다.

중단되면 발생합니다. 버튼을 눌렀을 때 접점이 밀착되기 전에 진동하여 여러 작업이 생성됩니다. 바운스를 처리하는 방법에는 하드웨어, 즉 버튼에 커패시터를 납땜하는 방법과 소프트웨어의 두 가지 방법이 있습니다.

이 기능을 사용하면 잡담을 없앨 수 있습니다. 버튼을 처음 누른 후 경과한 시간을 측정할 수 있습니다.

If(digitalRead(2)==HIGH) ( //버튼을 눌렀을 때 //이전 버튼을 누른 후 100밀리초 이상 경과한 경우 if (millis() - PreviousMillis >= 100) ( //첫 번째 버튼을 누른 시간 작업이 기억됩니다. PreviousMillis = millis(); if (led==oldled) ( //버튼 상태가 변경되지 않았는지 확인합니다. led=!led; )

이 코드를 사용하면 디바운스를 제거할 수 있으며 인터럽트에서 허용되지 않는 지연 기능의 경우처럼 프로그램 실행을 차단하지 않습니다.

타이머 인터럽트

타이머는 프로세서 16MHz에서 얻은 특정 주파수에서 계산되는 카운터입니다. 원하는 카운팅 모드를 얻도록 주파수 분배기를 구성할 수 있습니다. 설정된 값에 도달하면 인터럽트를 생성하도록 카운터를 구성할 수도 있습니다.

그리고 타이머 인터럽트를 사용하면 밀리초마다 한 번씩 인터럽트를 수행할 수 있습니다. Arduino에는 Timer0, Timer1 및 Timer2의 3가지 타이머가 있습니다. Timer0은 밀리초마다 한 번씩 인터럽트를 생성하는 데 사용되며, 이는 카운터를 업데이트하고 이를 millis() 함수에 전달합니다. 이 타이머는 8비트이며 0부터 255까지 계산됩니다. 값이 255에 도달하면 인터럽트가 생성됩니다. 기본적으로 65의 클록 분배기는 1kHz에 가까운 주파수를 얻는 데 사용됩니다.

비교 레지스터는 타이머의 상태와 저장된 데이터를 비교하는 데 사용됩니다. 이 예에서 코드는 카운터가 0xAF에 도달할 때 인터럽트를 생성합니다.

TIMSK0 |= _BV(OCIE0A);

타이머 인터럽트 벡터에 대한 인터럽트 핸들러를 정의해야 합니다. 인터럽트 벡터는 인터럽트가 호출될 때 실행될 명령의 위치 주소를 가리키는 포인터입니다. 여러 인터럽트 벡터가 인터럽트 벡터 테이블로 결합됩니다. 이 경우 타이머는 TIMER0_COMPA_vect라고 합니다. 이 핸들러는 loop()와 동일한 작업을 수행합니다.

SIGNAL(TIMER0_COMPA_vect) ( unsigned long currentMillis = millis(); 스위퍼1.업데이트(currentMillis); if(digitalRead(2) == HIGH) ( 스위퍼2.Update(currentMillis); led1.Update(currentMillis); ) led2.Update( currentMillis); led3.Update(currentMillis); //loop() 함수는 비어 있습니다. 무효 루프() ( )

요약

Arduino의 중단은 다소 복잡한 주제입니다. 프로젝트의 전체 아키텍처를 한 번에 생각해야 하고, 코드가 어떻게 실행되는지, 어떤 이벤트가 가능한지, 메인 코드가 중단되면 어떤 일이 발생하는지 상상해야 하기 때문입니다. 우리는 이 언어 구성 작업의 모든 기능을 공개하지 않았습니다. 주요 목표는 주요 사용 사례를 소개하는 것이었습니다. 향후 기사에서는 중단에 대한 대화를 더 자세히 계속할 것입니다.

프로젝트가 진행되는 동안 여러 번의 인터럽트가 필요할 수 있지만 각각의 인터럽트가 최대 우선순위를 갖는 경우 실제로는 어떤 기능에도 이를 갖지 않습니다. 같은 이유로 12개 이상의 인터럽트를 사용하는 것은 권장되지 않습니다.

처리기는 시간 간격에 대해 가장 높은 민감도를 갖는 프로세스에만 적용되어야 합니다. 프로그램이 인터럽트 핸들러에 있는 동안에는 다른 모든 인터럽트가 비활성화된다는 점을 잊지 마십시오. 중단 횟수가 많으면 응답이 저하됩니다.

하나의 인터럽트가 활성화되고 다른 인터럽트는 비활성화되는 순간 회로 설계자가 고려해야 할 두 가지 중요한 차이가 발생합니다. 첫째, 중단시간은 최대한 짧아야 한다.

이렇게 하면 예약된 다른 인터럽트를 놓치지 않게 됩니다. 둘째, 인터럽트를 처리할 때 프로그램 코드는 다른 인터럽트의 활동을 요구해서는 안 됩니다. 이를 방지하지 않으면 프로그램이 정지됩니다.

장기간 처리를 사용하지 마십시오 고리() , 변수를 휘발성으로 설정하여 인터럽트 핸들러에 대한 코드를 개발하는 것이 좋습니다. 더 이상의 처리가 필요하지 않다는 것을 프로그램에 알려줍니다.

함수 호출의 경우 업데이트() 여전히 필요하므로 먼저 상태 변수를 확인해야 합니다. 이를 통해 추가 처리가 필요한지 여부를 결정할 수 있습니다.

타이머를 구성하기 전에 코드를 확인해야 합니다. 안두이노 타이머는 3개만 있고 다양한 기능을 수행하는 데 사용되기 때문에 제한된 리소스로 분류되어야 합니다. 타이머 사용과 혼동되면 여러 작업이 단순히 작동을 멈출 수 있습니다.

이 타이머 또는 저 타이머는 어떤 기능을 작동합니까?

Arduino Uno 마이크로컨트롤러의 경우 3개의 타이머 각각에 자체 작동이 있습니다.

그래서 타이머0 다섯 번째 및 여섯 번째 핀의 PWM을 담당하는 기능 밀리초() , 마이크로() , 지연() .

또 다른 타이머 - 타이머1, 라이브러리와 함께 9번째 및 10번째 핀에서 PWM과 함께 사용됨 WaveHC 및 서보.

타이머2 핀 11과 13의 PWM과 함께 작동합니다. 음정.

회로 설계자는 공유 데이터를 안전하게 사용하도록 주의를 기울여야 합니다. 결국 인터럽트는 밀리초 동안 모든 프로세서 작업을 중지하고 프로세서 간의 데이터 교환을 중지합니다. 고리() 인터럽트 핸들러는 지속적이어야 합니다. 컴파일러가 최대 성능을 달성하기 위해 코드 최적화를 시작할 때 상황이 발생할 수 있습니다.

이 프로세스의 결과는 레지스터에 주요 코드 변수의 복사본을 저장하는 것이며, 이는 해당 변수에 대한 최대 액세스 속도를 보장합니다.

이 프로세스의 단점은 실제 값이 저장된 복사본으로 대체되어 기능 손실이 발생할 수 있다는 것입니다.

이런 일이 발생하지 않도록 하려면 변수를 사용해야 합니다. 변동성이 있는 , 이는 불필요한 최적화를 방지하는 데 도움이 됩니다. 업데이트 주기가 필요한 대규모 어레이를 사용하는 경우 이러한 업데이트 중에 인터럽트를 비활성화해야 합니다.

이 프로그램을 설치하면 Arduino IDE와 얼마나 유사한지 놀라실 것입니다. 놀라지 마십시오. 두 프로그램 모두 동일한 엔진에서 만들어집니다.

이 응용 프로그램에는 라이브러리를 포함하여 많은 기능이 있습니다. 연속물, 그래서 우리는 보드와 . 사이의 데이터 전송을 연결할 수 있습니다.

Arduino IDE를 실행하고 가장 간단한 데이터 출력 예를 선택해 보겠습니다. 직렬 포트:

Void setup() ( Serial.begin(9600); ) void loop() ( Serial.println("Hello Kitty!"); // 다음을 보내기 전에 500밀리초를 기다립니다. Delay(500); )

예제를 실행하고 코드가 작동하는지 확인해 보겠습니다.

데이터 수신

이제 우리는 동일한 텍스트를 . 새 프로젝트를 시작하고 코드를 작성해 보겠습니다.

첫 번째 단계는 라이브러리를 가져오는 것입니다. 가자 스케치 | 라이브러리 가져오기 | 연속물. 스케치에 선이 나타납니다.

가져오기 처리.직렬.*; 시리얼 시리얼; // 직렬 포트 객체를 생성합니다. String received; // 직렬 포트에서 수신된 데이터 void setup() ( String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial.available() > 0) ( // 데이터가 있으면 received = serial.readStringUntil("\n"); // 데이터를 읽습니다. ) println(received) // 콘솔에 데이터를 표시합니다.

직렬 포트에서 데이터가 수신되도록 하려면 클래스의 객체가 필요합니다. 연속물. Arduino로 String 형태의 데이터를 보내기 때문에, 프로세싱에서 해당 문자열을 받아야 합니다.

방법에서 설정()사용 가능한 직렬 포트를 가져와야 합니다. 일반적으로 이는 목록에서 사용 가능한 첫 번째 포트입니다. 그런 다음 객체를 구성할 수 있습니다. 연속물, 포트 및 데이터 전송 속도를 나타냅니다(속도가 일치하는 것이 바람직함).

남은 것은 보드를 다시 연결하고, 처리에서 스케치를 실행하고, 애플리케이션 콘솔에서 들어오는 데이터를 관찰하는 것입니다.

프로세싱을 통해 콘솔 작업뿐만 아니라 표준 창 생성도 가능합니다. 코드를 다시 작성해 보겠습니다.

가져오기 처리.직렬.*; 시리얼 시리얼; // 직렬 포트 객체를 생성합니다. String received; // 직렬 포트에서 수신된 데이터 void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial .available() > 0) ( // 데이터가 있으면 // 읽어서 수신된 변수에 씁니다 received = serial.readStringUntil("\n"); ) // 텍스트 설정 textSize(24); (); if (수신됨 != null) ( text(수신됨, 10, 30); ) )

예제를 다시 실행하여 한 곳에 다시 그려진 문구가 있는 창을 살펴보겠습니다.

이를 통해 Arduino로부터 데이터를 수신하는 방법을 배웠습니다. 이를 통해 아름다운 그래프를 그리거나 센서 판독값을 모니터링하는 프로그램을 만들 수 있습니다.

데이터 보내기

우리는 보드로부터 데이터를 수신할 수 있을 뿐만 아니라 보드로 데이터를 전송하여 보드가 컴퓨터로부터 명령을 실행하도록 할 수도 있습니다.

처리에서 문자 "1"을 보낸다고 가정해 보겠습니다. 보드가 전송된 기호를 감지하면 포트 13(내장)의 LED를 켭니다.

스케치는 이전 스케치와 유사합니다. 예를 들어 작은 창을 만들어 보겠습니다. 창 영역을 클릭하면 "1"이 전송되고 확인을 위해 콘솔에 복제됩니다. 클릭이 없으면 "0" 명령이 전송됩니다.

가져오기 처리.직렬.*; 시리얼 시리얼; // 직렬 포트 객체를 생성합니다. String received; // 직렬 포트에서 수신된 데이터 void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (mousePressed == true) ( ​​​​//창 내에서 마우스를 클릭한 경우 serial.write("1"); //1을 보냅니다 println("1"); ) else ( //클릭이 없는 경우 serial.write ("0" ); //0을 보냅니다 ) )

이제 Arduino에 대한 스케치를 작성해 보겠습니다.

Char 명령값; // 직렬 포트에서 오는 데이터 int ledPin = 13; // 내장 LED void setup() ( pinMode(ledPin, OUTPUT); // 데이터 출력 모드 Serial.begin(9600); ) void loop() ( if (Serial.available()) ( commandValue = Serial.read ( ); ) if (commandValue == "1") ( digitalWrite(ledPin, HIGH); // LED 켜기 ) else ( digitalWrite(ledPin, LOW); // 그렇지 않으면 끄기 ) 지연(10); 독서)

두 스케치를 모두 실행해 보겠습니다. 창 안쪽을 클릭하면 LED가 켜집니다. 클릭할 필요도 없지만 마우스 버튼을 누르고 있으면 LED가 계속 켜져 있습니다.

데이터 교환

이제 두 가지 접근 방식을 결합하고 보드와 애플리케이션 간에 두 방향으로 메시지를 교환해 보겠습니다.

효율성을 극대화하기 위해 부울 변수를 추가해 보겠습니다. 결과적으로 우리는 더 이상 처리에서 1 또는 0을 지속적으로 보낼 필요가 없으며 직렬 포트가 언로드되어 불필요한 정보를 전송하지 않습니다.

보드가 전송된 유닛을 감지하면 Boolean 값을 현재 상태와 반대되는 값으로 변경합니다( 낮은~에 높은그 반대). 안에 또 다른우리는 "1"을 감지하지 못한 경우에만 전송하는 문자열 "Hello Kity"를 사용합니다.

기능 연락처 확립()처리 중 수신할 것으로 예상되는 문자열을 보냅니다. 응답이 오면 처리 중이 데이터를 수신할 수 있습니다.

Char 명령값; // 직렬 포트에서 오는 데이터 int ledPin = 13; 부울 ledState = LOW; //LED 상태 제어 void setup() ( pinMode(ledPin, OUTPUT); Serial.begin(9600); 확립Contact(); // 수신기가 응답하는 동안 접촉에 대한 바이트 전송) void loop() ( / / 데이터를 읽을 수 있는 경우 if (Serial.available() > 0) ( // 데이터 읽기 commandValue = Serial.read(); if (commandValue == "1") ( ledState = !ledState; digitalWrite(ledPin, ledState ); ) 지연(100) ; ) else ( // Serial.println("Hello Kitty"); ) 지연(50 ) void discoverContact() ( while (Serial.available());<= 0) { Serial.println("A"); // отсылает заглавную A delay(300); } }

처리 스케치로 넘어 갑시다. 우리는 방법을 사용할 것입니다 직렬이벤트(), 버퍼에서 특정 문자를 만날 때마다 호출됩니다.

새로운 부울 변수를 추가해 보겠습니다. 첫 번째연락처, Arduino에 연결되어 있는지 확인할 수 있습니다.

방법에서 설정()줄을 추가하다 serial.bufferUntil("\n");. 이를 통해 특정 문자를 만날 때까지 들어오는 데이터를 버퍼에 저장할 수 있습니다. 이 경우에는 보내기 때문에 (\n)을 반환합니다. 직렬.println()아두이노에서. "\N"끝에는 새 행이 활성화된다는 의미입니다. 즉, 이것이 우리가 보게 될 마지막 데이터가 될 것입니다.

우리는 지속적으로 데이터를 전송하고 있기 때문에 방법은 직렬이벤트()루프 작업을 수행합니다 그리다(), 그런 다음 비워 둘 수 있습니다.

이제 주요 메소드를 살펴보겠습니다. 직렬이벤트(). 새 줄(\n)을 입력할 때마다 이 메서드가 호출됩니다. 그리고 다음과 같은 일련의 작업이 수행될 때마다:

  • 들어오는 데이터를 읽습니다.
  • 값이 포함되어 있는지 확인합니다(즉, 빈 데이터 배열 또는 "null"이 우리에게 전달되었는지 여부).
  • 공백을 제거하십시오.
  • 필요한 데이터를 처음으로 받으면 부울 변수의 값을 변경합니다. 첫 번째연락처 Arduino에게 새로운 데이터를 받아들일 준비가 되었다고 알려주세요.
  • 이것이 필요한 데이터 유형의 첫 번째 수신이 아닌 경우 이를 콘솔에 표시하고 발생한 클릭에 대한 마이크로컨트롤러 데이터를 보냅니다.
  • 우리는 Arduino에게 새로운 데이터 패킷을 받을 준비가 되었음을 알립니다.
가져오기 처리.직렬.*; 시리얼 시리얼; // 직렬 포트 객체를 생성합니다. String received; // 직렬 포트에서 수신된 데이터 // Arduino에서 수신된 데이터 확인 boolean firstContact = false; void setup() ( size(320, 120); String port = Serial.list(); serial = new Serial(this, port, 9600); serial.bufferUntil("\n"); ) void draw() ( ) void serialEvent(Serial myPort) ( //도착된 데이터에서 문자열을 형성합니다. // "\n" - 구분 기호 - 수신된 데이터 패킷의 끝 = myPort.readStringUntil("\n"); //데이터가 계속하는 방법 앞에는 비어 있지 않습니다. if (received != null) ( //공백 제거 received = Trim(received); println(received); //문자열 "A"를 찾아 핸드셰이크를 시작합니다. //찾으면 지웁니다. 데이터에 대한 버퍼 및 전송 요청 if (firstContact == false) ( if (received.equals("A")) ( serial.clear(); firstContact = true; myPort.write("A"); println("contact "); ) ) else ( //연락이 성립되면 데이터를 수신하고 구문 분석합니다. println(received); if (mousePressed == true) ( ​​​​//창을 클릭한 경우 serial.write("1 "); //1개 보내기 println(" 1"); ) // 모든 데이터가 확보되면 새 패키지를 요청합니다. serial.write("A"); ) ) )

연결되어 실행되면 "Hello Kitty"라는 문구가 콘솔에 나타납니다. 프로세싱 창에서 마우스를 클릭하면 13번 핀의 LED가 켜지거나 꺼집니다.

처리 외에도 PuTTy 프로그램을 사용하거나 포트 작업을 위해 미리 만들어진 클래스를 사용하여 C#으로 자신만의 프로그램을 작성할 수 있습니다.

04.통신 : 디머

이 예에서는 LED의 밝기를 제어하기 위해 컴퓨터에서 보드로 데이터를 전송하는 방법을 보여줍니다. 데이터는 0부터 255까지의 개별 바이트 형태로 제공됩니다. 데이터는 처리를 포함하여 직렬 포트에 액세스할 수 있는 컴퓨터의 모든 프로그램에서 가져올 수 있습니다.

예를 들어 저항과 핀 9에 LED가 있는 표준 회로가 필요합니다.

Arduino를 위한 스케치.

Const int ledPin = 9; // 핀 9의 LED void setup() ( Serial.begin(9600); // 모드를 핀으로 설정 pinMode(ledPin, OUTPUT); ) void loop() ( 바이트 밝기; // 해당 핀에 데이터가 있는지 확인 컴퓨터 if (Serial.available()) ( // 마지막으로 수신된 바이트를 0에서 255까지 읽습니다. 밝기 = Serial.read(); // LED 밝기를 설정합니다. AnalogWrite(ledPin, 밝기); ) )

처리 코드

가져오기 처리.직렬.*; 직렬 포트; void setup() ( size(256, 150); println("사용 가능한 직렬 포트:"); println(Serial.list()); // 이 목록의 첫 번째 포트(번호 0)를 사용합니다. // Arduino 보드에 해당합니다. 마지막 매개변수(예: 9600)는 // 통신 속도입니다. // Arduino 스케치 포트 = new Serial의 Serial.begin()에 전달된 값과 일치해야 합니다. (this, Serial.list(), 9600); // Arduino 보드에서 사용하는 포트 이름을 알고 있는 경우 //port = new Serial(this, "COM1", 9600 ) void draw() (); // 검정색에서 흰색으로 그라데이션을 그립니다. for (int i = 0; i

이를 실행하고 생성된 창 위로 마우스를 원하는 방향으로 이동합니다. 왼쪽으로 이동하면 LED의 밝기가 감소하고, 오른쪽으로 이동하면 증가합니다.

04.통신 : PhysicalPixel (마우스로 LED 켜기)

문제를 조금 바꿔보겠습니다. 사각형 위로 마우스를 이동하고 "H"(높음) 기호를 보내 보드의 LED를 켭니다. 마우스가 사각형 영역을 벗어나면 "L"(낮음) 기호를 보내 LED를 끕니다.

Arduino용 코드.

Const int ledPin = 13; // LED용 핀 13 int incomingByte; // 데이터 수신용 변수 void setup() ( Serial.begin(9600); pinMode(ledPin, OUTPUT); ) void loop() ( // 데이터가 있는 경우 if (Serial.available() > 0) ( // 버퍼의 바이트 읽기comingByte = Serial.read(); // 문자 H(ASCII 72)이면 LED를 켭니다. if (incomingByte == "H") ( digitalWrite(ledPin, HIGH); ) / / 문자 L(ASCII 76)인 경우 LED를 끕니다. if (incomingByte == "L") ( digitalWrite(ledPin, LOW); ) ) )

처리를 위한 코드.

가져오기 처리.직렬.*; 플로트 박스X; 플로트 박스Y; int boxSize = 20; 부울 mouseOverBox = false; 직렬 포트; void setup() ( size(200, 200); boxX = width / 2.0; boxY = height / 2.0; retMode(RADIUS); println(Serial.list()); // Arduino 보드가 연결된 포트를 엽니다. (이 경우 #0) // Arduino가 사용하는 것과 동일한 속도(9600bps)로 포트를 열어야 합니다. port = new Serial(this, Serial.list(), 9600 ) void draw() ( background(0); ); // 커서가 사각형 위에 있는 경우 if (mouseX > boxX - boxSize && mouseX boxY - boxSize && mouseY

04.소통: 그래프(그래프 그리기)

이전 예에서 컴퓨터에서 보드로 데이터를 보냈다면 이제 반대 작업을 수행합니다. 전위차계에서 데이터를 수신하여 그래프 형식으로 표시합니다.


하드웨어 인터럽트

이 수업에 대한 재미있는 그림을 찾을 수 없었고 프로그래밍에 대한 강의 몇 개만 찾았습니다. 이 강의의 시작 부분은 우리에게 무엇을 완벽하게 설명합니까? 방해하다. Arduino의 중단은 정확히 같은 방식으로 설명될 수 있습니다. 마이크로컨트롤러는 "모든 것을 삭제"하고, 인터럽트 처리기의 기능 블록 실행으로 전환하고, 이를 실행한 다음, 중단되었던 주 코드의 위치로 정확하게 돌아갑니다.

중단에는 다양한 유형이 있습니다. 즉, 중단 자체가 아니라 원인이 있습니다. 중단은 아날로그-디지털 변환기, 타이머 카운터 또는 말 그대로 마이크로컨트롤러 핀으로 인해 발생할 수 있습니다. 이러한 인터럽트를 외부 인터럽트라고 합니다. 하드웨어, 이것이 바로 오늘 우리가 이야기할 내용입니다.

외부 하드웨어 인터럽트마이크로컨트롤러 핀의 전압 변화로 인해 발생하는 인터럽트입니다. 요점은 마이크로컨트롤러(컴퓨팅 코어)가 핀을 폴링하지 않습니다그리고 그것에 시간을 낭비하지 않는다, 핀은 다른 하드웨어에 의해 처리됩니다. 핀의 전압이 변경되자마자(디지털 신호, +5 적용/+5 제거) 마이크로 컨트롤러는 신호를 수신하고 모든 것을 삭제하고 인터럽트를 처리한 후 작업으로 돌아갑니다. 이것이 왜 필요한가요? 대부분의 경우 인터럽트는 짧은 이벤트(펄스)를 감지하거나 메인 코드를 로드하지 않고 이벤트 수를 계산하는 데 사용됩니다. 하드웨어 인터럽트는 복잡하고 긴 계산이나 코드 지연 중에 짧은 버튼 누르기 또는 센서 트리거를 포착할 수 있습니다. 대략적으로 말하면 핀이 폴링되고 있습니다. 메인 코드와 병행. 인터럽트는 거의 모든 주변 장치가 꺼진 경우 절전 모드에서 마이크로컨트롤러를 깨울 수도 있습니다. Arduino IDE에서 하드웨어 인터럽트를 사용하는 방법을 살펴보겠습니다.

Arduino의 인터럽트

모든 핀이 인터럽트를 “할 수” 있는 것은 아니라는 사실부터 시작해 보겠습니다. 네, 그런 게 있어요 pinChange인터럽트, 그러나 이에 대해서는 고급 수업에서 이야기하겠습니다. 이제 하드웨어 인터럽트는 특정 핀만 생성할 수 있다는 점을 이해해야 합니다.

MK / 인터럽트 번호 지능 0 지능 1 지능 2 지능 3 지능 4 지능 5
ATmega 328/168(나노, UNO, 미니) D2 D3
ATmega 32U4 (레오나르도, 마이크로) D3 D2 D0 D1 D7
ATmega 2560 (메가) D2 D3 D21 D20 D19 D18

표에서 알 수 있듯이 인터럽트에는 핀 번호와 다른 고유한 번호가 있습니다. 그런데 편리한 기능이 있습니다 digitalPinToInterrupt(핀), 핀 번호를 가져와 인터럽트 번호를 반환합니다. 이 함수에 숫자 3을 Arduino nano에 입력하면 1이 됩니다. 모든 것은 위의 표에 따르며 게으른 사람들을 위한 함수입니다.

인터럽트는 함수를 사용하여 연결됩니다. attachmentInterrupt(핀, 핸들러, 모드):

  • – 인터럽트 번호
  • 매니저– 인터럽트 핸들러 함수의 이름(직접 생성해야 함)
  • 방법– 인터럽트 동작의 “모드”:
    • 낮은(낮음) – 신호에 의해 트리거됨 낮은핀에
    • 상승(성장) - 핀 C의 신호가 변경될 때 트리거됩니다. 낮은~에 높은
    • 떨어지는(드롭) – 핀의 신호가 변경될 때 트리거됩니다. 높은~에 낮은
    • 변화(change) – 신호가 변경될 때 트리거됩니다( 낮은~에 높은그 반대)

인터럽트는 다음 함수를 사용하여 비활성화할 수도 있습니다. 분리인터럽트(핀), 여기서 핀 - 다시 인터럽트 번호.

또한 함수를 사용하여 인터럽트를 전역적으로 비활성화할 수도 있습니다. 인터럽트 없음()다시 해결해 보세요. 인터럽트(). 조심하세요! 인터럽트 없음()타이머 인터럽트도 중지되고 모든 시간 기능과 PWM 생성이 중단됩니다.

버튼 누름이 인터럽트에서 계산되지만 메인 루프에서는 1초의 지연으로 출력되는 예를 살펴보겠습니다. 일반 모드에서 버튼을 사용하면 이러한 대략적인 출력과 지연을 결합하는 것이 불가능합니다.

휘발성 int 카운터 = 0; // 카운터 변수 void setup() ( Serial.begin(9600); // 통신을 위해 포트를 열었습니다 // D2 및 GND pinMode(2, INPUT_PULLUP)에 버튼을 연결했습니다. \ // D2는 인터럽트 0 // 핸들러 - functionbuttonTick // FALLING - 버튼을 누르면 신호 0이 발생하고 이를 포착합니다.attachInterrupt(0,buttonTick,FALLING) voidbuttonTick()(counter++; // + 누르기) void loop()(Serial.println (카운터); // 출력 지연(1000); // 대기)

따라서 우리 코드는 지연 중에도 키 입력을 계산합니다! 엄청난. 하지만 무엇입니까? 휘발성 물질? 전역 변수를 선언했습니다. 카운터, 버튼 클릭 횟수를 저장합니다. 인터럽트에서 변수 값이 변경되면 지정자를 사용하여 이에 대해 마이크로컨트롤러에 알려야 합니다. 휘발성 물질, 변수의 데이터 유형을 표시하기 전에 작성됩니다. 그렇지 않으면 작업이 올바르지 않습니다. 이것은 단지 기억해야 할 것입니다: 인터럽트에서 변수가 변경되면 그렇게 하십시오. 휘발성 물질.

몇 가지 더 중요한 사항:

  • 인터럽트에서 수정된 변수는 다음과 같이 선언되어야 합니다. 휘발성 물질
  • 이와 같은 지연은 인터럽트에서 작동하지 않습니다. 지연()
  • 인터럽트에서 의미가 변경되지 않습니다. 밀리초()그리고 마이크로()
  • 인터럽트에서 포트로의 출력이 제대로 작동하지 않습니다( 직렬.인쇄()), 또한 거기에서 사용하면 안 됩니다. 커널을 로드합니다.
  • 방해할 때 가능한 한 계산을 적게 하고 일반적으로 "긴" 작업을 수행해야 합니다. 이렇게 하면 중단이 자주 발생하여 MC 작동이 느려집니다! 무엇을 해야 할까요? 아래를 읽어보세요.

인터럽트가 즉시 처리할 필요가 없는 일부 이벤트를 포착하는 경우 인터럽트 작업에 다음 알고리즘을 사용하는 것이 좋습니다.

  • 인터럽트 핸들러에서는 단순히 플래그를 올립니다.
  • 프로그램의 메인 루프에서 플래그를 확인하고, 플래그가 발생하면 재설정하고 필요한 작업을 수행합니다.
휘발성 부울 intFlag = false; // 플래그 void setup() ( Serial.begin(9600); // 통신을 위해 포트를 열었습니다 // 버튼을 D2 및 GND pinMode(2, INPUT_PULLUP)에 연결했습니다. // D2는 인터럽트 0입니다. // 핸들러 - 함수 버튼Tick // FALLING - 버튼을 누르면 신호 0이 발생하고 이를 포착합니다.attachInterrupt(0,buttonTick,FALLING) voidbuttonTick()(intFlag = true; //인터럽트 플래그를 발생시킵니다.) void loop()(if (intFlag) ( intFlag = false; // 재설정 // 일부 작업 수행 Serial.println("Interrupt!");

이는 기본적으로 중단에 대해 알아야 할 전부입니다. 고급 강의에서 보다 구체적인 사례를 살펴보겠습니다.

동영상

타이머 인터럽트를 다루는 방법을 배워봅시다. 병렬 프로세스를 갖춘 간단한 프로그램을 작성해 보겠습니다.

실제 프로그램에서는 많은 작업을 동시에 수행해야 합니다. 서론에서 예시를 들었습니다. 그녀가 수행하는 작업을 나열하겠습니다.

작업

사이클 시간
3개의 버튼을 조사하고 해당 버튼의 신호를 처리하여 바운스를 제거합니다. 2ms
7세그먼트 LED 표시기에서 데이터를 재생성합니다. 2ms
2개의 DS18B20 온도 센서에 대한 제어 신호를 생성하고 여기에서 데이터를 읽습니다. 센서에는 1선 직렬 인터페이스가 있습니다. 각 비트당 100 µs,
총 읽기 주기 1초
펠티에 소자의 전류 및 전압, 공급 전압의 아날로그 값 읽기 100μs
아날로그 전류 및 전압 값의 디지털 필터링 10ms
펠티에 소자의 전력 계산 10ms
PID(비례 적분 차동) 전류 및 전압 안정화 컨트롤러 100μs
전력 조절기 10ms
온도 조절기 1 초
보안 기능, 데이터 무결성 모니터링 1 초
관리, 시스템 운영의 일반 로직 10ms

이러한 모든 작업은 서로 다른 주기를 가지고 주기적으로 수행됩니다. 그 중 어느 것도 정지될 수 없습니다. 단기간이라도 작동 기간을 변경하면 심각한 측정 오류, 안정기의 잘못된 작동, ​​깜박이는 표시기, 버튼 누름의 불안정한 반응 등 문제가 발생할 수 있습니다.

냉장고 컨트롤러 프로그램에는 이러한 모든 작업을 각각 고유한 기간을 가진 주기로 수행하는 여러 병렬 프로세스가 있습니다. 병렬 프로세스는 작업이 동시에 수행되는 프로세스입니다.

이전 수업에서는 버튼 개체에 대한 클래스를 만들었습니다. 우리는 이것이 병렬 프로세스에서 신호를 처리하는 클래스라고 말했습니다. 정상적인 작동을 위해서는 일정한 주기(2ms의 시간을 선택함)의 주기로 신호 처리 기능(메소드)을 호출해야 합니다. 그리고 프로그램의 어느 곳에서나 버튼이나 신호의 현재 상태를 보여주는 표시를 사용할 수 있습니다.

하나의 루프에는 버튼 상태를 처리하고 LED를 제어하는 ​​코드를 배치했습니다. 그리고 루프의 끝에서 지연 함수인 Delay(2)를 설정합니다. 그러나 루프에서 프로그램을 실행하는 데 걸리는 시간에 따라 루프의 총 시간이 달라집니다. 그리고 사이클 주기는 분명히 2ms와 같지 않습니다. 또한, Delay() 함수가 실행되는 동안에는 프로그램이 정지되어 다른 작업을 수행할 수 없습니다. 복잡한 프로그램은 완전한 혼란을 초래할 것입니다.

종료 - 하드웨어 타이머 중단 시 버튼 상태를 처리하기 위한 함수를 호출합니다. 2ms마다 메인 프로그램 루프가 중단되어야 하며, 버튼 신호가 처리되고 제어가 중단된 코드의 메인 루프로 돌아갑니다. 버튼 신호를 처리하는 짧은 시간은 메인 루프 실행에 큰 영향을 미치지 않습니다. 저것들. 버튼 처리는 메인 프로그램에서 눈치 채지 못한 채 병렬로 발생합니다.

하드웨어 타이머 인터럽트.

하드웨어 인터럽트는 일부 이벤트를 보고하는 신호입니다. 도착하자마자 프로그램 실행이 일시 중지되고 제어가 인터럽트 핸들러로 전달됩니다. 처리 후 제어는 중단된 프로그램 코드로 돌아갑니다.

프로그램의 관점에서 보면 인터럽트는 프로그램 코드와 직접적인 관련이 없는 외부 이벤트로 인한 함수 호출입니다.

타이머 인터럽트 신호는 지정된 주기 시간을 가지고 주기적으로 생성됩니다. 이는 하드웨어 타이머(특정 값에 도달하면 코드를 재설정하는 논리가 포함된 카운터)에 의해 생성됩니다. 재설정 논리에 대한 코드를 프로그래밍 방식으로 설정함으로써 타이머 중단 기간의 시간을 설정할 수 있습니다.

Arduino 타이머 기간의 모드와 시간 설정은 마이크로 컨트롤러의 하드웨어 레지스터를 통해 수행됩니다. 원한다면 이것이 어떻게 수행되는지 알아낼 수 있습니다. 하지만 저는 MsTimer2 라이브러리를 사용하는 더 간단한 옵션을 제안합니다. 또한 타이머 모드 설정은 거의 발생하지 않습니다. 즉, 라이브러리 기능을 사용해도 프로그램 속도가 느려지지 않습니다.

라이브러리 MsTimer2.

라이브러리는 마이크로컨트롤러의 타이머 2에서 하드웨어 인터럽트를 구성하기 위한 것입니다. 세 가지 기능만 있습니다:

  • MsTimer2::set(unsigned long ms, void (*f)())

이 기능은 인터럽트 기간을 ms 단위로 설정합니다. 이 기간 동안 인터럽트 핸들러 f가 호출됩니다. void(아무것도 반환하지 않음)로 선언되어야 하며 인수가 없어야 합니다. * f는 함수 포인터입니다. 대신, 함수 이름을 작성해야 합니다.

  • MsTimer2::시작()

이 기능은 타이머 인터럽트를 활성화합니다.

  • MsTimer2::중지()

이 기능은 타이머 인터럽트를 비활성화합니다.

함수 이름 앞에 MsTimer2::를 작성해야 합니다. 라이브러리는 네임스페이스 지시문을 사용하여 작성됩니다.

라이브러리를 설치하려면 MsTimer2 디렉터리를 Arduino IDE 작업 폴더의 라이브러리 폴더에 복사하세요. 그런 다음 Arduino IDE 프로그램을 실행하고 스케치 -> 라이브러리 연결 MsTimer2 라이브러리가 라이브러리 목록에 있는지 확인하세요.

MsTimer2 라이브러리를 zip 아카이브로 다운로드할 수 있습니다. 설치하려면 포장을 풀어야 합니다.

버튼 신호를 병렬 처리하는 간단한 프로그램입니다.

이제 6과의 버튼 하나와 LED가 있는 간단한 프로그램을 작성해 보겠습니다. 다이어그램에 따라 하나의 버튼이 Arduino 보드에 연결됩니다.

다음과 같습니다:

버튼을 누를 때마다 Arduino 보드의 LED 상태가 변경됩니다. MsTimer2 및 Button 라이브러리를 설치해야 합니다.

MsTimer2

그리고 지불하십시오. 단지 40 문지름. 매달 모든 사이트 리소스에 액세스할 수 있습니다!

// Sketch_10_1 10과
// 버튼을 누르면 LED 상태가 변경됩니다.

#포함하다
#포함하다

#LED_1_PIN 13 정의 //
#define BUTTON_1_PIN 12 // 버튼은 12번 핀에 연결되어 있습니다

버튼버튼1(BUTTON_1_PIN, 15); // 객체 생성 - 버튼

무효 설정() (

MsTimer2::set(2,timerInterupt); // 타이머 인터럽트 기간을 2ms로 설정
MsTimer2::start(); //
}

무효 루프() (

// LED 제어
if (button1.flagClick == true) (
// 버튼이 클릭되었습니다.



}
}

// 인터럽트 핸들러
무효 타이머인터럽트() (
버튼1.스캔상태(); // 버튼의 안정적인 상태를 기다리는 메서드 호출
}

setup() 함수에서 타이머 인터럽트 주기 시간을 2ms로 설정하고 인터럽트 핸들러인timerInterrupt의 이름을 지정합니다. 버튼 신호 처리 함수인 Button1.scanState()는 2ms마다 타이머 인터럽트 핸들러에서 호출됩니다.

따라서 우리는 버튼 상태를 병렬 프로세스로 처리합니다. 그리고 프로그램의 메인 루프에서 버튼 클릭의 신호를 확인하고 LED 상태를 변경합니다.

휘발성 한정자입니다.

이전 프로그램의 loop() 루프를 수정해 보겠습니다.

무효 루프() (

동안(참) (
if (button1.flagClick == true) 중단;
}

// 버튼이 클릭되었습니다.
버튼1.flagClick= false; // 부호를 재설정
digitalWrite(LED_1_PIN, !digitalRead(LED_1_PIN)); // LED 반전
}

논리적으로 아무것도 바뀌지 않았습니다.

  • 첫 번째 버전에서는 프로그램이 루프를 끝까지 진행하고 그 안의 버튼1.flagClick 플래그를 분석했습니다.
  • 두 번째 옵션에서 프로그램은 무한 while 루프에서 버튼1.flagClick 플래그를 분석합니다. 플래그가 활성화되면 break를 통해 while 루프를 종료하고 LED 상태를 반전시킵니다.

유일한 차이점은 프로그램이 루프 또는 while에서 실행되는 주기입니다.

그러나 최신 버전의 프로그램을 실행하면 버튼을 눌러도 LED가 반응하지 않는 것을 볼 수 있습니다. 클래스를 제거하고 프로그램을 단순화해 보겠습니다.

#포함하다
#LED_1_PIN 13 정의 // 핀 13에 연결된 LED
정수 개수=0;

무효 설정() (
핀모드(LED_1_PIN, OUTPUT); // LED 핀을 출력으로 정의
MsTimer2::set(500, timeInterupt); // 타이머 인터럽트 기간을 500ms로 설정
MsTimer2::start(); // 타이머 인터럽트 활성화
}

무효 루프() (

동안 (참) (
if (count != 0) 중단;
}

개수= 0;
digitalWrite(LED_1_PIN, !digitalRead(LED_1_PIN)); // LED 상태 반전
}

// 인터럽트 핸들러
무효 타이머인터럽트() (
카운트++;
}

이 프로그램에서는 인터럽트 핸들러에서 카운트 카운터가 500ms마다 1씩 증가합니다. while 루프에서는 이를 분석하고, break를 사용하여 루프를 종료하고 LED 상태를 반전시킵니다. 이보다 더 간단한 프로그램은 상상할 수 없지만 이 역시 작동하지 않습니다.

사실 C++ 언어 컴파일러는 지능에 따라 프로그램을 최적화합니다. 때로는 이것이 잘 되지 않을 때도 있습니다. 컴파일러는 while 루프의 count 변수에 대해 어떤 작업도 수행되지 않음을 확인합니다. 따라서 그는 카운트 상태를 한 번만 확인하면 충분하다고 믿습니다. 결코 변경될 수 없는 것을 루프에 체크인하는 이유는 무엇입니까? 컴파일러는 코드를 수정하여 실행 시간에 맞게 최적화합니다. 간단히 말해서 루프에서 변수 검사 코드를 제거합니다. 컴파일러는 count 변수가 인터럽트 핸들러에서 상태를 변경한다는 것을 이해할 수 없습니다. 결과적으로 우리는 while 루프에 갇히게 됩니다.

끝까지 루프를 실행하는 프로그램 버전에서 컴파일러는 모든 변수가 변경될 수 있다고 가정하고 검사 코드를 그대로 둡니다. while 루프에 시스템 함수에 대한 호출을 삽입하면 컴파일러는 변수가 변경될 수 있는지도 결정합니다.

예를 들어, while 루프에 지연() 함수 호출을 추가하면 프로그램이 작동합니다.

동안 (참) (
if (count != 0) 중단;
지연(1);
}

좋은 스타일은 루프가 끝까지 실행되고 프로그램이 어디에서나 멈추지 않는 프로그램을 개발하는 것입니다. 다음 강의에는 끝없는 while 루프에서 플래그를 구문 분석하는 유일한 코드가 포함됩니다. 다음에는 모든 프로그램에서 끝까지 루프를 실행할 예정이다.

때때로 이것은 쉽지 않거나 효과적이지 않습니다. 그런 다음 휘발성 한정자를 사용해야 합니다. 변수가 선언될 때 지정되며 컴파일러에게 변수 사용을 최적화하지 말라고 지시합니다. 병렬 프로세스와 같은 다른 프로그램 단위에서 변수가 변경될 수 있기 때문에 컴파일러가 변수 값에 대해 가정하는 것을 방지합니다. 또한 컴파일러는 범용 레지스터가 아닌 RAM에 변수를 배치합니다.

프로그램에서 카운트를 선언할 때 다음과 같이 작성하면 충분합니다.

휘발성 정수 개수=0;

모든 옵션이 작동합니다.

버튼을 제어하는 ​​프로그램의 경우 Button 클래스 인스턴스의 속성이 변경될 수 있음을 선언해야 합니다.

휘발성 버튼 버튼1(BUTTON_1_PIN, 15); // 객체 생성 - 버튼

내 관찰에 따르면, 휘발성 한정자를 사용해도 프로그램 코드의 길이는 전혀 늘어나지 않습니다.

Bounce 라이브러리와 버튼 신호 처리 방식 비교.

바운스 버튼을 디바운싱하기 위해 미리 만들어진 라이브러리가 있습니다. update() 함수가 호출되면 버튼 상태가 확인됩니다. 이 기능에서:

  • 버튼 신호를 읽습니다.
  • update()에 대한 이전 호출 중의 상태와 비교됩니다.
  • millis() 함수를 사용하여 이전 호출 이후 얼마나 많은 시간이 지났는지 확인합니다.
  • 버튼의 상태가 변경되었는지 여부에 대한 결정이 내려집니다.
  • 그러나 이것은 병렬 신호 처리가 아닙니다. update() 함수는 일반적으로 기본 비동기 프로그램 루프에서 호출됩니다. 일정 시간 이상 호출되지 않으면 버튼 신호 정보가 손실됩니다. 불규칙한 호출은 알고리즘의 잘못된 작동으로 이어집니다.
  • 함수 자체에는 코드가 상당히 많고 Button() 라이브러리 함수보다 실행하는 데 훨씬 더 오래 걸립니다.
  • 평균값을 기반으로 한 신호의 디지털 필터링은 전혀 없습니다.

복잡한 프로그램에서는 이 라이브러리를 사용하지 않는 것이 좋습니다.

다음 강의에서는 병렬 프로세스를 사용하는 좀 더 복잡한 프로그램을 작성해 보겠습니다. 단일 타이머 인터럽트에서 서로 다른 시간 간격을 갖는 루프에서 프로그램 블록의 실행을 구현하는 방법을 알아 보겠습니다.

범주: . 북마크할 수 있습니다.