[STM32] PVDによるマイコン電源電圧の監視

**この記事は、[高知工科大 Advent Calendar 2017](https://adventar.org/calendars/2305)の12日目の記事です。**

#はじめに

Mbedからマイコンを始めた私ですが、最近はSTM32を少しずつ勉強しています。
そんな中で便利そうだなと思った機能について、HALの日本語記事があまりなさそうだったので紹介します。


#概要
STM32 HALによる、**PVD(Programmable Voltage Detection: プログラマブル電圧検出器)**を用いたマイコン電源電圧の監視。


#PVDについて

ざっくりいえば、電源電圧が設定した値を 下回った or/and 上回った 場合にイベントを発生させる機能です。

- マイコンのVDD電圧を監視
- イベント発生時の動作をカスタマイズできる
- 2.2-2.9Vの範囲でしきい値を設定可能

![4531_20171208_STM32F303_リファレン.png](https://qiita-image-store.s3.amazonaws.com/0/128470/bc7a0bdc-921e-90bf-929c-e544de910cee.png)


##API

###構造体
**PWR_PVDTypeDef**
<dl><dt>- PVDLevel</dt><dd>PWR_PVDLEVEL_0 &nbsp; (2.2V)</dd><dd>PWR_PVDLEVEL_1 &nbsp; (2.3V)</dd><dd>PWR_PVDLEVEL_2 &nbsp; (2.4V)</dd><dd>PWR_PVDLEVEL_3 &nbsp; (2.5V)</dd><dd>PWR_PVDLEVEL_4 &nbsp; (2.6V)</dd><dd>PWR_PVDLEVEL_5 &nbsp; (2.7V)</dd><dd>PWR_PVDLEVEL_6 &nbsp; (2.8V)</dd><dd>PWR_PVDLEVEL_7 &nbsp; (2.9V)</dd>
<dt>- Mode</dt><dd>PWR_PVD_MODE_NORMAL</dd><dd>PWR_PVD_MODE_IT_RISING</dd><dd>PWR_PVD_MODE_IT_FALLING</dd><dd>PWR_PVD_MODE_IT_RISING_FALLING</dd><dd>PWR_PVD_MODE_EVENT_RISING</dd><dd>PWR_PVD_MODE_EVENT_FALLING</dd><dd>PWR_PVD_MODE_EVENT_RISING_FALLING</dd></dl>

###関数
<dl><dt>HAL_PWR_ConfigPVD(<i>PWR_PVDTypeDef* sConfigPVD</i>)</dt><dd>PWR_CRレジスタに設定を書き込む</dd><br>
<dt>HAL_PWR_EnablePVD(<i>void</i>)</dt><dd>PVDの有効化</dd><br>
<dt>HAL_PWR_DisablePVD(<i>void</i>)</dt><dd>PVDの無効化</dd><br>
<dt>HAL_PWR_PVDCallback(<i>void</i>)</dt><dd>コールバック関数</dd></dl>

#PVDの使い方

今回は割り込みを用いて、電圧が下回っている場合にLEDが点灯するようにしました。

はじめに、CubeMXでPVD割り込みを有効にします。
設定は、Configuration > System > NVIC で現れるダイアログの、NVICタブにある`PVD interrupt through EXTI line 16`の右横に:white_check_mark:チェックを入れることで有効になります。
![4535_20171212__Unknown_LI.jpg](https://qiita-image-store.s3.amazonaws.com/0/128470/b5ee650b-820c-e033-9820-5e4e30b695ff.jpeg)

コードを生成して、処理を書き加えます。

こんなかんじになりました。CubeMXの初期化関数風に書いてみましたがどうでしょう。

```c
#define PWR_CSR_PVDO 0x04 /* CSR PVDO bit */

static void PVD_Init(void) {

	PWR_PVDTypeDef PVD_InitStruct;

	/* PWR Interface Clock Enable */
	__HAL_RCC_PWR_CLK_ENABLE();

	/* Configure PVD */
	PVD_InitStruct.PVDLevel = PWR_PVDLEVEL_7; // 2.9V
	PVD_InitStruct.Mode = PWR_PVD_MODE_IT_RISING_FALLING;
	HAL_PWR_ConfigPVD(&PVD_InitStruct);

	HAL_PWR_EnablePVD();
}

void HAL_PWR_PVDCallback() {
	/* Indicate PVDO bit Status */
	HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, (PWR->CSR & PWR_CSR_PVDO));
}
```

`__HAL_RCC_PWR_CLK_ENABLE()`でPWRインターフェイスにクロックを供給します。
私はこれが必要なことを知らず、しばらく悩みました:innocent:
クロックを供給しないと、設定レジスタに書き込みができません。

構造体の各フィールドに設定する値を入力し、`HAL_PWR_PVDCallback`でレジスタに設定を書き込みます。

最後に、`HAL_PWR_EnablePVD`でPVDを有効化します。

今回は、Modeを`PWR_PVD_MODE_IT_RISING_FALLING`としているので、電圧がしきい値を下回った場合と上回った場合に割り込みが起こります。
コールバック関数内では、PVDOレジスタを読み、レジスタの値をそのままLEDに出力しています。

<br>

正常に設定できているかを確認するためには、プログラムを起動してデバッガでレジスタを覗きます。

まず、PWRにクロックが供給されているかを確認します。
APB1ペリフェラルクロック有効レジスタ(RCC > APB1ENR)内のPWREN[bit 28]が1となっていればOKです。

![4534_20171212_workspace_v2.0 (2).png](https://qiita-image-store.s3.amazonaws.com/0/128470/9082cd30-7b9d-dca8-bbe0-172c7efe22a4.png)


次に、PVDの設定を確認します。
電圧制御レジスタ(PWR > CR)内のPVDE[bit 4]が1となっていればPVDが有効です。また、PLS[bit 7-5]はPVDのしきい値電圧設定ビットで、000から111の間でそれぞれ2.2Vから2.9Vのしきい値電圧に対応しています。今回は2.9Vに設定しているので、111となっていれば正常です。

![4533_20171212_workspace_v2.0_LI.jpg](https://qiita-image-store.s3.amazonaws.com/0/128470/f1527d1f-c911-14e0-5947-59fc8f0d9d4e.jpeg)




##動かしてみた

###条件
- STM32F303RET6
- STM32CubeF3 1.9.0
- SW4STM32 + OpenOCD
- しきい値 : 2.9V
- 立ち上がり/立ち下がり割り込み

マイコンを載せた基板に安定化電源から電源を供給し、マルチメータでマイコン電源電圧を確認しながら安定化電源の電圧を上げ下げしてみました。

###結果
しきい値の上から徐々に電圧を下げていくと、2.85VでLEDが点灯。その後、徐々に電圧を上げていくと、2.90VでLEDが消灯しました。

###考察
リファレンスマニュアルには `100mVのヒステリシス` とありますが、実際のヒステリシスは50mV程度でした。HALのドキュメントには _PVD threshold **around** 2.9 V_ と書いてあるので、PVDは正確な電圧を検知するものではないということなのでしょう。
![4531_20171208_STM32F303_リファレン.png](https://qiita-image-store.s3.amazonaws.com/0/128470/bc7a0bdc-921e-90bf-929c-e544de910cee.png)

###他のモードについて
ソースコード中のModeをNormal、Eventにして、他の条件は変えずに同様に実験をしてみました。

Normalはコールバックが発生しなかったので、割り込みではなくポーリングしたい場合に使用するのかな？
また、Eventもコールバックが発生しませんでした。Eventはなんのためのモードなのでしょう…（誰か教えて）

#まとめ
PVDによる電圧監視を実装できた。
HALへの理解を深めた。

#あとがき
今年はAdvent Calendarの期日に間に合わせることができました！:confetti_ball:
公式ドキュメントの絶妙な不親切さに翻弄されながらも、動かせるところまで持っていけたので良かったです。

実は、本当にやりたかったネタは別にあったりするのですが、、、いい具合に煮詰まったら投稿したいと思います。

誤字脱字、内容の間違い等あればご指摘いただければありがたいです。

#参考資料
* [RM0316 リファレンスマニュアル - STM32F303xB/C/D/E, STM32F303x6/8, STM32F328x8,STM32F358xC, STM32F398xE advanced ARM®-based MCUs](http://www.st.com/content/ccc/resource/technical/document/reference_manual/4a/19/6e/18/9d/92/43/32/DM00043574.pdf/files/DM00043574.pdf/jcr:content/translations/ja.DM00043574.pdf)
* [UM1786 User Manual - Description of STM32F3 HAL and low-layer drivers](http://www.st.com/content/ccc/resource/technical/document/user_manual/a6/79/73/ae/6e/1c/44/14/DM00122016.pdf/files/DM00122016.pdf/jcr:content/translations/en.DM00122016.pdf)