M5StackGrayでPWM信号発生テスト

2021.5.24 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)プログラムが行うこと(詳細)

  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チャンネル)に与えます。
    これは何回でも与え直すことができます。

2 使用環境

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

PWM波形を生成しますが,生成するデューティー比は離散的で
0/256, 1/256, 10/256, 64/256, 128/256, 192/256, 255/256, 256/256
とします。ボタンAでデューティー比を増加させ,最大になったら元に戻ります。
その他の設定値は次の通りです。
 ・PWM生成に使用するビット数: 8bit
 ・PWM周波数:2000Hz
 ・使用するPWMモジュール(PWMチャンネル):2
 ・PWM出力に使用するGPIO PIN番号:2

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

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

//PWMTest01.ino//

#include <M5Stack.h>

const uint8_t nBits_forPWM = 8; // PWMに使用するビット数 n=1~16[bit]
const double PWM_Frequency = 2000.0;   // PWM周波数 Maxfreq=80000000.0/2^n[Hz]

const uint8_t PWM_CH = 2;  // PWMチャンネル
const uint8_t PWM_PIN = 2; // PWM出力に使用するGPIO PIN番号

const int PWM_Values[] = {0, 1, 10, 64, 128, 192, 255, 256}; //デューティ
                         //MaxDuty=2^n  DutyRatio = Duty/MaxDuty
const int nValues = sizeof(PWM_Values)/sizeof(int);

int ValueIndex = 0;

void setup() {
  M5.begin();
  M5.Power.begin();
  M5.Lcd.setTextSize(2);
  M5.Lcd.print("PWM Test");

  //PWM信号発生ピンを出力モードにする
  pinMode(PWM_PIN, OUTPUT); 

  // チャンネルと周波数の分解能を設定
  ledcSetup(PWM_CH, PWM_Frequency, nBits_forPWM);
  // PWM出力ピンとチャンネルの設定
  ledcAttachPin(PWM_PIN, PWM_CH);

  M5.Lcd.setCursor(10, 20);
  M5.Lcd.printf("duty ratio = %d/256  ", PWM_Values[ValueIndex]);
  //PWM信号発生開始
  ledcWrite(PWM_CH, PWM_Values[ValueIndex]);
}

void loop() {
  M5.update();

  if (M5.BtnA.wasPressed()) {
    ValueIndex++;
    if (nValues<=ValueIndex) ValueIndex = 0;
    M5.Speaker.tone(880, 200); //Peett
    M5.Lcd.setCursor(10, 20);
    M5.Lcd.printf("duty ratio = %d/256  ", PWM_Values[ValueIndex]);
    ledcWrite(PWM_CH, PWM_Values[ValueIndex]);
  }
}

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 1ms/div

設定どおり,PWM周波数2000Hz
PWM周期0.5msec,500μsec
dutyRatio50%で動作している

PWM発生に使用するビット数を8にしたので,
PWM設定値は0から256までとなっている
(0から255でないのが親切な設定)

2) PWM_Frequency = 2000.0Hz, PWM_Value = 1, PWM ratio=1/256

1μs/div
設定どおり,PWM周波数2000Hz
PWM周期0.5msec,500μsec
dutyRatio1/256なので
パルス幅は500μsec/256=1.953125μsecのはず
実測値は1.94μsec(オシロの自動計測)

3) PWM_Frequency = 2000.0Hz, PWM_Value = 0, PWM ratio=0/256

すべてL信号

4) PWM_Frequency = 2000.0Hz, PWM_Value = 1, PWM ratio=1/256

1/256幅の短いパルスが見える

5) PWM_Frequency = 2000.0Hz, PWM_Value = 10, PWM ratio=10/256

10/256幅の短いパルスが見える(見やすい)

6) PWM_Frequency = 2000.0Hz, PWM_Value = 64, PWM ratio=64/256=1/4

25%幅のパルスが見える

7) PWM_Frequency = 2000.0Hz, PWM_Value = 128, PWM ratio=128/256=1/2

50%幅のパルスが見える

8) PWM_Frequency = 2000.0Hz, PWM_Value = 192, PWM ratio=192/256=3/4

75%幅のパルスが見える

9) PWM_Frequency = 2000.0Hz, PWM_Value = 255, PWM ratio=255/256

255/256幅のパルスが見える
(1/256だけ短くお休み)

10) PWM_Frequency = 2000.0Hz, PWM_Value = 256, PWM ratio=256/256

全期間にわたってH信号が見える
通常デジタル機器は8bitを扱うなら0から255 (28-1)までしか対応できないのに, 親切な設計となっています

7 PWM信号で負荷を駆動

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

LEDをチカチカ
まずは,LEDを点灯させましょう。
ほとんどのLEDは,順方向電圧低下が2Vくらいで,1~10mAほどの順方向電流を流せば点灯します。
(詳細は各LEDのデータシートを確認してください。)
M5StackのGPIO 2からは,3.3Vが出てしまうので,保護抵抗が必要となります。
10mAで余計な電圧3.3V-2V=1.3Vを消費するためにはR = V/I = 1.3V / 0.01A = 130 Ω の抵抗を挟めば良いことになります。
実際には安全を見て入手しやすい220Ω程度の抵抗でよいでしょう。
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チャンネル)割り当てには注意が必要であることがわかりました。
意図したデューティー比のPWM波形を生成することができました。
信号観察結果も予想通りでした。