時々使っているマイコンのADCは10bitなので1024段階(0-1023)での出力が得られるが、時間応答を犠牲にしてももうちょっと分解能を高くしたいときがある。温度計とか。
ArduinoではAnalogRead(A0)といった関数でA0端子のアナログ値を得られるのでコレを使って書くと、
ADC = r* ADC + AnalogRead(A0);
という加算をしていくと、ADCの値にはAnalogRead(A0)/(1-r)の値が入るので、コレをつかうことでADCの見かけ上の分解能を増大させることができる、はず。
解説:
ADC += AnalogRead(A0);
というコードを書くとADCの値は無限に増大していく。なので、ある程度値を抜いてやると値がある値に収束するであろうことがわかる。
例えば、
ADC = 0.9*ADC+AnalogRead(A0);
と計算をすると、現在の加算値の0.9倍に読み取りの値を加算するので、感覚的に、AnalogRead(A0)の10倍の値に収束する感じがわかる。
で、0.9のところをr(0<r<1)にして見ると、
ADC = r*ADC + AnalogRead(A0);
で、AnalogRead(A0)の値が一定で、ADCが一定にに収束した状態では、左辺と右辺が等しいので、
(1-r)*ADC = AnalogRead(A0)となり、ADC = AnalogRead(A0) / (1-r)となる。
r=0.9の場合が、上の例なので、そのとおりである。
次に、マイコンの演算として浮動小数点演算は重い場合があるので、ビットシフトで除算相当をやるといいのではないだろうかと思うと・・・・
r=3/4, 7/8, 15/16, 31/32といった(2^n-1)/2^nという値にするとビットシフトで計算できて良いのではと思う。
実際には、
ADC = ADC - ADC>>4 + AnalogRead(A0);
とやると、4bitシフトなので、ADC*(1-1/16)+AnalogRead(A0)という演算と等価のはず。
と、ここまで思ったが、四捨五入問題どうなるんだろうか?
16>>4は1なので16で割ってもビットシフトしても同じ値。
24>>4はやはり1ではあるものの、24/16=1.50なので、四捨五入を食らってしまうので値がずれる。
少数第一まで計算するので、例えば4ビットシフトは16で除算、なので16の半分の8を加算してからビットシフトするとちょうど良くなるのでは無いだろうか。
そうすると、ビットシフト量をsとすると、次のような演算をするのが四捨五入にも対応するようだ。きっとsは固定値だからコンパイラがうまく処理してくれるだろう。
ADC = ADC - (ADC + 2^(s-1))>>s + AnalogRead(A0);
これが、読み取り値の、2^s倍の値になっていることにだけ気をつけたらいい。
10進数でも全くおなじことが言える。
1025を100で割ったら10.25で四捨五入して整数にすると10
1051を同上で11
除算前に四捨五入相当するとなると、除算する100の半分の値の50を加算しておいてから100で割って切り捨てると同じことになる。