Its designed to be used with a 2 sensor optical gate, though it can accurately measure the time of any single pulse.
It is mostly an experiment to carry as much precision as possible.
But also a useful gadget in it's own right.
It uses Darrel' Taylor's Instant Interrupts and N-Bit Math (64-Bits) throughout.
The core program is based on an implementation of CCP1, TIMER-1 Interrupts that was originally developed by Bruce from Rentron.
http://www.picbasic.co.uk/forum/show...01&postcount=3
Here is the Start of the Code:
'****************************************************************** '*********************[ High-Resolution Timer ]******************** '****************************************************************** '** Author : Robert Wozniak, Robert.Wozniak -at- gmail.com ** '** Notice : This code may be freely used and distributed ** '** for use in non-commercial applications ** '** Version : 6.10 : Feb 06, 2010 ** '** Date : JAN 14, 2010 ** '****************************************************************** '** Versions: ** '** 6.0 : All 64 bit MATH, 99 min limit ** '** 5.0 : Working Timer and Speed Calculator with 64 bit math ** '****************************************************************** '** Hardware Tested: PIC18F4680 @20MHz ** '** 4x16 Parallel LCD Module ** '** PBP 2.60 / MPLAB IDE 8.40.000 ** '** Compiled with: [-n -ampasmwin] Build Options ** '** 32 BIT LONG SIGNED INTEGER ** '** ** '** Thanks to Darrel & Bruce at MEL PB Forums for Code examples ** '** Alexander for N-Bit Math...Excellent ASM code ** '****************************************************************** '** MAX OVRFLO=457763, COUNTS=44041 for 99min:59.9999998sec ** '** MIN OVRFLO=0, COUNTS=261 for 9999.999MPH ** '****************************************************************** PRECISION CON 8 SYSTEM ; Set 8 bytes = 64-bit Precision for N-Bit_Math.pbp INCLUDE "N-BIT_Math.pbp" ; Include Alexander Avtanski's Multibyte Arithmetic ; Assembly Library Wrapped for PBP by Darrel Taylor [Version:1.3 Beta (JAN 07,2010)] ; Version:1.3 Beta (1/7/2010) INCLUDE "DT_INTS-18.bas" ; Include Darrel Taylor's Base Interrupt System for PIC18F [Version:3.3 (MAR 24, 2008)] INCLUDE "ReEnterPBP-18.bas" ; Include Darrel Taylor's PBP interrupts for PIC18F [Version:1.4 (MAR 24, 2008)] DEFINE OSC 20 DEFINE DEBUG_REG PORTC DEFINE DEBUG_BIT 6 DEFINE DEBUG_BAUD 57600 ' 38400 BAUD N-8-1, PORT C6 DEFINE DEBUG_MODE 1 ' 1 = inverted, 0 = true DEFINE LCD_DREG PORTA ' Set LCD Data port DEFINE LCD_DBIT 0 ' Set starting Data bit (0 or 4) if 4-bit bus DEFINE LCD_RSREG PORTA ' Set LCD Register Select port DEFINE LCD_RSBIT 4 ' Set LCD Register Select bit DEFINE LCD_EREG PORTB ' Set LCD Enable port DEFINE LCD_EBIT 3 ' Set LCD Enable bit DEFINE LCD_BITS 4 ' Set LCD bus size (4 or 8 bits) DEFINE LCD_LINES 4 ' Set number of lines on LCD DEFINE LCD_COMMANDUS 2000 ' Set command delay time in us (2000) DEFINE LCD_DATAUS 50 ' Set data delay time in us (50) TRISA=%00000000 ' SET to Output TRISC.2=1 ' SET to Input ADCON0=%11000000 ADCON1=%00000111 T1CON.7=1 ' Set Timer1 to 16 bit mode LED VAR PORTD.1 ' Alias PORTD.0 to LED I VAR BYTE J VAR BYTE OVRFLO VAR LONG ' Timer1 Overflow total COUNTS VAR WORD ' Timer1 Counts after falling edge capture PRESET VAR BYTE OVFLS32 VAR LONG COUNTS32 VAR LONG TSEC32 VAR LONG OVFLS64 VAR BYTE[PRECISION] OVFLS64M VAR BYTE[PRECISION] COUNTS64 VAR BYTE[PRECISION] TSEC64 VAR BYTE[PRECISION] BITS16 VAR LONG BITS1664 VAR BYTE[PRECISION] TMIN64 VAR BYTE[PRECISION] TSECINT64 VAR BYTE[PRECISION] TSECDEC64 VAR BYTE[PRECISION] CONVSEC VAR LONG CONVSEC64 VAR BYTE[PRECISION] AA32 VAR LONG AA64 VAR BYTE[PRECISION] BB32 VAR LONG BB64 VAR BYTE[PRECISION] CC32 VAR LONG CC64 VAR BYTE[PRECISION] TMIN VAR LONG TSEC VAR LONG TSECINT VAR LONG TSECDEC VAR LONG DISTINCH VAR LONG DISTFT VAR LONG DISTFT64 VAR BYTE[PRECISION] DISTFT64M VAR BYTE[PRECISION] FPS32 VAR LONG FPS64 VAR BYTE[PRECISION] FPSINT VAR LONG FPSINT64 VAR BYTE[PRECISION] FPSDEC VAR LONG FPSDEC64 VAR BYTE[PRECISION] MPH64 VAR BYTE[PRECISION] MPHINT64 VAR BYTE[PRECISION] MPHDEC64 VAR BYTE[PRECISION] MPH32 VAR LONG MPHINT VAR LONG MPHDEC VAR LONG ' ****** Setup Variables with Initial Parameters and 64-BIT Constants I = 0 J = 1 PRESET = 3 ' ********** PER OVERFLOW LOOP TICS CORRECTION ********** BITS16 = 65536 * 2 ' ********** COUNTS MAX * 200 NS PER TIC********** @ MOVE?LP _BITS16, _BITS1664 DISTINCH = 6000 ' ********** 6.000 inches (MAX = 65.535 in) ********** DISTFT = ((DISTINCH*10000)/12) @ MOVE?LP _DISTFT, _DISTFT64 ' ********** CONVERT TO FEET ********** AA32 = 1000000 ' ********** BUMP UP RESOLUTION @ MOVE?LP _AA32, _AA64 @ MATH_MUL _DISTFT64, _AA64, _DISTFT64M ' ********** BUMP UP DISTANCE DIGITS CONVSEC = 600000000 @ MOVE?LP _CONVSEC, _CONVSEC64 ' ********** BUMP UP TIME DIGITS Pause 500 LCDOUT $FE,1 LCDOUT $FE,$80,"HIGH-RESOLUTION " LCDOUT $FE,$C0," TIMER " LCDOUT $FE,$90," Version 6.10 " LCDOUT $FE,$D0," Robert Wozniak " DEBUG 10,13,10,13,10,13 DEBUG "HIGH-RESOLUTION ",10,13 DEBUG " TIMER ",10,13 DEBUG " Version 6.10 ",10,13 DEBUG " Robert Wozniak ",10,13,10,13,10,13 For I = 1 to 5 HIGH LED ' Turn ON LED connected to PORTD.1 Pause 50 LOW LED PAUSE 50 ' Turn OFF LED connected to PORTD.1 NEXT I I = 0 ;----[High Priority Interrupts (DT_INTS_18)]---------------------------------------- ASM INT_LIST macro ; IntSource, Label, Type, ResetFlag? INT_Handler CCP1_INT, _Capture, PBP, yes INT_Handler TMR1_INT, _Timer1, PBP, yes endm INT_CREATE ; Creates the High Priority interrupt processor ENDASM CCP1CON = %00000101 ' Capture mode, capture on rising edge T1CON = 0 ' TMR1 prescale=1, clock=Fosc/4, TMR1=off (200nS per count @20MHz) @ INT_ENABLE CCP1_INT ; enable Capture interrupts Main: IF T1CON.0 <> 0 And I = 0 THEN LCDOUT $FE,1 LCDOUT $FE,$C0," MEASURING" I = 1 GOTO LCDEND ENDIF IF T1CON.0 <> 0 THEN LCDEND IF T1CON.0 = 0 AND J = 0 THEN '********** CONVERT COUNTS, OVERFLOWS to SECONDS ********** @ MOVE?LP _OVRFLO, _OVFLS64 @ MATH_MUL _OVFLS64, _BITS1664, _OVFLS64M COUNTS32 = (COUNTS-10)*2 ' 10 TICS START CORRECTION, 200 nS PER COUNT @ MOVE?LP _COUNTS32, _COUNTS64 @ MATH_ADD _OVFLS64M, _COUNTS64, _TSEC64 ' ********** CONVERT SECONDS TO MINUTES ********** @ MATH_DIV _TSEC64, _CONVSEC64, _TMIN64 @ MOVE?PL _TMIN64, _TMIN ' ********** GET INTEGER SECONDS ********** CC32 = 10000000 @ MOVE?LP _CC32, _CC64 @ MATH_MUL _TMIN64, _CONVSEC64, _AA64 @ MATH_SUB _TSEC64, _AA64, _BB64 @ MATH_DIV _BB64, _CC64, _TSECINT64 @ MOVE?PL _TSECINT64, _TSECINT ' ********** GET DECIMAL SECONDS ********** @ MATH_MUL _TSECINT64, _CC64, _AA64 @ MATH_SUB _BB64, _AA64, _TSECDEC64 @ MOVE?PL _TSECDEC64, _TSECDEC ' ********** CALCULATE SPEED (FPS) ********** @ MATH_DIV _DISTFT64M, _TSEC64, _FPS64 ' ********** GET INTEGER FPS ********** CC32 = 1000000 @ MOVE?LP _CC32, _CC64 @ MATH_DIV _FPS64, _CC64, _FPSINT64 @ MOVE?PL _FPSINT64, _FPSINT ' ********** GET DECIMAL FPS ********** @ MATH_MUL _FPSINT64, _CC64, _BB64 @ MATH_SUB _FPS64, _BB64, _FPSDEC64 @ MOVE?PL _FPSDEC64, _FPSDEC ' ********** CONVERT FPS to MPH ********** AA32 = 15 @ MOVE?LP _AA32, _AA64 BB32 = 22 @ MOVE?LP _BB32, _BB64 @ MATH_MUL _AA64, _FPS64, _CC64 @ MATH_DIV _CC64, _BB64, _MPH64 ' ********** GET INTEGER MPH ********** AA32 = 1000000 @ MOVE?LP _AA32, _AA64 @ MATH_DIV _MPH64, _AA64, _MPHINT64 @ MOVE?PL _MPHINT64, _MPHINT ' ********** GET DECIMAL MPH ********** @ MATH_MUL _MPHINT64, _AA64, _BB64 @ MATH_SUB _MPH64, _BB64, _MPHDEC64 @ MOVE?PL _MPHDEC64, _MPHDEC ' ********** DISPLAY RESULTS ********** LCDOUT $FE,1 LCDOUT $FE,$80," ",DEC2 TMIN,":",DEC2 TSECINT,".",DEC7 TSECDEC LCDOUT $FE,$90, DEC4 FPSINT,".",DEC6 FPSDEC," FPS" LCDOUT $FE,$D0, DEC4 MPHINT,".",DEC6 MPHDEC," MPH" DEBUG "OVRFLO: ",DEC10 OVRFLO,13,10 DEBUG "COUNTS: ",DEC10 COUNTS,13,10,13,10 DEBUG "TIME: ",DEC2 TMIN,":", DEC2 TSECINT,".",DEC7 TSECDEC,13,10 DEBUG "SPEED: ",DEC4 FPSINT,".",DEC6 FPSDEC," FPS",13,10 DEBUG "SPEED: ",DEC4 MPHINT,".",DEC6 MPHDEC," MPH",13,10,13,10 J = 1 ENDIF GOTO LCDEND LCDEND: PAUSE 25 @ INT_ENABLE CCP1_INT ; Start new capture GOTO Main '---[CCP1 - interrupt handler]------------------------------------------ Capture: IF CCP1CON = %00000101 THEN ' If rising edge capture then HIGH LED ' Turn on LED to Indicate Measuring TMR1L = PRESET ' Clear Timer1 counts with Preset (High) TMR1H = 0 ' Clear Timer1 counts (LOW) T1CON.0 = 1 ' Turn Timer1 on at rising edge capture OVRFLO = 0 ' Zero Over flow counts COUNTS = 0 ' Zero remainder CCP1CON = %00000100 ' Switch to falling edge capture PIR1.0 = 0 ' Clear Timer1 overflow flag before enable I = 0 : J = 0 @ INT_ENABLE TMR1_INT ; Enable Timer 1 Interrupts GOTO OVER_CCP ' Done, exit ENDIF IF CCP1CON = %00000100 THEN ' If falling edge capture then T1CON.0 = 0 ' Stop Timer1 CCP1CON = %00000101 ' Switch back to rising edge capture @ INT_DISABLE TMR1_INT ; Disable Timer 1 Interrupts @ INT_DISABLE CCP1_INT ; Disable CCP1 Interrupts COUNTS.LowByte = TMR1L ' Get remaining Timer1 counts on falling edge (Low Byte) COUNTS.HighByte = TMR1H ' Get remaining Timer1 counts on falling edge (High Byte) LOW LED ' Turn off LED ENDIF OVER_CCP: @ INT_RETURN '---[TMR1 - interrupt handler]--------------------------------------------- Timer1: OVRFLO = OVRFLO + 1 @ INT_RETURN END
Here is the Schematic of the setup
The Full Resolution PDF file is here:
http://www.picbasic.co.uk/forum/atta...1&d=1265519692
Here is an photo of the actual Circuit:
This timer was developed to use an input pulse from the 2-Beam Pulse Generator Circuit which I presented here:http://www.picbasic.co.uk/forum/showthread.php?t=12614
While this code looks like an abomination, it's actually pretty cool that all the math is done with 64 bit precision.
I'm pretty new at all this, so I'd really like to hear your comments or criticism.
This is a work in progress. I have several features I'm still planning to include.
I'll follow up with a post with some comments and limitations.
Many thanks to Darrel, Bruce and Alexander for their excellent Code examples.
Notes, Limitations, and future releases:
Features:
1) MAXIMUM TIME: 99 MIN, 99.9999999 SEC ...Really just a display limitation
2) MINIMUM TIME: 0.0000522 SEC also a display limit at 9999.999998 MPH
3) Time Resolution: 200 nS = 0.0000002 SEC
4) Input single PULSE to Pin RC2
5) Displays Measurement and resets awaiting next pulse.
6) Data is sent out to 57600 N-8-1 Serial terminal on Pin RC6
7) Status LED shows when pulse is being timed.
Comments:
This was an experiment in precision of the code.
No oscillator is really this accurate.
Probably pretty close for relative measurements.
But Not for absolute measurements
Currently the speed is based on fixed distance of the optical sensors.
they are fixed and hard coded at 6.000 inches.
The circuit I use to derive the pulse is here:
http://www.picbasic.co.uk/forum/showthread.php?t=12614
Future Plans:
1) Add additional speed units (M/S, KMH)
2) Add user menu to set gate distance and displayed units
3) Add an oscillator correction factor after comparing it to bench-top timer.
4) Write settings to Onboard EEPROM
5) Try a 40 MHz crystal oscillator or other High accuracy clock
6) Build a thermal oven for crystal oscillator ...
Enjoy,
--Bob
Re: K42 and Timer Interrupts
Thanks for the explanation.
Ioannis - 28th April 2025, 19:28I misinterpreted these paragraphs. My understanding was to have ASYNC cleared and use Fosc/4.
Ioannis