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

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

今回は，実際にマイコンに書き込むプログラムの***Rust***での書き方について，実際のコードなども見せながらなるべく詳しく説明していきます．

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

#はじめに

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

#ペリフェラル

マイコンには，***CPU***や***メモリ***などが搭載されている．
***ROM***にプログラムを書き込めば，***CPU***がそれを実行してくれるわけだが，ただこれだけでは，演算を行うだけで，今回のようにLEDを光らせたり信号を出力したりということはできない．

そこで，信号の入出力を行ったりする際には，***<font color="#0080FF">ペリフェラル（周辺回路）</font>***を利用する．
***ペリフェラル***とは，ある特定のことを行う機能を持った回路．
というよりは，そのことを行うことだけが目的の回路．

マイコンには，様々な***ペリフェラル***が搭載されている．
>例: *`GPIO`*，*`USART`*，*`CAN`*，*`I2C`*，*`TIM`*，etc.

これら***ペリフェラル***の機能は，マイコンの各ピンに割り当てられている．
しかし，電力を節約するため，初期状態では，ほとんどの***ペリフェラル***には，電源が投入されていない．つまり動作していない．

今回使う***ペリフェラル***は*`GPIOA`*，*`GPIOD`*，*`SPI1`*，*`TIM2`*の4つなので，まずはこれらに***電源を投入***するのがファーストステップ．

#GPIO

*`GPIO`*は，***General-purpose Input/Output（汎用入出力）***つまり，デジタルの入出力を行うための***ペリフェラル***．
今回は，[概要編](https://qiita.com/lenna_kun/items/b49c743d401d113cb949)でも述べたように，7ピンをデジタル出力として使うので，*`GPIO`****ペリフェラル***に電源を投入する必要がある．

stm32f3discoveryには，*`GPIOx`*（$x=A,B,...,F$）の6個の*`GPIO`****ペリフェラル***があるが，機能は同じ．

今回は*PA4*（ポートAの4番ピン），*PA5*，*PA7*，*PD1*，*PD2*，*PD3*，*PD4*の7ピンを使うので，*`GPIOA`*と*`GPIOD`*を使う．

*PD1*〜*PD4*は7セグLEDの桁の選択，つまり***コモン端子***への入力に使う．
*PA4*，*PA5*，*PA7*は***SPI***バスに使う．
*PD1〜PD4，PA4*に割り当てたのは，配線の都合だが，*PA5*，*PA7*に割り当てるのには理由がある．
*`SPI1`****ペリフェラル***の*`SCK`*線と*`MOSI`*線として使えるピンは限られているからだ．詳しくは後程説明する．

##電源の投入とリセット
*`GPIOA`*と*`GPIOD`*に電源を投入し，リセットするためには，どうすればいいんだろうか．その答えは，[リファレンスマニュアル](https://www.st.com/resource/en/reference_manual/dm00043574-stm32f303xb-c-d-e-stm32f303x6-8-stm32f328x8-stm32f358xc-stm32f398xe-advanced-arm-based-mcus-stmicroelectronics.pdf)の*p.148〜*と*p.158〜*にある．
<div style="float:left;">
`電源`
<img width="623" alt="スクリーンショット 2020-05-04 13.02.47.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/132988e7-ab71-6e15-b55b-1fda3f8629e6.png">
<img width="623" alt="スクリーンショット 2020-05-04 13.02.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/003ec69d-da1e-a370-0129-d3993ed9fc54.png">
<img width="234" alt="スクリーンショット 2020-05-04 12.11.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/d60f1bcd-86b8-7649-f79d-b1835e1696c5.png"><img width="234" alt="スクリーンショット 2020-05-04 12.17.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/d3083b4d-a391-926e-52cd-6a9d5497cfea.png">
`リセット`
<img width="623" alt="スクリーンショット 2020-05-04 13.03.52.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/86f67f2d-556c-fd98-08d1-93655b33b929.png">
<img width="623" alt="スクリーンショット 2020-05-04 13.04.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/2672fb0a-0b34-a582-fec0-c8d1d0c1f39d.png">
<img width="205" alt="スクリーンショット 2020-05-04 12.19.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/f1fc9366-4de3-6d49-de90-ac7220403aa6.png"><img width="205" alt="スクリーンショット 2020-05-04 12.20.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/d84d706f-09da-b3b8-0e2a-68b2daad7316.png">
</div>
*`GPIOA`*ペリフェラルを使うためには，*`RCC`*ペリフェラルの*`AHBENR`*っていうレジスタの，17bit目を*1*にし，*`AHBRSTR`*っていうレジスタの，17bit目を*1*にすればいいというわけですね．
（リセットの方は*0*に戻さないとリセットされ続ける．）
コードはこちら↓

```rust:gpio.rs
use stm32f30x::RCC;

pub fn activate_gpioa() {
    let rcc = unsafe { &*RCC::ptr() }; // 1073876992

    rcc.ahbenr.modify(|_, w| w.iopaen().enabled());
    rcc.ahbrstr.modify(|_, w| w.ioparst().set_bit());
    rcc.ahbrstr.modify(|_, w| w.ioparst().clear_bit());
}
```
そのまんま！
[***`stm32f30x`***](https://docs.rs/stm32f30x/0.7.0/stm32f30x/index.html)クレートのおかげで，抽象化されてわかりやすくなってるわけですね．何bit目とか気にしなくても，簡単にコードが書けちゃいます！
[***`stm32f30x`***](https://docs.rs/stm32f30x/0.7.0/stm32f30x/index.html)クレートの中にある構造体とか関数とかが中でどんな実装になっているかは，ライブラリ編（執筆中）で説明します．

##ピンごとにGPIOペリフェラルの設定

使う7つのピン全てに*`GPIO`****ペリフェラル***を繋ぐが，それぞれのピンで，使う機能（***モード***）が違う．

###*push-pull output*モード

*PD1〜PD4，PA4*は，*`push-pull output`*モードで使う．
>デジタル出力には*`open drain output`*というモードもあるのですが，この違いについてはここでは述べません．知りたい方は，[こちらの記事](https://blog.vivita.io/entry/2018/07/23/070000)がわかりやすいです．

*PD1〜PD4，PA4*を，*`push-pull output`*モードで使うためにはどうすればいいのだろうか．答えは*p.237〜*にある．
<img width="623" alt="スクリーンショット 2020-05-04 13.44.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/03d366a6-fafc-b06d-b34d-dcb0cac0306d.png">
<img width="623" alt="スクリーンショット 2020-05-04 13.48.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/d2feb1af-e1b0-e563-7dae-ce7411babf5a.png">
>この*y*っていうのは，何番のピンかってこと．
それぞれ2bitでモードを指定するようだ．*x*番ピンの設定をするには，`2*x`bit〜`2*x+1`bitを***0b01***にすれば良さそうだ．

<img width="623" alt="スクリーンショット 2020-05-04 13.46.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/29e83904-0d6c-eddc-b1bf-f1163f15846d.png">
<img width="623" alt="スクリーンショット 2020-05-04 13.49.17.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/e00b5570-858f-3e42-5b83-cd50f3424321.png">
<img width="409" alt="スクリーンショット 2020-05-04 13.50.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/f44fa6a8-b633-17d8-eec0-36c8b9e42740.png">
>それぞれ1bitでモードを指定するようだ．*x*番ピンの設定をするには，`x`bit目を***0b0***にすれば良さそうだ．

というわけでコードは↓のようになる．（*`GPIOA`*の場合も同様）

```rust:pin.rs
use stm32f30x::GPIOD;

// port D
pub struct PDxL {
    x: u8, // pin number
}

impl PDxL {
    pub fn new(x: u8) -> PDxL {
        PDxL {
            x: x,
        }
    }

    pub fn mode_push_pull_output(&self) {
        let gpiod = unsafe { &*GPIOD::ptr() };

        // general purpose output mode
        let mode = 0b01;
        let offset = 2 * self.x;
        gpiod.moder.modify(|r, w| unsafe {
            w.bits((r.bits() & !(0b11 << offset)) | (mode << offset))
        });

        // push pull output
        let mode = 0b0;
        let offset = self.x;
        gpiod.otyper.modify(|r, w| unsafe {
            w.bits(r.bits() & !(0b1 << self.x))
        });
    }
}
```
（*`PAxL{ x: 1 }`*で，*PA1*を指します．）
>ビット演算に慣れていない方はちょっとわかりづらいかもしれませんね．
*`MODER`*の方を例に，少し説明します．
>
```rust
(r.bits() & !(0b11 << offset)) | (mode << offset)
```
>レジスタの指定したbit以外は，変えたくないわけです．
>
そこで，まず指定したbitだけを*0*にする．
それが`(r.bits() & !(0b11 << offset))`っていうところ．`r.bits()`は，*`MODER`*レジスタの全32bitです．
そのあとで，`(mode << offset)`と*OR*をとれば，指定したbitだけを`mode`に変えられる．

実際にピンを*Low/High*にするには*`GPIOx_BSRR`*というレジスタを使う．
*p.240*．
<img width="618" alt="スクリーンショット 2020-05-05 16.59.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/8fc9b7f5-205d-040d-7096-a3d85fd65d5c.png">
<img width="618" alt="スクリーンショット 2020-05-05 17.01.39.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/ceffe969-1289-87f1-0401-1289d84f093f.png">
>*x*番ピンを*High*にするためには下位16bitの*x*番目を*1*にし，*Low*にするためには上位16bitの*x*番目を*1*にする．

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

```rust
    pub fn set_high(&self) {
        unsafe { (*GPIOD::ptr()).bsrr.write(|w| w.bits(1 << self.x)) }
    }

    pub fn set_low(&self) {
        unsafe { (*GPIOD::ptr()).bsrr.write(|w| w.bits(1 << (16 + self.x))) }
    }
```

###*alternate function*モード

*PA5*，*PA7*は，*`alternate function`*モードで使う．
*PA5*，*PA7*を*`alternate function`*モードで使うためにはどうすればいいのだろうか．答えは*p.237〜*と*p.241〜*にある．
<img width="623" alt="スクリーンショット 2020-05-04 13.44.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/03d366a6-fafc-b06d-b34d-dcb0cac0306d.png">
<img width="623" alt="スクリーンショット 2020-05-04 13.48.38.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/d2feb1af-e1b0-e563-7dae-ce7411babf5a.png">
>それぞれ2bitでモードを指定するようだ．*x*番ピンの設定をするには，`2*x`bit〜`2*x+1`bitを***0b10***にすれば良さそうだ．

<img width="618" alt="スクリーンショット 2020-05-04 15.23.05.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/93ab0270-3f31-610f-c2b8-5e1ed97ff80d.png">
<img width="619" alt="スクリーンショット 2020-05-04 15.25.45.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/ffa21edd-fa3c-7603-c377-e2cedbb15b0f.png">
>それぞれ4bitでモードを指定するようだ．*x*番ピンの設定をするには，`4*x`bit〜`4*x+3`bitをいじれば良さそうだ．
でもよく見ると*7*番ピンまでの設定しかできない感じになっているぞ．
実は，*0〜7*番ピンの*`AF`*の設定はこの*`AFRL`*レジスタで行うが，*8〜15*番ピンの*`AF`*の設定は，*`AFRH`*レジスタで行うようになっている．
レジスタのサイズは32bitで，1つのピンの設定に4bit使うから，2つのレジスタに分けてあるのだ．

ところで，*`AF`*にも番号があるようだが，*PA5*，*PA7*ではどの*`AF`*を使えばいいのだろうか．
その答えは[データシート](https://www.st.com/resource/en/datasheet/stm32f303vc.pdf)（[リファレンスマニュアル](https://www.st.com/resource/en/reference_manual/dm00043574-stm32f303xb-c-d-e-stm32f303x6-8-stm32f328x8-stm32f358xc-stm32f398xe-advanced-arm-based-mcus-stmicroelectronics.pdf)とは別の資料）の*p.45〜*にある．
<img width="756" alt="スクリーンショット 2020-05-04 16.04.30.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/c1ac5a11-dfe8-631a-bfb5-6c92675228e4.png">
<img width="756" alt="スクリーンショット 2020-05-04 15.58.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264330/4399b7df-00a7-f4cc-4ea8-5467908832a6.png">
[概要編](https://qiita.com/lenna_kun/items/b49c743d401d113cb949)でも述べたように，今回は*`MISO`*線は使わないので，*PA5*と*PA7*でそれぞれ*`AF5`*を指定すればいいということがわかる．

というわけでコードは↓のようになる．

```rust:shift.rs
use stm32f30x::GPIOA;

// port A
pub struct PAxL {
    x: u8, // pin number
}

impl PAxL {
    pub fn new(x: u8) -> PAxL {
        PAxL {
            x: x,
        }
    }
    /// Configures the pin to serve as alternate function 5 (AF5)
    pub fn mode_af5(&self) {
        let gpioa = unsafe { &*GPIOA::ptr() };

        let offset = 2 * self.x;

        // alternate function mode
        let mode = 0b10;
        gpioa.moder.modify(|r, w| unsafe {
            w.bits((r.bits() & !(0b11 << offset)) | (mode << offset))
        });

        let af = 5; // = 0b0101
        let offset = 4 * (self.x % 8);
        gpioa.afrl.modify(|r, w| unsafe {
            w.bits((r.bits() & !(0b1111 << offset)) | (af << offset))
        });
    }
}
```
さっきとあまり変わらないので，コードの説明は省く．

*`GPIO`****ペリフェラル***に対して，*PA5*と*PA7*でそれぞれ*`AF5`*を使うよっていう設定までした．しかし，これだけではまだ***SPI***は使えない．
***AF5***を使うことにしただけで，その実態はまだないわけです．
じゃあどうすれば***SPI***を使うことができるのだろうか．

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

長くなってきたので，今回はここで終わり．

#次回

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