이 블로그는 Web 환경을 이용한 원격 제어 기술에 필요한 지식을 공유 하기 위한 블로그 입니다.
실제 개발과 프로그램 예를 위하여 WiFi Module과 Raspberry Pi, Raspberry Pi Pico, ATmega128 보드, Arduino Mega 보드(ATmega2560)를 사용 합니다.

timer-pwm-arduino

ESP8266/ESP32 Timer와 PWM - Arduino
ESP8266/ESP32 Timer와 PWM(Pulse width modulation) - Arduino


  • Arduino에서 사용하는 시간(Time)에 관계되는 기본함수(Basic functions)
    • Arduino 프로그램에서 사용하는 시간(Time)에 관계되는 기본함수(Basic functions)
    • Arduino 기본함수는 대부분의 프로세서에서 Hardware timer를 사용하지 않고 프로세서에 별도로 내장된 RTC(Real Time Clock) counter를 이용하여 구현된다.

      참고자료: ATmega128에서 RTC를 구현(ATmega128에는 별도의 RTC counter가 없기 때문에 Timer0를 이용함)하는 예 "Real time clock(Timer를 이용)를 이용한 Time delay"

      • millis()
        • 이 함수는 Arduino 보드가 현재 프로그램을 실행하기 시작한 이후 경과된 milliseconds 수를 반환한다.
        • 이 숫자는 약 50일 후에 Overflows(rolls back to zero) 된다.
        • micros가 반환하는 값은 unsigned long int 이다.
        • 사용 예
        • unsigned long time;

          time = millis();

      • micros()
        • 이 함수는 Arduino 보드가 현재 프로그램을 실행하기 시작한 이후 경과된 microseconds 수를 반환한다.
        • 이 숫자는 약 70분 후에 Overflows(rolls back to zero) 된다.
        • micros가 반환하는 값은 unsigned long int 이다.
        • 사용 예
        • unsigned long time;

          time = micros();

      • delay(value)
        • value 값(milliseconds) 만큼 프로그램을 일시 지연(중지)하는 함수이다.
      • delayMicroseconds(value)
        • value 값(microseconds) 만큼 프로그램을 일시 지연(중지)하는 함수이다.
    • Arduino 기본 시간 함수(Basic time functions)를 이용한 LED 점멸 주기 제어
      • 이 예는 Arduino 기본 시간 함수(Basic time functions)를 이용한 LED 점멸 주기 제어 예이다.

        LED는 개발보드(GPIO2)에 내장된 LED를 이용한다. 회로 구성을 위한 개발보드 Pin 배열은 "ESP8266/ESP32 개발보드 Pin"을 참고하기 바람.

        실험 회로 구성 예

      • LED 점멸 주기 제어 프로그램 예: led-period-control-basic.ino
      • 실험을 위한 준비
        • LED: 개발보드에 내장(GPIO2)되어 있기 때문에 별도의 회로를 필요로 하지 않는다.
        • Button switch: 위 "실험 회로 구성 예"와 같이 GPIO4와 GPIO5에 switch를 연결한다.
      • 실험 방법
        • Arduino IDE를 실행하고 Arduino IDE의 툴 메뉴에서 사용 환경(보드와 COM Port 선택)이 바르게 되어 있는지 확인한다.
        • 위 프로그램을 복사하여 Arduino IDE의 편집 창에 복사한다.
        • 편집 창의 프로그램을 File name "led-period-control-basic.ino"로 저장 한다.
        • 실험
          • "업로드" Icon을 클릭하면 프로그램이 컴파일되고 개발보드로 전송이 시작되는 매세지(Connecting... )가 출력된다.
          • "Connecting... " 메세지가 출력된 다음 약 2Sec 동안 개발보드의 GPIO0 SW를 누르고 있으면(NodeMCU ESP8266 개발보드는 GPIO0를 프로그램에서 제어하기 때문에 GPIO0 SW를 누르지 않아도 됨) 개발 보드에 업로드가 시작된다.
          • "업로드 완료" 매세지가 출력되고 프로그램이 시작되어 LED가 2Sec 간격으로 점멸한다.
          • "툴 -> 시리얼 모니터"를 실행하면 시리얼 모니터 창이 열리고 Interval 값이 출력된다. 필요한 경우 보드레이트를 115200로 설정한다.
          • "GPIO4" Switch을 누르면 Interval 값이 작아지고 LED의 점멸 속도가 빨라진다.
          • "GPIO5" Switch을 누르면 Interval 값이 커아지고 LED의 점멸 속도가 느려진다.

          주: Switch의 Bouncing 현상으로 Switch을 한번 눌러도 두번 또는 세번 누른 것과 같은 결과가 발생할 수 있다.


    • Switch에서 발생하는 Bouncing 문제
      • 아래 그림은 Switch의 금속 접점이 이상적이지 않기 때문에 Switch를 Closed 하거나 Open 하는 순간에 발생하는 Bouncing 현상 예 이다. Bouncing은 Switch의 상태에 따라 대략 5mSec - 16mSec 사이에 발생 한다.

        소프트웨어로 Bouncing 문제를 해결(Debouncing) 하는 방법은 처음 Switch 누름을 인지한 다음 약 15mSec - 20mSec 후, Switch 상태를 다시 확인하여 이 때 까지 누름 상태가 지속되는 경우 누름 상태로 판단한다.

      • SW의 Bouncing 현상을 관찰하기 위한 프로그램 예: sw-counter-bouncing.ino
        • 이 프로그램은 Switch를 사용할 때 발생하는 Bouncing 현상을 관찰하기 위한 프로그램이다.

          Bouncing 현상을 관찰하기 위하여 Switch Input port에 GPIO Interrupt를 사용하고, 빠른 ISR의 실행을 위하여 ISR 내 Code의 길이을 짧게 작성하였다.

      • 실험을 위한 준비
        • LED: 개발보드에 내장(GPIO2)되어 있기 때문에 별도의 회로를 필요로 하지 않는다.
        • Button switch: 위 "실험 회로 구성 예"와 같이 GPIO4에 switch를 연결한다.
      • 실험 방법
        • Arduino IDE를 실행하고 Arduino IDE의 툴 메뉴에서 사용 환경(보드와 COM Port 선택)이 바르게 되어 있는지 확인한다.
        • 위 프로그램을 복사하여 Arduino IDE의 편집 창에 복사한다.
        • 편집 창의 프로그램을 File name "sw-counter-bouncing.ino"로 저장 한다.
        • 실험
          • "업로드" Icon을 클릭하면 프로그램이 컴파일되고 개발보드로 전송이 시작되는 매세지(Connecting... )가 출력된다.
          • "Connecting... " 메세지가 출력된 다음 약 2Sec 동안 개발보드의 GPIO0 SW를 누르고 있으면(NodeMCU ESP8266 개발보드는 GPIO0를 프로그램에서 제어하기 때문에 GPIO0 SW를 누르지 않아도 됨) 개발 보드에 업로드가 시작된다.
          • 업로드가 종료되면 프로그램이 자동으로 실행된다.
          • "툴 -> 시리얼 모니터"를 실행하면 시리얼 모니터 창이 열린다. 필요한 경우 보드레이트를 115200로 설정한다.
          • "GPIO4" Switch을 누르면 counter 값이 1씩 증가하고 결과가 시리얼 모니터 창에 출력되고, 개발 보드의 LED가 Toggle 된다.

          주: Switch의 Bouncing 현상으로 Switch을 한번 눌러도 두번 또는 세번 누른 것과 같은 결과가 발생할 수 있다.


      • Software에 의한 SW Debouncing 프로그램 예: sw-counter-soft-debouncing.ino
        • 이 프로그램은 Software를 사용한 Debouncing 프로그램이다. Software debouncing 기술을 사용하기 때문에 프로그램이 효율적이지는 않지만 Debouncing 기술을 이해하는데는 유용한 방법이다.

      • 실험을 위한 준비
        • LED: 개발보드에 내장(GPIO2)되어 있기 때문에 별도의 회로를 필요로 하지 않는다.
        • Button switch: 위 "실험 회로 구성 예"와 같이 GPIO4에 switch를 연결한다.
      • 실험 방법
        • Arduino IDE를 실행하고 Arduino IDE의 툴 메뉴에서 사용 환경(보드와 COM Port 선택)이 바르게 되어 있는지 확인한다.
        • 위 프로그램을 복사하여 Arduino IDE의 편집 창에 복사한다.
        • 편집 창의 프로그램을 File name "sw-counter-soft-debouncing.ino"로 저장 한다.
        • 실험
          • "업로드" Icon을 클릭하면 프로그램이 컴파일되고 개발보드로 전송이 시작되는 매세지(Connecting... )가 출력된다.
          • "Connecting... " 메세지가 출력된 다음 약 2Sec 동안 개발보드의 GPIO0 SW를 누르고 있으면(NodeMCU ESP8266 개발보드는 GPIO0를 프로그램에서 제어하기 때문에 GPIO0 SW를 누르지 않아도 됨) 개발 보드에 업로드가 시작된다.
          • 업로드가 종료되면 프로그램이 자동으로 실행된다.
          • "툴 -> 시리얼 모니터"를 실행하면 시리얼 모니터 창이 열린다. 필요한 경우 보드레이트를 115200로 설정한다.
          • "GPIO4" Switch을 누르면 counter 값이 1씩 증가하고 결과가 시리얼 모니터 창에 출력되고, 개발 보드의 LED가 Toggle 된다.

          주: Bouncing 문제가 해결 되었으면 버튼을 누를 때 마다 카운터 값이 1 씩 증가 하여야 한다.


  • ESP8266/ESP32 Timer와 Timer interrupt
    • ESP8266는 Hardware timer보다 시간(Time) 함수를 사용하는 것이 일반적이기 때문에 여기서는 ESP32 Hardware timer에 대하여만 설명한다.

      Hardware Timer 이해에 도움이되는 참고자료: "ATmega128 timer"

    • Hardware Timer
      • ESP8266 Hardware timer
        • ESP8266 보드는 2개의 Timer(Timer0, Timer1)가 있지만 Timer0는 WiFi function에서 사용하기 때문에 WiFi를 사용하는 경우 Timer1만 사용할 수 있다.
        • ESP8266 Hardware timer interrupt 참고자료: "ESP8266 TimerInterrupt"

      • ESP32의 Hardware timer
        • ESP32에는 두 개의 Hardware timer 그룹이 있고, 각 그룹에는 2개의 범용 Hardware timer가 있기 때문에 ESP32에는 0-3으로 번호가 매겨진 총 4개의 Timer가 있다.
        • ESP32의 Hardware timer는 모두 일반 64비트 기반 Timer 이다.
        • 각 Timer에는 16-bit Prescaler(2 ~ 65536)가 있고,
        • 자동으로 다시 로드(Automatically reloaded)할 수 있는,
        • 64비트 오름차순/내림차순 카운터(Ascending/Descending counters)가 있다.
      • Timer interrupt를 사용하는 프로그램 작성시 고려하여야하는 사항
        • Timer interrupt ISR 내에는 가능한 빠르게 실행되는 코드(변수 값의 update 등)만 포함하여야 한다. 속도가 느린 코드(Serial port를 이용한 통신 등)는 Arduino 프로그램인 경우 loop()에서 처리하는 것이 바람직하다.
        • Timer interrupt ISR은 Timer interrupt이 발생하면 바로 시작된다. 실제 ISR 내에 있는 Code의 실행 시간은 Interrupt mode에 따라 다르다. One-shot mode인 경우 지정된 시간 후에 Code가 실행된다.
        • ESP32에 있는 4개의 타이머는 서로 다른 별도의 Timer interrupt ISR를 실행할 수 있다.
        • volatile로 선언된 변수의 내용을 Interrupt ISR에서 공유할 수 있다.
      • Prescaler (Time divider) 와 Tics
        • Timer는 Processor clock를 사용하여 경과 시간을 계산한다. ESP32의 Clock 주파수는 80MHz 이다.
        • ESP32 Timer는 64비트 Counters와 16비트 Prescaler(Time dividers)를 기반으로 한다.
        • Prescaler는 기본 신호의 주파수(ESP32의 경우 80MHz)를 나누는 데 사용되며, Prescaler의 출력은 Timer counter의 입력(Timer counter를 Up/Down 하는 Clock)으로 사용된다.
      • ESP32를 사용하는 Arduino project에서 Timer 사용하기: Arduino-ESP32 Timer API
        • 참고자료: "Arduino-ESP32 Timer API"

        • Timer를 구성하려면 아래 예와 같은 hw_timer_t 유형의 변수 포인터가 필요하다.
        • hw_timer_t * timer = NULL;

        • timerBegin: Timer를 구성하는 데 사용된다. 설정이 성공적으로 완료되면 Timer가 자동으로 시작된다.
        • hw_timer_t * timerBegin(uint32_t frequency);

          • frequency: Timer counter의 "Ticking" 주파수(Hz)
          • Timer 구성이 성공하면 timer structure를 반환하고, 오류가 발생하면 NULL이 반환된다.
        • timerEnd: Timer를 타이머를 종료한다.
        • void timerEnd(hw_timer_t * timer);

          • timer: timer structure
        • timerStart: Timer의 counter를 시작한다.
        • void timerStart(hw_timer_t * timer);

          • timer: timer structure
        • timerStop: Timer의 counter를 정지한다.
        • void timerStop(hw_timer_t * timer);

          • timer: timer structure
        • timerRestart: Timer의 counter를 다시 시작한다.
        • void timerRestart(hw_timer_t * timer);

          • timer: timer structure
        • timerWrite: Timer의 counter 값을 설정한다.
        • void timerWrite(hw_timer_t * timer, uint64_t val);

          • timer: timer structure
          • var: 설정할 counter 값.
        • timerRead: Timer의 counter 값을 읽는다.
        • uint64_t timerRead(hw_timer_t * timer);

          • timer: timer structure
          • Timer의 counter value를 Rerutn 한다.
        • timerReadMicros: Timer의 Microseconds 값을 읽는다.
        • uint64_t timerReadMicros(hw_timer_t * timer);

          • timer: timer structure
          • Timer의 counter value(microseconds)를 Rerutn 한다.
        • timerReadMilis: Timer의 Miliseconds 값을 읽는다.
        • uint64_t timerReadMilis(hw_timer_t * timer);

          • timer: timer structure
          • Timer의 counter value(miliseconds)를 Rerutn 한다.
        • timerReadSeconds: Timer의 Seconds 값을 읽는다.
        • double timerReadSeconds(hw_timer_t * timer);

          • timer: timer structure
          • Timer의 counter value(Seconds)를 Rerutn 한다.
        • timerGetFrequency: Timer의 Frequency(Hz)을 읽는다.
        • uint16_t timerGetFrequency(hw_timer_t * timer);

          • timer: timer structure
          • Timer의 Frequency(Hz)을 Rerutn 한다.
        • timerAttachInterrupt: Timer에 Interrupt를 연결한다.
        • void timerAttachInterrupt(hw_timer_t * timer, void (*userFunc)(void));

          • timer: timer structure
          • userFunc: Timer interrupt 가 발생하는 경우 실행되는 함수(ISR)이다.
        • timerAttachInterruptArg: 인수를 사용하여 Timer에 Interrupt를 연결한다.
        • void timerAttachInterruptArg(hw_timer_t * timer, void (*userFunc)(void*), void * arg);

          • timer: timer structure
          • userFunc: Timer interrupt 가 발생하는 경우 실행되는 함수(ISR)이다.
          • arg: Interrupt arguments의 pointer.
        • timerDetachInterrupt: Timer에서 Interrupt를 해제한다.
        • void timerDetachInterrupt(hw_timer_t * timer);

          • timer: timer structure
        • timerAlarm: Alarm 값을 구성하고 Timer를 자동으로 다시 로드하는 데 사용한다. 알람이 자동으로 활성화된다.
        • void timerAlarm(hw_timer_t * timer, uint64_t alarm_value, bool autoreload, uint64_t reload_count);

          • timer: timer structure
          • alarm_value: Trigger 발생에 필요한 Timer의 clock(Tic) 수를 설정한다.
          • autoreload: Trigger가 발생한 다음 Timer의 Counter에 초기값의 자동 Load 여부를 설정한다.
            • true: Trigger가 발생한 다음 Timer의 Counter에 초기값의 자동으로 Load 된다. 이 경우 Timer는 일정한 주기마다 Trigger가 발생(Period mode)한다.
            • false: Trigger가 발생한 다음 Timer의 Counter에 초기값의 Load 되지 않는다. 이 경우 Timer는 일정한 시가 후에 한번만 Trigger가 발생(One-shot mode)한다.
          • reload_count: Autorealod 회수(0 = unlimited). Autorealod가 비활성화된 경우에는 아무런 효과가 없다.

    • 주기적인 Timer interrupt를 이용한 LED 점멸 주기 제어
      • ESP32 Hardware timer를 사용하여 일정한 주기를 발생시키는 프로그램 예: ESP32-led-period-control-timer.ino

      • 실험을 위한 준비
        • LED: 개발보드에 내장(GPIO2)되어 있기 때문에 별도의 회로를 필요로 하지 않는다.
        • Button switch: 위 "실험 회로 구성 예"와 같이 GPIO4에 switch를 연결한다.
      • 실험 방법
        • Arduino IDE를 실행하고 Arduino IDE의 툴 메뉴에서 사용 환경(보드와 COM Port 선택)이 바르게 되어 있는지 확인한다.
        • 위 프로그램을 복사하여 Arduino IDE의 편집 창에 복사한다.
        • 편집 창의 프로그램을 File name "led-period-control-timer.ino"로 저장 한다.
        • 실험
          • "업로드" Icon을 클릭하면 프로그램이 컴파일되고 개발보드로 전송이 시작되는 매세지(Connecting... )가 출력된다.
          • "Connecting... " 메세지가 출력된 다음 약 2Sec 동안 개발보드의 GPIO0 SW를 누르고 있으면 개발 보드에 업로드가 시작된다.
          • 업로드가 종료되면 프로그램이 자동으로 실행된다.
          • 프로그램이 시작되면 LED가 1초에 한번씩 점멸 된다.
          • "GPIO4" Switch을 누르면 timerEnd() 함수가 실행되어 LED의 점멸이 정지 된다.

    • Hardware timer의 One-shot mode를 이용한 Debouncing
    • 이 예는 Timer를 이용하여 일정한 시간 후에 Trigger를 발생(One-shot interrupt)하는 방법을 이해하기 위한 것이다. 이 예는 일정한 시간이 지난 다음 발생하는 Trigger를 이용하여 스위치에서 발생하는 Bouncing 문제를 해결(Debouncing) 하는 예 이다.

        ESP32 Hardware timer의 One-shot mode를 이용한 Debouncing 프로그램 예: ESP32-sw-counter-timer-debouncing.ino

      • 실험을 위한 준비
        • LED: 개발보드에 내장(GPIO2)되어 있기 때문에 별도의 회로를 필요로 하지 않는다.
        • Button switch: 위 "실험 회로 구성 예"와 같이 GPIO4에 switch를 연결한다.
      • 실험 방법
        • Arduino IDE를 실행하고 Arduino IDE의 툴 메뉴에서 사용 환경(보드와 COM Port 선택)이 바르게 되어 있는지 확인한다.
        • 위 프로그램을 복사하여 Arduino IDE의 편집 창에 복사한다.
        • 편집 창의 프로그램을 File name "sw-counter-timer-debouncing.ino"로 저장 한다.
        • 실험
          • "업로드" Icon을 클릭하면 프로그램이 컴파일되고 개발보드로 전송이 시작되는 매세지(Connecting... )가 출력된다.
          • "Connecting... " 메세지가 출력된 다음 약 2Sec 동안 개발보드의 GPIO0 SW를 누르고 있으면 개발 보드에 업로드가 시작된다.
          • 업로드가 종료되면 프로그램이 자동으로 실행된다.
          • "툴 -> 시리얼 모니터"를 실행하면 시리얼 모니터 창이 열린다. 필요한 경우 보드레이트를 115200로 설정한다.
          • "GPIO4" Switch을 누르면 counter 값이 1씩 증가하고 count 값, sw가 Push된 시간, Timer interrupt 실행 시간이 시리얼 모니터 창에 출력되고, 개발 보드의 LED가 Toggle 된다.

          주: Bouncing 문제가 해결 되었으면 버튼을 누를 때 마다 카운터 값이 1 씩 증가 하여야 한다.


    • Hardware timer의 One-shot mode를 이용한 장치 제어
      • 이 예는 Timer를 이용하여 일정한 시간 후에 장치가 동작(One-shot interrupt)하도록 제어하는 예 이다. 이 예는 일정한 시간이 지난 다음 장치가 동작하여 설정한 시간 후에 자동으로 종료되도록 제어한다.

        ESP32 Timer One-shot interrupt를 이용하여 장치를 제어하는 프로그램 예: ESP32-sw-led-timer-one-shot.ino

      • 실험을 위한 준비
        • LED: 개발보드에 내장(GPIO2)되어 있기 때문에 별도의 회로를 필요로 하지 않는다.
        • Button switch: 위 "실험 회로 구성 예"와 같이 GPIO4에 switch를 연결한다.
      • 실험 방법
        • Arduino IDE를 실행하고 Arduino IDE의 툴 메뉴에서 사용 환경(보드와 COM Port 선택)이 바르게 되어 있는지 확인한다.
        • 위 프로그램을 복사하여 Arduino IDE의 편집 창에 복사한다.
        • 편집 창의 프로그램을 File name "sw-led-timer-one-shot.ino"로 저장 한다.
        • 실험
          • "업로드" Icon을 클릭하면 프로그램이 컴파일되고 개발보드로 전송이 시작되는 매세지(Connecting... )가 출력된다.
          • "Connecting... " 메세지가 출력된 다음 약 2Sec 동안 개발보드의 GPIO0 SW를 누르고 있으면 개발 보드에 업로드가 시작된다.
          • 업로드가 종료되면 프로그램이 자동으로 실행된다.
          • "툴 -> 시리얼 모니터"를 실행하면 시리얼 모니터 창이 열린다. 필요한 경우 보드레이트를 115200로 설정한다.
          • Command Switch(GPIO4)을 누르면 3초 후에 LED가 On 되고 2초 후에 Off 된다.

          주: 시리얼 모니터에 출력되는 내용은 프로그램의 실행 순서를 이해 하도록 하기 위한 메세지이다. 프로그램 개발이 완료되면 이 메세지 출력 명령은 삭제한다.


  • ESP8266/ESP32 PWM(Pulse width modulation)
    • PWM(Pulse width modulation) 개요
    • PWM 신호의 Pulse 폭과 PWM 신호의 평균 값(Analog 값)의 관계

      • PWM은 일정한 주기(T)의 펄스 신호에서 신호가 High 상태인 비율을 조정(제어)하여 부하(예: LED, DC 모터 등)에 가하는 전력을 제어하는 기술이다.
      • 신호 주기 동안 계속 High 상태인 경우 부하에 최대 전력이 공급되고, 주기의 50% 동안 High 인경우 최대 전력의 50%가 공급된다. 신호 주기 동안 계속 Low 상태인 경우 부하에 전력은 0 이 된다.
      • PWM 신호의 특성을 결정하는 중요한 매개변수(Parameter)
        • PWM Frequency: PWM frequency는 Pulse 주기가 T인 경우 1/T 로 정의된다. LED의 밝기를 제어(PWM 신호가 High 일때 LED 가 On 되고 Low 일 때 Off)하는 경우 PWM 주기가 너무 낮으면 LED의 깜박 거림이 느껴 진다. 일반적으로 LED 제어에는 1KHz - 5KHz 정도의 주기가 적당하다.
        • PWM Resolution: PWM 신호의 해상도는 한 주기 T를 표시하는 Bit 수를 나타낸다. 해상도를 8Bits로 설정하는 경우 PWM 신호의 한 주기는 256이 되고, 선택할 수 있는 Duty cycle의 값은 [0 - 255] 이 된다. 해상도를 10Bits로 설정하는 경우 PWM 신호의 한 주기는 1024이 되고, 선택할 수 있는 Duty cycle의 값은 [0 - 1023] 이 된다.해상도를 높게 설정하면 더 세밀하게 Duty를 설정할 수 있다.
        • PWM Duty Cycle: PWM 신호의 Pulse 폭을 결정하는 매개변수 이다. 해상도를 8Bits로 설정하면 설정할 수 있는 Duty 값의 범위는 0 - 255 가 된다.

    • ESP32 PWM(Pulse width modulation)
    • ESP32 PWM hardware block diagram

      • ESP32 PWM는 위 Block diagram과 같이 High Speed Channal과 Low Speed Channal 2개 그룹에 각각 8 Channels 씩, 모두 16개의 독립된 Channel로 구성되어 있다.
      • 해상도는 1 - 16 bits 사이에서 선택하여 설정할 수 있다.
      • ESP32 PWM에서 사용하는 Pins
        • ESP32 PWM hardware는 16 개의 독립된 Channel을 갖고 있다.
        • 각 Channel은 임의의 GPIO pin을 사용할 수 있다.
        • 그러나 우리가 사용하는 대부분의 개발보드는 4개의 GPIO pin(GPIO34, 35, 36, 39)은 Input으로만 사용할 수 있도록 설정되어 있기 때문에 이 4개의 Pin를 제외한 Pin를 PWM Output으로 사용할 수 있다.
        • GPIO6 ~ GPIO11은 일부 ESP32 개발 보인 경우 외부 Pin에 연결되어 있다. 그러나 이 핀은 ESP-WROOM-32 칩의 통합 SPI 플래시에 연결되기 때문에 다른 용도로 사용은 권장되지 않는다. 따라서 PWM 프로젝트에서 이 핀을 사용하지 않는 것이 좋다.
      • Arduino project(ESP32)에서 자주 사용하는 PWM 함수: Arduino-ESP32 LEDC API
        • 주: Arduino에서 PWM 기능을 LED의 밝기 제어에 주로 사용하여 LEDC API 라는 명칭을 사용하고 있으나 실제 기능은 PWM API 이다.

          참고자료: "Arduino-ESP32 LEDC API"

        • ledcAttach: 주어진 주파수와 해상도로 LEDC 핀을 설정한다. LEDC 채널은 자동으로 선택된다.
        • bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);

          • pin: PWM 신호 출력 pin
          • freq: PWM Base frequency
          • resolution: LEDC channel의 Resolution. 설정 범위는 1-14 bits (1-20 bits for ESP32) 이다.
          • 구성이 성공하면 true를 반환한다. 오류가 발생하고 LEDC 채널이 구성되지 않은 경우 false가 반환된다.

        • ledcAttachChannel: 주어진 주파수와 해상도, channel 번호로 LEDC 핀을 설정한다.
        • bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel););

          • pin: PWM 신호 출력 pin
          • freq: PWM Base frequency
          • resolution: LEDC channel의 Resolution. 설정 범위는 1-14 bits (1-20 bits for ESP32) 이다.
          • channel: LEDC channel 번호
          • 구성이 성공하면 true를 반환한다. 오류가 발생하고 LEDC 채널이 구성되지 않은 경우 false가 반환된다.

        • ledcWrite: LEDC 핀에 출력되는 PWM 신호 발생에 필요한 duty를 설정한다.
        • void ledcWrite(uint8_t pin, uint32_t duty);

          • pin: PWM 신호 출력 pin
          • duty: LEDC 핀에 출력되는 PWM 신호의 duty 값
          • 설정이 성공하면 true를 반환한다. 오류가 발생하면 false가 반환된다.

        • analogWrite: 핀에 출력되는 아날로그 값(실제는 설정된 duty 값에 해당하는 PWM 신호가 출력됨)을 설정한다. Arduinos AnalogWrite 기능과 호환된다.
        • void analogWrite(uint8_t pin, int value);

          • pin: PWM 신호 출력 pin
          • value: PWM duty 값. 설정 범위는 0 (always off) - 255 (always on) 이다.
    • ESP32 PWM을 이용한 LED 밝기 제어
      • 이 예는 PWM을 이용한 DC 전력제어(예: LED 밝기제어, DC 모터 속도제어 등) 기술를 이해하기 위한 것이다.

      • Arduino analogWrite function를 이용한 LED 밝기 제어(PWM) 프로그램 예: pwm_led_brigth_cont.ino
      • 주: ESP32(or ESP8266)에서 실제 출력되는 PWM(Pulse Width Modulation) 신호는 Duty 값에 따라 펄스폭이 변조된 펄스 신호(디지털 신호)이다. 이 신호를 LED 또는 DC 모터에 가하면 LED 와 DC 모터는 입력되는 신호의 평균 값(Analog 값)에 따라 출력(LED의 밝기 또는 DC 모터 속도)이 결정되기 때문에 Arduino에서 PWM 출력을 Analog 출력이라고 부른다. 아래 프로그램은 Arduino와 호환되는 analogWrite() 함수를 사용하기 때문에 ESP8266에서도 실행할 수 있다.

      • 실험을 위한 준비
        • LED: 개발보드에 내장(GPIO2)되어 있기 때문에 별도의 회로를 필요로 하지 않는다.
      • 실험 방법
        • Arduino IDE를 실행하고 Arduino IDE의 툴 메뉴에서 사용 환경(보드와 COM Port 선택)이 바르게 되어 있는지 확인한다.
        • 위 프로그램을 복사하여 Arduino IDE의 편집 창에 복사한다.
        • 편집 창의 프로그램을 File name "pwm_led_brigth_cont.ino"로 저장 한다.
        • 실험
          • "업로드" Icon을 클릭하면 프로그램이 컴파일되고 개발보드로 전송이 시작되는 매세지(Connecting... )가 출력된다.
          • "Connecting... " 메세지가 출력된 다음 약 2Sec 동안 개발보드의 GPIO0 SW를 누르고 있으면 개발 보드에 업로드가 시작된다.
          • 업로드가 종료되면 프로그램이 자동으로 실행된다.
          • 프로그램이 시작되면 LED가 점점 밝아지고, 점점 어두워 지기를 반복한다.

      • LEDC API를 이용한 LED 밝기 제어(PWM) 프로그램 예: pwm_led_brigth_cont_api.ino
      • 실험을 위한 준비와 실험 방법
        • Arduinos analogWrite function를 이용한 LED 밝기 제어(PWM) 프로그램 예와 동일하다.

    • Servo Motor 제어를 위한 구성도와 프로그램 예
    • Servo Motor는 입력되는 PWM 신호의 펄스폭을 Servo Motor에 내장된 제어회로(프로세서)가 측정하여 PWM 신호의 펄스폭에 비례한 회전을 하도록 모터를 제어 하는 것이기 때문에 원하는 회전 각도에 해당하는 PWM 신호를 한번만 가해 주면 된다. PWM을 이용한 전력제어(예: LED 밝기 제어, DC 모터 속도 제어 등)인 경우에는 PWM 신호를 계속 가해 주여야 한다.

      • Servo Motor 제어 구성도
      • Servo Motor SG90의 제어 신호(Timming 과 Angle)
      • Servo Motor 제어 프로그램 예: ESP32-servo-motor-control.ino
      • 실험을 위한 회로 구성
        • PC의 USB 포트를 ESP32 개발보드의 USB 포트에 연결한다. 이 포트를 사용하여 개발 프로그램이 ESP32에 업로드 되고, 또한 실험 시 Serial 통신을 통하여 Servo Motor를 제어한다.
        • 위 "Servo Motor 제어를 위한 구성도"를 참고하여 ESP32와 Servo Motor를 연결한다.
          • ESP32 GPIO25 Pin <-> Servo Motor 제어 신호(보통 Orange 색 선을 사용)를 연결한다.
          • 모터 제어 전원 5V <-> Servo Motor 5V(보통 Red 색 선을 사용)를 연결한다.
          • ESP32 GND <-> Servo Motor GND(보통 Brown 색 선을 사용)를 연결한다.
      • 실험 방법
        • Arduino IDE를 실행하고 Arduino IDE의 툴 메뉴에서 사용 환경(보드와 COM Port 선택)이 바르게 되어 있는지 확인한다.
        • 위 프로그램을 복사하여 Arduino IDE의 편집 창에 복사한다.
        • 편집 창의 프로그램을 File name "ESP32-servo-motor-control.ino"로 저장 한다.
        • 실험:
          • "업로드" Icon을 클릭하면 프로그램이 컴파일되고 개발보드로 전송이 시작되는 매세지(Connecting... )가 출력된다.
          • "Connecting... " 메세지가 출력된 다음 약 2Sec 동안 개발보드의 GPIO0 SW를 누르고 있으면 개발 보드에 업로드가 시작된다.
          • 업로드가 완료되면 프로그램이 자동으로 실행되어 Servo motor가 2초 간격으로 0도, 90도, 180도, 90도로 회전한다.
          • "툴 -> 시리얼 모니터"를 실행하면 시리얼 모니터 창이 열린다. 필요한 경우 보드레이트를 115200로 설정한다.
          • 모니터 창에서 'u' 명령을 입력하면 10씩 회전 각도가 증가하고, 'd' 명령을 입력하면 10씩 회전 각도가 감소한다. 'r' 명령을 입력하면 90도 위치로 Reset 된다.

    • ESP32 Pulse Width 제어와 Pulse Width 측정
      • 펄스폭 측정을 위한 회로 구성 예

        이 예는 펄스 신호의 펄스폭을 제어(변경)과 펄스폭을 측정하는 기술을 이해하기 위한 예로 시간 측정에 micros() 함수를 사용한다.

      • PWM과 micros() 함수를 이용한 Pulse 발생과 Width 측정 프로그램 예: pulse_width_measure.ino
      • 실험을 위한 준비
        • Button switch: 위 "실험 회로 구성 예"와 같이 GPIO4와 GPIO5에 switch를 연결한다.
        • LED: 개발보드에 내장(GPIO2)되어 있기 때문에 별도의 회로를 필요로 하지 않는다.
        • PWM 출력 신호(GPIO2)와 측정 신호 입력(GPIO16) Pin을 연결 한다.
      • 실험 방법
        • Arduino IDE를 실행하고 Arduino IDE의 툴 메뉴에서 사용 환경(보드와 COM Port 선택)이 바르게 되어 있는지 확인한다.
        • 위 프로그램을 복사하여 Arduino IDE의 편집 창에 복사한다.
        • 편집 창의 프로그램을 File name "pulse_width_measure.ino"로 저장 한다.
        • 실험
          • "업로드" Icon을 클릭하면 프로그램이 컴파일되고 개발보드로 전송이 시작되는 매세지(Connecting... )가 출력된다.
          • "Connecting... " 메세지가 출력된 다음 약 2Sec 동안 개발보드의 GPIO0 SW를 누르고 있으면 개발 보드에 업로드가 시작된다.
          • 업로드가 종료되면 프로그램이 자동으로 실행된다.
          • "툴 -> 시리얼 모니터"를 실행하면 시리얼 모니터 창이 열린다. 필요한 경우 보드레이트를 115200로 설정한다.
          • 콘솔 창에 초기 설정 측정값(대략 500uSec)이 1초 간격으로 출력된다. GPIO4 버튼을 누르면 펄스폭이 감소(약 50uSec)하고, GPIO5 버튼을 누르면 펄스폭이 증가(약 50uSec)한다.
          • 주: 초기 펄스폭을 500uSec로 설정하고, GPIO4 버튼을 누르면 펄스폭이 50uSec 씩 감소하고, GPIO5 버튼을 누르면 펄스폭이 50uSec 씩 증가하도록 설정하였으나 축정오차가 있을 수 있다.


    • DC Motor 속도 제어와 Pulse width 측정을 이용한 Motor 속도 측정
      • 실험에서 기대하는 목표
        • PWM을 이용한 전력제어(이 예에서는 DC Motor의 속도 제어에 이용 함)
        • DC Motor의 속도에 비례한 Event(Pulse) 신호 발생
        • Event(Pulse) 신호의 Pulse width 측정으로 모터 회전 속도를 측정

        DC Motor 속도 제어와 Pulse width 측정을 위한 회로 예

      • DC Motor 속도 제어와 Pulse width 측정을 이용한 Motor 속도 측정 프로그램 예: dc-motor-pwm-speed-pulse-width-measure.ino
      • 실험을 위한 회로 구성
        • PC의 USB 포트를 ESP32 개발보드의 USB 포트에 연결한다. 이 포트를 사용하여 개발 프로그램이 ESP32에 업로드 되고, 또한 실험 시 Serial 통신을 통하여 Servo Motor를 제어한다.
        • 위 "실험 회로 구성 예"와 같이 ESP32 GPIO17를 DC Motor controller의 Enable 단자에 연결한다.
        • ESP32 GPIO16를 Photo Interrupter 출력(74AC14)에 연결한다.
        • USB로 부터 공급되는 ESP32의 5V 전원은 전류 용량이 적어 Servo motor를 구동 시키지 못하기 때문에 별도 DC 5V(2A 정도) 전원을 Servo motor 5V 입력 단자에 연결하여야 한다.
      • 실험 방법
        • Arduino IDE를 실행하고 Arduino IDE의 툴 메뉴에서 사용 환경(보드와 COM Port 선택)이 바르게 되어 있는지 확인한다.
        • 위 프로그램을 복사하여 Arduino IDE의 편집 창에 복사한다.
        • 편집 창의 프로그램을 File name "dc-motor-pwm-speed-pulse-width-measure.ino"로 저장 한다.
        • 실험
          • "업로드" Icon을 클릭하면 프로그램이 컴파일되고 개발보드로 전송이 시작되는 매세지(Connecting... )가 출력된다.
          • "Connecting... " 메세지가 출력된 다음 약 2Sec 동안 개발보드의 GPIO0 SW를 누르고 있으면 개발 보드에 업로드가 시작된다.
          • 업로드가 종료되면 프로그램이 자동으로 실행되고, 모터가 회전하기 시작한다.
          • "툴 -> 시리얼 모니터"를 실행하면 시리얼 모니터 창이 열린다. 필요한 경우 보드레이트를 115200로 설정한다.
          • 모니터 창이 열리면 1초 간격으로 설정된 Duty 값과 측정된 Pulse width 값이 출력된다.
          • 모니터 창에서 'u' 명령을 입력하면 속도가 빨라지고, 'd' 명령을 입력하면 속도가 감소한다. 's' 명령을 입력하면 모터가 정지된다. 정지 상태에서 'u' 명령을 입력하면 모터가 초기 속도로 회전한다.

  • ESP8266/ESP32 Timer 관련 페이지 보기