PICで一定時間のスリープ(ウェイト)を行う

アセンブラではスリープ処理も,ひと苦労

プログラムの中で一定時間処理を止めたい場合、C言語だとsleep()関数を使用することで秒単位のスリープ処理を行えます。

一方、PICでアセンブラを使って処理を行う場合はどうかというと、残念ながら一定時間処理を待つという命令は有りません。アセンブリのニーモニック一覧を見るとSLEEPというのがあるので、一見するとこれでいけそうな気もしますが、残念ながらPICのSLEEPはMPU自体をスリープモードにする(Windowsでいうサスペンド状態)命令なのでこれは使えません。

このような場合、PICではループを空回しする事でビジーウェイトを作り、所定の時間だけ経過させるという方法を取ります。

何回ループを空回りさせるべき?


ループ空回りで時間をつぶすのは良いとして、ここで問題になってくるのは、ループを何回まわす必要があるかが分からないという事です。これに関しては、PICのクロック周波数から1命令あたりの消費時間を求めて、計算を行うという手間が必要になってきます。


クロック周波数と周期は逆数の関係になるので、例えばPICを20Mhzで駆動させている場合1クロックサイクルは”20M分の1秒”になります。
1Mは100万なので2000万分の1秒ということになります。


ただし、例えばPIC16F84Aのチップを使っている場合、1命令の実行に4クロックサイクル(分岐命令を除く)必要となります。なので、1命令の実行に必要な時間は500万分の1秒です。これは計算すると200ナノ秒(0.2マイクロ秒)に相当します。

先ほど”1命令の実行に4クロックサイクル必要”と書きましたが、自分が使用しているチップが1命令あたり何サイクル必要かは、製造元のMicrochipが公開しているデータシートに載っています。

データシートから引用した上の図を見ると、OSCから入力されたクロック4つで、PC(プログラムカウンタ)が1回分処理される事が分かります。

また、データシート最初に記載されている、機能一覧ページを見ると以下のような記述があり、ここからも上記の計算が合っていることが分かります。

Operation speed: DC -20MHz clock input
                 DC -200ns instruction cycle




というわけで、仮に1マイクロ秒の待ちを発生させたい場合は、5命令分の待ちが必要ですし、1ミリ秒待ちたいなら5000個の命令を実行されれば良いことが分かります。
但し、GOTO命令などの分岐命令は、2サイクル分の時間が必要な事に注意が必要です。

アセンブラでウェイト処理を作る


上記の計算で基礎資料がそろったので、実際にウェイト処理を作ります。

WAIT_USEC			EQU	0x20	;何マイクロ秒ウェイトするか(callerが指定する)
 
; WAIT_USECレジスタで、指定された時間(マイクロ秒)だけ待つ。
WAIT_TIME
	; 1回5cycle掛かる -> 20MHzで1usec消費
	NOP
	NOP
	DECFSZ	WAIT_USEC, F
	GOTO	WAIT_TIME
 
	RETURN


上記の処理を見てみると、WAIT_TIMEラベルで示される処理でループを行っています。
ループはNOPが2,DECFSZが1つ,そしてgotoが1つあり、gotoは命令の実行に2サイクル必要なので、合計2+1+2=5命令分の時間を消費し、丁度1マイクロ秒の待ちになります。
さらにそれをWAIT_USECレジスタに指定された回数分ループさせています。

この処理の呼び出し元は、以下のような記述になります。

	; 200マイクロ秒スリープする
	MOVLW	200
	MOVWF	WAIT_USEC
	CALL	WAIT_TIME



WAIT_USECにスリープさせたい時間を予めセットしてからWAIT_TIMEサブルーチンをコールする事で、任意の時間処理をスリープさせる事が出来る様になりました。

※ループ処理について知りたい場合は、こちらの記事も参考にしてください
PICアセンブラにおける分岐/ループ処理の組み立て方



この方法の欠点は?

上記の方法で、指定した時間だけ処理を待たせる事が可能となりましたが、残念ながらこの方法も万能では有りません。

例えば、何らかの処理を行いながら、10秒ごとにブザーを鳴らしたいという処理があった場合、上記の方法だと10秒間、空ループをひたすら実行し続ける事になるので、他の処理が行えません。
一方、10秒の間に”何らかの処理”のほうを作業させてしまうと、この処理に何クロックサイクル使用するかを確定できない為、こんどは10毎に処理を行うということが出来なくなってしまいます。

上記のような場合は、割り込み機能というものを使って対応する事になるのですが、
これについてはまた後日書くことにします。

関連記事

One Response to “PICで一定時間のスリープ(ウェイト)を行う”

  1. 匿名 より:

    人類に有益な情報をありがとうございます

    cでは__delay_us(n)がよういされてますが、
    いちいち xtal/4000000とかやってて信用ならないものになってます

コメントを残す

メールアドレスが公開されることはありません。