【受信部】
今回PICとボイスシンセサイザとの間をI2Cインターフェイスで繋いだ。I2Cインターフェイスをデフォルトのピン割り当てで使う場合は素直に使えるが、一方他のピンに割り当てる場合は少しややこしい。PIC16F15325のマニュアル15.3項のNoteによると特定のピン(RC3,RC4)だけがI2Cの入力レベル(スレッショルド)に対応しており、他のピンを使う場合はその設定の追加が必要。I2Cはデータとクロックの2つの信号を使うがPICでは2つの信号とも入力に初期設定するだけでなくスレッショルドの変更が必要であり、さらにPPSでは入力だけでなく(当然であるが)出力としても設定する必要がある。今回は以下のように記述した。
// 12:RA1 - I2C CLK - Bidirectional
TRISAbits.TRISA1 = 1; // set Abit1 to input
ANSELAbits.ANSA1 = 0; // set Abit1 to digital
INLVLAbits.INLVLA1 = 1; // ST level instead of TTL
WPUAbits.WPUA1 = ON; // weak pull-up
RA1PPS = 0x15; // SCK1/SCL1
SSP1CLKPPS = 0x01;
// 13:RA0 - I2C DAT - Bidirectional
TRISAbits.TRISA0 = 1; // set Abit0 to intput
ANSELAbits.ANSA0 = 0; // set Abit0 to digital
INLVLAbits.INLVLA0 = 1; // ST level instead of TTL
WPUAbits.WPUA0 = ON; // weak pull-up
RA0PPS = 0x16; // SD1/SDA1
SSP1DATPPS = 0x00;
このI2Cインターフェイスを使ったボイスシンセサイザとの通信は、一見上手く行ったように見えていたが暫く使っているとI2CのSCK(クロック)がLowになったままハングアップした。電源を入れなおしても同じだ。調べると同じような事例もあり、I2Cはソフト的には結構ややこしくロジックアナライザ無しではデバッグも容易でないようだ。よって、I2Cに代えてSPIインターフェイスを使うように変更した。SPIインターフェイスは構造が簡単で送受を同時に行うのが特徴。ソフト的な扱いも単純だが4本も信号線を使うので(最初は/SSをL固定し、それ以外の3本で通信を試みたが上手く機能しなかった)I2Cの2本と比べてPICの足数から自由度が減る事が残念だ。
受信部のプログラムは1000行にもなるのでここでこれ以上掲載するのは止めて、そこで使っているロジックについて概要を解説する。
受信部の最初のポイントは、2つのIRセンサーで赤外線信号を定常的に受信出来ている状態は無情報であり信号欠落した事が情報(イベント)となる事である。これは5ms毎の割込みとソフト的なカウンタで処理することができる。
2つ目のポイントは 2つのIRセンサーの前をどう横切った時どう判断してどんなメッセージを流すか、またノイズをどう排除するかである。
このプログラミングには条件を細かく設定して分岐で処理しても良いが、そうするとプログラムがスパゲティ状態になり天才でもない限り行き詰まるか保守できなく事は目に見えている。こういう場合はステートマシンを使うのが早道である。ステートマシンでは「状態」と「イベント」を定義し、イベントの発生に伴う「アクション」と「状態の遷移」を表形式でプログラミングすることでロジックの「穴」を防ぐことができる。ステートマシンは有向グラフや状態遷移表で表す事が出来る。
今回使うイベントは、
C0)2つのセンサーが信号を受けている
C1)センサー1が遮られた
C2)センサー2が遮られた
CB)センサー1及びセンサー2が遮られた
TO)タイマーが設定した時間を経過した
の5つとした。また状態としてはシンプルに、
IDL) 初期状態
SYN) 人がいない(2つのセンサーでデータを受信)
EN1)人がセンサー1の前に入った
OV1)人が両方のセンサーの前まで進んだ
EX1)人がセンサー1を通り過ぎた
EN2)人がセンサー2の前に入った
OV2)人が両方のセンサーの前まで進んだ
EX2)人がセンサー2を通り過ぎた
とした。これを使って人一人の基本的な動きをトレースするためのステートマシンは次の状態遷移表になろう。この遷移表で実線で囲まれた各升目には3つの欄があり、それぞれある状態にあるときに、あるイベントを受けたときの メッセージ、タイマー操作、次の状態への遷移を表している。
このステートマシンはTimer0で5ms毎に実行されるようにしている。IDLが初期状態で、2つの赤外センサー両方で赤外線を感じる(C0)とSYN状態に移行する。SYN状態が赤外線が遮られていない定常状態である。遷移表で灰色の網掛けは同じ状態に留まることを表している。
人が入店する場合は、
SYN→(C1)→EN1→(C3)→OV1→(C2)→EX1→(C0)→SYN
と移行するはずである。逆に人が退店する場合は、
SYN→(C2)→EN2→(C3)→OV2→(C1)→EX2→(C0)→SYN
と移行するはずである。単純に考えればEX1→(C0)の時に「いらっしゃいませ(WELCOM)」、逆にEX2→(C0)の時に「ありがとうございました(THANK)」と発声すればよい。
しかし、事はそう単純ではなくこの表には空欄が多い。つまりまだ振る舞いが定義されてない条件がある事が分かる。空欄を全て埋めてステートマシンは完成する。例えばEN1-(C0)は人がセンサー1まで来て引き返した場合である。この時どう反応すべきか。またSYN-(CB)等のようにオレンジで網掛けしている欄は人一人の移動としてはあり得ない状態であり、たぶん二人の人がすれ違った場合、或いはセンサーが赤外リモコン等ノイズを拾った場合か、または送信部がいきなり停止した場合などが考えられる。このような場合にどう振舞えばよいか、このシステムへの要請に応じて欄を埋める必要がある。
これを実際に埋めてみたのが次の遷移表である。INITは電源を入れた時一度だけ通る状態であり、2つの赤外線を最初に感知したときチャイムを鳴らすために設けた。なお、このステートマシンは人が一人の場合は正しく判断できるが二人が絡むと結果は予測できない。
ここでT+2は2秒のタイマーを設定、Toffはタイマーを止める事を表す。赤色で示した欄はこの状態になることは無く何らかの内部エラーが発生した事を表すので動作としてはエラーと叫んでIDL状態に戻す事とした。またオレンジの欄も一人の移動では発生しない状態なので、「今日も良い天気」「大谷がんばれ」「大吉」など幾つか用意したメッセージをランダムに選んで発声するとともにIDLやSYN状態に戻す事とした。なお実際のメッセージは次の様になっている。HELLO こんにちわ
WELCOM いらっしゃいませ
THANK ありがとうございました
CHM チャイム音
ERR エラー
RDM (ランダムなメッセージ)
このほか、「送信機の電圧が低下しました」、「受信機の電圧が低下しました」なども必要に応じて喋るようにしている。
このステートマシンをプログラミングし実際に動かしてみたら上手く動いているようだ。センサーの前を移動したとき簡略化した状態遷移は次のようになった。
ID-S1-ET
ID-S2-e0-S2-e0-S2-e0-S2-e3-o2-eT
ID-S2-e0-S2-e0-S2-e0-S2-e0-S1-ET
ID-S1-E0-S2-e0-S1-E0-S1-E0-S2-e0-S1-E0-S2-e0-S1-E3-OT-O1-E0-S1-E3-O2-X0-S2-e3-o1-x0-S1-E3-O2-X0-S2-e3-o1-x0-S1-E3-OT-O1-E0-S1-E3-O2-X0-S2-e3-o1-x0-S1-E3-O2-X3-O2-X0-S2-e3-oT
ID-S2-e3-o1-x0-S1-E3-O2-X0-S3
ここでIDがIDL状態、S1がSYN状態で1番のイベントが発生した事を表す、EOXの各文字はそれぞれEN1,OV1,EX1、eoxの各文字はそれぞれEN2,OV2,EX2、また0123Tの各文字はそれぞれC1,C2,C3,TOのイベントを表す。
このシーケンスを見てセンサーの前で何が起きており、どういうメッセージが発声されているか分かるだろうか。例えば最初の ID-S1-ET は人が入口からセンサー1まで移動し、その状態で2秒経過したので「こんにちわ」と発声した事を表す。
なおステートマシンが5ms毎に動くのに比べ発声には時間がかかるので発声要求はキュー(FIFO)に入れて順次喋るようにしている。
このようなプログラミングの答えは1つとは限らない。もっと複雑な動作をさせたければセンサーを増やしたり状態の数を増やすのも良いだろう。目的に合ったステートマシンを構築すれば今回のように一人ではなく二人の動作を追跡できるかもしれない。昨今流行のAIに作らせる/DEEP LEARNINGで学習させることも出来るかもしれない。
話は変わるが、最近PICが品薄のようである。 今回使った16F15325は秋月で在庫なし。そのほか多くのPICが在庫なしで入荷予定も立たないという事であった。EUSARTを2個内蔵したPICで手頃なものはこれしかないので困ったものである。
0 件のコメント:
コメントを投稿