;********************************************************************** ; PIC アセンブ ラテンプレート File name=PIC_Blog_Drill_001 ;********************************************************************** ; 1,TAB設定は = 10 にする ; 2,caseセンシティブを解除する ; 3,デバイスを設定する(16F84A、16F873、16F876などに設定する) ; ; ;================================================================== ; タイトル:ブログ掲載のPICアセンブラ練習 ; 作成開始 2014年4月25日 ; プログラムの仕様 ; * 電源ONで、パイロットランプとしてのLEDを点灯させる。 ; *初期設定として、二つのLEDを一秒毎に点滅させる。 ; *プッシュSWを一回押下すると、点滅のサイクルが二秒に変化する。 ; *再度プッシュSWを一回押下すると、点滅のサイクルが一秒にもどる。 ; *以降、この動作を繰り返す。 ; ;================================================================== ;(注)プログラムの簡単な仕様や、更新のバージョンなどは記入する ;--------------------------------------------------- ; PIC16F84A用の定義データをセット (使用するPICのデバイスを以下の定義する) ; デバイス番号以外は、”おまじない”としてそのまま記入する ;--------------------------------------------------- LIST P=PIC16F84A INCLUDE P16F84.INC ;FOR PIC 16F84A __CONFIG _HS_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF ;FOR PIC 16F873/876 ; __CONFIG _HS_OSC & _WDT_OFF & _PWRTE_ON & _CP_OFF & _LVP_OFF ;(注)LVP_OFFはPICに低電圧でプログラムを書き込む事を指す。 ;-------------------------------------------------- ; 変数定義ブロック ; ここにプログラム中で使用する各種変数を定義(1バイト)する ;-------------------------------------------------- CBLOCK H'0C' ;変数はPICのRAM領域があるアドレスH20から開始される ;使用するPICによりRAM容量が異なるので注意。 ;16F84Aは、0C−4F番地の68バイト ;16F873Aは、20−7F番地の96バイトです。 ;-- タイマーループ用変数定義 --ソフトタイマー用のカウント変数(ソフトタイマーはよく使用する) COUNT COUNT1 COUNT2 COUNT3 COUNT4 COUNT5 ;-- 割り込み処理用待避 -- 割り込み処理中にCPU内部レジスタの退避エリアを確保しておく(今回は未使用) ; W_SAVE ;Wレジスタ ; S_SAVE ;Sレジスタ ; PCLATH_SAVE ;プログラムカウンタ ;----------------------------------------------------------------------- ;-- その他変数 -- それぞれのプログラムで使用する変数を定義していく WORK ;作業領域として確保 ENDC ;変数定義領域の終了を示す ;(注)変数の定義は、よく使用するサブルーチンの変数と、個別で使用する変数を分けて ;   定義するとわかりやすい ;----------------------------------------------------------------------- ; DEFINE 定義セクション DEFINE命令を使用すると下記のように複雑な長い命令文を簡単なわかりやすい ; 命令に代用できる ;----------------------------------------------------------------------- ;例 ;#DEFINE SWITCH PORTB, 0 <-- "PORTB, 0" と記入しなくても"SWITCH"と記入することができる ; たとえば、PORTBの0にスイッチを接続している場合、わかりやすいプログラムを書ける ;----------------------------------------------------------------------- #DEFINE RED_LED PORTB, 0 ;PICのPORTB0-PORTB2に接続したLEDを定義する #DEFINE YELLOW_LED PORTB, 1 #DEFINE GREEN_LED PORTB, 2 #DEFINE SWITCH PORTA, 0 #DEFINE BANK_SW STATUS,RP0 ;BANK切り替え用 ;(注)DEFINE命令は、うまく使うとわかりやすいプログラムが書ける。ということは ;   ミスのないプログラムが書けるということ ;=================================================================== ;プログラム開始番地定義 ;=================================================================== ORG 0 ;プログラムの開始番地の指定 GOTO START ORG 4 GOTO INTRERRUPT ;割り込みルーチン開始番地指定 ;(注)PICの仕様として、電源ONのリセットで、0番地の命令を実行する。 ;   また割り込みが生じると4番地の命令を実施する。よってそれぞれに分岐命令を記入する。 ;=========================================================== ; Software Version ;=========================================================== DT "PIC_Blog_Drill_001" ;ここにソフトのバージョンを記載するとPICに書き込まれる ;=========================================================== ;(注)プログラムを一旦PICに書き込むと、それを外部から確認する方法が難しい。 ;   DT命令で、プログラム中にアスキーコードで文字を書き込んでおけば ;   ソフトの更新(バージョン)の管理も容易 ;================================================================ ; メインルーチン開始番地 ;================================================================ START ;---------------------------------------------------------------- ; ポート入出力設定(バンク1切り替え) ;---------------------------------------------------------------- ; PORT ASSIGN ; RA0 入力 = スイッチの端子を接続 SWが押下されるとLOWが入力される ; RA1 = ; RA2 = ; RA3 = ; RA4 = ; RA5 = ; ; RB0 出力 = 赤色LEDのカソードを接続 このポートをLOWにするとLEDが点灯する ; RB1 出力 = 黄色LEDのカソードを接続   同上 ; RB2 出力 = 緑色LEDのカソードを接続   同上 ; RB3 = ; RB4 = ; RB5 = ; RB6 = ; RB7 = ; ; RC0 = ; RC1 = ; RC2(CCP1) = ; RC3 = ; RC4 = ; RC5 = ; RC6(TX) = ; RC7(RX) = ;------------------------------------------------------------------- ;(注)PICはハードに近いアーキテクチャであるから、入出力ピンのアサインはキッチリ ;   管理しなければならない。どのピン(Port)に何を接続したのかを把握する事は ;   大切 BSF BANK_SW ;CHANGE TO BANK 1 MOVLW b'00000001' ;PORTA0を入力に設定 MOVWF TRISA ;SET TRISA REG MOVLW b'00000000' ;PORTB0-2を出力に設定 MOVWF TRISB ;SET TRISB REG BCF BANK_SW ;Change to Bank0 ;(注)PICの入出力ピンの設定は、バンクを切り替えて行うので、BSF命令でバンクを切り替える ;   すなわちSTATUSレジスタのビットRP0をゼロにする。(前のDEFINE命令を参照。BANK_SWを ;   定義している) ;   設定後は、再度バンク切り替え命令で、元のバンクに戻しておくのを忘れないように ;   MOVLW 命令は、そこで指定した数値をWレジスタに格納すること。 ;   MOVWF 命令は、Wレジスタの内容を指定されたファイルレジスタ(ここではAレジスタ)かメモリー ;   に格納すること ;----------------------------------------------------------------------------- ; INTERCON レジスター割り込み使用設定(バンク0)  (今回は割り込みは未使用) ; Set Intercon reg ;----------------------------------------------------------------------------- ; GIE(7) GLOBAL INT 0=NO, 1=OK ; PEIE(6) PERIPHERAL INT 0=NO, 1=OK ; T0IE(5) TMR0 INT 0=NO, 1=OK ; INTE(4) RB0 INT 0=NO, 1=OK ; BRIE(3) RB4-7 INT 0=NO, 1=OK ; T0IF(2) TMR0 FLG 0=NO, 1=YES ; INTF(1) RB0 FLG 0=NO, 1=YES ; RBIF(0) RB4-7 FLG 0=NO, 1=YES ;----------------------------------------------------------------------------- ; MOVLW b'00000000' ; MOVWF intcon ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ; OPTION レジスター設定 (バンク1へ切り替え)  (今回は割り込みは未使用) ; グローバル割り込み禁止し、個別割り込みを設定 ; TMR0 ; PRESCALER 設定初期値=X/XXX ;----------------------------------------------------------------------------- ; PBPU(7) PORTB PULLUP 0=YES, 1=NO ; INTEDG(6) RB0 INT EDGE 0=DW, 1=UP ; T0CS(5) TMR CLK 0=INT, 1=RA4 ; T0SE(4) TMR CNT EDGE 0=RA4UP, 1=RA4DW ; PSA(3) TMR PRESCALER 0=TMR0, 1=WDT ; PS2-0 PRESCALER 111=1/256(TMR) ; 110=1/128 ; 101=1/64 ; 100=1/32 ; 011=1/16 ; 010=1/8 ; 001=1/4 ; 000=1/2 ;----------------------------------------------------------------------------- ; BSF BANK_SW ;CHANGE TO BANK 1 ; MOVLW B'10000011' ;TMR0をフリーランで使用する ; MOVWF OPTION_REG ;Set OPTION_REG ; BCF BANK_SW ;Change to Bank0 ; CLRF TMR0 ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ; PIE1 PERIPHERAL INTERRUPT SETTING RESISTER (バンク1に切り替え) (今回は周辺機器は未使用) ; 周辺機器割り込み設定 ;----------------------------------------------------------------------------- ; PSPIE(7) PARALEL SLAVE NOT IN USE (0) ; ADIE(6) A/D INT USE 0=NO, 1=YES ; RCIE(5) USART RX INT USE 0=NO, 1=YES ; TXIE(4) USART TX INT USE 0=NO, 1=YES ; SSPIE(3) SSP INT USE 0=NO, 1=YES ; CCPIE(2) CCP INT USE 0=NO, 1=YES ; TMR2IE(1) TMR2/PR2 INT USE 0=NO, 1=YES ; TRM1IE(0) TMR1 OVER INT USE 0=NO, 1=YES ;----------------------------------------------------------------------------- ; BSF BANK_SW ;CHANGE TO BANK 1 ; MOVLW B'00000000' ; MOVWF PIE1 ; BCF BANK_SW ;Change to Bank0 ;----------------------------------------------------------------------------- ; PIR1 PERIPHERAL INTERRUPT FLAG RESISTER (BANK 0) ; 周辺機器割り込みFLG ;----------------------------------------------------------------------------- ; PSPIF(7) PARALEL NOT IN USE (0) ; ADIF(6) A/D INT 0=A/D NOT READY, 1=READY ; RCIF(5) USART RECEIVE INT 0=NO DATA, 1=DATA(READY) ; TXIF(4) UASRT TRANSMIT INT 0=DATA(BUSY), 1=NO DATA(READY) ; SSPIF(3) SSP INT FLG 0=NO INT, 1=SSP INT ; CCPIF(2) ; TMR2IF TMR2/PR2 SAME INT F 0=NO, 1=YES ; TMR1IF TMR1 OVER INT FLG 0=NO, 1=YES ;----------------------------------------------------------------------------- ; MOVLW B'00000000' ; MOVWF PIR1 ;---------------------------------------------------- ;==================================================== ;初期値設定 ;==================================================== ;--------------------------------------------------- ; 各ポートの初期値を設定 ;--------------------------------------------------- INITIALIZE MOVLW B'11111111' MOVWF PORTA ;とりあえず各ポートはHIGHにしておく MOVWF PORTB BCF RED_LED ;赤色LED点灯 ;(注)全ポートに"1"を出力して初期化する。その後、赤色LEDを接続したポートB0に"0"を ;   出力して、点灯させる。 CHK_SW BTFSC SWITCH ;SWITCHがLOWになるまでLOOPする GOTO CHK_SW ;(注)このプログラミングは、処理をある条件が満足されるまで待つ時に使う方法です ;   BTFSC命令は、SWITCHで定義された、PORTA0の入力ピンをチェックし、 ;   入力が"0"になったら次のGOTO命令を飛ばしてその先へ進みます。 ;   入力が"1"の間は、次のGOTO命令を実行し、CHK_SWへ分岐します。 POINT_A BSF GREEN_LED ;緑色LED消灯 BCF YELLOW_LED ;黄色LED点灯 CALL TIME1S ;1秒間のソフトタイマールーチン BSF YELLOW_LED ;黄色LED消灯 CALL TIME1S ;1秒間のソフトタイマールーチン ;(注)BSF命令とBCF命令は、指定されたレジスタやメモリーの指定ビットを ;   "0"にしたり(BCF命令)、"1"にしたり(BSF命令)します。 ;   この場合は、指定レジスタと指定ビットをDEFINE命令で先に定義して ;   あり、GREEN_LEDとしては、PORTB, 2なので、PORTBのビット2が対象と ;   なります。 SW_CHECK1 BTFSC SWITCH ;SWがこの時点で押下されていれば先へ GOTO POINT_A ;そうでなければ、POINT_Aに戻る POINT_B BSF YELLOW_LED ;黄色LED消灯 BCF GREEN_LED ;緑色LED点灯 CALL TIME1S ;1秒間のソフトタイマールーチン BSF GREEN_LED ;緑色LED消灯 CALL TIME1S ;1秒間のソフトタイマールーチン SW_CHECK2 BTFSC SWITCH ;SWがこの時点で押下されていればPOINT_Aへ GOTO POINT_B ;そうでなければ、POINT_Bに戻る GOTO POINT_A ;=================================================== ;以下の割り込み処理ルーチンは今回は未使用だがラベル定義はしておく ;=================================================== ; 割り込みルーチン開始番地 ;=================================================== ;(注)割り込み処理は、大変便利な機能ですが、PICの割り込みは ;   プログラミング側で割り込み後の処理(割り込み理由の解析と ;   分岐を定義する必要があります。 ;   また、それゆえに割り込み中の各種レジスターや変数の退避と ;   復元には相当気を使う必要があります。 ;   デバッグも大変です。思わぬ落とし穴があるのもこの割り込み ;   処理です INTRERRUPT ; MOVWF W_SAVE ;SAVE W REGISTER ; MOVFW STATUS ;READ STATUS REG ; MOVWF S_SAVE ;SAVE STATUS REG ; MOVFW PCLATH ; MOVWF PCLATH_SAVE ;--------------------------------------------------- ;ここから割り込み処理を記述する ;--------------------------------------------------- ; ; ; ;--------------------------------------------------- ;割り込み処理終了ルーチン ;--------------------------------------------------- ;INT_END ; MOVFW PCLATH_SAVE ; MOVWF PCLATH ; MOVFW S_SAVE ;READ SAVED STATUS REG ; MOVWF STATUS ;RECOVER STATUS REG ; SWAPF W_SAVE,F ;READ SAVED W REGISTER ; SWAPF W_SAVE,W ;RECOVER W REGISTER RETFIE ;------------ END OF INTERRUPT ROUITNE -------------------------------------------------- ;============================================================================= ;============================================================================= ; 各種サブルーチン開始番地 ;============================================================================= ;============================================================================= ;***************************************************************************** ;遅延サブルーチンブロック ;***************************************************************************** ;(注)以下のタイマーループのサブルーチンは、以前にどこかのWEBサイトからお借りした ;   ものですが、実際には時間がそれほど正確ではありません。 ;   ソフト的にちょっと待を入れたい時に使用していますが、正確に時間計測が必要な ;   場合には、タイマー割り込みなどを使用します。 ; ;--------------------------------------------------- ;100マイクロS遅延サブルーチン(20MHZクロック時) ;--------------------------------------------------- TIME100   MOVLW 03FH ; 1サイクル A5H=165 MOVWF COUNT ; 1サイクル NOP ; 1サイクル 微調整ダミー NOP ; 1サイクル 微調整ダミー LOOP DECFSZ COUNT,F ; 1×(165-1)+2 = 166サイクル GOTO LOOP ; 2×(165-1) = 328サイクル RETURN ; 2サイクル ;--------------------------------------------------- ;3MS遅延サブルーチン(20MHZクロック時) ;--------------------------------------------------- TIME3M MOVLW H'21' ; 1サイクル 63H=99で10MSEC, 21=3MSEC MOVWF COUNT1 ; 1サイクル NOP ; 1サイクル 微調整ダミー NOP ; 1サイクル 微調整ダミー LOOP6 CALL TIME100 ; (2+500)×99=49698サイクル DECFSZ COUNT1,F ; 1×(99-1)+2 = 100サイクル GOTO LOOP6 ; 2×(99-1) = 196サイクル RETURN ; 2サイクル ;--------------------------------------------------- ;10MS遅延サブルーチン(20MHZクロック時) ;--------------------------------------------------- TIME10M MOVLW H'63' ; 1サイクル 63H=99 MOVWF COUNT1 ; 1サイクル NOP ; 1サイクル 微調整ダミー NOP ; 1サイクル 微調整ダミー LOOP1 CALL TIME100 ; (2+500)×99=49698サイクル DECFSZ COUNT1,F ; 1×(99-1)+2 = 100サイクル GOTO LOOP1 ; 2×(99-1) = 196サイクル RETURN ; 2サイクル ;--------------------------------------------------- ;50MSEC遅延ルーチン ;--------------------------------------------------- TIME50M MOVLW H'05' MOVWF COUNT5 LOOP5 CALL TIME10M DECFSZ COUNT5,F GOTO LOOP5 RETURN ;--------------------------------------------------- ;1秒遅延サブルーチン(20MHZクロック時) ;--------------------------------------------------- TIME1S MOVLW 63H ; 1サイクル 63H=99 MOVWF COUNT2 ; 1サイクル MOVLW 62H ; 1サイクル 62H=98 MOVWF COUNT3 ; 1サイクル MOVLW 03H ; 1サイクル 03H=3 MOVWF COUNT4 ; 1サイクル NOP ; 1サイクル 微調整ダミー LOOP2 CALL TIME10M ;(2+50000)×99=4950198サイクル DECFSZ COUNT2,F ; 1×(99-1)+2 = 100サイクル GOTO LOOP2 ; 2×(99-1) = 196サイクル LOOP3 CALL TIME100 ;(2+500)×98=49196 DECFSZ COUNT3,F ; 1×(98-1)+2 = 99サイクル GOTO LOOP3 ; 2×(98-1) = 194サイクル LOOP4 DECFSZ COUNT4,F ; 1×(3-1)+2 = 4サイクル GOTO LOOP4 ; 2×(3-1) = 4サイクル RETURN ; 2サイクル ;---------------------------------------------------- PROG_END END ;プログラム終了