RustでSTM32ベアメタル [4桁7セグメントLED]（実装編2）〜SPIペリフェラル〜

***<font color="#0080FF">Rust</font>***という言語で***STM32F3Discovery***というマイコンを動かし，***4桁7セグメントLED***を***ダイナミック点灯方式***で光らせるっていう記事の***3***編目です．
この記事を読み終わるころには***完全に理解した***って言ってもらえるくらい丁寧に書いたつもり．
<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`****ペリフェラル***について説明しました．

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

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

#はじめに

コードでは，***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)を横に並べて読むことをお勧めします．見るべきページは，適宜示します．

#SPI1

***SPI***を用いて通信するため，*`SPI1`****ペリフェラル***を使う．
[前回](https://qiita.com/lenna_kun/items/0667127a3b4223183936)までで，*`GPIO`****ペリフェラル***に対して，*PA5*と*PA7*でそれぞれ*`AF5`*を使うよっていう設定までした．しかし，これだけではまだ***SPI***は使えない．
***AF5***を使うことにしただけで，その実態はまだないわけです．
じゃあどうすれば***SPI***を使うことができるのだろうか．

*`GPIO`****ペリフェラル***には，*`SPI1`****ペリフェラル***が接続されている．この*`SPI1`****ペリフェラル***に電源を投入することで初めて*PA5*と*PA7*で***SPI***バスが使えるようになるのだ．

##電源の投入とリセット
じゃあというわけでまずは電源とリセット．
はい，教科書*p.150〜*．
`電源`
<img width="616" alt="スクリーンショット 2020-05-04 17.06.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/4e942026-590b-5ec2-516f-b5b88483da4d.png">
<img width="618" alt="スクリーンショット 2020-05-04 17.06.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/7afa1067-f429-dd67-7a35-fbd1d7877fcd.png">
<img width="200" alt="スクリーンショット 2020-05-04 17.07.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/a797ab1b-d3ec-cc3d-e4d9-11405b7527ff.png">
`リセット`
<img width="619" alt="スクリーンショット 2020-05-04 17.12.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/62eb9df8-e5df-f8ec-c0cc-0e0f9a3f0a76.png">
<img width="619" alt="スクリーンショット 2020-05-04 17.12.51.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/b232c967-4425-080c-385d-ef003a51aa94.png">
<img width="203" alt="スクリーンショット 2020-05-04 17.13.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/6915c2dc-b405-67ca-494f-6b8f7e077bc1.png">
*`GPIO`*のときも似たようなことをしたので，説明は省く．
コードはこちら↓

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

    rcc.apb2enr.modify(|_, w| w.spi1en().enabled());
    rcc.apb2rstr.modify(|_, w| w.spi1rst().set_bit());
    rcc.apb2rstr.modify(|_, w| w.spi1rst().clear_bit());
```

##SPI1ペリフェラルの設定
*`GPIO`****ペリフェラル***のときはピンごとに設定していたが，*`SPI1`****ペリフェラル***の場合は複数のピンで1つの機能を成すので，それぞれのピンで別々に設定するということはしない．

どんな設定にすればいいかは[概要編](https://qiita.com/lenna_kun/items/b49c743d401d113cb949)でほとんど話したので，端折り気味で説明する．

*`SPI1`****ペリフェラル***の設定を行うレジスタは2つある．

###*SPI1_CR1（SPI control register 1）*

1つ目は*`SPI1_CR1`*．（*p.998〜*）
<img width="618" alt="スクリーンショット 2020-05-04 22.20.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/062db4a5-df8a-732d-32b4-3249fb26138e.png">
<img width="618" alt="スクリーンショット 2020-05-04 22.20.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/1084935b-97f1-5aa8-91bf-a458b4248212.png">
このレジスタで設定すべきなのは，
下から順に*`CPHA`*，*`CPOL`*，*`MSTR`*，*`BR`*，*`SPE`*，*`LSBFIRST`*，*`SSI`*，*`SSM`*，*`BIDIMODE`*の9個．
順番に説明していきます．

*`CPHA`*と*`CPOL`*については[概要編](https://qiita.com/lenna_kun/items/b49c743d401d113cb949)で話した通り*`0`*．↓の画像で思い出せない人は[概要編](https://qiita.com/lenna_kun/items/b49c743d401d113cb949)に戻ってみてください．
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/e3a680b2-8d69-67dd-0b96-90ef7e32481f.png" width=400px>
*`MSTR`*は，このマイコンを***SPI***バスのマスターとして使うかどうかってことなので，もちろん*1*．

*`BR`*は少しややこしいので後回し．

*`SPE`*は*`SPI1`****ペリフェラル***を有効化するかどうかってことなので，もちろん*1*．

*`LSBFIRST`*は，8bitのデータを，先頭から送るか末尾から送るか．これはプログラムをこの設定に合わせればいい話なのでどっちでもいいが，どっちかには決めとかないとプログラムが書けないので好みで*0*（***MSB first***）か*1*（***LSB first***）にする．僕のプログラムでは***MSB first***になっている．

*`SSI`*は，***SPI***バスにおいて，***マスターモード***のときの*`SS`*線の出力を設定しておけばいい？はず？
次に示す，*`SSM`*が*1*のときのみしか機能しない．
（すみません，このbitに関してはあまり理解していません．）
*1*にします．

*`SSM`*は，*`SS`*線の出力の制御を，自分で別に書く場合は*1*．
今回は*1*にする．詳しくはあとで触れる．

*`BIDIMODE`*っていうのは，***SPI***バスで用いる通信方式を選択するためのbit．
実は***SPI***バスには一般的な***全二重3線式***の他にも，***半二重***とかいう方式もあるらしい．詳しく知りたい方は[こちらの記事](https://ednjapan.com/edn/articles/1511/18/news018_2.html)がわかりやすかった．
今回使うのは普通のやつなので，***全二重3線式***．
というわけでこちらは*0*．

最後に，後回しにしていた*`BR`*．
これは，つまりは***ボーレート***のことで，簡単にいうなら通信速度．
単位は*bps*で，1秒間に何bitの情報を送れるかということを表す．
***SPI***通信では，クロックの立ち上がりでデータがサンプルされるため，クロックの周波数と，この***ボーレート***の値は一致する．
がしかし，どんな周波数でも設定できるわけではない．
設定できる周波数は以下のようになっている．
<img width="254" alt="スクリーンショット 2020-05-05 10.24.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/f2ffbad1-5324-d27f-e649-eb35fca106ae.png">
>***pclk1***の分周で設定するようだ．

通信相手の***74HC595***は，***SCK***に設定できるのは，最大で*50MHz*程度まで．
そもそもこのマイコンでは***pclk1***の最大値が*64MHz*なので，*`BR`*には*32MHz*までしか設定できない．なのでこれは気にしなくて良さそう．

話を戻して，仮に*1MHz*に設定したいとしたら，どうすればいいだろうか．
まず，*`BR`*には，***pclk1***の分周という形で設定するため，丁度*1MHz*に設定できるかどうかは***pclk1***の周波数次第ということになる．
***pclk1***の値さえわかれば，*`BR`*の値は，$$\frac{pclk1}{1_\rm{MHz}}$$の値によって決められる．
コードだとこんな感じ↓だ．

```rust:spi.rs
    let freq = 1000_000; // = 1MHz

    let br = match pclk1 / freq {
        0 => unreachable!(),
        1..=2 => 0b000,
        3..=5 => 0b001,
        6..=11 => 0b010,
        12..=23 => 0b011,
        24..=39 => 0b100,
        40..=95 => 0b101,
        96..=191 => 0b110,
        _ => 0b111,
    };
```
さて，残る疑問はただ1つ．
***pclk1***の値はどうやって得られるのか．
教科書*p.126*！
<img width="493" alt="スクリーンショット 2020-05-05 12.03.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/90252346-fd0c-7a97-ec57-e8cf00ca0067.png">
>ふむふむ，*8MHz*の***HSI***とやらを$\frac{1}{2}$倍してそれを***PLLMUL***倍したものが***SYSCLK***で．
それを***AHB prescaler***（***hpre***）で割ったものが***HCLK***．
んでそれを***APB1 prescaler***（***ppre1***）で割ったものが***pclk1***か！

ところで，この***pllmul***とか***hpre***とか***ppre1***ってどこからきてるんだろうか．
教科書*p.138*．
<img width="622" alt="スクリーンショット 2020-05-05 12.32.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/9dbfdf27-03c5-7989-47e5-9c71413eefc9.png">
<img width="622" alt="スクリーンショット 2020-05-05 12.33.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/84473416-9a33-10ea-4b77-44e019cba9ec.png">
<div style="float:left;">
<img alt="スクリーンショット 2020-05-05 12.35.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/1cc94efa-3aab-d0f1-76d0-1269801637ae.png" width="360px"><img alt="スクリーンショット 2020-05-05 12.36.13.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/56d644fe-5c9f-42a8-e369-fac1f8fad812.png" width="360px">
</div>
何度も同じことをやってるので説明は省略します．
コードはこんな感じ↓になる．

```rust:spi.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;
```
*`SPI1_CR1`*についての説明はこれで終わり．
コードはこんな感じ↓．

```rust:spi.rs
    spi.cr1.write(|w| unsafe {
        w.cpha()
            .bit(false)
            .cpol()
            .bit(false)
            .mstr()
            .set_bit()
            .br()
            .bits(br)
            .spe()
            .set_bit()
            .lsbfirst()
            .clear_bit()
            .ssi()
            .set_bit()
            .ssm()
            .set_bit()
            .bidimode()
            .clear_bit()
    });
```

###*SPI1_CR2（SPI control register 2）*

2つ目は*`SPI1_CR2`*．（*p.998〜*）
<img width="623" alt="スクリーンショット 2020-05-05 12.48.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/149455a3-f72d-3b27-ad5c-a7be8b8c7a6f.png">
<img width="619" alt="スクリーンショット 2020-05-05 12.50.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/8180ba2d-0a0d-a562-63f6-2130575a0a43.png">
このレジスタで設定すべきなのは，
下から順に*`SSOE`*，*`DS`*の2個．
<div style="float:left;">
<img width="300" alt="スクリーンショット 2020-05-05 12.57.08.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/7c7078de-b8f1-e12f-c6f3-a20dbce8f6ad.png"><img alt="スクリーンショット 2020-05-05 12.58.20.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/ae9349b2-a33e-51f0-fc9c-2afed199c8f6.png" width="420px">

順番に説明していきます．

*`SSOE`*は，*`SS`*線の出力を有効にするかどうか．
今回は*`SS`*線の出力の制御は，自分で別に書くので無効にする．
というわけで*0*．

*`DS`*は，データサイズ．
今回は8bitなので，***0b0111***．

*`SPI1_CR2`*についての説明はこれで終わり．
コードはこんな感じ↓．

```rust:spi.rs
    spi.cr2.write(|w| unsafe {
        w.ds().bits(0b0111).ssoe().clear_bit()
    });
```

##SPI1ペリフェラルを使ってデータを送信

設定が終わったので，あとは実際にシフトレジスタにデータを送信するだけです．
*p.1004*
<img width="620" alt="スクリーンショット 2020-05-05 16.46.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/ffe7bc45-d260-a972-23cd-ae1389d5c5c2.png">
<img width="620" alt="スクリーンショット 2020-05-05 16.46.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/4efabaaa-1529-ad0f-e8fe-3facee505360.png">
この*`SPI1_DR`*というレジスタに送信したいデータを書き込むだけ．

でも忘れてはいけないのが，*`SS`*線（シフトレジスタの*`RCK`*）．
この信号は自分で別に書くことにしていたので，それも忘れずに．
前回*PA4*に割り当てた*`GPIO`*の*`push-pull output`*で*`SS`*線の制御を行う．
なのでコードはこんな感じ↓．

```rust
const NUMBERS: [u8; 11] = [
    0b11101011, // 0
    0b00101000, // 1
    0b10110011, // 2
    0b10111010, // 3
    0b01111000, // 4
    0b11011010, // 5
    0b11011011, // 6
    0b11101000, // 7
    0b11111011, // 8
    0b11111010, // 9
    0b00000000  // OFF
];

... // 省略

    pub fn display_num(&self, number: usize) {
        self.rck.set_low();
        unsafe { ptr::write_volatile(&(*SPI1::ptr()).dr as *const _ as *mut u8, NUMBERS[number]) };
        self.rck.set_high();
    }
```

これで*`SPI1`****ペリフェラル***についての説明は終わりです．

あとは，***7セグ***の，表示する桁とLEDを一定時間ごとに切り替えるだけです！
そのために，今度は*`TIM2`*っていう***ペリフェラル***を使います．

#次回

というわけで，次回はこの*`TIM`****ペリフェラル***の有効化〜設定について書きます．
拙い文章ですが，ここまで読んでいただきありがとうございました．