12)非同期パルス

毎分数パルスから数十パルスのパルス数を、moteで観測するにはどうすればよいだろうか?
GPIOの入力の変化で数えさせても可能なのだが、変化があるごとにアプリが起動することになるので、わりと電池を食ってしまいそうだ。
外付けの省電力マイコンのタイマーカウンターでカウントさせて、一定時間ごとにシリアル通信から読むのはどうだろう。
Sleepさせておいて、通信する時だけ起こせば消費電力を抑えられそうだ。

そこで手元にPIC12F1822があったので、試してみることにした。
シリアルI/FはSPIとI2Cのどちらにするか迷ったのだが、以前SPIで似たものをやったことがあったのでSPIを使うことにした。
(実はこれが後でやっかいなことになる。)
各信号(SS,SCLK,SDO,SDI)の接続は下図のようになる。


moteがMaster、PICがSlaveになる。
パルスカウントはTMR1を使う。
TMR1のクロック入力T1CKIはシュミットになっているので、バッファなしで大丈夫そうだ。
Sleep中もカウンターは動作するので、その点もクリアしている。

PICのクロックはHFINTOSCの16MHzに設定しておいて、動作確認後に落としていこうと思っていた。
PIC12F1822のProgram Memoryは2KWなので、ASMで組むことにした。
Sleepからの復帰はSSP1の割り込みで行えば良さそうだ。

moteのSPI通信のバイト長は2の倍数なので、4バイト長に設定して後半の2バイトにTMR1の16bitカウンター値を乗せるようにしたいと思った。
moteのFWはappの02-spiをほぼそのまま使用した。
SPI_TX_BUFFER_LENGTHを4に変更するだけで4バイトのSPI通信を1秒ごとに実行してくれる。

PIC側は割り込みサービスでほとんどの処理を行い、mainはSleepのみといった構成になる。
SSP1の割り込みが発生した時点で1バイト目のデータがSSP1BUFに入っているので、フラグをクリアして2バイト目以降を待つ。
2バイト目以降はSSP1STATのBFが1になるのを待って、TMR1Hの値を2バイト目の終了時、TMR1Lの値を3バイト目の終了時にSSP1BUFに書き込めばよいはずだ。

PICのプログラムを組んで動作確認をしたが、これが全くうまくいかない。
書き込めなかったり、値が化けたりで安定しない。
ひょとしてSleepから復帰後のOSCが安定するまでの時間が足りないのか?
OSCの状態を示すOSCSTATの内容を見たいのだが、PIC12F1822に空きピンがない。
困ったなーと部品箱をつついてみたら、なんとPIC16F1823が1個だけ残っていた。
そこで下図のように組み直して、CLKOUTとRC4を観測できるようにしてみた。


また、SPI通信の最初でSleepから復帰させたいのでSS信号をRA2につなぎ、状態変化割り込み(RA2の立下り)でSleepから復帰するように変更してみた。
下図に転送長を10バイトに設定した時の各信号の状態を示す。


RC4にはOSCSTATのHFIOFLの状態を出力させている。
Sleepから復帰してHFIOFLが立つまで130usec以上かかっていた。
電源電圧を2.2V~3.6Vまで変化させてみたが、この時間はあまり変化しなかった。温度は室温(20℃)であった。

それなら、9、10バイト目に書き込めばよいかなと思ってやってみたのだが、多少ましにはなったものの、MSBが化けたり、1bitシフトしたりしてしまう。
まだ間に合わないのかとBFフラグのスキャンループを次のようにしてみた。

    BANKSEL SSP1STAT
    MOVFW   SSP1BUF     ; Dummy Read
    MOVWF   xmit_data

Loop
    MOVWF   SSP1BUF
    BTFSS   SSP1STAT,BF
    GOTO    Loop
    MOVWF   SSP1BUF

これならBFが立つ前後でSSP1BUFに書き込むので、どちらかがヒットしそうだ。
だがこのLoopは終わらなかった。無限Loopになるのだ。
Loopの中のMOVWFをコメントアウトするとちゃんと終わる。(なんじゃこりゃ)
BFの替わりにPIR1のSSP1IFを使ってみたらどうだろう。
PIR1とSSP1BUFはバンクが違うのでSSP1BUFのアクセスを間接アドレッシングに変更せねば。

    MOVLW   0x02
    MOVWF   FSR0H
    MOVLW   0x11
    MOVWF   FSR0L
    BANKSEL PIR1
    BCF     PIR1,SSP1IF
    MOVWF   xmit_data

Loop
    MOVWF   INDF0
    BTFSS   PIR1,SSP1IF
    GOTO    Loop
    MOVWF   INDF0

こちらはちゃんと終わる。(なんじゃこりゃ)
しかし、これでもまだ安定しない。
試しにOSCTUNEをMaxにしてみたら、少し良くなったがごくたまにこける。

まだ間に合わないのか、もう32MHzにするしかないかな。
ということでx4PLLをenableに設定し、Sleepから復帰にかなりの時間がかかりそうなのでSleepなしで動作を確認してみる。

速い、バッチリです。
やはり16MHzでは無理だったのか。
再度タイミングを確認してみよう。
下図はPIC12(L)F1822/PIC16(L)F1823 Data SheetのP242からの抜粋である。


SCKはCKP=0、CKE=1になり、SCKの立上がりでDataがラッチされ、立下りでシフトされる。(moteのapiではDN_SPI_CPOL_0、DN_SPI_CPHA_0のことで、前からまぎらわしいと思っていた。)

SSP1BUTからの読み出しはダブルバッファになっているので、BFが立ったあと次の1バイトが終わるまでに読み出せばよいので楽ですよねと書いてあった。
そののりで書き込みもいけるのかなと思ったのが間違い。

上図のWrite Collision detection activeが立っている間は書き込みが無視される。
activeになるのはSCKがLで最初の有効な書き込みが行われた時だ。
inactiveになるのは8bitシフト後のSCKの立下り時だ。
次のデーターのMSBはWrite Collision detection activeがinactiveになったすぐ後のSCKの立上がりでラッチされるので、SCKの半サイクルよりもデーターラッチのsetup時間分短くした時間しかない。
SCKはmoteのCPU clockの1/16になっており、こちらの説明では2.16usecの半分の1.08usecである。

<下図は実測値>


moteのデーターシートによるとSPIM_MOSI Data Setupは30nsec(min)となっている。

書き込みに許される時間は約1usecとなり、16MHzのPICでは4clockしかない。
バイト間のSCK=Lの時間をストレッチできればよいのだがその機能は無く、SCK周波数を落とせればよいのだが、1/16が最低のようだ。
32MHzで行きましょう。

PLLの安定まで2msecかかるようなので、4バイトを2回送るようにしよう。
1回目でSleepから復帰させ、2msec後に2回目の通信でデータを送るようにした。


1回目と2回目の間に
       OSTimeDly(2);
を入れている。

大丈夫そうなので、再度PIC12F1822に実装した。
CLIには1秒ごとに受信したカウンター値が3、4バイト目に入る。
(SS信号は2回出るので、カウンター値も2つづつ上がる。)


66はコマンドでPIC側で読んで66ならカウンター値を返すようにした。

これで完了だが駄作っぽい気がする。
今回使ったPICよりも、もっとスマートに実装できるデバイスがあるのではないだろうか。
少ない利点の1つは、moteがDisconnectしてResetがかかってしまっても、カウント値はPICが保持しているので、電源が落ちなければカウント値が消えてしまうことはない。

□ソースをこちらに置きました。
mote
PIC


◎追記 [2017.5.22]

気持ち悪いので他の方法を試してみよう。
再々度タイミングチャートを見てみると、Collision detection activeはSSがHの状態ではinactiveである。
SSがLになる前にSSP1BUFへの書き込みを行えば、最初の1バイトはセットできるということだろう。
1バイトづつ通信してねと言っているのだ。

それではmoteから2バイトを4回送ることにして、1回目でSleepから復帰、2回目でコマンド発行、3回目でカウンター値の上位バイト読み出し、4回目でカウンター値の下位バイト読み出しとしてみた。
下図が改造後の観測結果である。


連続して4回出しても結構間が開いていて約290usec間隔となっている。
2回目をPICが受信している頃には、クロックは安定していると思われる。
全体で約1msecで、改造前の2/5程度になった。
PICのクロック設定は8MHzで動作している。
これならまあまあじゃないですかね。
SSは4回出るので、カウント値も4つづつ上がる。



□ソースをこちらに置きました。
mote2
PIC2

コメント

このブログの人気の投稿

13)こんどはI2Cで

2)OnChipSDKとは