『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を光らせるのはオマケ。
いったんA0を出力にして、コンデンサを放電します。
0V近くまでの放電を確かめたら、A0を入力に戻して
A/D変換を続行。
読み出した10bitA/D値の上位2bitを使って、2bitの
A/Dコンバータとして扱います。
その結果をD8(LSB)とD9ポート(MSB)に出力します。
こんな波形が得られます。
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)の記載
・英文
・和訳
出てくるのはあたりまえに「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の「原理的」な変換結果がオシロ波形に現れま
した。
| 固定リンク
「Arduino」カテゴリの記事
- Arduino、analogWriteは捨てちゃえ。ちゃんとしたPWMの例(2025.03.22)
- パルスジェネレータをI2C液晶で動かす(2025.01.28)
- EEPROMを使ったシリアル受信バッファ 512kバイトに増設(2024.12.26)
- 1/nカウント方式とDDS方式の2相パルス発生回路(2024.10.13)
- おっと。map関数の計算桁に注意(2024.10.06)
「1023 vs 1024」カテゴリの記事
- Arduino、analogWriteは捨てちゃえ。ちゃんとしたPWMの例(2025.03.22)
- 1023 vs 1024:AVRマイコンとPICマイコンのデータシートより(2025.03.19)
- 1/1023監視団 活動中!(2025.03.10)
- おっと。map関数の計算桁に注意(2024.10.06)
- サーミスタでの温度測定、「inf」の出現に耐えられるか?(2024.05.13)
コメント
コンデンサへの充電カーブを使ったので
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分