Youtubeでも活動中チャンネルはこちら!

ArduinoUNOR4で周波数を調整したPWMを出す方法を解説

how-to-control-pwm-frequency-on-arduino-uno-r4-eyecatch

 今回はArduino UNO R4を使います。題名の通り、Arduino UNO R4からPWMを出すのですが、そのPWMの周波数をいじります。通常、Arduino UNO R3までは、PWMのデューティー比を変えることはできても、周波数を変えることは標準ライブラリでは難しかったです。※Arduinoとしてではなく、Atmelのチップとして使う分には当然変更可能だったので誤解しないように…

 ただ、Arduino UNO R4が登場してからは、状況が少し変わりました。Arduino UNO R4に関しては、標準のライブラリを使うことで、PWMのデューティ比だけでなく、周波数も指定して変更することが可能になったんです。この機能、地味に便利。

 そんなArduino UNO R4から使えるようになった標準ライブラリでPWMの周波数を変更してみたいと思います。

この記事を読むことでわかること

Arduino UNO R4からできるようなったPWM出力の周波数を変更する方法がわかる。

自己紹介

東証一部上場企業でサラリーマンしてます。

主に工場(生産現場)で使用する検査装置のアプリケーション開発してます。

ヒトの作業を自動化して簡略化するアプリケーションを日々開発中。

転職に成功して現在は超大手企業でシステム系の開発をしています。

_/_/_/_/_/Youtubeでも情報発信中!_/_/_/_/_/

本ブログはアフィリエイトを用いた広告を掲載している場合があります。

【まずは測定結果から】周波数を変更してみた結果をご紹介

 まずは周波数を変更したPWMの信号を見ていただいたほうがイメージが湧くと思います。

 こちらが、オシロスコープで測定したPWMの信号です。周波数はFのところです。チャンネルは4つ使用しており、それぞれの波形の色と下の数値の番号の色と一致しています。

 グラフの見方ですが、グラフの上から順に1,2,3,4番と番号が振ってあります。この番号に対応するのが左下の表です。注目してもらいたいのは、表の右半分です。この縦に並んでいる数値が、PWMの周波数を表しています。ご覧の通り、4つの波形とも周波数はバラバラになっていることがわかると思います。

実際につかったコードと配線をご紹介

 それでは、あなたもすぐに実験できるように、配線方法と、Arduinoに書き込んだソースコードについて解説していきましょう。ライブラリも標準で入っているものを使用しますので、そこまで難しくないです。気楽に試してみてください。

配線方法のご紹介

 配線方法は、オシロスコープとつなげるために、観察用のピン4本をジャンパーワイヤーで引き出しているだけです。オシロスコープで電圧測定するときはGNDも一緒に使いますので、忘れずにつまみましょう。

 ただただオシロスコープでつまむだけなので、GNDが摘まみにくい程度で、ブレッドボードすら必要ないです。今回測定するのは、~が付いている、3,5,6,9番ピンです。ソースコード上でもこのピンの番号は指定するので、配線とソースコードは一致させておきましょう。

使用したコード

 補足の章で詳しく解説しますが、PWMの周波数を設定する方法はいくつかあります。周期とパルス幅を周波数と時間で指定する方法が一番イメージがつきやすいかと思いますので、まずはその方法をご紹介します。

 まずはこちらのソースコードをご覧下さい。このソースコードを丸コピするだけでも、3,5,6,9番ピンから、周波数とデューティー比の異なる信号が出てきます。先程の配線方法でオシロスコープでそれぞれのピンをつまんでおけばOKです。

#include "pwm.h"

PwmOut pwm_3(3);
PwmOut pwm_5(5);
PwmOut pwm_6(6);
PwmOut pwm_9(9);

void setup() {
  //bool begin(float freq_hz, float duty_perc); pwm.hより抜粋
  pwm_3.begin((float)100.0,(float)50.0); // 100Hz,duty50%
  pwm_5.begin((float)200.0,(float)50.0); // 200Hz,duty50%
  pwm_6.begin((float)100.0,(float)20.0); // 100Hz,duty20%
  pwm_9.begin((float)100.0,(float)80.0);  // 100Hz,duty80%
}

void loop() {}

 このソースコードで出てくる信号をオシロスコープで確認したものが次の図です。

 ここでのポイントは3つです。それが、

PWMの周波数とデューティー比を設定するソースコードの解説
  • 標準ライブラリのpwm.hを使うと宣言する。
  • PwmOutのクラスを使うために、PWM用の端子の数だけ先に定義しておく。
  • 周波数とduty比はbegin関数を使用する。

標準ライブラリのpwm.hを使うと宣言する。

 まずはこれを宣言しないことには何も始まりません。宣言をしているのは冒頭の行です。

#include "pwm.h"

 #includeとライブラリ名で宣言できます。繰り返しますが、この行がないと、何もできません。忘れずに入れてください。

PwmOutのクラスを使うために、PWM用の端子の数だけ先に定義しておく必要がある。

まず、pwm.hのライブラリを使おうとすると、PwmOutというクラスを使うことになります。ですから、最初にそのクラスを使った変数を作成しておく必要があります。

 とはいってもそんなに難しい話ではありません。ただ変数に名前を付けるだけです。

PwmOut pwm_3(3);
PwmOut pwm_5(5);
PwmOut pwm_6(6);
PwmOut pwm_9(9);

 この場合だと、pwm_3やpwm_6という変数が、PwmOutのクラスを使いますよ。と宣言しています。この宣言が無いと、PwmOutクラス、つまりPWMを自由に設定することができない。ということになります。

 今回はpwm_3や_6と名前をつけていますが、この名前はわかりやすくすれば何でもOKです。ただの名前ですので、こうしないと動かない、ということはないです。

周波数とduty比はbegin関数を使用する。

 続きまして、いよいよ具体的にPWMの周波数とduty比を設定するところです。先ほど宣言したpwm_xに周波数とデューティー比を設定していきます。設定しているのが、この部分です。

  pwm_3.begin((float)100.0,(float)50.0); // 100Hz,duty50%
  pwm_5.begin((float)200.0,(float)50.0); // 200Hz,duty50%
  pwm_6.begin((float)100.0,(float)20.0); // 100Hz,duty20%
  pwm_9.begin((float)100.0,(float)80.0);  // 100Hz,duty80%

 各行にコメントを入れているので何となくわかるかもしれません。PwmOutクラスのbeginという関数を使用して設定することになります。一個目の引数には周波数、二個目の引数にはデューティー比を記述します。注意点としては、引数にはfloatのデータ型を指定する必要があるので、必ず(float)とするか、数字の最後にfをつけてfloatであることを明示してください。これが無いとエラーになります。

 もう一度波形とソースコードを突き合わせて見てみましょう。

 どうでしょうか?こんなに簡単に制御できるなんて意外じゃないですか?

 実はたったこれだけで終わりなんです。大した行数もないですし、お手軽ですよね。ここからは、ちょっとディープな知らなくてもいいけど、知っていると面白い部分になります。

他にもあるPWMの設定項目

 今回は、標準ライブラリのpwm.hを使用しています。こちらのライブラリ、中身を確認すると、もう少しいろいろな設定ができます。もちろんここまでご紹介した内容で周波数の変更は可能ですので、ここからは補足の内容です。

 PWM.hの中身を見てみると、pwmoutという関数以外にも条件を追加するような関数があります。その関数の使い方は、このような形式です。

 bool begin(uint32_t period_usec, uint32_t pulse_usec, bool raw = false, timer_source_div_t sd = TIMER_SOURCE_DIV_1);

 先程の章のbegin関数とは異なるbegin関数です。同じ名前ですが、引数の数が明確に違いますよね。先程使用したbegin関数は、周期とデューティー比のみの引数でしたが、今回のbegin関数はそれに加えて2つ引数があります。

 この追加された引数で説明しておかないとわかりにくいところがあります。具体的にはrawという三個目の引数です。第三引数とこれから呼びますが、このrawをtrueにするかfalseにするかがわかりにくいです。まずはrawをfalseにした場合をご覧下さい。

#include "pwm.h"

PwmOut pwm_3(3);
PwmOut pwm_5(5);
PwmOut pwm_6(6);
PwmOut pwm_9(9);

void setup() {
  //bool begin(uint32_t period_usec, uint32_t pulse_usec, bool raw = false, timer_source_div_t sd = TIMER_SOURCE_DIV_1);
  pwm_3.begin(1000,500,false,TIMER_SOURCE_DIV_1); // PWM周期1000μs,パルス幅500μs,分周比設定不使用,分周比設定
  pwm_5.begin(1000,500,false,TIMER_SOURCE_DIV_4); // 分周比設定不使用なので、↑と同じ波形になる。
  pwm_6.begin(1000,500,false,TIMER_SOURCE_DIV_16); // 分周比設定不使用なので、↑と同じ波形になる。 
  pwm_9.begin(1000,500,false,TIMER_SOURCE_DIV_64);  // 分周比設定不使用なので、↑と同じ波形になる。

}

void loop() {}

 ポイントとしては、第三引数のrawをfalseにするか、trueにするかで第一引数と、第二引数の役割が変わるということです。上記のソースコードの場合は、第四引数のTIMER_SOURCE_DIVがそれぞれ異なりますが、実は波形としては全く同じものになります。

 このことから、「第三引数をfalseにすると、第四引数が無視される。」ということがわかりますね。

 次に第三引数をtrueにしてみましょう。

#include "pwm.h"

PwmOut pwm_3(3);
PwmOut pwm_5(5);
PwmOut pwm_6(6);
PwmOut pwm_9(9);

void setup() {
  //bool begin(uint32_t period_usec, uint32_t pulse_usec, bool raw = false, timer_source_div_t sd = TIMER_SOURCE_DIV_1);
  pwm_3.begin(1000,500,true,TIMER_SOURCE_DIV_1); // 48kHz,パルス幅11μs
  pwm_5.begin(1000,500,true,TIMER_SOURCE_DIV_4); // 12kHz,パルス幅41μs
  pwm_6.begin(1000,500,true,TIMER_SOURCE_DIV_16); // 3kHz,パルス幅0.17ms
  pwm_9.begin(1000,500,true,TIMER_SOURCE_DIV_64);  //750hz,パルス幅0.67ms

}

void loop() {}

 コメントのところにPWM信号の周波数とパルスの出ている幅の時間をメモしています。第一引数と第二引数の数字が同じ1000と500なのに、全然違う波形になります。引数が2つの時では考えられないですよね??

 これは、第三引数をtrueにすることで、分周比の機能を使うか使わないかを指定しているためです。分周比とは、マイコン本体の固有の動作周波数の何分の何の周波数で動作させますか?という機能です。

 具体的な数字のほうがイメージがつきやすいと思います。Arduino UNO R4の本体の動作周波数は48MHzです。この値に対して、分周比1とすると、1/1なので48Mhzの周期を使用する。ということになります。

 値を変更して、1/16とすると、3MHzですね。この値の指定が第四引数のTIMER_SOURCE_DIV_Xでできるということなんですね。もちろん、さきほどからお伝えしているように、この機能を使うためには第三引数のrawはtrueにしておく必要があります。

 では、第三、第四引数の役割はいいとして、第一、第二引数の役割についてもお伝えしておきましょう。引数が2つしかないときは、周波数とデューティー比でしたね。ただ、今回のbegin関数では、第四引数で分周比を設定し、動作周波数はマイコン固有な値なわけですから、PWMの周波数の半分くらいの要素はここですでに決まっています。※これだけでは周波数は決定できません。後述のカウント数がないとダメです。

 実は、第一引数はクロックカウント数、第二引数はパルス幅用のカウントに変わります。クロックカウント数とは、この回数だけタイマーが来たら終わりにする。というときに使われるイメージです。パルス幅用のカウントとは、PWMの正の電圧が出ている区間がどれくらいですか?というのを示します。

 具体例としては、pwm_3.begin(1000,500,true,TIMER_SOURCE_DIV_1); // 48kHz,パルス幅11μs
この場合は、全体として1000回カウントをPWMの1セットとして、500カウントの間だけパルスを出します。

 TIMER_SOURCE_DIV_1を使っているので、動作周波数は48M/1で、48MHz。これで1000回カウントを1セットとする場合、48Mを1000で割ればいいので、PWMの周波数は48kHzとなります。

 パルス幅の計算は、まず、48MHzの周期を計算すると、T=1/fで、約20.8nsとなります。これを500カウントすればいいので、20.8[ns]x500=10.42[μs]となります。オシロスコープで測定した結果とほぼ一致していますね。

 ここからは、第一引数と第二引数の数値を変えてみた結果です。原理とオシロスコープの結果がよく一致しているのがわかると思います。

#include "pwm.h"

PwmOut pwm_3(3);
PwmOut pwm_5(5);
PwmOut pwm_6(6);
PwmOut pwm_9(9);

void setup() {
  //bool begin(uint32_t period_usec, uint32_t pulse_usec, bool raw = false, timer_source_div_t sd = TIMER_SOURCE_DIV_1);
  pwm_3.begin(500,250,true,TIMER_SOURCE_DIV_1); // 96kHz,パルス幅6μs
  pwm_5.begin(500,250,true,TIMER_SOURCE_DIV_4); // 24kHz,パルス幅22μs
  pwm_6.begin(500,250,true,TIMER_SOURCE_DIV_16); // 6kHz,パルス幅84μs
  pwm_9.begin(500,250,true,TIMER_SOURCE_DIV_64);  //1.5kHz,パルス幅0.333ms
}

void loop() {}

今回のまとめ「Arduino UNO R4になってPWMの使い勝手が向上」

 Arduino UNOのR3からR4になって、機能追加や性能UPが図られていますが、今回のような地味に使い勝手の向上する機能追加は、趣味で電子工作する立場としてはとても助かります。

 今回の内容をまとめておきましょう。

今回のまとめ「これであなたもPWMマスター!」
  • Arduino UNO R4では標準ライブラリのpwm.hを使うことで簡単にpwmの周波数変更が可能。
  • PwmOutというクラスを使用する。
  • 簡単な設定方法は、begin(周波数,デューティ比)関数を使うこと。
  • begin(カウント数,正パルス幅,分周比使用有無,分周比)関数もつかえる。※使わなくても大丈夫。

 こんなことしたい、あんなことしたい。という要望が、ver.upによってどんどん実現していきますね。電子工作レベルであれば、もうArdino UNO R4で十分な気がします。これ以上のスペックというと、扱いきれないのでは?とさえ思います。すでに私は使えていない機能もあります…

 ということで今回はArduino UNO R4でPWMの周波数を変更する方法について解説しました。ぜひいろんな周波数にして試してみてくださいね。