2018년 11월 20일 화요일

ESP32 PWM 사용하기

ESP32에는 16개의 독립적인 LED PWM 채널을 가지고 있어 각각 다른 속성의 PWM 신호를 만들어 낼 수 있다.

1. 어떤 PWM 채널을 사용할 지 골라야 한다. 0~15 사이의 값을 사용할 수 있다.
2. PWM 주파수를 설정해 줘야 한다. LED의 경우 5000 Hz 정도면 충분하다.
3. PWM duty cycle resolution을 설정해 줘야 한다. 해상도(resolution)은 1비트에서 16비트까지 지정할 수 있다. 여기서는 8 bit 해상도를 사용하는데 그 경우 LED 밝기 값은 0에서 255가 될 수 있다. 만일 16 bit로 설정하면 좀 더 세밀하게 (0~65535) 밝기 값을 조정할 수 있게 된다.

ledcSetup(ch, freq, resolution);   // ch: PWM channel (0~15), freq: PWM 주파수, resolution: PWM  해상도


PWM 주파수는 1초에 몇번 신호를 ON/OFF 할 것인지 결정한다. 위의 그림에서 처럼 PWM 주파수를 4 Hz로 한다면 CH0에 연결되어 있는 LED는 4번 on/off를 반복할 것이다. 8 Hz인 경우 CH1에 연결되어 있는 LED는 8번 on/off를 하게 될 것이다. 아두이노 우노의 경우 PWM 주파수는490 Hz(5,6번 핀은 980 Hz)로 고정되어 있다.

Resolution은 한 주기 내에서 얼마나 세밀하게 시간을 지정할 수 있는가를 결정한다. 8-bit resolution인 경우 2^8 = 256 등분으로 나누지만 10-bit resolution이 되면 2^10 = 1024 등분으로 나뉘어 훨씬 더 정밀하게 제어가 가능해진다. ESP32에서는 1-bit 부터 16-bit까지 resolution을 지정할 수 있다. 아두이노 우노의 경우에는 analogWrite()의 resolution은 8-bit로 고정되어 있어 출력값으로 0~255 사이의 값을 사용해야 한다.

4. 설정한 채널의 출력을 어느 GPIO 핀으로 보낼것인지 지정해야 한다.

ledcAttachPin(gpio, ch);   // gpio : GPIO 핀 번호,  ch : PWM channel

5. 이제 ledcWrite() 함수를 사용해 LED 밝기를 조절할 수 있다.

ledcWrite(ch, duty); // ch : PWM channel, duty : Duty cycle

duty 값은 한 주기 내에서 ON 시간과 OFF 시간의 비율을 결정하는 값이다.


위의 그림을 보면 알 수 있듯이 PWM 주파수가 다르면 같은 duty 값이라도 ON 되어 있는 시간이 달라진다. 다만 전체적으로 ON 시간의 합과 OFF 시간의 합의 비율은 동일하다.




위와 같이 LED를 연결한 경우 LED가 점점 밝아졌다가 다시 점점 어두워지는걸 반복하는 코드는 다음과 같다.

const int ledPin = 16;  // 16 corresponds to GPIO16
// setting PWM properties
const int freq = 5000;
const int ledChannel = 0;
const int resolution = 8;
 

void setup(){
  ledcSetup(ledChannel, freq, resolution);  
// configure LED PWM functionalitites  
  ledcAttachPin(ledPin, ledChannel);  
  // attach the channel to the GPIO to be controlled
}
 void loop(){
  // increase the LED brightness
  for(int dutyCycle = 0; dutyCycle <= 255; dutyCycle++){   
    // changing the LED brightness with PWM
    ledcWrite(ledChannel, dutyCycle);
    delay(15);
  }

  // decrease the LED brightness
  for(int dutyCycle = 255; dutyCycle >= 0; dutyCycle--){
    // changing the LED brightness with PWM
    ledcWrite(ledChannel, dutyCycle);   
    delay(15);
  }
}

PWM 신호의 frequency를 조정할 수 있으므로 16개의 서보모터를 동시에 제어할 수도 있다.


서보모터는 위와 같은 PWM 신호로 제어를 하기 때문에 PWM frequency를 50 Hz로 해 주고 16-bit resolution을 사용하는 경우 duty값 3277이 0도, 4915가 90도, 6554가 180도가 된다.

즉 GPIO16에 연결된 서보모터를 CH0를 사용해서 제어한다고 하면 다음과 같이 할 수 있다.

void setup()
{
  ...
  ledcSetup(0, 50, 16);    // PWM CH 0, Freq. 50 Hz, 16-bit resolution
  ledcAttach(16, 0);         // PWM CH 0을 GPIO 16번으로 출력
  ...
}

// deg는 0~180도 까지
void servoWrite(int ch, int deg)
{
  int duty = deg*18.2 + 3277;

  ledcWrite(ch, duty);
}

void loop()
{
  ...
  servoWrite(0, 90);   // CH 0에 연결된 서보를 90도로
  ...
}