Photo-gate timer with a PICAXE 20M2

From ShawnReevesWiki
Jump to navigationJump to search

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.

See Photo-gate timer for a full discussion.

;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