2016年5月25日水曜日

mbedのAnalogInでPotentiometerの読み取り値のふらつきを抑える。

mbedのAnalogInを使ってみたが、読み取り値がふらつく。値が変動したのをトリガーとして何らかの処理を行いたい場合、少しでも値が変動すると困る。

自作の可変両電源のAVRのプログラミングでは、移動平均をとるのと、ADCの変換中に他の処理をストップさせてノイズを減らすということをやったが、POTの読み取りごときでいちいち止めていられないので移動平均をとって改善するかどうか見ることにした。

「移動平均」というと何かかっこよさそうですが、前に読み取った値をいくつか覚えておいてその間の平均をとって今の値として採用するというだけ。


前作ったぴゅんぴゅんコントローラー2号で実験してみた。POT3個とアナログジョイスティック 1個(POTが縦横で2個)でアナログ素子は5個。Arduino 互換のHeaderのA0~A4につないだ。(タクトスイッチは使っていない。)

#include "mbed.h"

class AverageAnalogIn {
public:
    AverageAnalogIn(PinName _pin, int _bufferSize=16) : m_AnalogIn(_pin), bufferSize(_bufferSize), index(0) {
        buffer = new unsigned short[bufferSize];
    }
    
    ~AverageAnalogIn() {
        delete buffer;
    }
    
    unsigned short read_u16() {
        buffer[index] = m_AnalogIn.read_u16();
        index++;
        if (index == bufferSize) {
            index = 0;
        }
        unsigned int sum = 0;
        for (int i = 0; i < bufferSize; i++) {
            sum += buffer[i];
        }
        return sum / bufferSize;
    }

private:
    AnalogIn m_AnalogIn;
    int bufferSize;
    int index;
    unsigned short *buffer;
};
 
AverageAnalogIn Pots[] = {
    AverageAnalogIn(A0, 4),
    AverageAnalogIn(A1, 8),
    AverageAnalogIn(A2, 16),
    AverageAnalogIn(A3, 32),
    AverageAnalogIn(A4, 64),
};

#define POTS_SIZE (sizeof(Pots)/sizeof(Pots[0]))

int main() {
    static uint16_t prevMeas[POTS_SIZE];

    printf("\r\nAnalogIn example\r\n");
    
    while(1) {
        unsigned int error = 0;
        for (int i = 0; i < POTS_SIZE; i++) {
            unsigned short meas = Pots[i].read_u16() >> 6; // 10bit width
            printf("%u,", meas);
            if (prevMeas[i] != meas) {
                prevMeas[i] = meas;
                error |= (1 << i);
            }
        }
        printf("%x\r\n", error);
        
        wait(0.01); // 10 ms
    }
}


一応、C++らしくクラス化してみた。(ちゃんとテストしていないのでお気をつけて。)

<追記:2016.06.06>

早速ですが、デストラクタの「delete buffer」は「delete[] buffer」じゃないと作ったり壊したりしたらメモリー・リークする(^q^;

</追記>

オブジェクト指向というのは、STL(Windowsではないほう)、WindowsのMFC、DirectX、Java EEなどで使っていたのでどの流儀がいいのか記法レベルで迷う。組み込みだし、コストも気にしないといけないのでなかなか悩みどころが多い。

かと言って平文(?)でダラダラ書いてると、もう以前自分が何のつもりで書いてたのかわからなくなってきたので何か流儀を見つけないといけないなと思っています。

10bit精度で値のふらつきをみる。



AnalogInのread_u16は[0..0xFFFF]の値を返すので、ソースの通りunsigned 16bitで読み取った値を6bit右シフトして10bit精度に落としてCPLTでグラフ化してみた。


ch1(黒) POT1 4個の平均
ch2(青) POT2 8個の平均
ch3(緑) POT3 16個の平均
ch4(暗緑) アナログジョイスティック 32個の平均
ch5(赤) アナログジョイスティック 64個の平均
ch6(紫) エラー値

グラフの一番下の棒グラフ状になっているところを見ると10bit精度では常に値が変動していそうだ。bitごとに1 or 0にしているので紫のグラフの背が高いところがエラーが多いというわけではない。

ch4(暗緑)は32個の平均をとって変動しているのに、64個の平均をとったch5(赤)はほぼ変動していない。

経験上このアナログジョイスティックは普通のPOTよりかなり精度が悪いのがわかっているので、64個も平均を取れば大体のPOTは大丈夫そうだ。

ただし、かなり反応が鈍くなる。