前回の精度の話で、計測の分解能は8μsと記した。それでは、正確な8μsは、どのように実現すればよいのだろう。
ロジック上は、一定間隔毎に割込みをかけ、割込みの回数をカウントすることでシャッターの開時間を計測している。割込みが正確な間隔でかからなければ計測値の意味も損なわれることとなり、プログラムの心臓部と言える部分である。
PICのタイマ割込みは、タイマレジスタが満了して0に戻る際に割込みが発生する。前の割込みが発生してから次の割込みが発生するまでを正確に8μsに調整しなければならない。
タイマ割込みでは、タイマの初期値を設定し、そこから定間隔でカウントアップを開始する。カウントアップはプログラムで行うのではなく、設定条件に従ってCPUの中で自動的に実行される。そして、カウンタのビットがフルになり0に戻った瞬間に割込みが発生する。
タイマ初期値の設定は、プログラムに記述する処理内容に応じ前後に移動する。どのような場合でも、割込みが発生してからタイマ初期値設定処理の前にかかる時間(Xμs)だけ差し引いた(8-X)μsを計測する値をタイマの初期値として設定しなければならない。
ところが、CのプログラムソースではXμsを正確に知ることはできない。なぜなら、命令時間が明らかになっているのはアセンブラ命令についてであり、コンパイルする前のCのプログラムでは計算できないのである。
そこで、PICの開発環境であるMPLABXで、
プロジェクトディレクトリdistdefaultproduction
にある
プロジェクト名.lst
というファイルに含まれるアセンブルリストでコンパイル後のアセンブラプログラムから処理時間を計算しなければならない。
PICは、ほとんどの命令は、4クロック(=1サイクル)で実行される。たとえば、CPUクロックが4MHzの場合、1命令は1μsで実行される。例外として条件分岐やジャンプにかかわる命令は2サイクルが必要となる。
また、タイマに初期値がセットされると、書込み直後の2サイクルはカウントアップが停止する等、データシートを注意深く読まなければ正確な初期値を算出することができない。
机上で正しい初期値を計算するのは、実は、結構大変だ。そこで、割込みのサイクルに同期させてLEDを点滅するようなプログラムをテスト用に作り、オシロスコープでLEDの点滅周期を測定して初期値を調整するといったことが可能になる。しかし、オシロを所有していないとそういう離れ業が使えないため、地道にアセンブルコードを見ながら計算しなければならない。
例えば、シャッターテスタの割込み処理ルーチンを以下に示すが、赤字のところがチェック対象となる。
/*#################### 割込みハンドラ #######################
*/
void interrupt handler( void ){
if(IOCAF){ // デジタルポート状態変化割込み
if(!TMR0IE){ // 初めてのフォトトラ割込なら
TMR0=TMR0_INIT2; // 次の割込まで8us(TMR0_INITより11クロック短く)
TMR0IE=1; // TMR0割込開始
}
}
if(TMR0IE&&TMR0IF) { // 4,3TMR0割込み(This17+Aft7=Total24)
TMR0=TMR0_INIT; // 次の割込まで8us
// LED点滅
if(RA0) RA0=0;
else RA0=1;
time_count++; // 4:計測カウンタアップ
if(time_count==0xFFFF){ // 6,5:タイムアウトカウンタ満了なら
TMR0IE=0; // 1:TMR0割込禁止
stage=FINISH; // 4:計測タイムアウト発生
}
TMR0IF=0; // 1:割込みフラグクリア
} // 2:GOTO Return
GIE=1; // 1:割込み許可
} // 6:return処理(レジスタ復帰等)
これに対応するコンパイル後のアセンブラプログラム
3393 ;; *************** function _handler *****************
3394 ;; Defined at:
3395 ;; line 165 in file “main.c”
3396 ;; Parameters: Size Location Type
3397 ;; None
3398 ;; Auto vars: Size Location Type
3399 ;; None
3400 ;; Return value: Size Location Type
3401 ;; None void
3402 ;; Registers used:
3403 ;; wreg, fsr0l, fsr0h, fsr1l, fsr1h, status,2, status,0, pclath, cstack
3404 ;; Tracked objects:
3405 ;; On entry : 0/0
3406 ;; On exit : 0/0
3407 ;; Unchanged: 0/0
3408 ;; Data sizes: COMMON BANK0 BANK1
3409 ;; Params: 0 0 0
3410 ;; Locals: 0 0 0
3411 ;; Temps: 0 3 0
3412 ;; Totals: 0 3 0
3413 ;;Total ram usage: 3 bytes
3414 ;; Hardware stack levels used: 1
3415 ;; Hardware stack levels required when called: 5
3416 ;; This function calls:
3417 ;; _to_sleep_mode
3418 ;; This function is called by:
3419 ;; Interrupt level 1
3420 ;; This function uses a non-reentrant model
3421 ;;
3422
3423 0004 _handler:
3424
3425 ;incstack = 0
3426 ; Regs used in _handler: [wreg-fsr1h+status,2+status,0+pclath+cstack]
3427 0004 147E bsf 126,0 ;set compiler interrupt flag
3428 0005 3180 pagesel $
3429 0006 0020 movlb 0 ; select bank0
3430 0007 087F movf 127,w
3431 0008 00A5 movwf ??_handler+2
3432
3433 ;main.c: 171: if(IOCAF){
3434 0009 0027 movlb 7 ; select bank7
3435 000A 0813 movf 19,w ;volatile
3436 000B 1903 btfsc 3,2
3437 000C 28B4 goto i1l1825
3438
3439 ;main.c: 172: if(!TMR0IE){
3440 000D 1A8B btfsc 11,5 ;volatile
3441 000E 2813 goto i1l1747
3442
3443 ;main.c: 173: TMR0=206;
3444 000F 30CE movlw 206
3445 0010 0020 movlb 0 ; select bank0
3446 0011 0095 movwf 21 ;volatile
3447
3448 ;main.c: 174: TMR0IE=1;
3449 0012 168B bsf 11,5 ;volatile
3450 0013 i1l1747:
3451
3452 ;main.c: 175: }
3737 ;main.c: 236: if(TMR0IE&&TMR0IF) {
3738 00B4 1A8B btfsc 11,5 ;volatile
3739 00B5 1D0B btfss 11,2 ;volatile
3740 00B6 28C9 goto i1l1837
3741
3742 ;main.c: 237: TMR0=206;
3743 00B7 30CE movlw 206
3744 00B8 0020 movlb 0 ; select bank0
3745 00B9 0095 movwf 21 ;volatile
3746
3747 ;main.c: 238: time_count++;
3748 00BA 3001 movlw 1
3749 00BB 07D2 addwf _time_count,f
3750 00BC 3000 movlw 0
3751 00BD 3DD3 addwfc _time_count+1,f
3752
3753 ;main.c: 239: if(time_count==0xFFFF){
3754 00BE 30FF movlw 255
3755 00BF 0653 xorwf _time_count+1,w
3756 00C0 1D03 skipz
3757 00C1 28C4 goto u134_25
3758 00C2 30FF movlw 255
3759 00C3 0652 xorwf _time_count,w
3760 00C4 u134_25:
3761 00C4 1D03 skipz
3762 00C5 28C8 goto i1l1835
3763
3764 ;main.c: 240: TMR0IE=0;
3765 00C6 128B bcf 11,5 ;volatile
3766
3767 ;main.c: 241: stage=0;
3768 00C7 01DF clrf _stage
3769 00C8 i1l1835:
3770
3771 ;main.c: 242: }
3772 ;main.c: 243: TMR0IF=0;
3773 00C8 110B bcf 11,2 ;volatile
3774 00C9 i1l1837:
3775
3776 ;main.c: 244: }
3805 ;main.c: 253: GIE=1;
3806 00D7 178B bsf 11,7 ;volatile
3807 00D8 0825 movf ??_handler+2,w
3808 00D9 00FF movwf 127
3809 00DA 107E bcf 126,0 ;clear compiler interrupt flag
3810 00DB 0009 retfie
3811 00DC __end_of_handler:
今回は、ソフトの底辺の制御方法に関するさわりの解説となり、ハードルが高かったかもしれないが、厳密な精度を求めるような機器の開発の場合に必要となるテクニックとして紹介した。