１００円マイコン、Ａ／Ｄコンバーターの使い方

![IMG_0142s.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58667/dd367dcc-09e3-ef24-5a0e-9798accaa440.jpeg)


## 内臓Ａ／Ｄコンバーターの使い方

![R8C_ADC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58667/de4d227a-95ea-e7e3-01a6-a39a501ed1ad.png)

R8C/Mxx は、サンプルホールド付き、１０ビットＡ／Ｄコンバーターを内蔵しています。

変換時間も２．２マイクロ秒と高速で、最大６チャネルのアナログ入力をサポートしています。

今回は、このデバイスの基本的な使い方を説明します。
※低価格マイコンで、サンプルホールド付１０ビットＡ／Ｄがあるのは珍しいと思います。

https://github.com/hirakuni45/R8C/tree/master/ADC_sample

## サポートクラス common/adc_io.hpp

R8C/C++ フレームワークでは、A/D 変換を簡単に扱う為、サポートクラスを提供しています。

このクラスを使う事で、簡単にアナログ電圧を変換する事が出来ます。

このクラスは、割り込みに対応していますが、今回は割り込みを使わない方法を示します。

割り込みを使えば、変換時間を有効に使う事が出来ますが、１つのアナログ入力に対して２．２マイクロ秒だと、20MHz で動作する R8C では、４４サイクル分です。
このくらいのコストなら、割り込みで変換通知を受け取らなくても、そんなに大きなハンデとならないと思います。
変換を開始してから、何か簡単な処理を行い、変換結果を得るようにすれば、変換に掛かるコストを相殺出来ます。
多数のアナログ入力を連続して変換するような場合には、割り込みを使い連続して変換し、結果を格納すればパフォーマンスが上がります。

## ハードウェアー（準備）

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58667/e32a0bf9-d7e9-7265-be86-ab4271bd191d.png" width="50%">

- 実験では、2個のボリュームをアナログ入力端子に接続します。

## 定義

```cpp
	// 電源電圧を１０倍した整数を設定
//	constexpr int16_t VCC = 50;    ///< 5.0V (1.25)
	constexpr int16_t VCC = 33;    ///< 3.3V (0.825)

	typedef device::adc_io<utils::null_task> ADC;
	ADC		adc_;
```

※電圧表示用に、マイコンの駆動電圧を設定します。（この例では３．３Ｖ）
※今回、割り込みを使わないので、割り込みから呼ばれる変換型には「utils::null_task」を選択しています。

## 開始

この例では、P1_0（CH0）、P1_1（CH1） をアナログ入力として使います。

```cpp
	// ADC の設定
	{
		utils::PORT_MAP(utils::port_map::P10::AN0);
		utils::PORT_MAP(utils::port_map::P11::AN1);
		adc_.start(ADC::CH_TYPE::CH0_CH1, ADC::CH_GROUP::AN0_AN1, true);
	}
```

## 結果取得と表示

```cpp
			adc_.scan();
			adc_.sync();
			// 「%3.2:8y」は小数点以下 8 ビットの固定小数点を 3 桁、小数点以下 2 桁表示
			//   5V の場合 1.25  倍して、小数点以下 8 ビットで、1023 で   5V 表示となる。
			// 3.3V の場合 0.825 倍して、小数点以下 8 ビットで、1023 で 3.3V 表示となる。
			{
				auto v = adc_.get_value(0);
				utils::format("(%5d) CH0: %3.2:8y[V], %d\n")
					% nnn
					% static_cast<uint16_t>(((v + 1) * VCC) / (1024 * 10 / 256))
					% v;
			}

			{
				auto v = adc_.get_value(1);
				utils::format("        CH1: %3.2:8y[V], %d\n")
					% static_cast<uint16_t>(((v + 1) * VCC) / (1024 * 10 / 256))
					% v;
			}
			++nnn;
```

「scan()」で A/D 変換をスタートして、「sync()」で変換終了を待ちます。

```cpp
		void sync() const {
			while(!get_state()) sleep_();
		}
```

「sync()」関数の実装は上記のようになっているので、何か別の処理をしながら、変換が終了するまで待つ事も出来ます。

「utils::format」は、固定小数点表示をサポートしており、電圧の表示などに便利です。
※浮動小数点を使う事も出来ますが、オブジェクトが大きくなります。

上記の例では、設定した電源電圧のパラメーターを使い、変換された電圧をＸ．ＸＸで表示します。
「%3.2:8y」は、format 独自の固定小数点フォームで、小数点以下８ビットとして整数を扱います。

- ５Ｖの場合、A/D 変換後のデータに１を加えて、１．２５倍しています。
- ３．３Ｖの場合、A/D 返還後のデータを０．８２５倍しています。

ターミナル出力：

```
        CH1: 1.61[V], 500
(  310) CH0: 2.28[V], 708
        CH1: 1.61[V], 500
(  311) CH0: 2.28[V], 709
        CH1: 1.61[V], 500
(  312) CH0: 2.28[V], 709
        CH1: 1.61[V], 500
(  313) CH0: 2.28[V], 707
        CH1: 1.62[V], 501
(  314) CH0: 2.28[V], 708
        CH1: 1.61[V], 500
(  315) CH0: 2.28[V], 708
        CH1: 1.61[V], 500
(  316) CH0: 2.28[V], 708
        CH1: 1.61[V], 500
(  317) CH0: 2.28[V], 708
        CH1: 1.61[V], 500
(  318) CH0: 2.28[V], 709
        CH1: 1.61[V], 500
(  319) CH0: 2.29[V], 710
        CH1: 1.61[V], 500
(  320) CH0: 2.28[V], 708
        CH1: 1.61[V], 500
```

---

## サーミスターテンプレートクラスを使った温度測定

https://github.com/hirakuni45/R8C/tree/master/THERMISTOR_sample

![IMG_0141s.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58667/cae19077-4a1b-e1be-50ba-5de6bab3aff3.jpeg)

サーミスターは、安価で、簡易的に温度を測定するには最適なデバイスです。
但し、直線性が悪く、補正が必要です。
R8C/C++ フレームワークでは、[サーミスター用テンプレートクラス](https://github.com/hirakuni45/R8C/blob/master/chip/NTCTH.hpp)を用意してあり、非直線性を補正した温度を取得する事が出来ます。
※対数計算が含まれていて、浮動小数点を扱うので、実行バイナリーはそれなりに大きくなります。

### 定義

サーミスターは、品種により直線性が異なり、対数計算のパラメーターを選ぶ必要があります。

このテンプレートクラスでは、現在３つの品種を扱えるようにしてあります。
※他の品種を使いたい場合、パラーメーターを追加して下さい、ソースコードを観れば、比較的簡単に品種を増やす事が出来ると思います。

標準で用意されている品種：

```cpp
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief  サーミスタ型
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	enum class thermistor {
		NT103_34G,	///< THB:3435, TR25:10K
		NT103_41G,	///< THB:4126, TR25:10K
		HX103_3380,	///< THB:3380, TR25:10K (25C to 50C)
	};
```

３つのサーミスター型のうち適切な型を選択して、typedef します。
又、分圧抵抗（通常１０キロオーム）を直列に接続し、サーミスターを電源のどちらかに接続します。

- ＶＣＣ側、又は、ＧＮＤ側
- A/D 変換の分解能は１０ビットで、０～１０２３の値が返るので、1023 (1024-1) を指定しています。
- 「TH_OUT」は AN0/P1_0(20) に接続します。

![Thermister_circuit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58667/a4e38379-84a2-593c-0416-39179f892c70.png)


```cpp
	// サーミスタ定義：
	// A/D: 10 bits (1023), HX103_3380, 分圧抵抗: 10K (10000) オーム、サーミスタ: ＶＣＣ側
	typedef chip::NTCTH<1023, chip::thermistor::HX103_3380, 10000, true> THMISTER;
	THMISTER thmister_;
```

### 開始

サーミスターを利用する A/D 変換ポートに接続し、A/D 変換を開始します。
※この例では、P1_0 に接続しています。

```cpp
	// ADC の設定
	{
		utils::PORT_MAP(utils::port_map::P10::AN0);
		adc_.start(ADC::CH_TYPE::CH0, ADC::CH_GROUP::AN0_AN1, true);
	}
```

### 結果表示

A/D 変換された値をサーミスターテンプレートに入れて、補正された温度を取得します。
※温度は、float 型で返ります。

```cpp
			adc_.scan();
			adc_.sync();
			{
				auto v = adc_.get_value(0);
				utils::format("(%5d) CH0: %3.2:8y[V], %d\n")
					% nnn
					% static_cast<uint16_t>(((v + 1) * VCC) / (1024 * 10 / 256))
					% v;

				utils::format("温度： %5.2f [度]\n") % thmister_(v);
			}
```

テンプレートクラスを使うと簡単に扱えます。

ターミナル出力：

```
Start R8C THERMISTOR sample
(    0) CH0: 2.53[V], 517
温度： 25.57 [度]
(    1) CH0: 2.53[V], 517
温度： 25.57 [度]
(    2) CH0: 2.53[V], 517
温度： 25.57 [度]
(    3) CH0: 2.52[V], 516
温度： 25.46 [度]
(    4) CH0: 2.52[V], 515
温度： 25.36 [度]
(    5) CH0: 2.51[V], 514
温度： 25.26 [度]
(    6) CH0: 2.53[V], 517
温度： 25.57 [度]
(    7) CH0: 2.56[V], 523
温度： 26.19 [度]
(    8) CH0: 2.56[V], 523
温度： 26.19 [度]
(    9) CH0: 2.55[V], 522
温度： 26.08 [度]
(   10) CH0: 2.55[V], 522
温度： 26.08 [度]
(   11) CH0: 2.57[V], 525
温度： 26.40 [度]
(   12) CH0: 2.58[V], 528
温度： 26.71 [度]
(   13) CH0: 2.57[V], 525
温度： 26.40 [度]
(   14) CH0: 2.57[V], 526
温度： 26.50 [度]
(   15) CH0: 2.57[V], 526
温度： 26.50 [度]
(   16) CH0: 2.58[V], 527
温度： 26.60 [度]
(   17) CH0: 2.59[V], 530
温度： 26.92 [度]
(   18) CH0: 2.59[V], 530
温度： 26.92 [度]
(   19) CH0: 2.60[V], 532
温度： 27.12 [度]
(   20) CH0: 2.58[V], 528
温度： 26.71 [度]
(   21) CH0: 2.60[V], 531
温度： 27.02 [度]
(   22) CH0: 2.60[V], 532
温度： 27.12 [度]
(   23) CH0: 2.58[V], 528
温度： 26.71 [度]
(   24) CH0: 2.57[V], 526
温度： 26.50 [度]
(   25) CH0: 2.57[V], 525
温度： 26.40 [度]
(   26) CH0: 2.56[V], 524
温度： 26.29 [度]
(   27) CH0: 2.55[V], 522
温度： 26.08 [度]
(   28) CH0: 2.55[V], 522
温度： 26.08 [度]
(   29) CH0: 2.55[V], 522
温度： 26.08 [度]
(   30) CH0: 2.55[V], 521
温度： 25.98 [度]
(   31) CH0: 2.54[V], 520
温度： 25.88 [度]
```

---

## まとめ

A/D 変換の場合、アナログ電圧を扱うので、ノイズや、A/D 変換に係る色々な事象が発生し、実用的に利用するには、色々なノウハウがあります。
R8C/Mxx の場合、マイコンの電源と、A/D 変換で使われるリファレンス電圧が共通になっているので、マイコン側のノイズが変換結果に影響を与えます。
精密な変換を行いたい場合、色々な工夫が必要と思いますが、R8C/Mxx の内蔵 A/D 変換は、ノイズも少なく、かなり良く出来ています。

サンプルホールドがあり、変換時間が短く、分解能も１０ビットあるので、応用次第で、色々なアプリケーションに活用できるものと思えます。

## 参考リンク

次の記事：

https://qiita.com/hira_kuni_45/items/09b4acd4633b4a11d180

前の記事：

https://qiita.com/hira_kuni_45/items/ed0559b4b836d1644f6d

開発環境の構築など：

https://qiita.com/hira_kuni_45/items/ce7c493756daa5fc1a41

format テンプレートクラス：

https://qiita.com/hira_kuni_45/items/790d379034c747e66d40