;-----------------------------------------------------------
; 5-digit counter with CW readout
; started: 4/21/2005 EJB
; this version: 4/23/2005 EJB
;-----------------------------------------------------------
; (c) 2005 Eric Behr
; Free to use, modify, redistribute - with attribution - for
;   non-commercial purposes
; This code comes with no warranty whatsoever! In fact, I can
;   assure you that it has bugs.
;-----------------------------------------------------------

; states:
;   0  nothing happening, go to sleep
;   1  button just went on (pin down to Vss)
;   2  button still on, 4 sec timer running
;   3  button went off in state 2
;   4  reset pin down to Vss

; power from a 3V coin cell
; average current drain in tenths of a mA
; connections:
;   buzzer pin to 1N4148 diode to buzzer to Vss
;   button pin to 10k pullup resistor to Vdd
;   button pin to 10uF capacitor to Vss
;   button pin to switch to Vss
;   reset pin to 10k pullup resistor to Vdd

	list p=12f629
	include "p12f629.inc"
	__CONFIG _CPD_OFF & _CP_OFF & _BODEN_OFF & _MCLRE_OFF & _PWRTE_ON & _WDT_ON & _INTRC_OSC_NOCLKOUT

#define	bank0	bcf STATUS, RP0
#define	bank1	bsf STATUS, RP0
#define	reset_p	GPIO,GPIO0
#define	button	GPIO,GPIO4
#define	buzzer	GPIO,GPIO2
#define	ndig	5

w_t		equ	0x20		; for saving w
s_t		equ	0x21		; for saving status
ee_addr		equ	0x22		; eeprom address
ee_data		equ	0x23		; data to be written to eeprom
state		equ	0x24		; current state
timer_c		equ	0x25		; timer counter
pulse_c		equ	0x26		; pulse counter for beeps
space_c		equ	0x27		; counter for digit space
dly_c		equ	0x28		; short delay counter
dig_ptr		equ	0x29		; pointer to a digit
dig_ctr		equ	0x2a		; digit counter
dig1		equ	0x2b		; digit 1, must be consecutive
		res	ndig		; don't use the next ndig - 1
					; locations for anything else
eedig1		equ	0		; first eeprom location, keep the
					; next ndig - 1 free

; --------------------------------------------
	org	0x00
	goto	main

; --------------------------------------------
	org	0x04
; ISR
	movwf	w_t			; save w and status
	swapf	status,w
	movwf	s_t

	btfsc	intcon,gpif
	goto	port_change
	goto	gie_out			; nothing interesting

port_change
	btfss	reset_p			; reset pin went to ground?
	goto	do_reset

	btfsc	button			; must read to clear flag
	goto	button_off
	goto	button_on

do_reset
	call	debounce
	btfsc	reset_p
	goto	gie_out
	movlw	.4
	movwf	state
	goto	gie_out

button_on
	call	debounce
	btfsc	button
	goto	gie_out			; went back off too soon
	movlw	.1
	movwf	state
	goto	gie_out

button_off
	call	debounce
	btfss	button
	goto	gie_out			; went back on too soon
	; handle off here
	movf	state,w
	sublw	.2
	btfsc	status,z
	incf	state,f			; switch to state 3
	goto	gie_out

gie_out
	clrwdt
	; clear flags
	bcf	intcon,gpif
	; restore registers
	swapf	s_t,w			; restore status and w
	movwf	status
	swapf	w_t,f
	swapf	w_t,w
	retfie

; --------------------------------------------
init
	clrf	gpio
	clrf	state
	bsf	cmcon,cm0		; comparator off
	bsf	cmcon,cm1
	bsf	cmcon,cm2
	bank1
	movlw	b'00010001'		; GPIO0 & 4 inputs
	movwf	trisio
	bsf	option_reg,psa		; WDT prescaler
	bcf	option_reg,ps0		; prescaler 1:64
	bsf	option_reg,ps1
	bsf	option_reg,ps2
	bsf	ioc,ioc0		; interrupt on change GPIO0
	bcf	wpu,0			; weak pullup on GPIO0
	bsf	ioc,ioc4		; interrupt on change GPIO2
	bcf	wpu,4			; weak pullup on GPIO2
	bcf	option_reg,not_gppu	; global pullup enable
	bank0
; later we'll call digit_load here, and clear them
; only in response to a pin change
	call	load_digits
	clrwdt
	clrf	intcon
	bsf	intcon,gpie
	bsf	intcon,gie
	return

; --------------------------------------------
main
	call	init

infinite_l
	sleep
	nop				; gets executed on wakeup!
	; also remember that any enabled interrupt with a set
	; flag will immediately wake us up, even if gie is off
	nop				; just in case...
	; back from an interrupt, or wdt wakeup
	movf	state,w
	addwf	pcl,f
	goto	infinite_l
	goto	state1
	goto	state2
	goto	state3
	goto	state4
	goto	infinite_l		; shouldn't be here!
	goto	infinite_l

; --------------------------------------------
state1
	; button went on
	; enable and start timer, set state 2
	movlw	.3			; for about 4 seconds
	movwf	timer_c
	movlw	.2
	movwf	state
	clrwdt
	goto	infinite_l

; --------------------------------------------
state2
	; timer running, overflow
	; decrement timer counter, check if zero
	; read out and set state to 0
	decfsz	timer_c,f
	goto	infinite_l
	bcf	intcon,gpie
	call	read_out
	movlw	.0
	movwf	state
	bcf	intcon,gpif
	bsf	intcon,gpie
	goto	infinite_l
	

; --------------------------------------------
state3
	; timer running but button went off
	; beep, increment count, store
	bcf	intcon,gpie
	call	incr_count
	clrwdt
	movlw	.100
	movwf	pulse_c
	call	beep
	; set state to 0
	movlw	.0
	movwf	state
	bcf	intcon,gpif
	bsf	intcon,gpie
	goto	infinite_l

; --------------------------------------------
state4
	; reset pin down - beep, clear digits
	bcf	intcon,gpie
	clrwdt
	movlw	.80
	movwf	pulse_c
	call	beep
	call	cw_space
	clrwdt
	movlw	.80
	movwf	pulse_c
	call	beep
	call	cw_space
	clrwdt
	movlw	.80
	movwf	pulse_c
	call	beep
	call	cw_space
	clrwdt
	call	clear_digits
	; set state to 0
	movlw	.0
	movwf	state
	bcf	intcon,gpif
	bsf	intcon,gpie
	goto	infinite_l

; --------------------------------------------
clear_digits
; clear all digits
	movlw	eedig1		; note we are getting the address!
	movwf	ee_addr
	movlw	dig1
	movwf	dig_ptr
	movlw	ndig
	movwf	dig_ctr

digitclear_l
	clrwdt
	movf	dig_ptr,w
	movwf	fsr
	; write to eeprom
	movlw	.0
	movwf	indf
	call	ee_write
	incf	ee_addr,f
	incf	dig_ptr,f
	decfsz	dig_ctr,f
	goto	digitclear_l

	return

; --------------------------------------------
load_digits
; read all digits from eeprom
	movlw	eedig1		; note we are getting the address!
	movwf	ee_addr
	movlw	dig1		; note we are getting the address!
	movwf	dig_ptr
	movlw	ndig
	movwf	dig_ctr

digitload_l
	clrwdt
	movf	dig_ptr,w
	movwf	fsr
	; read from eeprom
	call	ee_read
	movwf	indf
	incf	ee_addr,f
	incf	dig_ptr,f
	decfsz	dig_ctr,f
	goto	digitload_l

	return

; --------------------------------------------
incr_count
; we will use decimal digits stored in dig1,...,dig5 to avoid
; the hassle of converting from two bytes later; dig1 is the
; least significant digit
	movlw	eedig1		; eeprom location for digit 1
	movwf	ee_addr
	movlw	dig1		; address of digit 1
	movwf	dig_ptr
	movlw	ndig
	movwf	dig_ctr

	; now loop ndig times at most, while there is
	; overflow

digitinc_l
	clrwdt
	movf	dig_ptr,w
	movwf	fsr
	incf	indf,w		; fetch the digit + 1
	xorlw	.10
	btfsc	status,z
	goto	overfl

	; increment for real, write to eeprom, return
	incf	indf,f
	movf	indf,w
	call	ee_write
	return

overfl	
	movlw	.0
	movwf	indf
	call	ee_write
	incf	ee_addr,f	; on to next digit
	incf	dig_ptr,f
	decfsz	dig_ctr,f	; no more digits left?
	goto	digitinc_l

	return

; --------------------------------------------
read_out
; beep the counter value
	movlw	eedig1		; get eeprom location
	addlw	ndig		; we start from the last digit
	movwf	ee_addr
	movlw	dig1
	addlw	ndig
	movwf	dig_ptr
	movlw	ndig
	movwf	dig_ctr

; you may want to suppress leading zeroes

digitread_l
	clrwdt
	decf	ee_addr,f
	decf	dig_ptr,f
	movf	dig_ptr,w
	movwf	fsr
	movf	indf,w
	call	cw_digit
	call	cw_space
	call	cw_space
	decfsz	dig_ctr,f	; no more digits left?
	goto	digitread_l

	return

; --------------------------------------------
debounce
	call	ms_delay	; wait a bit to settle
	call	ms_delay	; wait a bit to settle
	call	ms_delay	; wait a bit to settle
	call	ms_delay	; wait a bit to settle
	call	ms_delay	; wait a bit to settle
	call	ms_delay	; wait a bit to settle
	return

; --------------------------------------------
ms_delay
	movlw	.86
	movwf	dly_c
ms_dly_l
	decfsz	dly_c,f
	goto	ms_dly_l
	return

; --------------------------------------------
beep
; number of pulses in pulse_c
pulse_l
	bsf	buzzer
	call	ms_delay
	bcf	buzzer
	call	ms_delay
	bsf	buzzer
	call	ms_delay
	bcf	buzzer
	call	ms_delay
	decfsz	pulse_c,f
	goto	pulse_l
	return

; --------------------------------------------
cw_digit
; assume digit in w
	addwf	pcl,f
	goto	cw_0
	goto	cw_1
	goto	cw_2
	goto	cw_3
	goto	cw_4
	goto	cw_5
	goto	cw_6
	goto	cw_7
	goto	cw_8
	goto	cw_9
	return

; --------------------------------------------
cw_space
	clrwdt
	movlw	.250
	movwf	space_c
space_l
	call	ms_delay
	call	ms_delay
	decfsz  space_c,f
	goto	space_l
	return

; --------------------------------------------
cw_dit
	clrwdt
	movlw	.80
	movwf	pulse_c
	call	beep
	call	cw_space
	return

; --------------------------------------------
cw_dah
	clrwdt
	movlw	.200
	movwf	pulse_c
	call	beep
	call	cw_space
	return

; --------------------------------------------
cw_0
	call	cw_dah
	call	cw_dah
	call	cw_dah
	call	cw_dah
	call	cw_dah
	return

; --------------------------------------------
cw_1
	call	cw_dit
	call	cw_dah
	call	cw_dah
	call	cw_dah
	call	cw_dah
	return

; --------------------------------------------
cw_2
	call	cw_dit
	call	cw_dit
	call	cw_dah
	call	cw_dah
	call	cw_dah
	return

; --------------------------------------------
cw_3
	call	cw_dit
	call	cw_dit
	call	cw_dit
	call	cw_dah
	call	cw_dah
	return

; --------------------------------------------
cw_4
	call	cw_dit
	call	cw_dit
	call	cw_dit
	call	cw_dit
	call	cw_dah
	return

; --------------------------------------------
cw_5
	call	cw_dit
	call	cw_dit
	call	cw_dit
	call	cw_dit
	call	cw_dit
	return

; --------------------------------------------
cw_6
	call	cw_dah
	call	cw_dit
	call	cw_dit
	call	cw_dit
	call	cw_dit
	return

; --------------------------------------------
cw_7
	call	cw_dah
	call	cw_dah
	call	cw_dit
	call	cw_dit
	call	cw_dit
	return

; --------------------------------------------
cw_8
	call	cw_dah
	call	cw_dah
	call	cw_dah
	call	cw_dit
	call	cw_dit
	return

; --------------------------------------------
cw_9
	call	cw_dah
	call	cw_dah
	call	cw_dah
	call	cw_dah
	call	cw_dit
	return


; --------------------------------------------
ee_write
; write 1 byte (in w) into data EEPROM (addr. in ee_addr)
; only one byte should be written in each pass
gie_l
	bcf	intcon,gie
	btfsc	intcon,gie
	goto	gie_l
	clrwdt			; shouldn't be needed, but...
	bank1
	movwf	eedata		; copy data from w
	movf	ee_addr,w	; set up address
	movwf	eeadr
	bsf	eecon1,wren	; enable write
; standard write sequence
	movlw	0x55		; unlock write
	movwf	eecon2
	movlw	0xAA
	movwf	eecon2
	bsf	eecon1,wr
wait_ee
	btfsc	eecon1,wr
	goto	wait_ee
; done writing
	bcf	eecon1,wren	; disable write
	clrwdt			; shouldn't be needed, but...
	bcf	intcon,gpif
	bsf	intcon,gie
; verify, data written is still in eedata
	movf	eedata,w
	bsf	eecon1,rd
	xorwf	eedata,w
	bank0
	btfss	status,z
	retlw	.1
	retlw	.0

; --------------------------------------------
ee_error
; trouble writing to ee_addr; try again?
; warn and readout ee_data three times
	return

; --------------------------------------------
ee_read
; enter with EEPROM address in ee_addr
; exit with data in w
	bank1
	movf	ee_addr,w
	movwf	eeadr		; setup address to read
	bsf	eecon1,rd	; read it
	movf	eedata,w
	bank0
	return

; --------------------------------------------

	end


