I2C / 静電容量式近接 & タッチセンサーAdafruit MPR121 / GPIOを使う / LEDドライバー

pp.614ff.

今度はGPIO機能を試してみる。普通のCMOSロジック入出力だけでなく、入力時は、内部プルアップ、内部プルダウンができ、出力時は、ハイサイドのみオープンドレイン(LEDドライバー用、要するにソース専用)、ローサイドのみオープンドレイン(要するにシンク専用)という回路構成にできる。

ここではLEDドライバーとして使ってみる。

f:id:ti-nspire:20200901064029p:plain:h315

#define F_CPU 8000000UL

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

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

// 全チャンネルのタッチステータスを端末に表示する函数。
void print_touch_status(){
    usart.printString("Touch (Prox) Status[12:0]: ");
    usart.printBinaryWord(pad.get_touch_status());
    usart.printString("\n");
}

// (n)本のチャンネルの電極データを端末に表示する函数。
void print_filtered_data(uint8_t n){
    usart.printString("Filtered Data: ");
    uint16_t buff[n];
    pad.get_filtered_data(n, buff);
    for(int i=0; i<n; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printWord(buff[i]);
        usart.printString(", ");
    }
    usart.printString("\n");
}

// (n)本のチャンネルのベースライン値を端末に表示する函数。
void print_baseline_val(uint8_t n){
    usart.printString("Baseline Val : ");
    uint16_t buff[n];
    pad.get_baseline_val(n, buff);
    for(int i=0; i<n; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printWord(buff[i]);
        usart.printString(", ");
    }
    usart.printString("\n");
}

// (n)本のチャンネルのタッチ閾値を端末に表示する函数。
void print_touch_threshold(uint8_t n){
    usart.printString("Touch   Threshold: ");
    uint8_t buff[n];
    pad.get_touch_threshold(n, buff);
    for(int i=0; i<n; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printByte(buff[i]);
        usart.printString(", ");
    }
    usart.printString("\n");
}

// (n)本のチャンネルのリリース閾値を端末に表示する函数。
void print_release_threshold(uint8_t n){
    usart.printString("Release Threshold: ");
    uint8_t buff[n];
    pad.get_release_threshold(n, buff);
    for(int i=0; i<n; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printByte(buff[i]);
        usart.printString(", ");
    }
    usart.printString("\n");
}

// (n)本のチャンネルのタッチ閾値、リリース閾値の両方を端末に表示する函数。
void print_both_threshold(uint8_t n){
    usart.printString("Touch   Threshold: ");
    uint8_t   touch_buff[n];
    uint8_t release_buff[n];
    pad.get_both_threshold(n, touch_buff, release_buff);
    for(int i=0; i<n; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printByte(touch_buff[i]);
        usart.printString(", ");
    }
    usart.printString("\n");

    usart.printString("Release Threshold: ");
    for(int i=0; i<n; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printByte(release_buff[i]);
        usart.printString(", ");
    }
    usart.printString("\n");
}

// タッチ、リリース両方のディバウンス回数(全チャンネル共通)を端末に表示する函数。
void print_both_debounce_num(){
    usart.printString("Touch   Debounce#: ");
    usart.printNibble(pad.get_touch_debounce_num());
    usart.printString("\n");
    usart.printString("Release Debounce#: ");
    usart.printNibble(pad.get_release_debounce_num());
    usart.printString("\n");
}

// フィルターのサンプリング回数、グローバルCDC値、グローバルCDT値を端末に表示する函数。
void print_filter_global_cdc_cdt(){
    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 CDT10 (0.1 us): ");
    usart.printByte(pad.get_gloval_cdt10());
    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)本の各チャンネルの充放電電流設定値を端末に表示する函数。
void print_cdc(uint8_t n){
    usart.printString("CDC (uA)      : ");
    uint8_t buff[n];
    pad.get_cdc(n, buff);
    for(int i=0; i<n; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printByte(buff[i]);
        usart.printString(", ");
    }
    usart.printString("\n");
}

// (n)本の各チャンネルの充放電時感設定値(10倍値)を端末に表示する函数。
void print_cdt10(uint8_t n){
    usart.printString("CDT10 (0.1 us): ");
    uint8_t buff[n];
    pad.get_cdt10(n, buff);
    for(int i=0; i<n; i++){
        usart.printNibble(i);
        usart.printString("ch:");
        usart.printByte(buff[i]);
        usart.printString(", ");
    }
    usart.printString("\n");
}

// 自動(再)コンフィギュレーションを実行した結果、範囲外になったかどうかを端末に表示する函数。
void print_out_of_range_status_status(){
    usart.printString("Out-Of-Range Status[12:0]: ");
    usart.printBinaryWord(pad.get_out_of_range_status());
    usart.printString("\n");
}

// 自動コンフィギュレーションに失敗したかどうかを端末に表示する函数。
void print_auto_config_failed(){
    usart.printString("Auto Config   Failed?: ");
    usart.printNibble(pad.auto_config_failed());
    usart.printString("\n");
}

// 自動再コンフィギュレーションに失敗したかどうかを端末に表示する函数。
void print_auto_reconfig_failed(){
    usart.printString("Auto-ReConfig Failed?: ");
    usart.printNibble(pad.auto_reconfig_failed());
    usart.printString("\n");
}


const uint8_t n = 4; // 電極をn本使うことにする。
ISR(INT1_vect){ // 電極の状態が変化したら、
    ///*
    uint8_t touch_status = (uint8_t)(pad.get_touch_status() & 0x000F); // ele[3:0]のタッチ状態を取得して、
    pad.write_gpio_data(touch_status);                                 // それをled[3:0] (ele[7:4])から出力する。
    //*/
    /*
   print_touch_status();   // 全チャンネルのタッチ状態を端末に表示し、
   print_filtered_data(n); // 電極データを端末に表示し、
   print_baseline_val(n);  // ベースライン値を端末に表示する。
   //*/
}

int main(){
    usart.init();

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

    usart.printString("\n**********************************");
    usart.printString("\n********** TOUCH SENSOR **********");
    usart.printString("\n**********************************\n");
    
    // ソフトリセットをかける。
    pad.soft_reset();

    // 自動(再)コンフィギュレーションの設定をする。
    pad.set_up_side_limit(202);
    pad.set_low_side_limit(117);
    pad.set_target_level(181);
    pad.set_auto_config(6, 4, 1, 1, // (ffi=6,10,18,34回; retry=0,2,4,8回; are=1,0; ace=1,0
                        0, 1, 1, 1);//  scts=1,0; oorie=1,0; arfie=1,0; acfie=1,0); 
    /*
   pad.set_auto_config_0(6, 4, 1, 1);
   pad.set_auto_config_1(0, 1, 1, 1);
   */

    pad.set_baseline_tracking(2);                               // 初回電極データの上位5ビットを初期ベースライン値としてロードする。
    pad.set_touch_debounce_num(4);                              // タッチ時のディバウンス回数(0~7)をセットする。
    pad.set_release_debounce_num(3);                            // リリース時のディバウンス回数(0~7)をセットする。
    for(int ch=0; ch<n; ch++) pad.set_touch_threshold(ch, 15);  // (チャンネル0~12)にタッチ閾値(0~255)をセットする。
    for(int ch=0; ch<n; ch++) pad.set_release_threshold(ch, 5); // (チャンネル0~12)にリリース閾値(0~255)をセットする。
    pad.enable_touch(n);                                        // (n)本の電極をタッチセンサーとして有効化する。

    print_out_of_range_status_status(); // 自動(再)コンフィギュレーションの範囲外ステータスを端末に表示する。
    print_auto_config_failed();         // 自動コンフィギュレーションに失敗したかどうかを端末に表示する。
    //print_auto_reconfig_failed();

    /*
   print_touch_threshold(n);   // (n)本のチャンネルのタッチ閾値を端末に表示する。
   print_release_threshold(n); // (n)本のチャンネルのリリース閾値を端末に表示する。
   */
    print_both_threshold(n);       // (n)本のチャンネルのタッチ閾値、リリース閾値の両方を端末に表示する。
    print_both_debounce_num();     // タッチ、リリース両方のディバウンス回数を端末に表示する。
    print_filter_global_cdc_cdt(); // フィルターのサンプリング回数、グローバルCDC値、グローバルCDT値を端末に表示する。
    print_cdc(n);                  // (n)本の各チャンネルの充放電電流値設定値を端末に表示する。
    print_cdt10(n);                // (n)本の各チャンネルの各充放電時間設定値(10倍値)を端末に表示する。
    
    pad.enable_gpio(0x0F);           // led[3:0] (ele[7:4])をGPIOにする。
    pad.set_gpio_direction(0x0F);    // led[3:0] (ele[7:4])のIOをOUTにする。
    pad.set_gpio_circuit(0, SOURCE); // led_0 (ele[4])の出力回路をソース(ハイサイドオープンドレイン)にする。
    pad.set_gpio_circuit(1, SOURCE); // led_1 (ele[5])の出力回路をソース(ハイサイドオープンドレイン)にする。
    pad.set_gpio_circuit(2, SOURCE); // led_2 (ele[6])の出力回路をソース(ハイサイドオープンドレイン)にする。
    pad.set_gpio_circuit(3, SOURCE); // led_3 (ele[7])の出力回路をソース(ハイサイドオープンドレイン)にする。


    while(1);
    return 0;
}
void MPR121Class::enable_gpio(uint8_t led_7_0){
    i2c.write(_SLA, GPIO_ENABLE, led_7_0);
}
void MPR121Class::set_gpio_direction(uint8_t led_7_0){
    i2c.write(_SLA, GPIO_DIRECTION, led_7_0);
}
void MPR121Class::write_gpio_data(uint8_t led_7_0){
    i2c.write(_SLA, GPIO_DATA, led_7_0);
}
void MPR121Class::set_gpio_data(uint8_t led_7_0){
    i2c.write(_SLA, GPIO_DATA_SET, led_7_0);
}
void MPR121Class::clear_gpio_data(uint8_t led_7_0){
    i2c.write(_SLA, GPIO_DATA_CLEAR, led_7_0);
}
void MPR121Class::toggle_gpio_data(uint8_t led_7_0){
    i2c.write(_SLA, GPIO_DATA_TOGGLE, led_7_0);
}
void MPR121Class::set_gpio_circuit(uint8_t led_n, uint8_t circuit){
    uint8_t cont_reg[2];
    i2c.read(_SLA, GPIO_CONTROL_0, 2, cont_reg);                                   // 一旦制御レジスタを読み出して、
    cont_reg[0] = (cont_reg[0] & ~(1 << led_n)) | (((circuit >> 1) & 1) << led_n); // 目的のビットだけを書き換えて、
    cont_reg[1] = (cont_reg[1] & ~(1 << led_n)) | ( (circuit       & 1) << led_n); // 目的のビットだけを書き換えて、
    i2c.write(_SLA, GPIO_CONTROL_0, 2, cont_reg);                                  // 制御レジスタに書き戻す。
} 
uint8_t MPR121Class::read_gpio_data(){
    return i2c.read(_SLA, GPIO_DATA);
}