« GX200でもLEDリングライト活躍 | トップページ | FLUKE 87IV デジタルテスターのデータをIrDAで吸い上げる »

2022年11月12日 (土)

『1/1023 vs 1/1024』問題 analogReadを2bitにして確かめてみる

未だ、Arduino環境での「1/1023」が蔓延っています。
10bitでデータを出すanalogRead()の値から、実際の
入力電圧を算出するのは・・・
何度も書いてますが正しくは「1/1024」
「1/1023」じゃありません

2020年1月8日:ミスが広まる 1/1023 vs 1/1024
2020年5月17日:Arduino なんとかして誤用を正したい:A/Dの1/1023とmap関数
2022年10月 6日:ArduinoのA/D:1/1023 vs 1/1024 その後

このA/D入力を10bitじゃなく2bitにして変化の様子
見られるようにしてみました。
オシロとシリアル・モニターで観察します。

使うのはArduino-UNOと抵抗、コンデンサ。
  LEDを光らせるのはオマケ。

Aa1_20221112132201
いったんA0を出力にして、コンデンサを放電します。
0V近くまでの放電を確かめたら、A0を入力に戻して
A/D変換を続行。
読み出した10bitA/D値の上位2bitを使って、2bitの
A/Dコンバータとして扱います。
その結果をD8(LSB)とD9ポート(MSB)に出力します。

こんな波形が得られます。
Aa2_20221112132601

100kΩの抵抗で1uFを充電するので、
0Vから上昇する指数関数カーブになります。
時定数は「100ms」。 (63%位置 3.15V)
  5V近くになるとなかなか上がらないので
  1000を越えると再放電しています。

2bitですので0V~5Vが4分割されます。
  5V÷4=1.25Vになるので、オシロのY軸を
  1.25V/divにしたかったのですが、
  1.24V/divしかできませんでした。

1.25Vピッチで4分割されてデータが変化して
いる様子が分かるかと思います。

シリアルモニターには、2bit値変化点での
10bit値(0~1023)が出てきます。
  :
1 256 512 768
0 257 512 768
1 256 512 768
0 256 513 768
0 256 512 768
  :

1/1024」の式にならえば2bitだと「1/4」。
1/1023」だと「1/3」。
「1/3」しちゃうと、半値の2.50Vは出ませんし、
実値入力が3.75Vを越えると、いきなり5.00Vに
なっちゃうしで、「1/3だとなんかおかしい」を
感じていただけるかと。

こんなスケッチです。

//  『1/1023 vs 1/1024』問題
// analogRead()を2bitにして動作を確かめてみる
// 10bitデータを1/256して上位2bitだけの値を見る
// A0 A/D入力 100k抵抗で5Vにプルアップ
// 1uFコンデンサでGNDに
// Lレベルを出力し放電してからスタート
// 0Vから始まるエクスポネンシャルカーブが得られる
// 2bitでの変化点をチェック
// D8 LED bit0 LSB Hで点灯
// D9 LED bit1 MSB

/***** STEUP *****/
void setup() {
pinMode(8, OUTPUT); // D8 出力に設定 HでLED点灯
pinMode(9, OUTPUT); // D9 (bit 1のLED)
analogReference(DEFAULT); // 5Vを基準電圧に
Serial.begin(9600); // 9600BPSでシリアル出力
}

/***** LOOP *****/
void loop() {
word ad10; // 10bit A/D値 0~1023
short ad2; // 1/256して2bitにした値(比較するので+/-値に)
short ad2x; // 変化チェックデータ (-1からスタート)
byte exc = 0; // 実行区分
while(1){
// A/D入力
ad10 = analogRead(A0); // 10bit A/D値読み出し (0~1023)
ad2 = ad10 / 256; // 2bitに (0,1,2,3) プラスの値
// LED出力
if(ad2 & 0b01) digitalWrite(8, HIGH); // bit 0
else digitalWrite(8, LOW);
if(ad2 & 0b10) digitalWrite(9, HIGH); // bit 1
else digitalWrite(9, LOW);
// 実行区分で処理
switch(exc){
case 0: // 放電開始
pinMode(A0, OUTPUT); // A/D入力をいったん出力に
digitalWrite(A0, LOW); // Lにしてコンデンサを放電
Serial.println(); // 改行
exc++; // 次処理
break;
case 1: // 0V近くになるまで待つ
if(ad10 < 1){ // 10bit A/D値がゼロに近づいた
pinMode(A0, INPUT); // A/D入力を入力に戻す
ad2x = -1; // 変化チェックデータをセット
exc++; // 次処理
}
break;
case 2: // 5V近くになるまで待つ
if(ad2 > ad2x){ // 2bit A/D値が大きくなった
ad2x = ad2; // チェックデータ更新
Serial.print(ad10); // 10bit A/D値をシリアル出力
Serial.print(" "); // space
}
if(ad10 > 1000){ // フルスケール近くになった
exc = 0; // 放電から繰り返し
}
break;
}
}
}
/*===== end of "test_ad_2bit.ino" =====*/

直線で変化するノコギリ波が良いのですが、
ノコギリ波を作ろうとすると、定電流回路用に
オペアンプがいります。
エクスポネンシャル・カーブだと抵抗+コンデンサ
だけなんで簡単。

※データシート(ATmega328P)の記載
・英文
Aa4_20221112150401
・和訳
Aa3_20221112150401

出てくるのはあたりまえに「1024」です。
最大値 「1023 = 0x3FF」になる電圧は「Vref - 1LSB」と。

※参
ルネサス:A/D,D/Aの量子化の単位は、2^n ? 2^n-1?
  量子化誤差というと「1/2LSB」の変動を問題にします。
  Vref=5Vで2bit ADCだと、1/2LSBは0.625V。
  さすがここまで大きくはフラフラしませんので、
  ADCの「原理的」な変換結果がオシロ波形に現れま
  した。

|

« GX200でもLEDリングライト活躍 | トップページ | FLUKE 87IV デジタルテスターのデータをIrDAで吸い上げる »

Arduino」カテゴリの記事

1023 vs 1024」カテゴリの記事

コメント

コンデンサへの充電カーブを使ったので
n倍の時定数で何%まで上がるかをメモしときます。
  % = 1-e^(-t)
 t = CR :時定数
------------------
1t 63.2%
2t 86.5
3t 95.0
4t 98.2
5t 99.3
6t 99.75
7t 99.91
8t 99.97
9t 99.988
10t 99.995

投稿: 居酒屋ガレージ店主(JH3DBO) | 2022年11月24日 (木) 09時50分

コメントを書く



(ウェブ上には掲載しません)




« GX200でもLEDリングライト活躍 | トップページ | FLUKE 87IV デジタルテスターのデータをIrDAで吸い上げる »