PIC で長時間のウェイトを実現するために、8 ビットカウンタのループを複数段で構成して、むしろ問題を複雑に考えすぎていました。そもそも難しく考えず、「16 ビット以上のカウンタ変数を利用できるループを作る」と考えればよく、そうすれば問題は単なる一次方程式の問題で終わります。年が明けてから気が付いた覚書き。
国民機起動音発生装置 PiPo*1のソースコードを読んでいて発見しました。いや、ホント、なんでこんな難しく考えていたのかなぁ…という感じです。*2
1 段構成のループで実現できるのは高々 1000 サイクル、400 μ秒程度。9 行目に NOP を追加して多少の時間延長は可能ですが、ループカウンタ cnt がどう頑張っても 8 ビットしかないので短時間のウェイト向きです。キャリー フラグ(Carry Flag)を利用してループ脱出を判断しているため、cnt で指定した回数に加えて、余分に 1 回ループが回ります。
call LOOP ; 2c ; ... LOOP: ; 0x4F + 1 = 80 回ループする movlw 0x4F ; 1c movwf cnt ; 1c, 0x4F → W → cnt movlw d'1' ; 1c, 1 → W LOOP_MAIN: ; 必要に応じてここに NOP を放り込むことも可能 subwf cnt, F ; 1c, cnt - W → cnt btfsc STATUS, C ; 1c, cnt が 0x00 → 0xFF でキャリー発生(0 → C) goto LOOP_MAIN ; 2c return ; 2c
decf
ではなく、わざわざワーキング レジスタを使って subwf
を使うのは、decf
ではキャリー フラグが変化しないため、と思う。ゼロ フラグだけでループを組めれば、ワーキング レジスタは不要になるかもしれないが、そのパズルには挑戦していません。
cntH と cntL を用いて、ループ カウンタを 16 ビット幅にしたものです。ウェイト処理が二重になっていますが、各段ごとにウェイト数を計算するとかヤヤコシイことを考えずとも、希望のループ回数を cntH と cntL に WORD 値で直接指定すればよいだけなので簡単。
call LOOP ; 2c ; ... LOOP: ; 0x4FFF + 1 = 20,480 回ループする movlw 0x4F ; 1c movwf cntH ; 1c, 0x4F → W → cntH movlw 0xFF ; 1c movwf cntL ; 1c, 0xFF → W → cntL movlw d'1' ; 1c, 1 → W LOOP_MAIN: subwf cntL, F ; 1c, cntL - W → cntL btfss STATUS, C ; 1c, cntL が 0x00 → 0xFF でキャリー発生(0 → C) subwf cntH, F ; 1c, cntH - W → cntH btfsc STATUS, C ; 1c, cntH が 0x00 → 0xFF でキャリー発生(0 → C) goto LOOP_MAIN ; 2c return
更に長時間のウェイトが必要であれば、同じ考え方でカウンタ変数とキャリー発生の条件分岐(13~18行目)を増やせば多段構成が可能ですが、もうここまで来ると、省電力動作*3のためにもウォッチドッグタイマなど別の方法が良いと思います。
call LOOP ; 2c ; ... LOOP: ; 0x4FFFFF + 1 = 5,242,880 回ループする movlw 0x4F ; 1c movwf cntX ; 1c, 0x4F → W → cntX movlw 0xFF ; 1c movwf cntH ; 1c, 0xFF → W → cntH movlw 0xFF ; 1c movwf cntL ; 1c, 0xFF → W → cntL movlw d'1' ; 1c, 1 → W LOOP_MAIN: subwf cntL, F ; 1c, cntL - W → cntL btfss STATUS, C ; 1c, cntL が 0x00 → 0xFF でキャリー発生(0 → C) subwf cntH, F ; 1c, cntH - W → cntH btfss STATUS, C ; 1c, cntH が 0x00 → 0xFF でキャリー発生(0 → C) subwf cntX, F ; 1c, cntX - W → cntX btfsc STATUS, C ; 1c, cntX が 0x00 → 0xFF でキャリー発生(0 → C) goto LOOP_MAIN ; 2c return