; ; 初期化 ; list p=16f877a #include p16f877a.inc variable env env = _BODEN_OFF env &= _CP_OFF env &= _PWRTE_OFF env &= _WDT_OFF env &= _LVP_OFF env &= _HS_OSC __config env cblock 0x20 endc nolist ; ; リテラル→Fへ(見かけ上wを経由せずに直接Fレジスタへ移動) ; move L to F ; ; 例 25をfileレジスタに格納 ; movlf d'25',file movlf macro L,F movlw L movwf F endm ; ; タイマ1の初期化 ; sett1でT1CONレジスタの設定 ; tmrl は TMR1L (下位)の設定 ; tmrh は TMR1H (上位)の設定 ; tmrl+tmrh の計16bitでカウント数の設定 ; ; 例 プリスケーラ8,内部クロック,0~15999回(3E7F)カウントの場合 ; tmr1_ini b'00110001',07fh,03eh ; 上記設定でフリーランの場合 ; tmr1_ini 031h,00h,00h ; tmr1_ini macro sett1,tmrl,tmrh banksel T1CON movlw sett1 movwf T1CON movlw tmrl movwf TMR1L movlw tmrh movwf TMR1H endm ; ; ccpの初期化 ; setccpでCCP1CONレジスタの設定 ; ccpl は CCPR1L(下位)の設定 ; ccph は CCPR1H (上位)の設定 ; ccpl+ccphで比較値の設定(16bit) ; ; 例 コンペアモード、スペシャルイベントで比較値0~15999(3E7F)の場合 ; ccp_ini b'00001011',07fh,03eh ; ccp_ini macro setccp,ccpl,ccph banksel CCP1CON movlw setccp movwf CCP1CON movlw ccpl movwf CCPR1L movlw ccph movwf CCPR1H banksel PIE1 bsf PIE1,CCP1IE ;CCP1の割り込み許可 endm ; ; LCD の初期化 ; 4bitでの使用、あらかじめ #define 等で E,RW,RS のポートを指定しておく必要あり ; 遅延で15msec,4.1msec,100μsecが必要 ; 液晶設定は標準設定 ; lcd_dlay 部分で 5msec の遅延サブルーチンが呼ばれればok ; LCD関係のプログラムで使用する変数を一括で設定 ; lcd_ini macro lcd_dlay cblock lcd_data lcd_temp endc clrf lcd_data clrf lcd_temp call lcd_dlay ;15msec以上待機 call lcd_dlay ;ここでは余裕を持って call lcd_dlay ;20msec待機 call lcd_dlay movlw b'00110000' ;"0011"をDB4〜DB7へ movwf PORTB ;8bitモードに設定。計3回繰り返し bsf E bcf E call lcd_dlay ;4.1msec以上待機 movlw b'00110000' movwf PORTB bsf E bcf E call lcd_dlay ;100μsec以上待機、ここでは5msec movlw b'00110000' movwf PORTB bsf E bcf E call lcd_dlay ;念のため遅延 movlw b'00100000' ;4bitモードに設定 movwf PORTB ;LCDに出力。以下4bitモード bsf E bcf E call lcd_dlay ;念のため遅延 movlw b'00101000' ;functionの設定 ;2行、5x7dot文字 call lcd_cmd ;コマンド送信 movlw b'00001100' ;ディスプレー制御 ;ディスプレーON、カーソル無し call lcd_cmd movlw b'00000001' ;ディスプレークリア call lcd_cmd movlw b'00000110' ;エントリーモード設定 ;インクリメント、表示シフト無し call lcd_cmd endm ; ; LCDへのコマンド送信マクロ ; 4bit用 ; あらかじめ #define 等で E,RW,RS のポートを指定しておく必要あり ; lcd_cmd_prg macro lcd_cmd movwf lcd_data ;データをlcd_dataに call lcd_busyck ;ビジーチェック movf lcd_data,0 ;lcd_dataをwに andlw b'11110000' ;上位4bitをマスク movwf PORTB bcf RW ;RWをLow bcf RS ;コマンド送信のためRSはLow bsf E bcf E swapf lcd_data,0 ;上下ビットの入れ替えをしてwに andlw b'11110000' ;マスク movwf PORTB ;残りの下位bitを出力 bsf E bcf E return endm ; ; LCDのビジーチェック ; 4bit用 ; LCDはポートBに接続 ; あらかじめ #define 等で E,RW,RS のポートを指定しておく必要あり ; lcd_busyck_prg macro lcd_busyck banksel TRISB movlw 0xff ;PORTBを入力に movwf TRISB banksel PORTB bcf RS ;ビジー信号入力 bsf RW ;読み込み bsf E bcf E movf PORTB,0 ;データ取り出し andlw b'11110000' ;マスク movwf lcd_temp ;仮置き bsf E bcf E swapf PORTB,0 ;入れ替え andlw b'00001111' ;下位取り出し iorwf lcd_temp,1 ;上下bit合体 btfsc lcd_temp,7 ;7bit目(ビジーフラグ)チェック goto lcd_busyck bcf RW banksel TRISB clrf TRISB ;PORTBを出力に戻す banksel PORTB return endm ; ; LCDの表示クリア ; 念のため 1msec の遅延を指定 ; lcd_clr_prg macro lcd_dlay lcd_clr movlw b'00000001' call lcd_cmd call lcd_dlay ;念のため遅延 return endm ; ; LCDへ1文字データ送信マクロ ; あらかじめ #define 等で E,RW,RS のポートを指定しておく必要あり ; lcd_char_prg macro lcd_char movwf lcd_data ;データをlcd_dataに call lcd_busyck ;ビジーチェック movf lcd_data,0 ;lcd_dataをwに andlw b'11110000' ;上位4bitをマスク movwf PORTB bcf RW ;RWをLow bsf RS ;データ出力 bsf E bcf E swapf lcd_data,0 ;上下ビットの入れ替えをしてwに andlw b'11110000' ;マスク movwf PORTB ;残りの下位bitを出力 bsf E bcf E return endm ; ; lcd_dataを液晶表示用の数字(アスキーコード)に変換し送信 ; lcd_charcon_prg macro lcd_charcon andlw b'00001111' ;入れ替えられた上位あるいは下位bitを取り出し addlw 030h ;アスキーコードに変換 call lcd_char return endm ; ; 1文字を lcd_char へ送るマクロ ; lcd_char が必要 ; mov_char macro char movlw char call lcd_char endm ; ; USARTの初期設定マクロ ; 送信側 ; tx_modeでTXSTAレジスタの設定(例 非同期8ビット高速モードのとき、b'00100100' 16進 024h) ; tx_brgでSPBRGレジスタの設定(例 4MHz 9600bps BRGH=1のとき、d'25') ; ; 受信側 ; rc_modeでRCSTAレジスタの設定(例 一般ではb'10010000' 16進 090h) ; enable_txrc macro tx_mode,tx_brg,rc_mode banksel TXSTA movlw tx_mode movwf TXSTA movlw tx_brg movwf SPBRG banksel RCSTA movlw rc_mode movwf RCSTA endm ; ; PCへ1文字送信するマクロ ; 送信したいデータをwレジスタに格納してから呼ぶ ; ; 例 movlw 'A' ; call txchar ; tx_char_prg macro txchar btfss PIR1,TXIF goto $-1 movwf TXREG return endm ; ; w レジスタの内容(数字)をアスキーコードに変換し送信 ; txchar が必要 ; tx_charcon_prg macro tx_charcon andlw b'00001111' ;入れ替えられた上位あるいは下位bitを取り出し addlw 030h ;アスキーコードに変換 call txchar return endm ; ; 1文字を txchar に送るマクロ ; txchar が必要 ; tx_mov_char macro char movlw char call txchar endm ; ; 割り込み部分のマクロ化 ; タイマ1(ccp)の割り込みかどうかを確認後、その割り込みを rate 分カウントし、 ; カウント値になったら clockflg の 0 bit目 をセットし、funcで指定したサブルーチンに移動 ; 割り込みでなければ no_intrupt へ ; ; 例 割り込みを25回カウントする場合 ; intrupt_ini d'25' ; intrupt_ini macro rate #define post_rate rate ;割り込みカウント数 cblock savew savest savepc post_count endc clrf savew clrf savest clrf savepc movlw post_rate movwf post_count endm intrupt_prg macro func intrupt movwf savew ;wレジスタの内容を退避 swapf STATUS,0 ;swapfはSTATUS内のフラグに影響なし clrf STATUS ;バンク0に固定 movwf savest ;STATUSレジスタの内容を退避 movf PCLATH,0 movwf savepc ;ページ情報を退避 clrf PCLATH ;ページを0に固定。以下割り込み条件判定 btfss PIR1,CCP1IF ;タイマ1割り込みチェック(コンペアモード) goto no_intrupt ;割り込みじゃない bcf PIR1,CCP1IF ;割り込みフラグをクリア decfsz post_count,1 ;割り込みを25回カウント goto no_intrupt bsf clockflg,0 ;時間用フラグのセット movlw post_rate ;割り込みカウント再設定 movwf post_count call func no_intrupt ;レジスタ復旧 movf savepc,0 movwf PCLATH ;PCLATHレジスタの復旧 swapf savest,0 ;STATUSレジスタの復旧 movwf STATUS swapf savew,1 ;wレジスタの復旧 フラグ影響なし swapf savew,0 ;一度ひっくり返してからwへ retfie ;割り込み復帰 endm ; ; 時刻表示用マクロ ; sec は秒、mint は分、hour は時 ; timeup_ini macro cblock sec mint hour_12 hour_24 ap endc clrf sec clrf mint clrf hour_12 clrf hour_24 movlw 'A' movwf ap endm timeup_prg macro timeup incf sec,1 ;sec+1 movlw b'00001111' ;下位マスク andwf sec,0 ;下位取り出し→wへ sublw 009h ;secが9以上かどうかの確認 sec-9=負ならC=0 btfsc STATUS,C ;C=0(secが0)の時スキップ return ;secが1~9の時 movlw 006h addwf sec,1 ;sec(A)+6=16で上位に繰り上げ下位は0(0A→10) movlw 060h ;60秒かどうかのチェック(上位が6) subwf sec,0 btfss STATUS,C ;sec-60=正なら C=1 return ;59秒まで clrf sec ;0秒に minup incf mint,1 ;mint+1 movlw b'00001111' ;以下、上記secと同じ andwf mint,0 sublw 009h btfsc STATUS,C return movlw 006h addwf mint,1 movlw 060h subwf mint,0 btfss STATUS,C return clrf mint horup_12 incf hour_12,1 ;hour_12+1 movlw b'00001111' andwf hour_12,0 sublw 009h btfsc STATUS,C goto ap_check movlw 006h addwf hour_12,1 horup_24 incf hour_24,1 ;hour_24+1 movlw b'00001111' andwf hour_24,0 sublw 009h btfsc STATUS,C goto check_24 movlw 006h addwf hour_24,1 return ap_check ;AM,PMの確認 movlw 012h subwf hour_12,0 btfss STATUS,C goto horup_24 clrf hour_12 movlw b'00001000' ;3bitをマスク xorwf clockflg,1 ;3bit目を反転 btfsc clockflg,3 ;3bit目=1→PM、3bit目=0→AM goto pm am movlw 'A' movwf ap goto horup_24 pm movlw 'P' movwf ap goto horup_24 check_24 movlw 024h subwf hour_24,0 btfss STATUS,C return clrf hour_24 return endm ; ; 時刻修正用マクロ ; 上記とほぼ同じ。ボタンを押すたびにカウントアップされる ; settimeup_ini macro cblock setsec setmint sethour_12 sethour_24 setap endc clrf setsec clrf setmint clrf sethour_12 clrf sethour_24 movlw 'A' movwf setap endm settimeup_prg macro settimeup incf setsec,1 ;sec+1 movlw b'00001111' ;下位マスク andwf setsec,0 ;下位取り出し→wへ sublw 009h ;secが9以上かどうかの確認 sec-9=負ならC=0 btfsc STATUS,C ;C=0(secが0)の時スキップ return ;secが1~9の時 movlw 006h addwf setsec,1 ;sec(A)+6=16で上位に繰り上げ下位は0(0A→10) movlw 060h ;60秒かどうかのチェック(上位が6) subwf setsec,0 btfss STATUS,C ;sec-60=正なら C=1 return ;59秒まで clrf setsec ;0秒に return setminup incf setmint,1 ;mint+1 movlw b'00001111' ;以下、上記secと同じ andwf setmint,0 sublw 009h btfsc STATUS,C return movlw 006h addwf setmint,1 movlw 060h subwf setmint,0 btfss STATUS,C return clrf setmint return sethorup_12 incf sethour_12,1 ;hour_12+1 movlw b'00001111' andwf sethour_12,0 sublw 009h btfsc STATUS,C goto setap_check movlw 006h addwf sethour_12,1 sethorup_24 incf sethour_24,1 ;hour_24+1 movlw b'00001111' andwf sethour_24,0 sublw 009h btfsc STATUS,C goto setcheck_24 movlw 006h addwf sethour_24,1 return setap_check movlw 012h subwf sethour_12,0 btfss STATUS,C goto sethorup_24 clrf sethour_12 movlw b'00010000' ;4bitをマスク xorwf clockflg,1 ;4bit目を反転 btfsc clockflg,4 ;4bit目=1→PM、4bit目=0→AM goto setpm setam movlw 'A' movwf setap goto sethorup_24 setpm movlw 'P' movwf setap goto sethorup_24 setcheck_24 movlw 024h subwf sethour_24,0 btfss STATUS,C return clrf sethour_24 return endm ; ; 16×16ビット=32ビットの乗算 ; mula_h(上位)mula_l(下位)×mulb_h、mulb_l = mulc_h、mulc_mh、mulc_ml、mulc_l ; mula_h,mula_l に被乗数、mulb_h,mulb_l に乗数を代入して呼ぶ ; 被乗数、乗数とも保存 ; 引数は8つ。最初の4つはa,b、残り4つで結果を保存するレジスタを指定する mul_16_32_ini macro cblock mul_16_count endc movlw 010h ;10h=16 movwf mul_16_count ;16 を代入 endm mul_16_32_prg macro mula_h,mula_l,mulb_h,mulb_l,mulc_h,mulc_mh,mulc_ml,mulc_l movf mulb_l,0 ;乗数を答え下位(ワーク)にコピーする movwf mulc_l movf mulb_h,0 movwf mulc_ml clrf mulc_h ;答用変数、上位をクリアしておく clrf mulc_mh movlw 010h ;10h=16 movwf mul_16_count ;16 を代入 ;a bcf STATUS,C ;キャリフラグのクリア btfss mulc_l,0 ;最下位ビットが 0 なら b へ飛ぶ goto $+7 movf mula_l,0 ;mula_l(下位) を w にコピー addwf mulc_mh,1 ;答(下位)に加える btfsc STATUS,C ;キャリフラグが 0 なら次の命令をスキップ incf mulc_h,1 ;繰り上がりの処理 movf mula_h,0 ;mula_h を w にコピー addwf mulc_h,1 ;答に加える(キャリフラグが次のシフトに必要) ;b rrf mulc_h,1 ;答を1ビット右に送る(被乗数を倍する) rrf mulc_mh,1 rrf mulc_ml,1 rrf mulc_l,1 decfsz mul_16_count,1 ;ビット数分の繰り返し goto $-d'14' ;aに endm ; ; 値を2^n倍するマクロ ; shiftbitでシフト回数を選択する ; 扱える値は24bit ; H=上位 M=中位 L=下位の3バイトを指定 ; mul_ini macro cblock mul_count endc clrf mul_count endm mul_prg macro H,L,shiftbit movlw shiftbit movwf mul_count ;シフト回数セット bcf STATUS,C rlf L,1 rlf H,1 decfsz mul_count,1 ;カウント分左へシフト goto $-4 ;6行前へ endm ; ; 値を1/2^n倍するマクロ ; shiftbitでシフト回数を選択する ; 扱える値は24bit ; H=上位 M=中位 L=下位の3バイトを指定 ; div_ini macro cblock div_count endc clrf div_count endm div_prg macro H,M,L,shiftbit movlw shiftbit movwf div_count ;シフト回数セット bcf STATUS,C rrf H,1 rrf M,1 rrf L,1 decfsz div_count,1 ;カウント分右へシフト goto $-5 endm ; ; a_hとa_l(以下a),b_hとb_l(以下b)の ; 2バイト(16bit)の加算(a+b)を行う ; 結果はaに格納される ; 引数は4つ ; add_16 macro a_h,a_l,b_h,b_l movf b_l,0 addwf a_l,1 movf b_h,0 btfsc STATUS,C incf b_h,0 addwf a_h,1 endm ; ; a_H~a_Lの4バイトとb_H~b_Lの4バイト(32bit)との加算(a+b)を行う ; 結果はa_H~a_Lに格納 ; 引数は8つ ; add_32 macro a_H,a_MH,a_ML,a_L,b_H,b_MH,b_ML,b_L movf b_L,0 ;下位の計算 addwf a_L,1 movf b_ML,0 ;中下位の計算 btfsc STATUS,C incf b_ML,0 addwf a_ML,1 movf b_MH,0 ;中上位の計算 btfsc STATUS,C incf b_MH,0 addwf a_MH,1 movf b_H,0 ;上位の計算 btfsc STATUS,C incf b_H,0 addwf a_H,1 endm ; ; a_H~a_Lの4バイトとb_H~b_Lの4バイトとの減算(a-b)を行う ; 結果はa_H~a_Lに格納 ; 引数は8つ ; sub_32 macro a_H,a_MH,a_ML,a_L,b_H,b_MH,b_ML,b_L movf b_L,0 ;下位の計算 subwf a_L,1 movf b_ML,0 ;中下位の計算 btfss STATUS,C incf b_ML,0 subwf a_ML,1 movf b_MH,0 ;中上位の計算 btfss STATUS,C incf b_MH,0 subwf a_MH,1 movf b_H,0 ;上位の計算 btfss STATUS,C incf b_H,0 subwf a_H,1 endm ; ; 遅延マクロ ; クロック 12.8MHz限定 ; busy_dlay_ini macro cblock TIM1 TIM2 TIM3 TIM4 TIM5 TIM6 TIM7 endc clrf TIM1 clrf TIM2 clrf TIM3 clrf TIM4 clrf TIM5 clrf TIM6 clrf TIM7 endm busy_dlay1 macro DLY1 ;約20μsecタイマ movlw d'19' movwf TIM1 nop nop DLY1_loop decfsz TIM1,1 goto DLY1_loop return endm busy_dlay2 macro DLY2 ;約100μmsecタイマ movlw d'5' movwf TIM2 DLY2_loop call DLY1 decfsz TIM2,1 goto DLY2_loop return endm busy_dlay3 macro DLY3 ;約1msecタイマ movlw d'10' movwf TIM3 DLY3_loop call DLY2 decfsz TIM3,1 goto DLY3_loop return endm busy_dlay4 macro DLY4 ;約5msecタイマ movlw d'5' movwf TIM4 DLY4_loop call DLY3 decfsz TIM4,1 goto DLY4_loop return endm busy_dlay5 macro DLY5 ;約20msecタイマ movlw d'4' movwf TIM5 DLY5_loop call DLY4 decfsz TIM5,1 goto DLY5_loop return endm busy_dlay6 macro DLY6 ;約100msecタイマ movlw d'5' movwf TIM6 DLY6_loop call DLY5 decfsz TIM6,1 goto DLY6_loop return endm busy_dlay7 macro DLY7 ;約1secタイマ movlw d'10' movwf TIM7 DLY7_loop call DLY6 decfsz TIM7,1 goto DLY7_loop return endm