こんなことをやる方は少ないともいますが、何かしらの理由があって、QMK firmwareで標準で定義されているスペックから変更したいことがあると思います(←本当に????)。今回は訳がありHSE(High Speed External Clock Signal: HSE)を変更したいという話を軸に書いていこうと思います。
まあ普通はこんなことはないと思うのですが、部品のスペックやら価格の都合は色々ありますからね。というわけでそんなハードウェアスペックが一般的な構成が異なる場合のQMK firmwareの設定です。
自分はマイコンを普段触るので、クロックの構成などはある程度把握しているのですが、これをQMKで設定するのはどうすればいいのか?というのがことの発端です。ChibiOSに反映する必要があるのですが、QMK firmwareだとデバイスに関する固有部分の設定など隠蔽されているので、いまいちよくわからなかったのでここにまとめておこうと思います。少しだけ解説も加えておきます。
目次
クロック回りの前提
一般的にはProMicroなどは既製品は構成部品は決まった構成で、クローン品もほぼ同じ構成です。そして、QMK firmwareもそのデファクトスタンダードに従っています。なので、ハードウェアが違う構成だと色々設定をしないといけません。
QMK firmwareを触る層がどういう層なのか、はっきりわかっていないので少し詳細を書きますが、マイコンは通信をするために高速な外部クロック(High Speed External Clock Signal: HSE)をほとんどの場合で利用しています(一部は内部クロックです)。クロックの設定の概念があり、動作周波数の他に通信用や割り込み用のクロックといったものを生成しています。その生成の経路としては一般的にHSEをベースとして、それを特定倍することで多様なクロックを生成します。特定倍するのはソフト上で行います。
つまり、ハードウェアの構成から決まる部分と、ソフトウェアの部分があり相互に関係しています。今回はハードの方を変えるということで、ソフトの方も変更が必要というわけです。
次のところで説明で使う「STM32F103」のクロック構成について見てみましょう。

このような構成になっています。この図を眺めてみると、入力は2つ存在することがわかるかと思います。OSCとOSC32の2つですね。
QMKで最も大事というか基本のUSB CLKを見てみましょう(STM公式なのにUSBC LKとかいうガバ翻訳なんですね…)。このUSB CLKの元をだどっていくと、PLL CLKを利用していることがわかりますね。PLL CLKのさらに大元を辿るとHSEとHSIのどちらかにつながっていることがわかると思います。
STM32F103ではHSIのクロック精度はUSBのスペックに対しては適当ではないので、必然的にHSEを選択していることがわかるかと思います。STM32F072なんかだと校正済みの8MHzのHSIを持っていてHSEなしでUSBを動作したりできるのですが、少なくともSTM32F103においてはHSEが必須です。高精度HSEが必要ということはリファレンスマニュアルにも書いてあります。
つまり、QMKでUSB通信ができる=HSEを使った正しいクロックツリーが構成されていることになります。
本来であれば、クロックツリーが全て問題ない値で設定されている必要があるので、AHBやAPB1やAPB2プリスケーラなどにも気を配る必要があるのですが、それを含めてQMKでは正しく設定されていることになります。
デフォルトの値について考える
実際にこれを例に取って実際に設定を行います。STM32F103だとBluePillが標準的なボードとして定義しています(参照)。このボードの構成を見てみると、HSEは8MHzになっています。
正しく動くUSB CLKの値から、正しい値を導いてみましょう(もちろん、適切に動く個体のレジスタ値を読みだして判断、なんて方法もありますが、お勉強的には邪道なので考えないでおきましょう)。
クロックツリーのUSBCLKがありますが、ここの出力が48MHzであることが書いてありますね。他のところは最大何Hzとかですが、ここはUSBの仕様合わせて48MHHzを必ず出すという指定になります。さらに手前にはプリスケーラがありますね。プリスケーラは/1か/1.5のどちらかしかありません。つまり、PLL CLKは48MHzか72MHzのどちらかであるということがわかるかと思います。
PLL CLK=48[MHz]*(USBプリスケーラ値)で求まるので、
(PLL CLK)=48*1=48MHz
or
(PLL CLK)=48*1.5=72MHzPLLCLKの下ではなく、その先を見るとSYS CLKがあるかと思います。PLL CLKからSYS CLKを生成しているので、SYS CLKに合わせてPLLの倍率を設定してあげればいいわけです。
そしてQMKのコードを流し読みしてみると、BluePillが何もしない設定で動くようですが、これと言って何かを指定しているようには読めません。なので、何かしらのデフォルト値が設定されていると考えて良さそうです。全てのコードを調べる必要はなく、USBプリスケーラの部分を調べることで芋づる式にパラメータがわかります。そこだけ調べてみると、値は/1.5のようです。BluePillについて調べてみると、HSEは8MHzを使っているようなので、PLLはx9となっているはずです。これがデフォルト何も明記しないと適応されるパラメーターということになります。
また、SYS CLKを入力するAHBプリスケーラ以降の段に関しては特に触らなくても大丈夫そうなことがわかりますね。USBプリスケーラはPLL CLKから入力していますが、結局SYS CLKとイコールになるような構造なので、8MHz *PLL=PLL CLK (=SYS CLK)の構造は変わりません。なので、HSEを変更しても出力のPLL CLKが同じであれば他の値は正しく動くことがわかります。つまり、HSEを変更したとしても、PLL以外は8MHzのときと同じで動くということがわかります。
HSEを12MHzに変更した場合を考えてみる
さて、HSEを変更した場合はどのようにパラメーターを設定したらよいのでしょうか。見出しのように12MHzに変更してみましょう。

まず、48MHzを作る方法を考えると2パターンあることになります。先ほど書いた分周がいくつかという話ですね。元がPLL CLK依存なので、PLL CLKで出力されるべき値は48MHzか72MHzということになります。 適切に設定すればどちらでも動かすことは可能ですが、一般的には省電力用途でなければ早い72MHzを選択するかと思うので、72MHzを想定してみましょう。
するとPLLがx6、プリスケーラが/1.5とすれば正しく動作することがわかります。
PLL=72MHz/12MHz=6
USB Pre-Scaler=72/48=1.5システムクロックを48MHzとする場合は、PLLが4、プリスケーラが/1で動作することが導けます。この場合はAHB以降の段で調整が必要になる可能性があります(8MHzのときと設定が異なってくるため)。
PLL=48MHz/12MHz=4
USB Pre-Scaler=48/48=148MHzでもキーボードとして遅いことはないので、どちらにせよ正しく動きます。STM32F072などは最高でも48MHzなので、キーボードとしてはそこまでの性能が必要ないというのが実態です。
ひとまずこれで、最低限動作させるのに必要なPLLとプリスケーラ値を判断することができました。
QMKでの設定方法
設定したい値というのはわかったのですが、QMKではこれをどう設定するのかはドキュメントにも記載がありません。
コードを調べてみると、mcuconf.hはキーボードのプロジェクトごとに適用できるヘッダの一つで、ChibiOSのビルドで使う値を定義できるようです。ChibiOSはQMKを動かすベースとなっているOSSのRTOSの事です。深く知らなくても、「mcuconf.h」を変更するという事実だけ理解しておけばよいです。
そのmcuconf.hをなければ作成、あれば追記という形で以下のようにします。ChibiOSの定義に従った書き方になっています。
#pragma once
#define _12MHz_HSE
#ifdef _12MHz_HSE
#define STM32F103_MCUCONF
#undef STM32_HSECLK
#define STM32_HSECLK 12000000
#undef STM32_PLLSRC
#define STM32_PLLSRC STM32_PLLSRC_HSE
#undef STM32_PLLMUL_VALUE
#define STM32_PLLMUL_VALUE 6
#define STM32_USB_USE_USB1 TRUE
#else
/*If not define _12MHz_HSE, default values are used*/
#endif
最初はdefineのSTM32_HSECLKやSTM32_PLLMUL_VALUEを定義すればよいのかと思ったのですが、どうも明確にSTM32F103の設定であるということも定義する必要があるようで、
define STM32F103_MCUCONFは定義する必要がありました。また、USBを使うということも明示しないといけないようで、
#define STM32_USB_USE_USB1 TRUEについても定義する必要がありました。これだけ定義すれば12MHzに変更したものでも動きました。色々デフォルト値を調べると、USBプリスケーラは何も定義しなかった場合は/1.5になっているようなので、ここでは定義していませんが、明示する場合は
STM32_USBPRE_DIV1P5
//余談:/1のときは
//STM32_USBPRE_DIV1を定義しておけば良いです。基本的には最大クロックのSYS CLKが入力されても正しく動くようになっているみたいですが、本来はこれだけではなく、クロックツリーにあるAHB/APB1/APB2プリスケーラなどが正しく設定(最大366MHzという範囲内)されていないと動かないので、ふとした時に足をすくわれないように気を付けないといけません。
ちなみに、今回はちゃんとHALレイヤのデフォルト値を確認して問題ないことは見ています。なので、ここで出している設定は一例であり、ほかの周波数のHSEを使うと追加で設定が必要かもしれないことは注意してください。
もし変更する必要があっても、このようにmcuconf.hに追記をすればなんとかなるということがわかっていれば理解できるかと思います。
これを設定してビルドしたところ、12MHzのオシレータを装備したSTM32F103マイコンでも問題なく動作することが確認できました。
STM32F103に限った話ですが、Vialで使う場合はそもそもviblという専用のブートローダーを使うかと思います(参考)。そうでなくても、USBブートローダーを何かしら使うことがあると思いますが、これもPLLの値を変更したものを書き込まないといけません。F103の場合はこのあたりも専用コードを用意してあげる必要があります。
なお、Vialで特に違う点はないので、同じ方法で追記を行えば大丈夫です。
おまけ:Vial用にviblを変更する
というわけで自分の場合はviblを変更する必要があるのですが、これについては、べた書きになっているコードを読んで探すしかありません。というわけで探すと、main分の最初の方に普通に書いてありました。
LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE_DIV_1, LL_RCC_PLL_MUL_9);ここを
RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSE_DIV_1, LL_RCC_PLL_MUL_6);と変更するだけです。一応自分のブランチはここら辺もビルドを切り替えられるようにコードを用意してみました。とはいえ、べた書きなので何とかしたいですが。自分のブランチはこちら。
–GitHub–
https://github.com/T-H-Un/vibl
上述しましたが、8MHzで正しく動くコードということは、基本的にPLL CLKが8MHzの時と同じ値にする場合、他の値は一緒で問題ないということがわかります。なので、ここのPLLの値を変更するだけで正しく動くであろうということはわかったうえでやっています。
これをビルドしたところ問題なく動作していることが確認できました。これはHIDブートローダーなので、HIDデバイスとして正しく認識できていることが確認できれば問題ありせん。正しく認識できていない場合はクロックツリーか回路がおかしい可能性が高いです。
まとめ
今回はデフォルト値を生かしつつ比較的楽に動くように設定し動くところまで確認しました。また、実用的に使うためにVialで使うviblの変更についても検証しました。
こんなことをやろうという人がいないためか、リファレンスにこれ類することは書いていなかったので、誰かの助けになればなと思い書きました。後はいつも通り自分の備忘録ですね。
ベースにあるのがChibiOSであることがわかっていて、それをmcuconf.hに書くことがわかっていれば違う設定でもなんとかなると思います。今回は一例としてHSEを変更する方法を紹介しましたが、他の部分でスペック違いになるというのはあまり想像できませんね。せいぜい無線とかのために省電力化のクロックダウンとかでしょうか。
マイコン触る方はクロックツリーを正しく読み取って設定するだけなのですが、昨今は自分で一からクロックツリーを設定するような開発も減ってきたように思うので、これを一から触ろうとすると苦戦する人が今後は増えてくるのかなとも感じました。
以上です。お読みいただきありがとうございました。