Arduino II에서 대기: 구조를 위한 millis() 함수

12190 단어 diyarduinocpp

소개: 지연 없이 LED 깜박임()



첫 번째 게시물에서 우리는 delay() 패턴이 작업 사이를 기다리는 데 사용되지 않는 이유를 인용하기 위해 official Arduino tutorial에 연결했습니다. 그 당시에 완전히 읽었다면 아마도 이 게시물에서 제시할 대안인 millis() function을 대신 사용하는 것을 망쳤을 것입니다.
millis() "Arduino 보드가 현재 프로그램을 실행하기 시작한 이후 경과된 밀리초 수를 반환"하므로 delay()를 대체할 수 없습니다(그렇지 않으면 동일한 방법일 뿐입니다!).

다음은 delay()millis()로 바꾸는 데 사용할 수 있는 패턴입니다( 와 비교할 수 있음).

const int waitingMs = 2000;   // 2 seconds
unsigned long previousMillis = 0;
bool ledState = LOW;

void setup() { pinMode(LED_BUILTIN, OUTPUT); }

void loop()
{
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= waitingMs)
  {
    previousMillis = currentMillis;
    ledState = !ledState;
    digitalWrite(LED_BUILTIN, ledState);
  }
}

이 코드here도 찾을 수 있습니다.
  • 특정 양의 밀리초( previousMillis )를 저장하기 위해 전역 변수를 생성하고 이를 0으로 초기화합니다.
  • LED의 현재 상태를 저장할 전역 변수를 만듭니다( ledState ).
  • 각 루프 반복에서 millis()를 호출하여 현재 밀리초 수를 가져오고 그 값을 currentMillis 라는 로컬 변수에 저장합니다.
  • 현재 밀리초 수( currentMillis )와 저장한 밀리초 수( previousMillis ) 사이의 차이를 비교하여 대기하려는 시간( waitingMs )보다 큰지 확인합니다.
  • 그렇다면 전역 변수previousMillis를 현재 밀리초 수(currentMillis)로 업데이트하고 계획했던 모든 작업을 수행합니다(이 경우 LED 상태 변경: 값 업데이트 ledState 변수를 만들고 digitalWrite 메서드를 사용하여 적용합니다.
  • 그렇지 않으면 스케치 실행을 계속하십시오.


  • 사례 연구로 돌아가기



    우리는 간단한 레이저 절단기를 만들고 싶습니다.

    우리는 상당히 올바른 상태 기계를 기반으로 식별했지만 주요 결함이 있었습니다. 크고 둥근 빨간색 비상 정지 버튼을 눌렀을 때 즉시(또는 전혀) 식별할 수 없다는 것입니다.

    첫 번째 구현의 주요 문제는 delay() method 을 사용하는 것인데, 기본적으로 우리가 원하는 만큼 자주 비상 버튼을 확인할 수 없습니다.

    millis()를 사용하는 솔루션



    방금 배운 패턴을 코드에 적용해 보겠습니다.

    const unsigned long waiting_time_ms = 3000;   // 3s
    unsigned long saved_moment = 0;
    int state = 0;
    
    void setup() { Serial.begin(9600); }
    
    void loop()
    {
      unsigned long current_moment = millis();
      if (current_moment - saved_moment >= waiting_time_ms)
      {
        saved_moment = current_moment;
        state = ++state % 2;        // 0 -> 1 -> 0 -> 1 -> 0 -> 1 -> 0 ...
        do_action();
      }
    
      stop_in_case_of_emergency();  // This is checked every few milliseconds
    }
    
    void do_action()
    {
      switch(state)
      {
        case 0:
        {
          turn_laser_off();
          turn_motor_on();
          break;
        }
        case 1:
        {
          turn_motor_off();
          turn_laser_on();
          break;
        }
      }
    }
    
    void turn_laser_on()  { Serial.println("laser on"); }
    void turn_laser_off() { Serial.println("laser off"); }
    void turn_motor_on()  { Serial.println("motor on"); }
    void turn_motor_off() { Serial.println("motor off"); }
    
    bool is_emergency_button_pressed()
    {
      // Serial.println("Checking emergency button");
      if(Serial.available())
      {
        Serial.println("Stopping due to emergency: " + Serial.readString());
        Serial.println("Please reset");
        return true;
      }
    
      return false;
    }
    
    void stop_in_case_of_emergency()
    {
      if (is_emergency_button_pressed())
      {
        turn_motor_off();
        turn_laser_off();
    
        while (0 != 1) { }
      }
    }
    

    이 코드here도 찾을 수 있습니다.

    이 구현과 이 구현 간의 주요 기능적 차이점은 여기에서 각 루프 반복이 몇 밀리초 동안만 지속된다는 것입니다(다른 경우에는 6초 이상).

    Arduino는 이 코드를 실행하는 동안 더 많은 작업을 수행하지만 결국 그것이 우리가 가지고 있는 것입니다. 맞습니까? 우리는 추가 '가격'*을 지불하지 않습니다.

    사실, 그것은 정반대입니다. 우리(제조자/프로그래머)에게 Arduino는 millis()의 값을 계산하고 몇 밀리초마다 저장된 변수의 내용과 비교하는 것이 Arduino가 delay() 메서드에서 기다리는 것보다 훨씬 효율적입니다. , 전자를 사용하면 '대기'하는 동안 다른 작업을 수행할 수 있기 때문입니다.

    일반적으로 이 millis() 패턴은 Arduino 코딩 수준을 높이거나 실제 프로젝트에 사용하려는 경우 채택해야 하는 접근 방식입니다.

    이 시리즈에는 이 문제를 해결하는 더 복잡한 방법을 탐구하는 더 많은 항목이 있지만(계속 지켜봐 주십시오!), 모두 언급된millis() 패턴에 의존하므로 먼저 해당 패턴을 이해하는 것이 중요합니다.


    * 단순한 일화로서 다음은 두 스케치를 컴파일한 결과입니다(arduino-cli 사용).

    *       Compiling 1_delay_implementation
    Sketch uses 3664 bytes (11%) of program storage space. Maximum is 32256 bytes.
    Global variables use 302 bytes (14%) of dynamic memory, leaving 1746 bytes for local variables. Maximum is 2048 bytes.
    *       Compiling 3_basic_millis_implementation
    Sketch uses 3588 bytes (11%) of program storage space. Maximum is 32256 bytes.
    Global variables use 282 bytes (13%) of dynamic memory, leaving 1766 bytes for local variables. Maximum is 2048 bytes.
    

    millis를 사용하는 것이 스토리지 및 메모리 측면에서 약간 더 효율적입니다! 물론 실행할 때 초당 더 많은 작업을 실행하지만 동일한 코드를 반복해서 실행하는 이러한 종류의 장치에서 우리가 기꺼이 지불하는 가격은 작업 시간이 매우 짧기 때문입니다!

    좋은 웹페이지 즐겨찾기