ESP32 の PWM
設定
code:esp32-hal-ledc.c
double ledcSetup(uint8_t chan, double freq, uint8_t bit_num);
table:引数と説明
引数名 説明
chan チャネル(<15)
freq 設定したい周波数
bit_num PWMビット分解能
チャネル
ESP32特有の機能(他のArduino対応ボードにはあまり見られない、という意味)でPWM出力をIOを指定して束ねられる
例えば、LED3つをそれぞれ別のピンに接続し、チャネルにその3本のピンを関連づけると同じPWM値で指定可能
値の範囲
uint8_t
0~15
chan > 15 でreturn 0
内部タイマ設定が2つずつセットになっているため、別々の周波数を設定する場合は8種類まで
具体的には以下のチャネルのペアで周波数が共有される
0,1
2,3
4,5
6,7
8,9
10,11
12,13
14,15
例えばチャネル0と1で別々の周波数を設定しようとしても同じ周波数になってしまう
別々の周波数に設定するなら、例えばチャネル0と2、といった具合で選択する必要がある
esp32-hal-ledc.cにあったコメントから表にしてみた
table:esp32-hal-ledc.c
LEDC Chan Group Channel Timer Mapping
0 0 0 0
1 0 1 0
2 0 2 1
3 0 3 1
4 0 4 2
5 0 5 2
6 0 6 3
7 0 7 3
8 1 0 0
9 1 1 0
10 1 2 1
11 1 3 1
12 1 4 2
13 1 5 2
14 1 6 3
15 1 7 3
チャネルは内部的にチャンネル(あえて区別)があって、グループ0/1にそれぞれチャンネル0~7があって、タイマは2つずつ共有していることがわかる
設定したい周波数/PWMビット分解能
タイマ(カウンタ)の分周によってPWMを生成している
分周値は以下の関数によって計算されてる
code:esp32-hal-ledc.c
static double _ledcSetupTimerFreq(uint8_t chan, double freq, uint8_t bit_num);
引数は同じなので割愛
分周はuint32_t div_num = (clk_freq >> bit_num) / freq;
clk_freqがCPU周波数(32bit値を8bit左シフトしたもの)
ビット分解能右シフトしたものを設定したい周波数で割る
ビット分解能はつまりはカウントアップの最大値である
例えば3bitであれば0~7の8であり、CPU=80MHz(の右8bitシフト20480000000)のカウンタから割れば2560000000となる
そしてこれを10kHzのPWMで出したいとすれば、div_num = 256000という値が求まる
ただし、なにやら特殊な場合があるようで...
div_num > LEDC_DIV_NUM_HSTIMER0_V (=0x3FFFF=262143)の場合
CPU周波数を80で割った上で分周値を再計算
それでもまだdiv_num > LEDC_DIV_NUM_HSTIMER0_V (=0x3FFFF)の場合
div_num = LEDC_DIV_NUM_HSTIMER0_V (=0x3FFFF);
lowest clock possible : できる限り低い周波数、とのこと
div_num<256の場合
div_num = 256;
highest clock possible : できる限り高い周波数、とのこと
以上で求めたdiv_numが以下の関数によってタイマに設定される
code:esp32-hal-ledc.c
static void _ledcSetupTimer(uint8_t chan, uint32_t div_num, uint8_t bit_num, bool apb_clk);
これ以上は掘り下げないこととする
結果的に求まったPWM周波数がreturn (clk_freq >> bit_num) / (double)div_num;される
CPUクロック80MHzの時に3bit,10kHzのPWMなら、周波数は $ \frac{\frac{20480000000}{8}}{256000}=10000 \mathrm{Hz}=10\mathrm{kHz}となる
期待通りに求まっていることがわかる
最大のPWM周波数は1bit(つまりは50%デューティの矩形波)で256分周の時なので$ \frac{\frac{20480000000}{2}}{256}=40000000 \mathrm{Hz} = 40\mathrm{MHz}となる
https://gyazo.com/cad24c87bb1fdd7070b6013c0326394e
手持ちの機材ではこれが限界
ピン設定
code:esp32-hal-ledc.c
void ledcAttachPin(uint8_t pin, uint8_t chan);
table:引数と説明
引数 説明
pin ピン番号
chan チャネル
チャネルにピンを割り当てる関数
関数内でOutput設定をしているようなので外部では不要(?)
チャネルについては上記で述べているので割愛、0~15である
ピン番号はデータシートに”Any GPIO Pins”と書いてあるので、そうなんだと思う
全部確かめてはいない
内部的にはピンマトリクスへの設定をしているようだ
PWM出力
code:esp32-hal-ledc.c
void ledcWrite(uint8_t chan, uint32_t duty);
table:引数と説明
引数 説明
chan チャネル
duty デューティー比
もうここまでくれば簡単
duty はビット分解能で指定した値を最大値としたデューティ比である
例えば8bit(=0~255)指定したなら127でだいたい50%である
おまけ
ビット毎の最大周波数(div_num=256)を計算した
table:bit_vs_freq
bit Hz kHz
1 40000000 40000.000
2 20000000 20000.000
3 10000000 10000.000
4 5000000 5000.000
5 2500000 2500.000
6 1250000 1250.000
7 625000 625.000
8 312500 312.500
9 156250 156.250
10 78125 78.125
11 39063 39.063
12 19531 19.531
13 9766 9.766
14 4883 4.883
15 2441 2.441
16 1221 1.221