Photo-gate timer with a PICAXE 20M2
From ShawnReevesWiki
Program listing
This program can be compiled for a PICAXE 20M2, paired with a Newhaven 3V i2c LCD display. It will watch for high signals on five input pins, starting with C.5, and display times between C.5 tripping and the rest of the gates.
;2014 Shawn Reeves
;Hardware notes:
;only C.1 to C.5 may be used as interrupts on the 20M2 or 20X2 parts
;Only 20X2 and greater parts offer a dedicated hardware timer. To use 20M2, we use software counting
;Tested with a 20M2 to 20ms precision, but may drift with temperature, ICs, VCC.
;For more precise timing, leave the PICAXE environment for a crystal-timed µC with hardware timer.
;Attach a photo-gate to pins 5, 6 (and optionally 7, 8, then 9) of the PICAXE 20M2
;Tie unused photo-gate pins to ground or spurious signals will trip floating pins.
;Gates should provide TTL output
;LCD display contrast depends on voltage of power supplied.
; Here it is adjusted to be readable from 3.3V to to 2.9V. Lower voltage will make lighter characters.
#picaxe 20M2 ;declare type so compiler works correctly.
symbol count1 = w1 ;track the time at second gate (pin 6)
symbol count2 = w2 ;track the time at third gate
symbol count3 = w3 ;track the time at fourth gate
symbol count4 = w4 ;track the time at fifth gate
symbol counter = w5 ;track the time since first gate was crossed (pin 5)
symbol loopWord = w6
symbol ones = b14
symbol tens = b15
symbol hundreds = b16
symbol thousands = b17
symbol tenThousands = b18
symbol interruptPin = b1 ;Track which gate we expect to close next
symbol LCDAddress = 124 ;0x7C
symbol controlByte = 0
symbol functionSetDisplay = 0x38
symbol clearDisplay = 1
symbol homeDisplay = 2
symbol updatePeriod = 100 ;How often to refresh display, in centiseconds. The larger, the more precise the timing
init:
pause 1000 ;give a second for inputs to settle
;setfreq will affect all future timing instructions, including pause, pauseus, and hi2csetup
setfreq m32
;HI2CSETUP I2CMASTER, slaveaddress, i2cfast | i2cslow | i2cfast_8 | i2cslow_8 | i2cfast_16 | etc, i2cbyte | i2cword
HI2CSETUP I2CMASTER, LCDAddress, i2cfast_32, i2cbyte
initLCD:
hi2cout 0,($38);%00111000; FUNCTION SET 8 bit, 2 line, not double-height, 0, instruction set zero
pause 2
hi2cout 0,($39);%00111001; FUNCTION SET 8 bit, 2 line, not double-height, 0, instruction set one
pause 2
hi2cout 0,($14);%00010100; OSC set 1/5 bias, frequency last three bits
hi2cout 0,(%01111111);%01111000; Contrast set, four least significant contrast bits
hi2cout 0,(%01011101);%01011110; Power/ICON set, Icon on, booster on, two most significant contrast bits
hi2cout 0,($6D);%01101101; FOLLOWER CONTROL, follower on, last three bits follower amp ratio
hi2cout 0,($38);BACK TO INSTRUCTION SET ZERO
hi2cout 0,($0C);%00001100; DISPLAY ON, display on, cursor off, cursor position off
hi2cout 0,($01) ; Clear display
hi2cout 0,($06);%00000110; ENTRY MODE, increment cursor, don't shift display
pause 3
restarting:
hi2cout 0, ($80) ;set data address to beginning of line 1
hi2cout $40, ("waiting for 1st gate")
counter = 0
count1 = 0
count2 = 0
count3 = 0
count4 = 0
interruptPin = 32 ;%00100000 ;Start at C.5
;set interrupt on inputs along port C, starting with C.5, C.4, ... for each photogate
setint interruptPin,interruptPin ;Interrupt only on current expected pin
main:
;Breaking 10µs pause into 1µs pauses, since interrupt causes program to go to next line
;;no matter how far pause was in execution.
;the timing overhead for each iteration of the following loop is about 40µs
for loopWord = 2 to updatePeriod
pauseus 7700 ;in units of 10µs (divided by frequency scale, 8), so this will count every 10 ms, our least significant digit
inc counter
next loopWord
pauseus 6000 ;adjust down from 10ms to account for display routine below. since setfreq is 32, 8000 is 10ms.
inc counter
#ifdef calibrateTime
;the following conversion and display take about 2.5ms
BINTOASCII counter, tenThousands, thousands, hundreds, tens, ones ;display counter to calibrate timing
hi2cout 0, ($C8) ;set data address to middle of line 2
hi2cout $40, (tenThousands, thousands, hundreds, %00101110, tens, ones)
#else
pauseus 1500;estimate of amount of overhead the above time display would take.
#endif
goto main
interrupt:
if interruptPin = 32 then ;first gate was tripped, so start counting
counter = 0
count1 = 0
count2 = 0
count3 = 0
count4 = 0
interruptPin = 16
setint interruptPin,interruptPin ;Interrupt only on current expected pin
hi2cout 0, ($80) ;set data address to beginning of line 1
hi2cout $40, ("1st gate tripped")
else if interruptPin = 16 then ;next gate was tripped, get time
count1 = counter
interruptPin = 8
setint interruptPin,interruptPin ;Interrupt only on current expected pin
hi2cout 0, ($80) ;set data address to beginning of line 1
BINTOASCII count1, tenThousands, thousands, hundreds, tens, ones
hi2cout $40, (tenThousands, thousands, hundreds, %00101110, tens, ones,32,32,32,32,32,32,32,32,32,32);clear 1st line
else if interruptPin = 8 then
count2 = counter
interruptPin = 4
setint interruptPin,interruptPin ;Interrupt only on current expected pin
hi2cout 0, ($88) ;set data address to middle of line 1
BINTOASCII count2, tenThousands, thousands, hundreds, tens, ones
hi2cout $40, (tenThousands, thousands, hundreds, %00101110, tens, ones,32,32)
else if interruptPin = 4 then
count3 = counter
interruptPin = 2
setint interruptPin,interruptPin ;Interrupt only on current expected pin
hi2cout 0, ($C0) ;set data address to beginning of line 2
BINTOASCII count3, tenThousands, thousands, hundreds, tens, ones
hi2cout $40, (tenThousands, thousands, hundreds, %00101110, tens, ones)
else if interruptPin = 2 then
count4 = counter
interruptPin = 32 ;allow hitting the original to reset
hi2cout 0, ($C8) ;set data address to middle of line 2
BINTOASCII count4, tenThousands, thousands, hundreds, tens, ones
hi2cout $40, (tenThousands, thousands, hundreds, %00101110, tens, ones)
endif
return