STM32をレジスタで制御する③~PLL~
結局夏休み中に二個しか記事を投稿できませんでした、結構ロボコンやばいです。
ESP32に苦しんでる今こそ、STM32で癒される必要があるのです。
というわけで、今回はPLLです。
クロック上げて幸せになりましょう!
はじめに少しだけ
前回まではSTM32F334K8T6を使用していましたが、今回から使用するマイコンをSTM32F303K8T6に変更します。
といってもピン配置は互換性が多分ありますし、レジスタ単位でみても致命的な変更はなさそうです。(少なくとも前回までの制御には互換性があります)
それに合わせて、F303K8T6のドキュメントをST公式から入手しました。
データシート
リファレンスマニュアル
PLL
なぜこのタイミング?
先にUSARTとかI2C解説しろよという声が聞こえそうですが、先にクロックをいじるのは一応理由があります。
これから様々なペリフェラルを触るとき、適切な制御をおこなうためにクロック周波数を気にする必要があります。
たとえばシリアル通信系の通信速度ではないでしょうか。
そのため、先にこういったペリフェラルを触った後にクロックを変更してしまうといろいろと面倒になりそうです。
なので先にクロックをいじるわけです。
クロックの設定
さて、さっそくレジスタを…と行きたいところですが、まずはCubeMXのClock Configulationを利用して、クロックの上限等に問題ないか確認しておきましょう。
もちろん、データシートのクロックツリーをにらめっこしながらでもいいのですが、設定に問題がある場合に警告してくれたほうがコーディングするときに楽だと思うので、MXの力を借りましょう。
今回はクロック源をHSIとして、64MHzまで逓倍します。
MXの図を参考にコーディングしていきましょう(というか結構そのまま)。
コーディング
まず、PLLのクロック源や逓倍、分周等の設定をしましょう。
PLLにはHSIを入れる(実際にはHSIを2分周したもの)ので、RCC_CFGRのPLLSRCビットを0にするとよさそうですね。
RCC -> CFGR &= (~(1 << 16));
ここでちょっと注意ですが、APB1のクロックの上限は36MHzです。
これは先ほどのMXが警告を出してくれましたね、ありがたい。
今回、64MHzまで逓倍するので、APB1は2分周すればよさそうです。
RCC -> CFGR |= (4 << 8);
では、クロックを逓倍しましょう。
4MHzを16倍にして64MHzにするので、PLLMULは1111にします。
RCC -> CFGR |= (15 << 18);
そこいじるの?
一通りクロックの設定が終わったから早速PLLを起動して動作させましょう!
…と行きたいところなのですが、まだ設定は終わっていないのです。
RCCの項目見てもそれっぽいところなさそうじゃないか!という声が聞こえてきますがその通りです。
というわけでFlashのレジスタを設定していきましょう。(!?)
はい、なぜかACRレジスタをいじる必要があるのです。
自分は超アマチュア制御屋なので詳しいことはわからないのですが、リファレンスマニュアルの説明には、「フラッシュから正しくデータを読むためにクロックに応じて読み出しまでの遅延時間を調整する必要がある」的なことが書かれていました。(間違ってるかもしれませんごめんなさい)
このレジスタのLATENCYを010にすればよさそうですね。
FLASH -> ACR |= (2 << 0);
いよいよ起動!
ちょっとした躓きポイントを超えたらあとは簡単です、頑張りましょう!
RCC_CRレジスタのPLLONのビットを立て、その後PLLRDYのビットが1になるのを待機すればPLLを起動できそうです。
whileで待機しましょう。
RCC -> CR |= (1 << 24);
while (!(RCC -> CR & (1 << 25)));
最後にRCC_CFGRレジスタのSWでシステムのクロックをHSIからPLLに切り替え、先ほどと同じようにSWSでシステムクロックがPLLに切り替わるまでwhileで待機するだけです。
RCC -> CFGR |= (2 << 0);
while ((RCC -> CFGR & (3 << 2)) != (2 << 2));
おめでとうございます!これにてPLLの設定は終わりです!
ちゃんと高速になってるか動作確認をしよう!
前回までと違い、今回の内容は別になにかデバイスを制御するものではないってのが困るところですね…。
とはいえ簡単に確認する方法がないわけではありません。
次のコードをマイコンに書き込んでみましょう。(今回コーディングしたPLLについてのコードはmain関数に書くとあれなので関数にしておきました)
どうでしょう?かなりGPIOの切り替えがトグルが遅いと思います。
そこでmain関数内のPLL_Init関数のコメントアウトを外して再び書き込んでみてください。
どうですか?先ほどよりトグルの速度が速くなっていればうまくPLLできています。
レジスタ制御って必要?
ここからは余談になります。
高専ロボコン、ロボカップでもなんでもそうですが、ArduinoとかMbed、HAL使えばいいじゃんって無限に言われます。
確かにそうです。
本質はアイデアからアルゴリズムを考え、コーディング、制御してしっかりとロボットを動作させ競技に挑むことなのですから。
なので高専ロボコンのような「制御」よりも「アイデア」が主軸の競技においてはレジスタ制御は多用しないほうがいいと思います。
しかも大人数開発ですからね、部内で「レジスタ制御しかするな」と言われない限りはArduinoとか使って制御が無難だと思います。
ただ、レジスタ制御は「制御」が主軸の競技、つまりロボカップのような競技では十分採用する価値があります。
…といっても何でもかんでもレジスタ…ってのもあまりよくない気がします。
もう少し詳しく説明すると、GPIOやペリフェラルの初期化等は生成されたコードや用意された関数を使い、mainの処理にレジスタ制御を使用、これがうまい付き合い方だと思います。
お察しの通り、初期化なんてのは制御周波数にほとんど関係ないわけで、わざわざ自分でコーディングする必要はないのです。
しかし、単純なGPIOの操作、PWMやADCの制御をレジスタ制御で、かつ割り込み処理やDMAといった機能を活用すれば、とんでもない複雑なアルゴリズムでもなければ十分高い制御周波数を出してくれるでしょう。
ロボカップはどの部門においても大量の入出力、処理が必要だと思います。
各チームの制御屋の腕の見せ所ってところで、細かいところにはなりますがThrcotではレジスタ制御を徐々に導入しています。
まとめると今回の記事は自分にとって特に役に立つ機会はないってことです。
まとめ
今回はPLLでクロックを逓倍し、処理速度を向上させました。
初期化コードをわざわざ書く意味はあまりないといいましたが、マイコンの気持ちを理解するという点においては一度コーディングするという経験は決して無駄ではないと私は思います。
でも重要度は低いです、これが自分の中で覆るのは難しいですね…。
ま、これ以降64MHzで動作させることができると考えると興奮してきますねニヤニヤしてしまいます。
やけに長い記事を読んでいただきありがとうございました。
それでは、よいSTM32ライフを。
おやすみなさい。