ESP32-WROOM-32EでPWM波形を出力する

組み込みエンジニア

こんにちは、ENGかぴです。

ESP32シリーズにおいてPWMは通常のArduinoライブラリでなく専用のライブラリを使用する必要があります。PWMで振動波形を生成しブザーを鳴らす方法やPWMのデューティー比を変更して直流モーターを駆動する方法をまとめています。

ESP32-WROOM-32Eの開発環境はArduino IDEを使用しています。またESP32-WROOM-32E開発ボード(秋月電子)を使用しています。

PWM波形を出力する

PWM波形の説明
PWM波形の説明

PWM波形でキャリア周波数を変更するとブザーの音程を変更することができます。キャリア周波数を固定しデューティー比を変更することで平均電圧を制御することができます。

PWMのチャンネル設定からピンの設定などの初期化の例やキャリア周波数を変更してブザーの音程を変える方法、デューティー比を設定する方法を説明しています。

PWM波形を出力するピンの初期化

ESP32シリーズでは通常のArduinoライブラリとはPWM波形の生成の仕方が異なります。ESP32シリーズ専用に実装された関数を組み合わせてPWM波形を出力します。

#define BUZ_PIN 5
#define MOTOR_PIN 32

void setup() {

  ledcSetup(0,1000,8); //チャンネル0、キャリア周波数1kHz、8ビットレンジ
  ledcAttachPin(BUZ_PIN,0); //PWMピンにチャンネル0を指定
  ledcSetup(1,1000,16); //チャンネル1、キャリア周波数1kHz、16ビットレンジ
  ledcAttachPin(MOTOR_PIN,1); //PWMピンにチャンネル1を指定
}

PWMは特にライブラリをインクルードする必要はありません。

ledcSetup()ではPWMを管理するチャンネルについて設定します。第1引数にチャンネル番号として0~15を指定します。

第2引数にはPWMのキャリア周波数を設定します。キャリア周波数はPWM波形のパルスの周波数となり周波数が高いほど精細なPWM波形となります。

第3引数にPWMのビット数を指定します。8ビットを指定すると0~255の範囲でデューティー比を設定でき、16ビットを指定すると0~65535の範囲でデューティー比を設定することができます。

ledAttachPin()はPWMに使用するピンの設定を行います。第1引数にPWM出力するGPIOピンの番号を指定します。第2引数にPWM設定を行ったチャンネル番号を指定します。

キャリア周波数を変更してブザーを鳴らす

PWMにおいてキャリア周波数が一定であるとブザーは鳴りますが音程に変化を与えることができません。音程を変更するためにはキャリア周波数を変更する必要があります。ESP32シリーズでは音名が定数として実装されています。

ledWriteTone(0, 500); //チャンネル0のキャリア周波数を500Hzにする
//使用例
ledWriteNote(0,NOTE_F,3); //チャンネル0でオクターブ3でNOTE_Fの音を出す
ledWriteTone(0,0); //音を停止する場合

ledWriteTone()で第1引数にPWM設定したチャンネルを指定します。第2引数にキャリア周波数をしていします。キャリア周波数が変更できるため音程を変えることができます。

LedWriteNote()は定数で定義された音名とオクターブを指定して音程を作ることができます。第1引数にPWM設定したチャンネルを指定します。第2引数に音名を指定します。音名の変数はnote_tとしてenumで順番に定義されています。

typedef enum {
  NOTE_C,  //ド
  NOTE_Cs, //ド#
  NOTE_D,  //レ
  NOTE_Eb, //レ#
  NOTE_E,  //ミ
  NOTE_F,  //ファ
  NOTE_Fs, //ファ#
  NOTE_G,  //ソ
  NOTE_Gs, //ソ#
  NOTE_A,  //ラ
  NOTE_Bb, //ラ#
  NOTE_B,  //シ
  NOTE_MAX
} note_t;

第3引数はオクターブを指定します。オクターブは0~8まで指定することができます。数値が大きくなるほど音色が高くなります。

第2引数の音名と第3引数のオクターブの組み合わせでキャリア周波数を生成し音色を作る仕組みになっています。音を停止する場合はledWriteTone()の第2引数の周波数に0を指定します。

デューティー比を設定する

PWMのデューティー比はパルスのHIGHとLOWの比率です。キャリア周波数に対してHIGHの時間が長いほどデューティー比が高くなり、平均電圧が高くなります。

uint16_t duty; //デューティー比を16ビットで管理する
//使用例
ledcWrite(1,duty); //チャンネル1をdutyで指定したデューティー比で出力

ledcWrite()を使用するとledcSetup()で指定したキャリア周波数とレンジに対応したPWM波形を生成することができます。第1引数にPWM出力するチャンネルを指定します。第2引数にデューティー比を指定する値を指定します。

LedcSetup()で指定したビット数に対応してデューティー比を計算します。デューティー比を50%にしたい場合は8ビットの場合は128を指定し、16ビットの場合は32768を指定します。

デューティー比の計算方法の例
8ビット:128/255 ≒50%
16ビット: 32768/65535≒50%

動作確認

PWM動作確認の回路図
PWM動作確認の回路図

ESP32-WROOM-32Eとブザーやモーターの配線例を示しています。回路図の番号はESP32-WROOM-32Eの左上を1ピンとした時反時計回りにピンを数えた場合の番号としています。ピン番号横の()内の番号はシルク印刷されているピンの名称です。

PWM0はブザーを鳴らすために使用しています。音色とオクターブを変更しながらブザーが鳴るのを確認します。オクターブが低い間はブザーの音がほとんど聞こえずにプツプツとした音が聞こえますが、オクターブが高くなる(キャリア周波数が高くなる)に連れて「ピッ」という音が聞こえるようになります。

使用しているブザーの対応周波数によって音が鳴るか決まります。

PWM1はモーターの駆動に使用しています。デューティー比を少しずつ高くしていき平均電圧が高くなってくるとモーターが回転を始めます。トランジスタのベースを抵抗を実装していますがベース電流を絞り過ぎるとトランジスタで電流を増幅してもモーターが回転しなくなるため調整が必要です。

シリアルモニタの確認
シリアルモニタの確認

シリアルモニタでブザーの音が「ピッ」鳴りだすオクターブ数を確認したところ3付近でした。モーターを回転させるためのデューティー比については8000付近からモーターが回転し始めました。

使用するモーターによってはキャリア周波数やデューティー比によってはコイルが共振してしまいブザーのように音が鳴ることがあります。モーターの電源にコンデンサをつけるとその傾向が高くなるので注意が必要です。

ソースコード全体

以下のソースコードはコンパイルして動作確認をしております。チェックはしておりますがコメントなど細かな部分で間違っている可能性があります。参考としてお使いいただければと思います。

#define TIME_UP 0
#define TIME_OFF -1
#define BASE_CNT 10 //ベースタイマカウント値
#define BUZ_MAX 50
#define BUZ_PIN 5
#define MOTOR_PIN 32
#define OCTAVE_MAX 9
/* 変数宣言 */
uint32_t beforetimCnt = millis();
int16_t timBuz = TIME_OFF;
uint8_t soundMode;
uint8_t octaveMode;
uint16_t duty;

/* プロトタイプ宣言 */
void mainTimer(void);
void SetSound( uint8_t sndmode, uint8_t octa);
void SetDuty(void);

void setup() {

  Serial.begin(115200);
  ledcSetup(0,1000,8); //チャンネル0、キャリア周波数1kHz、8ビットレンジ
  ledcAttachPin(BUZ_PIN,0); //PWMピンにチャンネル0を指定
  ledcSetup(1,1000,16); //チャンネル0、キャリア周波数50kHz、16ビットレンジ
  ledcAttachPin(MOTOR_PIN,1); //PWMピンにチャンネル1を指定
  timBuz = TIME_UP;
}

void loop() {

  mainTimer();

  if( timBuz == TIME_UP ){
    timBuz = BUZ_MAX;
    SetSound( (note_t)soundMode, octaveMode );

    if( ++soundMode >= note_t::NOTE_MAX){
      soundMode = NOTE_C;
      if( ++octaveMode >= OCTAVE_MAX ){
        octaveMode = 0;
      }
    }
    SetDuty();
  }
}
/* タイマ管理 */
void mainTimer(void){

  if ( millis() - beforetimCnt > BASE_CNT ){
    beforetimCnt = millis();
    if( timBuz > TIME_UP){
      --timBuz;
    }
  }
}
/* 音色変更処理*/
void SetSound( note_t sndmode, uint8_t octa){

  ledcWriteNote(0, sndmode, octa);
  Serial.print("SoundMode: ");
  Serial.print(sndmode);
  Serial.print(" ");
  Serial.print("Octave: ");
  Serial.print(octa);
  Serial.print(" ");
}
/* デューティー比を設定 */
void SetDuty(void){

  if( duty >= 0 && duty < 5000){
    ledcWrite(1,0);
  }
  else if( duty >= 60000 ){
    duty = 0;
  }
  else{
    ledcWrite(1,duty);
  }

  duty += 200;
  
  Serial.print("Motor duty: ");
  Serial.print(duty);
  Serial.println(" ");
}

関連リンク

Arduinoのライブラリを使って動作確認を行ったことを下記リンクにまとめています。

Arduinoで学べるマイコンのソフト開発と標準ライブラリの使い方

Seeeduino XIAOで学べるソフト開発と標準ライブラリの使い方

ESP32-WROOM-32Eで学べるソフト開発と標準ライブラリの使い方

テックジム|ゼロからはじめるPython入門講座の申込

最後まで、読んでいただきありがとうございました。

タイトルとURLをコピーしました