STM32いじってみた(5) 簡易1msタイマー編

クロックに引き続き今度は便利な便利な 1ms タイマーを作ってみたいと思います。
ただしタイトルに有るようにあくまで簡易的なものです。設定が簡単なんですね。またそれほど精度は期待してはいけませんよ。といっても誤差が分からないくらい結構正確ですが。

使うペリフェラル

このマイコンには TIM という名の高機能タイマーがありますが、そちらだとやはり高機能なタイマー機能の1つをただのディレイで使ってしまいますのでもったいないです。さらに設定が割と面倒くさいので別のペリフェラルを使います。
では何を使うか?ですが、今回はシステムTickタイマー(以下 SysTick タイマー) を使います。これは初期設定としていじる箇所が1箇所しか無いため、めちゃ楽にできるんですよ。このタイマーは ARM コアに標準でついている、システムクロックを分周したタイマーです。設定していきましょう。

性能やタイマクロックの確認

ではどのような動作をするか?ということで確認します。マニュアル第9章にある 9.2 の下部ですね。ここにシステムクロックを8分周している的なことが書いてあります。ということは前回のように 64MHz で設定した場合は 64 / 8 = 8MHz で動くということです。おっそい。

続いて…と行きたいところなのですが、ユーザーマニュアルにはこれ以上の有用な説明は書いてありません。そこで ARM Cortex-M4 のリファレンスマニュアルを見てきたんですが、「ARMv7-Mアーキテクチャのリファレンス見てちょ」とたらい回しにしてきました。更に調べていくとこのリファレンスマニュアルは ARM に会員登録をしないと見られないらしいので断念。ちょっとアーキテクチャが異なりますが、ARM Cortex-M0等のユーザーガイドから該当箇所を抜き出してご紹介します。

これが SysTick のアブストラクトです。要は「タイマ有効化時にカウンターはリセット値になるよ」「ゼロになったら例外(割り込み)とかあるよ」「適切にビット設定してね」と言っています(要約には程遠い超絶意訳)。そこで設定すべきレジスタですが、まぁこんな感じです。

ENABLE ビットと COUNTFLAG ビットを上手く使えばきっちりとカウントできそうですよね。

実際の設定

ではやっていきましょう。上では紹介しませんでしたが、その他リセット値が23ビットで設定できるレジスタ、現在のタイマカウントを格納しているレジスタ、カウントをキャリブレーション出来るレジスタがあります。ここでは前2つを利用し、割り込み無しで実装していきたいと思います。
1つ注意なのですが、ライブラリにあるレジスタ構造体と実際のユーザーガイドに書かれている名前が結構異なります。察してあげましょう。

ではまずリセット値です。1ms タイマーを作りたいわけですから、周波数が 8MHz であることを考慮すると 8000 カウント目でフラグが立てば良いのですね。このタイマーはダウンカントであり、かつカウントがゼロになった次のカウントでフラグが立つのですから、リセット値を 8000 としてしまうと、1カウント多くなってしまいます。ですから、以下のように1だけ引いてあげます。

void ms_wait(uint32_t ms) {
    SysTick->LOAD = 8000 - 1; // reset value for count-down timer
    SysTick->VAL = 0; // initial value
// まだまだ続くよ
}

この LOAD がリセット値、VAL が現在のタイマカウントです。はいこれで初期設定終了。簡単でしたね。また、この関数では任意の時間(msec単位)だけ待つタイマーとしていますから、普通に5秒(5000msec) とか待てるようになるんですね。便利。
続いてカウントをスタートさせましょう。これは上の ENABLE ビットを立てれば良いのでしたね。

void ms_wait(uint32_t ms) {
    SysTick->LOAD = 8000 - 1; // reset value for count-down timer
    SysTick->VAL = 0; // initial value

    SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; // count start
// ここから本体
}

では1msec 経ったかどうかはどう判断するのでしょうか??それは上で見せました COUNTFLAG が立てば良いのです。ですから

while( !(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) );

とすればゼロの間無限ループにハマりっぱなしですからOKですね。これを関数引数の指定 ms 回だけ繰り返せば良いのですから

となります。最後に ENABLE ビットをクリアしてカウンターを無効化しています。これで、クロック周波数によって容易に待ち時間が変わってしまう無駄ループを使わずに済みますね。