STM32 Neucleo Boardで割り込みを使う

#STM32 NuleoBoardで割り込みを使う
##はじめに
割り込みは知ってはいるんだけど、イチから作る機会はなかったので、
ちゃんとは理解できていないかも。
ということで、STM32 NucleoBoardの割り込みを使ってみる!
##開発ターゲット
STM32F303K8
##割り込みとは
書籍などでは「実行中のプログラムの実行を一時中止して、特定の処理を行う」といった説明がなされることが多いようだ。
また電話などを例に出して、「今やっている作業を一時中断して電話を受ける。電話が終わったらもともとやっていた作業に戻る」という説明がされることも多い。
![F798623B-83A1-4DD2-BC75-DF553B8BA73D.png](https://qiita-image-store.s3.amazonaws.com/0/140055/ac1d478f-fe5b-bd7e-c716-41a3f4ac01a7.png)
CPUは割り込みを受け付けると、現在処理中のプログラムの実行を中断し、特定の処理に実行を移す。
この割り込み発生時に実行される処理のことを割り込みハンドラと呼ぶ。
割り込みハンドラではその割り込みに応じた処理を行い、その後に中断していた本来の処理に戻る。
割り込みハンドラは前もって登録しておく必要がある。
[スタートアップルーチン](https://qiita.com/mitazet/items/54713ec4e89f0b57ac6e)で割り込みベクタテーブルで登録を行なっている。

##調査
ここでは確認しやすいので、ボタンが押されたことを割り込みで検出する。PB0にボタンを接続することを想定する。
割り込みはCPU種別によりやり方が違うため調査を行った。
###STM32の割り込みの使い方
GPIOポートにボタンを接続し入力を確認するので、GPIOによる割り込みを設定する。
割り込み制御について確認するためにSTM32のReference Manualを参照した。
STM32ではEXTIレジスタで外部割り込みの設定を行うようだ。
###ハードウェア割り込みの選択方法
GPIOを割り込みソースとして設定するには、次の手順になる。
・EXTI_IMRレジスタに対応するマスクビットを設定する。
・割り込みラインのトリガ選択ビット(EXTI_RTSR/EXTI_FTSR)を設定する。
・EXTIライン割り込みを正しく処理できるように、対応したNVIC IRQチャネルを制御する有効化ビットとマスクビットを設定する。
他にも、外部ラインをソフトウェア割り込みとして使用する手順などが載っているが、ここでは使用しない。
###割り込みラインの配置
リファレンスマニュアルにGPIOとEXTIのマッピングが記載されていた。
![exti.png](https://qiita-image-store.s3.amazonaws.com/0/140055/5b2fca1a-e330-03be-9afa-cc4e4f6bd155.png)
これによるとPB0はEXTI0につながっている。
そして、SYSCFG_EXTICR1レジスタのEXTI0[3:0]ビットを設定することで、ソースのピンを決定できるようだ。
PB0を選択するにはEXTI0[3:0]=001にする。

ここまでをプログラムにするとこのような感じだろうか。

```c
void InitEXTI()
{
	SYSCFG->EXTICR[0] = 0x00000001;
	EXTI->IMR = 0x00000001;
	EXTI->RTSR = 0x00000001;
	NVIC_EnableIRQ(EXTI0_IRQn);
}
```

##作る
###接続する
PB0が入力端子となるようにボタンを接続した。
プルアップにしているので、ボタンを押すとPB0への入力がHigh→Lowとなるはず。
![4C78E94C-9327-4402-ADAF-579E392F6742.png](https://qiita-image-store.s3.amazonaws.com/0/140055/7b13a370-9ac6-d62f-5adc-b13ad6cdbf8e.png)

###割り込みブログラムを書く
ソースコード全体は[github](https://github.com/mitazet/stm32/tree/master/v03)においた。
####GPIOの初期化
使用するGPIOピンの初期化の方法自体は通常の初期化の方法と変わらない。
それに加えて、EXTIを使用する場合には、SYSCFGにクロックを供給する必要がある。

```c
static void InitGPIO()
{
    // distibute clock
    RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    
    // GPIOB0 Input mode
    GPIOB->MODER = 0;
}
```

####割り込みの設定
割り込み設定は先ほどと同様である。割り込み発生トリガを立ち上がりエッジにしていたが、
High→Lowを検出したいため、立ち下がりエッジに変更した。設定はEXTI_FTSRレジスタで行う。

```c
static void InitEXTI()
{
    SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI0_PB;
    EXTI->IMR = EXTI_IMR_MR0;
    EXTI->FTSR = EXTI_FTSR_TR0;
    NVIC_EnableIRQ(EXTI0_IRQn);
}
```
####割り込みハンドラ
[スタートアップルーチン](https://qiita.com/mitazet/items/54713ec4e89f0b57ac6e)でEXTI0の割り込みハンドラである`EXTI0_IRQHandler`がweakで宣言されている。
weakというのは「実際に`EXTI0_IRQHandler`というのがあればEXTI0_IRQHandlerはそれにする。なければ`Default_Handler`にする」という意味である。
そのためここで`EXTI0_IRQHandler`を宣言すればこれがEXTI0_IRQHandlerになる。
処理自体は「pushed」と表示するだけのもの。
__割り込みハンドラ内でEXTIペンディングレジスタ(EXTI_PR1)をクリアしないと無限ループになるので注意。__
EXTI_PR1はビットに1を書き込むことによりクリアされる。

```c
void EXTI0_IRQHandler()
{
    EXTI->PR |= EXTI_PR_PR0;
    printf("pushed\n");
}
```
##テスト
###テストプログラム
初期化を行って、無限ループするだけ。
`InitButtonIntr()`で上記の`InitGPIO()`と`InitEXTI()`を呼んでいる。

```c
int main(void)
{
    InitUSART();
    InitButtonIntr();
    init_printf(PutcUSART);

    printf("Button Input Test\n");

    while(1);

	return 0;
}
```
###実行
ボタンを押すと割り込みハンドラが実行された!! (これだけだと非常にわかりずらいですが)
成功だ:grinning:
![E057F385-3D6C-4E4A-A5AF-D9DC70F714CB.png](https://qiita-image-store.s3.amazonaws.com/0/140055/bce03667-6129-398e-010f-a5a93b61ef0d.png)
3回押してみた。

##おわりに
マイコンにより設定方法が違うので、応用はききにくいかもしれない。
しかし、マイコンによってかけ離れていることもないだろう。(たぶん)

##参考サイト
[マイコン徹底入門](http://miqn.net/periph/151.html)
[JellyWare](http://jellyware.jp/kurage/raspi/shutdown_button.html)