MastHead
  Entry     Photo Lib     Forum     Links     Glossary     Misc.  
周波数カウンタと電圧計

一つ前の記事で低歪率の発振器について言及しましたが、もし作成するとなると出力電圧と周波数を表示したくなります。
そのような小物はもう市場にあるだろう探してみましたが、それがなかなかありません。  電圧計はアナログでもいいのですが周波数カウンタはアナログというのはちょっと考えられません。  2 つ別々につけるとパネルの面積を取ってしまいますので、一つにまとまっている方が便利です。

無いものはないのですから、作ってしまうことにします。
測定対象が同じ箱の中にある発振器ですので、インターフェイスはそれに特化した限定したもので済みます。

発振器の概念と、つなぎの部分の回路とを想定したのが右の図です。
電圧は、出力端子の信号を両波整流した脈流です。 波高値は、そうですねえ、0V から 6V 程度としますか。  指示値を Vpeak とするか Vrms とするかで悩みましたが、交流電圧はたぶん Vrms が一般的だろうと考えてそうすることにします。  けれども、この脈流をキャパシタで平均化すると平均値(Vave)になってしまいます。 ここは考えどころ。

周波数カウンタへのつなぎは、一度バッファを通して負荷による歪を導入しない様にします。  電圧はまあ最低でも 4Vpp はあるでしょうからそうします。 バイアスはありません。  ただ、発振回路としてはあまり大振幅を取り扱うとキャパシタや抵抗が歪を発生させますから、場合によってはもっと小さくするかもしれません。  その時はバッファで少し増幅させればいいでしょう。

インターフェイスが単純ですのでそれに沿って回路を考えてみました。 右の図です。  16F1705 を使ったのは、少し前の記事で IC-R75 の周波数安定化の時に使ったものが残っていたからです。  この石は少し古くてもう Microchip は生産していないかもしれません。  代替品に 16F18323 というのが相当しますが、多分コードを少し変える必要があるでしょう (未検証: 少なくとも Pin3 の AN は 3 から 4 に変更されています)。

周波数と電圧を表示するので、2 行表示できる LCD を使います。  いろいろ探したら Linkman からいくつか製品が出ていました。  ところがほとんど全部 6 時方向の視野で、パネルを水平に置いて使うタイプなのです。  垂直なパネルに取り付けますので 12 時方向視野のものが要ります。  やっと一つ見つかって、それが図中に示したものです。  黒背景に赤字という配色はちょっといただけませんが、まあ我慢します。
注意: 後日分かったことですが、この LCD パネルは TN 液晶で、視野角が非常に狭く(上下左右とも)お勧めはできません。  選択するときは TN ではなく FSTN であることを確認して採用してください。 Linkman にはありません。

リセットスイッチとデバッグ用の LED ランプは不要です。  3.3V レギュレータは 1cm 角程度の銅板をつけておいてください。 温度が上がって電圧測定基準が変動するのを緩和するためです。
システムのクロックは 4MHz にしてあります。

平均値の測定をしながら実効値を表示するには、ちょっとトリックを使います。
右の図を見てください。  10bit の AD 変換の最大値が 1023 であることを利用します。 このちょっと下の値は 1000 ですので、これに小数点を付けて 10.00 と読み替えることにします。
ここでちょっと表記の説明:
以下では、10 進数の 100 を 100d
その量を 16 進数で表した 64 を 64h
2 進数で表した 01100100 を 01100100b
のように書くことにします。 他の値も同様です。
いま、サイン波を両波整流した信号があって、ピークの電圧値が 14.1V であるとします。 実効値は 10V で平均値は 9V です。
これを 10/27.935 に分圧すると平均値は 3.22V になります。 それをキャパシタで直流にしておきます。  非線形素子はかんでいませんので平均値は直流値になります。
AD 変換の参照電圧は、下が GND で、上は 3.3V と設定してあります。 3.297V 入力なら 10bit AD 出力は 1111111111b (1023d, 3FFh)です。  でも 3.22V ですから 1111101000b が出てきます。
これを 10 進数に変換すると 1000d となります。 この 1000 に小数点を無理やり取り付け、最後に Vrms という文字を追加します。  そうすると 10.00Vrms と表示されます。 振り返ってみますと、入力の電圧は 10Vrms でした。 (ちょっと騙された感じですが、うまくいきました)

なお、ADC は 1LSB 程のエラーがあります。 これは補正できるようにしておきました。 コードの最初のほうに値が入れられます。
すぐ気がつくと思いますが、この測定器は直流電圧は測れません。 直流を計測するのは 17.935k を 21.03k に変更しなければいけません。  これはアナログの電圧計でも同じことです。 昔の記事を参照してください。
17.935k は多回転の Pot を使ってください。  単回転の Pot では抵抗体の寸法が小さいので、摺動子の接触している寸法が問題になり、動かしたときに全体の抵抗値がわずかに上下します。  ですから精密な設定ができません。 多回転型では抵抗体が比較的大きいのでその影響が緩和できます。  まあバックラッシュは出てきますけど。
Vdd の 3.3V が正確でないときは、 入力に直流の 9.003V を与えて、表示が 10.00 になるように調整してください。  なお TI の LM2937-3.3 は温度が 20 度ぐらい上がると 0.01V 程度上がります。 これは我慢します。  どうしても我慢できないときは、 PIC が温度も測れますから、その値を使って補正する手もあります(めんどうなので止め)。 PIC 内部にある Vref(2.048V) を使う手もありましたが、分圧比が大きくなってしまうので Vdd を使いました。

周波数カウンタの方は簡単です。 Timer0 (T0)に 32.768kHz を入れ、Timer1 (T1)に被測定信号を入れます。  T0 はプリスケーラに 128 を設定してあるので 1 秒後にインタラプトが出ます。  それとは別に、T0 がオーバーフローしたら T1 のゲートが閉じるようにすることも検討しました。  ところがこのゲートは閉じたままにはならなくて、約 4 システムクロック(つまり 1us)後にはまた開きます(詳しくは下の参考資料 4)。  ですからその方法は使えません。
それで、インタラプトルーチンで T1 を止めて、カウンタを退避させることにします。 そのあとすぐ T0 と T1 を同時スタートさせるようにしてあります。  インタラプトの遅れ+止めるまでの遅れによる誤差、および 32.768kHz の発振器が持つ誤差はプログラムの中で補正しています。  補正値はコードの中の最初のほうに書き込む場所を設けておきました。

インターフェイスの回路はとても簡単にしておきました。 トランジスタで ON/OFF させているだけです。 +側に 1.5V も振れば ON になります。  こんな簡単な回路でも 5MHz ぐらいまでは難なく測れます。
実験では 2.3Vpp 入力で 1MHz まで動作しています。 4Vpp で 6MHz。  最近の小信号トランジスタは、FT が 1GHz はあるでしょうから何でも大丈夫です。


この周波数カウンタ/電圧測定回路と、LCD 表示器はデジタル動作なので周囲に電磁界をばらまきます。  シールドを考慮して、小さく、凸部が無く、シールド板を設置しやすい構造としておきます。
忘れがちなのがプログラム用のピンの設置です。 でき上がった時にPICKit-4 がちゃんと差し込めるような構造が必要です。
ちゃんと動き始めて、キャリブレーションも終わりました。

下にコードを掲載しておきます。 C ではなく Assembler で書いています。 Assembler はハードウェアの中をいちいち触りに行きますからとても面白く感じます。  手作りの楽しみがあります。
仕事でやる時は C のほうが作業の能率は上がるでしょうが、定年後で時間は十分にあります。

コード:
;===== FreqM.asm =====
;Frequency counter and volt meter using 16F1705
;to be used by low distortion signal generator
;alternative to 16F1705 is 16F18323
;display= 2 lines x 16 characters LCD panel
;spec: freq=1Hz-1MHz (-1.5V~+1.5V sine wave input), 
;      volt=0.10-10.00Vrms (full-wave rectified AC),  
;      update=1sec+α, Vcc=3.3V, clk=4MHz internal HF
;coding start 7/20/2020
;Copyright 2020 T. Fujiwara   all rights reserved
;version 1.0 release, 8/3/2020
;v.1.1 8/11: T1 overflow handling change

;****** Code must be in UTF-8 format ********    

;pin assignment:
;1 Vdd                   |*------|  ;14 Vss
;2 RA5 T1 clock-in       |       |  ;13 RA0 Program
;3 RA4 Analog Voltage-in |       |  ;12 RA1 Program
;4 RA3 Reset             |       |  ;11 RA2 T0 clock-in, 32.768kHz 
;5 RC5 RS                |       |  ;10 RC0 D4
;6 RC4 E                 |       |  ;9  RC1 D5
;7 RC3 D7                |_______|  ;8  RC2 D6

;MPLAB X set: case=insensitive, radix=dec, Device=16F1705, 
;             tool=PICkit 4, compiler=mpasm
 LIST P=16F1705, c=132, n=0
#include "p16f1705.inc"

; CONFIG1
;__config1 0xC7E4
 __CONFIG _CONFIG1, _FOSC_INTOSC & _WDTE_OFF & _PWRTE_OFF & _MCLRE_ON & _CP_OFF & _BOREN_ON & _CLKOUTEN_OFF & _IESO_OFF & _FCMEN_OFF
; CONFIG2
; __config2 0xFEFF
 __CONFIG _CONFIG2, _WRT_OFF & _PPS1WAY_ON & _ZCDDIS_ON & _PLLEN_OFF & _STVREN_ON & _BORV_LO & _LPBOR_OFF & _LVP_ON & _STVREN_ON

        radix       dec
;--- GPR addresses 20~6F ----
dispchr equ         0x20     ;32bytes of display characters               20~3F
binfreq equ         0x40     ;3 bytes of binary frequency is saved here   40~42
binfreq0 equ        0x43     ;T1 overflow reg                             43
binvolt equ         0x44     ;3 bytes of binary voltage is saved here     44~46
flag    equ         0x47     ;T0 overflow flag bit0, 1=O/F                47
loopcnt equ         0x48     ;various loop count                          48
loopcnt2 equ        0x49      ;ditto                                      49
work1   equ         0x4A     ;3 bytes of packed hex to be converted to dec4A~4c
work2   equ         0x4d     ;3 bytes of working value to subtract        4d~4f
work3   equ         0x50     ;7 bytes of accumulater, unpacked decimal    50~56
                             ;unused: 57,58,59,5a
work5   equ         0x5b     ;misc work                                   5b
work6   equ         0x5c     ;misc                                        5c
loopcnt3 equ        0x5d     ;mainloop cnt low                            5d
loopcnt4 equ        0x5e     ;mainloop cnt high                           5e
lcdwk1   equ        0x5f     ;lcd work area                               5f
lcdwk2   equ        0x60     ;  ditto                                     60
                             ;up to 6F 
common  equ         0x70     ;common 16 registers                         70~7F
        
;--- Literal Values ---
calibrate equ       .37      ;T0 deviation and "late T1 halt" is calibrated
                             ;00= no comp
                             ;01= +1 ppmb (displays higher freq than measured)
                             ;7F= +127 ppmb (+121ppm)
                             ;FF= -1 ppmb (makes lower freq display)
                             ;80= -128 ppmb (-122ppm)
                             ;ppmb is "binary ppm": 1/1MiB, 1ppmb= 0.9527559ppm
                             ;note: 32.768kHz crystal has +-50ppm deviation
adcoffseth equ      0xff     ;ADC offset compensation, use it for LSB error
adcoffsetl equ      0xff     ;0xffff makes measured 1 to display 0.00Vrms

;--- bits ----
w       equ         0
f       equ         1
rs      equ         b'00100000' ;rs bit to LCD,  used as "iorlw rs"
e       equ         4        ;write strobe to LCD, used as "bsf portc,e"

;--- Code ---
;RESET START ===============
        org         0
        nop
        goto        entry
        db          0x11             ;version
        nop
        org         4                ;interrupt routine

;INT routine ==============
        bcf         INTCON,GIE       ;kill all interrupts
        ;check T0 int
        btfss       INTCON,TMR0IF    ;1sec overflowed? skip if yes
        goto        t1up             ;otherwise, it must be the T1 overflow
        ;move T1 counter to binfreq
        bcf         T1CON,TMR1ON     ;stop T1 counting, 8us late, +8ppmb err
        movf        TMR1L,w          ;save T1 counters
        movwf       binfreq+2
        movf        TMR1H,w
        movwf       binfreq+1
        movf        binfreq0,w
        movwf       binfreq
        ;clear counters and restart
        bcf         INTCON,TMR0IF    ;reset T0 int flag
        clrf        TMR1L
        clrf        TMR1H
        clrf        binfreq0
        clrf        TMR0             ;reset TMR0 and prescaler, need two insn
        bsf         T1CON,TMR1ON     ;resume T1 counting 
                                     ;******** start T0/T1 freq. count ******
        bsf         flag,0           ;notice 1 sec int happened
        goto        iret
t1up:   ;increment timer 1 overflow reg
        incf        binfreq0,f       ;increment at every 64k cycles
        bcf         PIR1,TMR1IF      ;reset o/f bit in pir1
iret:   bsf         INTCON,GIE       ;enable global int
        retfie

;Entry  ==============
entry:  ;-----------------------initialize PIC and LCD
        call        init
        ;-----------------------display LCD, welcome msg
        call        lcd1
        call        lcd2
        ;-----------------------enable interrupts of T0/1
        movlb       1
        clrf        PIE3
        clrf        PIE2        ;disable misc int's
        clrf        PIE1
        bsf         PIE1,TMR1IE ;enable T1 int *****
        movlb       0
        clrf        INTCON
        bsf         INTCON,TMR0IE ;enable T0 int
        bsf         INTCON,PEIE ;enable periferal int's (for T1)
        ;-----------------------clear counters  ;T0=0, T1=0
        clrf        TMR1L       ;ensure tmr1h will have no change by tmr1l o/f
        clrf        TMR1H
        clrf        TMR0        ;clear T0/prescaler, takes place 2 cycles later
        clrf        TMR1L       ;start T0 and T1 simultaneously
        bsf         INTCON,GIE  ;enable global int

;Main Loop =======================================================
mainloop:
        ;get voltage and prepare displaychr, display line2
        call        voltage
        ;0.1sec wait loop while watching flag0 (1 sec timer comp)
        movlw       78          ;1281us*78=0.1sec
        movwf       loopcnt4
        clrf        loopcnt3
loop8:  ;wait 0.1sec loop, avoid too frequent voltage display
        btfss       flag,0      ;test if freq measured
        goto        sklp8       ;skip if no 1sec process
        call        frequency   ;treat frequency number, display line1
        goto        mainloop    ;then skip wait
sklp8:  decfsz      loopcnt3,f
        goto        loop8       ;one loop=5insn=5us, 256loops+1=1281us
        decfsz      loopcnt4,f
        goto        loop8
        goto        mainloop

;Subroutines =====================================================
frequency: ;get binfreq, convert to dec, display on LCD
        bcf         flag,0  ;reset timer0 o/f flag, we display frequency
        ;move to work1
        movf        binfreq,w
        movwf       work1
        movf        binfreq+1,w
        movwf       work1+1
        movf        binfreq+2,w
        movwf       work1+2
        
        ;calibrate: make work1 higher or lowerer by calibration value
        movlw       calibrate      ;ppmb value
        movwf       work5          ;test if no calibration
        sublw       0x00
        btfsc       STATUS,Z
        goto        gofreq         ;branch if not required
        call        clearwork2
        btfsc       work5,7        ;is it negative?
        goto        negative
        ;positive calibration
        call        mullp          ;multiply by ppmb times
        addwf       work1+2,f      ;add w to freq
        clrw
        addwfc      work1+1,f
        addwfc      work1,f
        goto        gofreq
negative: ;negative calibration
        comf        work5,f        ;complement: FF=>00, 80=>7F
        movlw       1              ;make it 2's complement
        addwf       work5,f        ;00=>1, 7F=80
        call        mullp
        subwf       work1+2,f      ;sub from freq
        clrw
        subwfb      work1+1,f
        subwfb      work1,f
        goto        gofreq
        
mullp:  movf        work1+1,w
        addwf       work2+2,f ;accumulate high two bytes into work2 three bytes
        movf        work1,w
        addwfc      work2+1,f
        clrw
        addwfc      work2,f
        decfsz      work5,f        ;loop ppmb times
        goto        mullp          ;exit if multiply comp
        swapf       work2+1,w      ;work2 has comp value x16
        andlw       0x0f
        movwf       work6   ;high nibble of work2+1 is now low nibble of work6
        swapf       work2,w
        andlw       0xf0         
        addwf       work6,f ;fill low nibble of work2 into high nibble
        movf        work6,w        ;w has comp value
        return
        
gofreq:
        call        h2d            ;work1=packed hex => work6=ascii decimal
        ;move from work3 to dispchr, erase left 0's, put comma
        movlw       work3+6
        movwf       FSR0L
        clrw
        movwf       FSR0H          ;fsr points work3+6, lowest byte
        moviw       FSR0--
        movwf       dispchr+10     ;1
        moviw       FSR0--
        movwf       dispchr+9      ;10
        moviw       FSR0--
        movwf       dispchr+8      ;100
        movlw       a','
        movwf       dispchr+7
        moviw       FSR0--
        movwf       dispchr+6      ;1k
        moviw       FSR0--
        movwf       dispchr+5      ;10k
        moviw       FSR0--
        movwf       dispchr+4      ;100k
        movlw       a','
        movwf       dispchr+3
        moviw       FSR0--
        movwf       dispchr+2      ;1M
        
        ;erase left 0's
        sublw       0x30
        btfss       STATUS,Z
        goto        skip3
        movlw       a' '
        movwf       dispchr+2
        movwf       dispchr+3   ;erase comma
        
        movf        dispchr+4,w
        sublw       0x30
        btfss       STATUS,Z
        goto        skip3
        movlw       a' '
        movwf       dispchr+4
        movf        dispchr+5,w
        sublw       0x30
        btfss       STATUS,Z
        goto        skip3
        movlw       a' '
        movwf       dispchr+5
        movf        dispchr+6,w
        sublw       0x30
        btfss       STATUS,Z
        goto        skip3
        movlw       a' '
        movwf       dispchr+6
        movwf       dispchr+7   ;comma location
        
        movf        dispchr+8,w
        sublw       0x30
        btfss       STATUS,Z
        goto        skip3
        movlw       a' '
        movwf       dispchr+8
        movf        dispchr+9,w
        sublw       0x30
        btfss       STATUS,Z
        goto        skip3
        movlw       a' '
        movwf       dispchr+9
skip3:  ;no more left zeros
        call        lcd1        ;display frequency
        return
        
voltage: ;get voltage and store in binvolt =========
        movlb       1
        bsf         ADCON0,1    ;AD Go!  bit1
        btfsc       ADCON0,1
        goto        $-1         ;wait for adc complete
        ;read V
        movf        ADRESL,w
        movlb       0
        movwf       binvolt+2
        movlb       1
        movf        ADRESH,w
        movlb       0
        movwf       binvolt+1
        ;move to work1
        call        clearworks
        movf        binvolt+2,w
        movwf       work1+2
        movf        binvolt+1,w
        movwf       work1+1
        
        ;error compensation
        movlw       0
        subwf       work1+2,w   ;test if low byte is zero
        btfss       STATUS,Z
        goto        adadjust    ;compensate if not zero
        movlw       0
        subwf       work1+1,w   ;test high byte
        btfsc       STATUS,Z
        goto        noadjust
adadjust:
        movlw       adcoffsetl  ;ADC offset compensation
        addwf       work1+2,f
        movlw       adcoffseth
        addwfc      work1+1,f
noadjust:
        ;convert hex to ascii decimal
        call        h2d  ;convert hex in work1 to ascii decimal in work3
        ;move ascii decimal in work3 to dispchr
        movlw       work3+6
        movwf       FSR0L
        clrw
        movwf       FSR0H       ;fsr points work3+6, lowest byte
        moviw       FSR0--
        movwf       dispchr+26
        moviw       FSR0--
        movwf       dispchr+25
        moviw       FSR0--
        movwf       dispchr+23
        moviw       FSR0--
        movwf       dispchr+22
        ;erase left 0
        sublw       0x30
        btfss       STATUS,Z
        goto        skip2
        movlw       a' '
        movwf       dispchr+22
skip2:  ;if binvolt=1023(0x3ff) then display ">", else erase ">"
        movlw       a' '
        movwf       dispchr+21
        movf        binvolt+2,w
        sublw       0xff
        btfss       STATUS,Z    ;lower 8bit is FFh
        goto        skip1       ;no, keep ">" erased
        ;test high byte
        movf        binvolt+1,w
        sublw       0x03
        btfss       STATUS,Z    ;flag z if full scale
        goto        skip1
        movlw       a'>'
        movwf       dispchr+21
skip1:  call        lcd2        ;display volt
        return
        
;clear work1,2,3  ==========
clearworks:
        clrf        work1
        clrf        work1+1
        clrf        work1+2
clearwork23:
        clrf        work3
        clrf        work3+1
        clrf        work3+2
        clrf        work3+3
        clrf        work3+4
        clrf        work3+5
        clrf        work3+6
clearwork2:
        clrf        work2
        clrf        work2+1
        clrf        work2+2
        return

;Wait Loop routine ============
wait:   movwf       loopcnt     ;approx. 769 * w +3 us, i.e. w:52=40ms
        clrf        loopcnt2
loop1:  decfsz      loopcnt2,f
        goto        loop1       ;one loop=3insn=3us, 256loops+1=769us
        decfsz      loopcnt,f
        goto        loop1
        return

;LCD Display routine ==============
lcd1:   ;display the first line
        movlw       dispchr
        movwf       FSR0L       ;set gpr address 0x20, in FSR0
        movlw       0x08        ;set address 0
        call        sendhalf
        clrw        
        call        sendhalf2
        goto        send16bytes
lcd2:   ;display the second line
        movlw       dispchr+16
        movwf       FSR0L       ;set gpr address 0x30, in FSR0
        movlw       0x0a        ;set address 0x28
        call        sendhalf
        movlw       0x08
        call        sendhalf2
send16bytes:
        clrw
        movwf       FSR0H
        movlw       16          ;16 characters to send
        movwf       lcdwk2      ;loop count=16
loop3:  moviw       FSR0++      ;get a byte from dispchr
        movwf       lcdwk1      ;save in work
        swapf       lcdwk1,w    ;get high nibble
        andlw       0x0f
        call        sendhalfd   ;send high nibble to lcd
        movf        lcdwk1,w    ;get low nibble
        andlw       0x0f
        call        sendhalf2d  ;send low nibble to lcd
        decfsz      lcdwk2,f    ;repeat it 16 times
        goto        loop3
        return

sendhalfd: ;send higher nibble for data
        iorlw       rs          ;flag this is data
sendhalf: ;send w reg to lcd, high nibble
        movwf       PORTC
        bsf         PORTC,e     ;up e
        bcf         PORTC,e     ;down e, write comp
        return
sendhalf2d: ;send lower nibble for data
        iorlw       rs
sendhalf2: ;send w reg to lcd, 2nd time, low nibble
        movwf       PORTC
        bsf         PORTC,e     ;up e
        bcf         PORTC,e     ;down e, write comp
wait56: movlw       18
        movwf       loopcnt2    ;wait for 53us
loop2:  decfsz      loopcnt2,f
        goto        loop2
        return
        
;Hex to Decimal routine ===========
h2d:    ;source is in work1, in packed hex format, 3 bytes
        ;work2 is 3 bytes working area of packed hex value of a decimal digit
        ;work3 is accumulator, filled by unpacked decimal digit
digit7: call        clearwork23  ;7th digit  1,000,000d=0F4240h
        movlw       0x0f
        movwf       work2
        movlw       0x42
        movwf       work2+1
        movlw       0x40
        movwf       work2+2
d7lp:   call        compare      ;w1>w2?  yes, then sub
        btfss       STATUS,C     ;yes, skip and sub
        goto        digit6       ;no or Z, go next digit
        call        subw1w2
        incf        work3,f      ;inc w3 1000000's digit
        goto        d7lp
        
digit6: call        clearwork2   ;6th digit    100,000d=0186A0h
        movlw       0x01
        movwf       work2
        movlw       0x86
        movwf       work2+1
        movlw       0xa0
        movwf       work2+2
d6lp:   call        compare
        btfss       STATUS,C
        goto        digit5
        call        subw1w2
        incf        work3+1,f
        goto        d6lp
        
digit5: call        clearwork2    ;5th digit     10,000d=2710h
        movlw       0x27
        movwf       work2+1
        movlw       0x10
        movwf       work2+2
d5lp:   call        compare
        btfss       STATUS,C
        goto        digit4
        call        subw1w2
        incf        work3+2,f
        goto        d5lp
        
digit4: call        clearwork2    ;4th digit      1,000d=03E8h
        movlw       0x03
        movwf       work2+1
        movlw       0xe8
        movwf       work2+2
d4lp:   call        compare
        btfss       STATUS,C
        goto        digit3
        call        subw1w2
        incf        work3+3,f
        goto        d4lp
        
digit3: call        clearwork2    ;3rd digit        100d=64h
        movlw       0x64
        movwf       work2+2
d3lp:   call        compare
        btfss       STATUS,C
        goto        digit2
        call        subw1w2
        incf        work3+4,f
        goto        d3lp
        
digit2: call        clearwork2    ;2nd digit         10d=0Ah
        movlw       0x0a
        movwf       work2+2
d2lp:   call        compare
        btfss       STATUS,C
        goto        digit1
        call        subw1w2
        incf        work3+5,f
        goto        d2lp
        
digit1: movf        work1+2,w     ;residue            1d=01h
        movwf       work3+6
        
        ;ascii
        movlw       0x30
        iorwf       work3+6,f
        iorwf       work3+5,f
        iorwf       work3+4,f
        iorwf       work3+3,f
        iorwf       work3+2,f
        iorwf       work3+1,f
        iorwf       work3,f
        return
        
compare: ;compare work1 and work2, C=0 if no sub, work2>work1
        movf        work2,w
        subwf       work1,w       ;test 1st byte
        btfss       STATUS,Z      ;skip if equal
        return
        movf        work2+1,w
        subwf       work1+1,w
        btfss       STATUS,Z
        return
        movf        work2+2,w
        subwf       work1+2,w
        return                   ;return having Z or C

subw1w2: ;do (work1 - work2 => work1) one time only
        movf        work2+2,w     ;lowest byte first
        subwf       work1+2,f
        movf        work2+1,w
        subwfb      work1+1,f
        movf        work2,w
        subwfb      work1,f
        return

;PIC and LCD Initialize routine==========================        
init:   ;initialize PIC
        ;-------------------Interrupt
        ;kill interrupt
        clrf        INTCON
        ;-------------------Clock
        ;INTOSC= internal high frequency, 4MHz, by HFINTOSC
        movlb       1           ;select bank 1
        movlw       b'01101000' ;4x pll disabled, 4MHz, internal HF clock
        movwf       OSCCON
        ;-------------------disbale OP Amp
        movlb       10
        bcf         OPA1CON,7
        bcf         OPA2CON,7
        ;-------------------port A/C set
        movlb       0
        clrf        PORTA       ;clear PORTA
        clrf        PORTC       ;clear PORTC
        clrf        PIR1        ;clear T1 o/f bit
        clrf        PIR2        ;clear flag bits etc
        clrf        PIR3        ;clear flag bits
        movlb       1
        movlw       b'00110111' ;port A bits are all input
        movwf       TRISA
        clrf        TRISC       ;all pins of port C are output
        movlb       2
        clrf        LATA        ;clear PORTA data LATCH
        clrf        LATC        ;ditto PORTC
        movlb       3
        movlw       b'00010000' ;RA4=analog for ADC
        movwf       ANSELA
        clrf        ANSELC
        ;--------------------T1CON
        movlb       0
        movlw       b'10000101' ;source=external, prescaler off, 
                                ;disable secondary osc, T1 enabled
        movwf       T1CON
        movlw       b'00000000' ;timer1 gating disabled, count when gate=low, 
                                 ;timer0 O/F gate killed
        movwf       T1GCON
        ;--------------------T0 option
        movlb       1
        movlw       b'00110110' ;weak pullup off, T0=pin, prescaler=on and 128, 
                                ;rising edge
        movwf       OPTION_REG
        ;--------------------WPU latch exclude RA5, RA4, RA2
        movlb       4
        movlw       b'00001011' ;wpu for RA3, RA1,RA0
        movwf       WPUA
        clrf        WPUC
        ;--------------------wdt off
        movlb       1
        clrf        WDTCON      ;off wdt
        ;------------------- ADC set
        movlb       2
        clrf        FVRCON      ;do not use FVR(2.048V)
        movlb       1
        movlw       b'00001101' ;ADC=AN3(RA4), 
                                ;ADC enabled (at least 9us before AD-GO)
        movwf       ADCON0
        movlw       b'11000000' ;right justified, Fosc/4, Vref-=Vss, Vref+=Vdd
                               ;ADC Clock must be 1us~4us
        movwf       ADCON1
        clrf        ADCON2      ;no auto conversion triger
        movlb       0           ;bank 0
        
        ;-------------------initialize data
           ;F=x,xxx,xxx Hz  
           ;V=    xx.xx Vrms
        movlw       a'F'
        movwf       dispchr
        movlw       a'='
        movwf       dispchr+1
        movlw       a'1'
        movwf       dispchr+2
        movlw       a','
        movwf       dispchr+3
        movlw       a'2'
        movwf       dispchr+4
        movlw       a'3'
        movwf       dispchr+5
        movlw       a'4'
        movwf       dispchr+6
        movlw       a','
        movwf       dispchr+7
        movlw       a'5'
        movwf       dispchr+8
        movlw       a'6'
        movwf       dispchr+9
        movlw       a'7'
        movwf       dispchr+10
        movlw       a' '
        movwf       dispchr+11
        movlw       a'H'
        movwf       dispchr+12
        movlw       a'z'
        movwf       dispchr+13
        movlw       a' '
        movwf       dispchr+14
        movlw       a' '
        movwf       dispchr+15
        movlw       a'V'
        movwf       dispchr+16
        movlw       a'='
        movwf       dispchr+17
        movlw       a' '
        movwf       dispchr+18
        movlw       a' '
        movwf       dispchr+19
        movlw       a' '
        movwf       dispchr+20
        movlw       a' '
        movwf       dispchr+21
        movlw       a'1'
        movwf       dispchr+22
        movlw       a'2'
        movwf       dispchr+23
        movlw       a'.'
        movwf       dispchr+24
        movlw       a'3'
        movwf       dispchr+25
        movlw       a'4'
        movwf       dispchr+26
        movlw       a' '
        movwf       dispchr+27
        movlw       a'V'
        movwf       dispchr+28
        movlw       a'r'
        movwf       dispchr+29
        movlw       a'm'
        movwf       dispchr+30
        movlw       a's'
        movwf       dispchr+31

        ;-----------------------initialize LCD
        movlw       52
        call        wait	;40ms wait
        movlw       b'00000011' ;-----------
        call        sendhalf
        movlw       6
        call        wait	;wait 4.1ms
        movlw       b'00000011' ;-----------
        call        sendhalf
        movlw       1
        call        wait        ;wait100us
        movlw       b'00000011' ;-----------
        call        sendhalf2
        movlw       b'00000010' ;-----------
        call        sendhalf2
        ;4bit enabled
        movlw       b'00000010' ;-----------  fuction set
        call        sendhalf
        movlw       b'00001100' ;4bit mode, 2 lines, 5x10 dots
        call        sendhalf2
        clrw                    ;-----------  display on/off
        call        sendhalf
        movlw       b'00001100' ;display on, cursor off, no blink
        call        sendhalf2
        clrw                    ;----------- clear display, takes 2.2ms
        call        sendhalf
        movlw       b'00000001' ;clear
        call        sendhalf2
        movlw       3
        call        wait        ;wait 2.2ms, clear comp
        clrw                    ;----------- entry mode
        call        sendhalf
        movlw       b'000110'   ;increment direction, no screen shift
        call        sendhalf2
        return        
testend: bsf        PORTC,e
        goto        $           ;debug purpose
        end



[参考資料]
1.Microchip site PIC 16F1705
2.16F1705 は少し古いので、代替え品の PIC 16F18323
3.3 バイトの引き算(PIC 仕様書の致命的な間違い)Forum: Three bytes subtraction by 16F
4.Timer0 のオーバーフローで Timer1 を停止できない Forum: TMR0IF control Timer1 counting?
5.LCD Controller chip SDLC780D1 datasheet (PDF)
6.Microchip Application Note AN592 プリスケーラを読みだす方法(今回は使わない)


[補足、もっと高い周波数をカウントしたい方へ]
T1 入力は非同期では1サイクルが 60ns が仕様書上の最小値です。 これは 16MHz になります。
ところが、T0 側はプリスケーラが高い周波数に対応していて 20ns まで短くできます(duty=50% のこと)。 これは 50MHz です。
ですから T0 と T1 を入れ替えると高い周波数まで計数できます。  もちろん T0 側はプリスケーラも入れて 16bit のカウンタですから、システムクロックを速くして、頻繁にオーバーフローの処理にいそしまないといけません。
分解能がプリスケーラで悪くなると考えられるでしょうが、上記の AN592 のやり方でプリスケーラの値を読みだせば 1Hz の分解能が得られます。  あ、AN592 の中にある 470Ω と何かのピン出力で AND Gate とする方法は使ってはいけません。  ゲート入力キャパシタンスで高域が減衰して高い周波数のカウントができなくなります。  インタラプトで止める方が楽です。




コメントはこちらへ==> Forum

サイトの最初のページに行くTopPage

frequency counter volt meter
(07/29/2020)武        

Ⓒ Copyright 2020  T. Fujiwara      All rights reserved.
Archiving or copying this article without the author's permission is prohibited.

周波数カウンタ 電圧計 frequency counter volt meter
作者: 藤原 武 Tak Fujiwara