M5StackGray デューティー比固定周波数可変PWM

2021.6.4 Coskx Lab  

1 はじめに

(1)PWM信号

PWM(Pulse Width Modulation)信号というのは,短いパルス信号を一定周期で発し,モータなどのONOFF制御を高速に行うためのものです。
しかもONの時間とOFFの時間の比率を自由に変更できるようになっています。
参考 PWM波形の例 (https://www.mbtechworks.com/projects/raspberry-pi-pwm.html)


パルス周期はPWM周期(PWM period)とも呼ばれ,その逆数はPWM周波数(PWM frequency)と呼ばれます。
また「パルス幅/パルス周期」の値はデューティー比(duty ratio, duty cycle)と呼ばれます。

(2)M5StackがPWM信号を発生する仕組み

PWM信号はM5Stackの指定したピンに対してプログラムがHとLを交互に出力する作業をしているわけではありません。
M5StackではEPS32というマイコンが内部で動いています。EPS32にはPWM発生回路があります。
プログラムはこのPWM発生回路(PWMモジュール)に対してPWM周期やパルス幅を与えて,PWM波形を発生します。
PWMモジュールがPWM波形を発生しただけではまだ外部にPWM信号は見えません。
EPS32は多くのGPIOピン(GPIOは,General-purpose input/output,汎用入出力)を持っていて,M5Stackではその一部分が,外部から見えるようになっています。
GPIOピンは入力に使われるのか出力に使われるのかさえ決まっていない多用途ピンです。
選定したGPIOピンを出力用途に設定し,PWMモジュールが生成しているPWM信号をそのピンから出力するように設定します。
こうして初めてあるピンからPWM信号が出力されるようになります。

(3)デューティー比固定周波数可変PWM

PWMを使用した制御は調光やモータ駆動などに使われますが,その場合周波数を固定してデューティー比を変化させます。
しかし,振動アクチュエータなどの場合は,デューティー比を50%に固定し,周波数を可変にする使い方が考えられます。
EPS32のPWMモジュールは周波数固定・デューティー比可変に都合よくできています。
少し工夫して,デューティー比固定・周波数可変の動作をプログラムすることができます。

(4)プログラムが行うこと(詳細)

  1. ある1つのGPIOピンを指定し,出力用途だと設定します。
    動作テストでは,M5StackGrayのGPIO 2のピンをPWM出力として使ってみます。それ以外のピンについてもプログラムを変更しながら試します。
  2. M5Stack(EPS32)にはPWMモジュール(PWMチャンネル)は0から15までの16個ありますが,その中の1つを選びます。 ここでは,PWMチャンネル2を使います。 (当初PWMチャンネル1を使用してみましたが,意味不明な動作をしてしまいました。すでになにか別の用途で使われているようです。)
    (PWMモジュール(PWMチャンネル)は0から15までの16個ありますが,その中でどこが使えるか,あとで確かめます。)
  3. PWM周波数とPWMモジュールが内部で用いるカウンタのビット数を設定します。
    カウンタのビット数を多くするとデューティー比を細かく指定することができるようになりますが,高いPWM周波数を使うことができなくなります。
  4. 指定した出力ピンからPWM信号を出すように結びつけます。
  5. 希望するデューティー比を先に指定したPWMモジュール(PWMチャンネル)に与え,PWM信号を発生させます。
    PWMモジュールを再設定した場合は,この作業を行いPWMモジュールを再起動させる必要があります。

2 使用環境

3 PWMの動作テスト用プログラム

デューティー比を50%に固定し,周波数を可変にする動作を行います。このプログラムではPWM周波数を
2000,4000,6000,8000,10000,12000,14000 [Hz]
とします。選択肢として加えれば,最大312500Hz(312.5kHz)まで設定できます。
実行時にボタンAを押すことにより,周波数が順番に切り替わります。
その他の設定値は次の通りです。
 ・PWM生成に使用するビット数: 8bit
 ・PWM周波数候補:2000Hzから14000Hz
 ・使用するPWMモジュール(PWMチャンネル):2
 ・PWM出力に使用するGPIO PIN番号:2

PWMに関連する関数名はledc○○○○○になっています。
最初に開発されたときに,LEDの明るさ制御に使われたようで,LED_Controlの名残と思われます。

ledcSetup()は,実行されるたびにPWMモジュールが初期化されてしまいます。 初期化してしまった場合には,ledcWrite()によって再起動する必要があります。
そのため,loop内では,PWM周波数を変更したときにだけledcSetup()およびledcWrite()を実行するようにプログラムします。

  PWMの動作テスト用プログラム本体

//PWMFScan.ino//

//モータ駆動に使用されるPWM信号はPWM周期一定(PWM周波数一定)でデューティー比を変化させるものでした。
//ここではデューティー比50%に保ったまま,PWM周波数を変化させるPWM信号を発生させます。
#include <M5Stack.h>

const uint8_t nBits_forPWM = 8; // PWMに使用するビット数 n=1~16[bit]
                            //PWM周波数の最大値 Maxfreq=80000000.0/2^n[Hz]=312500[Hz]
const uint8_t PWM_CH = 2;   // PWMチャンネル
const uint8_t PWM_PIN = 2;  // PWM出力に使用するGPIO PIN番号
const int PWM_Values = 128; //デューティ デューティー比50%固定
                            //MaxDuty=2^n  DutyRatio = Duty/MaxDuty
const double PWM_Frequencies[] = {2000.0, 4000.0, 6000.0, 8000.0, 10000.0, 12000.0, 14000.0};
                            // PWM周波数 Maxfreq=80000000.0/2^n[Hz]
const int nPWM_Frequencies = sizeof(PWM_Frequencies)/sizeof(double);

int freqIndex = 0;

void setup() {
  M5.begin();
  M5.Power.begin();
  M5.Lcd.setTextSize(2);
  M5.Lcd.print("PWM Test");
  M5.Lcd.setCursor(10, 20);
  M5.Lcd.printf("freqIndex = %d\n", freqIndex);
  M5.Lcd.printf("PWM frequency = %.1f  \n", PWM_Frequencies[freqIndex]);

  pinMode(PWM_PIN, OUTPUT); 

  // チャンネルと周波数の分解能を設定
  ledcSetup(PWM_CH, PWM_Frequencies[freqIndex], nBits_forPWM);
  // PWM出力ピンとチャンネルの設定
  ledcAttachPin(PWM_PIN, PWM_CH);
  // デューティーの設定と出力開始
  ledcWrite(PWM_CH, PWM_Values);
}

void loop() {
  static int ValueIndex = 0;
  M5.update();

  if (M5.BtnA.wasPressed()) {
    freqIndex++;
    if (nPWM_Frequencies<=freqIndex) freqIndex = 0;
    M5.Speaker.tone(880, 200); //Peett
    M5.Lcd.setCursor(10, 20);
    M5.Lcd.printf("freqIndex = %d\n", freqIndex);
    M5.Lcd.printf("PWM frequency = %.1f  \n", PWM_Frequencies[freqIndex]);
    // チャンネルと周波数を更新
    ledcSetup(PWM_CH, PWM_Frequencies[freqIndex], nBits_forPWM);
    // 出力再開
    ledcWrite(PWM_CH, PWM_Values);
  }
}

4 配線

PWM信号の出力は「GPIO 2」です。「GPIO 2」はM5Stackの裏側に 2 と書いてあるところです。 G2 と書いてあるところも同じ信号です。
この信号をオシロスコープで観察するために,次のように配線しました。


参考 M5Stackの裏側に書いてあるピンの名称
写真はM5Stackの裏側です。裏側の左側に3, 1, 16, 17, ...と書いてありますが,これがGPIO 3, GPIO 1, GPIO 16, GPIO 17, ...です。 GPIOとはGeneral Purpose Input/Output(汎用入出力)のことで,プログラムによってその用途を変更できるピンです。
一方,裏側の右側には,R0, T0, R2, T2, ...と書いてあります。左側と右側の表記は同じ色のラベルになっていて,対向するピンは内部で電気的につながっています。 すなわち,GPIO 3 は R0 と,GPIO 1 は T0 とそれぞれ電気的につながっています。
裏側上端下端のピンの関係も同様にそれぞれ電気的につながっています。
黒の G は GND,赤の 3V3 は 3.3V 出力を表しています。(5V は 5V 出力,BAT は分解してみたら電池の + に直接つながっていました。電池は 3.8V くらいです。)

プログラムの「PWM_PIN = 2」はこの青ラベルの2のピン(同時にG2のピン)をPWM出力に使用するという意味になります。
注意 ピンのいくつかは,他の用途に割り当てられている場合があるため,自由に選択することはできません。

5 PWMのための設定値の制限と性質

PWM信号の生成は内部のカウンタが用いられています。そのためカウンタを何ビット使うかを設定する必要があります。
カウンタのビット数が決まると,最大PWM周波数・最小PWM周波数が決まり,デューティー指令値(PWM指令値)の範囲が定まります。
デューティー指令値(PWM指令値)を指示すると,デューティー比が決まります。

1)PWMに使用するビット数 n = 1~16 [bit]

2)PWM周波数に設定できる最大周波数,最小周波数は, n に依存します。
  最大PWM周波数 MaxFrequency = 80000000.0 ÷ 2n[Hz]
  最小PWM周波数 MinFrequency = 80000000.0 ÷ 2n+16[Hz]
  例
bit数 nresolution 2nMaxFrequency[Hz]MinFrequency[Hz]
16655361220.700.018626451
12409619531.250.298023224
82563125004.768371582
416500000076.29394531
1240000000610.3515625

3)デューティー指令値(PWM指令値) v の範囲は n に依存します。
  0 ≦ v ≦ MaxValue
  MaxValue = 2n = resolution

4)デューティー比
  dutyratio = v ÷ MaxValue

例 n=12のとき
  設定できる最大PWM周波数19531.25Hz,最小PWM周波数0.298023224Hzなので,例えばPWM周波数を5000Hzに設定できます
  設定できるデューティー指令値(PWM指令値)は0から4096になります。
  デューティー指令値を1024にするとデューティー比は25%になります。
  デューティー指令値を2048にするとデューティー比は50%になります。


6 M5Stack Gray 出力ピン割り当てとPWMチャンネル選定テストの結果

M5Stack Grayでは多くのピンが見えていますが,PWM信号出力として利用できるピンは少ないです。
また,PWMチャンネルの選定も自由ではありません。
問題なのは,どこのピンが何に使われているかの公式情報が少ないことです。
回路図から読み取り,Net情報を集め,自分で試して確認することが大事なように思います。
ここではPWM出力用として検討しましたが,一般的な出力として利用可能なGPIOピンの検討になります。

1)M5Stack Grayで用途が制限されているGPIOピン
  ・回路図から読み取った,すでに使用されているGPIOピン
   GPIO 1 3 ............. USB UART0 (RXD0,TXD0)で使用
   GPIO 4 23 18 19 ...... SD SPIと共有
   GPIO 33 27 23 18 14 .. LCDで使用
   GPIO 21 22 ........... I2C,電源管理,モーションセンサ,磁気センサで使われている
   GPIO 25 .............. Audioで使用
  ・ESP32仕様
   GPIO 34~39は読み取り専用

2)M5Stack Grayで使えることを確認したGPIOピン
  GPIO 2, GPIO 5 他とは共用していないので自由に使える
  GPIO 16, GPIO 17 UART2 (TXD2・RXD2)を使わなければ OK
  GPIO 26 DAを使わなければ OK
  GPIO 19 SDを使わなければ OK
  GPIO 35, GPIO 36 ADを使わなければ,入力のみOK

3)M5Stack Grayで使ってはいけない PWMチャンネル
  0 音源として使われている (動作させるとビープ音がなる)
  1 用途は不明だが,干渉され周期もデューティ比も指定とは異なる動作をする
  6 LCDの輝度調整に使われている (動作させるとLCDの文字の明るさが変化する)
    使用bit数が12以上のとき,この現象が現れる
  7 LCDの輝度調整に使われている (動作させるとLCDの文字の明るさが変化する)

4)M5Stack Grayで使用確認できたPWMチャンネル
  2,3,4,5,8,9,10,11,12,13,14,15

6 オシロスコープ測定結果

思い通りの波形が得られています。

1) PWM_Frequency = 2000.0Hz, PWM_Value = 128, PWM ratio=50%

2V/div 200μs/div

2) PWM_Frequency = 4000.0Hz, PWM_Value = 128, PWM ratio=50%

2V/div 200μs/div

3) PWM_Frequency = 6000.0Hz, PWM_Value = 128, PWM ratio=50%

2V/div 200μs/div

7 PWM信号で負荷を駆動

PWM信号をオシロスコープで観察すると,信号Hが3.3V(≒M5Stackの電源電圧),信号Lが0Vでした。
3.3Vもあれば模型用モータを回すことが出来そうな気がしますが。それはやらないでください。

LEDをチカチカ
まずは,LEDを点灯させましょう。
LEDは,順方向電圧低下が約2Vくらいで,10mAほどの順方向電流を流せば点灯します。
(詳細は各LEDのデータシートを確認してください。)
M5StackのGPIO 2からは,3.3Vが出てしまうので,保護抵抗が必要となります。
10mAで余計な電圧3.3V-2V=1.3Vを消費するためにはR = V/I = 1.3V / 0.01A = 130 Ω の抵抗を挟めば良いことになります。
実際には入手しやすい220Ω程度の抵抗でよいでしょう。
(電流値は1mA以上あればなんとかLEDが点灯します。)
PWM周波数が数Hz以下なら人間の目にも点滅が見えますが,50Hz以上では点滅は見えません。
デューティー比を変えていくと明るさが変化したように見えます。


モータを回す?
模型用130モータは1.5Vで回すのが推奨ですが,3Vでも良いことになっています。
電圧だけ見ると,3.3Vが出力されるM5StackのGPIO 2をつなげば,回せそうですが, M5Stackに使われているCPUのEPS32のデータシートを見ると,出力ピンの電流はIOHはMAX40mAです。
ところが,モータは使い方にもよりますが1A程度流れる場合が多いです。起動時には数A流れます。
そのため,M5Stackのピンの電流容量を超えてしまって,M5Stackを故障させる原因になります。
M5StackのGPIO 2はモータを駆動する電源としては使えません。これは論理信号です。


モータのように大きな電流を必要とするものを駆動する場合は,M5StackのGPIO 2の論理信号に合わせて大きな電流をONOFFする必要があります。
「N Channel パワーMOSFET」は,「Source-Gate間に4V以上を与えるとDrain→Source間の大きな電流が流れ, Source-Gate間が0VになるとDrain→Source間に電流が流れなくなる」というスイッチのような働きをする素子です。
MOSFET単体は単純なスイッチ動作ですので,モータは片方にしか回すことはできません。
5V論理出力ができるCPUであれば,「N Channel パワーMOSFET」をうまく制御でき,モータを思い通りに制御できます。
ところが,M5Stackは3.3V系なので,3.3Vまでの論理信号しか作れません。
そのため,通常の「N Channel パワーMOSFET」を使うためには,レベルシフト回路で3.3Vの信号を5Vにしなければうまく動作しません。
しかし,最近は,3.3V論理信号でも駆動可能な「N Channel MOSFET」があり,これを使うと駆動が可能です。
例えば 2SK2412,2SK3134,EKI04036,2SK4019 などです。データシートで確認してください。

8 まとめ

ピン割り当て,PWMモジュール(PWMチャンネル)割り当てには注意が必要であることがわかりました。
意図したデューティー比50%でPWM周波数を切り替えることができました。
信号観察結果も予想通りでした。