RustでSTM32ベアメタル [4桁7セグメントLED]（実装編3）<最終回>〜TIMペリフェラル〜

***<font color="#0080FF">Rust</font>***という言語で***STM32F3Discovery***というマイコンを動かし，***4桁7セグメントLED***を***ダイナミック点灯方式***で光らせるっていう記事の***4***編目です．
細かく書いてるのもあって長くなってます．ここまで読んでいただいてる方はもしかしたら少ないかもしれませんが，まだまだ丁寧に書いていきます．

この記事を読み終わるころには***完全に理解した***って言ってもらえるはず！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/285beced-2702-6e44-0939-382dcda6b508.gif" width=320px>
成果物はこちら．↓
<a href="https://github.com/lenna-kun/stm32f3discovery-7_seg_led"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/lenna-kun/stm32f3discovery-7_seg_led.png" width="400px"></a>
[概要編](https://qiita.com/lenna_kun/items/b49c743d401d113cb949)では，主に***4桁7セグメントLED***を***ダイナミック点灯方式***で光らせる方法について，概要を説明しました．
[実装編1](https://qiita.com/lenna_kun/items/0667127a3b4223183936)では，***ペリフェラル***全般とか，*`GPIO`****ペリフェラル***について説明しました．
[実装編2](https://qiita.com/lenna_kun/items/dfa21e8eddea433143ba)では，*`SPI`****ペリフェラル***について説明しました．

今回は，前回の続きで，*`TIM`****ペリフェラル***のところから始めていきたいと思います．

筆者も組み込み初心者であるため，説明が行き届かないところもあるかもしれませんが，ご容赦ください．
誤情報だけは書かないように注意を払ってますので，ご安心ください．

#はじめに

コードでは，***Rust***の[***`stm32f30x`***](https://docs.rs/stm32f30x/0.7.0/stm32f30x/index.html)というクレートを用いていますが，本記事では，このクレートをただ利用するだけではなく，使用する構造体や関数が，どのような役目を果たしているかや，本当はどんなことをしているかなど，なるべく***低レベルに降りて***説明するようにしています．
多少***自分に言い聞かせるように***書いてる部分もあり，鬱陶しく感じることがあるかもしれません．そう感じたところはどうぞ読み飛ばしちゃってくださいませ．

では本題に入ります．
ここからは[リファレンスマニュアル](https://www.st.com/resource/en/reference_manual/dm00043574-stm32f303xb-c-d-e-stm32f303x6-8-stm32f328x8-stm32f358xc-stm32f398xe-advanced-arm-based-mcus-stmicroelectronics.pdf)を横に並べて読むことをお勧めします．見るべきページは，適宜示します．

#TIM2

***7セグメントLED***の，表示する桁とLEDを一定時間ごとに切り替えるために，*`TIM2`*っていう***ペリフェラル***を使います．

##電源の投入とリセット

まずは電源とリセット．
教科書*p.152〜*．
`電源`
<img width="628" alt="スクリーンショット 2020-05-05 15.43.21.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/afeecb1e-e4be-fcdb-0f2c-e74df40a727d.png">
<img width="620" alt="スクリーンショット 2020-05-05 15.44.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/fe69f008-773b-3d05-8799-05ef3f17722e.png">
<img width="217" alt="スクリーンショット 2020-05-05 15.45.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/9111273a-e333-0297-75ad-73bf6851bff5.png">
`リセット`
<img width="617" alt="スクリーンショット 2020-05-05 15.47.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/3fadf2bd-304c-6819-e1e6-dcdbc89826a4.png">
<img width="620" alt="スクリーンショット 2020-05-05 15.48.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/c6127b86-ccd2-b49b-88c9-e00d92fc2445.png">
<img width="192" alt="スクリーンショット 2020-05-05 15.49.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/ae6d974b-3662-5f31-2912-90d0989b5407.png">
*`GPIO`*や*`SPI`*のときも似たようなことをしたので，説明は省く．
コードはこちら↓

```rust:timer.rs
    let rcc = unsafe { &*RCC::ptr() };

    rcc.apb1enr.modify(|_, w| w.tim2en().set_bit());
    rcc.apb1rstr.modify(|_, w| w.tim2rst().set_bit());
    rcc.apb1rstr.modify(|_, w| w.tim2rst().clear_bit());
```

##TIM2ペリフェラルの使い方

*`TIM2`****ペリフェラル***の設定を行うために扱うレジスタは4つある．
が，その前にまずはこの*`TIM2`*の使い方から説明します．

*`TIM2`*のカウンタを有効にすると，*`TIM2_CNT`*レジスタの値が*`TIM2_ARR`*レジスタの値と等しくなるまで一定時間ごとにインクリメントされ，等しくなると*`TIM2_SR`*レジスタの0bit目（*`uif`*）が*1*になる．
というような動作をするので，*`TIM2_SR`*の*`uif`*を監視することにより，時間を測ることができる．
これを利用して，***7セグメントLED***の，表示する桁とLEDを***一定時間ごとに***切り替えるわけだ．

##TIM2ペリフェラルの設定

というわけで，設定する必要があるのは，*`TIM2_CNT`*レジスタの値がインクリメントされる間隔と，*`TIM2_ARR`*レジスタの値だ．

~~なんか記事が長くなって，無意識に説明が雑になってそうで怖い~~

まずは*`TIM2`*のカウンタを無効にして，さらにカウンタの値も*0*にリセットします．
*p.647〜*と*p.663〜*
<img width="618" alt="スクリーンショット 2020-05-05 18.42.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/24f4e1b9-e234-0383-c1dd-c3f8b1d2d419.png">
<img width="619" alt="スクリーンショット 2020-05-05 18.43.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/bbd283dd-75d0-0110-a28a-893bd5d57a69.png">
<img width="151" alt="スクリーンショット 2020-05-05 18.43.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/6c29e88c-55ff-2e73-47ea-525717bafcf9.png">
>*`TIM2_CR1`*の0bit目(*`CEN`*)を*0*にして，

<img width="620" alt="スクリーンショット 2020-05-05 18.47.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/8b36f734-81aa-a3a9-8e52-67ea236b5d26.png">
<img width="623" alt="スクリーンショット 2020-05-05 18.48.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/d3439c9a-295b-8d84-a9a1-14c1499e7893.png">
>*`TIM2_CNT`*の全ビットを*0*にする．

コードは下↓のようになる．

```rust:timer.rs
    let tim = unsafe { &*TIM2::ptr() };
    tim.cr1.modify(|_, w| w.cen().clear_bit());
    tim.cnt.reset();
```
次に，*`TIM2_PSC`*と*`TIM2_ARR`*レジスタの値を設定する．
*`TIM2_ARR`*については先ほど少し説明したが，*`TIM2_PSC`*との関連性が少し大事なので改めて説明する．

ここで，とりあえず*`TIM2`*に入力されるクロックの周波数を*clk*とおく．
*`TIM2_CNT`*レジスタの値は，毎秒$$\frac{clk}{{\tiny TIM2}PSC+1}$$という周波数で増加する．
*`TIM2_CNT`*の値が*`TIM2_ARR`*の値まで達すると，イベントが発生する（*`TIM2_SR`*レジスタの0bit目（*`uif`*）が*1*になる）．

ここで気づいて欲しいのが，このイベントが発生するまでの時間は，$${\tiny TIM2}ARR\times({\tiny TIM2}PSC+1)$$で決まるということだ．
つまり，***それぞれの値はそれほど大事ではなく，2つを掛け合わせた値が大事***ということだ．この性質をあとで利用するので，頭の片隅に置いておいて欲しい．

では，仮に，*freq*という周波数でこのイベントを発生させたいとしよう．
このとき，*`TIM2_PSC`*と*`TIM2_ARR`*レジスタの値はどのように設定すれば良いのだろうか．
すぐに，
$$\frac{1}{freq} == \frac{{\tiny TIM2}ARR}{\frac{clk}{{\tiny TIM2}PSC+1}}$$
が成り立てばいいということがわかる．
式変形して，
$$\frac{clk}{freq} == {\tiny TIM2}ARR\times({\tiny TIM2}PSC+1)$$
頭に置いといたやつが出てきた．
*`TIM2_PSC`*と*`TIM2_ARR`*のどちらかの値を決めれば，他方も決まるというわけだ．

しかし，適当に決めればいいというものでもない．
こちらを見ていただきたい．
<img width="623" alt="スクリーンショット 2020-05-05 22.18.53.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/5ce9f348-aceb-dc77-7613-74db474ee553.png">
<img width="623" alt="スクリーンショット 2020-05-05 22.28.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/32fad6e5-6930-72ad-117f-6a104acff758.png">
*`TIM2_PSC`*は***16bit***なのだ！（*`TIM2_ARR`*も*`TIM2`*じゃない時は***16bit***．）
だから何？って思った方もいるかもしれないのでちゃんと説明すると，*`TIM2`*入力されるクロックの周波数*freq*は，最大で***64MHz***（多分）．
これを2進数で表すと，***26bit***になる．
つまり，適切な値にしなければ，*`TIM2_PSC`*レジスタのサイズに収まらなくなってしまう．
今回は，より汎用性の高いコードにしたかったので，*`TIM2_PSC`*と*`TIM2_ARR`*がどちらも***16bit***に収まるようなコードにした．それを以下に示す．

```rust:timer.rs
    let ticks = clk / freq;

    let psc = u16((ticks - 1) >> 16).unwrap();
    tim.psc.write(|w| unsafe { w.psc().bits(psc) });

    let arr = u16(ticks / u32(psc + 1)).unwrap();
    tim.arr.write(|w| unsafe { w.bits(u32(arr)) });
```
>*`TIM2_PSC`*の値を，*ticks-1*の***上位16bit***にして，*`TIM2_ARR`*はそれに合わせて決める．
これなら確かにどちらも***16bit***に収まりそう．
（[こちら](https://github.com/japaric/stm32f30x-hal/blob/master/src/timer.rs)のコードを参考にしています．）

あとは，*`TIM2_CEN`*を*1*にしてカウンタを有効にするだけ．

```rust:timer.rs
    tim.cr1.modify(|_, w| w.cen().set_bit());
```
なのだが，1つだけ忘れていることがある．それは，*`TIM2`*に入力されるクロック*clk*の周波数だ．
*p.125*
<img width="493" alt="スクリーンショット 2020-05-05 12.03.04のコピー.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/36d58494-6f8c-a442-bb2c-ee28a903a5f5.png">
*`SPI`*のときにも見たやつですね．
ただ1つ注意するのは，***APB1 prescaler***（***ppre1***）が*1*のとき以外は，最後に2倍されるということ．

*`SPI`*のときにもほとんど同じことをしたので，説明は省略して，いきなりコードを示す．

```rust:timer.rs
const HSI: u32 = 8_000_000;

... // 関係ないので省略

    let rcc = unsafe { &*RCC::ptr() };

    let pllmul_bits = rcc.cfgr.read().pllmul().bits();
    let pllmul: u32 = u32(pllmul_bits + 2);

    let ppre1_bits = rcc.cfgr.read().ppre1().bits();
    let ppre1: u32 = if ppre1_bits & 0b100 == 0 { 1 } else { 1 << (ppre1_bits - 0b011) };
    
    let hpre_bits = rcc.cfgr.read().hpre().bits();
    let hpre: u32 = if hpre_bits & 0b1000 == 0 { 1 } else { 1 << (hpre_bits - 0b0111) };
    
    let sysclk = pllmul * HSI / 2;
    let hclk = sysclk / hpre;
    let pclk1 = hclk / ppre1;

    let clk = pclk1 * if ppre1 == 1 { 1 } else { 2 }; // Don't forget!
```
これで設定の説明は終わり．

##使ってみる

実際に，イベントが発生するまで待つっていうだけの関数を書くとこんな感じになる．

```rust
pub fn wait() {
    let tim = unsafe { &*TIM2::ptr() };
    while tim.sr.read().uif().bit_is_clear() {}

    tim.sr.modify(|_, w| w.uif().clear_bit()); // Don't forget!
}
```
*`TIM2_SR`*レジスタの*`uif`*ビットを*0*に戻すことで，この関数を同じ機能で何回も使えるようにする．

*`TIM2`*のイベントを発生させる周波数を変更するような関数を作っておけば，この↓ように切り替える速さを徐々に速くしたりすることができる．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/285beced-2702-6e44-0939-382dcda6b508.gif" width=320px>

#おわりに
こんだけのことをするのに長々***4***編も書いてしまいました．
でも結構詳しく書いたので，皆さんの役に立てれば幸いです．

全体をまとめたコードは記事には載せていませんが，あとは***4***編分の内容を組み合わせるだけです．
一応冒頭にgithubのリンクも貼ってますから，参考にしたい方はどうぞm(_ _)m．

拙い文章をこんなところまで読んでいただき本当にありがとうございました．