list p=16f877a ; PIC16F877A 用のプログラムであることを宣言 #include p16f877a.inc ; PIC16F877A 用のヘッダ・ファイルを読み込む __config _HS_OSC & _CP_OFF & _PWRTE_ON & _WDT_OFF & _LVP_OFF PORTC_ equ 0x20 TMP equ 0x21 TMP_CHK equ 0x22 TMP_HEXL equ 0x23 TMP_HEXH equ 0x24 MSG_LOOP equ 0x25 MYSTATE equ 0x26 MYFLAGS equ 0x27 ANGLE0 equ 0x28 ANGLE1 equ 0x29 PCLATH_TEMP equ 0x7d W_TEMP equ 0x7e STATUS_TEMP equ 0x7f T20MS equ D'61' T1MS equ D'125' org 0x0 goto start org 0x4 ;; レジスタの退避 movwf W_TEMP ; wレジスタを一時レジスタへ swapf STATUS,W ; STATUSレジスタをwへ clrf STATUS ; バンク0にする movwf STATUS_TEMP ; コピーしたSTATUSレジスタを一時レジスタへ movfw PCLATH ; PCLATHを保存する movwf PCLATH_TEMP clrf PCLATH ; PCLATHで指定するページも0にする ;; ここから割り込み処理 ;; 割り込み要因のチェック ; タイマ0割り込みの処理 INT_TMR0 btfss INTCON, T0IF ; タイマ0割り込みかをチェック goto INT_TMR2 bcf INTCON, T0IF ; 割り込みフラグを消す movlw T20MS addwf TMR0, f ; 次回のタイマ割り込みのタイミングを決める STATE0: ; 状態0での処理 bsf PORTC_, 3 ; サーボ0へのパルスを1にする movfw PORTC_ movwf PORTC bcf PIR1, TMR2IF ; 割り込みフラグを消す clrf TMR2 bsf STATUS, RP0 movlw T1MS ; 1[ms]後にTMR2割り込みが起こるようにする movwf PR2 bsf PIE1, TMR2IE ; TMR2割り込みを許可 bcf STATUS, RP0 incf MYSTATE, f ; 状態を一つ進める ; タイマ2割り込み処理 INT_TMR2 btfss PIR1, TMR2IF ; タイマ2割り込みかチェック goto INT_NEXT bcf PIR1, TMR2IF ; 割り込みフラグを消す decf MYSTATE, w addwf PCL, f goto STATE1 ; 状態1でのタイマ2割り込み goto STATE2 ; 状態2でのタイマ2割り込み goto STATE3 ; 状態3でのタイマ2割り込み goto STATE4 ; 状態4でのタイマ2割り込み STATE1 movfw ANGLE0 ; 指定されたとおりにTMR2割り込みが起こるようにする bsf STATUS, RP0 movwf PR2 bcf STATUS, RP0 incf MYSTATE, f ; 状態を1つ進める goto INT_NEXT STATE2 bcf PORTC_, 3 ; サーボ0へのパルスを0にする bsf PORTC_, 4 ; サーボ1へのパルスを1にする movfw PORTC_ movwf PORTC movlw T1MS ; 1[ms]後にTMR2割り込みが起こるようにする bsf STATUS, RP0 movwf PR2 bcf STATUS, RP0 incf MYSTATE, f ; 状態を一つ進める goto INT_NEXT STATE3 movfw ANGLE1 ; 指定された通りに TMR2 割り込みが起こるようにする bsf STATUS, RP0 movwf PR2 bcf STATUS, RP0 incf MYSTATE, f ; 状態を一つ進める goto INT_NEXT STATE4 bcf PORTC_, 4 ; サーボ1へのパルスを0にする movfw PORTC_ movwf PORTC bsf STATUS, RP0 ; バンク1 bcf PIE1, TMR2IE ; TMR2割り込みを禁止 bcf STATUS, RP0 ; バンク0 clrf MYSTATE ; 状態を0にする INT_NEXT ;; ほかの割り込み要因があればチェックする ;; レジスタの復帰 movfw PCLATH_TEMP ; PCLATHを戻す movwf PCLATH ; swapf STATUS_TEMP,W ; STATUSレジスタをwへ movwf STATUS ; STAUTSレジスタを戻す ; バンクも元に戻る swapf W_TEMP,F ; swapを2回行うことでwレジスタを戻す swapf W_TEMP,W ; これはSTATUSレジスタに影響させないため retfie ; 割り込みからの復帰 start: ; 初期設定をする bcf STATUS, RP0 ; この2行でバンク0にする bcf STATUS, RP1 clrf INTCON ; 割り込み禁止 clrf PORTC ; LEDが点灯しない, サーボが動かないように0に clrf PORTD ; 出力は0にしておく clrf PORTE ; 出力は0にしておく movlw B'10010000' ; シリアルの設定 movwf RCSTA movlw B'00100101' ; bit 6-3 ポストスケーラ 1:5 '0100' ; bit 2 タイマ2オン 1 ; bit 1-0 プリスケーラ 1:4 '01' movwf T2CON bsf STATUS, RP0 ; バンク1にする movlw 0xff movwf TRISB ; ポートBはディジタル入力 movlw B'00000111' ; PORTB のウィーク・プルアップを有効に ; プリスケーラはタイマに割り当て ; プリスケーラは1/256にする movwf OPTION_REG movlw B'10000000' movwf TRISC ; RXピン(ビット7)以外を出力に clrf TRISD ; 全ピン出力に clrf PIE1 ; TMR2 割り込み禁止 v movlw B'00100110' ; シリアルの設定 movwf TXSTA movlw D'64' ; ボーレートの設定 movwf SPBRG bcf STATUS, RP0 ; バンク0にする movlw B'00100000' ; LED0は消灯LED1は点灯, サーボはオフ movwf PORTC_ movwf PORTC movlw D'127' ; 最初のパルスの長さ1000+127×8=2016[us]=2[ms] movwf ANGLE0 movwf ANGLE1 clrf MYSTATE movlw T20MS ; 20[ms] タイマの設定 movwf TMR0 ; 初期設定は終了 ; 割り込みを有効にする movlw B'01100000' ; PEIE, TMR0IEを1に、TMR0IFを0にする, GIEは0 movwf INTCON clrf PIR1 ; TMR2IE, TMR2IFは0にしておく bsf INTCON, GIE ; GIEを1にする ; 電源を入れたときにメッセージを出す call bootmsg ;; メインルーチン main call RCVCHR ; 1文字受信(エコーバックつき) movwf TMP ; 受信した文字をTMPに書いておく ; parse command to ADC sublw '?' ; '?' を受信したら btfsc STATUS, Z call bootmsg ; 起動時のメッセージを出す movfw TMP sublw 'x' ; 'x' を受信したら btfsc STATUS, Z call servo0 ; サーボ0コマンド movfw TMP sublw 'y' ; 'y' を受信したら btfsc STATUS, Z call servo1 ; サーボ1コマンド goto main ;; メイン・ルーチンはここまで ;; サーボ0についてコマンドを受け取る servo0 call RCV_HEX ; 2文字16進数として受信する btfss STATUS, Z movwf ANGLE0 ; zフラグが0ならエラーでないので,パルス幅を更新 return ;; サーボ1についてコマンドを受け取る servo1 call RCV_HEX ; 2文字16進数として受信する btfss STATUS, Z movwf ANGLE1 ; zフラグが0ならエラーでないので,パルス幅を更新 return ;; 2文字受信し16進数として解釈する RCV_HEX ;; 上位バイトを受信 call RCVCHR call CHK_02F movwf TMP_HEXH sublw 0xff ; 0xffかチェック btfsc STATUS, Z return ; 0xffならエラー swapf TMP_HEXH, f ; 下位4ビットを上位4ビットへ移動 ;; 下位バイトを受信 call RCVCHR call CHK_02F movwf TMP_HEXL sublw 0xff ; 0xffかチェック btfsc STATUS, Z return ; 0xffならエラー movfw TMP_HEXL iorwf TMP_HEXH, w ; 前に受信した上位4ビットと下位4ビットをあわせる bcf STATUS, Z ; ちゃんと受信した印にzフラグを0にする return ;; wレジスタが '0' から 'f' なら0xfをwレジスタに返し、 ;; それ以外なら0xffをwレジスタに返すチェック・ルーチン CHK_02F movwf TMP_CHK addlw D'255' - '9' addlw '9' - '0' +1 btfss STATUS, C goto CHK_02F_1 ; W<'0' か W>'9' ; W は、'0' -> 0, '1' -> 1,..., '9' -> 9となっている return CHK_02F_1 movfw TMP_CHK addlw D'255' - 'f' addlw 'f' - 'a' + 1 btfss STATUS, C retlw 0xff ; '0'から'f'でないのでエラー ; W は、'a' -> 0, 'b' -> 1,..., 'f' -> 5となっている addlw 0xa ; 'a' -> 0xa, 'b'->0xb,..., 'f' -> 0xf return ;; 1文字受信してエコーバックするサブルーチン ;; 受信した文字はwレジスタで返す RCVCHR call RCVCHR1 movfw RCREG call TXCHR return ;; 1文字受信サブルーチン ;; 受信した文字はRCREGレジスタを読む RCVCHR1 ; 1バイト受信するまで待つ btfss PIR1, RCIF goto $-1 ; エラーかチェックする btfss RCSTA, OERR goto CHK_FERR bcf RCSTA, CREN ; オーバラン・エラーを消す bsf RCSTA, CREN goto RCVCHR1 CHK_FERR btfss RCSTA, FERR return ; 正常に受信したので戻る movfw RCREG ; フレーミング・エラーを消して、次の受信を行う goto RCVCHR1 ;; 1文字送信サブルーチン TXCHR btfss PIR1, TXIF ; Check if the buffer is empty goto $-1 movwf TXREG return ;; メッセージを送るサブルーチン bootmsg clrf MSG_LOOP bootmsg_0 movfw MSG_LOOP call MSG0 addlw 0 btfsc STATUS, Z return call TXCHR incf MSG_LOOP, f goto bootmsg_0 org 0x300 ;; メッセージ中の1文字を返すサブルーチン MSG0 clrf PCLATH bsf PCLATH, 0 bsf PCLATH, 1 addwf PCL, f retlw 'S' retlw 'e' retlw 'r' retlw 'v' retlw 'o' retlw '\r' retlw '\n' retlw 0 end リスト6-2 Copyright 2005 Noriaki Mitsunaga