Frequency meter with a PICAXE 08M2

From ShawnReevesWiki
Jump to navigationJump to search

For an explanation, see Frequency meter. This program runs on an inexpensive PICAXE 08M2 micro-controller with mere software timing. For better accuracy and precision, switch to a micro-controller with hardware-timer-interrupt, and/or increase oscillator frequency to decrease the time instructions take.

Program listing

;2014 Shawn Reeves
;measures a frequency of switches between .1Hz and 50Hz
;To improve precision, and accuracy, decrease period,
; which will require more careful measurement of instructional overhead.

#picaxe 08m2;tell compiler which chip

;UNCOMMENT ONLY ONE OF THE FOLLOWING TO ALLOW DISPLAY METHOD, since Serial and SPI conflict on C.0
;#define useSerial
#define useSPILCD

;Define interruptOnHigh if pin goes high when switch on, else comment out
;#define interruptOnHigh

symbol switchPin = C.3
symbol clockPin = C.1;idle low, clock on rise, wait for falling to load next bit.
symbol dataPin = C.2;high = on
symbol loadDataPin = C.4;high = load shift register to segs. AKA CSB
symbol interruptPin = %00001000	;C.3
symbol interruptValue = %00000000	;interrupt on low
symbol slowbit = bit0	;track if frequency too low to measure.
symbol counter = b2
symbol byteOut = b3
symbol shiftCounter = b4
#ifdef useSPILCD
symbol RS = C.0;Note this conflicts with serial out, so one or the other!
	symbol tenthousands = b8
	symbol thousands = b9
	symbol hundreds = b10
	symbol tens = b11
	symbol ones = b12
#endif
symbol counts = w7
symbol mHz = w8
symbol period = w9
symbol loopWord = w10

init:
pause 100	;wait 0.1s for LCD to power up
#ifdef useSPILCD
;This routine should initialize the Electronic Assembly DOG-ME-081 8 character LCD display
;use symbol command to assign loadDataPin, RS, dataOut, and SCK to out pins
;assign a byte variable to byteOut and shiftCounter
initDOG:
high loadDataPin
low loadDataPin
low RS
;The following lines from Electronic Assembly's data sheet, with cursor mode altered.
;One might repeat the following command many times to get the attention of the ST7032 driver
byteOut = %00110000 ;Function Set, 1 line, instruction table 0
gosub shiftout_MSBFirst
pauseus 27;recommended by ST7032 datasheet

byteOut = %00110001 ;Function Set, 1 line, instruction table 1
gosub shiftout_MSBFirst
pauseus 27;recommended by ST7032 datasheet

byteOut = %00110001 ;Function Set, 1 line, instruction table 1, repeat request recommended by ST7032 datasheet
gosub shiftout_MSBFirst
pauseus 27;recommended by ST7032 datasheet

byteOut = %00011100 ;Bias 1/4, 1 line LCD
gosub shiftout_MSBFirst
pauseus 27;recommended by ST7032 datasheet

byteOut = %01010001 ;Booster off, set contrast C5, C4
gosub shiftout_MSBFirst
pauseus 27;recommended by ST7032 datasheet

byteOut = %01101010 ;set voltage follower on, gain 2/7
gosub shiftout_MSBFirst
pause 200;recommended by ST7032 datasheet

byteOut = %01110100 ;set contrast C3, C2, C1
gosub shiftout_MSBFirst
pauseus 27;recommended by ST7032 datasheet

byteOut = %00001100 ;display on, cursor off, blink cursor position
gosub shiftout_MSBFirst
pauseus 27;recommended by ST7032 datasheet

byteOut = %00000001 ;clear display, home cursor
gosub shiftout_MSBFirst
pauseus 2

byteOut = %00000110 ;entry from right to left (or opposite?), no scrolling.
gosub shiftout_MSBFirst
high RS
#endif ;useSPILCD

setint interruptValue, interruptPin

main:
;Breaking 10ms pause into 1ms 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 100
	pauseus 990	;in units of 10µs, so this will count every 10 ms, our least significant digit
	inc period
next loopWord
pauseus 800	;adjust down from 10ms to account for display routine(s) below.
inc period
if period > 1000 then ;more than ten seconds have passed.
	slowbit = 1
	else
		slowbit = 0
endif

sendData:
#ifdef useSerial
	sertxd (#mHz,"mHz",13,10)
#endif

#ifdef useSPILCD
	low RS	;about to issue a command
	byteOut = 1	;clear display and go to first position
	gosub shiftout_MSBFirst
	high RS	;about to issue data
if slowbit = 0 then
	bintoascii mHz, tenthousands, thousands, hundreds, tens, ones
	byteOut = tenthousands
	gosub shiftout_MSBFirst
	byteOut = thousands
	gosub shiftout_MSBFirst
	byteOut = "."
	gosub shiftout_MSBFirst
	byteOut = hundreds
	gosub shiftout_MSBFirst
	byteOut = tens
	gosub shiftout_MSBFirst
	byteOut = ones
	gosub shiftout_MSBFirst
	byteOut = "H"
	gosub shiftout_MSBFirst
	byteOut = "z"
	gosub shiftout_MSBFirst
else
	byteOut = "-"
	gosub shiftout_MSBFirst
	byteOut = "-"
	gosub shiftout_MSBFirst
endif
#endif ;useSPILCD

goto main

;This subroutine requires byte variables shiftcounter, byteOut; port.pins dataOut, and SCK
;RS pin should be set high or low before, according to whether a command or data is to be sent.
shiftout_MSBFirst:
for shiftCounter = 1 to 8
	if byteOut > 127 then ;MSb must be 1
		high dataPin
	else
		low dataPin
	endif
	;data must be set at least 10ns before clock rises. If overclocking, uncomment the following line:
	;pauseus 1
	pulsout clockPin,1	;rising clock edge initiates read of data
	byteOut = byteOut * 2	;Shift the value so we mask the next bit
	;data must remain set at least 150ns after clock rises.
	pauseus 1	;wait 10 microseconds.
next shiftCounter
pause 1
return

interrupt:
	mHz = 50000 / period
	mHz = mHz * 2	;fingers crossed period was greater than 1.
	period = 0
debounce:
	pauseus 1990	;debounce
	period = period + 2
	if pin3 = 0 then
		goto debounce
	endif
	setint interruptValue, interruptPin
return