PAWセンサをmbedから使ってみましょう。[更新]

こんにちは、水曜日に担当が変更になりました。佐倉です。

今回はマイクロマウスシリーズではなく、PAWセンサの使用方法を、mbedとの組み合わせの例でご紹介します。
PAWセンサは下の左側画像のようなセンサです。ウレタンスポンジ部分の、どのあたりがどれくらいの距離押し込まれているかを検知する事ができます。
PAWセンサ+mbed
PAWセンサの原理は、LEDとフォトトランジスタ(光検出器)を対向させて置き、密度の変化に伴う光の透過率の変化を検出するというものです。
PAWセンサの原理
PAWセンサには、LED2個×フォトトランジスタ2個による、4箇所の検出エリアが存在します。
PAWセンサの検出エリア
2つのLEDの出力を切り替えながら、フォトトランジスタの出力を見る事で、この4箇所のセンシングを行います。

しかし、フォトトランジスタの応答はあまり早くないため、LEDを切り替えてから一定期間以上待機する必要があります。
これに伴い、以下のようなシーケンスで使用する事が求められます。
PAWセンサのシーケンス

このシーケンスと、PCへの読み出し値の送信を行うmbedのソースコードが以下のものになります。
30msごとにPAWセンサの、その時の値をPCへ送信します。

2014/02/12更新
一時的なゼロ点を考慮したコードに更新しました。外乱光の影響を受けにくくなりました。
[c]
#include "mbed.h"

AnalogIn ain1(p15);
AnalogIn ain2(p16);

DigitalOut led1(p11);
DigitalOut led2(p12);

Serial pc(USBTX, USBRX); // tx, rx
Ticker pawtick;
Ticker serialtick;

unsigned short paw1, paw2, paw3, paw4;
unsigned short ain1_v0, ain2_v0;

typedef enum
{
CHARGE_1,
READ_1,
CHARGE_2,
READ_2,
}t_paw_state;

t_paw_state pawstat = CHARGE_1;

void periodicPawRead()
{
switch(pawstat)
{
case CHARGE_1:
ain1_v0 = ain1.read_u16();
ain2_v0 = ain2.read_u16();
led1 = 1;
break;

case READ_1:
paw3 = ain1.read_u16() – ain1_v0;
paw4 = ain2.read_u16() – ain2_v0;
led1 = 0;
break;

case CHARGE_2:
ain1_v0 = ain1.read_u16();
ain2_v0 = ain2.read_u16();
led2 = 1;
break;

case READ_2:
paw2 = ain1.read_u16() – ain1_v0;
paw1 = ain2.read_u16() – ain2_v0;
led2 = 0;
break;
}

switch(pawstat)
{
case CHARGE_1:
pawstat = READ_1;
break;
case READ_1:
pawstat = CHARGE_2;
break;
case CHARGE_2:
pawstat = READ_2;
break;
case READ_2:
pawstat = CHARGE_1;
break;
}

}

void periodicSerialSend()
{
pc.printf("%05d,%05d,%05d,%05d\n", paw1, paw2, paw3, paw4);
}

int main()
{
pc.baud(115200);

pawtick.attach_us(periodicPawRead, 500);
serialtick.attach_us(periodicSerialSend, 30000);
}

[/c]

旧バージョン(こちらでも動きますが、外乱光の影響を受けやすい可能性があります。)
[c]
#include "mbed.h"

//PAWセンサのシーケンスのステートを定義
typedef enum
{
CHARGE_1,
READ_1,
CHARGE_2,
READ_2,
}t_paw_state;

//PAWセンサのPhoto1をmbedのp15に接続
AnalogIn ain1(p15);
//PAWセンサのPhoto2をmbedのp16に接続
AnalogIn ain2(p16);

//mbedのp11をPAWセンサのLED1に接続
DigitalOut led1(p11);
//mbedのp12をPAWセンサのLED2に接続
DigitalOut led2(p12);

Serial pc(USBTX, USBRX); // tx, rx
Ticker pawtick;
Ticker serialtick;

unsigned short paw1, paw2, paw3, paw4;
t_paw_state pawstat = CHARGE_1;

void periodicPawRead()
{

//現在のステートに基いて処理を実行
switch(pawstat)
{
case CHARGE_1:
led1 = 1;
break;
case READ_1:
paw3 = ain1.read_u16();
paw4 = ain2.read_u16();
led1 = 0;
break;
case CHARGE_2:
led2 = 1;
break;
case READ_2:
paw2 = ain1.read_u16();
paw1 = ain2.read_u16();
led2 = 0;
break;
}

//次のステートに移行
switch(pawstat)
{
case CHARGE_1:
pawstat = READ_1;
break;
case READ_1:
pawstat = CHARGE_2;
break;
case CHARGE_2:
pawstat = READ_2;
break;
case READ_2:
pawstat = CHARGE_1;
break;
}

}

void periodicSerialSend()
{
//PCへ値を送信する。0~65535をとり得る。0~3.3Vに対応。paw1~paw4は、エリアの画像のch1~ch4に対応
pc.printf("%05d,%05d,%05d,%05d\n", paw1, paw2, paw3, paw4);
}

int main()
{
//PCと115200bpsで通信する
pc.baud(115200);

//PAWセンサの駆動用タイマー 500uS
pawtick.attach_us(periodicPawRead, 500);

//シリアル通信用タイマー 30mS
serialtick.attach_us(periodicSerialSend, 30000);
}

[/c]

このサンプルでは2つのタイマ割り込みが起こっていますが、優先順位が不明なので、シリアル通信中はPAWセンサを駆動するタイミングが狂っている可能性があります。
純粋なmbed的プログラミングでは割り込み優先順位の設定はできないようなので、このあたりは妥協するか、
PAWセンサのタイミングを優先する場合には、シリアル通信の処理を割り込み外のポーリングで行うようにしたほうが良いでしょう。

PAWセンサはあまりタイミングにシビアではないので、これくらい適当でもうまく動きます。

きちんとマイコンを使う場合には、タイマによる波形出力でLEDを駆動し、タイマトリガでA/D変換を開始し、A/D変換終了信号でDMAないしは割り込みを使って値を読み込むのが王道で、処理も食いません。

次回は、PCでの値の取り込みをご紹介します。

PAWセンサーの詳細なマニュアルはこちら

PAWセンサ
PAWセンサ用ケーブル
mbed NXP LPC1768