HOME >> 鉄道模型実験室 > Arduino Uno のPWMキャリア周波数を20kHzにする
別室のブログにて、同じテーマで実施方法を報告しました。 しかしその内容は、こうしたら出来ましたという単なる ノウハウ として報告しました。 でも、なぜこうすれば出来たのか? それぞれの設定値はどんな意味があるのか? など、ノウホワイ? の部分について、自分が理解した内容に沿って記録しておくことにしました。
■ Arduino Uno におけるPWM制御の設定方法
いろいろなネット情報をもとに、 Arduino Uno のCPUである ATmega328P の DATASHEET を紐解き、PWM制御に関する部分を眺めました。 そして何とかそお動きを理解することが出来ましたが、まだまだ不十分です。 他のネット情報と合わせて自分なりにまとめたレポートを紹介します。
PWM制御の流れと指定方法 ⇒ ATnega329-PWM.pdf
この内容を要約します。 まず、CPUを動かししているメインのクロック信号ををもとに、タイマーをカウントしていきます。 タイマー、遅延命令、PWMなどの時間に関する機能はこのタイマ/カウンタが中心となって作動していきます。
そして、このタイマーはそのモデルによって、独立した複数のタイマーをもっております。 また、CPUを作動させているクロックはメガオーダーの速いしサイクルで動いていますので、プリスケーラと呼ばれている減速装置を通して作動させています。
この、タイマ/カウンタでは、減速された信号をもとにカウンタを作動させて、カウントアップしていき、オーバーフローするとまたゼロからカウントする事によって、ノコギリ波状の信号を作ることが出来ます。 この周期がPWM制御のためのキャリヤ周波数となります。
このカウントアップしている途中で、ある設定値になると信号を出し、その設定になりましたという信号をもとにパルス波形を生成すると、PWM信号が出来上がります。 この設定値がデューティ比を決める数値であり、カウンタの数値と比較する役割がコンパレータです。 そして、この比較とパルス整形ルートは、一つのカウンタから2チャネル設定されており、それぞれ独立して設定・制御されます。
これらの状態をコントロールするレジスタが、タイマ カウンタ コントロールと言われているレジスタで、ここのビットをそれぞれ設定することによって、これらの機能を設定することができるのです。 例えばプリスケーラの分周比をいくつの設定するのか、ノコギリ波でなくて三角波にしたい、コンパレータの作動内容を変えたい、とか色々なメニューが用意されていますので、これらの説明書を紐解く必要があるのです。
素人が云々するよりも、詳しい内容については専門の方にお任せして、PWMキャリヤ波の周波数を20kHz に設定する方法を検討してみましょう。
■ 20kHz に設定する方法
まず、ノコギリ波か三角波かの選択を決めておきましょう。 上記の資料の中に図で説明してありますが、Fast PWM と Phase Correct PWM 、あるいは、シングルスロープとデュアルスロープ、エッジアラインモードとセンターアラインモードとも呼ばれており、パルスに位相が狂うと問題になる音響関係などに使用される場合に用意されているサービスのようですが、単なるモータ制御である鉄道模型の制御ではその選択は関係ないと思います。 ただ、三角波になりますと説明図からも分かるように、周波数は半分になってしまいます。 Unoの場合も、976Hzと490Hzの2種類あるのはこのためです。 そこで、ノコギリ波、即ち Fast PWM モードを選択しましょう。
次に、ノコギリ波のトップの値を選択する必要があるのです。 8ビットタイマーですとトップ値は 255 です。 16ビットタイマーは 255×255 ですが、この値まで到着する時間は8ビットの255倍、即ち周波数は1/255となって目的とは逆の方向です。 即ち、8ビットのトップ値 255 で充分なのです。 さらに嬉しい機能として、このトップ値を指定する事が出来るのです。 255までカウントアップせずに途中で折り返すのです。 すると、その分だけ折り返し時間が速くなりますから周波数も高くすることが出来るのです。
もうひとつの重要な設定は、プリスケーラの倍率を変更する事です。 Uno 場合は、分周比が 64 に設定されていますので、クロック周波数 16MHz を 1/64 倍してカウンタをカウントアップしていくのですが、この分周比を小さくすると早くカウントアップするので、周波数はアップします。
例えば、分周比32なら1.95kHz(足りない!)、分周比8なら7.81kHz(まだ足りない!)、分周比1なら62.5kHz(早すぎる!)、と言うことで狙いどうりの周波数に設定できません。 この中間の値の選択肢が無いのです。
このため、残る手段はノコギリ波のトップ値を低く設定して、早めに折り返す方法しかありません。 いくつに設定すると良いのでしょうか?
分周比を8に設定すると、255で7.81kHzですから、255×7.8÷20 ≒ 99 となります。 即ち、99まてカウントすると折り返す様にすると 20kHz が出ることになります。
このような小さな数字で良いのでしょうか? コンパレータでは、比較する数値を指定するレジスタ、即ち比較値 OCRnA/B の値と比較しますので、この値と99の比でデューティ比が設定出来るのです。 言い換えると1%の区切りでデューティ比が制御出来るのです。 鉄道模型の速度制御では充分な設定ですね!
ここで、忘れてはいけない大切な事があります。 ノコギリ波のトップ値はAチャネルの大切なレジスタ OCRnA を使用すると言うことです。 このレジスタを使ってしまうとこのチャネルからPWM信号を出力することができません。 ただし、設定によっては上の図のように、パル毎に反転させて、半分の周波数のヂューティ比50%のパルス波を生成させることも出来るのですが、利用価値がありません。 このポートを殺しているのと変わらないのです。
■ レジスタの実際の設定値
各レジスタの設定方法が理解出来ましたので、PWMキャリ周波数の20kHz 化に挑戦してみましょう。
ます、Arduino Uno にはタイマーが3個ありますので、それぞれのタイマ毎に設定する必要があります。 その設定内容は、
とし、各タイマーの設定方法に合わせて、タイマ カウンタ コントロール レジスタ TCCRnA/B を設定する。 その様子を下の一覧表に示す。
タイマ0 | TCCR0A | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|
COM0A1 | COM0A0 | COM0B1 | COM0B0 | * | * | WGM01 | WGM00 | ||
TCCR0B | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
FOC0A | FOC0B | * | * | WGM02 | CS02 | CS01 | CS00 | ||
波形生成モードの設定 | Mode | WGM02 | WGM01 | WGM00 | 意味 | TOP | |||
7 | 1 | 1 | 1 | Fast PWM | OCR0A | ||||
クロックの選択 | CS02 | CS01 | CS00 | 意味 | |||||
0 | 1 | 0 | 分周比 8 | ||||||
比較出力モードの設定 | COM0A1 | COM0A0 | D6の制御 | ||||||
0 | 1 | WGM02=0:通常のポート動作、OC0Aは切断、WGM02=1:比較一致でOC0Aを切替 | |||||||
COM0B1 | COM0B0 | D5の制御 | |||||||
1 | 0 | 比較一致でOC0Bをクリア、OC0BをBOTTOMに設定(非反転モード)。 | |||||||
タイマ1 | TCCR1A | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
COM1A1 | COM1A0 | COM1B1 | COM1B0 | * | * | WGM11 | WGM10 | ||
TCCR1B | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
ICNC1 | ICES1 | * | WGM13 | WGM12 | CS12 | CS11 | CS10 | ||
TCCR1C | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
FOC1A | FOC1B | * | * | * | * | * | * | ||
波形生成モードの設定 | Mode | WGM13 | WGM12 | WGM11 | WGM10 | 意味 | TOP | ||
15 | 1 | 1 | 1 | 1 | Fast PWM | OCR1A | |||
クロックの選択 | CS12 | CS11 | CS10 | 意味 | |||||
0 | 1 | 0 | 分周比 8 | ||||||
比較出力モードの設定 | COM1A1 | COM1A0 | 意味 | ||||||
0 | 1 | 比較一致でOC1Aを切替。 | |||||||
COM1B1 | COM1B0 | 意味 | |||||||
1 | 0 | 比較一致でOC0Bをクリア、OC0BをBOTTOMに設定(非反転モード)。 | |||||||
タイマ2 | TCCR2A | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
COM2A1 | COM2A0 | COM2B1 | COM2B0 | * | * | WGM21 | WGM20 | ||
TCCR2B | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
FOC2A | FOC2B | * | * | WGM22 | CS22 | CS21 | CS20 | ||
波形生成モードの設定 | Mode | WGM22 | WGM21 | WGM20 | 意味 | TOP | |||
7 | 1 | 1 | 1 | Fast PWM | OCR2A | ||||
クロックの選択 | CS22 | CS21 | CS20 | 意味 | |||||
0 | 1 | 0 | 分周比 8 | ||||||
比較出力モードの設定 | COM2A1 | COM2A0 | 意味 D11の制御 | ||||||
0 | 1 | WGM22=0:通常のポート動作、OC2Aは切断、WGM22=1:比較一致でOC2Aを切替 | |||||||
COM2B1 | COM2B0 | 意味 D3の制御 | |||||||
1 | 0 | 比較一致で加算時にOC2Bをクリア、減算時にOC2Bを設定。 |
// Uno 20KHzPWM test // 2020.4.3 #include <avr/io.h> void setup() { pinMode(3,OUTPUT); pinMode(5,OUTPUT); pinMode(6,OUTPUT); pinMode(9,OUTPUT); pinMode(10,OUTPUT); pinMode(11,OUTPUT); TCCR0A = 0b01100011; TCCR0B = 0b00001010; OCR0A = 99; TCCR1A = 0b01100011; TCCR1B = 0b00011010; OCR1A = 99; TCCR2A = 0b01100011; TCCR2B = 0b00001010; OCR2A = 99; } void loop() { int vol=20; // OCR0B = vol; // OCR1B = vol; // OCR2B = vol; analogWrite(5,vol); analogWrite(10,70); analogWrite(3,40); }
そして、各レジスタのビットの値をまとめると下記の様になります。
レジスタ名 |
7 |
6 |
5 |
4 |
3 |
2 |
1 |
0 |
---|---|---|---|---|---|---|---|---|
TCCR0A |
0 |
1 |
1 |
0 |
0 |
0 |
1 |
1 |
TCCR0B |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
TCCR1A |
0 |
1 |
1 |
0 |
0 |
0 |
1 |
1 |
TCCR1B |
0 |
0 |
0 |
1 |
1 |
0 |
1 |
0 |
TCCR2A |
0 |
1 |
1 |
0 |
0 |
0 |
1 |
1 |
TCCR2B |
0 |
0 |
0 |
0 |
1 |
0 |
1 |
0 |
これを、スケッチに書き込む場合の記述方法は、
TCCR0A = 0b01100011;
TCCR0B = 0b00001010;
TCCR1A = 0b01100011;
TCCR1B = 0b00011010;
TCCR2A = 0b01100011;
TCCR2B = 0b00001010;
となります。 また、トップ値の指定は、
OCR0A = 99;
OCR1A = 99;
OCR2A = 99;
です。 そして、PWMの出力ポートは、タイマ0のBチャネルであるD5、タイマ1のBチャネルであるD10、タイマ2のBチャネルであるD3から出力されます。
スケッチに記述する場合は、右の様に記述します。
そして、デューティ比の指定は、BチャネルのOCRnBレジスタにその値を右のように書き込みます。 当初はレジスタに直接指示していましたが、analogWritr( ) の命令が使えましたので、こちらの記述でもOKです。
*************************************************************************** ***** analogWritr( ) の記述時の注意事項を追記します。 ************* ***************************************************************************
analogWritr( ) の記述時の注意事項を追記します。
今回の処理は、ノコギリ波のトップの値を 255 から 99 に変更して20kHz 化を実施しています。 従ってデューティ比の指定は0〜99までしか指定できません。 このため、スケッチでのanalogWritr( ) のコマンドは使用できるものの、その指定値には注意が必要です。 99以上の値は無効なので、結果的にはデューティ比100%の状態になるものと推定します(未確認ですが・・・・・)。
しかし幸いなことに、0〜99までで 0〜100%を指定することになるので、デューティ比のパーセント表示そのままの値を使用すれば良い事になります。
********************************************************* 2021/5/2 追記 ***************************************************
■ 実際の観測結果
それでは実際にArduino Uno に書き込んでパルス波形を観察してみましょう。
それぞれのポート出力の波形です。
狙いどうりに 20kHz のPWM波形が出力されています。 また、各ポート間の波形の立ち上がりタイミングは、かなりドリフトしています。 特にD10ポートは大きく離れています。 |
||
CH1はD10ポート、CH2はD3ポートです。 | CH1はD5ポート、CH2はD3ポートです。 | |
Aチャネルの出力もチェックしてみました。やはり、半分の周波数で、50%デューティのPWM波形です。 同じタイマ/カウンタを使用しているため、立ち上げリタイミングはピタリと一致しています。 |
||
CH1はD9ポート、CH2はD10ポートです。 | CH1はD6ポート、CH2はD5ポートです。 |
■ まとめ
Arduino Uno でも 20kHz のPWM波形を出力させる事が出来ました。 しかし、問題点として、
このため、自分が使いたい鉄道模型の自動運転制御用コンピュータとしては、不向きであると結論しました。 もし、あえて使うとするならば、何らかの工夫が必要となるでしょう。
2020/4/4 作成 2021/5/2 追記