入力値の振れ止め改良

受け取った数値データにふれが出るのを丸める処理について。
データの数値振れが出る原因を考え、1からわかりやすい手法で丸め処理する方法の解説です


振れ止め改良

ボリュームの振れ

このA/D変換結果はボリューム(可変抵抗)を境い目のあたりにすると、出てくる数字が行ったり来たりと、振れがでます。

 

このA/D変換の振れ対策は何とかしなければと思っていたので、A/D変換はこれだけに限らずいろんな場合によく使うので、 A/D変換値を細かく求めることにはいろいろ考えさせらることが多々あります。

 

原因は明らか  入力される電圧が変動してるからです。その電圧を数値にすると変動する。

対策
  1. ハード、回路で電圧変動がない電圧をA/D変換に送り込む
  2. 電圧が変動してるので、それを安定させるのが最良なのは、そうなんですが。。。。

    微妙な電圧変動なので、使用する機器、回路、A/D変換の参照電圧の質 によっても微小な変動は必ず出ることはあります。特にA/D変換値を細かく求めるほど。

    できるならソフト設定だけで何とかしようということなんでこれを全部ハード側でするのは、つなぐ機器、回路、電源によっても変わるので、外部回路を付け足す必要もあり厄介です。

    それも、必要と場合に応じてできるだけやりましょうということで、切り分けて考えます。

  3. ソフトで処理する
  4. やはり、切り分けて考えると、ハード側だけでは厄介なので、できるだけ電圧変動のないような回路にして、。。ということと、
    ソフト側でも振れ止めの処理ということになります。
    しかし、入力電圧に変動がある限り、細かくとると振れは必ず出ます。

 

ソフトで対策

境目で行ったり来たりになるので、 仮に 2←→3 と振れるなら
どちらかに決めてることで振れを止めてしまう処理ということになるのですが、どちらに決めるか、、ということと、どれぐらいの振れ速度に対応するかということはなかなか厄介な問題です。

 

振れた場合、大小どちらに、どちらの数字にするか?

2←→3 と境目で振れを繰り返すなら 
その間の微妙なところがどちらになるかわからない!とA/D変換が困っているので、どっちに?といわれても、私も困ります

やはり決めるなら、10回ぐらいの値を拾った内、出てきた目の多い方に。。のようにしましょう。

 

  -全部で10回拾っていく → 

例) 振れている数値を10回拾ったうち

   →4回、  3 →6回    なら   3に決め! というように 

 

結局、平均を取って四捨五入するような処理になります。


しかし、どのような処理をとっても デジタルの数値なので境目の線引きが必要で、電圧が振れる限り、境目で変動はでることになります。 (でも振れの発生する範囲はかなり狭まります)

 

この処理をしても、
 2→4回、  3→6回
 2→5回、  5→5回

とか、この辺を行ったり来たりとなることは出てきます。

しかし、変動がでる範囲は確実に小さくはなります。実用で影響が少なくなる程度を目指します。その変動の影響を小さくしようということが目的です。

 

振れ速度

また変動する振れ速度も考える必要があります。

A/D変換速度が十分遅いと、振れも遅くなります。遅い振れだとより扱いやすく、間隔を大きく空けて変換値を拾ってくれば、振れ変動も遅くなり、その数値を上のように丸める、というような処理でかなり抑えられるはずです。

でも、間を空けるということは、A/D変換値を得るまでの反応時間が長くなります。この辺のところがバランスですね。

 

丸め処理

100回の変換値を拾い、その平均をとり四捨五入します。

少数を使わない簡易的な固定小数点の考え方の手法にします。
(以下の方法はまとめると、少数を使わないようにする方法と、四捨五入する方法です)

 少数を使えばいいのですが、精度なんていらないのでルーチンとしては少数を使わない方がメモリも喰わないし一般に処理負担は軽くなります。
このため平均するだけなのですが、すこしややこしい方法になっています。


一桁増やす

10倍して一桁増やし、割ったときに小数部がでると、10倍して増やした一桁(末桁)が小数部になります。

例) 2 2 2 3 3 の5回の平均なら


全部10倍して

 20+20+20+30+30
 ÷5

五回の合計
 =25 (本来2.5 の10倍)

 

四捨五入

その増えた末桁(本来小数部)を四捨五入するのですが、
その手法は一般に 

 +5 して、末桁を切り捨てる手法 (+5 して小数部を切り捨て)  です。
(マイナスの場合は-5)
5を足すと四捨五入で次の桁が上がる

 

まず、合計に5を足します。
これが四捨五入のためで、これで四捨五入になり次の桁が上がります

例)
五回の合計が
24なら 24+5=29 桁上がりなし
25なら 25+5=30 桁上がりアリ
26なら 26+5=31 桁上がりアリ

 

このあと末桁を切り捨てます。

 

その方法は 10で割って整数に変換する
(整数型にすると、あまりは自動的に切り捨てられるので)

 振れ止め値=(int)(29/10) → 振れ止め値=2 になる

 振れ止め値=(int)(30/10) → 振れ止め値=3 になる
振れ止め値=(int)(31/10) → 振れ止め値=3 になる

と、結果 少数1桁目を四捨五入したのと同じになります。

 

振れ速度

A/D変換速度は 低速に設定します。

100回の変換値を拾い、その平均をとり四捨五入しています

100回のA/D変換値で1つの結果をだしていることになり、速度はずいぶん遅くなりますが、反対に遅らせているということにもなります。

この100回の変換値を拾うのを増やしていくと、どんどん変換を遅くすることができます。それを全部平均計算するので処理は増します。

しかし、回数を増しても振れがその分、減るというわけでもありません。サンプリング数を多くして精度を増しても、変動しているものなのですから。;


遅らせるだけなら、間隔を空けて変換値を取ってくるほうがよいでしょう。

A/D変換速度は十分速いので、この程度ではボリューム入力では問題ありません。

 

プログラム

 

 


void ini_AD(void){

AD.ADCSR.BYTE = 0x33; /* 0011 0011*/
                /* A/D変換開始 */
                /* スキャンモード */
                /* × 高速変換 */
                /* 低速変換 */
                /* AN0 AN1 AN2 AN3*/
}

unsigned char AD_Vlm(int div,volatile unsigned int *ADDRX){
/**************************************
 引数 : ・div (VLキザミ値)
      4,8,16,32,64,128,256 で指定
・ADDRX
A/D値のレジスタアドレス

 返値 : 0~div を返す
      VLボリュームの回転に応じて
**************************************/

unsigned long Add=0;
unsigned char i,k;

switch(div){
  case 4: i=14; break;
  case 8: i=13; break;
  case 16: i=12; break;
  case 32: i=11; break;
  case 64: i=10; break;
  case 128: i=9 ; break;
  case 256: i=8; break;
  default: i=14;
}

//100回のA/D値の総和をとる
for(k=0;k<100;k++){

  while(AD.ADCSR.BIT.ADF==0); // 変換結果がでるまで待つ

    Add += ( (*ADDRX>> i) * 10 ); //ビットシフト(i桁ずらす 10倍long)して累積
    AD.ADCSR.BIT.ADF=0;
}

/* 平均して四捨五入 */
  Add=(unsigned char)(((Add/100)+5)/10); //5を足した後、10倍longを元に戻すのに10で割る
  //→末桁を四捨五入して切り捨て
return(Add);
}

       

 

まとめ

 

ボリュームAD変換関数

  1. ini_AD() 初期化関数
  2. スキャンモード
    高速変換
    AN0

  3. AD_Vlm( div ) ボリュームAD変換関数
  4. <機能>
    入力動作 :  ボリュームの回転
    出力    :  ボリュームの回転に応じた出力値
    調節    :  引数divで出力数値の範囲を変更できる

7通りの出力範囲 設定
引数div 16 32 64 128 256
出力値 0~3 0~7 0~15 0~31 0~63 0~127 0~255

 


 

 

スポンサーリンク
  • facebook
  • twtter
  • google+
  • hatena