MPLAB IDE v9.82, MPASM v5.51, PICKit3の環境で、PIC16F1827のADCのテストをします。
PIC16F1827は、10bit分解能のADコンバータ(ADC)を12チャンネル持っています。今回はこのADCの使い方を勉強します。
回路図です。電圧はVRで調整して、17番ピンのAN0(RA0)から入力して、AD変換してその値をシリアル通信で出力してTeraTermで、その値を読み取ります。今回もICSPでプログラミングし、電源(5V)はUSBシリアル変換モジュールFT-234から供給します。PICのクロックは内蔵の4MHzとします。
AD変換の手順です。
1 アナログ入力ピンの指定とピンの入力設定
2 ADCのチャンネル指定とADCを有効にする。
3 ADCの準備時間(チャージ時間)を待ち、AD変換をスタート
4 AD変換が終わったら変換結果を取り出す。
まず、ANSELAレジスタとANSELBレジスタでアナログピンを指定します。
AN0を使用しますので、bit0を1にして、他のビットは0にします。
ANSELA=b'00000001'
AN5~AN11は使わないので、
ANSELB=b'00000000' つまり、CLRF ANSELB
つぎにADCのチャンネルをADCON0レジスタで指定します。
bit6~bit2でチャンネルを設定します。AN0の場合、CHS4-CHS0=00000
bit0のADON=1としてADCを有効にします。
更にADCON1でADCの各種設定をします。
AD変換結果を右詰めでADRESHとADRESLに格納しますので、bit7のADFM=1とします。また、ADCのクロックは、Fosc=4MHzの1/4で1MHz=1usとしますので、ADCS2-ADCS0=100とします。
ADCの負参照電圧はVss(GND)としますので、bit2のADNREF=0、正参照電圧は、Vddとしますので、ADPREF1-ADPREF0=00とします。
以上からADCON1=b'11000000'と設定します。
EUSARTの設定とバイナリからBCDへの変換は、これまでの記事と同じです。
プログラムです。1秒ごとにAN0の電圧をAD変換してシリアル通信で出力します。"ADC "+変換値でTeraTermに表示されます。チャージ時間は、MAINループ内で確保できると考え、特にとっていません。
-------------------------------------------------
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; PIC16F1827 ADC TEST
; 2014.11.28
; JH7UBC Keiji Hata
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LIST P=PIC16F1827
INCLUDE P16F1827.INC
__CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _CPD_OFF & _BOREN_OFF & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
__CONFIG _CONFIG2, _WRT_OFF & _PLLEN_OFF & _STVREN_OFF & _BORV_19 & _LVP_OFF
ERRORLEVEL -302
CBLOCK 020h
BYTE0
BYTE1
DIGIT12
DIGIT34
SHIFT
WORK1
TEMP
CNT1ms
CNT250ms
ENDC
SETUP
;クロック設定
BANKSEL OSCCON
MOVLW b'01101000' ;PLLなし、4MHz,Fosc
MOVWF OSCCON
;入出力設定
BANKSEL TRISA
MOVLW b'00000001' ;RA0とRA5は入力、他は出力
MOVWF TRISA
MOVLW b'00000010' ;RB1は入力、他は出力
MOVWF TRISB
BANKSEL ANSELA
MOVLW b'00000001' ;AN0を使用する。AN1-AN4使用しない
MOVWF ANSELA
CLRF ANSELB ;AN5-AN11使用しない
;ADC関係設定
BANKSEL ADCON0
MOVLW b'00000001' ;AN0を使う。ADC Enable
MOVWF ADCON0
BANKSEL ADCON1
MOVLW b'11000000' ;右詰め、クロックFosc/4=1us、Vref-=Vss、Vref+=Vdd
MOVWF ADCON1
;USART設定
;RX,TX PIN 設定
BANKSEL APFCON0
BCF APFCON0,7 ;RXDSEL=0 RX=RB1
BCF APFCON1,0 ;RXCKSEL=0 TX=RB2
BANKSEL TXSTA
MOVLW b'00100100'
MOVWF TXSTA ;非同期モード、高速通信モードBRGH=1
BANKSEL RCSTA
MOVLW 090h
MOVWF RCSTA ;受信可、非同期モード
BANKSEL BAUDCON
BCF BAUDCON,3 ;BRG16=0 BRG 8bit
MOVLW 019h ;019h=25
MOVWF SPBRGL ;Fosc=4MHz,Baud Rate=9600bps
MAIN
MOVLW "A"
CALL SENDCHR
MOVLW "D"
CALL SENDCHR
MOVLW "C"
CALL SENDCHR
MOVLW " "
CALL SENDCHR
BANKSEL ADCON0
BSF ADCON0,ADGO ;AD変換スタート
BTFSC ADCON0,ADGO ;変換終了まで待つ
GOTO $-1
BANKSEL ADRESL
MOVFW ADRESL ;変換結果を格納
BANKSEL BYTE0
MOVWF BYTE0
BANKSEL ADRESH
MOVFW ADRESH
BANKSEL BYTE1
MOVWF BYTE1
CALL CONV_BCD ;バイナリからBCDに変換
CALL NUMPRINT
CALL LOOP250ms
CALL LOOP250ms
CALL LOOP250ms
CALL LOOP250ms
GOTO MAIN
NUMPRINT
BANKSEL DIGIT34
MOVFW DIGIT34
MOVWF WORK1
MOVWF TEMP
SWAPF TEMP,W
ANDLW 0Fh ;DIGIT34の上位4bitを取り出す
IORLW 30h ;ASCIIコードに変換
CALL SENDCHR
BANKSEL WORK1
MOVFW WORK1
ANDLW 0Fh ;DIGIT34の下位4bitを取り出す
IORLW 30h ;ASCIIコードに変換
CALL SENDCHR
BANKSEL DIGIT12
MOVFW DIGIT12
MOVWF WORK1
MOVWF TEMP
SWAPF TEMP,W
ANDLW 0Fh ;DIGIT12の上位4bitを取り出す
IORLW 30h ;ASCIIコードに変換
CALL SENDCHR
BANKSEL WORK1
MOVFW WORK1
ANDLW 0Fh ;DIGIT12の下位4bitを取り出す。
IORLW 30h ;ASCIIコードに変換
CALL SENDCHR
MOVLW 0Dh ;改行コード
CALL SENDCHR
MOVLW 0Ah ;NLコード
CALL SENDCHR
RETURN
;1文字送信
SENDCHR
BANKSEL TXSTA
BTFSS TXSTA,TRMT ;送信可能かチェック(1:可 0:禁止)
GOTO $-1
MOVWF TXREG
RETURN
;1文字受信
GETCHR
BANKSEL PIR1
BTFSS PIR1,RCIF ;RCIF=1ならスキップ
GOTO $-1
BANKSEL RCREG
MOVFW RCREG
RETURN
;--------------------------------------------
; バイナリからBCDへの変換
;--------------------------------------------
CONV_BCD
BANKSEL STATUS
BCF STATUS,C ;キャリーをクリア
MOVLW 10h ;16ビットカウンタ
MOVWF SHIFT ;SHIFT=8
CLRF DIGIT12 ;BCDデータクリア
CLRF DIGIT34
LOOP
RLF BYTE0,F ;shift BYTE to DIGIT
RLF BYTE1,F
RLF DIGIT12,F
RLF DIGIT34,F
DECFSZ SHIFT,F ;end check
GOTO ADJST ;adjust to BCD
RETURN
ADJST
MOVF DIGIT12,W ;DIGIT12 adjust TO BCD
CALL ADJBCD
MOVWF DIGIT12
MOVF DIGIT34,W ;DIGIT34 adjust to BCD
CALL ADJBCD
MOVWF DIGIT34
GOTO LOOP
;**** Each digit adjust to BCD ****
ADJBCD
MOVWF WORK1 ;save
MOVLW 3 ;W+3
ADDWF WORK1,W
MOVWF TEMP
BTFSC TEMP,3 ;Test W+3>7
MOVWF WORK1 ;>7 then W+3 else W
MOVLW 030h ;W+30
ADDWF WORK1,W
MOVWF TEMP
BTFSC TEMP,7 ;Test W+30>7*
MOVWF WORK1 ;>70 then W+30 else W
MOVF WORK1,W
RETURN
;250msタイマー
LOOP250ms
MOVLW 0FAh ;0FAh=250
BANKSEL CNT250ms
MOVWF CNT250ms
LOOP2
CALL LOOP1ms
DECFSZ CNT250ms
GOTO LOOP2
RETURN
;1msループ
LOOP1ms
MOVLW 0F9h ;0F9h=249
MOVWF CNT1ms
LOOP1
NOP
DECFSZ CNT1ms,F
GOTO LOOP1
RETURN
END
-------------------------------------------------
ブレッドボードです。
TeraTermの画面です。
ボリュームを回すと電圧が変化し、電圧(0~5V)に対応したAD変換値0~1023が表示されました。
トラブルシューティング
最初にプログラムを書いた時は、1秒ごとに"ADC 0000"とだけ表示され、ボリュームを回しても数値が変化しませんでした。
IDEでシミュレーションするとバイナリBCD変換がうまくいっていないことが分かりました。このルーチンは既に何度も使用して動作は確認してあります。
はて、何が原因か?分かるまで少し時間がかかりました。
ADRESLとADRESHの値をBYTE0とBYTE1に格納するのですが、ここがうまくいっていませんでした。これまで、BYTE0やBYTE1などのGPRに数値を書き込むときはBANK切り替えを気にしていなかったのですが、この時もBANKSEL BYTE0などと指定しなければならなかったようです。
ということで、SFRの時はもちろんGPRの時もBANKSELでレジスタのバンクを指定することにしまいた。これで問題なく動作するようになりました。
※コメント投稿者のブログIDはブログ作成者のみに通知されます