PIC新16Fのタッチセンサ(mTouch)を使ってみた


概要

PICの新16FシリーズからmTouchという静電容量式のタッチセンサ用モジュールが載り始め、タッチセンサが簡単に作れるようになったというので遅ればせながらちょっと試してみました。

基礎

新しいPIC16F(Enhanced Mid-Range PIC)にはCapacitive Sensing Module(CSM)が載ったものがあります。タッチセンサの実現にはこのモジュールを使います。
タッチパッドに指が触れることでマイコンから見たタッチパッドの静電容量が大きくなります。内部ではこのCと別に内部で用意されたRで発振回路が構成され、タッチパッドの静電容量で周波数が変化するクロック源となります。
PICに載っているタイマのクロック源としてこのCR発信器によるクロックを指定することができます。通常、指がセンサに触れると静電容量が大きくなり、発振周波数は小さくなります。すなわちそれがクロックとなっているタイマにとっては指が触れるとカウントが遅くなります。
一定時間ごとにタイマの値を監視し続けることで静電容量の変化を読み取ることができます。
(この仕組みを理解するのにちょっと時間がかかってしまいました^^;)


きむ茶工房ガレージハウスさんの記事が大変参考になりました。

容量検知(CPS)モジュールをテストしてみます
http://www.geocities.jp/zattouka/GarageHouse/micon/MPLAB/12F1822/CPSM/CPSM.htm

実装

(1年以上眠らせていた)18ピンのPIC16F1827を使いました。新16Fを触るのは初めてでしたが、これまでの16Fと同じように使えました。

PIC1F1827データシート
http://ww1.microchip.com/downloads/en/DeviceDoc/41391D.pdf


基本的な制御方法はエレクトロニクスラボさんの記事が分かりやすく参考にさせていただきました。

CSMモジュールの使い方
http://www.geocities.jp/ishibappo/nouhau/csmnotsukaikata/csmnotsukaikata.html


pickit2で書き込もうとしたらデバイスが見つからないとなって、もしかしてpickit3しか対応してないのか!と思いましたがデバイスファイルを更新したところ使えました。
コンパイラにはmikroCを使用。


タイマ0はプリスケーラ1:128に設定し、定間隔でのキャプチャ用に使用。クロックは内部8MHzのため、(1 / 8000000) * 4(システムクロック) * 128 * 256 = 0.016384[sec] = 16.384[ms]ごとにタイマ0による割り込みがかかります。
つまりこの16.384[ms]の間に発振した周波数をタイマ1でカウントすることになります。
ちなみにタイマ0は8bit、タイマ1は16bit。キャプチャ周期を大きくするとその間にカウントされる値も増えるため、ビット数の多いタイマ1が適しています。
この周期は任意で、例えば上記のエレクトロニクスラボさんでは1.248[ms]にされています。あまりにも周期を長くし過ぎると発振周波数がタイマ1の16ビットを超えてしまう可能性があります。一方短すぎるとカウント値が小さいため判別のしきい値設定がシビアになってきます。


RA1にはLEDが接続されており、タッチすると光るようになっています。
タッチのしきい値は値をUARTで出力するようにして、目視で決定しました。本来であれば環境や電圧によって非タッチ時の値も常時変化するので常に基準値も更新し続ける必要がありますが、今回はとりあえず動かしてみたという程度なのでその処理は行なっていません。キャプチャ値の平均化(16点くらい?)も本来は必要です。
タイマ1の読み取り値は16bitですが、下位はそんなに必要ないと思い上位8ビットを見るようにしています。


タッチパッドは転がっていたアルミ板をジャンパワイヤにくっつけているだけのものです。12x18[mm]くらい?


また、タイマ0による割り込みを使わなくてもメインループ内で定間隔実行するようにしても同じ動作となります。


CPSCON0レジスタの2, 3bit目はCapacitive Sensing Oscillator Range bitsでレンジを決められ、それにより電流も変化するようです。今回は「11 = Oscillator is in high range. Charge/discharge current is nominally 18 µA.」を選択。小さくするとカウント値も小さくなるようです。

動作

タッチは快適に動作しました。バタつきや遅延、取りこぼしもなく良い感じで動いています。
値は以下のような感じです。アルミ板の上に1mmほどのプラスチック板を置いて、その上からタッチもしてみました。

非タッチ時 タッチ時
直接タッチ 23前後 2前後
プラスチック板あり 21前後 17前後

しきい値は10に設定しています。
プラスチック板ありだと直接タッチに比べると変化はかなり小さくなりました。しかしタッチを読み取るには十分です。(しきい値10のままではもちろんLEDは光りません)


導体などを載せても指と同じような効果が得られます。

まとめ

PICでとても簡単にタッチセンサを作ることができました。
さらなる安定動作に向けて処理方法を調べたり考えたりしていこうと思います。


そういえばタッチセンサのパターンを基板上に作る時は単にパッドを置くだけのがいいのか、GNDもしくはVDDのパターンが入り乱れたようなのがいいのか、どうなんだろう。

プログラム

#define THRESHOLD_H 10

unsigned int cap;        // For calc
unsigned char intFlag;   // Interrupt flag
unsigned char cnt;       // For UART cnt

void interrupt() {
     TMR0 = 0;
     INTCON.TMR0IF = 0;
     intFlag = 1;
}

void mainFunc (void) {
     CPSCON0.CPSON = 0;

     cap = (TMR1H << 8) + TMR1L;
     
     if ((cap >> 8) > THRESHOLD_H) {    // Not touch
          PORTA.RA1=0;
     } else {                           // Touch
          PORTA.RA1=1;
     }

     TMR1L=0;
     TMR1H=0;
     CPSCON0.CPSON=1;
     
     if (cnt++ == 10) {                 // 10回に1度(16.384 * 10[ms])くらいにしないと早すぎる
          UART1_Write(cap >> 8);
          cnt = 0;
     }
}

void main() {
     cap = 0;
     intFlag = 0;

     OSCCON = 0x70;             // Internal 8MHz clock
     ANSELA = 0x01;             // RA0 for cap sensor
     ANSELB = 0x00;
     TRISA = 0x01;
     TRISB = 0x00;
     
     UART1_Init(9600);
     Delay_ms(100);
     
     CPSCON0 = 0b00001100;      // 11 = Oscillator is in high range. Charge/discharge current is nominally 18 uA.
     CPSCON1 = 0x00;            // Port select
     T1CON = 0b11000001;
     T1GCON.TMR1GE = 0;
     TMR0 = 0;
     OPTION_REG = 0b00000110;   // Prescaler Rate Select bits 110 1:128
     INTCON = 0b01100000;       // PEIE, TMR0IE = 1
     
     TMR1L=0;
     TMR1H=0;
     CPSCON0.CPSON = 1;
     INTCON.GIE=1;

     while(1) {
          if (intFlag == 1) {    // Every 16.384ms
               intFlag = 0;
               mainFunc();
          }
     }
}