はじめに:なぜAD変換を学ぶ必要があるのか

センサーから出力されるアナログ電圧信号——温度センサの出力、光センサの明るさ、加速度センサの傾き——これらを「数値」として読み取り、処理するために必要不可欠な技術がAD変換(Analog-to-Digital Conversion) です。

デジタルマイコンであるArduinoは、そのままではアナログ信号を理解できません。そこで、内蔵のADコンバータ(ADC)がアナログ電圧を0〜1023の整数値に変換し、プログラムで扱えるようにします。

本記事では、可変抵抗(ボリューム)を使って電圧を変化させ、その値をAD変換で読み取り、LEDの明るさ(PWM制御)シリアル通信の両方で可視化する実験を行います。単なるLチカではなく、アナログ→デジタル→制御の一連の流れを体験できる、センサー工作の第一歩となる内容です。

Arduino UNO R4 Minima

Arduino UNO R4 Minima

Arduino UNO R4 Minima starter kit

Arduino UNO R4 Minima starter kit


Arduino UNO R4について

本記事で使用するArduino UNO R4は、従来のR3からルネサスの32bit ARM Cortex-M4マイコン(RA4M1)に刷新され、処理能力が大幅に向上しました。しかし、ピン配置や基本的なADC機能はR3と完全互換なので、この実験はR3でもそのまま動作します。

Arduino UNO R4の主要スペック

  • マイコン: Renesas RA4M1 (Cortex-M4, 48MHz)
  • 動作電圧: 5V
  • ADC分解能: 10bit(0〜1023)
  • アナログ入力ピン: A0〜A5(6チャンネル)
  • PWM出力ピン: D3, D5, D6, D9, D10, D11(6ピン)

AD変換(ADC)の技術仕様

ArduinoのADCの特性

Arduino UNOのADコンバータは10bit分解能を持ちます。これは、基準電圧(デフォルトで5V)を1024段階(0〜1023)に分割して測定できることを意味します。

分解能の計算:

電圧ステップ = 5V ÷ 1024 = 0.00488V ≒ 4.88mV

つまり、約4.88mV単位で電圧を検出できます。例えば、ADC値が512なら:

電圧 = 512 × (5V / 1024) = 2.5V

AD変換の内部動作原理(SAR ADC)

ArduinoのADCはSAR(Successive Approximation Register)方式、日本語で逐次比較型ADCと呼ばれる仕組みを採用しています。これは、「バイナリサーチ(二分探索)」のように、基準電圧を半分ずつ区切りながら入力電圧を特定していく方法です。

変換プロセスの仕組み

10bitの場合、10回の比較で電圧値を確定します。以下は、入力電圧が3.2Vの場合の例です:

ステップ1:最上位ビット(MSB)の判定

比較電圧 = 5V ÷ 2 = 2.5V
入力電圧(3.2V) > 2.5V → ビット9 = 1
範囲を 2.5V〜5V に絞る

ステップ2:次のビットの判定

比較電圧 = 2.5V + (5V - 2.5V) ÷ 2 = 3.75V
入力電圧(3.2V) < 3.75V → ビット8 = 0
範囲を 2.5V〜3.75V に絞る

ステップ3:さらに絞り込み

比較電圧 = 2.5V + (3.75V - 2.5V) ÷ 2 = 3.125V
入力電圧(3.2V) > 3.125V → ビット7 = 1
範囲を 3.125V〜3.75V に絞る

このプロセスを10回繰り返すことで、最終的に0〜1023の整数値が得られます。

ハードウェア構成

SAR ADCは以下の主要部品で構成されています:

  1. コンパレータ(比較器)

    • 入力電圧と比較電圧を比較し、大小を判定
    • 出力はHIGH(1)またはLOW(0)の2値
  2. DAC(Digital-to-Analog Converter)

    • 内部で生成したデジタル値を比較用のアナログ電圧に変換
    • 各ステップで基準電圧を生成
  3. SAR(逐次比較レジスタ)

    • 比較結果を記憶し、次の比較電圧を計算
    • 10bitなら10個のビットを順次確定
  4. サンプル&ホールド回路

    • 測定開始時に入力電圧を一時的に保持
    • 比較中に入力電圧が変動しても正確に測定

変換時間と精度のトレードオフ

Arduino UNO(ATmega328P)のADCは、システムクロック(16MHz)を分周して動作します。

ADCクロック周波数:

ADCクロック = 16MHz ÷ 128(プリスケーラ) = 125kHz

1回の変換時間:

変換時間 = 13クロックサイクル ÷ 125kHz ≒ 104μs

この「13クロックサイクル」の内訳は:

  • サンプリング:1.5クロック
  • 変換(10bit比較):10クロック
  • その他処理:1.5クロック

高速化のための注意点: プリスケーラを小さくすれば高速化できますが、ADCクロックが200kHzを超えると精度が低下します。Arduino標準設定(128分周)は、速度と精度のバランスを取った設定です。

バイナリサーチとの類似性

プログラミングの二分探索アルゴリズムと同じ原理です:

// バイナリサーチの疑似コード
int low = 0, high = 1023;
while (low < high) {
  int mid = (low + high) / 2;
  if (入力電圧 > mid に対応する電圧) {
    low = mid + 1;  // 上半分を探索
  } else {
    high = mid;     // 下半分を探索
  }
}
return low;  // 最終的なADC値

この探索をハードウェア(アナログ回路)で高速実行しているのがSAR ADCです。

ADC値と実電圧の対応表

ADC値 電圧(V) 割合
0 0.00 0%
256 1.25 25%
512 2.50 50%
768 3.75 75%
1023 5.00 100%

AREF(基準電圧)について

Arduinoには AREF ピンがあり、これに外部電圧を接続することでADCの基準電圧を変更できます。例えば、3.3Vを基準にすれば、より低電圧範囲を高精度に測定可能です。

⚠️ 注意: AREFを使用する際は、プログラム内で analogReference(EXTERNAL) の設定が必要です。未設定のままAREFに電圧を印加するとマイコンが破損する恐れがあります。


実験の構成:可変抵抗とPWM制御

今回の実験では、以下の機能を実装します:

  1. 可変抵抗(ボリューム) で電圧を0〜5Vの範囲で調整
  2. AD変換(A0ピン) でアナログ電圧を読み取り
  3. PWM出力(D9ピン) でLEDの明るさを制御
  4. シリアル通信 で電圧値をPCに送信(100msごと)

これにより、「アナログ入力→デジタル処理→アナログ出力(PWM)」という、センサー工作の基本フローを体験できます。


回路設計と実装

必要な部品

部品名 仕様 数量
Arduino UNO R4(またはR3) 5V動作 1
可変抵抗(ボリューム) 10kΩ 1
LED 赤色(Vf=1.8〜2.2V) 1
カーボン抵抗 220Ω(電流制限用) 1
ブレッドボード 標準サイズ 1
ジャンパー線 適量 -

回路図

可変抵抗とLEDを使ったArduino回路

AD変換とPWM制御の回路図

配線のポイント

可変抵抗の接続

可変抵抗は3端子(両端と中央)を持ちます:

  • 端子1: 5V(Arduino VCC)
  • 端子2: A0ピン(Arduino)
  • 端子3: GND(Arduino GND)

ボリュームを回すと、中央端子の電圧が0V〜5Vの範囲で連続的に変化します。

LEDの接続

  • D9ピン(PWM出力) → 220Ω抵抗 → LEDアノード(+) → LEDカソード(−) → GND

電流制限抵抗(220Ω)の計算:

LEDの順方向電圧を Vf = 2.0V、推奨電流を 20mA とすると:

R = (5V - 2.0V) / 0.02A = 150Ω

220Ωを使用すれば、電流は約13.6mAに制限され、LEDの寿命を延ばせます。


プログラムコード

以下のコードは、AD変換値を電圧に変換し、PWM制御とシリアル通信で可視化します。

#include <MsTimer2.h>  // タイマー割り込みライブラリ

const int LED_PIN = 9;    // PWM出力ピン
int adcValue = 0;         // AD変換値(0〜1023)
float voltage = 0.0;      // 電圧値(V)

// タイマー割り込み処理(100msごとに実行)
void timerInterrupt() {
  voltage = adcValue * 5.0 / 1023.0;  // ADC値を電圧に変換
  Serial.print("ADC: ");
  Serial.print(adcValue);
  Serial.print(" | Voltage: ");
  Serial.print(voltage, 3);  // 小数点以下3桁まで表示
  Serial.println(" V");
}

void setup() {
  pinMode(LED_PIN, OUTPUT);
  
  Serial.begin(9600);
  while (!Serial) {
    ; // シリアルポートの接続を待機(R4 Minimaで必要)
  }
  
  // タイマー割り込みの設定(100ms間隔)
  MsTimer2::set(100, timerInterrupt);
  MsTimer2::start();
  
  Serial.println("AD Conversion Started");
  Serial.println("ADC | Voltage (V)");
  Serial.println("----+-------------");
}

void loop() {
  // A0ピンからアナログ値を読み取り(0〜1023)
  adcValue = analogRead(A0);
  
  // ADC値をPWM値に変換(0〜255)
  int pwmValue = adcValue / 4;  // 1023 ÷ 4 ≒ 255
  
  // PWM出力でLEDの明るさを制御
  analogWrite(LED_PIN, pwmValue);
  
  delay(10);  // 安定化のための短い遅延
}

プログラムの動作原理

1. AD変換の実行(loop関数)

adcValue = analogRead(A0);

analogRead() 関数は、指定したアナログピン(A0〜A5)の電圧を読み取り、0〜1023の整数値で返します。

変換時間は約100μs(マイクロ秒)と高速です。

2. PWM値への変換

int pwmValue = adcValue / 4;

ADC値(0〜1023)をPWM値(0〜255)に変換します。analogWrite() は0〜255の範囲で動作するため、4で割って範囲を調整します。

  • ADC = 0 → PWM = 0(消灯)
  • ADC = 512 → PWM = 128(約50%の明るさ)
  • ADC = 1023 → PWM = 255(最大輝度)

3. PWM出力

analogWrite(LED_PIN, pwmValue);

analogWrite() 関数は、PWM(Pulse Width Modulation)信号を出力します。Arduino UNO R4では約490Hzの周波数で、デューティ比を0〜100%(0〜255の値で指定)で制御できます。

デューティ比の例:

  • 0:常にLOW(0V) → LED消灯
  • 128:50%の時間HIGH(2.5V相当) → LED半輝度
  • 255:常にHI(5V) → LED最大輝度

4. タイマー割り込みによる定期データ送信

MsTimer2::set(100, timerInterrupt);

MsTimer2 ライブラリを使用して、100msごとtimerInterrupt() 関数を実行します。

なぜタイマー割り込みが必要なのか?

  • loop() 内でシリアル通信を行うと、実行速度にバラつきが生じる
  • データ送信間隔が不安定になり、定量的な分析ができない
  • 割り込みを使えば、正確な時間間隔でデータを取得できる

これは、センサーデータのロギングや計測器開発で重要な技術です。


MsTimer2ライブラリのインストール

MsTimer2.h を使用するには、Arduino IDEでライブラリをインストールする必要があります。

インストール手順

  1. Arduino IDEを起動
  2. メニューバーから「スケッチ」→「ライブラリをインクルード」→「ライブラリを管理…」を選択
  3. ライブラリマネージャが開くので、検索欄に「MsTimer2」 と入力
  4. 「MsTimer2 by Javier Valencia」 を選択し、「インストール」をクリック
Arduinoへのライブラリ導入画面

ライブラリマネージャの起動方法

MsTimer2ライブラリのインストール画面

MsTimer2ライブラリの検索とインストール

インストールが完了すると、#include <MsTimer2.h> が使用可能になります。

Arduino UNO R4での注意点

Arduino UNO R4では、従来のR3で使われていた Timer2 の実装が異なる場合があります。もし MsTimer2 が動作しない場合は、以下の代替案を検討してください:

代替案1:millis()を使ったタイマー実装

unsigned long previousMillis = 0;
const long interval = 100;  // 100ms

void loop() {
  unsigned long currentMillis = millis();
  
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    // 100msごとの処理をここに記述
    voltage = adcValue * 5.0 / 1023.0;
    Serial.println(voltage);
  }
  
  adcValue = analogRead(A0);
  analogWrite(LED_PIN, adcValue / 4);
}

代替案2:IntervalTimerライブラリ(R4推奨)

R4シリーズでは、より高精度な IntervalTimer が使える可能性があります(要確認)。


シリアル通信の出力確認

プログラムをアップロード後、シリアルモニタを開いて動作を確認します。

シリアルモニタの開き方

  1. Arduino IDEの右上にある虫眼鏡アイコン(シリアルモニタ)をクリック
  2. 右下のボーレート設定を9600 baudに設定
  3. ボリュームを回すと、リアルタイムで電圧値が表示されます

出力例:

AD Conversion Started
ADC | Voltage (V)
----+-------------
ADC: 0 | Voltage: 0.000 V
ADC: 256 | Voltage: 1.252 V
ADC: 512 | Voltage: 2.504 V
ADC: 768 | Voltage: 3.757 V
ADC: 1023 | Voltage: 5.009 V

動作確認と測定精度

実機での動作確認

YouTube動画で実際の動作をご覧ください:

動作のポイント:

  • 赤色の線: ボリュームから出力されるアナログ電圧
  • 黄色の線: LEDに入力されるPWM信号

電圧が変化すると、PWMのパルス幅(デューティ比) が比例して変化し、LEDの明るさが滑らかに変わります。

測定精度の検証

実際にテスターで測定した電圧と、Arduino のADC値を比較すると:

テスター測定値 ADC値 Arduino計算値 誤差
1.25V 256 1.252V +0.002V
2.50V 512 2.504V +0.004V
3.75V 768 3.757V +0.007V

約0.01V以内の誤差で測定できており、一般的な電子工作には十分な精度です。

PWM周波数の測定

オシロスコープで測定すると、Arduino UNO R4のPWM周波数は約490Hzです。人間の目には点滅に見えず、滑らかな明るさ調整として認識されます。


応用例とセンサーへの展開

この回路とプログラムの応用例を紹介します。

1. 温度センサー(LM35)との組み合わせ

LM35温度センサーは、温度に比例した電圧(10mV/℃)を出力します。

voltage = adcValue * 5.0 / 1023.0;
temperature = voltage * 100.0;  // 温度(℃)に変換
Serial.print("Temperature: ");
Serial.print(temperature);
Serial.println(" °C");

2. 光センサー(CdSフォトセル)

明るさによって抵抗値が変化するCdSセルを使えば、自動調光LEDを実現できます。

int brightness = map(adcValue, 0, 1023, 255, 0);  // 明るいほどLED暗く
analogWrite(LED_PIN, brightness);

3. バッテリー電圧監視

分圧回路を使えば、リチウムイオン電池(3.7V〜4.2V)の電圧監視も可能です。

float batteryVoltage = voltage * 2.0;  // 分圧回路の倍率
if (batteryVoltage < 3.3) {
  Serial.println("Battery Low!");
}

トラブルシューティング

問題1:電圧が安定しない(ノイズ)

原因: アナログ入力がフローティング状態、または配線の接触不良

対策:

  • プルアップ/プルダウン抵抗を追加
  • 複数回測定して平均化する
int sum = 0;
for (int i = 0; i < 10; i++) {
  sum += analogRead(A0);
  delay(1);
}
adcValue = sum / 10;  // 10回の平均

問題2:PWMでLEDがちらつく

原因: PWM周波数が低い、または電源電圧が不安定

対策:

  • 安定化電源を使用
  • コンデンサ(100μF)をVCCとGND間に接続

問題3:シリアル通信が文字化けする

原因: ボーレートの不一致

対策:

  • シリアルモニタとプログラムのボーレートを9600に統一
  • Arduino UNO R4では while (!Serial) {} で初期化を待つ

まとめ

本記事では、ArduinoのAD変換機能を使って以下を実現しました:

10bit ADCによる電圧測定(分解能4.88mV)
PWM制御によるLED調光(0〜255段階)
タイマー割り込みによる定期データ送信(100ms間隔)
シリアル通信による可視化

この技術は、温度センサー、光センサー、圧力センサーなど、あらゆるアナログセンサーを扱う基礎となります。「アナログ信号をデジタル化し、処理し、出力する」というフローは、IoTデバイスやロボット制御の核心です。

次のステップとして、I2Cデジタルセンサーやより高精度な16bit ADC(ADS1115など)の活用も検討してみてください。

See you…