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(); } } }
8月3日(土)と4日(日)はNT金沢2013!
石川県金沢市で8月3日(土)と4日(日)にモノづくりの祭典、NT金沢2013が行われます。
NTとは元々ニコニコ動画に技術系動画(ニコニコ技術部カテゴリ)を投稿したり見ていた人たちが始めた展示交流会・勉強会です。現在はニコニコ技術部の枠だけでなくプロ・アマ・団体・個人を問わずモノづくり、手芸、工作が好きな人が広く集まって開催される一大イベントとなっています。
京都や福岡、名古屋などで定期的に開催されています。
金沢で開催されるNTも今年で3回目となります。今年は会場の規模も大きくし、ゲストの方もお呼びして展示者・見学者共にさらに楽しめるイベントとなる予定です。
公式サイトがありますのでぜひご覧ください。
NT金沢2013 公式ウェブサイト
http://nt-kanazawa.net/
実はイベント運営のほうにも少し関わらせてもらっています。
私は去年作った心拍ぬいぐるみの改良版を展示する予定です。
NT金沢、たくさんの面白い展示がありますのでぜひご来場を!
出展者も大募集中ですので興味ある方はぜひ!
キミノヒカリ -心拍を使ったコミュニケーション- まとめ記事
心拍に同期して光るぬいぐるみ「キミノヒカリ」についてのまとめページです。
追加があればその都度追記していきます。
- moviewさんにてレビューしていただきました(2013/01/30)
スマートフォン向け動画レビューサイトmoveiwさんにてそらのさんに作品を紹介していただきました!
http://www.youtube.com/watch?v=gH6LQ6BK-cc
- 電子工作コンテスト2012にてP板.comプロダクト化支援賞受賞(2012/12/04)
今年も開催された電子工作コンテスト2012にて、なんとP板.comプロダクト化支援賞をいただきました!
http://elecontest.com/prizelist/a7
作品の商品化に向けて動き出すことになりました( `・ω・´)
- Maker Faire Tokyo 2012にて展示とデモ(2012/12/01-02)
日本科学未来館で行われたMaker Faire Tokyo 2012にて金沢大学 ifDL 有志一同のブースで展示とデモを行いました。
Maker Faire Tokyo : http://makezine.jp/
金沢大学 ifDL 有志一同 : http://ifdl.ec.t.kanazawa-u.ac.jp/blog/?p=112
- ニコニコ動画に投稿(2012/11/11)
作品についての動画をニコニコ動画に投稿しました
ブログ記事 : 「心拍で光るぬいぐるみを作ったみた」を投稿しました
ニコニコ動画 : http://www.nicovideo.jp/watch/sm19327418
- いしかわ夢未来博2012にて展示とデモ(2012/11/10-11)
石川県金沢大学産業展示館にて行われたいしかわ夢未来博にて金沢大学のブース内で展示とデモを行いました。
封筒型のiPad miniケースを作ってみた(マグネットスリープ対応)
フェルトで封筒型のiPad miniケースを作ってみました。マグネットスリープ/スリープ解除に対応しています。
Maker Faire Tokyo 2012にて展示予定です!3F-03です。
詳細は近いうちに書きますー
mbedでSerialHalfDuplexが使えなくなったので古いリビジョンのmbedライブラリを使ってみる
ブログをご覧になっている方から2011/04のmbedによるシリアル半二重通信のコードでエラーが出るという連絡をいただきました。
あれ?と思い、その記事を書いた際に作ったプロジェクトをビルドしてみると正常にビルドできました。
ところが新しくプロジェクトを作成し、ソースコードをブログからコピーしてビルドすると、ご指摘いただいた内容と同じ以下のエラーが出ました。
identifier "SerialHalfDuplex" is undefined
expected a ")"
class "mbed::Serial" has no member "Even"
Unable to download. Fix the reported errors...
よくよく見てみると、記事を書いた時のプロジェクトではプロジェクト内のmbedライブラリ(main.cppなどのソースコードと一緒に入っている)のClassesにSerialHalfDuplexが含まれています(図1)。
ちなみにそのリビジョンは26でした(図2)。
図2 "mbed"を選択するとIDEの右側に表示される
一方、新しく作ったプロジェクトでは、このSerialHalfDuplexが存在していません(図3)。
ちなみにリビジョンは最新の45でした(2012/11/25現在)。
詳しくは分かりませんが、現状SerialHalfDuplexが使えなくなってしまっているようです。
しかしmbedのHandbookを見るとSerialHalfDuplexの項目はあり、サンプルコードも以前のままです。どういうことなんだろう...
(追記)と思ったらページのデザインが他とは違う様子。もう使えなくなったってことなんだろうか...
mbed.org Handbook SerialHalfDuplex
https://mbed.org/projects/libraries/api/mbed/trunk/SerialHalfDuplex
そこでとりあえずSerialHalfDuplexが使えていた時点のmbedライブラリを使えばいいのではと思い、以下の方法を取ってビルドを通すことができました。
ちなみにSerialHalfDuplexはリビジョン43まで利用できていたようです。
(1)対象のプロジェクトの"mbed"(main.cppと一緒に並んでいるファイル)をクリック。
(2)すると右側に"Build Details"という項目が出てくる。その中の"Build Homepage"をクリックする。
(3)新しいタブが開くので、その中の"43:e2ed12d17f06 26 10月 2012 Update documentation"をクリック。
(4)前のコンパイラのタブに戻り、対象プロジェクト内の"mbed"を右クリックし、"Delete Buid... Del"を選択し、"mbed"を削除する。
(5)先の"mbed"ライブラリのページに戻り、"Import this build"をクリック。
(6)"Import Library"の項目が出てくるので、"Library Name"はそのまま"mbed"に、"Path:"で対象のプロジェクトを指定。
(7)プロジェクトにRevision 43の"mbed"が追加されているのでこれでビルド可能になる。
詳しい情報をお持ちの方がいらっしゃいましたら教えていただけると幸いです。
「心拍で光るぬいぐるみを作ったみた」を投稿しました
心拍で光るぬいぐるみを作ってみた(ニコニコ動画) http://www.nicovideo.jp/watch/sm19327418
ニコニコ動画に最近作っていた動画を投稿しました!初めてのテクノ手芸作品になります。
脈拍を使ったネタは前々から興味があって、今回がそのアウトプットになりました。
外装のデザインはベースのアイデアは持ちつつ、詳しいところは手芸が得意な方に依頼したりと見た目にも綺麗な作品を目指してみました。
基板起こしてキット化できたらMake:などのイベントで数量決めて配布もいいかなぁなどの妄想もしています。
よろしければご覧ください!
LEDで炎のゆらめき(1/fゆらぎ)をPWMで表現してみる
2012/08/25-26と大垣で行われたMake: Ogaki Meeting 02にてある方がLEDでろうそくの明かりを表現している作品を展示されていました。なかなかリアルで息を吹きかけると消えそうになったりと芸が細かく、面白いと思ってお話を伺ったところ、そこで使っているというLED(Cree社のMX-6、日本では売っていないとのこと)と変換用の基板をいただいてしまいました。
Cree Components XLamp MX-6 LEDs
http://www.cree.com/led-components-and-modules/products/xlamp/discrete-nondirectional/xlamp-mx6
他に流通しているLEDと比べてより炎の色に近いそうです。
せっかくLEDもいただいたので、自分でも作ってみようと思ってmbedで簡単にやってみました。
炎のゆらめきは1/fゆらぎでリアルに表現できるようです。「1/fゆらぎ」という言葉は歌か何かの表現で聞いたことがあった言葉でしたが、ちゃんとこういうところで出てくるものだったんですね。
1/fゆらぎは間欠カオス法で作っています。以下のエレキジャックのページが分かりやすかったです。
LEDイルミネーションの製作(第6回 最終回) 応用編・星のまたたき
http://www.eleki-jack.com/mycom2/2008/07/led6.html
単純に間欠カオス法を実装しただけでもそこそこのリアルさを感じました。
今回のプログラムではループを40ms間隔で回しています。また、少し消灯くらいの明るさの部分が大きいかな?と思ったので、計算結果が0.4未満の場合はその結果に0.8を足して極端に暗い場合をなくしました。
ちなみに上記のどちらの数値も間隔で適当に決めました。
回路はトランジスタを使って100Ωの抵抗を通して駆動しています。本来なら1A流せるLEDのようですが、今回は10mAほどしか流していません。それでもろうそくくらいの明るさは十分出ます。
最後に紙で覆って完成。
ほとんど時間をかけずに作りましたが、割と満足できるものができました。暇があればもっとチューニングしようと思います。
mbedプログラム
#include "mbed.h" PwmOut led(p21); AnalogIn Ap(p20); int main() { float randFloat; float lightPercent; while(1) { randFloat = (float)rand() / RAND_MAX; if(randFloat < 0.5) { lightPercent = randFloat + 2 * randFloat * randFloat; } else { lightPercent = randFloat - 2 * (1 - randFloat) * (1 - randFloat); } if(lightPercent < 0.8) { lightPercent += 0.8; } led = lightPercent; srand(Ap.read() * 10000); wait(0.04); } }