現在私のお気に入りマイコン開発環境はST社のSTM32シリーズをEclipseによるリモートデバッグなんですが今回はPIC16F84Aです。
何を今さらPIC16F84Aなのかという程に古くて使いにくいマイコンなんですが、PIC16F84Aは1990年頃に諸事情で入手。
先日、無線の電信用にエレクトリックキーヤーをPIC16F84Aで作ったりしましたがまだ少し残ってるので可能なら何かに使ってしまいたい。
そういえば、このPICマイコンでタイマー割り込みをやった事がありません。
今回はタイマー割り込みを使ってどの程度正確に一定間隔でパルスを出すことが可能なのかの実機確認をやります。
このマイコンの諸設定は設定用のレジスタファイルというI/Oメモリーで行います。
タイマー割り込み設定はレジスタファイル0x0B番と0x81番で行います。
タイマーはレジスタファイル0x01番の数値を1命令サイクル毎に1つ加算していきます。
そしてレジスタファイル0x01番が0xFFから0x00になった瞬間、オーバーフロー割り込みが発生します。
ここで1命令サイクルは4システムクロック。
例えば4MHzのXTALで動作の場合は1命令サイクルは1μsecになります。
ちょっとわかりずらいのがレジスタファイル0x81です。
0x81番へのアクセスは0x03番の5ビット目と6ビット目を使ってバンク1番に切替えてから0x01番にアクセスしたらそこが0x81番です。
添付のプログラムソースの28行目から43行目がタイマー割り込みの設定個所になります。

試しに、レジスタファイル0x01番を完全に無視してタイマー割り込みをやってみます。
用意した回路図はこれです。

ブレッドボードを使って適当に回路を作りました。


タイマー動作はレジスタファイル0x01番が0x00から0xFFを延々繰り返し、1μsec x 256回 = 256μsecに1回 割り込みが発生します。
割り込み処理として、ポートAの0ビット目にHigh--->Lowのパルスを出力させた時の波形がこれです。(電圧1/10プローブで測定)


256μsecの逆数で3906.25Hzのバルスを計測してますので計算通りです。

では、25KHz(40μsec)で割り込ませる為にレジスタファイル0x01番に40命令サイクルに一回の設定をします。
レジスタファイル0x01番が0xFFから0x00になるまで40サイクルにするには256-40=216を設定すれば良い計算なんですが実は割り込みが発生してから割り込みルーチンに入るまでに8命令サイクル必要な様です。
なので8サイクル少ない32サイクルの設定を割り込みルーチンの最初に行います。
こうすることで正確に40サイクルで再割り込みが入ります。

こうやって25KHzを作った出力波形がこれ。(電圧1/10プローブで測定)


きっちり25KHzになってます。
波形を見る限り、パルスのズレもなく綺麗です。


また、マイコン動作中表示としてのLED点滅もさせており、チカチカ点灯しつつパルスが出ています。

もうしばらく動作確認を行い、結果良好ならHF無線機内蔵用マーカーにしてしまおうかなと考えてます。

(マーカーならロジック回路で作れば正確なのに何でマイコンというツッコミは当然あると思ってます)

 


動作ブログラムは整数型C言語です。
興味のある方はここにあるpic_cc-0.4.tar.gzをご参照ください。

今回作ったC言語のソースを下記に張り付けておきますので参考まで。

(C言語の中にアセンブラを混ぜてます)

 

---------------------------- ここから -----------------------------------
#include "io.c"
#include "stdio.h"

/* 割り込み処理ルーチン・・・・・ タイマ割り込み、ここに到達までに8カウント消費済 */
#asm
interrupt
    mov    01h,#224        ;count 40(256-40+8カウント=224) set for 25KHz by4MHz (=1000000/25000)

    bcf    0bh,7            ;Disable interrupt

    bsf    05h,0            ;pulse out to H
    bcf    05h,0            ;pulse out to L

    bcf    0bh,2            ;next interrupt ok.....TMR0

    bsf    0bh,7            ;Enable interrupt
    retfie                ;Return interrupt
#endasm




main(){
    int    i;

/* TMR0 オーバーフロー割り込み設定 */
#asm
    mov    0bh,#00100000b    ;TIM int set
    bcf    0bh,7            ;Disable interrupt

    
    bsf    03h,5            ;bank1
    
    bcf    01h,5            ;TMR0のソースはCLKOUT

    bsf    01h,3            ;プリスケーラはWDTに

    bsf    01h,0            ;プリスケーラ(001=1:4)

    bcf    03h,5            ;bank0


    mov    01h,#0            ;count 0 for start!!

#endasm


    SetP_A(0xf8);        /* Port A Set   0,1,2 :OUT   */
    SetP_B(0xfe);        /* Port B Set   0: OUT  1-7 :IN    */

#asm
    bsf    0bh,7            ;Enable interrupt
#endasm

/* 無限ループ */
    while(1){
#asm
        clrwdt            ;ウオッチドッグ
        bsf    06h,0        ;LED out H
#endasm
        for(i=0;i<255;i++){    
        }
#asm
        clrwdt            ;ウオッチドッグ
        bcf    06h,0        ;LED out L
#endasm
        for(i=0;i<255;i++){    
        }
    }
}