footstep detector / 振動が検出されたときにLEDを点ける

これでほぼ完成形。振動が検出されたらある程度の時間だけLEDを点灯してから消す。振動が検出され続けている限りLEDも点灯し続ける。テキストは、ノイズの上限を上回ったときと下限を下回ったときとで点灯のパターンを分けているがここでは省いた。
f:id:ti-nspire:20191024113522p:plain:h250

#include <avr/io.h>
#include <util/delay.h>
/*extern "C"{
    #include "USART.h"
}*/

#define ON_TIME 50000
#define DELAY   10
#define PADDING 2

void initADC(){
    ADMUX  |= (1 << REFS0);                                 // AVCCを基準電圧として使う。
    ADCSRA |= ((1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0)); // ADCプリスケーラーを128分周に設定する。
    ADCSRA |= (1 << ADEN);                                  // ADCを有効化する。
}

uint16_t readADC(char ch){
    ADMUX  |= ((0xF0 & ADMUX) | ch);       // チャンネル選択ビットを全部一旦クリアしてから目的のチャンネルをセットする。
    ADCSRA |= (1 << ADSC);                 // ADCを開始(Start Conversion)する。
    loop_until_bit_is_clear(ADCSRA, ADSC); // ADCの終わるまで待ってから、
    return ADC;                            // 10ビットのADC値を返す。
}

// 平均値を求めるための函数
uint16_t ewma(uint16_t newVal, uint16_t oldAverage){
    return newVal + oldAverage - ((oldAverage - 8) >> 4);
}

int main(){

    DDRB = 0xff; // 振動が検出されたときにポートBのLEDを点けることにする。

    uint16_t midVal  = 511;            // 中間電位の假の平均値
    uint16_t hiVal   = 540;            // 中間電位を上回ったAD変換値(ノイズと見なす値の上半分)の假の平均値。不感帯域の假の上限。
    uint16_t loVal   = 480;            // 中間電位を下回ったAD変換値(ノイズと見なす値の下半分)の假の平均値。不感帯域の假の下限。
    uint16_t padding = (PADDING << 4); // 背景ノイズの大きさにマージンとして追加する値(16倍した値)
    uint16_t noiseVol;                 // 背景ノイズ
    uint16_t adcVal;                   // AD変換値
    uint16_t lightsOutTimer;           // 消灯までのカウントダウンタイマー

    uint8_t isLowerThanMid;
    uint8_t isHigherThanMid;


    initADC();
//    initUSART();

    while(1){
        adcVal = readADC(PC2);

        // 中間電位の平均値を更新する。
        midVal = ewma(adcVal, midVal);

        // 中間電位を上回った値(ノイズと見なす値の上半分)の平均値を更新する。
        if (adcVal > (midVal >> 4)){
            hiVal = ewma(adcVal, hiVal);
        }

        // 中間電位を下回った値(ノイズと見なす値の下半分)の平均値を更新する。
        if (adcVal < (midVal >> 4)){
            loVal = ewma(adcVal, loVal);
        }

        // 背景ノイズの大きさを更新する。余裕を見て少し値(padding)を足す。
        noiseVol = (hiVal - loVal) + padding;

        // AD変換値がノイズの上限を上回るか下限を下回るかした場合は、
        // LEDを点けて、さらに消灯までのカウントダウンタイマーをリセットする。
        // 要するに振動の検出されるたびに消灯を先送りにする。
        (adcVal < ((midVal - noiseVol) >> 4)) ? isLowerThanMid  = 1 : isLowerThanMid  = 0;
        (adcVal > ((midVal + noiseVol) >> 4)) ? isHigherThanMid = 1 : isHigherThanMid = 0;
        if(isLowerThanMid || isHigherThanMid){
            PORTB = 0xff;
            lightsOutTimer = ON_TIME / DELAY;
        }

        // AD変換値がノイズの上限下限からはみ出なかった場合は、
        // カウントダウンを開始し、カウントダウンタイマーが0になったら時点でLEDを消す。
        else{
            (lightsOutTimer > 0) ? lightsOutTimer-- : PORTB &= 0x00;
        }

        /*
        //最後の最後に16で割る。
        printString("midVal: ")  ; printWord(midVal   >> 4) ;printString("\r\n");
        printString("hiVal: ")   ; printWord(hiVal    >> 4) ;printString("\r\n");
        printString("loVal: ")   ; printWord(loVal    >> 4) ;printString("\r\n");
        printString("noiseVol: "); printWord(noiseVol >> 4) ;printString("\r\n");
        printString("\r\n");
        */
        
        _delay_us(DELAY);
    }

    return 0;
}

これでChapter 12: Analog-to-Digital Conversion IIがおおむね終わり。