# Can MPLABX simulator count processor cycles?

#### BobW

##### Active Member
BINGO!!! Nice catch. I owe you a beer for that.
I guess I never caught that before, because it would probably exit the previous run with the correct value of zero. Anyway, I added an instruction to clear that register on entry, adjusted the cycle count, and now everything is correct.

#### Mike - K8LH

##### Well-Known Member
Good...

May I ask how you gate the frequency counter input, Bob, please?

Cheerful regards, Mike

#### BobW

##### Active Member
I'm using the method described in Microchip appnote AN592. A digital I/O pin is connected to the Tmr0 input pin. Setting the digital pin to input (Hi-Z) enables the timer, and and setting it to an output disables it. Toggling the I/O pin while in output mode flushes the prescaler into the Tmr0 register, in order to determine it's value.

Here is the working counter.

The top line shows, from left to right, the 1ms count, the number of averaged samples in the running average buffer (described below), and the input signal duty cycle. The bottom line shows the 1 second count. The way it's set up, it starts out in 1 ms sample mode and displays an averaged value on the bottom line starting with 16 samples, then 32, 64 and finally 128 samples. Once it gets 128 continuous samples at constant input frequency it automatically switches to 1 second sampling and displays the one second count on the bottom line. It also does 1 ms samples between each 1 s sample to update the top count and detect frequency changes. This seems to work well at responding quickly to frequency changes, but giving maximum resolution when the frequency is stable.
Here is the counter in 1 ms averaging mode:

In this mode, only two decimal places are displayed. Even with 128 samples at 1 ms, the last digit tends to vary by ±2 counts.

#### Mike - K8LH

##### Well-Known Member
Ah... Ok... You can do the same thing with just the RA2 (T0CKI) pin. That is, make it an 'input' for gate "on", make it an 'output' for gate "off", and clock it to flush and capture the prescaler value. Whether you're using one pin or two pins, the important thing to note is that you have a way to turn the gate "off" in a single cycle during that left over instruction cycle when we fall out of the gate timing loop.

That's really a very nice looking project, Bob.

Just for fun, and if you're interested... here's an example counter routine using a "cycle accurate" delay. There are two entry points. One for a 1-millisecond gate and one for a 1-second gate. The gate loop runs at 1-millisecond intervals using a delay value of 1-msec minus the 14 cycle loop overhead. With your 4-MHz clock, the gate is turned off precisely 1000 cycles (1-mS gate) or 1000000 cycles (1-sec gate) after being turned on.

Code:
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;                                                                 ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
go1000
movlw   high(1000)+1    ; 1-second gate                   |00
movwf   r3              ;                                 |00
movlw   low(1000)       ;                                 |00
movwf   r2              ;                                 |00
goto    prep            ; branch unconditionally          |00
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;                                                                 ~
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
go0001
movlw   high(1)+1       ; 1-msec gate                     |00
movwf   r3              ;                                 |00
movlw   low(1)          ;                                 |00
movwf   r2              ;                                 |00
prep
clrf    TMR0            ; clear TMR0 and prescaler        |00
clrf    acc0            ; clear the 32-bit counter        |00
clrf    acc1            ;  "                              |00
clrf    acc2            ;  "                              |00
clrf    acc3            ;  "                              |00
bcf     STATUS,IRP      ; set FSR/INDF for bank 0-1       |00
movwf   FSR             ; for T0CKI (RA2) data direction  |00
bsf     INDF,TRISA2     ; TRISA2 = 1, RA2 gate "on"       |00 (gate on)
gate
inDelay(1*msecs-14)     ; 1-msec minus 14 (loop) cycles   |00
movlw   1<<TMR0IF       ; check TMR0 overflow each loop   |00 ( 1)
andwf   INTCON,W        ;  "                              |00 ( 2)
skpz                    ; TMR0 overflow? no, skip, else   |00 ( 3)
bcf     INTCON,TMR0IF   ; clear TMR0IF flag               |00 ( 4)
skpz                    ; TMR0 overflow? no, skip, else   |00 ( 5)
movlw   1               ; bump the overflow counters      |00 ( 6)
skpnz                   ; zero result? no, skip, else     |00 ( 8)
decf    r2,F            ; decrement loop counter          |00 (10)
skpnz                   ;  "                              |00 (11)
decfsz  r3,F            ; done? yes, skip, else           |00 (12)
goto    gate            ; do another 1-msec loop          |00 (14)(13)
bcf     INDF,TRISA2     ; TRISA2 = 0, RA2 gate "off"      |00 (gate off)
;
;  perform a final check for TMR0 overflow
;
movlw   0               ;                                 |00
btfsc   INTCON,TMR0IF   ; TMR0 overflow? no, skip, else   |00
movlw   1               ; bump the overflow counters      |00
skpnz                   ; zero? no, skip, else            |00
bcf     INTCON,TMR0IF   ; clear TMR0IF unconditionally    |00
;
;  transfer TMR0 and prescaler values into 32-bit count & return
;
movf    TMR0,W          ;                                 |00
movwf   acc1            ; xfer TMR0 value into count      |00
flush   bcf     PORTA,RA2       ; clock the T0CKI pin             |00
bsf     PORTA,RA2       ;  "                              |00
decf    acc0,F          ; decrement counter LSB           |00
movf    TMR0,W          ; prescaler overflow into TMR0?   |00
xorwf   acc1,W          ;  "                              |00
bz      flush           ; no, loop (clock it again)       |00
return                  ;                                 |00
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For this example I used a simple "cycle accurate" in-line delay macro that doesn't use any variables. It supports a 0-1027 cycle range to match your requirements (delays no longer than 1000 cycles) and it generates 4, 5, or 6 instructions per macro call.

Have fun. Cheerful regards. Mike
Code:
;******************************************************************
;  K8LH inDelay(0..1027 cycle), in-line fixed delay, 0 bytes RAM  *
;******************************************************************
clock   equ     4               ; 4, 8, 12, 16, 20 MHz, etc.
usecs   equ     clock/4         ; cycles/usec operand multiplier
msecs   equ     clock/4*1000    ; cycles/msec operand multiplier

inDelay macro   delay           ; 0..1027 cycle range
if (delay > 1027)
error "inDelay range error"
endif
local   loop
if delay > 3
movlw   delay/4
loop    addlw   -1              ; 4 cycle loop (14 bit core)
skpz                    ; done? yes, skip, else
goto    loop            ; loop (do another 4 cycles)
endif
if delay&2                 ; delay%4 == 2 or delay%4 == 3
goto    $+1 ; delay 2 additional cycles endif if delay&1 ; delay%4 == 1 or delay%4 == 3 nop ; delay 1 additional cycle endif endm ;****************************************************************** Last edited: #### BobW ##### Active Member It's good to know that it can be done with just the RA2 pin. It had crossed my mind at one point that it might be possible to use a single I/O pin, but I'd never seen it done anywhere, and I had lots of pins available at the time. As the project progressed, and feature creep set in, having more I/O would have been useful. I'll need to redesign the PCB now anyway, because when I added an analog input, I had to do some surgery on the traces. I'll probably replace my old 1 ms delay routine with the one you posted earlier, or some variation of it. It's a lot more compact and versatile, and I'll be able to replace several other delay routines that are used by the LCD display. I was thinking about re-doing my 50-MHz counter with an OLED display on an 8-pin PIC FYI, my very first frequency counter project had a 3 1/2 digit Nixie display, and used just two chips: an 8-pin 12F629 and an Allegro A6818 32 bit serial-in parallel out HV driver. #### Mike - K8LH ##### Well-Known Member Hey, Bob (and gang). I just ran across an interesting dual 16-bit counter IC used in the following project; VHF Frequency Counter with PC Interface The SN74LV8154 Counter IC ($1.09 at Mouser) looks like it could be used to make a very nice 100-MHz frequency counter. The author's frequency measurement method is interesting, too.

Cheerful regards, Mike, K8LH

#### BobW

##### Active Member
Interesting project. I'm undecided about the idea of using a GPS module for the gate pulse though. The long term accuracy would be excellent of course, but if my calculations are correct, the jitter for a single gate pulse could cause an error of ±1 count (or more, if uncompensated) at 100 MHz, which admittedly is a small price to pay in order to avoid the drift of a crystal oscillator.

In fact, I had been thinking ahead to a future frequency counter project that could go above the 50 MHz limit of the PIC's Timer_0. I figured, why stop at 100 MHz which is only double the basic 50 MHz limit. So, I bought a 1.1 GHz prescaler IC, MC12080DG to do the heavy lifting (I haven't hooked it up yet). The idea would be to use exactly the same technique as the current project, to flush both the internal prescaler and the MC12080 by toggling a PIC output bit, so that the addition of the external prescaler wouldn't decrease the count resolution.

#### Mike - K8LH

##### Well-Known Member
Interesting project. I'm undecided about the idea of using a GPS module for the gate pulse though.
I'm not sure I would want to use a GPS module, either. However, the more I think about it, the more I like the idea of using this counter IC.

Here's a quick-n'-dirty concept drawing;

Last edited: