I2C / 静電容量式近接 & タッチセンサーAdafruit MPR121 / フィルターのサンプリング回数、充放電電流 & 時間の全体設定、サンプリング間隔を見る

pp.614ff.

今度は、フィルターのサンプリング回数、充放電電流&時間の全体設定、サンプリング間隔を見てみる。書き換えも可能だがここではしない。

CDTの計算:         
2^(n-2) = (2^n) * 2^(-2)
     = (2^n) / (2^2)
     = (2^n) / 4
     = (1 << n) >> 2

10倍する場合:               
10 * 2^(n-2) = 10 * (2^n) / 4
        = 5 * (2^n) / 2
        = 5 * (1 << n ) >> 1

何も設定していないのでデフォルト値が返ってくる。
f:id:ti-nspire:20200827192847p:plain:h250f:id:ti-nspire:20200828120102p:plain:h250

#define F_CPU 8000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include "USARTClass.h"

#include "MPR121Class.h"
MPR121Class pad(0x5A); // (I2Cスレーブアドレス)で実体化する。


ISR(INT1_vect){
    usart.printString("touch status (bit[3:0]): ");
    usart.printBinaryNibble(pad.get_touch_status() & 0xF);
    usart.printString("\n");
    
    usart.printString("filtered data    : ");
    for(int i=0; i<4; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printByte(pad.get_filtered_data(i));
        usart.printString(", ");
    }
    usart.printString("\n");

    usart.printString("baseline val     : ");
    for(int i=0; i<4; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printByte(pad.get_baseline_val(i));
        usart.printString(", ");
    }
    usart.printString("\n");
    
    usart.printString("touch threshold  : ");
    for(int i=0; i<4; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printByte(pad.get_touch_threshold(i));
        usart.printString(", ");
    }
    usart.printString("\n");
    
    usart.printString("release threshold: ");
    for(int i=0; i<4; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printByte(pad.get_release_threshold(i));
        usart.printString(", ");
    }
    usart.printString("\n");
    
    usart.printString("touch   debounce#: ");
    usart.printByte(pad.get_touch_debounce_num());
    usart.printString("\n");
    usart.printString("release debounce#: ");
    usart.printByte(pad.get_release_debounce_num());
    usart.printString("\n");

    usart.printString("First Filter#       : ");
    usart.printByte(pad.get_ffi());
    usart.printString("\n");

    usart.printString("Global CDC (uA)     : ");
    usart.printByte(pad.get_gloval_cdc());
    usart.printString("\n");

    usart.printString("Global CDT (0.1 us) : ");
    usart.printByte(pad.get_gloval_cdt());
    usart.printString("\n");
    
    usart.printString("Second Filter#      : ");
    usart.printByte(pad.get_sfi());
    usart.printString("\n");

    usart.printString("Sample Interval (ms): ");
    usart.printByte(pad.get_esi());

    usart.printString("\n\n");
}
    
int main(){
    usart.init();

    EIMSK |= (1 << INT1);  // INT1 (外部割り込み要求1)を有効化する。
    EICRA |= (1 << ISC11); // falling edgeで割り込む。
    sei();

    pad.soft_reset(); // ソフトリセットをかける。
    
    pad.enable_touch(4); // (n)本の電極をタッチセンサーにする。
    
    for(int i=0; i<4; i++){pad.set_touch_threshold(i, 0x10);} // ch0~3にタッチ閾値をセットする。
    
    while(1);
    return 0;
}
#ifndef MPR121CLASS_H_
#define MPR121CLASS_H_

#include <avr/io.h>
#include "I2CClass.h"

// register addresses and others
const uint8_t ECR                = 0x5E;
const uint8_t SOFT_RESET         = 0x80; const uint8_t SOFT_RESET_VAl = 0x63;
const uint8_t FILTERED_DATA      = 0x04;
const uint8_t BASELINE_VALUE     = 0x1E;
const uint8_t TOUCH_THRESHOLD    = 0x41;
const uint8_t RELEASE_THRESHOLD  = 0x42;
const uint8_t DEBOUNCE           = 0x5B;
const uint8_t FILTER1_GLOBAL_CDC = 0x5C;
const uint8_t FILTER2_GLOBAL_CDT = 0x5D;


class MPR121Class{
private:
    uint8_t _SLA;       // スレーブアドレス。
    uint8_t  get_ECR(); // ECRを読み出す。
    
public:
    void     soft_reset();                // ソフトリセットをかける。
    uint16_t get_touch_status();          // 12本ぶん(12ビット)のタッチ状態を取得する。
    void     enable_touch(uint8_t ele_n); // (n)本の電極をタッチセンサーにする。0~12本。
    void     enable_prox(uint8_t ele_n);  // (n)本の電極を近接センサーにする。0,2,4,12本。
    void     enter_stop_mode();           // STOPモードに移行する。
    
    uint16_t get_filtered_data(uint8_t ch); // (チャンネル)ごとのフィルター処理後データ(10ビット)を取得する。
    uint16_t get_baseline_val(uint8_t ch);  // (チャンネル)ごとのベースライン値(10ビット、下2ビットは無効)を取得する。

    uint8_t get_touch_threshold(uint8_t ch);              // (チャンネル)ごとのタッチ閾値を取得する。 
    void    set_touch_threshold(uint8_t ch, uint8_t val); // (チャンネル)ごとのタッチ閾値をセットする。

    uint8_t get_release_threshold(uint8_t ch);              // (チャンネル)ごとのリリース閾値を取得する。
    void    set_release_threshold(uint8_t ch, uint8_t val); // (チャンネル)ごとのリリース閾値をセットする。

    uint8_t get_touch_debounce_num();   // タッチディバウンス回数を取得する。全チャンネル共通。
    uint8_t get_release_debounce_num(); // リリースディバウンス回数を取得する。全チャンネル共通。
    
    void set_touch_debounce_num(uint8_t num);   // タッチディバウンス回数(0~7)をセットする。全チャンネル共通。
    void set_release_debounce_num(uint8_t num); // リリースディバウンス回数(0~7)をセットする。全チャンネル共通。
    
    uint8_t get_ffi();        // 1次フィルターのサンプリング回数。0~3 → 6,10,18,34回
    uint8_t get_gloval_cdc(); // 共通の充放電電流。直読0~63 uA。
    uint8_t get_gloval_cdt(); // 共通の充放電時間。0~7 → 2^(n-2) us。ただし整数のままにしたいので、10倍した値を返すことにする。
    uint8_t get_sfi();        // 2次フィルターのサンプリング回数。0~3 → 4,6,10,18回
    uint8_t get_esi();        // 2次フィルターでのサンプリング間隔。0~7 → 2^n ms。
    

    // コンストラクタ
    MPR121Class(uint8_t slave_addr);
};

#endif
#include "MPR121Class.h"

void MPR121Class::soft_reset(){
    i2c.write(_SLA, SOFT_RESET, SOFT_RESET_VAl); // (どのスレーブの, どのレジスタに, 1バイトを)書き込む。
}
uint16_t MPR121Class::get_touch_status(){
    uint8_t buff[2];
    i2c.read(_SLA, 0x00, 2, buff); // (どのスレーブの, どのレジスタから, nバイトを, どのバッファに)読み出す。
    return ((uint16_t)(buff[1] & 0x0F) << 8) | buff[0]; // 上位4ビット + 下位8ビットに並べ換えて返す。
}
uint8_t MPR121Class::get_ECR(){
    return i2c.read(_SLA, ECR); // (どのスレーブの, どのレジスタから) 1バイトを読み出す。
}
void MPR121Class::enable_touch(uint8_t ele_n){
    uint8_t sute = (get_ECR() & 0xF0) | ele_n; // ECRを読み出して下4ビットを消してそこだけ書き換える。
    i2c.write(_SLA, ECR, sute); // (どのスレーブの, どのレジスタに, 1バイトを)書き込む。
}
void MPR121Class::enable_prox(uint8_t ele_en){
    uint8_t n;
    switch(ele_en){
        case  0: n = 0; break;
        case  2: n = 1; break;
        case  4: n = 2; break;
        case 12: n = 3; break;
        default: n = 3; break;
    }
    uint8_t sute = (get_ECR() & ~(0b11 << 4)) | (n << 4);
    i2c.write(_SLA, ECR, sute);
}
void MPR121Class::enter_stop_mode(){
    enable_touch(0);
    enable_prox(0);
}
uint16_t MPR121Class::get_filtered_data(uint8_t ch){
    uint8_t buff[2];
    i2c.read(_SLA, FILTERED_DATA + 2 * ch, 2, buff);
    return ((uint16_t)(buff[1] & 0b11) << 8) | buff[0]; // 上位2ビット + 下位8ビットに並べ換えて返す。
}
uint16_t MPR121Class::get_baseline_val(uint8_t ch){
    return (uint16_t)i2c.read(_SLA, BASELINE_VALUE + ch) << 2; // 電極データと比較しやすいよう10ビットにして返す。
}
uint8_t MPR121Class::get_touch_threshold(uint8_t ch){
    return i2c.read(_SLA, TOUCH_THRESHOLD + 2 * ch); 
}
void MPR121Class::set_touch_threshold(uint8_t ch, uint8_t val){
    uint8_t ecr = get_ECR();                        // ECRの内容を取っておいて、
    enter_stop_mode();                              // STOPモードにして、
    i2c.write(_SLA, TOUCH_THRESHOLD + 2 * ch, val); // タッチ閾値を書き換えて、
    i2c.write(_SLA, ECR, ecr);                      // ECRを書き戻してRUNモードに戻す。
}
uint8_t MPR121Class::get_release_threshold(uint8_t ch){
    return i2c.read(_SLA, RELEASE_THRESHOLD + 2 * ch);
}
void MPR121Class::set_release_threshold(uint8_t ch, uint8_t val){
    uint8_t ecr = get_ECR();                          // ECRの内容を取っておいて、
    enter_stop_mode();                                // STOPモードにして、
    i2c.write(_SLA, RELEASE_THRESHOLD + 2 * ch, val); // リリース閾値を書き換えて、
    i2c.write(_SLA, ECR, ecr);                        // ECRを書き戻してRUNモードに戻す。
}
uint8_t MPR121Class::get_touch_debounce_num(){
    return i2c.read(_SLA, DEBOUNCE) & 0b111;
}
uint8_t MPR121Class::get_release_debounce_num(){
    return (i2c.read(_SLA, DEBOUNCE) >> 4) & 0b111;
}
void MPR121Class::set_touch_debounce_num(uint8_t num){
    uint8_t ecr = get_ECR();                                // ECRの内容を取っておいて、
    enter_stop_mode();                                      // STOPモードにして、
    uint8_t dt = (i2c.read(_SLA, DEBOUNCE) & ~0b111) | num; // ディバウンスレジスタの下3ビットだけを
    i2c.write(_SLA, DEBOUNCE, dt);                          // 書き換えて、
    i2c.write(_SLA, ECR, ecr);                              // ECRを書き戻してRUNモードに戻す。
}
void MPR121Class::set_release_debounce_num(uint8_t num){
    uint8_t ecr = get_ECR();                                             // ECRの内容を取っておいて、
    enter_stop_mode();                                                   // STOPモードにして、
    uint8_t dr = (i2c.read(_SLA, DEBOUNCE) & ~(0b111 << 4)) | (num <<4); // ディバウンスレジスタのビット[6:4]だけを
    i2c.write(_SLA, DEBOUNCE, dr);                                       // 書き換えて、
    i2c.write(_SLA, ECR, ecr);                                           // ECRを書き戻してRUNモードに戻す。
}
uint8_t MPR121Class::get_ffi(){
    uint8_t n;
    switch((i2c.read(_SLA, FILTER1_GLOBAL_CDC) >> 6) & 0b11){
        case 0 : n =  6; break;
        case 1 : n = 10; break;
        case 2 : n = 18; break;
        case 3 : n = 34; break;
        default: n =  0; break;
    }
    return n;
}
uint8_t MPR121Class::get_gloval_cdc(){
    return i2c.read(_SLA, FILTER1_GLOBAL_CDC) & ~(0b11 << 6);
}
uint8_t  MPR121Class::get_gloval_cdt(){
    uint8_t n = (i2c.read(_SLA, FILTER2_GLOBAL_CDT) >> 5) & 0b111;
    return (5 * (1 << n)) >> 1;
}
uint8_t MPR121Class::get_sfi(){
    uint8_t n;
    switch((i2c.read(_SLA, FILTER2_GLOBAL_CDT) >> 3) & 0b11){
        case 0 : n =  4; break;
        case 1 : n =  6; break;
        case 2 : n = 10; break;
        case 3 : n = 18; break;
        default: n =  0; break;
    }
    return n;
}
uint8_t MPR121Class::get_esi(){
    return 1 << (i2c.read(_SLA, FILTER2_GLOBAL_CDT) & 0b111);
}



MPR121Class::MPR121Class(uint8_t slave_addr){
    _SLA = slave_addr;
    i2c.init();
}