今回のテーマはクロックアップです。定格クロック周波数を超えて動作させてみます。
Arduino UNOに使われているATmega328P-PUというMCUは定格が20MHz @5Vです。しかしUNOは16MHz動作、と言うことで、最高スペック未満で使われています。そこで、「#9-5 ブレッドボードにArduinoっぽい物」の記事で作成した328duino(第一形態)の水晶発振子を変更して高速動作させてみたいと思います。
今回のターゲットは328duino(第一形態)なので、以下本ページでは単に328duinoと言ったら「第一形態」を指すこととします。
328duinoでは以下図の様な水晶発振子をATmega328の発振端子に接続して、動作に必要なクロックを生成しています。
水晶発振子にはそれぞれ決まった発振周波数があって、所望の周波数に合った発振子を接続する必要があります。Arduino UNOや328duinoでは16MHzの水晶発振子を使用しています。
今回入手したのは4〜48[MHz]の水晶発振子が15個入ったセットです。20MHz以上で整数MHzの発振子は以下の4種類でした。
20MHz,24MHz,25MHz,48MHz
定格20MHzのATmega328P-PUが20MHzで動くのは当たり前だし、さすがに48MHzで動くとは思えない(速すぎる)ので、今回のターゲットは24MHzや25MHzで動作するかどうか?でしょう。
最初に述べておきますが、20MHzを超える周波数は定格外なので、動いても動かなくても何の保証もありません。クロックアップは各自の責任で実施する物ですので、その点をご理解の上、先へ読み進んでいただきます様、お願いします。
クロックアップ自体は水晶発振子を取り換えて電源を投入するだけですので、いくらでも試すことができます。問題はクロックアップした328duinoが正常に動作していることを確かめる方法、です。
「動いてますか?」を知るためにLEDなどをチカチカさせる方法は一番簡単です。クロックが速すぎてCPUが動作できなくなると、LEDを点滅させることができなくなるので、その様子で動いているかどうか?を知ることができるのです。
しかし、MCUには「クリティカルパス」と言うのが有って、何か重い処理を実行するユニットがあるとクロックアップした時にそのユニットが活性化されるような動作を実行した時、最初に失敗します。このため、MCUの製造メーカではクリティカルパスを活性化できるテストパタンを使って動作テストを実行し、定格周波数で正常動作する個体だけを良品として出荷しています。
ATmega328P-PUのクリティカルパスがどこにあるかわからないし、どのようなテストパタンがクリティカルパスを活性化できるかも知りません。そこで、以下の様に考えることにします。
以下に円周率を計算してCPUのセルフテストを実行するプログラムをリンクしておきます。
円周率計算プログラム:calc_pi.ino
円周率πの求め方やそのプログラムの内容については次回に解説したいと思います。今回は使い方だけ説明します。コンパイル&書き込むとシリアルモニタから以下のコマンドを受け付けるようになっています。
では、入手した水晶発振子を使って、定格を超える周波数で328duinoが動作するかどうか確認してみましょう。確認手順は以下の通りです。
ボード「Arduino UNO」(@16MHz)で書き込んで別なクロックで動作させているので、シリアルモニタは使えません。LED_BUILTINの表示でしか動作確認できませんので、必ず、LED_BUILTINを接続してください。
どうしても必要なら、「Serial.begin (9600*(F_CPU/1000000)/25);」などと、動作対象の周波数(この例では25MHz)で校正しておくとシリアルモニタを使用することができるようになります(以下の様に……)。
実験サンプル:【 】内はシリアルモニタからの送信データ
【pi,200】
OK+ start calculation
OK+ PI=3.
1415926535 8979323846 2643383279 5028841971 6939937510
5820974944 5923078164 0628620899 8628034825 3421170679
8214808651 3282306647 0938446095 5058223172 5359408128
4811174502 8410270193 8521105559 6446229489 5493038197
OK+ digits=200 (size=51)
OK+ time=630694[us]
OK+ check=0x7fdafe13
OK: pi F_CPU=16000000 ← UNO @16MHzでコンパイルしたため、表示は16MHz。
※上の例(小数点以下200桁)だと、最下位の1桁は期待値と合いませんでした。正しくは「×8197 → ○8196」です。MCUのセルフテストとして使用する場合、「正しく間違っていること」の確認にも意味があるので、不一致桁も含めて期待値比較しています。
参考までに、UNO @16MHzでの実行結果が以下です。
: (省略)
OK+ digits=200 (size=51)
OK+ time=985452[us]
OK+ check=0x7fdafe13
OK: pi F_CPU=16000000
要約部分だけ表示しました。期待値(check)は双方とも一致しています。演算時間(time)の違いに注目してください。ほぼ、25MHz:16MHzの比になっています。
以下、手持ちの328duinoで確認した結果です。
定格20MHzのMCUなので、定格電圧(Vcc=5V)、室温(Ta≒25℃)の条件であれば、まあ、25MHzくらい動かないとおかしいよね。もっと厳しい条件、例えば、Vcc=4.5V,Ta=85℃で、20MHzが保証値だから。でも、さすがに48MHzは動くはずない。次の節で突っ込んで調査してみます。
ともあれ、まずは目標だった25MHzでの動作が確認できました。定格外の周波数ですので、MCUの個体差によって動かない場合もあります。皆様お手元のATmega328P-PUが25MHzで動くかどうかはわかりません。各自の責任において実験して確かめてください。
前節の48MHzは動作したけれど、さすがに20MHz定格のMCUが48MHzで動くとは思えません。それにLED_BUILTINの挙動(点滅速度)も、16MHzで動作させたときの速度とあまり変わらない様でした。そこで、「#9-7 内蔵RC発振器を校正する」の時に作ったプログラム(cali_rcosc.ino)を使って、328duino+48MHz水晶発振子のクロック周波数を測定してみようと思います。
以下、確認手順です。
UNOのシリアルモニタを開いて、328duino+48MHzのクロック周波数を計測してみました。
実験サンプル:【 】内はシリアルモニタからの送信データ
【show,8】
OK: show 8 (1000[ms])
OUT: freq=,15992,15994
OUT: freq=,15992,15994
OUT: freq=,15992,15994
OUT: freq=,15996,15994
OUT: freq=,15992,15994
OUT: freq=,15996,15994
OUT: freq=,15996,15994
OUT: freq=,15996,15994
何と!48MHzの水晶発振子を接続したのに、クロック周波数は16MHzでした。不思議🤩。
16MHz=48MHz/3って事なんでしょうね。そもそも、ATmega328の発振器自体が48MHzを発振しきれなくて、16MHzになってしまったみたいです。24MHz=48MHz/2ではなくて、1/3だった理由は解り兼ねます。
念のため、328duinoの水晶発振子を25MHzに付け替えて、同じくクロック周波数を測定してみました。
実験サンプル:【 】内はシリアルモニタからの送信データ
【show,8】
OK: show 8 (1000[ms])
OUT: freq=,25004,24996
OUT: freq=,24995,24995
OUT: freq=,24995,24995
OUT: freq=,24995,24995
OUT: freq=,24995,24995
OUT: freq=,24995,24995
OUT: freq=,24995,24995
OUT: freq=,24995,24995
良かったです。25MHzの方は、ちゃんと25MHzで発振していたようです。これでようやく、正しく発振して正しく動作することを確認できた、と言う事ですね。
つまり、単純に高速な水晶発振子を接続しただけでは、本当にその周波数で発振しているかどうかわからない、と言うことになります。厳密には、以下の確認手順が必要そうです。
どちらかと言うと、ハードウェアよりソフトウェアの方が問題かもしれません。「#9-9 328duinoをArduino IDEに組み込む」で検討した様に、コアライブラリの一部が動作周波数として2冪数MHzを前提としているので、2冪数ではない20,24,25[MHz]で動作させると不具合や誤差が出る場合があります。
まずは、millis()関数とmicros()関数の誤差をまとめておきましょう。
F_CPU | 理想millis | millis()関数 | 誤差[%] | 理想micros | micros() | 誤差[%] | _micros() | 誤差[%] | |
16MHz | 1.024 | 1.024 | 0.000 | 4.000 | 4.000 | 0.000 | 4.000 | 0.000 | |
20MHz | 0.819 | 0.816 | −0.391 | 3.200 | 3.000 | −6.250 | 3.188 | −0.391 | |
24MHz | 0.682 | 0.680 | −0.391 | 2.667 | 2.000 | −25.000 | 2.656 | −0.391 | |
25MHz | 0.655 | 0.648 | −1.123 | 2.560 | 2.000 | −21.875 | 2.531 | −1.123 |
ひどいですね、誤差が大きすぎます。内蔵RC発振器で動作させた328duino(第二形態)の時に作った、16MHz以下の表と見比べてみると:
少なくとも、「#9-9 328duinoをArduino IDEに組み込む」で対応した328duino(第二形態)の時と同じ様に、1)Arduino IDE(boards.txt)への組み込み、2)328duinoライブラリ(_micros()関数)の第一形態への拡張、が必要となりそうです。
Arduino IDE(boards.txt)へ組み込むため、もう一つクリアしておかなければならないことは、ブートローダのコンパイルです。本ページでは20,24,25[MHz]で動作可能なブートローダが無かったので、328duinoへ書き込む際、水晶発振子を16MHzに差し替える必要が生じました。
書き込んだ後で → 電源を遮断して → 水晶発振子を所望の値に変更後 → 電源を再投入、これでは、使い勝手が悪すぎます。また書き込むときは、電源を遮断して → 水晶発振子を16MHzに変更後 → 電源を再投入 → 書き込んで → ……、です(1x_x)。
例えば「25MHzで使う」と決めたら、25MHzのままArduino IDEからプログラムを書き込めるように、25MHz用のブートローダをコンパイルして328duinoに書き込んでおけば、こんな面倒は必要なくなります。
ArduinoのanalogRead()関数を使うと、ATmega328の10ビットAD変換器を使ってアナログ→デジタル変換したデータを読み出しています。
ATmega328のAD変換器にはAD変換クロックを指定するレジスタビット(ADP2:0)が有るのですが、このクロックを50〜200[kHz]で使用すると絶対精度±2LSBで変換することができます。これより速いクロックでも変換は可能ですが、精度は±4.5LSBに低下します。また、最速クロックは1MHzまでと規定されています。
F_CPU | ADP2:0ビット | AD変換クロック | 備考 |
16MHz | 0〜3 | 8〜2[MHz] | ×仕様範囲外 |
4 | 1MHz | ±4.5LSB | |
5 | 500kHz | ||
6 | 250kHz | ||
7 | 125kHz | ±2LSB | |
20MHz | 0〜4 | 10〜1.25[MHz] | ×仕様範囲外 |
5 | 625kHz | ±4.5LSB | |
6 | 312kHz | ||
7 | 156kHz | ±2LSB | |
24MHz | 0〜4 | 12〜1.5[MHz] | ×仕様範囲外 |
5 | 750kHz | ±4.5LSB | |
6 | 375kHz | ||
7 | 188kHz | ±2LSB | |
25MHz | 0〜4 | 12.5〜1.56[MHz] | ×仕様範囲外 |
5 | 781kHz | ±4.5LSB | |
6 | 391kHz | ||
7 | 195kHz | ±2LSB |
※水色着色箇所は、analogRead()関数が使うデフォルトの設定値、かつ、変換精度±2LSBで使用可能な設定値です。
動作クロックが高速化したことでAD変換クロックも変化してしまうため、上表のような形になります。
何とかfcpu=25MHzでも±2LSBの変換精度を確保することができました。ギリギリ195kHz<200kHzですので、これ以上クロックアップ(fcpu=26MHz等)してしまうと、±2LSBの変換設定が確保できなくなってしまいます。
また、fcpu=16MHzまではADPビット=4の設定が可能でしたが、fcpu>16MHzではADPビット=4の設定は仕様範囲外(>1MHz)となります。
変換時間については、fcpu=16MHzの時のAD変換クロック1MHzとした場合が、最速の13μsでした。fcpu=25MHzで使うと最速のAD変換クロックは781kHzですので、変換時間は16.6μsと、少し遅くなります。
もともと、定格外の25MHzで動作させているので、AD変換器だけスペックを守ろうとしても意味ないと言えば無いのですけどね。それでもやっぱり、デジタルとアナログでは性質が全く違うので、クロックアップと言う仕様範囲外使用と同じレベルで考えない方が良いです。
クロックアップしてアナログ(AD変換器)を使う時は、上述の様な注意点があることを認識しておいてください。
おまけで、Vcc-MIN;設定した周波数で正常動作する最低電源電圧を測定してみます。測定するのに必要なのは降圧電源モジュールです。下図の様な、LM2596ベースの降圧電源モジュールを入手して使用しました。
プラグイン端子と、ばらしたUSBケーブルから5V電源を入力して、そこから降圧した電圧を328duinoに供給、プログラム(calc_pi.ino)を走らせながら正常動作している間、電源電圧をどんどん下げて行きました。この降圧電源モジュールは出力電圧値を確認できる7SEG LED等の表示器が付いていないので、UNOのアナログ入力で電圧をモニタしながら実験しました(下図)。
測定した328duinoのクロック周波数は、8,16,25[MHz]の3点です。結果を以下図の様にプロットしてみました。
※個体差があるので、全てのATmega328が上の様な範囲で動作するとは限りません。
横軸に電源電圧、縦軸に動作周波数をプロットしてあります。正直、「うっそぉ〜!?」って感じなのですが、まぎれもなく手持ちのATmega328P-PUを使った実測結果となっています。
16MHzも、25MHzも、「スペックって何?」と、言わんばかりに、とてつもなく低電圧まで動作してしまいました。クロックアップした25MHzでさえ、3Vを下回って正常動作を確認できました。8MHzまで周波数を落とすと1.8Vまで動作確認できたのですが、実は、BOD(低電圧検知機能)レベルを1.8Vに設定してあるため、恐らく1.8V前後の所でBODリセットがかかってしまったものと思われます。→ ヒューズでBODを無効に設定すると、もう少し低電圧まで動作する可能性が高いです。
※こんな低電圧まで正常動作することに関して、原因として思いつくことが1つだけあるのですが、いくら調べてもその仮説を裏付ける情報に辿り着けなかったので、現時点では「夢か幻」。ご披露するのは差し控えておきます。
あらあら。どうします?例えば、16MHzのUNOっぽい物、328duino(第一形態)が3.3Vで動作するとしたら?あなたは何に使いますか?
【利点】:
【欠点】:
現に実測で動いているわけです。定格外なので、さすがにコンシューマ向けマスプロダクション(量産)で使う訳には行かないのですが、趣味の世界(DIY)であれば「アリ」じゃありませんか??
3.3V定格のモジュールを5Vで駆動する(← こっちはデバイスにダメージを与える恐れがある)のとは違って、5V定格のチップを3.3Vで動かすだけなのでデバイスへのダメージなどは無いはずです。問題は動くかどうか?なのだけど、見ての通り「動く」から……。後は各自の判断です。
今回のお買い物は、以下の通りです。
クロックアップが目的なので、最終的には動作する中で一番高速な1個しか使わない水晶発振子の15個セットに450円は、ちと、高かった(費用対効果悪すぎ)かな……。ATmega328P-PU×1個より高かったし。
UNO互換機が800円〜1,200円で売っていることを考えたら、市販されていないであろう25MHz版UNOっぽい物が作れて+450円なら悪くないと思おう(全体のコストとしては、ATmega328P-PUなど、更に+数100円かかっています) 。
そうね、25MHz動作は定格外なので、さすがに市販はされないでしょう。通常使用状態では動くのにもったいなか。DIY🛠っぽいテーマでピッタリですね。……続きは次回。