Arduino – ESP32 の PWM ( LEDC )で 40MHzまでの安定した高周波パルスを思い通りに出せたぞ

esp32_ledc00 ESP32 ( ESP-WROOM-32 )

こんばんは。
元号が令和に決まりましたね。
今年度一発目の記事投稿でーす。

今回は、かなり今更ではありますが、Arduino core for the ESP32 の PWM 制御の LEDC ライブラリ関数を使って、ESP32 や M5Stack の GPIO から、安定した高周波の矩形波パルスを、自分の思い通りの周波数やデューティ比( Duty Cycle )で出力することが、やっとできるようになりました。

スポンサーリンク

そもそも、今まで PWM ( Pulse Width Modulation ) の仕組みをイマイチ理解できておらず、Arduino – ESP32 の LEDC のサンプルスケッチの数値が意味不明で、ずーっと放置していました。
今回、ようやく理解することができるようになって、自分の意図した高周波パルスを ESP32 の GPIO から出力できるようになったのです。
しかも、
瞬断無しの安定したパルスやクロック信号を出力できるようになりました!!!

私の実験では、40MHz までは出力できると見受けられました。
ただ、手持ちのオシロスコープのサンプリング周波数が100MHzのため、正確な 40MHz波形を測定できませんが、HIGHレベルとLOWレベルは確実に切り替わっているものと推測できました。
さすがにメガヘルツ級になると、ESP32のスイッチングノイズの影響も有り、矩形波とは程遠い波形になりますが、クロック信号として認識できる程度は発信可能なようです。

ということで、これから LEDC ライブラリの使い方を私なりの解釈で説明してみたいと思います。

因みに、私はアマチュア素人ですので、誤りがあるかも知れません。
もし、お気づきの点がありましたら、コメント投稿等でご連絡いただけると助かります。

    【目次】

  1. 今まで Arduino – ESP32 の LEDC ライブラリの使い方が良く分からなかった
  2. ESP32 の GPIO からの PWM 出力について個人的解釈
  3. 今回の実験で使った物
  4. Arduino – ESP32 のインストールを予め済ませておく
  5. LEDC ライブラリ関数の分解能 16bit で最大 1.22kHz までPWM 出力するスケッチ
  6. LEDC ライブラリ関数の分解能 8bit で最大 312.5kHz までPWM 出力するスケッチ
  7. LEDC ライブラリ関数の分解能 1bit で最大 40MHz までPWM 出力するスケッチ
  8. まとめ

今まで Arduino – ESP32 の LEDC ライブラリの使い方が良く分からなかった

Arduino core for the ESP32 ( 以下 Arduino – ESP32 )には、かなり以前から LEDC ライブラリが存在していて、私自身も過去に何度も関数を試しました。
しかし、私自身のマイコンレジスタ関連の知識が乏しく、LEDC ライブラリ関数で 10kHz 以上の高周波クロックを生成することができませんでした。
Arduino – ESP32 のサンプルスケッチ(サンプルプログラム)で、LEDC Software Fade というものがありますが、その使い方も理解できずにいました。
みなさん使いこなせていたんでしょうか???

以前、LEDC 関数をあまりに使いこなせなかったので、こちらの記事では、digitalWrite関数でGPIOのHIGH,LOWを切り替えて40kHzの矩形波パルスを生成する荒業をやっていました。
実は、クロックパルス波形が微妙に揺れていて安定せずに、電波時計がイマイチ合わせ辛いものでした。
この揺れはいわゆるジッタと呼ばれるものだと思われます。

また、こちらの記事ではプログラミングでCPUクロックを抽出してGPIOから40kHz矩形波を生成していました。
以前より格段に安定しましたが、やはり、どうしても定期的に波形が崩れるために、電波時計に同期しない時もありました。
おそらく、プログラミングによって波形出力タイミングがズレているのだと思われました。

しかし、今回、LEDCの使い方を理解して、ガッツリ安定したクロックを出せたので、こちらの記事の電波時計合わせも精度がグーンと上がり、全ての時計が最短でガッツリ合うようになりました。
クロックの安定はとっても重要なんだなぁと改めて実感した次第です。
マイコン熟練者ならばあまりにも初歩的過ぎて取るに足らない事かもしれませんが、私自身にとっては大きな発見でした。
これ、ネットで調べても、なかなか情報が無いんですよねぇ・・・。

実はこの LEDC ライブラリは、前回の記事で使用した M5Camera のライブラリに、20MHz のクロック生成として使われていました。
それは私自身、M5Camera の動作を解明しようとしていた時に、ついでに得られた副産物的な知見でした。

ということで、次では LEDCライブラリの自己流の使い方を簡単に説明したいと思います。

ESP32 の GPIO からの PWM 出力について個人的解釈

以下、素人の私なりに ESP32 のデータシートと、PWM 制御のネット情報から個人的に解釈してみました。
想像で書いているところもあるので、誤っていたらコメント投稿等でご連絡いただけると助かります。
おそらく、実際に Arduino – ESP32 で実測してみたので、たぶん合っているはず!?

ESP32 のデータシートには、16チャンネル分の PWM ( Pulse Width Modulation ) 制御ができると書いてあります。
その16チャンネル分は、任意のGPIOに割り当てられるようです。

その PWM 制御は、80MHz のクロックタイマを使うそうです。
そして、最大分解能は16bitとのことです。
以下、ESP32データシートからの Google 翻訳直訳です。

【ESP32 datasheet ver 2.9 4.1.16 LED PWM 直訳】

LED PWMコントローラは、設定可能な周期と義務で16の独立したチャンネルのデジタル波形を生成することができます。
16チャンネルのデジタル波形は、80 MHzのAPBクロックで動作します。
これらのチャンネルのうち8チャンネルには、8 MHzのオシレータクロックを使用するオプションがあります。
各チャンネルは、設定可能なカウント範囲を持つ20ビットタイマーを選択できます。
そのデューティ精度は1 msの期間内で最大16ビットまで可能です。
ソフトウェアですぐにデューティ比を変更することができます。
さらに、各チャネルは自動的に段階的なデューティ比をサポートし、LED RGBカラーグラデーションジェネレータに便利です。

これを読むと、80MHzでカウントアップするタイマが ESP32 マイコン内部にあって、その分解能の最大値が20bitなのか16bitなのか良く分からない文章ですが、最大20bitなんて使う用途が無いような気がするので、16bitと思っておけばよいのではないかと思われます。
16bit の場合、0x0000 から  0xFFFF ( = 65535) でゼロに戻るカウンタでPWMパルスを作るということのようです。
ネット上の PWM の図でよく見かけるものですが、備忘録も兼ねて自分の解釈で ESP32 用の以下の図を作ってみました。
デューティ比 50% の場合です。

16bit でカウントアップするタイマとは、図のように 0~65535 ( 0xFFFF ) までカウントアップして、上限に達したらゼロにリセットされるようです。
それで、デューティ比50%の PWMパルスを出力したければ、半分の 32768 を「しきい値」として、それ以上になったら GPIO を HIGHレベルにするという制御にします。

ESP32 は 80MHz でカウントが1つアップするので、1カウントアップする時間は 12.5ns です。
そして、16bit の分解能なので、0xFFFF = 65535 までカウントアップし、それ以上はゼロに戻ります。
よって、16bit の PWM パルス波形の1周期(1波長分の経過時間)は、
12.5ns × 65536 = 819200ns = 819.2us
ということになります。

つまり、
80MHz タイマで 16bit にすると必然的に1周期(1波長分の経過時間)の最大値が 819.2us に決まるのです。
そして、
1周期(1波長分の経過時間)は 12.5ns の2倍の 25ns 単位でしか変えられないのです

819.2us ということは、おおよそ 1ms です。
つまり、ESP32 のデータシートに書いてあった以下の一文、
「デューティ精度は1 msの期間内で最大16ビットまで可能」
という説明の意味と概ね一致しますね。

そして、1周期 819us を周波数にすると、1220.70 = 約 1.22kHz です。

つまりは、
ESP32 で GPIO から16bitの分解能でPWM信号を出力する場合の最大周波数は 1.22kHz
ということになります。

そして、「しきい値」を変えれば、自分の好きなデューティ比にすることができます。
1.22kHz 以下の場合、分解能が16bitなので、かなり細かくデューティ比を設定できますね。
50% ならば65536 の半分の 32768 にすれば良いわけです。

しかし、1.22kHz ではあまりにも速度が遅すぎると思いませんか。
もっと高速にしたい場合がありますよね。
その場合、どうすれば良いのでしょうか?

カウンタの周波数設定が80MHzとした場合、分解能を 8bit や 4bit 、2bit 、1bit に下げれば良いわけです。

例えば 8bitにすると、以下の図のようになります。

このように、8bit にすると 255 でカウンタが上限になってゼロに戻ります。
1周期(1波長分の経過時間)は、
12.5ns × 256 = 3200ns = 3.2us
となり、周波数は 312.5 kHz となります。
16bitよりも約300倍速くなりました。
ただし、分解能が低くなるので、細かく周波数やデューティ比を決定できなくなるので、そのことを把握してプログラミングする必要があります。

では次に、分解能を最小の 1bitとした場合どうなるのでしょうか?
以下の図のようになります。

これから分かるように、80MHz 設定カウンタで 1bit にすると、PWM波形は 40MHz になります。
これが 周波数の上限ということになるわけです。

よって、
ESP32 の GPIO から出力できる PWM波形の最大周波数は、1bitモードで 40MHz
ということになります。
ただしこの場合、デューティ比は50%限定です。

ESP32 の PWM は、レジスタ制御で GPIO から出力できるようです。
そして、Arduino core for the ESP32 では、LEDC ライブラリ内でレジスタを直接制御するプログラムが既に組まれており、最大40MHzの安定したパルスを出力させることができるようです。
いわゆる、レジスタ直叩きライブラリなので、自身で digitalWrite 関数を使って力業でプログラミングして GPIO の HIGH-LOW を切り替えるよりも、遙かに安定した正確なクロックパルスを出力できます。
オシロスコープで波形を眺めていても、安定感の違いが良く分かります。
過去に、こちらこちらの記事で 40kHz のクロックを GPIO から力業で出力して電波時計を合わせていましたが、電波時計の種類によってうまく合わないものがありました。
しかし、今回、LEDC ライブラリを使ったら最短で一発で全ての手持ちの電波時計がカッツリ合ってくれました。
あまりにスバラシイ!!!
もっと早く理解できていれば、今までこんなに苦労しなかったのに・・・と思いました。

では、以上を踏まえて、Arduino core for the ESP32 のインストールが済んでいる方は、
「スケッチ例」→「ESP32」→「AnalogOut」→「 LEDCSoftwareFade 」
を開いてみて下さい。
これはArduino – ESP32 の LEDC サンプルスケッチ(サンプルプログラム)です。
先の解説で LEDC ライブラリの構造が理解できれば、このスケッチも何となく理解できてくると思いますが、下図の波線の 8191 という数値が理解し難いと思います。

それには分解能が 13bit になっています。
13bit の最大値は

0001111111111111 = 0x1FFF = 8191

となります。
最大の1周期(1波長分の経過時間)は
12.5ns × 8191 = 102,387.5ns
となり、約9766 Hz が最大周波数となります。

私はこの数値を使われている意味が長い間よく分からなかったのですが、今回、ようやく理解できるようになりました!!!
そして、先に述べたように、最初にビット数を設定してから、1周期(1波長分の経過時間)に換算して最大周波数を決定すれば、自分の思い通りの PWM 波形が出力できるようになると思います。

ちなみに、これは個人的解釈です。
私は素人なので、間違えていたらコメント投稿等でご連絡いただけると助かります。

では、次以降で実際に Arduino – ESP32 の LEDC ライブラリを使って GPIO から PWM 信号を出力し、オシロスコープで測定してみたいと思います。

コメント

  1. 匿名 より:

    波長って表現は間違ってませんか?

    • mgo-tec mgo-tec より:

      記事をご覧いただき、そして、コメント投稿していただき、ありがとうございます。

      早速、記事を修正しました。

      確かに波長という表現は誤りでした。
      「1周期」に修正しました。
      この当時は自分の頭の中では、1波長分の時間経過というのはあって、周期という言葉が浮かばず、そのまま波長と書いていました。
      何しろ独学なもので、電波時計合わせ回路作成の過程で、自分が分かり易いように記述していました。
      よくよく読み返してみると、明らかに誤りでした。
      大変失礼しました。
      ご指摘ありがとうございました。

      まだ誤りがありましたらコメントお願いいたします。
      m(_ _)m

  2. nyanta より:

    「ESP32のPWM出力何本あるのかな?」という検索をして辿り着きました。
    そして周波数の設定のことまで詳しく書かれていて、
    現時点で知りたい事が分かりやすく書かれていて有難いです。
    インバータの実験ができそうです。
    ありがとうございます。

  3. mick24 より:

    簡便な素人工作で電波時計を合わせたいと思ってたどり着きました。
    いざ真似をさせていただこうと思ってESP32のDACのドキュメントを見ていたら
    Cosine Wave Outputというものに気づきました。
    https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/dac.html
    https://github.com/krzychb/dac-cosine
    40KHzの生成に使えそうに思いました。
    (やってみてから書けよ、という話ですが。。。)

    • mgo-tec mgo-tec より:

      mick24さん

      記事をご覧いただき、ありがとうございます。

      こんなAPIできたんですね!!!
      最近できたのかな?
      40kHzでキレイなサイン波形が出力できるといいですね。
      自分は多忙で今は全く試せませんが…。
      有益な情報、ありがとうございま~す!!!
      m(_ _)m

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