はじめに
ここでは、Arduino UNOで アナログ出力 の実験をします。
目次へ戻るには ここ をクリック
※本ページは実験のテキストです。
アナログ出力
前回は、アナログ入力の解説をしました。
動作確認をする際、可変抵抗のツマミを回すとLEDの明るさが変化していたことに気付きましたか?
もう一度動作させて確認しましょう。
アナログ値が1023の時にはLEDは最も明るく、0の時には消灯します。
LEDに出力している電圧が変化しているので、明るさが変化しているのです。
今回は、この機能について解説します。
実際の作業としては
•【ソフトウェアの作業】プログラム解説
だけになります。
スケッチの解説
AnalogInOutSerialのプログラムを再掲しておきます。
/*
アナログ入力, アナログ出力, シリアル出力
アナログ入力ピンの値を読み込み、その結果を0~255の範囲に変換します。
0~255の値は出力ピンのPWM(パルス幅変調)値として使います。
また、シリアルモニターにも結果をプリントします。
回路:
- アナログ入力ピン0に、ポテンショメータ(可変抵抗)
ポテンショメータの中央のピンをアナログ入力ピンに接続する
ポテンショメータの側面のピン達は、+5V と GND に接続する
- LED はデジタル出力ピン9と、GNDに接続する(もちろん電流制限抵抗が必要)
*/
// これらの固定値は変更してはいけません。使用するピンに名前を与えています。
const int analogInPin = A0; // ポテンショメータに接続するアナログ入力ピン
const int analogOutPin = 9; // LEDに接続するアナログ出力ピン
int sensorValue = 0; // ポテンショメータから読み取った値
int outputValue = 0; // アナログ出力でPWM出力する値
void setup() {
// シリアル通信を初期化する。ボーレートは9600bps
Serial.begin(9600);
}
void loop() {
// アナログ入力の値を読む
sensorValue = analogRead(analogInPin);
// アナログ入力の0~1024の値を、アナログ出力の使用範囲0~255に変換する
outputValue = map(sensorValue, 0, 1023, 0, 255);
// アナログ出力値を変更する
analogWrite(analogOutPin, outputValue);
// シリアルモニターに結果をプリントする
Serial.print("sensor = ");
Serial.print(sensorValue);
Serial.print("\t output = ");
Serial.println(outputValue);
// アナログ->デジタル変換のために2ミリ秒のウェイトが必要
delay(2);
}
それでは、それぞれの関数を見ていきましょう。
前回の アナログ入力 で解説した部分は割愛します。
まずはグローバル変数とsetup関数です。
// これらの固定値は変更してはいけません。使用するピンに名前を与えています。
const int analogInPin = A0; // ポテンショメータに接続するアナログ入力ピン
const int analogOutPin = 9; // LEDに接続するアナログ出力ピン
int sensorValue = 0; // ポテンショメータから読み取った値
int outputValue = 0; // アナログ出力でPWM出力する値
void setup() {
// シリアル通信を初期化する。ボーレートは9600bps
Serial.begin(9600);
}
アナログ出力に使うピン9
に、int型の固定値(const)analogOutPin
という名前を付けています。
アナログ出力する値は、int型の変数outputValue
に格納します。
アナログ出力に使えるピンは、デジタル入出力の中にあります。
デジタル入出力ピンの中で~
が付いているピンだけが、アナログ出力に対応しています。
次はloop関数を見ていきます。
void loop() {
// アナログ入力の値を読む
sensorValue = analogRead(analogInPin);
// アナログ入力の0~1024の値を、アナログ出力の使用範囲0~255に変換する
outputValue = map(sensorValue, 0, 1023, 0, 255);
// アナログ出力値を変更する
analogWrite(analogOutPin, outputValue);
// シリアルモニターに結果をプリントする
Serial.print("sensor = ");
Serial.print(sensorValue);
Serial.print("\t output = ");
Serial.println(outputValue);
// アナログ->デジタル変換のために2ミリ秒のウェイトが必要
delay(2);
}
analogRead
関数で読み取った値がsensorValue
に入っています。
本当は、読み取った値をそのまま出力したいのですが、
- analogReadの値は0~1023
- analogWriteの値は0~255
で、使える範囲が違います。
そのため、outputValue
に入れる値は最大255にしなければいけません。
ですが、
outputValue = sensorValue;
if( outputValue > 255 ) outputValue = 255;
こんなプログラムを書いてしまうと、256~1023の値はすべて255にされてしまいます。
つまり、0-1023の4分の3が255にされてしまい、意味がなくなってしまいます。
では、どんな計算式を書けばよいでしょうか?
答えはこうです。
float ratio = sensorValue / 1023.0; // 最大値1023で割ることで、0.0~1.0の割合になる
outputValue = (int)(ratio*255); // 割合に255をかけることで0.0~255.0になる。最後にfloatはintにする
0.0~1.0の割合に変換(これを正規化と呼びます)して、そこに値を掛ければ良いのです。
しかし、Arduinoには便利な関数があります。それがmap
関数です。
map
関数は、入力された値の上限/下限で正規化し、出力用の上限/下限の範囲に変換します。
解説:map解説
outputValue = map(sensorValue, 0, 1023, 0, 255);
map
関数は非常によく使う関数なので覚えておきましょう。
- センサの値を角度に変換する
- センサの値を距離に変換する
- パルスカウントを角度に変換する
などなど、様々な変換計算を簡単にしてくれますよ。
こうして作りだしたoutputValue
は、analogWrite
関数で出力します。
解説:analogWrite解説
前述したように、analogWrite
に使えるピンはデジタル入出力のうち~
が付いたピンだけです。
今回は9番ピンを使っています。
こうして、LEDには0~5Vの可変電圧がかかるので、明るさが変化しているのです。
次は、このanalogWriteが中で実際に何をしているのか解説します。
パルス幅変調(PWM)
工学系出身であれば絶対知っていなければいけない知識のうち、
最も重要といっても過言ではないのが「PWM」です。
モータ制御に代表される現代の電子制御には、PWMはなくてはならない技術です。
PWM(パルス幅変調)を簡単に説明すると、
「ON/OFFの時間を調整することで、疑似的なアナログ電圧を作り出す」
となります。
5VのON時間と0VのOFF時間で平均電圧を計算すれば、実質的にアナログな電圧を作ったことと等しくなる、ということです。
下図のような考え方になります。
本当のデジタル->アナログ変換には、それ専用の装置(IC)が必要になるので高価になってしまうのですが、
デジタル出力のON/OFF時間を調整するだけで疑似アナログが作れるのであれば安上がりです。
なので、正確に言えばArduinoのanalogWrite
は「本当のアナログじゃなくて、なんちゃってアナログ」です。
「アナログ出力なのに、どうしてデジタル出力ピンから出力するの?」と疑問に思った人はいますか?
「アナログ入力ピン」があるのなら「アナログ出力ピン」が必要なはずだ、と思ったでしょう。
しかし、デジタルを使ったなんちゃってアナログなので、「PWM出力に対応したデジタル出力ピン」を使っているのです。
まとめ
アナログ出力では、
- analogWrite で出力する
- analogWriteはアナログと書いているが、実はデジタルでON/OFFをする「PWM」という手法
- analogWriteが使えるピンは、
~
が付いているデジタルピンだけ - analogWriteは0~255までの値が使える
-
map
関数を使うと、範囲(レンジ)の変換が簡単に行える
という事が分かったと思います。
目次 へ戻りましょう。次の作業を行ってください。