PWM (ESP-IDF 環境 + C 言語)
圧電ブザーを鳴らすプログラムの書き方
音は波であるため, 音を出したい時には電圧の HIGH と LOW を繰り返すことで, プログラム的に下記に示すような特定の周波数を持つ波 (矩形波) を作れば良い. HIGH と LOW の持続時間の和が波の周期に対応する.
矩形波を作るのには ESP32 の LEDC ライブラリを用いると良い. C 言語版 esp-idf の API Reference の LED Control のドキュメントを参照する. Overview に書かれているように, 以下の操作が必要となる.
//タイマーの設定 ledc_timer_config_t ledc_timer = { .duty_resolution = 解像度 .freq_hz = 周波数 .speed_mode = スピードモード (HIGH と LOW がある). }; ledc_timer_config(&ledc_timer); //pwmの設定. ledc_channel_config_t ledc_channel = { .channel = チャンネル番号 .gpio_num = ブザーの接続されたピンの GPIO .speed_mode = スピードモード }; ledc_channel_config(&ledc_channel); //出力 ledc_set_duty( スピードモード, チャンネル. デューティー比 ) ledc_update_duty( スピードモード, チャンネル )
ESP32 は最大で 16 チャンネル分の PWM 制御を行うことができ, 複数のピンを同じチャンネルにまとめて一度に制御することもできる. ESP-IDF 環境では PWM の HIGH スピードモードと LOW スピードモードが選べるが, 使い分けについては良くわかっていない.
また, デューティー比は比率 (%) で与えるのではなく, 下図でいうところの「閾値」として与える.
なお, ESP32 のクロックは 80 MHz = 12.5 μs で動作するため, 分解能をそれよりも小さくすることはできない. 逆に言うと, タイマの解像度によって表現できる最大波長が決まる. 1 bit ならば 1 / (12.5e-6 * 2) = 40 MHz, 8 bit ならば 1 / (12.5e-6 * 256) = 312.5 kHz, 13 bit ならば 1 / (12.5e-6 * 8192) = 9765 Hz, である.
ブザーを鳴らす
LEDC のサンプルの利用
PWM (Pulse Width Modulation) を司る LEDC モジュールを利用する.
$ cp -r esp-idf/examples/peripherals/ledc ./ $ cd ledc
main/ledc_example_main.c の以下の行で LED やブザーの GPIO を指定する.
39 #define LEDC_HS_CH0_GPIO (13) .... 41 #define LEDC_HS_CH1_GPIO (12) .... 52 #define LEDC_LS_CH2_GPIO (14) .... 54 #define LEDC_LS_CH3_GPIO (15)
修正したら, コンパイルとマイコンへの書き込みを行う.
$ make $ make flash monitor ...(略)... 1. LEDC fade up to duty = 4000 2. LEDC fade down to duty = 0 3. LEDC set duty = 4000 without fade 4. LEDC set duty = 0 without fade ...(略)...
make monitor の出力に合わせて LED の明るさとスピーカーからの音量が変化することがわかる. このサンプルでは 4 パターンの変化が見られる.
- 1: 徐々に LED が明るくなる / 音量が大きくなる
- fade : 徐々に変化
- 2: 徐々に LED が消える / 音量がゼロになる.
- 3: 一気に LED が明るくなる / 音量が大きくなる
- 4: 一気に LED が消える / 音量がゼロになる.
このサンプルより, LED の明るさを徐々に変えるには fade オプションをつけるのが良さそうである.
サンプルプログラムの簡略化
サンプルプログラム main/ledc_example_main.c は #ifdef などが多くて読みにくい. main/ledc_example_main.c を簡略化してブザーの音を鳴らすだけのプログラムを作成する. なお, このプログラムでは fade オプションは使っていない.
1 #include <stdio.h> 2 #include "freertos/FreeRTOS.h" 3 #include "freertos/task.h" 4 #include "driver/ledc.h" 5 #include "esp_err.h" 6 7 #define LEDC_GPIO 15 8 #define LEDC_FREQ 5000 9 #define LEDC_DUTY 128 10 11 void app_main(void) 12 { 13 ledc_timer_config_t ledc_timer = { 14 .duty_resolution = LEDC_TIMER_8_BIT, // resolution of PWM duty 15 .freq_hz = LEDC_FREQ, // frequency of PWM signal 16 .speed_mode = LEDC_HIGH_SPEED_MODE // timer mode 17 }; 18 ledc_timer_config(&ledc_timer); 19 20 ledc_channel_config_t ledc_channel = { 21 .channel = LEDC_CHANNEL_0, 22 .duty = 0, 23 .gpio_num = LEDC_GPIO, 24 .speed_mode = LEDC_HIGH_SPEED_MODE 25 }; 26 ledc_channel_config(&ledc_channel); 27 28 while (1) { 29 ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, LEDC_DUTY); 30 ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel); 31 vTaskDelay(3000 / portTICK_PERIOD_MS); 32 33 ledc_set_duty(ledc_channel.speed_mode, ledc_channel.channel, 0); //duty=0 で消音 34 ledc_update_duty(ledc_channel.speed_mode, ledc_channel.channel); 35 vTaskDelay(1000 / portTICK_PERIOD_MS); 36 } 37 }
- 4 行目: ヘッダファイル driver/ledc.h をインクルード.
- 7 行目: 圧電ブザーの接続されているピンの GPIO を設定.
- 8 行目: 周波数の設定
- 9 行目: デューティー比の設定. 8 bit で 50% となるような値を設定.
- 13-18 行目: タイマーの設定. タイマーの解像度, 周波数, スピードモードを与える.
- LEDC の 16 チャンネルのうち, 半分が High Speed Mode, もう半分が Low Speed Mode であり, Speed Mode の指定を行う必要があるらしい (違いがよくわかっていないが....).
- 解像度は 8 bit, スピードモードは HIGH とした.
- 行目: チャンネル 0 の設定. チャンネルの番号, デューティー比の初期値, ピンの接続されている GPIO, スピードモード, を与える.
- チャンネル番号: 0
- 初期デューティー比: 0
- スピードモード: High
- 29-35 行目: デューティー比 50% で矩形波を作成し出力. 音を鳴らす・止めるを繰り返す.
プログラムが作成できたら実行する.
$ make $ make flash
課題
LED, スイッチ, ブザーを用いたプログラムを作成してみよ.
- スイッチを入れたら音が鳴る
- スイッチを入れると, ブザーで「ドレミファソラシド」が鳴る.
- LED の点灯に合わせてブザーから曲が流れる.
- その他